@hacimertgokhan/next-audit 1.0.0 → 1.0.2

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.
@@ -0,0 +1,8 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <project version="4">
3
+ <component name="ProjectModuleManager">
4
+ <modules>
5
+ <module fileurl="file://$PROJECT_DIR$/.idea/next-audit.iml" filepath="$PROJECT_DIR$/.idea/next-audit.iml" />
6
+ </modules>
7
+ </component>
8
+ </project>
@@ -0,0 +1,12 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <module type="WEB_MODULE" version="4">
3
+ <component name="NewModuleRootManager">
4
+ <content url="file://$MODULE_DIR$">
5
+ <excludeFolder url="file://$MODULE_DIR$/.tmp" />
6
+ <excludeFolder url="file://$MODULE_DIR$/temp" />
7
+ <excludeFolder url="file://$MODULE_DIR$/tmp" />
8
+ </content>
9
+ <orderEntry type="inheritedJdk" />
10
+ <orderEntry type="sourceFolder" forTests="false" />
11
+ </component>
12
+ </module>
package/README.md ADDED
@@ -0,0 +1,58 @@
1
+ # @hacimertgokhan/next-audit
2
+
3
+ A lightweight TypeScript library for Next.js to audit backend requests automatically using decorators.
4
+
5
+ ## Features
6
+ - **Easy Integration**: Simply add the `@Audit()` decorator to your controller methods.
7
+ - **Automated Logging**: Captures method, URL, status code, execution duration, and IP.
8
+ - **Data Tracking**: Automatically records `old_value` (for DELETE) and `new_value` (for POST/PUT/PATCH).
9
+ - **MySQL Support**: Built-in adapter for MySQL storage.
10
+
11
+ ## Installation
12
+
13
+ ```bash
14
+ npm install @hacimertgokhan/next-audit mysql2 reflect-metadata
15
+ ```
16
+
17
+ ## Configuration
18
+
19
+ Create a `audit.conf.ts` file in your project root:
20
+
21
+ ```typescript
22
+ export default {
23
+ database: {
24
+ url: process.env.DATABASE_URL,
25
+ },
26
+
27
+ tableName: 'audit_logs',
28
+ debug: false
29
+ };
30
+ ```
31
+
32
+ ## Usage
33
+
34
+ Use the decorator in your Next.js API route class methods:
35
+
36
+ ```typescript
37
+ import { Audit } from '@hacimertgokhan/next-audit';
38
+ import { NextRequest, NextResponse } from 'next/server';
39
+
40
+ class UserController {
41
+
42
+ @Audit()
43
+ async createUser(req: NextRequest) {
44
+ const data = await req.json();
45
+
46
+ return NextResponse.json({ id: 1, ...data });
47
+ }
48
+
49
+ @Audit()
50
+ async deleteUser(req: NextRequest) {
51
+
52
+ return NextResponse.json({ success: true });
53
+ }
54
+ }
55
+ ```
56
+
57
+ ## License
58
+ ISC
package/dist/config.d.ts CHANGED
@@ -1,2 +1,11 @@
1
1
  import { AuditConfig } from './types';
2
+ /**
3
+ * Manually set the audit configuration.
4
+ * This is useful if you want to load config from a different source.
5
+ */
6
+ export declare function setAuditConfig(config: AuditConfig): void;
7
+ /**
8
+ * Loads the configuration from audit.conf.js or audit.conf.ts in the project root.
9
+ * Falls back to environment variables if no config file is found.
10
+ */
2
11
  export declare function loadConfig(): AuditConfig;
package/dist/config.js CHANGED
@@ -3,10 +3,22 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
3
3
  return (mod && mod.__esModule) ? mod : { "default": mod };
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.setAuditConfig = setAuditConfig;
6
7
  exports.loadConfig = loadConfig;
7
8
  const path_1 = __importDefault(require("path"));
8
9
  const fs_1 = __importDefault(require("fs"));
9
10
  let cachedConfig = null;
11
+ /**
12
+ * Manually set the audit configuration.
13
+ * This is useful if you want to load config from a different source.
14
+ */
15
+ function setAuditConfig(config) {
16
+ cachedConfig = config;
17
+ }
18
+ /**
19
+ * Loads the configuration from audit.conf.js or audit.conf.ts in the project root.
20
+ * Falls back to environment variables if no config file is found.
21
+ */
10
22
  function loadConfig() {
11
23
  if (cachedConfig)
12
24
  return cachedConfig;
@@ -14,42 +26,55 @@ function loadConfig() {
14
26
  const configPathTs = path_1.default.resolve(process.cwd(), 'audit.conf.ts');
15
27
  const configPathJs = path_1.default.resolve(process.cwd(), 'audit.conf.js');
16
28
  try {
17
- // Note: In a real compiled node_modules scenario, requiring a .ts file from cwd at runtime
18
- // requires ts-node or similar. We will assume the user has a setup that handles this,
19
- // or they compile it to .js. For this MVP, we try to require it.
20
- // If it fails, we look for defaults or env vars.
29
+ // Use eval('require') to bypass Webpack's static analysis in Next.js
30
+ // This allows loading a file from the disk at runtime.
31
+ const safeRequire = (p) => {
32
+ try {
33
+ // eslint-disable-next-line no-eval
34
+ return eval('require')(p);
35
+ }
36
+ catch (e) {
37
+ // Fallback for non-webpack environments
38
+ if (typeof require !== 'undefined') {
39
+ // eslint-disable-next-line @typescript-eslint/no-var-requires
40
+ return require(p);
41
+ }
42
+ throw e;
43
+ }
44
+ };
21
45
  if (fs_1.default.existsSync(configPathJs)) {
22
- // eslint-disable-next-line @typescript-eslint/no-var-requires
23
- const userConfig = require(configPathJs);
46
+ const userConfig = safeRequire(configPathJs);
24
47
  cachedConfig = userConfig.default || userConfig;
25
48
  }
26
49
  else if (fs_1.default.existsSync(configPathTs)) {
27
- // Only works if running via ts-node or if we invoke a transpiler on fly
28
- // For now, we warn or rely on an environment that supports it.
29
50
  try {
30
- // eslint-disable-next-line @typescript-eslint/no-var-requires
31
- require('ts-node/register'); // Try to register if possible? Risky for a lib.
32
- // eslint-disable-next-line @typescript-eslint/no-var-requires
33
- const userConfig = require(configPathTs);
51
+ // Try to register ts-node if it's available for .ts config files
52
+ try {
53
+ safeRequire('ts-node/register');
54
+ }
55
+ catch (e) {
56
+ // ts-node not available, hope for the best or it might already be registered
57
+ }
58
+ const userConfig = safeRequire(configPathTs);
34
59
  cachedConfig = userConfig.default || userConfig;
35
60
  }
36
61
  catch (e) {
37
- console.warn('[Next-Audit] Found audit.conf.ts but could not load it directly (ts-node missing?). Please ensure it is compiled to js or use audit.conf.js');
62
+ console.warn('[Next-Audit] Found audit.conf.ts but could not load it directly. Please ensure it is compiled to js or use audit.conf.js');
38
63
  }
39
64
  }
40
65
  }
41
66
  catch (error) {
67
+ // Only log if it's not a "module not found" error for the config files themselves
68
+ // but wait, we already check fs.existsSync, so any error here is likely real.
42
69
  console.error('[Next-Audit] Error loading config:', error);
43
70
  }
44
71
  if (!cachedConfig) {
45
- // Fallback: minimal valid config or throw
46
- // For MVP we assume environment variables might also be used
72
+ // Fallback: environment variables
47
73
  cachedConfig = {
48
74
  database: {
49
75
  url: process.env.DATABASE_URL
50
- // default to local if nothing found?
51
76
  },
52
- debug: true
77
+ debug: !!process.env.AUDIT_DEBUG
53
78
  };
54
79
  if (!process.env.DATABASE_URL) {
55
80
  console.warn('[Next-Audit] No config file found and DATABASE_URL not set. Audit might fail.');
@@ -11,39 +11,30 @@ function Audit(options) {
11
11
  const startTime = Date.now();
12
12
  let req;
13
13
  let context;
14
- // Try to find NextRequest in arguments
15
14
  for (const arg of args) {
16
15
  if (arg instanceof Request || (arg && arg.constructor && arg.constructor.name === 'NextRequest')) {
17
16
  req = arg;
18
17
  break;
19
18
  }
20
19
  }
21
- // Capture inputs
22
20
  const url = req?.url || '';
23
21
  const method = req?.method || 'UNKNOWN';
24
22
  const userAgent = req?.headers.get('user-agent') || '';
25
- // TODO: Extract user ID from session/token if available in headers or args
26
- let oldData = null; // For delete operations, we hope to capture this
23
+ let oldData = null;
27
24
  let newData = null;
28
- // Execute original method
29
25
  let result;
30
26
  let status = 200;
31
27
  let errorOccurred = false;
32
28
  try {
33
29
  result = await originalMethod.apply(this, args);
34
- // Analyze Result
35
30
  if (result instanceof Response) {
36
31
  status = result.status;
37
- // Clone to read body without consuming the original stream if possible,
38
- // or assume we can't read it if it's a stream already locked.
39
- // For JSON responses, we might want to peek.
40
32
  try {
41
33
  const clone = result.clone();
42
34
  const bodyText = await clone.text();
43
35
  if (bodyText) {
44
36
  try {
45
37
  const json = JSON.parse(bodyText);
46
- // If DELETE, assume result is the deleted data or confirmation
47
38
  if (method === 'DELETE') {
48
39
  oldData = json;
49
40
  }
@@ -52,16 +43,13 @@ function Audit(options) {
52
43
  }
53
44
  }
54
45
  catch (e) {
55
- // Not JSON
56
46
  }
57
47
  }
58
48
  }
59
49
  catch (e) {
60
- // Failed to read response body
61
50
  }
62
51
  }
63
52
  else {
64
- // If the function returns a plain object (not a Response), treat it as data
65
53
  if (method === 'DELETE') {
66
54
  oldData = result;
67
55
  }
@@ -73,7 +61,6 @@ function Audit(options) {
73
61
  catch (error) {
74
62
  errorOccurred = true;
75
63
  status = 500;
76
- // Try to get status from error if available
77
64
  if (error.status)
78
65
  status = error.status;
79
66
  if (error.statusCode)
@@ -94,12 +81,7 @@ function Audit(options) {
94
81
  user_agent: userAgent,
95
82
  old_value: oldData,
96
83
  new_value: newData,
97
- // user_id, entity, entity_id need more specific logic or config to extract
98
84
  };
99
- // Fire and forget audit log to not block response?
100
- // Or await it? User might want reliability.
101
- // We'll await it for now to ensure it's logged,
102
- // but catching errors so we don't break the app if logging fails.
103
85
  (0, mysql_1.saveAuditLog)(entry, config).catch(e => console.error('Audit Log Error', e));
104
86
  }
105
87
  }
package/dist/index.js CHANGED
@@ -17,4 +17,3 @@ Object.defineProperty(exports, "__esModule", { value: true });
17
17
  __exportStar(require("./decorators/audit"), exports);
18
18
  __exportStar(require("./types"), exports);
19
19
  __exportStar(require("./config"), exports);
20
- // We might not export lib/mysql directly unless user needs custom access
package/dist/lib/mysql.js CHANGED
@@ -35,8 +35,6 @@ async function saveAuditLog(entry, config) {
35
35
  }
36
36
  const db = await getDbConnection(config);
37
37
  const tableName = config.tableName || 'audit_logs';
38
- // Simple table check/creation (naive implementation for MVP)
39
- // In production, migrations are preferred.
40
38
  await db.query(`
41
39
  CREATE TABLE IF NOT EXISTS \`${tableName}\` (
42
40
  id INT AUTO_INCREMENT PRIMARY KEY,
package/dist/types.d.ts CHANGED
@@ -7,17 +7,8 @@ export interface AuditConfig {
7
7
  password?: string;
8
8
  database?: string;
9
9
  };
10
- /**
11
- * Table name to store audit logs. Default: 'audit_logs'
12
- */
13
10
  tableName?: string;
14
- /**
15
- * Enable console logging for debugging
16
- */
17
11
  debug?: boolean;
18
- /**
19
- * If true, database writes are skipped (for testing)
20
- */
21
12
  testMode?: boolean;
22
13
  }
23
14
  export type AuditLogEntry = {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hacimertgokhan/next-audit",
3
- "version": "1.0.0",
3
+ "version": "1.0.2",
4
4
  "description": "Next.js backend audit system with @Audit decorator",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -28,4 +28,4 @@
28
28
  "next": "^16.1.3",
29
29
  "typescript": "^5.5.0"
30
30
  }
31
- }
31
+ }
package/src/config.ts CHANGED
@@ -4,6 +4,18 @@ import { AuditConfig } from './types';
4
4
 
5
5
  let cachedConfig: AuditConfig | null = null;
6
6
 
7
+ /**
8
+ * Manually set the audit configuration.
9
+ * This is useful if you want to load config from a different source.
10
+ */
11
+ export function setAuditConfig(config: AuditConfig) {
12
+ cachedConfig = config;
13
+ }
14
+
15
+ /**
16
+ * Loads the configuration from audit.conf.js or audit.conf.ts in the project root.
17
+ * Falls back to environment variables if no config file is found.
18
+ */
7
19
  export function loadConfig(): AuditConfig {
8
20
  if (cachedConfig) return cachedConfig;
9
21
 
@@ -12,42 +24,54 @@ export function loadConfig(): AuditConfig {
12
24
  const configPathJs = path.resolve(process.cwd(), 'audit.conf.js');
13
25
 
14
26
  try {
15
- // Note: In a real compiled node_modules scenario, requiring a .ts file from cwd at runtime
16
- // requires ts-node or similar. We will assume the user has a setup that handles this,
17
- // or they compile it to .js. For this MVP, we try to require it.
18
- // If it fails, we look for defaults or env vars.
27
+ // Use eval('require') to bypass Webpack's static analysis in Next.js
28
+ // This allows loading a file from the disk at runtime.
29
+ const safeRequire = (p: string) => {
30
+ try {
31
+ // eslint-disable-next-line no-eval
32
+ return eval('require')(p);
33
+ } catch (e) {
34
+ // Fallback for non-webpack environments
35
+ if (typeof require !== 'undefined') {
36
+ // eslint-disable-next-line @typescript-eslint/no-var-requires
37
+ return require(p);
38
+ }
39
+ throw e;
40
+ }
41
+ };
19
42
 
20
43
  if (fs.existsSync(configPathJs)) {
21
- // eslint-disable-next-line @typescript-eslint/no-var-requires
22
- const userConfig = require(configPathJs);
44
+ const userConfig = safeRequire(configPathJs);
23
45
  cachedConfig = userConfig.default || userConfig;
24
46
  } else if (fs.existsSync(configPathTs)) {
25
- // Only works if running via ts-node or if we invoke a transpiler on fly
26
- // For now, we warn or rely on an environment that supports it.
27
47
  try {
28
- // eslint-disable-next-line @typescript-eslint/no-var-requires
29
- require('ts-node/register'); // Try to register if possible? Risky for a lib.
30
- // eslint-disable-next-line @typescript-eslint/no-var-requires
31
- const userConfig = require(configPathTs);
48
+ // Try to register ts-node if it's available for .ts config files
49
+ try {
50
+ safeRequire('ts-node/register');
51
+ } catch (e) {
52
+ // ts-node not available, hope for the best or it might already be registered
53
+ }
54
+ const userConfig = safeRequire(configPathTs);
32
55
  cachedConfig = userConfig.default || userConfig;
33
56
  } catch (e) {
34
- console.warn('[Next-Audit] Found audit.conf.ts but could not load it directly (ts-node missing?). Please ensure it is compiled to js or use audit.conf.js');
57
+ console.warn('[Next-Audit] Found audit.conf.ts but could not load it directly. Please ensure it is compiled to js or use audit.conf.js');
35
58
  }
36
59
  }
37
60
  } catch (error) {
61
+ // Only log if it's not a "module not found" error for the config files themselves
62
+ // but wait, we already check fs.existsSync, so any error here is likely real.
38
63
  console.error('[Next-Audit] Error loading config:', error);
39
64
  }
40
65
 
41
66
  if (!cachedConfig) {
42
- // Fallback: minimal valid config or throw
43
- // For MVP we assume environment variables might also be used
67
+ // Fallback: environment variables
44
68
  cachedConfig = {
45
69
  database: {
46
70
  url: process.env.DATABASE_URL
47
- // default to local if nothing found?
48
71
  },
49
- debug: true
72
+ debug: !!process.env.AUDIT_DEBUG
50
73
  };
74
+
51
75
  if (!process.env.DATABASE_URL) {
52
76
  console.warn('[Next-Audit] No config file found and DATABASE_URL not set. Audit might fail.');
53
77
  }
@@ -17,7 +17,7 @@ export function Audit(options?: { actionType?: string }) {
17
17
  let req: NextRequest | undefined;
18
18
  let context: any;
19
19
 
20
- // Try to find NextRequest in arguments
20
+
21
21
  for (const arg of args) {
22
22
  if (arg instanceof Request || (arg && arg.constructor && arg.constructor.name === 'NextRequest')) {
23
23
  req = arg as NextRequest;
@@ -25,16 +25,16 @@ export function Audit(options?: { actionType?: string }) {
25
25
  }
26
26
  }
27
27
 
28
- // Capture inputs
28
+
29
29
  const url = req?.url || '';
30
30
  const method = req?.method || 'UNKNOWN';
31
31
  const userAgent = req?.headers.get('user-agent') || '';
32
- // TODO: Extract user ID from session/token if available in headers or args
32
+
33
33
 
34
- let oldData = null; // For delete operations, we hope to capture this
34
+ let oldData = null;
35
35
  let newData = null;
36
36
 
37
- // Execute original method
37
+
38
38
  let result;
39
39
  let status = 200;
40
40
  let errorOccurred = false;
@@ -42,33 +42,33 @@ export function Audit(options?: { actionType?: string }) {
42
42
  try {
43
43
  result = await originalMethod.apply(this, args);
44
44
 
45
- // Analyze Result
45
+
46
46
  if (result instanceof Response) {
47
47
  status = result.status;
48
- // Clone to read body without consuming the original stream if possible,
49
- // or assume we can't read it if it's a stream already locked.
50
- // For JSON responses, we might want to peek.
48
+
49
+
50
+
51
51
  try {
52
52
  const clone = result.clone();
53
53
  const bodyText = await clone.text();
54
54
  if (bodyText) {
55
55
  try {
56
56
  const json = JSON.parse(bodyText);
57
- // If DELETE, assume result is the deleted data or confirmation
57
+
58
58
  if (method === 'DELETE') {
59
59
  oldData = json;
60
60
  } else if (method === 'POST' || method === 'PUT' || method === 'PATCH') {
61
61
  newData = json;
62
62
  }
63
63
  } catch (e) {
64
- // Not JSON
64
+
65
65
  }
66
66
  }
67
67
  } catch (e) {
68
- // Failed to read response body
68
+
69
69
  }
70
70
  } else {
71
- // If the function returns a plain object (not a Response), treat it as data
71
+
72
72
  if (method === 'DELETE') {
73
73
  oldData = result;
74
74
  } else {
@@ -79,7 +79,7 @@ export function Audit(options?: { actionType?: string }) {
79
79
  } catch (error: any) {
80
80
  errorOccurred = true;
81
81
  status = 500;
82
- // Try to get status from error if available
82
+
83
83
  if (error.status) status = error.status;
84
84
  if (error.statusCode) status = error.statusCode;
85
85
  throw error;
@@ -98,13 +98,9 @@ export function Audit(options?: { actionType?: string }) {
98
98
  user_agent: userAgent,
99
99
  old_value: oldData,
100
100
  new_value: newData,
101
- // user_id, entity, entity_id need more specific logic or config to extract
101
+
102
102
  };
103
103
 
104
- // Fire and forget audit log to not block response?
105
- // Or await it? User might want reliability.
106
- // We'll await it for now to ensure it's logged,
107
- // but catching errors so we don't break the app if logging fails.
108
104
  saveAuditLog(entry, config).catch(e => console.error('Audit Log Error', e));
109
105
  }
110
106
  }
package/src/index.ts CHANGED
@@ -1,4 +1,4 @@
1
1
  export * from './decorators/audit';
2
2
  export * from './types';
3
3
  export * from './config';
4
- // We might not export lib/mysql directly unless user needs custom access
4
+
package/src/lib/mysql.ts CHANGED
@@ -36,8 +36,8 @@ export async function saveAuditLog(entry: AuditLogEntry, config: AuditConfig) {
36
36
  const db = await getDbConnection(config);
37
37
  const tableName = config.tableName || 'audit_logs';
38
38
 
39
- // Simple table check/creation (naive implementation for MVP)
40
- // In production, migrations are preferred.
39
+
40
+
41
41
  await db.query(`
42
42
  CREATE TABLE IF NOT EXISTS \`${tableName}\` (
43
43
  id INT AUTO_INCREMENT PRIMARY KEY,
package/src/types.ts CHANGED
@@ -7,17 +7,8 @@ export interface AuditConfig {
7
7
  password?: string;
8
8
  database?: string;
9
9
  };
10
- /**
11
- * Table name to store audit logs. Default: 'audit_logs'
12
- */
13
10
  tableName?: string;
14
- /**
15
- * Enable console logging for debugging
16
- */
17
11
  debug?: boolean;
18
- /**
19
- * If true, database writes are skipped (for testing)
20
- */
21
12
  testMode?: boolean;
22
13
  }
23
14
 
@@ -1,5 +1,5 @@
1
1
  import { Audit } from '../src/index';
2
- // Mock NextRequest and NextResponse for testing
2
+
3
3
  class MockNextRequest {
4
4
  public url: string;
5
5
  public method: string;
@@ -11,15 +11,15 @@ class MockNextRequest {
11
11
  }
12
12
  }
13
13
 
14
- // Mock Config Loading
14
+
15
15
  jest.mock('../src/config', () => ({
16
16
  loadConfig: () => ({
17
- database: {}, // Won't connect in test unless we mock db too
17
+ database: {},
18
18
  debug: true
19
19
  })
20
20
  }));
21
21
 
22
- // Mock DB to avoid connection errors
22
+
23
23
  jest.mock('../src/lib/mysql', () => ({
24
24
  saveAuditLog: async (entry: any) => {
25
25
  console.log('MOCK DB SAVE:', JSON.stringify(entry, null, 2));
@@ -45,15 +45,15 @@ async function runTest() {
45
45
 
46
46
  console.log('--- Test 1: DELETE ---');
47
47
  const req1 = new MockNextRequest('http://localhost:3000/api/users/123', 'DELETE');
48
- // @ts-ignore
48
+
49
49
  await controller.deleteUser(req1);
50
50
 
51
51
  console.log('\n--- Test 2: POST (Response object) ---');
52
52
  const req2 = new MockNextRequest('http://localhost:3000/api/posts', 'POST');
53
- // @ts-ignore
53
+
54
54
  await controller.createPost(req2);
55
55
  }
56
56
 
57
- // runTest();
58
- // We can't easily run this with ts-node because we are mocking modules via jest logic which isn't present
59
- // We will just do a simple run script without jest mocks, but manual overrides if possible.
57
+
58
+
59
+
@@ -1,9 +1,9 @@
1
1
 
2
2
  import { Audit } from '../dist/index';
3
- // Usage of dist means we should build again before running this.
4
- // But technically in node we can import src if we use ts-node, but I am forcing dist usage to match real world.
5
3
 
6
- // Mock NextRequest and NextResponse for testing behavior
4
+
5
+
6
+
7
7
  class NextRequest {
8
8
  public url: string;
9
9
  public method: string;
@@ -15,10 +15,10 @@ class NextRequest {
15
15
  }
16
16
  }
17
17
 
18
- // We need to Mock the config loader behavior because we don't have audit.conf.ts
19
- // effectively we can set a global or mock the module if possible.
20
- // Since we are running a script, we can rely on `loadConfig` behavior fallback
21
- // or creating a dummy audit.conf.js in cwd.
18
+
19
+
20
+
21
+
22
22
 
23
23
  import fs from 'fs';
24
24
  import path from 'path';
@@ -28,7 +28,7 @@ const dummyConfigPath = path.resolve(process.cwd(), 'audit.conf.js');
28
28
  class TestController {
29
29
 
30
30
  @Audit()
31
- async deleteUser(req: any) { // req type is any here to pass mock
31
+ async deleteUser(req: any) {
32
32
  console.log(' -> Controller: deleteUser called');
33
33
  return { success: true, deletedId: 101, name: 'Deleted User' };
34
34
  }
@@ -36,15 +36,15 @@ class TestController {
36
36
  @Audit()
37
37
  async createPost(req: any) {
38
38
  console.log(' -> Controller: createPost called');
39
- // Simulate a response object
39
+
40
40
  return { status: 201, json: () => ({ id: 202, title: 'New Post' }) };
41
- // Note: Real Response object logic in decorator uses .clone(), so this mock is imperfect
42
- // but sufficient if we just want to see if it runs without crashing.
41
+
42
+
43
43
  }
44
44
  }
45
45
 
46
46
  async function runTest() {
47
- // 1. Create dummy config
47
+
48
48
  fs.writeFileSync(dummyConfigPath, `
49
49
  module.exports = {
50
50
  database: { url: 'mysql://mock:3306/db' },
@@ -59,23 +59,23 @@ async function runTest() {
59
59
 
60
60
  console.log('\n--- Test 1: DELETE ---');
61
61
  const req1 = new NextRequest('http://api.test/users/101', 'DELETE');
62
- // @ts-ignore
62
+
63
63
  await controller.deleteUser(req1);
64
64
 
65
65
  console.log('\n--- Test 2: POST ---');
66
66
  const req2 = new NextRequest('http://api.test/posts', 'POST');
67
- // @ts-ignore
67
+
68
68
  await controller.createPost(req2);
69
69
 
70
70
  console.log('\n--- Test 3: PATCH ---');
71
71
  const req3 = new NextRequest('http://api.test/users/101', 'PATCH');
72
- // @ts-ignore
73
- await controller.createPost(req3); // Reuse createPost for simplicity as it returns data
72
+
73
+ await controller.createPost(req3);
74
74
 
75
75
  } catch (e) {
76
76
  console.error('Test Failed:', e);
77
77
  } finally {
78
- // Cleanup
78
+
79
79
  if (fs.existsSync(dummyConfigPath)) {
80
80
  fs.unlinkSync(dummyConfigPath);
81
81
  console.log('Removed dummy audit.conf.js');