@ooneex/cli 1.28.4 → 1.29.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -5209,6 +5209,7 @@ _oo() {
5209
5209
  'make\\:migration:Generate a new migration file'
5210
5210
  'migration\\:up:Run migrations for all modules'
5211
5211
  'make\\:module:Generate a new module'
5212
+ 'module\\:lock:Lock module migrations by hashing their content into a yml file'
5212
5213
  'remove\\:module:Remove an existing module'
5213
5214
  'make\\:permission:Generate a new permission class'
5214
5215
  'make\\:pubsub:Generate a new PubSub event class'
@@ -5281,6 +5282,11 @@ _oo() {
5281
5282
  _arguments -s \\
5282
5283
  '--module=[Module name]:module:_oo_modules'
5283
5284
  ;;
5285
+ module:lock)
5286
+ _arguments -s \\
5287
+ '--module=[Module name]:module:_oo_modules' \\
5288
+ '--override[Override already registered migrations]'
5289
+ ;;
5284
5290
  make:seed)
5285
5291
  _arguments -s \\
5286
5292
  '--name=[Name of the resource]:name' \\
@@ -5368,6 +5374,7 @@ _ooneex() {
5368
5374
  'make\\:migration:Generate a new migration file'
5369
5375
  'migration\\:up:Run migrations for all modules'
5370
5376
  'make\\:module:Generate a new module'
5377
+ 'module\\:lock:Lock module migrations by hashing their content into a yml file'
5371
5378
  'remove\\:module:Remove an existing module'
5372
5379
  'make\\:permission:Generate a new permission class'
5373
5380
  'make\\:pubsub:Generate a new PubSub event class'
@@ -5440,6 +5447,11 @@ _ooneex() {
5440
5447
  _arguments -s \\
5441
5448
  '--module=[Module name]:module:_ooneex_modules'
5442
5449
  ;;
5450
+ module:lock)
5451
+ _arguments -s \\
5452
+ '--module=[Module name]:module:_ooneex_modules' \\
5453
+ '--override[Override already registered migrations]'
5454
+ ;;
5443
5455
  make:seed)
5444
5456
  _arguments -s \\
5445
5457
  '--name=[Name of the resource]:name' \\
@@ -5822,6 +5834,7 @@ ${closingIndent}`;
5822
5834
  await Bun.write(join6(srcDir, `${pascalName}Module.ts`), moduleContent);
5823
5835
  await Bun.write(join6(moduleDir, "package.json"), packageContent);
5824
5836
  await Bun.write(join6(moduleDir, "tsconfig.json"), tsconfig_default);
5837
+ await Bun.write(join6(moduleDir, `${kebabName}.yml`), "");
5825
5838
  await Bun.write(join6(testsDir, `${pascalName}Module.spec.ts`), testContent);
5826
5839
  if (kebabName !== "app") {
5827
5840
  const appModulePath = join6(cwd, "modules", "app", "src", "AppModule.ts");
@@ -7603,58 +7616,58 @@ git commit -m "refactor(product): Reorganize service file structure"
7603
7616
  `;
7604
7617
 
7605
7618
  // src/templates/claude/skills/make.ai.md.txt
7606
- 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## Important\n\nAll commands must be run from the root of the project (not from within a package or module directory).\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';
7619
+ 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## Important\n\nAll commands must be run from the root of the project (not from within a package or module directory).\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 replace the scaffolded tests with a comprehensive suite that covers all use cases.\n\n**Coverage requirements:**\n\n- **Class identity**: name ends with `Ai`, is a constructor function\n- **`run` contract**: method exists, returns a `Promise` when called with and without a prompt string\n- **`runStream` contract**: method exists, returns an `AsyncGenerator` (has `Symbol.asyncIterator`)\n- **Prompt forwarding**: use a mock `OpenAi` instance to verify the prompt is forwarded correctly\n- **Instance isolation**: two instances are independent\n\nThe complete test file must follow this structure:\n\n```typescript\nimport { describe, expect, mock, test } from "bun:test";\nimport { <Name>Ai } from "@/ai/<Name>Ai";\n\ndescribe("<Name>Ai", () => {\n // --- Class identity ---\n\n test("should have class name ending with \'Ai\'", () => {\n expect(<Name>Ai.name.endsWith("Ai")).toBe(true);\n });\n\n test("should be a constructor function", () => {\n expect(typeof <Name>Ai).toBe("function");\n });\n\n // --- run ---\n\n test("should have \'run\' method", () => {\n expect(typeof <Name>Ai.prototype.run).toBe("function");\n });\n\n test("\'run\' should return a Promise", () => {\n const mockAi = { run: mock(() => Promise.resolve("result")), runStream: mock(async function* () {}) };\n const ai = new <Name>Ai(mockAi as any);\n const result = ai.run();\n expect(result).toBeInstanceOf(Promise);\n return result.catch(() => {});\n });\n\n test("\'run\' should forward the prompt to the underlying AI client", async () => {\n const runMock = mock(() => Promise.resolve("result"));\n const mockAi = { run: runMock, runStream: mock(async function* () {}) };\n const ai = new <Name>Ai(mockAi as any);\n await ai.run("Custom prompt");\n expect(runMock).toHaveBeenCalledWith("Custom prompt", undefined);\n });\n\n test("\'run\' should use the default prompt when none is provided", async () => {\n const runMock = mock((_prompt: string) => Promise.resolve("result"));\n const mockAi = { run: runMock, runStream: mock(async function* () {}) };\n const ai = new <Name>Ai(mockAi as any);\n await ai.run();\n const calledWith = runMock.mock.calls[0]?.[0];\n expect(typeof calledWith).toBe("string");\n expect((calledWith as string).length).toBeGreaterThan(0);\n });\n\n // --- runStream ---\n\n test("should have \'runStream\' method", () => {\n expect(typeof <Name>Ai.prototype.runStream).toBe("function");\n });\n\n test("\'runStream\' should return an AsyncGenerator", () => {\n const mockAi = {\n run: mock(() => Promise.resolve("")),\n runStream: mock(async function* () { yield "token"; }),\n };\n const ai = new <Name>Ai(mockAi as any);\n const gen = ai.runStream();\n expect(typeof gen[Symbol.asyncIterator]).toBe("function");\n gen.return(undefined);\n });\n\n test("\'runStream\' should yield tokens from the underlying AI client", async () => {\n const mockAi = {\n run: mock(() => Promise.resolve("")),\n runStream: mock(async function* () { yield "hello"; yield " world"; }),\n };\n const ai = new <Name>Ai(mockAi as any);\n const tokens: string[] = [];\n for await (const token of ai.runStream()) {\n tokens.push(token);\n }\n expect(tokens).toEqual(["hello", " world"]);\n });\n\n // --- Instance isolation ---\n\n test("should produce independent instances", () => {\n const mockAi = { run: mock(() => Promise.resolve("")), runStream: mock(async function* () {}) };\n const a = new <Name>Ai(mockAi as any);\n const b = new <Name>Ai(mockAi as any);\n expect(a).not.toBe(b);\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';
7607
7620
 
7608
7621
  // src/templates/claude/skills/make.analytics.md.txt
7609
- 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## Important\n\nAll commands must be run from the root of the project (not from within a package or module directory).\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';
7622
+ 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## Important\n\nAll commands must be run from the root of the project (not from within a package or module directory).\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 replace the scaffolded tests with a comprehensive suite that covers all use cases.\n\n**Coverage requirements:**\n\n- **Class identity**: name ends with `Analytics`, is a constructor function\n- **`capture` contract**: method exists, is synchronous (returns `void`), does not throw with valid options\n- **Options shape**: calling `capture` with the expected `CaptureOptionsType` fields produces the expected side effect (spy on the underlying tracker if applicable)\n- **Edge cases**: calling `capture` with an empty object does not throw\n- **Instance isolation**: two instances are independent\n\nThe complete test file must follow this structure:\n\n```typescript\nimport { describe, expect, mock, test } from "bun:test";\nimport { <Name>Analytics } from "@/analytics/<Name>Analytics";\n\ndescribe("<Name>Analytics", () => {\n // --- Class identity ---\n\n test("should have class name ending with \'Analytics\'", () => {\n expect(<Name>Analytics.name.endsWith("Analytics")).toBe(true);\n });\n\n test("should be a constructor function", () => {\n expect(typeof <Name>Analytics).toBe("function");\n });\n\n // --- capture method ---\n\n test("should have \'capture\' method", () => {\n expect(typeof <Name>Analytics.prototype.capture).toBe("function");\n });\n\n test("\'capture\' should not throw when called with an empty object", () => {\n const analytics = new <Name>Analytics();\n expect(() => analytics.capture({} as any)).not.toThrow();\n });\n\n test("\'capture\' should not throw when called with valid options", () => {\n const analytics = new <Name>Analytics();\n expect(() => analytics.capture({ event: "page_view", userId: "u1" } as any)).not.toThrow();\n });\n\n test("\'capture\' should return void", () => {\n const analytics = new <Name>Analytics();\n const result = analytics.capture({ event: "click" } as any);\n expect(result).toBeUndefined();\n });\n\n // --- Behavioral tests (add after implementing capture) ---\n // Example: spy on the underlying tracker to verify the event is forwarded.\n //\n // test("should forward the event to the underlying tracker", () => {\n // const tracker = { track: mock(() => {}) };\n // const analytics = new <Name>Analytics(tracker as any);\n // analytics.capture({ event: "signup", userId: "u2" });\n // expect(tracker.track).toHaveBeenCalledWith(\n // expect.objectContaining({ event: "signup", userId: "u2" }),\n // );\n // });\n\n // --- Instance isolation ---\n\n test("should produce independent instances", () => {\n const a = new <Name>Analytics();\n const b = new <Name>Analytics();\n expect(a).not.toBe(b);\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';
7610
7623
 
7611
7624
  // src/templates/claude/skills/make.cache.md.txt
7612
- 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## Important\n\nAll commands must be run from the root of the project (not from within a package or module directory).\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';
7625
+ 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## Important\n\nAll commands must be run from the root of the project (not from within a package or module directory).\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 replace the scaffolded tests with a comprehensive suite that covers all use cases.\n\n**Coverage requirements:**\n\n- **Class identity**: name ends with `Cache`, is a constructor function\n- **Each method**: `get`, `set`, `delete`, `has` exist, are functions, return Promises\n- **Default implementation**: before replacing the throws, verify each method rejects with `CacheException`\n- **After implementation**: add set\u2192get round-trip, set\u2192has, set\u2192delete\u2192has, TTL semantics if supported\n- **Instance isolation**: two instances maintain separate state\n\nThe complete test file must follow this structure:\n\n```typescript\nimport { CacheException } from "@ooneex/cache";\nimport { describe, expect, test } from "bun:test";\nimport { <Name>Cache } from "@/cache/<Name>Cache";\n\ndescribe("<Name>Cache", () => {\n // --- Class identity ---\n\n test("should have class name ending with \'Cache\'", () => {\n expect(<Name>Cache.name.endsWith("Cache")).toBe(true);\n });\n\n test("should be a constructor function", () => {\n expect(typeof <Name>Cache).toBe("function");\n });\n\n // --- Method existence and return type ---\n\n test("should have \'get\' method that returns a Promise", () => {\n const cache = new <Name>Cache();\n expect(typeof <Name>Cache.prototype.get).toBe("function");\n const result = cache.get("k");\n expect(result).toBeInstanceOf(Promise);\n return result.catch(() => {});\n });\n\n test("should have \'set\' method that returns a Promise", () => {\n const cache = new <Name>Cache();\n expect(typeof <Name>Cache.prototype.set).toBe("function");\n const result = cache.set("k", "v");\n expect(result).toBeInstanceOf(Promise);\n return result.catch(() => {});\n });\n\n test("should have \'delete\' method that returns a Promise", () => {\n const cache = new <Name>Cache();\n expect(typeof <Name>Cache.prototype.delete).toBe("function");\n const result = cache.delete("k");\n expect(result).toBeInstanceOf(Promise);\n return result.catch(() => {});\n });\n\n test("should have \'has\' method that returns a Promise", () => {\n const cache = new <Name>Cache();\n expect(typeof <Name>Cache.prototype.has).toBe("function");\n const result = cache.has("k");\n expect(result).toBeInstanceOf(Promise);\n return result.catch(() => {});\n });\n\n // --- Behavioral tests (add after implementing the methods) ---\n // Replace the CacheException tests below with real round-trip tests once implemented.\n\n test("\'get\' should reject with CacheException before implementation", async () => {\n const cache = new <Name>Cache();\n await expect(cache.get("key")).rejects.toBeInstanceOf(CacheException);\n });\n\n test("\'set\' should reject with CacheException before implementation", async () => {\n const cache = new <Name>Cache();\n await expect(cache.set("key", "value")).rejects.toBeInstanceOf(CacheException);\n });\n\n test("\'delete\' should reject with CacheException before implementation", async () => {\n const cache = new <Name>Cache();\n await expect(cache.delete("key")).rejects.toBeInstanceOf(CacheException);\n });\n\n test("\'has\' should reject with CacheException before implementation", async () => {\n const cache = new <Name>Cache();\n await expect(cache.has("key")).rejects.toBeInstanceOf(CacheException);\n });\n\n // Once the cache is implemented, replace the four tests above with:\n //\n // test("set then get should return the stored value", async () => {\n // const cache = new <Name>Cache();\n // await cache.set("user:1", { name: "Alice" });\n // const value = await cache.get("user:1");\n // expect(value).toEqual({ name: "Alice" });\n // });\n //\n // test("has should return true after set", async () => {\n // const cache = new <Name>Cache();\n // await cache.set("flag", true);\n // expect(await cache.has("flag")).toBe(true);\n // });\n //\n // test("has should return false for unknown key", async () => {\n // const cache = new <Name>Cache();\n // expect(await cache.has("unknown-key")).toBe(false);\n // });\n //\n // test("delete should remove a key", async () => {\n // const cache = new <Name>Cache();\n // await cache.set("tmp", 42);\n // await cache.delete("tmp");\n // expect(await cache.has("tmp")).toBe(false);\n // });\n //\n // test("get should return undefined for a deleted key", async () => {\n // const cache = new <Name>Cache();\n // await cache.set("tmp", 42);\n // await cache.delete("tmp");\n // expect(await cache.get("tmp")).toBeUndefined();\n // });\n\n // --- Instance isolation ---\n\n test("should produce independent instances", () => {\n const a = new <Name>Cache();\n const b = new <Name>Cache();\n expect(a).not.toBe(b);\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';
7613
7626
 
7614
7627
  // src/templates/claude/skills/make.controller.md.txt
7615
- 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## Important\n\nAll commands must be run from the root of the project (not from within a package or module directory).\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., `/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., `/users/:id`). Remove entirely for routes with no URL parameters (e.g., `/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 /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 /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 /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 /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 (mutation only)\n\n**Only create a pubsub event for mutation routes** (`post`, `put`, `patch`, `delete`). Do NOT create a pubsub event for `get` routes or read-only endpoints.\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";
7628
+ 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## Important\n\nAll commands must be run from the root of the project (not from within a package or module directory).\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., `/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., `/users/:id`). Remove entirely for routes with no URL parameters (e.g., `/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 /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 /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 /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 /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 replace the scaffolded tests with a comprehensive suite that covers all use cases.\n\n**Coverage requirements:**\n\n- **Class identity**: name ends with `Controller`, is a constructor function\n- **`index` contract**: method exists, returns a `Promise`\n- **Minimal context**: use a stub context with a `response.json` spy to verify the response shape\n- **Instance isolation**: two instances are independent\n\nThe complete test file must follow this structure:\n\n```typescript\nimport { describe, expect, mock, test } from \"bun:test\";\nimport { <Name>Controller } from \"@/controllers/<Name>Controller\";\n\ndescribe(\"<Name>Controller\", () => {\n // --- Class identity ---\n\n test(\"should have class name ending with 'Controller'\", () => {\n expect(<Name>Controller.name.endsWith(\"Controller\")).toBe(true);\n });\n\n test(\"should be a constructor function\", () => {\n expect(typeof <Name>Controller).toBe(\"function\");\n });\n\n // --- index method ---\n\n test(\"should have 'index' method\", () => {\n expect(typeof <Name>Controller.prototype.index).toBe(\"function\");\n });\n\n test(\"'index' should return a Promise\", () => {\n const controller = new <Name>Controller();\n const context = { response: { json: () => {} } } as any;\n const result = controller.index(context);\n expect(result).toBeInstanceOf(Promise);\n return result.catch(() => {});\n });\n\n test(\"'index' should call context.response.json\", async () => {\n const controller = new <Name>Controller();\n const json = mock(() => {});\n const context = { response: { json } } as any;\n try {\n await controller.index(context);\n expect(json).toHaveBeenCalledTimes(1);\n } catch {\n // Expected when injected dependencies are absent \u2014 still validates delegation\n }\n });\n\n // --- Response shape tests ---\n // After injecting a mock service, add tests that verify the JSON payload shape.\n //\n // Example:\n // test(\"'index' should return the expected payload structure\", async () => {\n // let captured: unknown;\n // const context = { response: { json: (data: unknown) => { captured = data; } } } as any;\n // await new <Name>Controller().index(context);\n // expect(captured).toMatchObject({ success: true });\n // });\n\n // --- Instance isolation ---\n\n test(\"should produce independent instances\", () => {\n const a = new <Name>Controller();\n const b = new <Name>Controller();\n expect(a).not.toBe(b);\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 (mutation only)\n\n**Only create a pubsub event for mutation routes** (`post`, `put`, `patch`, `delete`). Do NOT create a pubsub event for `get` routes or read-only endpoints.\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";
7616
7629
 
7617
7630
  // src/templates/claude/skills/make.cron.md.txt
7618
- 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## Important\n\nAll commands must be run from the root of the project (not from within a package or module directory).\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';
7631
+ 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## Important\n\nAll commands must be run from the root of the project (not from within a package or module directory).\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 replace the scaffolded tests with a comprehensive suite that covers all use cases.\n\n**Coverage requirements:**\n\n- **Class identity**: name ends with `Cron`, is a constructor function\n- **`getTime`**: exists, returns a non-empty string, matches the `every N unit` format\n- **`getTimeZone`**: exists, returns `null` or a non-empty IANA timezone string\n- **`handler` contract**: exists, returns a `Promise`\n- **Instance isolation**: two instances are independent\n\nThe complete test file must follow this structure:\n\n```typescript\nimport { describe, expect, test } from "bun:test";\nimport { <Name>Cron } from "@/cron/<Name>Cron";\n\ndescribe("<Name>Cron", () => {\n // --- Class identity ---\n\n test("should have class name ending with \'Cron\'", () => {\n expect(<Name>Cron.name.endsWith("Cron")).toBe(true);\n });\n\n test("should be a constructor function", () => {\n expect(typeof <Name>Cron).toBe("function");\n });\n\n // --- getTime ---\n\n test("should have \'getTime\' method", () => {\n expect(typeof <Name>Cron.prototype.getTime).toBe("function");\n });\n\n test("\'getTime\' should return a non-empty string", () => {\n const cron = new <Name>Cron();\n const time = cron.getTime();\n expect(typeof time).toBe("string");\n expect(time.length).toBeGreaterThan(0);\n });\n\n test("\'getTime\' should use the \'every N unit\' format", () => {\n const cron = new <Name>Cron();\n expect(cron.getTime()).toMatch(/^every \\d+ (second|minute|hour|day)s?$/);\n });\n\n // --- getTimeZone ---\n\n test("should have \'getTimeZone\' method", () => {\n expect(typeof <Name>Cron.prototype.getTimeZone).toBe("function");\n });\n\n test("\'getTimeZone\' should return null or a non-empty string", () => {\n const cron = new <Name>Cron();\n const tz = cron.getTimeZone();\n expect(tz === null || (typeof tz === "string" && tz.length > 0)).toBe(true);\n });\n\n // --- handler ---\n\n test("should have \'handler\' method", () => {\n expect(typeof <Name>Cron.prototype.handler).toBe("function");\n });\n\n test("\'handler\' should return a Promise", () => {\n const cron = new <Name>Cron();\n const result = cron.handler();\n expect(result).toBeInstanceOf(Promise);\n return result.catch(() => {});\n });\n\n test("\'handler\' should resolve without throwing", async () => {\n const cron = new <Name>Cron();\n try {\n await cron.handler();\n } catch {\n // Expected when injected dependencies are absent\n }\n });\n\n // --- Instance isolation ---\n\n test("should produce independent instances", () => {\n const a = new <Name>Cron();\n const b = new <Name>Cron();\n expect(a).not.toBe(b);\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';
7619
7632
 
7620
7633
  // src/templates/claude/skills/make.database.md.txt
7621
- 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## Important\n\nAll commands must be run from the root of the project (not from within a package or module directory).\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';
7634
+ 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## Important\n\nAll commands must be run from the root of the project (not from within a package or module directory).\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 replace the scaffolded tests with a comprehensive suite that covers all use cases.\n\n**Coverage requirements:**\n\n- **Class identity**: name ends with `Database`, is a constructor function\n- **`getSource` contract**: method exists, returns a `DataSource` instance (has `initialize` and `destroy` methods)\n- **Default path**: calling `getSource()` without arguments uses `"var/db"` as the database path\n- **Custom path**: calling `getSource("custom/path")` uses the provided path\n- **Entities registered**: the returned `DataSource` options include the expected entities\n- **Instance isolation**: two instances are independent\n\nThe complete test file must follow this structure:\n\n```typescript\nimport { describe, expect, test } from "bun:test";\nimport { <Name>Database } from "@/databases/<Name>Database";\n\ndescribe("<Name>Database", () => {\n // --- Class identity ---\n\n test("should have class name ending with \'Database\'", () => {\n expect(<Name>Database.name.endsWith("Database")).toBe(true);\n });\n\n test("should be a constructor function", () => {\n expect(typeof <Name>Database).toBe("function");\n });\n\n // --- getSource ---\n\n test("should have \'getSource\' method", () => {\n expect(typeof <Name>Database.prototype.getSource).toBe("function");\n });\n\n test("\'getSource\' should return a DataSource-like object", () => {\n const db = new <Name>Database();\n const source = db.getSource();\n expect(source).toBeDefined();\n expect(typeof source.initialize).toBe("function");\n expect(typeof source.destroy).toBe("function");\n });\n\n test("\'getSource\' should use the default database path when no argument is given", () => {\n const db = new <Name>Database();\n const source = db.getSource();\n expect((source.options as any).database).toBe("var/db");\n });\n\n test("\'getSource\' should use the provided path when given", () => {\n const db = new <Name>Database();\n const source = db.getSource("custom/path/db");\n expect((source.options as any).database).toBe("custom/path/db");\n });\n\n test("\'getSource\' options should have synchronize disabled", () => {\n const db = new <Name>Database();\n const source = db.getSource();\n expect((source.options as any).synchronize).toBe(false);\n });\n\n test("\'getSource\' options should include the registered entities", () => {\n const db = new <Name>Database();\n const source = db.getSource();\n expect(Array.isArray((source.options as any).entities)).toBe(true);\n });\n\n // --- Instance isolation ---\n\n test("should produce independent instances", () => {\n const a = new <Name>Database();\n const b = new <Name>Database();\n expect(a).not.toBe(b);\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';
7622
7635
 
7623
7636
  // src/templates/claude/skills/make.entity.md.txt
7624
- 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, always add `null` to the type of optional properties (i.e., any property marked with `?`). For example, `createdAt?: Date;` must be written as `createdAt?: Date | null;`\n- For optional properties, do NOT use `= undefined` initializer. Use `name?: string | null;` not `name?: string | null = undefined;`\n- Avoid non-null assertions (`!`). For example, `public name!: string;` should be `public name: string;`\n- Always specify `nullable` explicitly in every `@Column` decorator. Never omit it. Use `nullable: true` or `nullable: false`. For example, never write `@Column({ name: \"name\", type: \"varchar\", length: 150 })` \u2014 always write `@Column({ name: \"name\", type: \"varchar\", length: 150, nullable: true })` or `@Column({ name: \"name\", type: \"varchar\", length: 150, nullable: false })`\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## Important\n\nAll commands must be run from the root of the project (not from within a package or module directory).\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 | null;\n\n @Column({ name: \"locked_at\", type: \"timestamptz\", nullable: true })\n lockedAt?: Date | null;\n\n @Column({\n name: \"is_blocked\",\n type: \"boolean\",\n default: false,\n nullable: true,\n })\n isBlocked?: boolean | null;\n\n @Column({ name: \"blocked_at\", type: \"timestamptz\", nullable: true })\n blockedAt?: Date | null;\n\n @Column({ name: \"block_reason\", type: \"text\", nullable: true })\n blockReason?: string | null;\n\n @Column({ name: \"is_public\", type: \"boolean\", default: true, nullable: true })\n isPublic?: boolean | null;\n\n @Column({ name: \"lang\", type: \"varchar\", length: 10, nullable: true })\n lang?: LocaleType | null;\n\n @CreateDateColumn({ name: \"created_at\" })\n createdAt?: Date | null;\n\n @UpdateDateColumn({ name: \"updated_at\" })\n updatedAt?: Date | null;\n\n @DeleteDateColumn({ name: \"deleted_at\" })\n deletedAt?: Date | null;\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";
7637
+ 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, always add `null` to the type of optional properties (i.e., any property marked with `?`). For example, `createdAt?: Date;` must be written as `createdAt?: Date | null;`\n- For optional properties, do NOT use `= undefined` initializer. Use `name?: string | null;` not `name?: string | null = undefined;`\n- Avoid non-null assertions (`!`). For example, `public name!: string;` should be `public name: string;`\n- Always specify `nullable` explicitly in every `@Column` decorator. Never omit it. Use `nullable: true` or `nullable: false`. For example, never write `@Column({ name: \"name\", type: \"varchar\", length: 150 })` \u2014 always write `@Column({ name: \"name\", type: \"varchar\", length: 150, nullable: true })` or `@Column({ name: \"name\", type: \"varchar\", length: 150, nullable: false })`\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## Important\n\nAll commands must be run from the root of the project (not from within a package or module directory).\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 | null;\n\n @Column({ name: \"locked_at\", type: \"timestamptz\", nullable: true })\n lockedAt?: Date | null;\n\n @Column({\n name: \"is_blocked\",\n type: \"boolean\",\n default: false,\n nullable: true,\n })\n isBlocked?: boolean | null;\n\n @Column({ name: \"blocked_at\", type: \"timestamptz\", nullable: true })\n blockedAt?: Date | null;\n\n @Column({ name: \"block_reason\", type: \"text\", nullable: true })\n blockReason?: string | null;\n\n @Column({ name: \"is_public\", type: \"boolean\", default: true, nullable: true })\n isPublic?: boolean | null;\n\n @Column({ name: \"lang\", type: \"varchar\", length: 10, nullable: true })\n lang?: LocaleType | null;\n\n @CreateDateColumn({ name: \"created_at\" })\n createdAt?: Date | null;\n\n @UpdateDateColumn({ name: \"updated_at\" })\n updatedAt?: Date | null;\n\n @DeleteDateColumn({ name: \"deleted_at\" })\n deletedAt?: Date | null;\n}\n```\n\n### 4. Complete the test file\n\nEdit `tests/entities/<Name>Entity.spec.ts` to replace the scaffolded tests with a comprehensive suite that covers all use cases.\n\n**Coverage requirements:**\n\n- **Class identity**: class name ends with `Entity`, is a constructor function\n- **ID generation**: auto-generated, string, 25 chars, unique per instance\n- **Each column property**: property exists (`\"prop\" in entity`), default value is correct, accepts valid values, accepts `null` for nullable columns, accepts `undefined` for optional columns\n- **Boolean columns with defaults**: verify the default is `false` or `true` as declared, verify toggling to the opposite value works\n- **Date/timestamp columns**: start as `undefined`/`null`, accept a `Date` object, accept `null`\n- **String columns**: default is `undefined`/`null`, accepts a string value, accepts `null`\n- **Property assignment**: setting a property on one instance does not affect another instance (no shared state)\n- **Multiple instances**: two instances created in the same test have different `id` values\n\nThe complete test file must follow this structure \u2014 replace `<Name>` with the actual entity name and add/remove property blocks to match the final entity columns:\n\n```typescript\nimport { describe, expect, test } from \"bun:test\";\nimport { <Name>Entity } from \"@/entities/<Name>Entity\";\n\ndescribe(\"<Name>Entity\", () => {\n // --- Class identity ---\n\n test(\"should have class name ending with 'Entity'\", () => {\n expect(<Name>Entity.name.endsWith(\"Entity\")).toBe(true);\n });\n\n test(\"should be a constructor function\", () => {\n expect(typeof <Name>Entity).toBe(\"function\");\n });\n\n // --- ID ---\n\n test(\"should auto-generate a nanoid for 'id'\", () => {\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 generate a unique 'id' for each instance\", () => {\n const a = new <Name>Entity();\n const b = new <Name>Entity();\n expect(a.id).not.toBe(b.id);\n });\n\n // --- isLocked ---\n\n test(\"should have 'isLocked' defaulting to undefined\", () => {\n const entity = new <Name>Entity();\n expect(\"isLocked\" in entity).toBe(true);\n expect(entity.isLocked).toBeUndefined();\n });\n\n test(\"should accept true for 'isLocked'\", () => {\n const entity = new <Name>Entity();\n entity.isLocked = true;\n expect(entity.isLocked).toBe(true);\n });\n\n test(\"should accept false for 'isLocked'\", () => {\n const entity = new <Name>Entity();\n entity.isLocked = false;\n expect(entity.isLocked).toBe(false);\n });\n\n test(\"should accept null for 'isLocked'\", () => {\n const entity = new <Name>Entity();\n entity.isLocked = null;\n expect(entity.isLocked).toBeNull();\n });\n\n // --- lockedAt ---\n\n test(\"should have 'lockedAt' defaulting to undefined\", () => {\n const entity = new <Name>Entity();\n expect(\"lockedAt\" in entity).toBe(true);\n expect(entity.lockedAt).toBeUndefined();\n });\n\n test(\"should accept a Date for 'lockedAt'\", () => {\n const entity = new <Name>Entity();\n const date = new Date(\"2025-01-01T00:00:00Z\");\n entity.lockedAt = date;\n expect(entity.lockedAt).toEqual(date);\n });\n\n test(\"should accept null for 'lockedAt'\", () => {\n const entity = new <Name>Entity();\n entity.lockedAt = null;\n expect(entity.lockedAt).toBeNull();\n });\n\n // --- isBlocked ---\n\n test(\"should have 'isBlocked' defaulting to undefined\", () => {\n const entity = new <Name>Entity();\n expect(\"isBlocked\" in entity).toBe(true);\n expect(entity.isBlocked).toBeUndefined();\n });\n\n test(\"should accept true for 'isBlocked'\", () => {\n const entity = new <Name>Entity();\n entity.isBlocked = true;\n expect(entity.isBlocked).toBe(true);\n });\n\n test(\"should accept null for 'isBlocked'\", () => {\n const entity = new <Name>Entity();\n entity.isBlocked = null;\n expect(entity.isBlocked).toBeNull();\n });\n\n // --- blockedAt ---\n\n test(\"should have 'blockedAt' defaulting to undefined\", () => {\n const entity = new <Name>Entity();\n expect(\"blockedAt\" in entity).toBe(true);\n expect(entity.blockedAt).toBeUndefined();\n });\n\n test(\"should accept a Date for 'blockedAt'\", () => {\n const entity = new <Name>Entity();\n const date = new Date(\"2025-06-15T12:00:00Z\");\n entity.blockedAt = date;\n expect(entity.blockedAt).toEqual(date);\n });\n\n test(\"should accept null for 'blockedAt'\", () => {\n const entity = new <Name>Entity();\n entity.blockedAt = null;\n expect(entity.blockedAt).toBeNull();\n });\n\n // --- blockReason ---\n\n test(\"should have 'blockReason' defaulting to undefined\", () => {\n const entity = new <Name>Entity();\n expect(\"blockReason\" in entity).toBe(true);\n expect(entity.blockReason).toBeUndefined();\n });\n\n test(\"should accept a string for 'blockReason'\", () => {\n const entity = new <Name>Entity();\n entity.blockReason = \"Violated terms of service\";\n expect(entity.blockReason).toBe(\"Violated terms of service\");\n });\n\n test(\"should accept null for 'blockReason'\", () => {\n const entity = new <Name>Entity();\n entity.blockReason = null;\n expect(entity.blockReason).toBeNull();\n });\n\n // --- isPublic ---\n\n test(\"should have 'isPublic' defaulting to undefined\", () => {\n const entity = new <Name>Entity();\n expect(\"isPublic\" in entity).toBe(true);\n expect(entity.isPublic).toBeUndefined();\n });\n\n test(\"should accept true for 'isPublic'\", () => {\n const entity = new <Name>Entity();\n entity.isPublic = true;\n expect(entity.isPublic).toBe(true);\n });\n\n test(\"should accept false for 'isPublic'\", () => {\n const entity = new <Name>Entity();\n entity.isPublic = false;\n expect(entity.isPublic).toBe(false);\n });\n\n test(\"should accept null for 'isPublic'\", () => {\n const entity = new <Name>Entity();\n entity.isPublic = null;\n expect(entity.isPublic).toBeNull();\n });\n\n // --- lang ---\n\n test(\"should have 'lang' defaulting to undefined\", () => {\n const entity = new <Name>Entity();\n expect(\"lang\" in entity).toBe(true);\n expect(entity.lang).toBeUndefined();\n });\n\n test(\"should accept a locale string for 'lang'\", () => {\n const entity = new <Name>Entity();\n entity.lang = \"en\";\n expect(entity.lang).toBe(\"en\");\n });\n\n test(\"should accept null for 'lang'\", () => {\n const entity = new <Name>Entity();\n entity.lang = null;\n expect(entity.lang).toBeNull();\n });\n\n // --- createdAt / updatedAt / deletedAt ---\n\n test(\"should have 'createdAt' defaulting to undefined\", () => {\n const entity = new <Name>Entity();\n expect(\"createdAt\" in entity).toBe(true);\n expect(entity.createdAt).toBeUndefined();\n });\n\n test(\"should accept a Date for 'createdAt'\", () => {\n const entity = new <Name>Entity();\n const date = new Date();\n entity.createdAt = date;\n expect(entity.createdAt).toEqual(date);\n });\n\n test(\"should have 'updatedAt' defaulting to undefined\", () => {\n const entity = new <Name>Entity();\n expect(\"updatedAt\" in entity).toBe(true);\n expect(entity.updatedAt).toBeUndefined();\n });\n\n test(\"should accept a Date for 'updatedAt'\", () => {\n const entity = new <Name>Entity();\n const date = new Date();\n entity.updatedAt = date;\n expect(entity.updatedAt).toEqual(date);\n });\n\n test(\"should have 'deletedAt' defaulting to undefined\", () => {\n const entity = new <Name>Entity();\n expect(\"deletedAt\" in entity).toBe(true);\n expect(entity.deletedAt).toBeUndefined();\n });\n\n test(\"should accept a Date for 'deletedAt'\", () => {\n const entity = new <Name>Entity();\n const date = new Date();\n entity.deletedAt = date;\n expect(entity.deletedAt).toEqual(date);\n });\n\n test(\"should accept null for 'deletedAt' (soft-delete reset)\", () => {\n const entity = new <Name>Entity();\n entity.deletedAt = null;\n expect(entity.deletedAt).toBeNull();\n });\n\n // --- Instance isolation ---\n\n test(\"should not share property state between instances\", () => {\n const a = new <Name>Entity();\n const b = new <Name>Entity();\n a.isLocked = true;\n expect(b.isLocked).toBeUndefined();\n });\n});\n```\n\n**When entity-specific columns were added or scaffolded columns removed**, update the test file accordingly:\n- Remove test blocks for columns that were removed from the entity.\n- Add new test blocks for every custom column following the same patterns above (presence check, default value, valid assignment, null acceptance for nullable columns).\n- For relation properties (e.g., `@ManyToOne`), add a test that the property exists and defaults to `undefined`.\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, use the `make:migration` skill to generate and complete the migration file for this entity.\n\n### 7. Create a repository for the entity\n\nAfter creating the entity, use the `make:repository` skill to generate and complete the repository for this entity.\n\n### 8. Lint and format\n\nRun linting and formatting on the entity files:\n\n```bash\nbunx biome check --fix src/entities/<Name>Entity.ts tests/entities/<Name>Entity.spec.ts\n```\n";
7625
7638
 
7626
7639
  // src/templates/claude/skills/make.logger.md.txt
7627
- 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## Important\n\nAll commands must be run from the root of the project (not from within a package or module directory).\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';
7640
+ 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## Important\n\nAll commands must be run from the root of the project (not from within a package or module directory).\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 replace the scaffolded tests with a comprehensive suite that covers all use cases.\n\n**Coverage requirements:**\n\n- **Class identity**: name ends with `Logger`, is a constructor function\n- **`init` contract**: method exists, returns a `Promise`\n- **Each log method** (`log`, `debug`, `info`, `success`, `warn`, `error`): method exists, does not throw when called with a message string, does not throw when called with a message and a data object\n- **`error` with exception**: calling `error` with an `IException` instance does not throw\n- **Instance isolation**: two instances are independent\n\nThe complete test file must follow this structure:\n\n```typescript\nimport { describe, expect, test } from "bun:test";\nimport { <Name>Logger } from "@/loggers/<Name>Logger";\n\ndescribe("<Name>Logger", () => {\n // --- Class identity ---\n\n test("should have class name ending with \'Logger\'", () => {\n expect(<Name>Logger.name.endsWith("Logger")).toBe(true);\n });\n\n test("should be a constructor function", () => {\n expect(typeof <Name>Logger).toBe("function");\n });\n\n // --- init ---\n\n test("should have \'init\' method", () => {\n expect(typeof <Name>Logger.prototype.init).toBe("function");\n });\n\n test("\'init\' should return a Promise", () => {\n const logger = new <Name>Logger();\n const result = logger.init();\n expect(result).toBeInstanceOf(Promise);\n return result.catch(() => {});\n });\n\n // --- log ---\n\n test("should have \'log\' method", () => {\n expect(typeof <Name>Logger.prototype.log).toBe("function");\n });\n\n test("\'log\' should not throw when called with a message", () => {\n const logger = new <Name>Logger();\n expect(() => logger.log("test message")).not.toThrow();\n });\n\n test("\'log\' should not throw when called with a message and data", () => {\n const logger = new <Name>Logger();\n expect(() => logger.log("test message", { key: "value" })).not.toThrow();\n });\n\n // --- debug ---\n\n test("should have \'debug\' method", () => {\n expect(typeof <Name>Logger.prototype.debug).toBe("function");\n });\n\n test("\'debug\' should not throw when called with a message", () => {\n const logger = new <Name>Logger();\n expect(() => logger.debug("debug message")).not.toThrow();\n });\n\n // --- info ---\n\n test("should have \'info\' method", () => {\n expect(typeof <Name>Logger.prototype.info).toBe("function");\n });\n\n test("\'info\' should not throw when called with a message", () => {\n const logger = new <Name>Logger();\n expect(() => logger.info("info message")).not.toThrow();\n });\n\n // --- success ---\n\n test("should have \'success\' method", () => {\n expect(typeof <Name>Logger.prototype.success).toBe("function");\n });\n\n test("\'success\' should not throw when called with a message", () => {\n const logger = new <Name>Logger();\n expect(() => logger.success("success message")).not.toThrow();\n });\n\n // --- warn ---\n\n test("should have \'warn\' method", () => {\n expect(typeof <Name>Logger.prototype.warn).toBe("function");\n });\n\n test("\'warn\' should not throw when called with a message", () => {\n const logger = new <Name>Logger();\n expect(() => logger.warn("warn message")).not.toThrow();\n });\n\n // --- error ---\n\n test("should have \'error\' method", () => {\n expect(typeof <Name>Logger.prototype.error).toBe("function");\n });\n\n test("\'error\' should not throw when called with a string message", () => {\n const logger = new <Name>Logger();\n expect(() => logger.error("error message")).not.toThrow();\n });\n\n test("\'error\' should not throw when called with an Error-like object", () => {\n const logger = new <Name>Logger();\n const err = { message: "Something went wrong", stack: "" } as any;\n expect(() => logger.error(err)).not.toThrow();\n });\n\n test("\'error\' should not throw when called with a message and data", () => {\n const logger = new <Name>Logger();\n expect(() => logger.error("error message", { code: 500 })).not.toThrow();\n });\n\n // --- Instance isolation ---\n\n test("should produce independent instances", () => {\n const a = new <Name>Logger();\n const b = new <Name>Logger();\n expect(a).not.toBe(b);\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';
7628
7641
 
7629
7642
  // src/templates/claude/skills/make.mailer.md.txt
7630
- 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## Important\n\nAll commands must be run from the root of the project (not from within a package or module directory).\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';
7643
+ 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## Important\n\nAll commands must be run from the root of the project (not from within a package or module directory).\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 replace the scaffolded tests with comprehensive suites that cover all use cases.\n\n**`<Name>Mailer.spec.ts` coverage requirements:**\n\n- **Class identity**: name ends with `Mailer`, is a constructor function\n- **`send` contract**: method exists, returns a `Promise`\n- **Delegation**: `send` forwards the call to the underlying `IMailer` instance (use a mock)\n- **Content rendering**: the template is rendered and passed as `content` to the underlying mailer\n\n```typescript\nimport { describe, expect, mock, test } from "bun:test";\nimport { <Name>Mailer } from "@/mailers/<Name>Mailer";\n\ndescribe("<Name>Mailer", () => {\n // --- Class identity ---\n\n test("should have class name ending with \'Mailer\'", () => {\n expect(<Name>Mailer.name.endsWith("Mailer")).toBe(true);\n });\n\n test("should be a constructor function", () => {\n expect(typeof <Name>Mailer).toBe("function");\n });\n\n // --- send method ---\n\n test("should have \'send\' method", () => {\n expect(typeof <Name>Mailer.prototype.send).toBe("function");\n });\n\n test("\'send\' should return a Promise", () => {\n const mockMailer = { send: mock(() => Promise.resolve()) };\n const mailer = new <Name>Mailer(mockMailer as any);\n const result = mailer.send({ to: ["user@example.com"], subject: "Test" });\n expect(result).toBeInstanceOf(Promise);\n return result.catch(() => {});\n });\n\n test("\'send\' should delegate to the underlying mailer", async () => {\n const sendMock = mock(() => Promise.resolve());\n const mockMailer = { send: sendMock };\n const mailer = new <Name>Mailer(mockMailer as any);\n await mailer.send({ to: ["user@example.com"], subject: "Hello" });\n expect(sendMock).toHaveBeenCalledTimes(1);\n });\n\n test("\'send\' should forward \'to\' and \'subject\' to the underlying mailer", async () => {\n const sendMock = mock((_config: unknown) => Promise.resolve());\n const mockMailer = { send: sendMock };\n const mailer = new <Name>Mailer(mockMailer as any);\n await mailer.send({ to: ["a@b.com"], subject: "Subj" });\n const calledWith = sendMock.mock.calls[0]?.[0] as any;\n expect(calledWith.to).toEqual(["a@b.com"]);\n expect(calledWith.subject).toBe("Subj");\n });\n\n test("\'send\' should include rendered \'content\' in the delegated call", async () => {\n const sendMock = mock((_config: unknown) => Promise.resolve());\n const mockMailer = { send: sendMock };\n const mailer = new <Name>Mailer(mockMailer as any);\n await mailer.send({ to: ["a@b.com"], subject: "Subj", data: { link: "https://example.com" } });\n const calledWith = sendMock.mock.calls[0]?.[0] as any;\n expect(calledWith.content).toBeDefined();\n });\n\n // --- Instance isolation ---\n\n test("should produce independent instances", () => {\n const mockMailer = { send: mock(() => Promise.resolve()) };\n const a = new <Name>Mailer(mockMailer as any);\n const b = new <Name>Mailer(mockMailer as any);\n expect(a).not.toBe(b);\n });\n});\n```\n\n**`<Name>MailerTemplate.spec.ts` coverage requirements:**\n\n- **Function existence**: `<Name>MailerTemplate` is a function\n- **Renders with props**: calling with props returns JSX (non-null, object-like)\n- **Renders without props**: calling with no arguments does not throw\n- **Props reflected in output**: rendered output contains expected content from props\n\n```typescript\nimport { describe, expect, test } from "bun:test";\nimport { <Name>MailerTemplate } from "@/mailers/<Name>MailerTemplate";\n\ndescribe("<Name>MailerTemplate", () => {\n test("should be a function", () => {\n expect(typeof <Name>MailerTemplate).toBe("function");\n });\n\n test("should render without props without throwing", () => {\n expect(() => <Name>MailerTemplate()).not.toThrow();\n });\n\n test("should render with props without throwing", () => {\n expect(() => <Name>MailerTemplate({ link: "https://example.com" })).not.toThrow();\n });\n\n test("should return a non-null value", () => {\n const result = <Name>MailerTemplate({ link: "https://example.com" });\n expect(result).not.toBeNull();\n expect(result).toBeDefined();\n });\n\n // Add props-specific assertions after updating <Name>MailerPropsType:\n //\n // test("should include the link in the rendered output", () => {\n // const result = <Name>MailerTemplate({ link: "https://example.com" });\n // // Inspect JSX element props or rendered HTML string as appropriate\n // expect(JSON.stringify(result)).toContain("https://example.com");\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/mailers/<Name>Mailer.ts src/mailers/<Name>MailerTemplate.tsx tests/mailers/<Name>Mailer.spec.ts tests/mailers/<Name>MailerTemplate.spec.ts\n```\n';
7631
7644
 
7632
7645
  // src/templates/claude/skills/make.middleware.md.txt
7633
- 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## Important\n\nAll commands must be run from the root of the project (not from within a package or module directory).\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';
7646
+ 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## Important\n\nAll commands must be run from the root of the project (not from within a package or module directory).\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 replace the scaffolded tests with a comprehensive suite that covers all use cases.\n\n**Coverage requirements:**\n\n- **Class identity**: name ends with `Middleware`, is a constructor function\n- **`handler` contract**: method exists, returns a `Promise`, resolves to the same context object (pass-through contract)\n- **Context immutability**: `handler` must return the original context reference (not a copy)\n- **Instance isolation**: two instances are independent\n\nThe complete test file must follow this structure:\n\n```typescript\nimport { describe, expect, test } from "bun:test";\nimport { <Name>Middleware } from "@/middlewares/<Name>Middleware";\n\ndescribe("<Name>Middleware", () => {\n // --- Class identity ---\n\n test("should have class name ending with \'Middleware\'", () => {\n expect(<Name>Middleware.name.endsWith("Middleware")).toBe(true);\n });\n\n test("should be a constructor function", () => {\n expect(typeof <Name>Middleware).toBe("function");\n });\n\n // --- handler method ---\n\n test("should have \'handler\' method", () => {\n expect(typeof <Name>Middleware.prototype.handler).toBe("function");\n });\n\n test("\'handler\' should return a Promise", () => {\n const middleware = new <Name>Middleware();\n const context = {} as any;\n const result = middleware.handler(context);\n expect(result).toBeInstanceOf(Promise);\n return result.catch(() => {});\n });\n\n test("\'handler\' should resolve to the context object", async () => {\n const middleware = new <Name>Middleware();\n const context = { request: {}, response: { header: () => {} } } as any;\n const result = await middleware.handler(context);\n expect(result).toBe(context);\n });\n\n test("\'handler\' should not replace the context with a different object", async () => {\n const middleware = new <Name>Middleware();\n const context = { request: {}, response: { header: () => {} } } as any;\n const returned = await middleware.handler(context);\n expect(returned).toBe(context);\n });\n\n // --- Behavior tests ---\n // Add tests for any transformations, header injections, auth checks, or\n // redirects the middleware performs. Use a minimal mock context.\n //\n // Example for a middleware that injects a header:\n // test("should add \'X-Custom-Header\' to the response", async () => {\n // const headers: Record<string, string> = {};\n // const context = {\n // request: {},\n // response: { header: (k: string, v: string) => { headers[k] = v; } },\n // } as any;\n // await new <Name>Middleware().handler(context);\n // expect(headers["X-Custom-Header"]).toBeDefined();\n // });\n\n // --- Instance isolation ---\n\n test("should produce independent instances", () => {\n const a = new <Name>Middleware();\n const b = new <Name>Middleware();\n expect(a).not.toBe(b);\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';
7634
7647
 
7635
7648
  // src/templates/claude/skills/make.migration.md.txt
7636
- 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## Important\n\nAll commands must be run from the root of the project (not from within a package or module directory).\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#### Indexes\n\nAlways add indexes on fields that are frequently queried, filtered, sorted, or used in joins. Apply the following rules:\n\n- **Foreign keys**: always indexed (e.g. `user_id`, `order_id`)\n- **Lookup fields**: fields used in `WHERE` clauses (e.g. `email`, `slug`, `token`, `status`, `type`)\n- **Sort fields**: fields used in `ORDER BY` (e.g. `created_at`, `updated_at`, `position`)\n- **Unique constraints**: add a unique index for fields that must be unique (e.g. `email`, `slug`, `uuid`)\n- **Composite indexes**: when two fields are always queried together, prefer a single composite index over two separate ones\n\nDrop each index explicitly in the `down` method before dropping the table or column it covers.\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';
7649
+ 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## Important\n\nAll commands must be run from the root of the project (not from within a package or module directory).\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#### Indexes\n\nAlways add indexes on fields that are frequently queried, filtered, sorted, or used in joins. Apply the following rules:\n\n- **Foreign keys**: always indexed (e.g. `user_id`, `order_id`)\n- **Lookup fields**: fields used in `WHERE` clauses (e.g. `email`, `slug`, `token`, `status`, `type`)\n- **Sort fields**: fields used in `ORDER BY` (e.g. `created_at`, `updated_at`, `position`)\n- **Unique constraints**: add a unique index for fields that must be unique (e.g. `email`, `slug`, `uuid`)\n- **Composite indexes**: when two fields are always queried together, prefer a single composite index over two separate ones\n\nDrop each index explicitly in the `down` method before dropping the table or column it covers.\n\n### 4. Complete the test file\n\nEdit `tests/migrations/Migration<version>.spec.ts` to replace the scaffolded tests with a comprehensive suite that covers all use cases.\n\n**Coverage requirements:**\n\n- **Class identity**: name starts with `Migration`, is a constructor function\n- **`up` / `down` contract**: both exist, return Promises (runtime execution requires a DB connection \u2014 structural test only)\n- **`getVersion`**: exists, returns a string or number that is non-empty/positive and matches the version in the class name\n- **`getDependencies`**: exists, returns an array (empty if no dependency, or containing migration class references)\n- **Symmetry hint**: comment reminder that every `up` operation must have a corresponding `down`\n\nThe complete test file must follow this structure:\n\n```typescript\nimport { describe, expect, test } from "bun:test";\nimport { Migration<version> } from "@/migrations/Migration<version>";\n\ndescribe("Migration<version>", () => {\n // --- Class identity ---\n\n test("should have class name starting with \'Migration\'", () => {\n expect(Migration<version>.name.startsWith("Migration")).toBe(true);\n });\n\n test("should be a constructor function", () => {\n expect(typeof Migration<version>).toBe("function");\n });\n\n // --- up ---\n\n test("should have \'up\' method", () => {\n expect(typeof Migration<version>.prototype.up).toBe("function");\n });\n\n test("\'up\' should return a Promise when called with a mock runner", () => {\n const migration = new Migration<version>();\n const mockRunner = {\n query: async (_sql: string) => {},\n startTransaction: async () => {},\n commitTransaction: async () => {},\n rollbackTransaction: async () => {},\n } as any;\n const result = migration.up(mockRunner);\n expect(result).toBeInstanceOf(Promise);\n return result.catch(() => {});\n });\n\n // --- down ---\n\n test("should have \'down\' method", () => {\n expect(typeof Migration<version>.prototype.down).toBe("function");\n });\n\n test("\'down\' should return a Promise when called with a mock runner", () => {\n const migration = new Migration<version>();\n const mockRunner = {\n query: async (_sql: string) => {},\n startTransaction: async () => {},\n commitTransaction: async () => {},\n rollbackTransaction: async () => {},\n } as any;\n const result = migration.down(mockRunner);\n expect(result).toBeInstanceOf(Promise);\n return result.catch(() => {});\n });\n\n // --- getVersion ---\n\n test("should have \'getVersion\' method", () => {\n expect(typeof Migration<version>.prototype.getVersion).toBe("function");\n });\n\n test("\'getVersion\' should return a non-empty value", () => {\n const migration = new Migration<version>();\n const version = migration.getVersion();\n expect(version !== null && version !== undefined && String(version).length > 0).toBe(true);\n });\n\n test("\'getVersion\' result should match the version suffix in the class name", () => {\n const migration = new Migration<version>();\n const version = String(migration.getVersion());\n expect(Migration<version>.name).toContain(version);\n });\n\n // --- getDependencies ---\n\n test("should have \'getDependencies\' method", () => {\n expect(typeof Migration<version>.prototype.getDependencies).toBe("function");\n });\n\n test("\'getDependencies\' should return an array", () => {\n const migration = new Migration<version>();\n expect(Array.isArray(migration.getDependencies())).toBe(true);\n });\n\n // --- Instance isolation ---\n\n test("should produce independent instances", () => {\n const a = new Migration<version>();\n const b = new Migration<version>();\n expect(a).not.toBe(b);\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';
7637
7650
 
7638
7651
  // src/templates/claude/skills/make.permission.md.txt
7639
- 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## Important\n\nAll commands must be run from the root of the project (not from within a package or module directory).\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 type { ContextType } from "@ooneex/controller";\nimport { decorator, Permission } from "@ooneex/permission";\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(context: ContextType): this {\n // Example: Grant full access to admins\n // const { user } = context;\n //\n // if (!user) {\n // return this;\n // }\n //\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 // const { user } = context;\n //\n // if (user) {\n // for (const role of user.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\n return this;\n }\n\n public check(context: ContextType): boolean {\n return true;\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';
7652
+ 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## Important\n\nAll commands must be run from the root of the project (not from within a package or module directory).\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 type { ContextType } from "@ooneex/controller";\nimport { decorator, Permission } from "@ooneex/permission";\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(context: ContextType): this {\n // Example: Grant full access to admins\n // const { user } = context;\n //\n // if (!user) {\n // return this;\n // }\n //\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 // const { user } = context;\n //\n // if (user) {\n // for (const role of user.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\n return this;\n }\n\n public check(context: ContextType): boolean {\n return true;\n }\n}\n```\n\n### 4. Complete the test file\n\nEdit `tests/permissions/<Name>Permission.spec.ts` to replace the scaffolded tests with a comprehensive suite that covers all use cases.\n\n**Coverage requirements:**\n\n- **Class identity**: name ends with `Permission`, is a constructor function\n- **Inheritance**: instance is a `Permission` instance\n- **`allow` contract**: exists, returns `this` (fluent interface)\n- **`setUserPermissions` contract**: exists, accepts a context, returns `this`\n- **`check` contract**: exists, returns a `boolean`\n- **Rule tests**: after implementing, add one test per role/action combination in `allow()` and `setUserPermissions()`\n- **Instance isolation**: two instances are independent\n\nThe complete test file must follow this structure:\n\n```typescript\nimport { Permission } from "@ooneex/permission";\nimport { describe, expect, test } from "bun:test";\nimport { <Name>Permission } from "@/permissions/<Name>Permission";\n\ndescribe("<Name>Permission", () => {\n // --- Class identity ---\n\n test("should have class name ending with \'Permission\'", () => {\n expect(<Name>Permission.name.endsWith("Permission")).toBe(true);\n });\n\n test("should be a constructor function", () => {\n expect(typeof <Name>Permission).toBe("function");\n });\n\n test("should extend Permission", () => {\n const permission = new <Name>Permission();\n expect(permission).toBeInstanceOf(Permission);\n });\n\n // --- allow ---\n\n test("should have \'allow\' method", () => {\n expect(typeof <Name>Permission.prototype.allow).toBe("function");\n });\n\n test("\'allow\' should return \'this\' for fluent chaining", () => {\n const permission = new <Name>Permission();\n expect(permission.allow()).toBe(permission);\n });\n\n test("\'allow\' should not throw", () => {\n const permission = new <Name>Permission();\n expect(() => permission.allow()).not.toThrow();\n });\n\n // --- setUserPermissions ---\n\n test("should have \'setUserPermissions\' method", () => {\n expect(typeof <Name>Permission.prototype.setUserPermissions).toBe("function");\n });\n\n test("\'setUserPermissions\' should return \'this\' for fluent chaining", () => {\n const permission = new <Name>Permission();\n const context = {} as any;\n expect(permission.setUserPermissions(context)).toBe(permission);\n });\n\n test("\'setUserPermissions\' should not throw when user is absent from context", () => {\n const permission = new <Name>Permission();\n expect(() => permission.setUserPermissions({ user: undefined } as any)).not.toThrow();\n });\n\n // --- check ---\n\n test("should have \'check\' method", () => {\n expect(typeof <Name>Permission.prototype.check).toBe("function");\n });\n\n test("\'check\' should return a boolean", () => {\n const permission = new <Name>Permission();\n const result = permission.check({} as any);\n expect(typeof result).toBe("boolean");\n });\n\n // --- Rule tests (add after implementing allow/setUserPermissions) ---\n // Add one test per role/action combination.\n //\n // Example for ADMIN having full access:\n // test("admin should be allowed to manage all resources", () => {\n // const permission = new <Name>Permission();\n // permission.setUserPermissions({ user: { roles: ["admin"] } } as any);\n // expect(permission.ability.can("manage", "all")).toBe(true);\n // });\n //\n // Example for GUEST having read-only access:\n // test("guest should only be allowed to read public resources", () => {\n // const permission = new <Name>Permission();\n // permission.setUserPermissions({ user: { roles: ["guest"] } } as any);\n // expect(permission.ability.can("read", "YourEntity")).toBe(true);\n // expect(permission.ability.can("create", "YourEntity")).toBe(false);\n // });\n\n // --- Instance isolation ---\n\n test("should produce independent instances", () => {\n const a = new <Name>Permission();\n const b = new <Name>Permission();\n expect(a).not.toBe(b);\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';
7640
7653
 
7641
7654
  // src/templates/claude/skills/make.pubsub.md.txt
7642
- 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## Important\n\nAll commands must be run from the root of the project (not from within a package or module directory).\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';
7655
+ 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## Important\n\nAll commands must be run from the root of the project (not from within a package or module directory).\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 replace the scaffolded tests with a comprehensive suite that covers all use cases.\n\n**Coverage requirements:**\n\n- **Class identity**: name ends with `Event`, is a constructor function\n- **`getChannel`**: exists, returns a non-empty string, and consistently returns the same value on repeated calls\n- **`handler` contract**: exists, returns a `Promise`\n- **Inherited methods** (`publish`, `subscribe`, `unsubscribe`, `unsubscribeAll`): all exist on prototype\n- **Handler behavior**: use a mock to verify the handler calls the injected service\'s `execute` method with the event data\n\nThe complete test file must follow this structure:\n\n```typescript\nimport { describe, expect, mock, test } from "bun:test";\nimport { <Name>Event } from "@/events/<Name>Event";\n\ndescribe("<Name>Event", () => {\n // --- Class identity ---\n\n test("should have class name ending with \'Event\'", () => {\n expect(<Name>Event.name.endsWith("Event")).toBe(true);\n });\n\n test("should be a constructor function", () => {\n expect(typeof <Name>Event).toBe("function");\n });\n\n // --- getChannel ---\n\n test("should have \'getChannel\' method", () => {\n expect(typeof <Name>Event.prototype.getChannel).toBe("function");\n });\n\n test("\'getChannel\' should return a non-empty string", () => {\n const event = Object.create(<Name>Event.prototype) as <Name>Event;\n const channel = event.getChannel();\n expect(typeof channel).toBe("string");\n expect(channel.length).toBeGreaterThan(0);\n });\n\n test("\'getChannel\' should return the same value on repeated calls", () => {\n const event = Object.create(<Name>Event.prototype) as <Name>Event;\n expect(event.getChannel()).toBe(event.getChannel());\n });\n\n // --- handler ---\n\n test("should have \'handler\' method", () => {\n expect(typeof <Name>Event.prototype.handler).toBe("function");\n });\n\n test("\'handler\' should return a Promise", () => {\n const mockClient = {\n subscribe: mock(() => Promise.resolve()),\n publish: mock(() => Promise.resolve()),\n unsubscribe: mock(() => Promise.resolve()),\n unsubscribeAll: mock(() => Promise.resolve()),\n };\n const event = new <Name>Event(mockClient as any);\n const result = event.handler({ data: {} as any, channel: event.getChannel() });\n expect(result).toBeInstanceOf(Promise);\n return result.catch(() => {});\n });\n\n // --- publish / subscribe / unsubscribe / unsubscribeAll ---\n\n test("should have \'publish\' method", () => {\n expect(typeof <Name>Event.prototype.publish).toBe("function");\n });\n\n test("should have \'subscribe\' method", () => {\n expect(typeof <Name>Event.prototype.subscribe).toBe("function");\n });\n\n test("should have \'unsubscribe\' method", () => {\n expect(typeof <Name>Event.prototype.unsubscribe).toBe("function");\n });\n\n test("should have \'unsubscribeAll\' method", () => {\n expect(typeof <Name>Event.prototype.unsubscribeAll).toBe("function");\n });\n\n // --- handler behavior (add after implementing handler) ---\n //\n // test("\'handler\' should call the service\'s execute method with event data", async () => {\n // const executeMock = mock(() => Promise.resolve());\n // const mockService = { execute: executeMock };\n // const mockClient = { subscribe: mock(() => Promise.resolve()), publish: mock(() => Promise.resolve()) };\n // const event = new <Name>Event(mockClient as any, mockService as any);\n // const data = { userId: "u1" };\n // await event.handler({ data, channel: event.getChannel() });\n // expect(executeMock).toHaveBeenCalledWith(data);\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';
7643
7656
 
7644
7657
  // src/templates/claude/skills/make.repository.md.txt
7645
- 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## Important\n\nAll commands must be run from the root of the project (not from within a package or module directory).\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';
7658
+ 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## Important\n\nAll commands must be run from the root of the project (not from within a package or module directory).\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 replace the scaffolded tests with a comprehensive suite that covers all use cases.\n\n**Coverage requirements:**\n\n- **Class identity**: name ends with `Repository`, is a constructor function\n- **Each retained CRUD method**: method exists on prototype (remove tests for methods that were removed in step 3)\n- **Each custom domain method**: existence test for every method added\n- **No live-database tests**: repository methods require a real connection \u2014 structural tests only; integration tests belong in a separate suite\n\nThe complete test file must follow this structure \u2014 remove test blocks for any methods removed from the repository, and add new blocks for each custom method:\n\n```typescript\nimport { describe, expect, test } from "bun:test";\nimport { <Name>Repository } from "@/repositories/<Name>Repository";\n\ndescribe("<Name>Repository", () => {\n // --- Class identity ---\n\n test("should have class name ending with \'Repository\'", () => {\n expect(<Name>Repository.name.endsWith("Repository")).toBe(true);\n });\n\n test("should be a constructor function", () => {\n expect(typeof <Name>Repository).toBe("function");\n });\n\n // --- Standard CRUD methods ---\n // Keep only the blocks for methods that were NOT removed in step 3.\n\n test("should have \'open\' method", () => {\n expect(typeof <Name>Repository.prototype.open).toBe("function");\n });\n\n test("should have \'close\' method", () => {\n expect(typeof <Name>Repository.prototype.close).toBe("function");\n });\n\n test("should have \'find\' method", () => {\n expect(typeof <Name>Repository.prototype.find).toBe("function");\n });\n\n test("should have \'findOne\' method", () => {\n expect(typeof <Name>Repository.prototype.findOne).toBe("function");\n });\n\n test("should have \'findOneBy\' method", () => {\n expect(typeof <Name>Repository.prototype.findOneBy).toBe("function");\n });\n\n test("should have \'create\' method", () => {\n expect(typeof <Name>Repository.prototype.create).toBe("function");\n });\n\n test("should have \'createMany\' method", () => {\n expect(typeof <Name>Repository.prototype.createMany).toBe("function");\n });\n\n test("should have \'update\' method", () => {\n expect(typeof <Name>Repository.prototype.update).toBe("function");\n });\n\n test("should have \'updateMany\' method", () => {\n expect(typeof <Name>Repository.prototype.updateMany).toBe("function");\n });\n\n test("should have \'delete\' method", () => {\n expect(typeof <Name>Repository.prototype.delete).toBe("function");\n });\n\n test("should have \'count\' method", () => {\n expect(typeof <Name>Repository.prototype.count).toBe("function");\n });\n\n // --- Custom domain methods ---\n // Add one block per custom method added in step 3.\n // Example:\n // test("should have \'revokeSession\' method", () => {\n // expect(typeof <Name>Repository.prototype.revokeSession).toBe("function");\n // });\n //\n // test("should have \'markAllAsRead\' method", () => {\n // expect(typeof <Name>Repository.prototype.markAllAsRead).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/repositories/<Name>Repository.ts tests/repositories/<Name>Repository.spec.ts\n```\n';
7646
7659
 
7647
7660
  // src/templates/claude/skills/make.seed.md.txt
7648
- 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## Important\n\nAll commands must be run from the root of the project (not from within a package or module directory).\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';
7661
+ 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## Important\n\nAll commands must be run from the root of the project (not from within a package or module directory).\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 replace the scaffolded tests with a comprehensive suite that covers all use cases.\n\n**Coverage requirements:**\n\n- **Class identity**: name ends with `Seed`, is a constructor function\n- **`run` contract**: method exists on prototype (requires DB \u2014 no runtime call)\n- **`isActive`**: exists, returns `true` by default (seed is active unless explicitly disabled)\n- **`getDependencies`**: exists, returns an array (empty or with seed class references)\n- **Data YAML file**: file exists, is not empty, parses as valid YAML with at least one entry\n- **Instance isolation**: two instances are independent\n\nThe complete test file must follow this structure:\n\n```typescript\nimport { existsSync, readFileSync } 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 // --- Class identity ---\n\n test("should have class name ending with \'Seed\'", () => {\n expect(<Name>Seed.name.endsWith("Seed")).toBe(true);\n });\n\n test("should be a constructor function", () => {\n expect(typeof <Name>Seed).toBe("function");\n });\n\n // --- run ---\n\n test("should have \'run\' method", () => {\n expect(typeof <Name>Seed.prototype.run).toBe("function");\n });\n\n // --- isActive ---\n\n test("should have \'isActive\' method", () => {\n expect(typeof <Name>Seed.prototype.isActive).toBe("function");\n });\n\n test("\'isActive\' should return a boolean", () => {\n const seed = Object.create(<Name>Seed.prototype) as <Name>Seed;\n expect(typeof seed.isActive()).toBe("boolean");\n });\n\n test("\'isActive\' should return true by default", () => {\n const seed = Object.create(<Name>Seed.prototype) as <Name>Seed;\n expect(seed.isActive()).toBe(true);\n });\n\n // --- getDependencies ---\n\n test("should have \'getDependencies\' method", () => {\n expect(typeof <Name>Seed.prototype.getDependencies).toBe("function");\n });\n\n test("\'getDependencies\' should return an array", () => {\n const seed = Object.create(<Name>Seed.prototype) as <Name>Seed;\n expect(Array.isArray(seed.getDependencies())).toBe(true);\n });\n\n // --- Data YAML file ---\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 test("data yml file should not be empty", () => {\n const dataFile = join(__dirname, "..", "src", "seeds", "<name-seed>.yml");\n const content = readFileSync(dataFile, "utf-8");\n expect(content.trim().length).toBeGreaterThan(0);\n });\n\n test("data yml file should contain at least one entry", () => {\n const dataFile = join(__dirname, "..", "src", "seeds", "<name-seed>.yml");\n const content = readFileSync(dataFile, "utf-8");\n // Each entry starts with \'- \' in YAML list format\n expect(content).toMatch(/^-\\s/m);\n });\n\n // --- Instance isolation ---\n\n test("should produce independent instances", () => {\n const a = Object.create(<Name>Seed.prototype) as <Name>Seed;\n const b = Object.create(<Name>Seed.prototype) as <Name>Seed;\n expect(a).not.toBe(b);\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';
7649
7662
 
7650
7663
  // src/templates/claude/skills/make.service.md.txt
7651
- 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## Important\n\nAll commands must be run from the root of the project (not from within a package or module directory).\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';
7664
+ 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## Important\n\nAll commands must be run from the root of the project (not from within a package or module directory).\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 replace the scaffolded tests with a comprehensive suite that covers all use cases.\n\n**Coverage requirements:**\n\n- **Class identity**: name ends with `Service`, is a constructor function\n- **`execute` contract**: method exists, returns a `Promise` when called with no args and with data\n- **Awaitable**: calling `await execute()` does not hang or reject unexpectedly\n- **Instance isolation**: two instances are independent objects\n\nThe complete test file must follow this structure:\n\n```typescript\nimport { describe, expect, test } from "bun:test";\nimport { <Name>Service } from "@/services/<Name>Service";\n\ndescribe("<Name>Service", () => {\n // --- Class identity ---\n\n test("should have class name ending with \'Service\'", () => {\n expect(<Name>Service.name.endsWith("Service")).toBe(true);\n });\n\n test("should be a constructor function", () => {\n expect(typeof <Name>Service).toBe("function");\n });\n\n // --- execute method ---\n\n test("should have \'execute\' method", () => {\n expect(typeof <Name>Service.prototype.execute).toBe("function");\n });\n\n test("\'execute\' should return a Promise when called with no arguments", () => {\n const service = new <Name>Service();\n const result = service.execute();\n expect(result).toBeInstanceOf(Promise);\n return result.catch(() => {});\n });\n\n test("\'execute\' should return a Promise when called with data", () => {\n const service = new <Name>Service();\n const result = service.execute({ key: "value" });\n expect(result).toBeInstanceOf(Promise);\n return result.catch(() => {});\n });\n\n test("\'execute\' result should be awaitable", async () => {\n const service = new <Name>Service();\n try {\n await service.execute();\n } catch {\n // Expected when dependencies are not injected \u2014 still validates the Promise contract\n }\n });\n\n // --- Business logic tests ---\n // Add one test per meaningful behavior of the service after implementing execute().\n // Focus on: happy path output, invalid input handling, edge cases.\n //\n // Example:\n // test("should return formatted result for valid input", async () => {\n // const service = new <Name>Service();\n // const result = await service.execute({ name: "Alice" });\n // expect(result).toMatchObject({ greeting: "Hello, Alice" });\n // });\n\n // --- Instance isolation ---\n\n test("should produce independent instances", () => {\n const a = new <Name>Service();\n const b = new <Name>Service();\n expect(a).not.toBe(b);\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';
7652
7665
 
7653
7666
  // src/templates/claude/skills/make.storage.md.txt
7654
- 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## Important\n\nAll commands must be run from the root of the project (not from within a package or module directory).\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';
7667
+ 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## Important\n\nAll commands must be run from the root of the project (not from within a package or module directory).\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 replace the scaffolded tests with a comprehensive suite that covers all use cases.\n\n**Coverage requirements:**\n\n- **Class identity**: name ends with `Storage`, is a constructor function\n- **Constructor \u2014 missing credentials**: throws `StorageException` when required env vars are absent\n- **Constructor \u2014 explicit credentials**: succeeds when all credentials are passed via options\n- **`getOptions` shape**: returns an object with `accessKeyId`, `secretAccessKey`, `endpoint`, `bucket`, and `region` keys\n- **`bucket`**: the returned options include the expected bucket name\n- **Default region**: when no region is provided, defaults to `"auto"`\n- **Instance isolation**: two instances are independent\n\nThe complete test file must follow this structure:\n\n```typescript\nimport { StorageException } from "@ooneex/storage";\nimport { describe, expect, test } from "bun:test";\nimport { <Name>Storage } from "@/storage/<Name>Storage";\n\nconst VALID_OPTIONS = {\n accessKey: "test-access-key",\n secretKey: "test-secret-key",\n endpoint: "https://s3.example.com",\n region: "eu-west-1",\n};\n\ndescribe("<Name>Storage", () => {\n // --- Class identity ---\n\n test("should have class name ending with \'Storage\'", () => {\n expect(<Name>Storage.name.endsWith("Storage")).toBe(true);\n });\n\n test("should be a constructor function", () => {\n expect(typeof <Name>Storage).toBe("function");\n });\n\n // --- Constructor validation ---\n\n test("should throw StorageException when accessKey is missing", () => {\n expect(() => new <Name>Storage({ secretKey: "s", endpoint: "https://s3.example.com" })).toThrow(StorageException);\n });\n\n test("should throw StorageException when secretKey is missing", () => {\n expect(() => new <Name>Storage({ accessKey: "a", endpoint: "https://s3.example.com" })).toThrow(StorageException);\n });\n\n test("should throw StorageException when endpoint is missing", () => {\n expect(() => new <Name>Storage({ accessKey: "a", secretKey: "s" })).toThrow(StorageException);\n });\n\n test("should construct successfully when all required credentials are provided", () => {\n expect(() => new <Name>Storage(VALID_OPTIONS)).not.toThrow();\n });\n\n // --- getOptions ---\n\n test("should have \'getOptions\' method", () => {\n expect(typeof <Name>Storage.prototype.getOptions).toBe("function");\n });\n\n test("\'getOptions\' should return an object with all required S3 keys", () => {\n const storage = new <Name>Storage(VALID_OPTIONS);\n const opts = storage.getOptions();\n expect(opts).toHaveProperty("accessKeyId");\n expect(opts).toHaveProperty("secretAccessKey");\n expect(opts).toHaveProperty("endpoint");\n expect(opts).toHaveProperty("bucket");\n expect(opts).toHaveProperty("region");\n });\n\n test("\'getOptions\' should reflect the provided credentials", () => {\n const storage = new <Name>Storage(VALID_OPTIONS);\n const opts = storage.getOptions();\n expect(opts.accessKeyId).toBe(VALID_OPTIONS.accessKey);\n expect(opts.secretAccessKey).toBe(VALID_OPTIONS.secretKey);\n expect(opts.endpoint).toBe(VALID_OPTIONS.endpoint);\n expect(opts.region).toBe(VALID_OPTIONS.region);\n });\n\n test("\'getOptions\' should default region to \'auto\' when not provided", () => {\n const storage = new <Name>Storage({ accessKey: "a", secretKey: "s", endpoint: "https://s3.example.com" });\n expect(storage.getOptions().region).toBe("auto");\n });\n\n test("\'getOptions\' should include the bucket name", () => {\n const storage = new <Name>Storage(VALID_OPTIONS);\n const opts = storage.getOptions();\n expect(typeof opts.bucket).toBe("string");\n expect((opts.bucket as string).length).toBeGreaterThan(0);\n });\n\n // --- Instance isolation ---\n\n test("should produce independent instances", () => {\n const a = new <Name>Storage(VALID_OPTIONS);\n const b = new <Name>Storage(VALID_OPTIONS);\n expect(a).not.toBe(b);\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';
7655
7668
 
7656
7669
  // src/templates/claude/skills/make.vector-database.md.txt
7657
- 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## Important\n\nAll commands must be run from the root of the project (not from within a package or module directory).\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';
7670
+ 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## Important\n\nAll commands must be run from the root of the project (not from within a package or module directory).\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 replace the scaffolded tests with a comprehensive suite that covers all use cases.\n\n**Coverage requirements:**\n\n- **Class identity**: name ends with `VectorDatabase`, is a constructor function\n- **`getDatabaseUri`**: exists, returns a non-empty string (the LanceDB path)\n- **`getEmbeddingModel`**: exists, returns an object with `provider` (string) and `model` (string) keys that are non-empty\n- **`getSchema`**: exists, returns a non-empty object whose keys match the `DataType` fields\n- **Instance isolation**: two instances are independent\n\nThe complete test file must follow this structure:\n\n```typescript\nimport { describe, expect, test } from "bun:test";\nimport { <Name>VectorDatabase } from "@/databases/<Name>VectorDatabase";\n\ndescribe("<Name>VectorDatabase", () => {\n // --- Class identity ---\n\n test("should have class name ending with \'VectorDatabase\'", () => {\n expect(<Name>VectorDatabase.name.endsWith("VectorDatabase")).toBe(true);\n });\n\n test("should be a constructor function", () => {\n expect(typeof <Name>VectorDatabase).toBe("function");\n });\n\n // --- getDatabaseUri ---\n\n test("should have \'getDatabaseUri\' method", () => {\n expect(typeof <Name>VectorDatabase.prototype.getDatabaseUri).toBe("function");\n });\n\n test("\'getDatabaseUri\' should return a string", () => {\n const db = new <Name>VectorDatabase();\n expect(typeof db.getDatabaseUri()).toBe("string");\n });\n\n test("\'getDatabaseUri\' should return a non-empty path after configuration", () => {\n const db = new <Name>VectorDatabase();\n const uri = db.getDatabaseUri();\n // A valid URI is non-empty once configured \u2014 check it\'s a string at minimum\n expect(typeof uri).toBe("string");\n });\n\n // --- getEmbeddingModel ---\n\n test("should have \'getEmbeddingModel\' method", () => {\n expect(typeof <Name>VectorDatabase.prototype.getEmbeddingModel).toBe("function");\n });\n\n test("\'getEmbeddingModel\' should return an object with \'provider\' and \'model\' keys", () => {\n const db = new <Name>VectorDatabase();\n const embedding = db.getEmbeddingModel();\n expect(embedding).toBeDefined();\n expect(typeof embedding.provider).toBe("string");\n expect(embedding.provider.length).toBeGreaterThan(0);\n expect(typeof embedding.model).toBe("string");\n expect(embedding.model.length).toBeGreaterThan(0);\n });\n\n test("\'getEmbeddingModel\' provider should be a recognized value", () => {\n const db = new <Name>VectorDatabase();\n const { provider } = db.getEmbeddingModel();\n expect(["openai", "cohere", "huggingface", "ollama"]).toContain(provider);\n });\n\n // --- getSchema ---\n\n test("should have \'getSchema\' method", () => {\n expect(typeof <Name>VectorDatabase.prototype.getSchema).toBe("function");\n });\n\n test("\'getSchema\' should return a non-empty object", () => {\n const db = new <Name>VectorDatabase();\n const schema = db.getSchema();\n expect(schema).toBeDefined();\n expect(typeof schema).toBe("object");\n expect(Object.keys(schema).length).toBeGreaterThan(0);\n });\n\n test("\'getSchema\' keys should match the DataType fields", () => {\n const db = new <Name>VectorDatabase();\n const schema = db.getSchema();\n // Update this list to match the actual DataType fields defined in the class\n const expectedFields = ["name"]; // e.g. ["title", "content", "source"]\n for (const field of expectedFields) {\n expect(Object.keys(schema)).toContain(field);\n }\n });\n\n // --- Instance isolation ---\n\n test("should produce independent instances", () => {\n const a = new <Name>VectorDatabase();\n const b = new <Name>VectorDatabase();\n expect(a).not.toBe(b);\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';
7658
7671
 
7659
7672
  // src/templates/claude/skills/optimize.md.txt
7660
7673
  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## Important\n\nAll commands must be run from the root of the project (not from within a package or module directory).\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";
@@ -9740,10 +9753,109 @@ class MakeMigrationCommand {
9740
9753
  MakeMigrationCommand = __legacyDecorateClassTS([
9741
9754
  decorator22.command()
9742
9755
  ], MakeMigrationCommand);
9743
- // src/commands/MakePermissionCommand.ts
9756
+ // src/commands/ModuleLockCommand.ts
9744
9757
  import { join as join23 } from "path";
9745
9758
  import { decorator as decorator23 } from "@ooneex/command";
9746
9759
  import { TerminalLogger as TerminalLogger22 } from "@ooneex/logger";
9760
+ import { migrationTestCreate } from "@ooneex/migrations";
9761
+ class ModuleLockCommand {
9762
+ getName() {
9763
+ return "module:lock";
9764
+ }
9765
+ getDescription() {
9766
+ return "Lock module migrations by hashing their content into a yml file";
9767
+ }
9768
+ async run(options) {
9769
+ const { module, override } = options;
9770
+ const logger = new TerminalLogger22;
9771
+ if (module) {
9772
+ await ensureModule(module);
9773
+ }
9774
+ const base = module ? join23("modules", module) : ".";
9775
+ const migrationsDir = join23(process.cwd(), base, "src", "migrations");
9776
+ const glob = new Bun.Glob("Migration*.ts");
9777
+ const migrationFiles = [];
9778
+ for await (const file of glob.scan({ cwd: migrationsDir, onlyFiles: true })) {
9779
+ migrationFiles.push(file);
9780
+ }
9781
+ if (migrationFiles.length === 0) {
9782
+ logger.info("No migration files found", undefined, {
9783
+ showTimestamp: false,
9784
+ showArrow: false
9785
+ });
9786
+ return;
9787
+ }
9788
+ const moduleName = module ?? await this.resolveModuleName(base);
9789
+ const ymlPath = join23(process.cwd(), base, `${moduleName}.yml`);
9790
+ const testsDir = join23(base, "tests", "migrations");
9791
+ const registered = await this.parseYml(ymlPath);
9792
+ let added = 0;
9793
+ let skipped = 0;
9794
+ for (const file of migrationFiles.sort()) {
9795
+ const name = file.replace(/\.ts$/, "");
9796
+ await migrationTestCreate({ name, testsDir, ...module && { module } });
9797
+ if (registered[name] && !override) {
9798
+ skipped++;
9799
+ continue;
9800
+ }
9801
+ const content = await Bun.file(join23(migrationsDir, file)).text();
9802
+ registered[name] = new Bun.CryptoHasher("sha256").update(content).digest("hex");
9803
+ added++;
9804
+ }
9805
+ await this.writeYml(ymlPath, registered);
9806
+ if (added > 0) {
9807
+ logger.success(`${ymlPath} updated (${added} migration(s) locked)`, undefined, {
9808
+ showTimestamp: false,
9809
+ showArrow: false,
9810
+ useSymbol: true
9811
+ });
9812
+ }
9813
+ if (skipped > 0) {
9814
+ logger.info(`${skipped} migration(s) already registered \u2014 use --override to update`, undefined, {
9815
+ showTimestamp: false,
9816
+ showArrow: false
9817
+ });
9818
+ }
9819
+ }
9820
+ async resolveModuleName(base) {
9821
+ const pkgFile = Bun.file(join23(process.cwd(), base, "package.json"));
9822
+ if (await pkgFile.exists()) {
9823
+ const pkg = await pkgFile.json();
9824
+ const name = pkg.name ?? "module";
9825
+ return name.split("/").pop() ?? name;
9826
+ }
9827
+ return "module";
9828
+ }
9829
+ async parseYml(ymlPath) {
9830
+ const file = Bun.file(ymlPath);
9831
+ if (!await file.exists())
9832
+ return {};
9833
+ const data = Bun.YAML.parse(await file.text());
9834
+ const result = {};
9835
+ for (const [name, entry] of Object.entries(data.migrations ?? {})) {
9836
+ if (entry?.hash)
9837
+ result[name] = entry.hash;
9838
+ }
9839
+ return result;
9840
+ }
9841
+ async writeYml(ymlPath, migrations) {
9842
+ const lines = ["migrations:"];
9843
+ for (const [name, hash] of Object.entries(migrations).sort(([a], [b]) => a.localeCompare(b))) {
9844
+ lines.push(` ${name}:`);
9845
+ lines.push(` hash: ${hash}`);
9846
+ }
9847
+ await Bun.write(ymlPath, `${lines.join(`
9848
+ `)}
9849
+ `);
9850
+ }
9851
+ }
9852
+ ModuleLockCommand = __legacyDecorateClassTS([
9853
+ decorator23.command()
9854
+ ], ModuleLockCommand);
9855
+ // src/commands/MakePermissionCommand.ts
9856
+ import { join as join24 } from "path";
9857
+ import { decorator as decorator24 } from "@ooneex/command";
9858
+ import { TerminalLogger as TerminalLogger23 } from "@ooneex/logger";
9747
9859
  import { toPascalCase as toPascalCase12 } from "@ooneex/utils";
9748
9860
 
9749
9861
  // src/templates/permission.test.txt
@@ -9881,28 +9993,28 @@ class MakePermissionCommand {
9881
9993
  if (module) {
9882
9994
  await ensureModule(module);
9883
9995
  }
9884
- const base = module ? join23("modules", module) : ".";
9885
- const permissionLocalDir = join23(base, "src", "permissions");
9886
- const permissionDir = join23(process.cwd(), permissionLocalDir);
9887
- const filePath = join23(permissionDir, `${name}Permission.ts`);
9996
+ const base = module ? join24("modules", module) : ".";
9997
+ const permissionLocalDir = join24(base, "src", "permissions");
9998
+ const permissionDir = join24(process.cwd(), permissionLocalDir);
9999
+ const filePath = join24(permissionDir, `${name}Permission.ts`);
9888
10000
  await Bun.write(filePath, content);
9889
10001
  const testContent = permission_test_default.replace(/{{NAME}}/g, name).replace(/{{MODULE}}/g, module ?? "");
9890
- const testsLocalDir = join23(base, "tests", "permissions");
9891
- const testsDir = join23(process.cwd(), testsLocalDir);
9892
- const testFilePath = join23(testsDir, `${name}Permission.spec.ts`);
10002
+ const testsLocalDir = join24(base, "tests", "permissions");
10003
+ const testsDir = join24(process.cwd(), testsLocalDir);
10004
+ const testFilePath = join24(testsDir, `${name}Permission.spec.ts`);
9893
10005
  await Bun.write(testFilePath, testContent);
9894
- const logger = new TerminalLogger22;
9895
- logger.success(`${join23(permissionLocalDir, name)}Permission.ts created successfully`, undefined, {
10006
+ const logger = new TerminalLogger23;
10007
+ logger.success(`${join24(permissionLocalDir, name)}Permission.ts created successfully`, undefined, {
9896
10008
  showTimestamp: false,
9897
10009
  showArrow: false,
9898
10010
  useSymbol: true
9899
10011
  });
9900
- logger.success(`${join23(testsLocalDir, name)}Permission.spec.ts created successfully`, undefined, {
10012
+ logger.success(`${join24(testsLocalDir, name)}Permission.spec.ts created successfully`, undefined, {
9901
10013
  showTimestamp: false,
9902
10014
  showArrow: false,
9903
10015
  useSymbol: true
9904
10016
  });
9905
- const packageJsonPath = join23(process.cwd(), "package.json");
10017
+ const packageJsonPath = join24(process.cwd(), "package.json");
9906
10018
  const packageJson = await Bun.file(packageJsonPath).json();
9907
10019
  const deps = packageJson.dependencies ?? {};
9908
10020
  const devDeps = packageJson.devDependencies ?? {};
@@ -9917,12 +10029,12 @@ class MakePermissionCommand {
9917
10029
  }
9918
10030
  }
9919
10031
  MakePermissionCommand = __legacyDecorateClassTS([
9920
- decorator23.command()
10032
+ decorator24.command()
9921
10033
  ], MakePermissionCommand);
9922
10034
  // src/commands/MakePubSubCommand.ts
9923
- import { basename as basename5, join as join24 } from "path";
9924
- import { decorator as decorator24 } from "@ooneex/command";
9925
- import { TerminalLogger as TerminalLogger23 } from "@ooneex/logger";
10035
+ import { basename as basename5, join as join25 } from "path";
10036
+ import { decorator as decorator25 } from "@ooneex/command";
10037
+ import { TerminalLogger as TerminalLogger24 } from "@ooneex/logger";
9926
10038
  import { toKebabCase as toKebabCase4, toPascalCase as toPascalCase13 } from "@ooneex/utils";
9927
10039
 
9928
10040
  // src/templates/pubsub.test.txt
@@ -10030,33 +10142,33 @@ class MakePubSubCommand {
10030
10142
  if (module) {
10031
10143
  await ensureModule(module);
10032
10144
  }
10033
- const base = module ? join24("modules", module) : ".";
10034
- const pubSubLocalDir = join24(base, "src", "events");
10035
- const pubSubDir = join24(process.cwd(), pubSubLocalDir);
10036
- const filePath = join24(pubSubDir, `${name}Event.ts`);
10145
+ const base = module ? join25("modules", module) : ".";
10146
+ const pubSubLocalDir = join25(base, "src", "events");
10147
+ const pubSubDir = join25(process.cwd(), pubSubLocalDir);
10148
+ const filePath = join25(pubSubDir, `${name}Event.ts`);
10037
10149
  await Bun.write(filePath, content);
10038
10150
  const testContent = pubsub_test_default.replace(/{{NAME}}/g, name).replace(/{{MODULE}}/g, module ?? "");
10039
- const testsLocalDir = join24(base, "tests", "events");
10040
- const testsDir = join24(process.cwd(), testsLocalDir);
10041
- const testFilePath = join24(testsDir, `${name}Event.spec.ts`);
10151
+ const testsLocalDir = join25(base, "tests", "events");
10152
+ const testsDir = join25(process.cwd(), testsLocalDir);
10153
+ const testFilePath = join25(testsDir, `${name}Event.spec.ts`);
10042
10154
  await Bun.write(testFilePath, testContent);
10043
10155
  const modulePascalName = module ? toPascalCase13(module) : toPascalCase13(basename5(process.cwd()));
10044
- const modulePath = join24(process.cwd(), base, "src", `${modulePascalName}Module.ts`);
10156
+ const modulePath = join25(process.cwd(), base, "src", `${modulePascalName}Module.ts`);
10045
10157
  if (await Bun.file(modulePath).exists()) {
10046
10158
  await this.addToModule(modulePath, name);
10047
10159
  }
10048
- const logger = new TerminalLogger23;
10049
- logger.success(`${join24(pubSubLocalDir, name)}Event.ts created successfully`, undefined, {
10160
+ const logger = new TerminalLogger24;
10161
+ logger.success(`${join25(pubSubLocalDir, name)}Event.ts created successfully`, undefined, {
10050
10162
  showTimestamp: false,
10051
10163
  showArrow: false,
10052
10164
  useSymbol: true
10053
10165
  });
10054
- logger.success(`${join24(testsLocalDir, name)}Event.spec.ts created successfully`, undefined, {
10166
+ logger.success(`${join25(testsLocalDir, name)}Event.spec.ts created successfully`, undefined, {
10055
10167
  showTimestamp: false,
10056
10168
  showArrow: false,
10057
10169
  useSymbol: true
10058
10170
  });
10059
- const packageJsonPath = join24(process.cwd(), "package.json");
10171
+ const packageJsonPath = join25(process.cwd(), "package.json");
10060
10172
  const packageJson = await Bun.file(packageJsonPath).json();
10061
10173
  const deps = packageJson.dependencies ?? {};
10062
10174
  const devDeps = packageJson.devDependencies ?? {};
@@ -10071,13 +10183,13 @@ class MakePubSubCommand {
10071
10183
  }
10072
10184
  }
10073
10185
  MakePubSubCommand = __legacyDecorateClassTS([
10074
- decorator24.command()
10186
+ decorator25.command()
10075
10187
  ], MakePubSubCommand);
10076
10188
  // src/commands/MakeReleaseCommand.ts
10077
10189
  import { readdir } from "fs/promises";
10078
- import { join as join25 } from "path";
10079
- import { decorator as decorator25 } from "@ooneex/command";
10080
- import { TerminalLogger as TerminalLogger24 } from "@ooneex/logger";
10190
+ import { join as join26 } from "path";
10191
+ import { decorator as decorator26 } from "@ooneex/command";
10192
+ import { TerminalLogger as TerminalLogger25 } from "@ooneex/logger";
10081
10193
  var {$ } = globalThis.Bun;
10082
10194
  var COMMIT_TYPE_TO_CATEGORY = {
10083
10195
  feat: "Added",
@@ -10100,7 +10212,7 @@ class MakeReleaseCommand {
10100
10212
  return "Release packages with version bump, changelog, and git tag";
10101
10213
  }
10102
10214
  async run() {
10103
- const logger = new TerminalLogger24;
10215
+ const logger = new TerminalLogger25;
10104
10216
  const cwd = process.cwd();
10105
10217
  const dirs = [];
10106
10218
  for (const { name, type } of [
@@ -10108,8 +10220,8 @@ class MakeReleaseCommand {
10108
10220
  { name: "modules", type: "module" }
10109
10221
  ]) {
10110
10222
  try {
10111
- const entries = await readdir(join25(cwd, name), { withFileTypes: true });
10112
- dirs.push(...entries.filter((d) => d.isDirectory()).map((d) => ({ base: join25(name, d.name), type })));
10223
+ const entries = await readdir(join26(cwd, name), { withFileTypes: true });
10224
+ dirs.push(...entries.filter((d) => d.isDirectory()).map((d) => ({ base: join26(name, d.name), type })));
10113
10225
  } catch {}
10114
10226
  }
10115
10227
  const logOptions = { showTimestamp: false, showArrow: false, useSymbol: true };
@@ -10119,8 +10231,8 @@ class MakeReleaseCommand {
10119
10231
  }
10120
10232
  let releasedCount = 0;
10121
10233
  for (const dir of dirs) {
10122
- const fullDir = join25(cwd, dir.base);
10123
- const pkgJsonPath = join25(fullDir, "package.json");
10234
+ const fullDir = join26(cwd, dir.base);
10235
+ const pkgJsonPath = join26(fullDir, "package.json");
10124
10236
  const pkgJsonFile = Bun.file(pkgJsonPath);
10125
10237
  if (!await pkgJsonFile.exists()) {
10126
10238
  continue;
@@ -10138,7 +10250,7 @@ class MakeReleaseCommand {
10138
10250
  await Bun.write(pkgJsonPath, `${JSON.stringify(pkgJson, null, 2)}
10139
10251
  `);
10140
10252
  await this.updateChangelog(fullDir, newVersion, tag, commits);
10141
- await this.gitAdd(join25(dir.base, "package.json"), join25(dir.base, "CHANGELOG.md"));
10253
+ await this.gitAdd(join26(dir.base, "package.json"), join26(dir.base, "CHANGELOG.md"));
10142
10254
  await this.gitCommit(`chore(release): ${pkgJson.name}@${newVersion}`);
10143
10255
  await this.gitTag(tag, `chore(release): ${pkgJson.name}@${newVersion}`);
10144
10256
  logger.success(`${pkgJson.name}@${newVersion} released (${bumpType} bump, ${commits.length} commit(s))`, undefined, logOptions);
@@ -10239,7 +10351,7 @@ class MakeReleaseCommand {
10239
10351
  }
10240
10352
  }
10241
10353
  async updateChangelog(dir, version, tag, commits) {
10242
- const changelogPath = join25(dir, "CHANGELOG.md");
10354
+ const changelogPath = join26(dir, "CHANGELOG.md");
10243
10355
  const today = new Date().toISOString().split("T")[0];
10244
10356
  const repoUrl = await this.getRepoUrl();
10245
10357
  const grouped = new Map;
@@ -10316,12 +10428,12 @@ ${section}
10316
10428
  }
10317
10429
  }
10318
10430
  MakeReleaseCommand = __legacyDecorateClassTS([
10319
- decorator25.command()
10431
+ decorator26.command()
10320
10432
  ], MakeReleaseCommand);
10321
10433
  // src/commands/MakeRepositoryCommand.ts
10322
- import { join as join26 } from "path";
10323
- import { decorator as decorator26 } from "@ooneex/command";
10324
- import { TerminalLogger as TerminalLogger25 } from "@ooneex/logger";
10434
+ import { join as join27 } from "path";
10435
+ import { decorator as decorator27 } from "@ooneex/command";
10436
+ import { TerminalLogger as TerminalLogger26 } from "@ooneex/logger";
10325
10437
  import { toPascalCase as toPascalCase14 } from "@ooneex/utils";
10326
10438
 
10327
10439
  // src/templates/repository.test.txt
@@ -10535,28 +10647,28 @@ class MakeRepositoryCommand {
10535
10647
  if (module) {
10536
10648
  await ensureModule(module);
10537
10649
  }
10538
- const base = module ? join26("modules", module) : ".";
10539
- const repositoriesLocalDir = join26(base, "src", "repositories");
10540
- const repositoriesDir = join26(process.cwd(), repositoriesLocalDir);
10541
- const filePath = join26(repositoriesDir, `${name}Repository.ts`);
10650
+ const base = module ? join27("modules", module) : ".";
10651
+ const repositoriesLocalDir = join27(base, "src", "repositories");
10652
+ const repositoriesDir = join27(process.cwd(), repositoriesLocalDir);
10653
+ const filePath = join27(repositoriesDir, `${name}Repository.ts`);
10542
10654
  await Bun.write(filePath, content);
10543
10655
  const testContent = repository_test_default.replace(/{{NAME}}/g, name).replace(/{{MODULE}}/g, module ?? "");
10544
- const testsLocalDir = join26(base, "tests", "repositories");
10545
- const testsDir = join26(process.cwd(), testsLocalDir);
10546
- const testFilePath = join26(testsDir, `${name}Repository.spec.ts`);
10656
+ const testsLocalDir = join27(base, "tests", "repositories");
10657
+ const testsDir = join27(process.cwd(), testsLocalDir);
10658
+ const testFilePath = join27(testsDir, `${name}Repository.spec.ts`);
10547
10659
  await Bun.write(testFilePath, testContent);
10548
- const logger = new TerminalLogger25;
10549
- logger.success(`${join26(repositoriesLocalDir, name)}Repository.ts created successfully`, undefined, {
10660
+ const logger = new TerminalLogger26;
10661
+ logger.success(`${join27(repositoriesLocalDir, name)}Repository.ts created successfully`, undefined, {
10550
10662
  showTimestamp: false,
10551
10663
  showArrow: false,
10552
10664
  useSymbol: true
10553
10665
  });
10554
- logger.success(`${join26(testsLocalDir, name)}Repository.spec.ts created successfully`, undefined, {
10666
+ logger.success(`${join27(testsLocalDir, name)}Repository.spec.ts created successfully`, undefined, {
10555
10667
  showTimestamp: false,
10556
10668
  showArrow: false,
10557
10669
  useSymbol: true
10558
10670
  });
10559
- const packageJsonPath = join26(process.cwd(), "package.json");
10671
+ const packageJsonPath = join27(process.cwd(), "package.json");
10560
10672
  const packageJson = await Bun.file(packageJsonPath).json();
10561
10673
  const deps = packageJson.dependencies ?? {};
10562
10674
  const devDeps = packageJson.devDependencies ?? {};
@@ -10571,11 +10683,11 @@ class MakeRepositoryCommand {
10571
10683
  }
10572
10684
  }
10573
10685
  MakeRepositoryCommand = __legacyDecorateClassTS([
10574
- decorator26.command()
10686
+ decorator27.command()
10575
10687
  ], MakeRepositoryCommand);
10576
10688
  // src/commands/MakeResourceBookCommand.ts
10577
- import { join as join28 } from "path";
10578
- import { decorator as decorator28 } from "@ooneex/command";
10689
+ import { join as join29 } from "path";
10690
+ import { decorator as decorator29 } from "@ooneex/command";
10579
10691
  var {Glob } = globalThis.Bun;
10580
10692
 
10581
10693
  // src/templates/resources/book/BookEntity.txt
@@ -11271,9 +11383,9 @@ export class UpdateBookService implements IService {
11271
11383
  `;
11272
11384
 
11273
11385
  // src/commands/MakeServiceCommand.ts
11274
- import { join as join27 } from "path";
11275
- import { decorator as decorator27 } from "@ooneex/command";
11276
- import { TerminalLogger as TerminalLogger26 } from "@ooneex/logger";
11386
+ import { join as join28 } from "path";
11387
+ import { decorator as decorator28 } from "@ooneex/command";
11388
+ import { TerminalLogger as TerminalLogger27 } from "@ooneex/logger";
11277
11389
  import { toPascalCase as toPascalCase15 } from "@ooneex/utils";
11278
11390
 
11279
11391
  // src/templates/service.test.txt
@@ -11325,28 +11437,28 @@ class MakeServiceCommand {
11325
11437
  if (module) {
11326
11438
  await ensureModule(module);
11327
11439
  }
11328
- const base = module ? join27("modules", module) : ".";
11329
- const serviceLocalDir = join27(base, "src", "services");
11330
- const serviceDir = join27(process.cwd(), serviceLocalDir);
11331
- const filePath = join27(serviceDir, `${name}Service.ts`);
11440
+ const base = module ? join28("modules", module) : ".";
11441
+ const serviceLocalDir = join28(base, "src", "services");
11442
+ const serviceDir = join28(process.cwd(), serviceLocalDir);
11443
+ const filePath = join28(serviceDir, `${name}Service.ts`);
11332
11444
  await Bun.write(filePath, content);
11333
11445
  const testContent = service_test_default.replace(/{{NAME}}/g, name).replace(/{{MODULE}}/g, module ?? "");
11334
- const testsLocalDir = join27(base, "tests", "services");
11335
- const testsDir = join27(process.cwd(), testsLocalDir);
11336
- const testFilePath = join27(testsDir, `${name}Service.spec.ts`);
11446
+ const testsLocalDir = join28(base, "tests", "services");
11447
+ const testsDir = join28(process.cwd(), testsLocalDir);
11448
+ const testFilePath = join28(testsDir, `${name}Service.spec.ts`);
11337
11449
  await Bun.write(testFilePath, testContent);
11338
- const logger = new TerminalLogger26;
11339
- logger.success(`${join27(serviceLocalDir, name)}Service.ts created successfully`, undefined, {
11450
+ const logger = new TerminalLogger27;
11451
+ logger.success(`${join28(serviceLocalDir, name)}Service.ts created successfully`, undefined, {
11340
11452
  showTimestamp: false,
11341
11453
  showArrow: false,
11342
11454
  useSymbol: true
11343
11455
  });
11344
- logger.success(`${join27(testsLocalDir, name)}Service.spec.ts created successfully`, undefined, {
11456
+ logger.success(`${join28(testsLocalDir, name)}Service.spec.ts created successfully`, undefined, {
11345
11457
  showTimestamp: false,
11346
11458
  showArrow: false,
11347
11459
  useSymbol: true
11348
11460
  });
11349
- const packageJsonPath = join27(process.cwd(), "package.json");
11461
+ const packageJsonPath = join28(process.cwd(), "package.json");
11350
11462
  const packageJson = await Bun.file(packageJsonPath).json();
11351
11463
  const deps = packageJson.dependencies ?? {};
11352
11464
  const devDeps = packageJson.devDependencies ?? {};
@@ -11361,7 +11473,7 @@ class MakeServiceCommand {
11361
11473
  }
11362
11474
  }
11363
11475
  MakeServiceCommand = __legacyDecorateClassTS([
11364
- decorator27.command()
11476
+ decorator28.command()
11365
11477
  ], MakeServiceCommand);
11366
11478
 
11367
11479
  // src/commands/MakeResourceBookCommand.ts
@@ -11374,7 +11486,7 @@ class MakeResourceBookCommand {
11374
11486
  }
11375
11487
  async run() {
11376
11488
  const module = "book";
11377
- const base = join28("modules", module);
11489
+ const base = join29("modules", module);
11378
11490
  const makeModuleCommand = new MakeModuleCommand;
11379
11491
  await makeModuleCommand.run({ name: module, silent: true });
11380
11492
  const makeEntityCommand = new MakeEntityCommand;
@@ -11394,26 +11506,26 @@ class MakeResourceBookCommand {
11394
11506
  for (const controller of controllers) {
11395
11507
  await makeControllerCommand.run({ ...controller, module, isSocket: false });
11396
11508
  }
11397
- const controllersDir = join28(process.cwd(), base, "src", "controllers");
11398
- await Bun.write(join28(controllersDir, "CreateBookController.ts"), CreateBookController_default);
11399
- await Bun.write(join28(controllersDir, "GetBookController.ts"), GetBookController_default);
11400
- await Bun.write(join28(controllersDir, "ListBooksController.ts"), ListBooksController_default);
11401
- await Bun.write(join28(controllersDir, "UpdateBookController.ts"), UpdateBookController_default);
11402
- await Bun.write(join28(controllersDir, "DeleteBookController.ts"), DeleteBookController_default);
11509
+ const controllersDir = join29(process.cwd(), base, "src", "controllers");
11510
+ await Bun.write(join29(controllersDir, "CreateBookController.ts"), CreateBookController_default);
11511
+ await Bun.write(join29(controllersDir, "GetBookController.ts"), GetBookController_default);
11512
+ await Bun.write(join29(controllersDir, "ListBooksController.ts"), ListBooksController_default);
11513
+ await Bun.write(join29(controllersDir, "UpdateBookController.ts"), UpdateBookController_default);
11514
+ await Bun.write(join29(controllersDir, "DeleteBookController.ts"), DeleteBookController_default);
11403
11515
  const makeServiceCommand = new MakeServiceCommand;
11404
11516
  const services = ["CreateBook", "GetBook", "ListBooks", "UpdateBook", "DeleteBook"];
11405
11517
  for (const name of services) {
11406
11518
  await makeServiceCommand.run({ name, module });
11407
11519
  }
11408
- const servicesDir = join28(process.cwd(), base, "src", "services");
11409
- await Bun.write(join28(servicesDir, "CreateBookService.ts"), CreateBookService_default);
11410
- await Bun.write(join28(servicesDir, "GetBookService.ts"), GetBookService_default);
11411
- await Bun.write(join28(servicesDir, "ListBooksService.ts"), ListBooksService_default);
11412
- await Bun.write(join28(servicesDir, "UpdateBookService.ts"), UpdateBookService_default);
11413
- await Bun.write(join28(servicesDir, "DeleteBookService.ts"), DeleteBookService_default);
11414
- const entityPath = join28(process.cwd(), base, "src", "entities", "BookEntity.ts");
11520
+ const servicesDir = join29(process.cwd(), base, "src", "services");
11521
+ await Bun.write(join29(servicesDir, "CreateBookService.ts"), CreateBookService_default);
11522
+ await Bun.write(join29(servicesDir, "GetBookService.ts"), GetBookService_default);
11523
+ await Bun.write(join29(servicesDir, "ListBooksService.ts"), ListBooksService_default);
11524
+ await Bun.write(join29(servicesDir, "UpdateBookService.ts"), UpdateBookService_default);
11525
+ await Bun.write(join29(servicesDir, "DeleteBookService.ts"), DeleteBookService_default);
11526
+ const entityPath = join29(process.cwd(), base, "src", "entities", "BookEntity.ts");
11415
11527
  await Bun.write(entityPath, BookEntity_default);
11416
- const migrationsDir = join28(process.cwd(), base, "src", "migrations");
11528
+ const migrationsDir = join29(process.cwd(), base, "src", "migrations");
11417
11529
  const glob = new Glob("Migration*.ts");
11418
11530
  for await (const file of glob.scan(migrationsDir)) {
11419
11531
  if (file === "migrations.ts")
@@ -11421,18 +11533,18 @@ class MakeResourceBookCommand {
11421
11533
  const name = file.replace(/\.ts$/, "");
11422
11534
  const version = name.replace("Migration", "");
11423
11535
  const content = BookMigration_default.replaceAll("{{ name }}", name).replaceAll("{{ version }}", version);
11424
- await Bun.write(join28(migrationsDir, file), content);
11536
+ await Bun.write(join29(migrationsDir, file), content);
11425
11537
  }
11426
- const repositoryPath = join28(process.cwd(), base, "src", "repositories", "BookRepository.ts");
11538
+ const repositoryPath = join29(process.cwd(), base, "src", "repositories", "BookRepository.ts");
11427
11539
  await Bun.write(repositoryPath, BookRepository_default);
11428
11540
  }
11429
11541
  }
11430
11542
  MakeResourceBookCommand = __legacyDecorateClassTS([
11431
- decorator28.command()
11543
+ decorator29.command()
11432
11544
  ], MakeResourceBookCommand);
11433
11545
  // src/commands/MakeResourceCalendarEventCommand.ts
11434
- import { join as join29 } from "path";
11435
- import { decorator as decorator29 } from "@ooneex/command";
11546
+ import { join as join30 } from "path";
11547
+ import { decorator as decorator30 } from "@ooneex/command";
11436
11548
  var {Glob: Glob2 } = globalThis.Bun;
11437
11549
 
11438
11550
  // src/templates/resources/calendar-event/CalendarEventEntity.txt
@@ -12124,7 +12236,7 @@ class MakeResourceCalendarEventCommand {
12124
12236
  }
12125
12237
  async run() {
12126
12238
  const module = "calendar-event";
12127
- const base = join29("modules", module);
12239
+ const base = join30("modules", module);
12128
12240
  const makeModuleCommand = new MakeModuleCommand;
12129
12241
  await makeModuleCommand.run({ name: module, silent: true });
12130
12242
  const makeEntityCommand = new MakeEntityCommand;
@@ -12159,12 +12271,12 @@ class MakeResourceCalendarEventCommand {
12159
12271
  for (const controller of controllers) {
12160
12272
  await makeControllerCommand.run({ ...controller, module, isSocket: false });
12161
12273
  }
12162
- const controllersDir = join29(process.cwd(), base, "src", "controllers");
12163
- await Bun.write(join29(controllersDir, "CreateCalendarEventController.ts"), CreateCalendarEventController_default);
12164
- await Bun.write(join29(controllersDir, "GetCalendarEventController.ts"), GetCalendarEventController_default);
12165
- await Bun.write(join29(controllersDir, "ListCalendarEventsController.ts"), ListCalendarEventsController_default);
12166
- await Bun.write(join29(controllersDir, "UpdateCalendarEventController.ts"), UpdateCalendarEventController_default);
12167
- await Bun.write(join29(controllersDir, "DeleteCalendarEventController.ts"), DeleteCalendarEventController_default);
12274
+ const controllersDir = join30(process.cwd(), base, "src", "controllers");
12275
+ await Bun.write(join30(controllersDir, "CreateCalendarEventController.ts"), CreateCalendarEventController_default);
12276
+ await Bun.write(join30(controllersDir, "GetCalendarEventController.ts"), GetCalendarEventController_default);
12277
+ await Bun.write(join30(controllersDir, "ListCalendarEventsController.ts"), ListCalendarEventsController_default);
12278
+ await Bun.write(join30(controllersDir, "UpdateCalendarEventController.ts"), UpdateCalendarEventController_default);
12279
+ await Bun.write(join30(controllersDir, "DeleteCalendarEventController.ts"), DeleteCalendarEventController_default);
12168
12280
  const makeServiceCommand = new MakeServiceCommand;
12169
12281
  const services = [
12170
12282
  "CreateCalendarEvent",
@@ -12176,15 +12288,15 @@ class MakeResourceCalendarEventCommand {
12176
12288
  for (const name of services) {
12177
12289
  await makeServiceCommand.run({ name, module });
12178
12290
  }
12179
- const servicesDir = join29(process.cwd(), base, "src", "services");
12180
- await Bun.write(join29(servicesDir, "CreateCalendarEventService.ts"), CreateCalendarEventService_default);
12181
- await Bun.write(join29(servicesDir, "GetCalendarEventService.ts"), GetCalendarEventService_default);
12182
- await Bun.write(join29(servicesDir, "ListCalendarEventsService.ts"), ListCalendarEventsService_default);
12183
- await Bun.write(join29(servicesDir, "UpdateCalendarEventService.ts"), UpdateCalendarEventService_default);
12184
- await Bun.write(join29(servicesDir, "DeleteCalendarEventService.ts"), DeleteCalendarEventService_default);
12185
- const entityPath = join29(process.cwd(), base, "src", "entities", "CalendarEventEntity.ts");
12291
+ const servicesDir = join30(process.cwd(), base, "src", "services");
12292
+ await Bun.write(join30(servicesDir, "CreateCalendarEventService.ts"), CreateCalendarEventService_default);
12293
+ await Bun.write(join30(servicesDir, "GetCalendarEventService.ts"), GetCalendarEventService_default);
12294
+ await Bun.write(join30(servicesDir, "ListCalendarEventsService.ts"), ListCalendarEventsService_default);
12295
+ await Bun.write(join30(servicesDir, "UpdateCalendarEventService.ts"), UpdateCalendarEventService_default);
12296
+ await Bun.write(join30(servicesDir, "DeleteCalendarEventService.ts"), DeleteCalendarEventService_default);
12297
+ const entityPath = join30(process.cwd(), base, "src", "entities", "CalendarEventEntity.ts");
12186
12298
  await Bun.write(entityPath, CalendarEventEntity_default);
12187
- const migrationsDir = join29(process.cwd(), base, "src", "migrations");
12299
+ const migrationsDir = join30(process.cwd(), base, "src", "migrations");
12188
12300
  const glob = new Glob2("Migration*.ts");
12189
12301
  for await (const file of glob.scan(migrationsDir)) {
12190
12302
  if (file === "migrations.ts")
@@ -12192,18 +12304,18 @@ class MakeResourceCalendarEventCommand {
12192
12304
  const name = file.replace(/\.ts$/, "");
12193
12305
  const version = name.replace("Migration", "");
12194
12306
  const content = CalendarEventMigration_default.replaceAll("{{ name }}", name).replaceAll("{{ version }}", version);
12195
- await Bun.write(join29(migrationsDir, file), content);
12307
+ await Bun.write(join30(migrationsDir, file), content);
12196
12308
  }
12197
- const repositoryPath = join29(process.cwd(), base, "src", "repositories", "CalendarEventRepository.ts");
12309
+ const repositoryPath = join30(process.cwd(), base, "src", "repositories", "CalendarEventRepository.ts");
12198
12310
  await Bun.write(repositoryPath, CalendarEventRepository_default);
12199
12311
  }
12200
12312
  }
12201
12313
  MakeResourceCalendarEventCommand = __legacyDecorateClassTS([
12202
- decorator29.command()
12314
+ decorator30.command()
12203
12315
  ], MakeResourceCalendarEventCommand);
12204
12316
  // src/commands/MakeResourceCategoryCommand.ts
12205
- import { join as join30 } from "path";
12206
- import { decorator as decorator30 } from "@ooneex/command";
12317
+ import { join as join31 } from "path";
12318
+ import { decorator as decorator31 } from "@ooneex/command";
12207
12319
  var {Glob: Glob3 } = globalThis.Bun;
12208
12320
 
12209
12321
  // src/templates/resources/category/CategoryEntity.txt
@@ -12794,7 +12906,7 @@ class MakeResourceCategoryCommand {
12794
12906
  }
12795
12907
  async run() {
12796
12908
  const module = "category";
12797
- const base = join30("modules", module);
12909
+ const base = join31("modules", module);
12798
12910
  const makeModuleCommand = new MakeModuleCommand;
12799
12911
  await makeModuleCommand.run({ name: module, silent: true });
12800
12912
  const makeEntityCommand = new MakeEntityCommand;
@@ -12829,26 +12941,26 @@ class MakeResourceCategoryCommand {
12829
12941
  for (const controller of controllers) {
12830
12942
  await makeControllerCommand.run({ ...controller, module, isSocket: false });
12831
12943
  }
12832
- const controllersDir = join30(process.cwd(), base, "src", "controllers");
12833
- await Bun.write(join30(controllersDir, "CreateCategoryController.ts"), CreateCategoryController_default);
12834
- await Bun.write(join30(controllersDir, "GetCategoryController.ts"), GetCategoryController_default);
12835
- await Bun.write(join30(controllersDir, "ListCategoriesController.ts"), ListCategoriesController_default);
12836
- await Bun.write(join30(controllersDir, "UpdateCategoryController.ts"), UpdateCategoryController_default);
12837
- await Bun.write(join30(controllersDir, "DeleteCategoryController.ts"), DeleteCategoryController_default);
12944
+ const controllersDir = join31(process.cwd(), base, "src", "controllers");
12945
+ await Bun.write(join31(controllersDir, "CreateCategoryController.ts"), CreateCategoryController_default);
12946
+ await Bun.write(join31(controllersDir, "GetCategoryController.ts"), GetCategoryController_default);
12947
+ await Bun.write(join31(controllersDir, "ListCategoriesController.ts"), ListCategoriesController_default);
12948
+ await Bun.write(join31(controllersDir, "UpdateCategoryController.ts"), UpdateCategoryController_default);
12949
+ await Bun.write(join31(controllersDir, "DeleteCategoryController.ts"), DeleteCategoryController_default);
12838
12950
  const makeServiceCommand = new MakeServiceCommand;
12839
12951
  const services = ["CreateCategory", "GetCategory", "ListCategories", "UpdateCategory", "DeleteCategory"];
12840
12952
  for (const name of services) {
12841
12953
  await makeServiceCommand.run({ name, module });
12842
12954
  }
12843
- const servicesDir = join30(process.cwd(), base, "src", "services");
12844
- await Bun.write(join30(servicesDir, "CreateCategoryService.ts"), CreateCategoryService_default);
12845
- await Bun.write(join30(servicesDir, "GetCategoryService.ts"), GetCategoryService_default);
12846
- await Bun.write(join30(servicesDir, "ListCategoriesService.ts"), ListCategoriesService_default);
12847
- await Bun.write(join30(servicesDir, "UpdateCategoryService.ts"), UpdateCategoryService_default);
12848
- await Bun.write(join30(servicesDir, "DeleteCategoryService.ts"), DeleteCategoryService_default);
12849
- const entityPath = join30(process.cwd(), base, "src", "entities", "CategoryEntity.ts");
12955
+ const servicesDir = join31(process.cwd(), base, "src", "services");
12956
+ await Bun.write(join31(servicesDir, "CreateCategoryService.ts"), CreateCategoryService_default);
12957
+ await Bun.write(join31(servicesDir, "GetCategoryService.ts"), GetCategoryService_default);
12958
+ await Bun.write(join31(servicesDir, "ListCategoriesService.ts"), ListCategoriesService_default);
12959
+ await Bun.write(join31(servicesDir, "UpdateCategoryService.ts"), UpdateCategoryService_default);
12960
+ await Bun.write(join31(servicesDir, "DeleteCategoryService.ts"), DeleteCategoryService_default);
12961
+ const entityPath = join31(process.cwd(), base, "src", "entities", "CategoryEntity.ts");
12850
12962
  await Bun.write(entityPath, CategoryEntity_default);
12851
- const migrationsDir = join30(process.cwd(), base, "src", "migrations");
12963
+ const migrationsDir = join31(process.cwd(), base, "src", "migrations");
12852
12964
  const glob = new Glob3("Migration*.ts");
12853
12965
  for await (const file of glob.scan(migrationsDir)) {
12854
12966
  if (file === "migrations.ts")
@@ -12856,18 +12968,18 @@ class MakeResourceCategoryCommand {
12856
12968
  const name = file.replace(/\.ts$/, "");
12857
12969
  const version = name.replace("Migration", "");
12858
12970
  const content = CategoryMigration_default.replaceAll("{{ name }}", name).replaceAll("{{ version }}", version);
12859
- await Bun.write(join30(migrationsDir, file), content);
12971
+ await Bun.write(join31(migrationsDir, file), content);
12860
12972
  }
12861
- const repositoryPath = join30(process.cwd(), base, "src", "repositories", "CategoryRepository.ts");
12973
+ const repositoryPath = join31(process.cwd(), base, "src", "repositories", "CategoryRepository.ts");
12862
12974
  await Bun.write(repositoryPath, CategoryRepository_default);
12863
12975
  }
12864
12976
  }
12865
12977
  MakeResourceCategoryCommand = __legacyDecorateClassTS([
12866
- decorator30.command()
12978
+ decorator31.command()
12867
12979
  ], MakeResourceCategoryCommand);
12868
12980
  // src/commands/MakeResourceColorCommand.ts
12869
- import { join as join32 } from "path";
12870
- import { decorator as decorator32 } from "@ooneex/command";
12981
+ import { join as join33 } from "path";
12982
+ import { decorator as decorator33 } from "@ooneex/command";
12871
12983
  var {Glob: Glob4 } = globalThis.Bun;
12872
12984
 
12873
12985
  // src/templates/resources/color/ColorEntity.txt
@@ -13659,9 +13771,9 @@ export class UpdateColorService implements IService {
13659
13771
  `;
13660
13772
 
13661
13773
  // src/commands/MakeSeedCommand.ts
13662
- import { join as join31 } from "path";
13663
- import { decorator as decorator31 } from "@ooneex/command";
13664
- import { TerminalLogger as TerminalLogger27 } from "@ooneex/logger";
13774
+ import { join as join32 } from "path";
13775
+ import { decorator as decorator32 } from "@ooneex/command";
13776
+ import { TerminalLogger as TerminalLogger28 } from "@ooneex/logger";
13665
13777
  import { seedCreate } from "@ooneex/seeds";
13666
13778
 
13667
13779
  // src/templates/module/seed.run.txt
@@ -13700,19 +13812,19 @@ class MakeSeedCommand {
13700
13812
  if (module) {
13701
13813
  await ensureModule(module);
13702
13814
  }
13703
- const base = module ? join31("modules", module) : ".";
13815
+ const base = module ? join32("modules", module) : ".";
13704
13816
  const { seedPath: filePath, dataPath } = await seedCreate({
13705
13817
  name,
13706
- seedsDir: join31(base, "src", "seeds"),
13707
- testsDir: join31(base, "tests", "seeds"),
13818
+ seedsDir: join32(base, "src", "seeds"),
13819
+ testsDir: join32(base, "tests", "seeds"),
13708
13820
  module: module ?? ""
13709
13821
  });
13710
- const binSeedRunPath = join31(process.cwd(), base, "bin", "seed", "run.ts");
13822
+ const binSeedRunPath = join32(process.cwd(), base, "bin", "seed", "run.ts");
13711
13823
  const binSeedRunFile = Bun.file(binSeedRunPath);
13712
13824
  if (!await binSeedRunFile.exists()) {
13713
13825
  await Bun.write(binSeedRunPath, seed_run_default.replace(/{{name}}/g, module ?? ""));
13714
13826
  }
13715
- const logger = new TerminalLogger27;
13827
+ const logger = new TerminalLogger28;
13716
13828
  logger.success(`${filePath} created successfully`, undefined, {
13717
13829
  showTimestamp: false,
13718
13830
  showArrow: false,
@@ -13726,7 +13838,7 @@ class MakeSeedCommand {
13726
13838
  }
13727
13839
  }
13728
13840
  MakeSeedCommand = __legacyDecorateClassTS([
13729
- decorator31.command()
13841
+ decorator32.command()
13730
13842
  ], MakeSeedCommand);
13731
13843
 
13732
13844
  // src/commands/MakeResourceColorCommand.ts
@@ -13739,7 +13851,7 @@ class MakeResourceColorCommand {
13739
13851
  }
13740
13852
  async run() {
13741
13853
  const module = "color";
13742
- const base = join32("modules", module);
13854
+ const base = join33("modules", module);
13743
13855
  const makeModuleCommand = new MakeModuleCommand;
13744
13856
  await makeModuleCommand.run({ name: module, silent: true });
13745
13857
  const makeEntityCommand = new MakeEntityCommand;
@@ -13762,26 +13874,26 @@ class MakeResourceColorCommand {
13762
13874
  for (const controller of controllers) {
13763
13875
  await makeControllerCommand.run({ ...controller, module, isSocket: false });
13764
13876
  }
13765
- const controllersDir = join32(process.cwd(), base, "src", "controllers");
13766
- await Bun.write(join32(controllersDir, "CreateColorController.ts"), CreateColorController_default);
13767
- await Bun.write(join32(controllersDir, "GetColorController.ts"), GetColorController_default);
13768
- await Bun.write(join32(controllersDir, "ListColorsController.ts"), ListColorsController_default);
13769
- await Bun.write(join32(controllersDir, "UpdateColorController.ts"), UpdateColorController_default);
13770
- await Bun.write(join32(controllersDir, "DeleteColorController.ts"), DeleteColorController_default);
13877
+ const controllersDir = join33(process.cwd(), base, "src", "controllers");
13878
+ await Bun.write(join33(controllersDir, "CreateColorController.ts"), CreateColorController_default);
13879
+ await Bun.write(join33(controllersDir, "GetColorController.ts"), GetColorController_default);
13880
+ await Bun.write(join33(controllersDir, "ListColorsController.ts"), ListColorsController_default);
13881
+ await Bun.write(join33(controllersDir, "UpdateColorController.ts"), UpdateColorController_default);
13882
+ await Bun.write(join33(controllersDir, "DeleteColorController.ts"), DeleteColorController_default);
13771
13883
  const makeServiceCommand = new MakeServiceCommand;
13772
13884
  const services = ["CreateColor", "GetColor", "ListColors", "UpdateColor", "DeleteColor"];
13773
13885
  for (const name of services) {
13774
13886
  await makeServiceCommand.run({ name, module });
13775
13887
  }
13776
- const servicesDir = join32(process.cwd(), base, "src", "services");
13777
- await Bun.write(join32(servicesDir, "CreateColorService.ts"), CreateColorService_default);
13778
- await Bun.write(join32(servicesDir, "GetColorService.ts"), GetColorService_default);
13779
- await Bun.write(join32(servicesDir, "ListColorsService.ts"), ListColorsService_default);
13780
- await Bun.write(join32(servicesDir, "UpdateColorService.ts"), UpdateColorService_default);
13781
- await Bun.write(join32(servicesDir, "DeleteColorService.ts"), DeleteColorService_default);
13782
- const entityPath = join32(process.cwd(), base, "src", "entities", "ColorEntity.ts");
13888
+ const servicesDir = join33(process.cwd(), base, "src", "services");
13889
+ await Bun.write(join33(servicesDir, "CreateColorService.ts"), CreateColorService_default);
13890
+ await Bun.write(join33(servicesDir, "GetColorService.ts"), GetColorService_default);
13891
+ await Bun.write(join33(servicesDir, "ListColorsService.ts"), ListColorsService_default);
13892
+ await Bun.write(join33(servicesDir, "UpdateColorService.ts"), UpdateColorService_default);
13893
+ await Bun.write(join33(servicesDir, "DeleteColorService.ts"), DeleteColorService_default);
13894
+ const entityPath = join33(process.cwd(), base, "src", "entities", "ColorEntity.ts");
13783
13895
  await Bun.write(entityPath, ColorEntity_default);
13784
- const migrationsDir = join32(process.cwd(), base, "src", "migrations");
13896
+ const migrationsDir = join33(process.cwd(), base, "src", "migrations");
13785
13897
  const glob = new Glob4("Migration*.ts");
13786
13898
  for await (const file of glob.scan(migrationsDir)) {
13787
13899
  if (file === "migrations.ts")
@@ -13789,23 +13901,23 @@ class MakeResourceColorCommand {
13789
13901
  const name = file.replace(/\.ts$/, "");
13790
13902
  const version = name.replace("Migration", "");
13791
13903
  const content = ColorMigration_default.replaceAll("{{ name }}", name).replaceAll("{{ version }}", version);
13792
- await Bun.write(join32(migrationsDir, file), content);
13904
+ await Bun.write(join33(migrationsDir, file), content);
13793
13905
  }
13794
- const repositoryPath = join32(process.cwd(), base, "src", "repositories", "ColorRepository.ts");
13906
+ const repositoryPath = join33(process.cwd(), base, "src", "repositories", "ColorRepository.ts");
13795
13907
  await Bun.write(repositoryPath, ColorRepository_default);
13796
13908
  const makeSeedCommand = new MakeSeedCommand;
13797
13909
  await makeSeedCommand.run({ name: "Color", module });
13798
- const seedsDir = join32(process.cwd(), base, "src", "seeds");
13799
- await Bun.write(join32(seedsDir, "ColorSeed.ts"), ColorSeed_default);
13800
- await Bun.write(join32(seedsDir, "color-seed.yml"), color_seed_default);
13910
+ const seedsDir = join33(process.cwd(), base, "src", "seeds");
13911
+ await Bun.write(join33(seedsDir, "ColorSeed.ts"), ColorSeed_default);
13912
+ await Bun.write(join33(seedsDir, "color-seed.yml"), color_seed_default);
13801
13913
  }
13802
13914
  }
13803
13915
  MakeResourceColorCommand = __legacyDecorateClassTS([
13804
- decorator32.command()
13916
+ decorator33.command()
13805
13917
  ], MakeResourceColorCommand);
13806
13918
  // src/commands/MakeResourceDiscountCommand.ts
13807
- import { join as join33 } from "path";
13808
- import { decorator as decorator33 } from "@ooneex/command";
13919
+ import { join as join34 } from "path";
13920
+ import { decorator as decorator34 } from "@ooneex/command";
13809
13921
  var {Glob: Glob5 } = globalThis.Bun;
13810
13922
 
13811
13923
  // src/templates/resources/discount/controllers/CreateDiscountController.txt
@@ -14448,7 +14560,7 @@ class MakeResourceDiscountCommand {
14448
14560
  }
14449
14561
  async run() {
14450
14562
  const module = "discount";
14451
- const base = join33("modules", module);
14563
+ const base = join34("modules", module);
14452
14564
  const makeModuleCommand = new MakeModuleCommand;
14453
14565
  await makeModuleCommand.run({ name: module, silent: true });
14454
14566
  const makeEntityCommand = new MakeEntityCommand;
@@ -14483,26 +14595,26 @@ class MakeResourceDiscountCommand {
14483
14595
  for (const controller of controllers) {
14484
14596
  await makeControllerCommand.run({ ...controller, module, isSocket: false });
14485
14597
  }
14486
- const controllersDir = join33(process.cwd(), base, "src", "controllers");
14487
- await Bun.write(join33(controllersDir, "CreateDiscountController.ts"), CreateDiscountController_default);
14488
- await Bun.write(join33(controllersDir, "GetDiscountController.ts"), GetDiscountController_default);
14489
- await Bun.write(join33(controllersDir, "ListDiscountsController.ts"), ListDiscountsController_default);
14490
- await Bun.write(join33(controllersDir, "UpdateDiscountController.ts"), UpdateDiscountController_default);
14491
- await Bun.write(join33(controllersDir, "DeleteDiscountController.ts"), DeleteDiscountController_default);
14598
+ const controllersDir = join34(process.cwd(), base, "src", "controllers");
14599
+ await Bun.write(join34(controllersDir, "CreateDiscountController.ts"), CreateDiscountController_default);
14600
+ await Bun.write(join34(controllersDir, "GetDiscountController.ts"), GetDiscountController_default);
14601
+ await Bun.write(join34(controllersDir, "ListDiscountsController.ts"), ListDiscountsController_default);
14602
+ await Bun.write(join34(controllersDir, "UpdateDiscountController.ts"), UpdateDiscountController_default);
14603
+ await Bun.write(join34(controllersDir, "DeleteDiscountController.ts"), DeleteDiscountController_default);
14492
14604
  const makeServiceCommand = new MakeServiceCommand;
14493
14605
  const services = ["CreateDiscount", "GetDiscount", "ListDiscounts", "UpdateDiscount", "DeleteDiscount"];
14494
14606
  for (const name of services) {
14495
14607
  await makeServiceCommand.run({ name, module });
14496
14608
  }
14497
- const servicesDir = join33(process.cwd(), base, "src", "services");
14498
- await Bun.write(join33(servicesDir, "CreateDiscountService.ts"), CreateDiscountService_default);
14499
- await Bun.write(join33(servicesDir, "GetDiscountService.ts"), GetDiscountService_default);
14500
- await Bun.write(join33(servicesDir, "ListDiscountsService.ts"), ListDiscountsService_default);
14501
- await Bun.write(join33(servicesDir, "UpdateDiscountService.ts"), UpdateDiscountService_default);
14502
- await Bun.write(join33(servicesDir, "DeleteDiscountService.ts"), DeleteDiscountService_default);
14503
- const entityPath = join33(process.cwd(), base, "src", "entities", "DiscountEntity.ts");
14609
+ const servicesDir = join34(process.cwd(), base, "src", "services");
14610
+ await Bun.write(join34(servicesDir, "CreateDiscountService.ts"), CreateDiscountService_default);
14611
+ await Bun.write(join34(servicesDir, "GetDiscountService.ts"), GetDiscountService_default);
14612
+ await Bun.write(join34(servicesDir, "ListDiscountsService.ts"), ListDiscountsService_default);
14613
+ await Bun.write(join34(servicesDir, "UpdateDiscountService.ts"), UpdateDiscountService_default);
14614
+ await Bun.write(join34(servicesDir, "DeleteDiscountService.ts"), DeleteDiscountService_default);
14615
+ const entityPath = join34(process.cwd(), base, "src", "entities", "DiscountEntity.ts");
14504
14616
  await Bun.write(entityPath, DiscountEntity_default);
14505
- const migrationsDir = join33(process.cwd(), base, "src", "migrations");
14617
+ const migrationsDir = join34(process.cwd(), base, "src", "migrations");
14506
14618
  const glob = new Glob5("Migration*.ts");
14507
14619
  for await (const file of glob.scan(migrationsDir)) {
14508
14620
  if (file === "migrations.ts")
@@ -14510,18 +14622,18 @@ class MakeResourceDiscountCommand {
14510
14622
  const name = file.replace(/\.ts$/, "");
14511
14623
  const version = name.replace("Migration", "");
14512
14624
  const content = DiscountMigration_default.replaceAll("{{ name }}", name).replaceAll("{{ version }}", version);
14513
- await Bun.write(join33(migrationsDir, file), content);
14625
+ await Bun.write(join34(migrationsDir, file), content);
14514
14626
  }
14515
- const repositoryPath = join33(process.cwd(), base, "src", "repositories", "DiscountRepository.ts");
14627
+ const repositoryPath = join34(process.cwd(), base, "src", "repositories", "DiscountRepository.ts");
14516
14628
  await Bun.write(repositoryPath, DiscountRepository_default);
14517
14629
  }
14518
14630
  }
14519
14631
  MakeResourceDiscountCommand = __legacyDecorateClassTS([
14520
- decorator33.command()
14632
+ decorator34.command()
14521
14633
  ], MakeResourceDiscountCommand);
14522
14634
  // src/commands/MakeResourceFolderCommand.ts
14523
- import { join as join34 } from "path";
14524
- import { decorator as decorator34 } from "@ooneex/command";
14635
+ import { join as join35 } from "path";
14636
+ import { decorator as decorator35 } from "@ooneex/command";
14525
14637
  var {Glob: Glob6 } = globalThis.Bun;
14526
14638
 
14527
14639
  // src/templates/resources/folder/controllers/CreateFolderController.txt
@@ -15148,7 +15260,7 @@ class MakeResourceFolderCommand {
15148
15260
  }
15149
15261
  async run() {
15150
15262
  const module = "folder";
15151
- const base = join34("modules", module);
15263
+ const base = join35("modules", module);
15152
15264
  const makeModuleCommand = new MakeModuleCommand;
15153
15265
  await makeModuleCommand.run({ name: module, silent: true });
15154
15266
  const makeEntityCommand = new MakeEntityCommand;
@@ -15183,26 +15295,26 @@ class MakeResourceFolderCommand {
15183
15295
  for (const controller of controllers) {
15184
15296
  await makeControllerCommand.run({ ...controller, module, isSocket: false });
15185
15297
  }
15186
- const controllersDir = join34(process.cwd(), base, "src", "controllers");
15187
- await Bun.write(join34(controllersDir, "CreateFolderController.ts"), CreateFolderController_default);
15188
- await Bun.write(join34(controllersDir, "GetFolderController.ts"), GetFolderController_default);
15189
- await Bun.write(join34(controllersDir, "ListFoldersController.ts"), ListFoldersController_default);
15190
- await Bun.write(join34(controllersDir, "UpdateFolderController.ts"), UpdateFolderController_default);
15191
- await Bun.write(join34(controllersDir, "DeleteFolderController.ts"), DeleteFolderController_default);
15298
+ const controllersDir = join35(process.cwd(), base, "src", "controllers");
15299
+ await Bun.write(join35(controllersDir, "CreateFolderController.ts"), CreateFolderController_default);
15300
+ await Bun.write(join35(controllersDir, "GetFolderController.ts"), GetFolderController_default);
15301
+ await Bun.write(join35(controllersDir, "ListFoldersController.ts"), ListFoldersController_default);
15302
+ await Bun.write(join35(controllersDir, "UpdateFolderController.ts"), UpdateFolderController_default);
15303
+ await Bun.write(join35(controllersDir, "DeleteFolderController.ts"), DeleteFolderController_default);
15192
15304
  const makeServiceCommand = new MakeServiceCommand;
15193
15305
  const services = ["CreateFolder", "GetFolder", "ListFolders", "UpdateFolder", "DeleteFolder"];
15194
15306
  for (const name of services) {
15195
15307
  await makeServiceCommand.run({ name, module });
15196
15308
  }
15197
- const servicesDir = join34(process.cwd(), base, "src", "services");
15198
- await Bun.write(join34(servicesDir, "CreateFolderService.ts"), CreateFolderService_default);
15199
- await Bun.write(join34(servicesDir, "GetFolderService.ts"), GetFolderService_default);
15200
- await Bun.write(join34(servicesDir, "ListFoldersService.ts"), ListFoldersService_default);
15201
- await Bun.write(join34(servicesDir, "UpdateFolderService.ts"), UpdateFolderService_default);
15202
- await Bun.write(join34(servicesDir, "DeleteFolderService.ts"), DeleteFolderService_default);
15203
- const entityPath = join34(process.cwd(), base, "src", "entities", "FolderEntity.ts");
15309
+ const servicesDir = join35(process.cwd(), base, "src", "services");
15310
+ await Bun.write(join35(servicesDir, "CreateFolderService.ts"), CreateFolderService_default);
15311
+ await Bun.write(join35(servicesDir, "GetFolderService.ts"), GetFolderService_default);
15312
+ await Bun.write(join35(servicesDir, "ListFoldersService.ts"), ListFoldersService_default);
15313
+ await Bun.write(join35(servicesDir, "UpdateFolderService.ts"), UpdateFolderService_default);
15314
+ await Bun.write(join35(servicesDir, "DeleteFolderService.ts"), DeleteFolderService_default);
15315
+ const entityPath = join35(process.cwd(), base, "src", "entities", "FolderEntity.ts");
15204
15316
  await Bun.write(entityPath, FolderEntity_default);
15205
- const migrationsDir = join34(process.cwd(), base, "src", "migrations");
15317
+ const migrationsDir = join35(process.cwd(), base, "src", "migrations");
15206
15318
  const glob = new Glob6("Migration*.ts");
15207
15319
  for await (const file of glob.scan(migrationsDir)) {
15208
15320
  if (file === "migrations.ts")
@@ -15210,18 +15322,18 @@ class MakeResourceFolderCommand {
15210
15322
  const name = file.replace(/\.ts$/, "");
15211
15323
  const version = name.replace("Migration", "");
15212
15324
  const content = FolderMigration_default.replaceAll("{{ name }}", name).replaceAll("{{ version }}", version);
15213
- await Bun.write(join34(migrationsDir, file), content);
15325
+ await Bun.write(join35(migrationsDir, file), content);
15214
15326
  }
15215
- const repositoryPath = join34(process.cwd(), base, "src", "repositories", "FolderRepository.ts");
15327
+ const repositoryPath = join35(process.cwd(), base, "src", "repositories", "FolderRepository.ts");
15216
15328
  await Bun.write(repositoryPath, FolderRepository_default);
15217
15329
  }
15218
15330
  }
15219
15331
  MakeResourceFolderCommand = __legacyDecorateClassTS([
15220
- decorator34.command()
15332
+ decorator35.command()
15221
15333
  ], MakeResourceFolderCommand);
15222
15334
  // src/commands/MakeResourceImageCommand.ts
15223
- import { join as join35 } from "path";
15224
- import { decorator as decorator35 } from "@ooneex/command";
15335
+ import { join as join36 } from "path";
15336
+ import { decorator as decorator36 } from "@ooneex/command";
15225
15337
  var {Glob: Glob7 } = globalThis.Bun;
15226
15338
 
15227
15339
  // src/templates/resources/image/controllers/CreateImageController.txt
@@ -15887,7 +15999,7 @@ class MakeResourceImageCommand {
15887
15999
  }
15888
16000
  async run() {
15889
16001
  const module = "image";
15890
- const base = join35("modules", module);
16002
+ const base = join36("modules", module);
15891
16003
  const makeModuleCommand = new MakeModuleCommand;
15892
16004
  await makeModuleCommand.run({ name: module, silent: true });
15893
16005
  const makeEntityCommand = new MakeEntityCommand;
@@ -15922,26 +16034,26 @@ class MakeResourceImageCommand {
15922
16034
  for (const controller of controllers) {
15923
16035
  await makeControllerCommand.run({ ...controller, module, isSocket: false });
15924
16036
  }
15925
- const controllersDir = join35(process.cwd(), base, "src", "controllers");
15926
- await Bun.write(join35(controllersDir, "CreateImageController.ts"), CreateImageController_default);
15927
- await Bun.write(join35(controllersDir, "GetImageController.ts"), GetImageController_default);
15928
- await Bun.write(join35(controllersDir, "ListImagesController.ts"), ListImagesController_default);
15929
- await Bun.write(join35(controllersDir, "UpdateImageController.ts"), UpdateImageController_default);
15930
- await Bun.write(join35(controllersDir, "DeleteImageController.ts"), DeleteImageController_default);
16037
+ const controllersDir = join36(process.cwd(), base, "src", "controllers");
16038
+ await Bun.write(join36(controllersDir, "CreateImageController.ts"), CreateImageController_default);
16039
+ await Bun.write(join36(controllersDir, "GetImageController.ts"), GetImageController_default);
16040
+ await Bun.write(join36(controllersDir, "ListImagesController.ts"), ListImagesController_default);
16041
+ await Bun.write(join36(controllersDir, "UpdateImageController.ts"), UpdateImageController_default);
16042
+ await Bun.write(join36(controllersDir, "DeleteImageController.ts"), DeleteImageController_default);
15931
16043
  const makeServiceCommand = new MakeServiceCommand;
15932
16044
  const services = ["CreateImage", "GetImage", "ListImages", "UpdateImage", "DeleteImage"];
15933
16045
  for (const name of services) {
15934
16046
  await makeServiceCommand.run({ name, module });
15935
16047
  }
15936
- const servicesDir = join35(process.cwd(), base, "src", "services");
15937
- await Bun.write(join35(servicesDir, "CreateImageService.ts"), CreateImageService_default);
15938
- await Bun.write(join35(servicesDir, "GetImageService.ts"), GetImageService_default);
15939
- await Bun.write(join35(servicesDir, "ListImagesService.ts"), ListImagesService_default);
15940
- await Bun.write(join35(servicesDir, "UpdateImageService.ts"), UpdateImageService_default);
15941
- await Bun.write(join35(servicesDir, "DeleteImageService.ts"), DeleteImageService_default);
15942
- const entityPath = join35(process.cwd(), base, "src", "entities", "ImageEntity.ts");
16048
+ const servicesDir = join36(process.cwd(), base, "src", "services");
16049
+ await Bun.write(join36(servicesDir, "CreateImageService.ts"), CreateImageService_default);
16050
+ await Bun.write(join36(servicesDir, "GetImageService.ts"), GetImageService_default);
16051
+ await Bun.write(join36(servicesDir, "ListImagesService.ts"), ListImagesService_default);
16052
+ await Bun.write(join36(servicesDir, "UpdateImageService.ts"), UpdateImageService_default);
16053
+ await Bun.write(join36(servicesDir, "DeleteImageService.ts"), DeleteImageService_default);
16054
+ const entityPath = join36(process.cwd(), base, "src", "entities", "ImageEntity.ts");
15943
16055
  await Bun.write(entityPath, ImageEntity_default);
15944
- const migrationsDir = join35(process.cwd(), base, "src", "migrations");
16056
+ const migrationsDir = join36(process.cwd(), base, "src", "migrations");
15945
16057
  const glob = new Glob7("Migration*.ts");
15946
16058
  for await (const file of glob.scan(migrationsDir)) {
15947
16059
  if (file === "migrations.ts")
@@ -15949,18 +16061,18 @@ class MakeResourceImageCommand {
15949
16061
  const name = file.replace(/\.ts$/, "");
15950
16062
  const version = name.replace("Migration", "");
15951
16063
  const content = ImageMigration_default.replaceAll("{{ name }}", name).replaceAll("{{ version }}", version);
15952
- await Bun.write(join35(migrationsDir, file), content);
16064
+ await Bun.write(join36(migrationsDir, file), content);
15953
16065
  }
15954
- const repositoryPath = join35(process.cwd(), base, "src", "repositories", "ImageRepository.ts");
16066
+ const repositoryPath = join36(process.cwd(), base, "src", "repositories", "ImageRepository.ts");
15955
16067
  await Bun.write(repositoryPath, ImageRepository_default);
15956
16068
  }
15957
16069
  }
15958
16070
  MakeResourceImageCommand = __legacyDecorateClassTS([
15959
- decorator35.command()
16071
+ decorator36.command()
15960
16072
  ], MakeResourceImageCommand);
15961
16073
  // src/commands/MakeResourceNoteCommand.ts
15962
- import { join as join36 } from "path";
15963
- import { decorator as decorator36 } from "@ooneex/command";
16074
+ import { join as join37 } from "path";
16075
+ import { decorator as decorator37 } from "@ooneex/command";
15964
16076
  var {Glob: Glob8 } = globalThis.Bun;
15965
16077
 
15966
16078
  // src/templates/resources/note/controllers/CreateNoteController.txt
@@ -16665,7 +16777,7 @@ class MakeResourceNoteCommand {
16665
16777
  }
16666
16778
  async run() {
16667
16779
  const module = "note";
16668
- const base = join36("modules", module);
16780
+ const base = join37("modules", module);
16669
16781
  const makeModuleCommand = new MakeModuleCommand;
16670
16782
  await makeModuleCommand.run({ name: module, silent: true });
16671
16783
  const makeEntityCommand = new MakeEntityCommand;
@@ -16700,26 +16812,26 @@ class MakeResourceNoteCommand {
16700
16812
  for (const controller of controllers) {
16701
16813
  await makeControllerCommand.run({ ...controller, module, isSocket: false });
16702
16814
  }
16703
- const controllersDir = join36(process.cwd(), base, "src", "controllers");
16704
- await Bun.write(join36(controllersDir, "CreateNoteController.ts"), CreateNoteController_default);
16705
- await Bun.write(join36(controllersDir, "GetNoteController.ts"), GetNoteController_default);
16706
- await Bun.write(join36(controllersDir, "ListNotesController.ts"), ListNotesController_default);
16707
- await Bun.write(join36(controllersDir, "UpdateNoteController.ts"), UpdateNoteController_default);
16708
- await Bun.write(join36(controllersDir, "DeleteNoteController.ts"), DeleteNoteController_default);
16815
+ const controllersDir = join37(process.cwd(), base, "src", "controllers");
16816
+ await Bun.write(join37(controllersDir, "CreateNoteController.ts"), CreateNoteController_default);
16817
+ await Bun.write(join37(controllersDir, "GetNoteController.ts"), GetNoteController_default);
16818
+ await Bun.write(join37(controllersDir, "ListNotesController.ts"), ListNotesController_default);
16819
+ await Bun.write(join37(controllersDir, "UpdateNoteController.ts"), UpdateNoteController_default);
16820
+ await Bun.write(join37(controllersDir, "DeleteNoteController.ts"), DeleteNoteController_default);
16709
16821
  const makeServiceCommand = new MakeServiceCommand;
16710
16822
  const services = ["CreateNote", "GetNote", "ListNotes", "UpdateNote", "DeleteNote"];
16711
16823
  for (const name of services) {
16712
16824
  await makeServiceCommand.run({ name, module });
16713
16825
  }
16714
- const servicesDir = join36(process.cwd(), base, "src", "services");
16715
- await Bun.write(join36(servicesDir, "CreateNoteService.ts"), CreateNoteService_default);
16716
- await Bun.write(join36(servicesDir, "GetNoteService.ts"), GetNoteService_default);
16717
- await Bun.write(join36(servicesDir, "ListNotesService.ts"), ListNotesService_default);
16718
- await Bun.write(join36(servicesDir, "UpdateNoteService.ts"), UpdateNoteService_default);
16719
- await Bun.write(join36(servicesDir, "DeleteNoteService.ts"), DeleteNoteService_default);
16720
- const entityPath = join36(process.cwd(), base, "src", "entities", "NoteEntity.ts");
16826
+ const servicesDir = join37(process.cwd(), base, "src", "services");
16827
+ await Bun.write(join37(servicesDir, "CreateNoteService.ts"), CreateNoteService_default);
16828
+ await Bun.write(join37(servicesDir, "GetNoteService.ts"), GetNoteService_default);
16829
+ await Bun.write(join37(servicesDir, "ListNotesService.ts"), ListNotesService_default);
16830
+ await Bun.write(join37(servicesDir, "UpdateNoteService.ts"), UpdateNoteService_default);
16831
+ await Bun.write(join37(servicesDir, "DeleteNoteService.ts"), DeleteNoteService_default);
16832
+ const entityPath = join37(process.cwd(), base, "src", "entities", "NoteEntity.ts");
16721
16833
  await Bun.write(entityPath, NoteEntity_default);
16722
- const migrationsDir = join36(process.cwd(), base, "src", "migrations");
16834
+ const migrationsDir = join37(process.cwd(), base, "src", "migrations");
16723
16835
  const glob = new Glob8("Migration*.ts");
16724
16836
  for await (const file of glob.scan(migrationsDir)) {
16725
16837
  if (file === "migrations.ts")
@@ -16727,18 +16839,18 @@ class MakeResourceNoteCommand {
16727
16839
  const name = file.replace(/\.ts$/, "");
16728
16840
  const version = name.replace("Migration", "");
16729
16841
  const content = NoteMigration_default.replaceAll("{{ name }}", name).replaceAll("{{ version }}", version);
16730
- await Bun.write(join36(migrationsDir, file), content);
16842
+ await Bun.write(join37(migrationsDir, file), content);
16731
16843
  }
16732
- const repositoryPath = join36(process.cwd(), base, "src", "repositories", "NoteRepository.ts");
16844
+ const repositoryPath = join37(process.cwd(), base, "src", "repositories", "NoteRepository.ts");
16733
16845
  await Bun.write(repositoryPath, NoteRepository_default);
16734
16846
  }
16735
16847
  }
16736
16848
  MakeResourceNoteCommand = __legacyDecorateClassTS([
16737
- decorator36.command()
16849
+ decorator37.command()
16738
16850
  ], MakeResourceNoteCommand);
16739
16851
  // src/commands/MakeResourceStatusCommand.ts
16740
- import { join as join37 } from "path";
16741
- import { decorator as decorator37 } from "@ooneex/command";
16852
+ import { join as join38 } from "path";
16853
+ import { decorator as decorator38 } from "@ooneex/command";
16742
16854
  var {Glob: Glob9 } = globalThis.Bun;
16743
16855
 
16744
16856
  // src/templates/resources/status/controllers/CreateStatusController.txt
@@ -17599,7 +17711,7 @@ class MakeResourceStatusCommand {
17599
17711
  }
17600
17712
  async run() {
17601
17713
  const module = "status";
17602
- const base = join37("modules", module);
17714
+ const base = join38("modules", module);
17603
17715
  const makeModuleCommand = new MakeModuleCommand;
17604
17716
  await makeModuleCommand.run({ name: module, silent: true });
17605
17717
  const makeEntityCommand = new MakeEntityCommand;
@@ -17634,26 +17746,26 @@ class MakeResourceStatusCommand {
17634
17746
  for (const controller of controllers) {
17635
17747
  await makeControllerCommand.run({ ...controller, module, isSocket: false });
17636
17748
  }
17637
- const controllersDir = join37(process.cwd(), base, "src", "controllers");
17638
- await Bun.write(join37(controllersDir, "CreateStatusController.ts"), CreateStatusController_default);
17639
- await Bun.write(join37(controllersDir, "GetStatusController.ts"), GetStatusController_default);
17640
- await Bun.write(join37(controllersDir, "ListStatusesController.ts"), ListStatusesController_default);
17641
- await Bun.write(join37(controllersDir, "UpdateStatusController.ts"), UpdateStatusController_default);
17642
- await Bun.write(join37(controllersDir, "DeleteStatusController.ts"), DeleteStatusController_default);
17749
+ const controllersDir = join38(process.cwd(), base, "src", "controllers");
17750
+ await Bun.write(join38(controllersDir, "CreateStatusController.ts"), CreateStatusController_default);
17751
+ await Bun.write(join38(controllersDir, "GetStatusController.ts"), GetStatusController_default);
17752
+ await Bun.write(join38(controllersDir, "ListStatusesController.ts"), ListStatusesController_default);
17753
+ await Bun.write(join38(controllersDir, "UpdateStatusController.ts"), UpdateStatusController_default);
17754
+ await Bun.write(join38(controllersDir, "DeleteStatusController.ts"), DeleteStatusController_default);
17643
17755
  const makeServiceCommand = new MakeServiceCommand;
17644
17756
  const services = ["CreateStatus", "GetStatus", "ListStatuses", "UpdateStatus", "DeleteStatus"];
17645
17757
  for (const name of services) {
17646
17758
  await makeServiceCommand.run({ name, module });
17647
17759
  }
17648
- const servicesDir = join37(process.cwd(), base, "src", "services");
17649
- await Bun.write(join37(servicesDir, "CreateStatusService.ts"), CreateStatusService_default);
17650
- await Bun.write(join37(servicesDir, "GetStatusService.ts"), GetStatusService_default);
17651
- await Bun.write(join37(servicesDir, "ListStatusesService.ts"), ListStatusesService_default);
17652
- await Bun.write(join37(servicesDir, "UpdateStatusService.ts"), UpdateStatusService_default);
17653
- await Bun.write(join37(servicesDir, "DeleteStatusService.ts"), DeleteStatusService_default);
17654
- const entityPath = join37(process.cwd(), base, "src", "entities", "StatusEntity.ts");
17760
+ const servicesDir = join38(process.cwd(), base, "src", "services");
17761
+ await Bun.write(join38(servicesDir, "CreateStatusService.ts"), CreateStatusService_default);
17762
+ await Bun.write(join38(servicesDir, "GetStatusService.ts"), GetStatusService_default);
17763
+ await Bun.write(join38(servicesDir, "ListStatusesService.ts"), ListStatusesService_default);
17764
+ await Bun.write(join38(servicesDir, "UpdateStatusService.ts"), UpdateStatusService_default);
17765
+ await Bun.write(join38(servicesDir, "DeleteStatusService.ts"), DeleteStatusService_default);
17766
+ const entityPath = join38(process.cwd(), base, "src", "entities", "StatusEntity.ts");
17655
17767
  await Bun.write(entityPath, StatusEntity_default);
17656
- const migrationsDir = join37(process.cwd(), base, "src", "migrations");
17768
+ const migrationsDir = join38(process.cwd(), base, "src", "migrations");
17657
17769
  const glob = new Glob9("Migration*.ts");
17658
17770
  for await (const file of glob.scan(migrationsDir)) {
17659
17771
  if (file === "migrations.ts")
@@ -17661,23 +17773,23 @@ class MakeResourceStatusCommand {
17661
17773
  const name = file.replace(/\.ts$/, "");
17662
17774
  const version = name.replace("Migration", "");
17663
17775
  const content = StatusMigration_default.replaceAll("{{ name }}", name).replaceAll("{{ version }}", version);
17664
- await Bun.write(join37(migrationsDir, file), content);
17776
+ await Bun.write(join38(migrationsDir, file), content);
17665
17777
  }
17666
- const repositoryPath = join37(process.cwd(), base, "src", "repositories", "StatusRepository.ts");
17778
+ const repositoryPath = join38(process.cwd(), base, "src", "repositories", "StatusRepository.ts");
17667
17779
  await Bun.write(repositoryPath, StatusRepository_default);
17668
17780
  const makeSeedCommand = new MakeSeedCommand;
17669
17781
  await makeSeedCommand.run({ name: "Status", module });
17670
- const seedsDir = join37(process.cwd(), base, "src", "seeds");
17671
- await Bun.write(join37(seedsDir, "StatusSeed.ts"), StatusSeed_default);
17672
- await Bun.write(join37(seedsDir, "status-seed.yml"), status_seed_default);
17782
+ const seedsDir = join38(process.cwd(), base, "src", "seeds");
17783
+ await Bun.write(join38(seedsDir, "StatusSeed.ts"), StatusSeed_default);
17784
+ await Bun.write(join38(seedsDir, "status-seed.yml"), status_seed_default);
17673
17785
  }
17674
17786
  }
17675
17787
  MakeResourceStatusCommand = __legacyDecorateClassTS([
17676
- decorator37.command()
17788
+ decorator38.command()
17677
17789
  ], MakeResourceStatusCommand);
17678
17790
  // src/commands/MakeResourceTagCommand.ts
17679
- import { join as join38 } from "path";
17680
- import { decorator as decorator38 } from "@ooneex/command";
17791
+ import { join as join39 } from "path";
17792
+ import { decorator as decorator39 } from "@ooneex/command";
17681
17793
  var {Glob: Glob10 } = globalThis.Bun;
17682
17794
 
17683
17795
  // src/templates/resources/tag/controllers/CreateTagController.txt
@@ -18268,7 +18380,7 @@ class MakeResourceTagCommand {
18268
18380
  }
18269
18381
  async run() {
18270
18382
  const module = "tag";
18271
- const base = join38("modules", module);
18383
+ const base = join39("modules", module);
18272
18384
  const makeModuleCommand = new MakeModuleCommand;
18273
18385
  await makeModuleCommand.run({ name: module, silent: true });
18274
18386
  const makeEntityCommand = new MakeEntityCommand;
@@ -18303,26 +18415,26 @@ class MakeResourceTagCommand {
18303
18415
  for (const controller of controllers) {
18304
18416
  await makeControllerCommand.run({ ...controller, module, isSocket: false });
18305
18417
  }
18306
- const controllersDir = join38(process.cwd(), base, "src", "controllers");
18307
- await Bun.write(join38(controllersDir, "CreateTagController.ts"), CreateTagController_default);
18308
- await Bun.write(join38(controllersDir, "GetTagController.ts"), GetTagController_default);
18309
- await Bun.write(join38(controllersDir, "ListTagsController.ts"), ListTagsController_default);
18310
- await Bun.write(join38(controllersDir, "UpdateTagController.ts"), UpdateTagController_default);
18311
- await Bun.write(join38(controllersDir, "DeleteTagController.ts"), DeleteTagController_default);
18418
+ const controllersDir = join39(process.cwd(), base, "src", "controllers");
18419
+ await Bun.write(join39(controllersDir, "CreateTagController.ts"), CreateTagController_default);
18420
+ await Bun.write(join39(controllersDir, "GetTagController.ts"), GetTagController_default);
18421
+ await Bun.write(join39(controllersDir, "ListTagsController.ts"), ListTagsController_default);
18422
+ await Bun.write(join39(controllersDir, "UpdateTagController.ts"), UpdateTagController_default);
18423
+ await Bun.write(join39(controllersDir, "DeleteTagController.ts"), DeleteTagController_default);
18312
18424
  const makeServiceCommand = new MakeServiceCommand;
18313
18425
  const services = ["CreateTag", "GetTag", "ListTags", "UpdateTag", "DeleteTag"];
18314
18426
  for (const name of services) {
18315
18427
  await makeServiceCommand.run({ name, module });
18316
18428
  }
18317
- const servicesDir = join38(process.cwd(), base, "src", "services");
18318
- await Bun.write(join38(servicesDir, "CreateTagService.ts"), CreateTagService_default);
18319
- await Bun.write(join38(servicesDir, "GetTagService.ts"), GetTagService_default);
18320
- await Bun.write(join38(servicesDir, "ListTagsService.ts"), ListTagsService_default);
18321
- await Bun.write(join38(servicesDir, "UpdateTagService.ts"), UpdateTagService_default);
18322
- await Bun.write(join38(servicesDir, "DeleteTagService.ts"), DeleteTagService_default);
18323
- const entityPath = join38(process.cwd(), base, "src", "entities", "TagEntity.ts");
18429
+ const servicesDir = join39(process.cwd(), base, "src", "services");
18430
+ await Bun.write(join39(servicesDir, "CreateTagService.ts"), CreateTagService_default);
18431
+ await Bun.write(join39(servicesDir, "GetTagService.ts"), GetTagService_default);
18432
+ await Bun.write(join39(servicesDir, "ListTagsService.ts"), ListTagsService_default);
18433
+ await Bun.write(join39(servicesDir, "UpdateTagService.ts"), UpdateTagService_default);
18434
+ await Bun.write(join39(servicesDir, "DeleteTagService.ts"), DeleteTagService_default);
18435
+ const entityPath = join39(process.cwd(), base, "src", "entities", "TagEntity.ts");
18324
18436
  await Bun.write(entityPath, TagEntity_default);
18325
- const migrationsDir = join38(process.cwd(), base, "src", "migrations");
18437
+ const migrationsDir = join39(process.cwd(), base, "src", "migrations");
18326
18438
  const glob = new Glob10("Migration*.ts");
18327
18439
  for await (const file of glob.scan(migrationsDir)) {
18328
18440
  if (file === "migrations.ts")
@@ -18330,18 +18442,18 @@ class MakeResourceTagCommand {
18330
18442
  const name = file.replace(/\.ts$/, "");
18331
18443
  const version = name.replace("Migration", "");
18332
18444
  const content = TagMigration_default.replaceAll("{{ name }}", name).replaceAll("{{ version }}", version);
18333
- await Bun.write(join38(migrationsDir, file), content);
18445
+ await Bun.write(join39(migrationsDir, file), content);
18334
18446
  }
18335
- const repositoryPath = join38(process.cwd(), base, "src", "repositories", "TagRepository.ts");
18447
+ const repositoryPath = join39(process.cwd(), base, "src", "repositories", "TagRepository.ts");
18336
18448
  await Bun.write(repositoryPath, TagRepository_default);
18337
18449
  }
18338
18450
  }
18339
18451
  MakeResourceTagCommand = __legacyDecorateClassTS([
18340
- decorator38.command()
18452
+ decorator39.command()
18341
18453
  ], MakeResourceTagCommand);
18342
18454
  // src/commands/MakeResourceTaskCommand.ts
18343
- import { join as join39 } from "path";
18344
- import { decorator as decorator39 } from "@ooneex/command";
18455
+ import { join as join40 } from "path";
18456
+ import { decorator as decorator40 } from "@ooneex/command";
18345
18457
  var {Glob: Glob11 } = globalThis.Bun;
18346
18458
 
18347
18459
  // src/templates/resources/task/controllers/CreateTaskController.txt
@@ -19014,7 +19126,7 @@ class MakeResourceTaskCommand {
19014
19126
  }
19015
19127
  async run() {
19016
19128
  const module = "task";
19017
- const base = join39("modules", module);
19129
+ const base = join40("modules", module);
19018
19130
  const makeModuleCommand = new MakeModuleCommand;
19019
19131
  await makeModuleCommand.run({ name: module, silent: true });
19020
19132
  const makeEntityCommand = new MakeEntityCommand;
@@ -19049,26 +19161,26 @@ class MakeResourceTaskCommand {
19049
19161
  for (const controller of controllers) {
19050
19162
  await makeControllerCommand.run({ ...controller, module, isSocket: false });
19051
19163
  }
19052
- const controllersDir = join39(process.cwd(), base, "src", "controllers");
19053
- await Bun.write(join39(controllersDir, "CreateTaskController.ts"), CreateTaskController_default);
19054
- await Bun.write(join39(controllersDir, "GetTaskController.ts"), GetTaskController_default);
19055
- await Bun.write(join39(controllersDir, "ListTasksController.ts"), ListTasksController_default);
19056
- await Bun.write(join39(controllersDir, "UpdateTaskController.ts"), UpdateTaskController_default);
19057
- await Bun.write(join39(controllersDir, "DeleteTaskController.ts"), DeleteTaskController_default);
19164
+ const controllersDir = join40(process.cwd(), base, "src", "controllers");
19165
+ await Bun.write(join40(controllersDir, "CreateTaskController.ts"), CreateTaskController_default);
19166
+ await Bun.write(join40(controllersDir, "GetTaskController.ts"), GetTaskController_default);
19167
+ await Bun.write(join40(controllersDir, "ListTasksController.ts"), ListTasksController_default);
19168
+ await Bun.write(join40(controllersDir, "UpdateTaskController.ts"), UpdateTaskController_default);
19169
+ await Bun.write(join40(controllersDir, "DeleteTaskController.ts"), DeleteTaskController_default);
19058
19170
  const makeServiceCommand = new MakeServiceCommand;
19059
19171
  const services = ["CreateTask", "GetTask", "ListTasks", "UpdateTask", "DeleteTask"];
19060
19172
  for (const name of services) {
19061
19173
  await makeServiceCommand.run({ name, module });
19062
19174
  }
19063
- const servicesDir = join39(process.cwd(), base, "src", "services");
19064
- await Bun.write(join39(servicesDir, "CreateTaskService.ts"), CreateTaskService_default);
19065
- await Bun.write(join39(servicesDir, "GetTaskService.ts"), GetTaskService_default);
19066
- await Bun.write(join39(servicesDir, "ListTasksService.ts"), ListTasksService_default);
19067
- await Bun.write(join39(servicesDir, "UpdateTaskService.ts"), UpdateTaskService_default);
19068
- await Bun.write(join39(servicesDir, "DeleteTaskService.ts"), DeleteTaskService_default);
19069
- const entityPath = join39(process.cwd(), base, "src", "entities", "TaskEntity.ts");
19175
+ const servicesDir = join40(process.cwd(), base, "src", "services");
19176
+ await Bun.write(join40(servicesDir, "CreateTaskService.ts"), CreateTaskService_default);
19177
+ await Bun.write(join40(servicesDir, "GetTaskService.ts"), GetTaskService_default);
19178
+ await Bun.write(join40(servicesDir, "ListTasksService.ts"), ListTasksService_default);
19179
+ await Bun.write(join40(servicesDir, "UpdateTaskService.ts"), UpdateTaskService_default);
19180
+ await Bun.write(join40(servicesDir, "DeleteTaskService.ts"), DeleteTaskService_default);
19181
+ const entityPath = join40(process.cwd(), base, "src", "entities", "TaskEntity.ts");
19070
19182
  await Bun.write(entityPath, TaskEntity_default);
19071
- const migrationsDir = join39(process.cwd(), base, "src", "migrations");
19183
+ const migrationsDir = join40(process.cwd(), base, "src", "migrations");
19072
19184
  const glob = new Glob11("Migration*.ts");
19073
19185
  for await (const file of glob.scan(migrationsDir)) {
19074
19186
  if (file === "migrations.ts")
@@ -19076,18 +19188,18 @@ class MakeResourceTaskCommand {
19076
19188
  const name = file.replace(/\.ts$/, "");
19077
19189
  const version = name.replace("Migration", "");
19078
19190
  const content = TaskMigration_default.replaceAll("{{ name }}", name).replaceAll("{{ version }}", version);
19079
- await Bun.write(join39(migrationsDir, file), content);
19191
+ await Bun.write(join40(migrationsDir, file), content);
19080
19192
  }
19081
- const repositoryPath = join39(process.cwd(), base, "src", "repositories", "TaskRepository.ts");
19193
+ const repositoryPath = join40(process.cwd(), base, "src", "repositories", "TaskRepository.ts");
19082
19194
  await Bun.write(repositoryPath, TaskRepository_default);
19083
19195
  }
19084
19196
  }
19085
19197
  MakeResourceTaskCommand = __legacyDecorateClassTS([
19086
- decorator39.command()
19198
+ decorator40.command()
19087
19199
  ], MakeResourceTaskCommand);
19088
19200
  // src/commands/MakeResourceTopicCommand.ts
19089
- import { join as join40 } from "path";
19090
- import { decorator as decorator40 } from "@ooneex/command";
19201
+ import { join as join41 } from "path";
19202
+ import { decorator as decorator41 } from "@ooneex/command";
19091
19203
  var {Glob: Glob12 } = globalThis.Bun;
19092
19204
 
19093
19205
  // src/templates/resources/topic/controllers/CreateTopicController.txt
@@ -19678,7 +19790,7 @@ class MakeResourceTopicCommand {
19678
19790
  }
19679
19791
  async run() {
19680
19792
  const module = "topic";
19681
- const base = join40("modules", module);
19793
+ const base = join41("modules", module);
19682
19794
  const makeModuleCommand = new MakeModuleCommand;
19683
19795
  await makeModuleCommand.run({ name: module, silent: true });
19684
19796
  const makeEntityCommand = new MakeEntityCommand;
@@ -19713,26 +19825,26 @@ class MakeResourceTopicCommand {
19713
19825
  for (const controller of controllers) {
19714
19826
  await makeControllerCommand.run({ ...controller, module, isSocket: false });
19715
19827
  }
19716
- const controllersDir = join40(process.cwd(), base, "src", "controllers");
19717
- await Bun.write(join40(controllersDir, "CreateTopicController.ts"), CreateTopicController_default);
19718
- await Bun.write(join40(controllersDir, "GetTopicController.ts"), GetTopicController_default);
19719
- await Bun.write(join40(controllersDir, "ListTopicsController.ts"), ListTopicsController_default);
19720
- await Bun.write(join40(controllersDir, "UpdateTopicController.ts"), UpdateTopicController_default);
19721
- await Bun.write(join40(controllersDir, "DeleteTopicController.ts"), DeleteTopicController_default);
19828
+ const controllersDir = join41(process.cwd(), base, "src", "controllers");
19829
+ await Bun.write(join41(controllersDir, "CreateTopicController.ts"), CreateTopicController_default);
19830
+ await Bun.write(join41(controllersDir, "GetTopicController.ts"), GetTopicController_default);
19831
+ await Bun.write(join41(controllersDir, "ListTopicsController.ts"), ListTopicsController_default);
19832
+ await Bun.write(join41(controllersDir, "UpdateTopicController.ts"), UpdateTopicController_default);
19833
+ await Bun.write(join41(controllersDir, "DeleteTopicController.ts"), DeleteTopicController_default);
19722
19834
  const makeServiceCommand = new MakeServiceCommand;
19723
19835
  const services = ["CreateTopic", "GetTopic", "ListTopics", "UpdateTopic", "DeleteTopic"];
19724
19836
  for (const name of services) {
19725
19837
  await makeServiceCommand.run({ name, module });
19726
19838
  }
19727
- const servicesDir = join40(process.cwd(), base, "src", "services");
19728
- await Bun.write(join40(servicesDir, "CreateTopicService.ts"), CreateTopicService_default);
19729
- await Bun.write(join40(servicesDir, "GetTopicService.ts"), GetTopicService_default);
19730
- await Bun.write(join40(servicesDir, "ListTopicsService.ts"), ListTopicsService_default);
19731
- await Bun.write(join40(servicesDir, "UpdateTopicService.ts"), UpdateTopicService_default);
19732
- await Bun.write(join40(servicesDir, "DeleteTopicService.ts"), DeleteTopicService_default);
19733
- const entityPath = join40(process.cwd(), base, "src", "entities", "TopicEntity.ts");
19839
+ const servicesDir = join41(process.cwd(), base, "src", "services");
19840
+ await Bun.write(join41(servicesDir, "CreateTopicService.ts"), CreateTopicService_default);
19841
+ await Bun.write(join41(servicesDir, "GetTopicService.ts"), GetTopicService_default);
19842
+ await Bun.write(join41(servicesDir, "ListTopicsService.ts"), ListTopicsService_default);
19843
+ await Bun.write(join41(servicesDir, "UpdateTopicService.ts"), UpdateTopicService_default);
19844
+ await Bun.write(join41(servicesDir, "DeleteTopicService.ts"), DeleteTopicService_default);
19845
+ const entityPath = join41(process.cwd(), base, "src", "entities", "TopicEntity.ts");
19734
19846
  await Bun.write(entityPath, TopicEntity_default);
19735
- const migrationsDir = join40(process.cwd(), base, "src", "migrations");
19847
+ const migrationsDir = join41(process.cwd(), base, "src", "migrations");
19736
19848
  const glob = new Glob12("Migration*.ts");
19737
19849
  for await (const file of glob.scan(migrationsDir)) {
19738
19850
  if (file === "migrations.ts")
@@ -19740,18 +19852,18 @@ class MakeResourceTopicCommand {
19740
19852
  const name = file.replace(/\.ts$/, "");
19741
19853
  const version = name.replace("Migration", "");
19742
19854
  const content = TopicMigration_default.replaceAll("{{ name }}", name).replaceAll("{{ version }}", version);
19743
- await Bun.write(join40(migrationsDir, file), content);
19855
+ await Bun.write(join41(migrationsDir, file), content);
19744
19856
  }
19745
- const repositoryPath = join40(process.cwd(), base, "src", "repositories", "TopicRepository.ts");
19857
+ const repositoryPath = join41(process.cwd(), base, "src", "repositories", "TopicRepository.ts");
19746
19858
  await Bun.write(repositoryPath, TopicRepository_default);
19747
19859
  }
19748
19860
  }
19749
19861
  MakeResourceTopicCommand = __legacyDecorateClassTS([
19750
- decorator40.command()
19862
+ decorator41.command()
19751
19863
  ], MakeResourceTopicCommand);
19752
19864
  // src/commands/MakeResourceUserCommand.ts
19753
- import { join as join41 } from "path";
19754
- import { decorator as decorator41 } from "@ooneex/command";
19865
+ import { join as join42 } from "path";
19866
+ import { decorator as decorator42 } from "@ooneex/command";
19755
19867
  var {Glob: Glob13 } = globalThis.Bun;
19756
19868
 
19757
19869
  // src/templates/resources/user/controllers/BanUserController.txt
@@ -20572,7 +20684,7 @@ class MakeResourceUserCommand {
20572
20684
  }
20573
20685
  async run() {
20574
20686
  const module = "user";
20575
- const base = join41("modules", module);
20687
+ const base = join42("modules", module);
20576
20688
  const makeModuleCommand = new MakeModuleCommand;
20577
20689
  await makeModuleCommand.run({ name: module, silent: true });
20578
20690
  const makeEntityCommand = new MakeEntityCommand;
@@ -20623,14 +20735,14 @@ class MakeResourceUserCommand {
20623
20735
  for (const controller of controllers) {
20624
20736
  await makeControllerCommand.run({ ...controller, module, isSocket: false });
20625
20737
  }
20626
- const controllersDir = join41(process.cwd(), base, "src", "controllers");
20627
- await Bun.write(join41(controllersDir, "BanUserController.ts"), BanUserController_default);
20628
- await Bun.write(join41(controllersDir, "LockUserController.ts"), LockUserController_default);
20629
- await Bun.write(join41(controllersDir, "SignOutController.ts"), SignOutController_default);
20630
- await Bun.write(join41(controllersDir, "UpdateUserProfileController.ts"), UpdateUserProfileController_default);
20631
- await Bun.write(join41(controllersDir, "UpdateUserProfileImageController.ts"), UpdateUserProfileImageController_default);
20632
- await Bun.write(join41(controllersDir, "DeleteUserProfileImageController.ts"), DeleteUserProfileImageController_default);
20633
- await Bun.write(join41(controllersDir, "UpdateUserRolesController.ts"), UpdateUserRolesController_default);
20738
+ const controllersDir = join42(process.cwd(), base, "src", "controllers");
20739
+ await Bun.write(join42(controllersDir, "BanUserController.ts"), BanUserController_default);
20740
+ await Bun.write(join42(controllersDir, "LockUserController.ts"), LockUserController_default);
20741
+ await Bun.write(join42(controllersDir, "SignOutController.ts"), SignOutController_default);
20742
+ await Bun.write(join42(controllersDir, "UpdateUserProfileController.ts"), UpdateUserProfileController_default);
20743
+ await Bun.write(join42(controllersDir, "UpdateUserProfileImageController.ts"), UpdateUserProfileImageController_default);
20744
+ await Bun.write(join42(controllersDir, "DeleteUserProfileImageController.ts"), DeleteUserProfileImageController_default);
20745
+ await Bun.write(join42(controllersDir, "UpdateUserRolesController.ts"), UpdateUserRolesController_default);
20634
20746
  const makeServiceCommand = new MakeServiceCommand;
20635
20747
  const services = [
20636
20748
  "BanUser",
@@ -20644,17 +20756,17 @@ class MakeResourceUserCommand {
20644
20756
  for (const name of services) {
20645
20757
  await makeServiceCommand.run({ name, module });
20646
20758
  }
20647
- const servicesDir = join41(process.cwd(), base, "src", "services");
20648
- await Bun.write(join41(servicesDir, "BanUserService.ts"), BanUserService_default);
20649
- await Bun.write(join41(servicesDir, "LockUserService.ts"), LockUserService_default);
20650
- await Bun.write(join41(servicesDir, "SignOutService.ts"), SignOutService_default);
20651
- await Bun.write(join41(servicesDir, "UpdateUserProfileService.ts"), UpdateUserProfileService_default);
20652
- await Bun.write(join41(servicesDir, "UpdateUserProfileImageService.ts"), UpdateUserProfileImageService_default);
20653
- await Bun.write(join41(servicesDir, "DeleteUserProfileImageService.ts"), DeleteUserProfileImageService_default);
20654
- await Bun.write(join41(servicesDir, "UpdateUserRolesService.ts"), UpdateUserRolesService_default);
20655
- const entityPath = join41(process.cwd(), base, "src", "entities", "UserEntity.ts");
20759
+ const servicesDir = join42(process.cwd(), base, "src", "services");
20760
+ await Bun.write(join42(servicesDir, "BanUserService.ts"), BanUserService_default);
20761
+ await Bun.write(join42(servicesDir, "LockUserService.ts"), LockUserService_default);
20762
+ await Bun.write(join42(servicesDir, "SignOutService.ts"), SignOutService_default);
20763
+ await Bun.write(join42(servicesDir, "UpdateUserProfileService.ts"), UpdateUserProfileService_default);
20764
+ await Bun.write(join42(servicesDir, "UpdateUserProfileImageService.ts"), UpdateUserProfileImageService_default);
20765
+ await Bun.write(join42(servicesDir, "DeleteUserProfileImageService.ts"), DeleteUserProfileImageService_default);
20766
+ await Bun.write(join42(servicesDir, "UpdateUserRolesService.ts"), UpdateUserRolesService_default);
20767
+ const entityPath = join42(process.cwd(), base, "src", "entities", "UserEntity.ts");
20656
20768
  await Bun.write(entityPath, UserEntity_default);
20657
- const migrationsDir = join41(process.cwd(), base, "src", "migrations");
20769
+ const migrationsDir = join42(process.cwd(), base, "src", "migrations");
20658
20770
  const glob = new Glob13("Migration*.ts");
20659
20771
  for await (const file of glob.scan(migrationsDir)) {
20660
20772
  if (file === "migrations.ts")
@@ -20662,18 +20774,18 @@ class MakeResourceUserCommand {
20662
20774
  const name = file.replace(/\.ts$/, "");
20663
20775
  const version = name.replace("Migration", "");
20664
20776
  const content = UserMigration_default.replaceAll("{{ name }}", name).replaceAll("{{ version }}", version);
20665
- await Bun.write(join41(migrationsDir, file), content);
20777
+ await Bun.write(join42(migrationsDir, file), content);
20666
20778
  }
20667
- const repositoryPath = join41(process.cwd(), base, "src", "repositories", "UserRepository.ts");
20779
+ const repositoryPath = join42(process.cwd(), base, "src", "repositories", "UserRepository.ts");
20668
20780
  await Bun.write(repositoryPath, UserRepository_default);
20669
20781
  }
20670
20782
  }
20671
20783
  MakeResourceUserCommand = __legacyDecorateClassTS([
20672
- decorator41.command()
20784
+ decorator42.command()
20673
20785
  ], MakeResourceUserCommand);
20674
20786
  // src/commands/MakeResourceVideoCommand.ts
20675
- import { join as join42 } from "path";
20676
- import { decorator as decorator42 } from "@ooneex/command";
20787
+ import { join as join43 } from "path";
20788
+ import { decorator as decorator43 } from "@ooneex/command";
20677
20789
  var {Glob: Glob14 } = globalThis.Bun;
20678
20790
 
20679
20791
  // src/templates/resources/video/controllers/CreateVideoController.txt
@@ -21352,7 +21464,7 @@ class MakeResourceVideoCommand {
21352
21464
  }
21353
21465
  async run() {
21354
21466
  const module = "video";
21355
- const base = join42("modules", module);
21467
+ const base = join43("modules", module);
21356
21468
  const makeModuleCommand = new MakeModuleCommand;
21357
21469
  await makeModuleCommand.run({ name: module, silent: true });
21358
21470
  const makeEntityCommand = new MakeEntityCommand;
@@ -21387,26 +21499,26 @@ class MakeResourceVideoCommand {
21387
21499
  for (const controller of controllers) {
21388
21500
  await makeControllerCommand.run({ ...controller, module, isSocket: false });
21389
21501
  }
21390
- const controllersDir = join42(process.cwd(), base, "src", "controllers");
21391
- await Bun.write(join42(controllersDir, "CreateVideoController.ts"), CreateVideoController_default);
21392
- await Bun.write(join42(controllersDir, "GetVideoController.ts"), GetVideoController_default);
21393
- await Bun.write(join42(controllersDir, "ListVideosController.ts"), ListVideosController_default);
21394
- await Bun.write(join42(controllersDir, "UpdateVideoController.ts"), UpdateVideoController_default);
21395
- await Bun.write(join42(controllersDir, "DeleteVideoController.ts"), DeleteVideoController_default);
21502
+ const controllersDir = join43(process.cwd(), base, "src", "controllers");
21503
+ await Bun.write(join43(controllersDir, "CreateVideoController.ts"), CreateVideoController_default);
21504
+ await Bun.write(join43(controllersDir, "GetVideoController.ts"), GetVideoController_default);
21505
+ await Bun.write(join43(controllersDir, "ListVideosController.ts"), ListVideosController_default);
21506
+ await Bun.write(join43(controllersDir, "UpdateVideoController.ts"), UpdateVideoController_default);
21507
+ await Bun.write(join43(controllersDir, "DeleteVideoController.ts"), DeleteVideoController_default);
21396
21508
  const makeServiceCommand = new MakeServiceCommand;
21397
21509
  const services = ["CreateVideo", "GetVideo", "ListVideos", "UpdateVideo", "DeleteVideo"];
21398
21510
  for (const name of services) {
21399
21511
  await makeServiceCommand.run({ name, module });
21400
21512
  }
21401
- const servicesDir = join42(process.cwd(), base, "src", "services");
21402
- await Bun.write(join42(servicesDir, "CreateVideoService.ts"), CreateVideoService_default);
21403
- await Bun.write(join42(servicesDir, "GetVideoService.ts"), GetVideoService_default);
21404
- await Bun.write(join42(servicesDir, "ListVideosService.ts"), ListVideosService_default);
21405
- await Bun.write(join42(servicesDir, "UpdateVideoService.ts"), UpdateVideoService_default);
21406
- await Bun.write(join42(servicesDir, "DeleteVideoService.ts"), DeleteVideoService_default);
21407
- const entityPath = join42(process.cwd(), base, "src", "entities", "VideoEntity.ts");
21513
+ const servicesDir = join43(process.cwd(), base, "src", "services");
21514
+ await Bun.write(join43(servicesDir, "CreateVideoService.ts"), CreateVideoService_default);
21515
+ await Bun.write(join43(servicesDir, "GetVideoService.ts"), GetVideoService_default);
21516
+ await Bun.write(join43(servicesDir, "ListVideosService.ts"), ListVideosService_default);
21517
+ await Bun.write(join43(servicesDir, "UpdateVideoService.ts"), UpdateVideoService_default);
21518
+ await Bun.write(join43(servicesDir, "DeleteVideoService.ts"), DeleteVideoService_default);
21519
+ const entityPath = join43(process.cwd(), base, "src", "entities", "VideoEntity.ts");
21408
21520
  await Bun.write(entityPath, VideoEntity_default);
21409
- const migrationsDir = join42(process.cwd(), base, "src", "migrations");
21521
+ const migrationsDir = join43(process.cwd(), base, "src", "migrations");
21410
21522
  const glob = new Glob14("Migration*.ts");
21411
21523
  for await (const file of glob.scan(migrationsDir)) {
21412
21524
  if (file === "migrations.ts")
@@ -21414,19 +21526,19 @@ class MakeResourceVideoCommand {
21414
21526
  const name = file.replace(/\.ts$/, "");
21415
21527
  const version = name.replace("Migration", "");
21416
21528
  const content = VideoMigration_default.replaceAll("{{ name }}", name).replaceAll("{{ version }}", version);
21417
- await Bun.write(join42(migrationsDir, file), content);
21529
+ await Bun.write(join43(migrationsDir, file), content);
21418
21530
  }
21419
- const repositoryPath = join42(process.cwd(), base, "src", "repositories", "VideoRepository.ts");
21531
+ const repositoryPath = join43(process.cwd(), base, "src", "repositories", "VideoRepository.ts");
21420
21532
  await Bun.write(repositoryPath, VideoRepository_default);
21421
21533
  }
21422
21534
  }
21423
21535
  MakeResourceVideoCommand = __legacyDecorateClassTS([
21424
- decorator42.command()
21536
+ decorator43.command()
21425
21537
  ], MakeResourceVideoCommand);
21426
21538
  // src/commands/MakeStorageCommand.ts
21427
- import { join as join43 } from "path";
21428
- import { decorator as decorator43 } from "@ooneex/command";
21429
- import { TerminalLogger as TerminalLogger28 } from "@ooneex/logger";
21539
+ import { join as join44 } from "path";
21540
+ import { decorator as decorator44 } from "@ooneex/command";
21541
+ import { TerminalLogger as TerminalLogger29 } from "@ooneex/logger";
21430
21542
  import { toPascalCase as toPascalCase16, toSnakeCase as toSnakeCase3 } from "@ooneex/utils";
21431
21543
 
21432
21544
  // src/templates/storage.test.txt
@@ -21526,28 +21638,28 @@ class MakeStorageCommand {
21526
21638
  if (module) {
21527
21639
  await ensureModule(module);
21528
21640
  }
21529
- const base = module ? join43("modules", module) : ".";
21530
- const storageLocalDir = join43(base, "src", "storage");
21531
- const storageDir = join43(process.cwd(), storageLocalDir);
21532
- const filePath = join43(storageDir, `${name}Storage.ts`);
21641
+ const base = module ? join44("modules", module) : ".";
21642
+ const storageLocalDir = join44(base, "src", "storage");
21643
+ const storageDir = join44(process.cwd(), storageLocalDir);
21644
+ const filePath = join44(storageDir, `${name}Storage.ts`);
21533
21645
  await Bun.write(filePath, content);
21534
21646
  const testContent = storage_test_default.replace(/{{NAME}}/g, name).replace(/{{MODULE}}/g, module ?? "");
21535
- const testsLocalDir = join43(base, "tests", "storage");
21536
- const testsDir = join43(process.cwd(), testsLocalDir);
21537
- const testFilePath = join43(testsDir, `${name}Storage.spec.ts`);
21647
+ const testsLocalDir = join44(base, "tests", "storage");
21648
+ const testsDir = join44(process.cwd(), testsLocalDir);
21649
+ const testFilePath = join44(testsDir, `${name}Storage.spec.ts`);
21538
21650
  await Bun.write(testFilePath, testContent);
21539
- const logger = new TerminalLogger28;
21540
- logger.success(`${join43(storageLocalDir, name)}Storage.ts created successfully`, undefined, {
21651
+ const logger = new TerminalLogger29;
21652
+ logger.success(`${join44(storageLocalDir, name)}Storage.ts created successfully`, undefined, {
21541
21653
  showTimestamp: false,
21542
21654
  showArrow: false,
21543
21655
  useSymbol: true
21544
21656
  });
21545
- logger.success(`${join43(testsLocalDir, name)}Storage.spec.ts created successfully`, undefined, {
21657
+ logger.success(`${join44(testsLocalDir, name)}Storage.spec.ts created successfully`, undefined, {
21546
21658
  showTimestamp: false,
21547
21659
  showArrow: false,
21548
21660
  useSymbol: true
21549
21661
  });
21550
- const packageJsonPath = join43(process.cwd(), "package.json");
21662
+ const packageJsonPath = join44(process.cwd(), "package.json");
21551
21663
  const packageJson = await Bun.file(packageJsonPath).json();
21552
21664
  const deps = packageJson.dependencies ?? {};
21553
21665
  const devDeps = packageJson.devDependencies ?? {};
@@ -21562,12 +21674,12 @@ class MakeStorageCommand {
21562
21674
  }
21563
21675
  }
21564
21676
  MakeStorageCommand = __legacyDecorateClassTS([
21565
- decorator43.command()
21677
+ decorator44.command()
21566
21678
  ], MakeStorageCommand);
21567
21679
  // src/commands/MakeVectorDatabaseCommand.ts
21568
- import { join as join44 } from "path";
21569
- import { decorator as decorator44 } from "@ooneex/command";
21570
- import { TerminalLogger as TerminalLogger29 } from "@ooneex/logger";
21680
+ import { join as join45 } from "path";
21681
+ import { decorator as decorator45 } from "@ooneex/command";
21682
+ import { TerminalLogger as TerminalLogger30 } from "@ooneex/logger";
21571
21683
  import { toPascalCase as toPascalCase17 } from "@ooneex/utils";
21572
21684
 
21573
21685
  // src/templates/vector-database.test.txt
@@ -21641,28 +21753,28 @@ class MakeVectorDatabaseCommand {
21641
21753
  if (module) {
21642
21754
  await ensureModule(module);
21643
21755
  }
21644
- const base = module ? join44("modules", module) : ".";
21645
- const vectorDatabaseLocalDir = join44(base, "src", "databases");
21646
- const vectorDatabaseDir = join44(process.cwd(), vectorDatabaseLocalDir);
21647
- const filePath = join44(vectorDatabaseDir, `${name}VectorDatabase.ts`);
21756
+ const base = module ? join45("modules", module) : ".";
21757
+ const vectorDatabaseLocalDir = join45(base, "src", "databases");
21758
+ const vectorDatabaseDir = join45(process.cwd(), vectorDatabaseLocalDir);
21759
+ const filePath = join45(vectorDatabaseDir, `${name}VectorDatabase.ts`);
21648
21760
  await Bun.write(filePath, content);
21649
21761
  const testContent = vector_database_test_default.replace(/{{NAME}}/g, name).replace(/{{MODULE}}/g, module ?? "");
21650
- const testsLocalDir = join44(base, "tests", "databases");
21651
- const testsDir = join44(process.cwd(), testsLocalDir);
21652
- const testFilePath = join44(testsDir, `${name}VectorDatabase.spec.ts`);
21762
+ const testsLocalDir = join45(base, "tests", "databases");
21763
+ const testsDir = join45(process.cwd(), testsLocalDir);
21764
+ const testFilePath = join45(testsDir, `${name}VectorDatabase.spec.ts`);
21653
21765
  await Bun.write(testFilePath, testContent);
21654
- const logger = new TerminalLogger29;
21655
- logger.success(`${join44(vectorDatabaseLocalDir, name)}VectorDatabase.ts created successfully`, undefined, {
21766
+ const logger = new TerminalLogger30;
21767
+ logger.success(`${join45(vectorDatabaseLocalDir, name)}VectorDatabase.ts created successfully`, undefined, {
21656
21768
  showTimestamp: false,
21657
21769
  showArrow: false,
21658
21770
  useSymbol: true
21659
21771
  });
21660
- logger.success(`${join44(testsLocalDir, name)}VectorDatabase.spec.ts created successfully`, undefined, {
21772
+ logger.success(`${join45(testsLocalDir, name)}VectorDatabase.spec.ts created successfully`, undefined, {
21661
21773
  showTimestamp: false,
21662
21774
  showArrow: false,
21663
21775
  useSymbol: true
21664
21776
  });
21665
- const packageJsonPath = join44(process.cwd(), "package.json");
21777
+ const packageJsonPath = join45(process.cwd(), "package.json");
21666
21778
  const packageJson = await Bun.file(packageJsonPath).json();
21667
21779
  const deps = packageJson.dependencies ?? {};
21668
21780
  const devDeps = packageJson.devDependencies ?? {};
@@ -21677,13 +21789,13 @@ class MakeVectorDatabaseCommand {
21677
21789
  }
21678
21790
  }
21679
21791
  MakeVectorDatabaseCommand = __legacyDecorateClassTS([
21680
- decorator44.command()
21792
+ decorator45.command()
21681
21793
  ], MakeVectorDatabaseCommand);
21682
21794
  // src/commands/MigrationUpCommand.ts
21683
21795
  import { existsSync as existsSync2 } from "fs";
21684
- import { join as join45 } from "path";
21685
- import { decorator as decorator45 } from "@ooneex/command";
21686
- import { TerminalLogger as TerminalLogger30 } from "@ooneex/logger";
21796
+ import { join as join46 } from "path";
21797
+ import { decorator as decorator46 } from "@ooneex/command";
21798
+ import { TerminalLogger as TerminalLogger31 } from "@ooneex/logger";
21687
21799
  class MigrationUpCommand {
21688
21800
  getName() {
21689
21801
  return "migration:up";
@@ -21692,8 +21804,8 @@ class MigrationUpCommand {
21692
21804
  return "Run migrations for all modules";
21693
21805
  }
21694
21806
  async run(options) {
21695
- const logger = new TerminalLogger30;
21696
- const modulesDir = join45(process.cwd(), "modules");
21807
+ const logger = new TerminalLogger31;
21808
+ const modulesDir = join46(process.cwd(), "modules");
21697
21809
  if (!existsSync2(modulesDir)) {
21698
21810
  logger.warn("No modules with migrations found", undefined, {
21699
21811
  showTimestamp: false,
@@ -21706,10 +21818,10 @@ class MigrationUpCommand {
21706
21818
  const modules = [];
21707
21819
  for await (const match of glob.scan({ cwd: modulesDir, onlyFiles: true })) {
21708
21820
  const entry = match.replace("/package.json", "");
21709
- const moduleDir = join45(modulesDir, entry);
21710
- const migrationUpFile = Bun.file(join45(moduleDir, "bin", "migration", "up.ts"));
21821
+ const moduleDir = join46(modulesDir, entry);
21822
+ const migrationUpFile = Bun.file(join46(moduleDir, "bin", "migration", "up.ts"));
21711
21823
  if (await migrationUpFile.exists()) {
21712
- const packageJson = await Bun.file(join45(modulesDir, match)).json();
21824
+ const packageJson = await Bun.file(join46(modulesDir, match)).json();
21713
21825
  modules.push({ name: packageJson.name ?? entry, dir: moduleDir });
21714
21826
  }
21715
21827
  }
@@ -21722,7 +21834,7 @@ class MigrationUpCommand {
21722
21834
  return;
21723
21835
  }
21724
21836
  for (const { name, dir } of modules) {
21725
- const migrationUpPath = join45(dir, "bin", "migration", "up.ts");
21837
+ const migrationUpPath = join46(dir, "bin", "migration", "up.ts");
21726
21838
  logger.info(`Running migrations for ${name}...`, undefined, {
21727
21839
  showTimestamp: false,
21728
21840
  showArrow: false,
@@ -21755,13 +21867,13 @@ class MigrationUpCommand {
21755
21867
  }
21756
21868
  }
21757
21869
  MigrationUpCommand = __legacyDecorateClassTS([
21758
- decorator45.command()
21870
+ decorator46.command()
21759
21871
  ], MigrationUpCommand);
21760
21872
  // src/commands/RemoveModuleCommand.ts
21761
21873
  import { rmdir } from "fs/promises";
21762
- import { join as join46 } from "path";
21763
- import { decorator as decorator46 } from "@ooneex/command";
21764
- import { TerminalLogger as TerminalLogger31 } from "@ooneex/logger";
21874
+ import { join as join47 } from "path";
21875
+ import { decorator as decorator47 } from "@ooneex/command";
21876
+ import { TerminalLogger as TerminalLogger32 } from "@ooneex/logger";
21765
21877
  import { toKebabCase as toKebabCase5, toPascalCase as toPascalCase18 } from "@ooneex/utils";
21766
21878
  class RemoveModuleCommand {
21767
21879
  getName() {
@@ -21832,7 +21944,7 @@ class RemoveModuleCommand {
21832
21944
  const kebabName = toKebabCase5(pascalName);
21833
21945
  if (kebabName === "app" || kebabName === "shared") {
21834
21946
  if (!silent) {
21835
- const logger = new TerminalLogger31;
21947
+ const logger = new TerminalLogger32;
21836
21948
  logger.error(`Cannot remove the "${kebabName}" module`, undefined, {
21837
21949
  showTimestamp: false,
21838
21950
  showArrow: false,
@@ -21841,11 +21953,11 @@ class RemoveModuleCommand {
21841
21953
  }
21842
21954
  return;
21843
21955
  }
21844
- const moduleDir = join46(cwd, "modules", kebabName);
21845
- const moduleDirExists = await Bun.file(join46(moduleDir, "package.json")).exists();
21956
+ const moduleDir = join47(cwd, "modules", kebabName);
21957
+ const moduleDirExists = await Bun.file(join47(moduleDir, "package.json")).exists();
21846
21958
  if (!moduleDirExists) {
21847
21959
  if (!silent) {
21848
- const logger = new TerminalLogger31;
21960
+ const logger = new TerminalLogger32;
21849
21961
  logger.error(`Module "${kebabName}" does not exist`, undefined, {
21850
21962
  showTimestamp: false,
21851
21963
  showArrow: false,
@@ -21862,17 +21974,17 @@ class RemoveModuleCommand {
21862
21974
  if (!confirmed)
21863
21975
  return;
21864
21976
  }
21865
- const appModulePath = join46(cwd, "modules", "app", "src", "AppModule.ts");
21977
+ const appModulePath = join47(cwd, "modules", "app", "src", "AppModule.ts");
21866
21978
  await this.removeFromAppModule(appModulePath, pascalName, kebabName);
21867
- const sharedModulePath = join46(cwd, "modules", "shared", "src", "SharedModule.ts");
21979
+ const sharedModulePath = join47(cwd, "modules", "shared", "src", "SharedModule.ts");
21868
21980
  await this.removeFromSharedModule(sharedModulePath, pascalName, kebabName);
21869
- const appTsconfigPath = join46(cwd, "tsconfig.json");
21981
+ const appTsconfigPath = join47(cwd, "tsconfig.json");
21870
21982
  await this.removePathAlias(appTsconfigPath, kebabName);
21871
- const commitlintPath = join46(cwd, ".commitlintrc.ts");
21983
+ const commitlintPath = join47(cwd, ".commitlintrc.ts");
21872
21984
  await this.removeModuleScope(commitlintPath, kebabName);
21873
21985
  await rmdir(moduleDir, { recursive: true });
21874
21986
  if (!silent) {
21875
- const logger = new TerminalLogger31;
21987
+ const logger = new TerminalLogger32;
21876
21988
  logger.success(`modules/${kebabName} removed successfully`, undefined, {
21877
21989
  showTimestamp: false,
21878
21990
  showArrow: false,
@@ -21882,13 +21994,13 @@ class RemoveModuleCommand {
21882
21994
  }
21883
21995
  }
21884
21996
  RemoveModuleCommand = __legacyDecorateClassTS([
21885
- decorator46.command()
21997
+ decorator47.command()
21886
21998
  ], RemoveModuleCommand);
21887
21999
  // src/commands/SeedRunCommand.ts
21888
22000
  import { existsSync as existsSync3 } from "fs";
21889
- import { join as join47 } from "path";
21890
- import { decorator as decorator47 } from "@ooneex/command";
21891
- import { TerminalLogger as TerminalLogger32 } from "@ooneex/logger";
22001
+ import { join as join48 } from "path";
22002
+ import { decorator as decorator48 } from "@ooneex/command";
22003
+ import { TerminalLogger as TerminalLogger33 } from "@ooneex/logger";
21892
22004
  class SeedRunCommand {
21893
22005
  getName() {
21894
22006
  return "seed:run";
@@ -21897,8 +22009,8 @@ class SeedRunCommand {
21897
22009
  return "Run seeds for all modules";
21898
22010
  }
21899
22011
  async run(options) {
21900
- const logger = new TerminalLogger32;
21901
- const modulesDir = join47(process.cwd(), "modules");
22012
+ const logger = new TerminalLogger33;
22013
+ const modulesDir = join48(process.cwd(), "modules");
21902
22014
  if (!existsSync3(modulesDir)) {
21903
22015
  logger.warn("No modules with seeds found", undefined, {
21904
22016
  showTimestamp: false,
@@ -21911,10 +22023,10 @@ class SeedRunCommand {
21911
22023
  const modules = [];
21912
22024
  for await (const match of glob.scan({ cwd: modulesDir, onlyFiles: true })) {
21913
22025
  const entry = match.replace("/package.json", "");
21914
- const moduleDir = join47(modulesDir, entry);
21915
- const seedRunFile = Bun.file(join47(moduleDir, "bin", "seed", "run.ts"));
22026
+ const moduleDir = join48(modulesDir, entry);
22027
+ const seedRunFile = Bun.file(join48(moduleDir, "bin", "seed", "run.ts"));
21916
22028
  if (await seedRunFile.exists()) {
21917
- const packageJson = await Bun.file(join47(modulesDir, match)).json();
22029
+ const packageJson = await Bun.file(join48(modulesDir, match)).json();
21918
22030
  modules.push({ name: packageJson.name ?? entry, dir: moduleDir });
21919
22031
  }
21920
22032
  }
@@ -21927,7 +22039,7 @@ class SeedRunCommand {
21927
22039
  return;
21928
22040
  }
21929
22041
  for (const { name, dir } of modules) {
21930
- const seedRunPath = join47(dir, "bin", "seed", "run.ts");
22042
+ const seedRunPath = join48(dir, "bin", "seed", "run.ts");
21931
22043
  logger.info(`Running seeds for ${name}...`, undefined, {
21932
22044
  showTimestamp: false,
21933
22045
  showArrow: false,
@@ -21960,9 +22072,9 @@ class SeedRunCommand {
21960
22072
  }
21961
22073
  }
21962
22074
  SeedRunCommand = __legacyDecorateClassTS([
21963
- decorator47.command()
22075
+ decorator48.command()
21964
22076
  ], SeedRunCommand);
21965
22077
  // src/index.ts
21966
22078
  await run();
21967
22079
 
21968
- //# debugId=C99AC9534779F36864756E2164756E21
22080
+ //# debugId=FFFA0F9ADF05768464756E2164756E21