@bitclaw/loadtest 1.1.0 → 1.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli.js CHANGED
File without changes
@@ -1,12 +1,27 @@
1
1
  /**
2
2
  * Config loader — resolves per-app loadtest.config.ts files.
3
+ *
4
+ * Resolution order:
5
+ * 1. Explicit `configDir` option (if provided)
6
+ * 2. `process.cwd()` — standalone repo pattern
7
+ * 3. `{REPO_ROOT}/apps/{appName}/` — monorepo pattern (legacy)
8
+ *
9
+ * If a config is found at an earlier path but its `appName` doesn't match,
10
+ * it is skipped and the next path is tried (mitigation against loading
11
+ * the wrong app's config from CWD).
3
12
  */
4
13
  import type { AppLoadTestConfig } from '../types';
5
14
  /**
6
15
  * Load the loadtest config for a given app name.
7
- * Expects the file at `apps/{appName}/loadtest.config.ts`.
16
+ *
17
+ * Tries multiple locations (in order):
18
+ * - `options.configDir/loadtest.config.ts` (explicit override)
19
+ * - `process.cwd()/loadtest.config.ts` (standalone repo)
20
+ * - `{REPO_ROOT}/apps/{appName}/loadtest.config.ts` (monorepo)
8
21
  */
9
- export declare function loadConfig(appName: string): Promise<AppLoadTestConfig>;
22
+ export declare function loadConfig(appName: string, options?: {
23
+ configDir?: string;
24
+ }): Promise<AppLoadTestConfig>;
10
25
  /**
11
26
  * List all available app names that have loadtest configs.
12
27
  */
@@ -1 +1 @@
1
- {"version":3,"file":"loader.d.ts","sourceRoot":"","sources":["../../src/config/loader.ts"],"names":[],"mappings":"AAAA;;GAEG;AAIH,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,UAAU,CAAC;AAKlD;;;GAGG;AACH,wBAAsB,UAAU,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,iBAAiB,CAAC,CAuC5E;AAED;;GAEG;AACH,wBAAsB,kBAAkB,IAAI,OAAO,CAAC,MAAM,EAAE,CAAC,CAe5D"}
1
+ {"version":3,"file":"loader.d.ts","sourceRoot":"","sources":["../../src/config/loader.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAIH,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,UAAU,CAAC;AAkBlD;;;;;;;GAOG;AACH,wBAAsB,UAAU,CAC9B,OAAO,EAAE,MAAM,EACf,OAAO,CAAC,EAAE;IAAE,SAAS,CAAC,EAAE,MAAM,CAAA;CAAE,GAC/B,OAAO,CAAC,iBAAiB,CAAC,CA8C5B;AAED;;GAEG;AACH,wBAAsB,kBAAkB,IAAI,OAAO,CAAC,MAAM,EAAE,CAAC,CAe5D"}
@@ -1,41 +1,70 @@
1
1
  /**
2
2
  * Config loader — resolves per-app loadtest.config.ts files.
3
+ *
4
+ * Resolution order:
5
+ * 1. Explicit `configDir` option (if provided)
6
+ * 2. `process.cwd()` — standalone repo pattern
7
+ * 3. `{REPO_ROOT}/apps/{appName}/` — monorepo pattern (legacy)
8
+ *
9
+ * If a config is found at an earlier path but its `appName` doesn't match,
10
+ * it is skipped and the next path is tried (mitigation against loading
11
+ * the wrong app's config from CWD).
3
12
  */
4
13
  import { dirname, join, resolve } from 'node:path';
5
14
  import { fileURLToPath } from 'node:url';
6
15
  const __dirname = dirname(fileURLToPath(import.meta.url));
7
16
  const REPO_ROOT = resolve(__dirname, '..', '..', '..', '..');
17
+ function isModuleNotFound(error) {
18
+ const message = error instanceof Error
19
+ ? error.message
20
+ : typeof error === 'object' && error !== null && 'message' in error
21
+ ? String(error.message)
22
+ : '';
23
+ return (message.includes('Cannot find module') ||
24
+ message.includes('ERR_MODULE_NOT_FOUND'));
25
+ }
8
26
  /**
9
27
  * Load the loadtest config for a given app name.
10
- * Expects the file at `apps/{appName}/loadtest.config.ts`.
28
+ *
29
+ * Tries multiple locations (in order):
30
+ * - `options.configDir/loadtest.config.ts` (explicit override)
31
+ * - `process.cwd()/loadtest.config.ts` (standalone repo)
32
+ * - `{REPO_ROOT}/apps/{appName}/loadtest.config.ts` (monorepo)
11
33
  */
12
- export async function loadConfig(appName) {
13
- const configPath = join(REPO_ROOT, 'apps', appName, 'loadtest.config.ts');
14
- try {
15
- const mod = await import(configPath);
16
- const config = mod.default ?? mod.config;
17
- if (!config) {
18
- throw new Error(`No default export or named "config" export found in ${configPath}`);
19
- }
20
- if (config.appName !== appName) {
21
- console.warn(`Warning: Config appName "${config.appName}" doesn't match requested "${appName}"`);
22
- }
23
- return config;
34
+ export async function loadConfig(appName, options) {
35
+ const searchPaths = [];
36
+ if (options?.configDir) {
37
+ searchPaths.push(join(options.configDir, 'loadtest.config.ts'));
24
38
  }
25
- catch (error) {
26
- const message = error instanceof Error
27
- ? error.message
28
- : typeof error === 'object' && error !== null && 'message' in error
29
- ? String(error.message)
30
- : '';
31
- if (message.includes('Cannot find module') ||
32
- message.includes('ERR_MODULE_NOT_FOUND')) {
33
- throw new Error(`No loadtest config found for app "${appName}".\n` +
34
- `Expected: apps/${appName}/loadtest.config.ts\n` +
35
- `Create one following the pattern in apps/runmist/loadtest.config.ts`);
39
+ searchPaths.push(join(process.cwd(), 'loadtest.config.ts'));
40
+ searchPaths.push(join(REPO_ROOT, 'apps', appName, 'loadtest.config.ts'));
41
+ for (const configPath of searchPaths) {
42
+ try {
43
+ const mod = await import(configPath);
44
+ const config = mod.default ?? mod.config;
45
+ if (!config) {
46
+ console.warn(`Warning: No export found in ${configPath} — skipping`);
47
+ continue;
48
+ }
49
+ // App name validation: skip if mismatch so the next path can be tried
50
+ if (config.appName !== appName) {
51
+ console.warn(`Warning: Config at ${configPath} has appName "${config.appName}", ` +
52
+ `requested "${appName}". Trying next path.`);
53
+ continue;
54
+ }
55
+ return config;
56
+ }
57
+ catch (error) {
58
+ if (isModuleNotFound(error)) {
59
+ continue;
60
+ }
61
+ // Re-throw genuine errors (parse failures, type errors, etc.)
62
+ throw new Error(`Error loading config from ${configPath}: ${error instanceof Error ? error.message : String(error)}`);
36
63
  }
37
- throw error;
38
64
  }
65
+ throw new Error(`No loadtest config found for app "${appName}".\n` +
66
+ `Tried:\n ${searchPaths.join('\n ')}\n\n` +
67
+ `Create one following the pattern in apps/runmist/loadtest.config.ts`);
39
68
  }
40
69
  /**
41
70
  * List all available app names that have loadtest configs.
package/package.json CHANGED
@@ -1,13 +1,20 @@
1
1
  {
2
2
  "name": "@bitclaw/loadtest",
3
- "version": "1.1.0",
3
+ "version": "1.1.1",
4
4
  "description": "Load testing framework for SQLite-backed web applications",
5
- "files": ["dist", "LICENSE", "README.md"],
5
+ "files": [
6
+ "dist",
7
+ "LICENSE",
8
+ "README.md"
9
+ ],
6
10
  "type": "module",
7
11
  "main": "./dist/index.js",
8
12
  "types": "./dist/index.d.ts",
9
13
  "exports": {
10
- ".": { "types": "./dist/index.d.ts", "default": "./dist/index.js" }
14
+ ".": {
15
+ "types": "./dist/index.d.ts",
16
+ "default": "./dist/index.js"
17
+ }
11
18
  },
12
19
  "bin": {
13
20
  "loadtest": "./dist/cli.js"
@@ -21,13 +28,19 @@
21
28
  "lint:fix": "biome check --write",
22
29
  "format": "biome format --write",
23
30
  "knip": "knip",
24
- "prepublishOnly": "npm run build && npm run test",
31
+ "prepublishOnly": "npm run build && chmod +x dist/cli.js && npm run test",
25
32
  "publish:dev": "npm run build && npm publish --tag dev --access public",
26
33
  "publish:patch": "npm whoami && npm version patch && git push --follow-tags && npm publish --access public",
27
34
  "publish:minor": "npm whoami && npm version minor && git push --follow-tags && npm publish --access public",
28
35
  "publish:major": "npm whoami && npm version major && git push --follow-tags && npm publish --access public"
29
36
  },
30
- "keywords": ["loadtest", "benchmark", "sqlite", "bun", "k6"],
37
+ "keywords": [
38
+ "loadtest",
39
+ "benchmark",
40
+ "sqlite",
41
+ "bun",
42
+ "k6"
43
+ ],
31
44
  "author": "bitclaw",
32
45
  "license": "MIT",
33
46
  "dependencies": {