@dainprotocol/cli 1.2.28 → 1.2.30

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,289 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ var path_1 = __importDefault(require("path"));
7
+ /**
8
+ * Build command tests - focusing on WASM detection, esbuild config, and static files
9
+ */
10
+ describe('build.ts', function () {
11
+ describe('findWasmFiles logic', function () {
12
+ // Test the WASM file filtering logic
13
+ function shouldIncludeWasmFile(fullPath) {
14
+ // Only include WASM files in dist/nodejs directories (Node.js bindings)
15
+ return fullPath.includes('/dist/') || fullPath.includes('/build/');
16
+ }
17
+ test('includes WASM files in /dist/ directories', function () {
18
+ expect(shouldIncludeWasmFile('/node_modules/package/dist/module.wasm')).toBe(true);
19
+ expect(shouldIncludeWasmFile('/node_modules/@scope/pkg/dist/nodejs/binding.wasm')).toBe(true);
20
+ });
21
+ test('includes WASM files in /build/ directories', function () {
22
+ expect(shouldIncludeWasmFile('/node_modules/package/build/Release/native.wasm')).toBe(true);
23
+ });
24
+ test('excludes WASM files in root of package', function () {
25
+ expect(shouldIncludeWasmFile('/node_modules/package/module.wasm')).toBe(false);
26
+ });
27
+ test('excludes WASM files in src directories', function () {
28
+ expect(shouldIncludeWasmFile('/node_modules/package/src/module.wasm')).toBe(false);
29
+ });
30
+ test('excludes WASM files in test directories', function () {
31
+ expect(shouldIncludeWasmFile('/node_modules/package/test/fixture.wasm')).toBe(false);
32
+ });
33
+ });
34
+ describe('directory scanning exclusions', function () {
35
+ // Test the directories that should be skipped
36
+ function shouldSkipDirectory(name) {
37
+ return name === '.bin' || name === '.cache';
38
+ }
39
+ test('skips .bin directory', function () {
40
+ expect(shouldSkipDirectory('.bin')).toBe(true);
41
+ });
42
+ test('skips .cache directory', function () {
43
+ expect(shouldSkipDirectory('.cache')).toBe(true);
44
+ });
45
+ test('does not skip regular directories', function () {
46
+ expect(shouldSkipDirectory('dist')).toBe(false);
47
+ expect(shouldSkipDirectory('src')).toBe(false);
48
+ expect(shouldSkipDirectory('@scope')).toBe(false);
49
+ });
50
+ test('does not skip .pnpm store (scanned for packages)', function () {
51
+ expect(shouldSkipDirectory('.pnpm')).toBe(false);
52
+ });
53
+ });
54
+ describe('copyWasmDependencies logic', function () {
55
+ // Test the bundle scanning for WASM references
56
+ function isWasmReferencedInBundle(wasmName, bundleContent) {
57
+ return bundleContent.includes(wasmName);
58
+ }
59
+ test('detects WASM file references in bundle', function () {
60
+ var bundle = "\n const wasmPath = require.resolve(\"zstd-codec/zstddec.wasm\");\n const decoder = new Decoder(wasmPath);\n ";
61
+ expect(isWasmReferencedInBundle('zstddec.wasm', bundle)).toBe(true);
62
+ });
63
+ test('does not match partial WASM names', function () {
64
+ var bundle = 'const zstddec_module = require("module");';
65
+ expect(isWasmReferencedInBundle('zstddec.wasm', bundle)).toBe(false);
66
+ });
67
+ test('handles multiple WASM references', function () {
68
+ var bundle = "\n require(\"libzstd.wasm\");\n require(\"wasm_exec.wasm\");\n ";
69
+ expect(isWasmReferencedInBundle('libzstd.wasm', bundle)).toBe(true);
70
+ expect(isWasmReferencedInBundle('wasm_exec.wasm', bundle)).toBe(true);
71
+ expect(isWasmReferencedInBundle('other.wasm', bundle)).toBe(false);
72
+ });
73
+ });
74
+ describe('runtime configuration', function () {
75
+ function getNodeBuildOptions(watch) {
76
+ return {
77
+ platform: 'node',
78
+ target: 'es2020',
79
+ minify: !watch,
80
+ };
81
+ }
82
+ function getWorkersBuildOptions() {
83
+ return {
84
+ format: 'esm',
85
+ target: 'esnext',
86
+ conditions: ['worker', 'workerd', 'browser'],
87
+ outExtension: { '.js': '.mjs' },
88
+ };
89
+ }
90
+ test('node runtime: platform is node', function () {
91
+ var options = getNodeBuildOptions(false);
92
+ expect(options.platform).toBe('node');
93
+ });
94
+ test('node runtime: minify when not watching', function () {
95
+ expect(getNodeBuildOptions(false).minify).toBe(true);
96
+ expect(getNodeBuildOptions(true).minify).toBe(false);
97
+ });
98
+ test('node runtime: targets ES2020', function () {
99
+ expect(getNodeBuildOptions(false).target).toBe('es2020');
100
+ });
101
+ test('workers runtime: uses ESM format', function () {
102
+ var options = getWorkersBuildOptions();
103
+ expect(options.format).toBe('esm');
104
+ });
105
+ test('workers runtime: targets esnext', function () {
106
+ var options = getWorkersBuildOptions();
107
+ expect(options.target).toBe('esnext');
108
+ });
109
+ test('workers runtime: has worker conditions', function () {
110
+ var options = getWorkersBuildOptions();
111
+ expect(options.conditions).toContain('worker');
112
+ expect(options.conditions).toContain('workerd');
113
+ expect(options.conditions).toContain('browser');
114
+ });
115
+ test('workers runtime: outputs .mjs files', function () {
116
+ var _a;
117
+ var options = getWorkersBuildOptions();
118
+ expect((_a = options.outExtension) === null || _a === void 0 ? void 0 : _a['.js']).toBe('.mjs');
119
+ });
120
+ test('runtime selection falls back to config then node', function () {
121
+ function resolveRuntime(optionsRuntime, configRuntime) {
122
+ return optionsRuntime || configRuntime || 'node';
123
+ }
124
+ expect(resolveRuntime('workers', 'node')).toBe('workers');
125
+ expect(resolveRuntime(undefined, 'workers')).toBe('workers');
126
+ expect(resolveRuntime(undefined, undefined)).toBe('node');
127
+ });
128
+ });
129
+ describe('output directory handling', function () {
130
+ test('resolves out-dir from config', function () {
131
+ var cwd = '/project';
132
+ var config = { 'out-dir': 'dist' };
133
+ var outDir = path_1.default.join(cwd, config['out-dir']);
134
+ expect(outDir).toBe('/project/dist');
135
+ });
136
+ test('defaults to "build" when not specified', function () {
137
+ var cwd = '/project';
138
+ var config = {};
139
+ var outDir = path_1.default.join(cwd, config['out-dir'] || 'build');
140
+ expect(outDir).toBe('/project/build');
141
+ });
142
+ test('watch mode uses .dain directory', function () {
143
+ var cwd = '/project';
144
+ var dainDir = path_1.default.join(cwd, '.dain');
145
+ var outDir = path_1.default.join(cwd, 'build');
146
+ function getTargetDir(isWatch) {
147
+ return isWatch ? dainDir : outDir;
148
+ }
149
+ expect(getTargetDir(true)).toBe('/project/.dain');
150
+ expect(getTargetDir(false)).toBe('/project/build');
151
+ });
152
+ });
153
+ describe('static files handling', function () {
154
+ test('resolves static-dir from config', function () {
155
+ var cwd = '/project';
156
+ var config = { 'static-dir': 'assets' };
157
+ var staticDir = path_1.default.join(cwd, config['static-dir']);
158
+ expect(staticDir).toBe('/project/assets');
159
+ });
160
+ test('defaults to "static" when not specified', function () {
161
+ var cwd = '/project';
162
+ var config = {};
163
+ var staticDir = config['static-dir']
164
+ ? path_1.default.join(cwd, config['static-dir'])
165
+ : path_1.default.join(cwd, 'static');
166
+ expect(staticDir).toBe('/project/static');
167
+ });
168
+ test('static output goes to targetDir/static', function () {
169
+ var targetDir = '/project/build';
170
+ var staticOutDir = path_1.default.join(targetDir, 'static');
171
+ expect(staticOutDir).toBe('/project/build/static');
172
+ });
173
+ });
174
+ describe('entry point handling', function () {
175
+ test('uses main-file from config', function () {
176
+ var config = { 'main-file': 'src/index.ts' };
177
+ expect(config['main-file']).toBe('src/index.ts');
178
+ });
179
+ test('entry point is always an array', function () {
180
+ var config = { 'main-file': 'src/server.ts' };
181
+ var entryPoints = [config['main-file']];
182
+ expect(Array.isArray(entryPoints)).toBe(true);
183
+ expect(entryPoints).toEqual(['src/server.ts']);
184
+ });
185
+ });
186
+ describe('WASM file extension detection', function () {
187
+ test('identifies .wasm files', function () {
188
+ var isWasm = function (filename) { return filename.endsWith('.wasm'); };
189
+ expect(isWasm('module.wasm')).toBe(true);
190
+ expect(isWasm('binding.wasm')).toBe(true);
191
+ expect(isWasm('decoder.WASM')).toBe(false); // Case sensitive
192
+ expect(isWasm('module.js')).toBe(false);
193
+ expect(isWasm('wasm.js')).toBe(false); // "wasm" in name, not extension
194
+ });
195
+ });
196
+ describe('path operations', function () {
197
+ test('basename extracts filename from path', function () {
198
+ expect(path_1.default.basename('/node_modules/pkg/dist/module.wasm')).toBe('module.wasm');
199
+ expect(path_1.default.basename('/long/nested/path/file.wasm')).toBe('file.wasm');
200
+ });
201
+ test('join constructs destination path', function () {
202
+ var outDir = '/project/build';
203
+ var wasmName = 'decoder.wasm';
204
+ var destPath = path_1.default.join(outDir, wasmName);
205
+ expect(destPath).toBe('/project/build/decoder.wasm');
206
+ });
207
+ });
208
+ describe('bundle path calculation', function () {
209
+ test('calculates bundle path for standard build', function () {
210
+ var targetDir = '/project/build';
211
+ var bundlePath = path_1.default.join(targetDir, 'index.js');
212
+ expect(bundlePath).toBe('/project/build/index.js');
213
+ });
214
+ test('calculates bundle path for watch mode', function () {
215
+ var dainDir = '/project/.dain';
216
+ var bundlePath = path_1.default.join(dainDir, 'index.js');
217
+ expect(bundlePath).toBe('/project/.dain/index.js');
218
+ });
219
+ });
220
+ describe('error handling', function () {
221
+ test('unsupported runtime throws error', function () {
222
+ function validateRuntime(runtime) {
223
+ if (runtime !== 'node' && runtime !== 'workers') {
224
+ throw new Error("Unsupported runtime: ".concat(runtime));
225
+ }
226
+ }
227
+ expect(function () { return validateRuntime('node'); }).not.toThrow();
228
+ expect(function () { return validateRuntime('workers'); }).not.toThrow();
229
+ expect(function () { return validateRuntime('deno'); }).toThrow('Unsupported runtime: deno');
230
+ expect(function () { return validateRuntime('bun'); }).toThrow('Unsupported runtime: bun');
231
+ });
232
+ });
233
+ describe('sourcemap generation', function () {
234
+ test('sourcemap is always enabled', function () {
235
+ var buildOptions = { sourcemap: true };
236
+ expect(buildOptions.sourcemap).toBe(true);
237
+ });
238
+ });
239
+ describe('bundle configuration', function () {
240
+ test('bundle option is always true', function () {
241
+ var buildOptions = { bundle: true };
242
+ expect(buildOptions.bundle).toBe(true);
243
+ });
244
+ });
245
+ });
246
+ describe('WASM detection patterns', function () {
247
+ // Common WASM packages that should be detected
248
+ var commonWasmPackages = [
249
+ '@noble/ed25519',
250
+ '@solana/buffer-layout',
251
+ 'zstd-codec',
252
+ 'zstddec',
253
+ 'wasm-pack',
254
+ ];
255
+ describe('common package paths', function () {
256
+ test.each(commonWasmPackages)('detects %s dist WASM files', function (pkg) {
257
+ var distPath = "/node_modules/".concat(pkg, "/dist/module.wasm");
258
+ expect(distPath.includes('/dist/')).toBe(true);
259
+ });
260
+ });
261
+ describe('pnpm store paths', function () {
262
+ test('handles .pnpm versioned paths', function () {
263
+ var pnpmPath = '/node_modules/.pnpm/zstd-codec@0.1.5/node_modules/zstd-codec/dist/zstddec.wasm';
264
+ expect(pnpmPath.includes('/dist/')).toBe(true);
265
+ expect(pnpmPath.endsWith('.wasm')).toBe(true);
266
+ });
267
+ test('handles scoped packages in pnpm', function () {
268
+ var pnpmPath = '/node_modules/.pnpm/@solana+web3.js@1.95.0/node_modules/@solana/web3.js/dist/binding.wasm';
269
+ expect(pnpmPath.includes('/dist/')).toBe(true);
270
+ });
271
+ });
272
+ });
273
+ describe('build process exit behavior', function () {
274
+ test('non-watch, non-deploy mode exits with 0', function () {
275
+ var options = { watch: false, deploy: false };
276
+ var shouldExit = !options.watch && !options.deploy;
277
+ expect(shouldExit).toBe(true);
278
+ });
279
+ test('watch mode does not exit', function () {
280
+ var options = { watch: true, deploy: false };
281
+ var shouldExit = !options.watch && !options.deploy;
282
+ expect(shouldExit).toBe(false);
283
+ });
284
+ test('deploy mode does not exit', function () {
285
+ var options = { watch: false, deploy: true };
286
+ var shouldExit = !options.watch && !options.deploy;
287
+ expect(shouldExit).toBe(false);
288
+ });
289
+ });
@@ -1,5 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
+ var utils_1 = require("../utils");
3
4
  // Mock fs-extra before importing
4
5
  jest.mock('fs-extra', function () { return ({
5
6
  readFile: jest.fn(),
@@ -10,27 +11,9 @@ jest.mock('fs-extra', function () { return ({
10
11
  // Test the env parsing logic directly
11
12
  describe('loadEnvVariables', function () {
12
13
  // Extract the parsing logic for testing
13
- function parseEnvContent(envContent) {
14
- return envContent
15
- .split('\n')
16
- .filter(function (line) { return line.trim() && !line.trim().startsWith('#'); })
17
- .map(function (line) {
18
- var equalsIndex = line.indexOf('=');
19
- if (equalsIndex === -1)
20
- return null;
21
- var name = line.substring(0, equalsIndex).trim();
22
- var value = line.substring(equalsIndex + 1).trim();
23
- // Remove surrounding quotes if present
24
- var unquotedValue = value.replace(/^["']|["']$/g, '');
25
- return { name: name, value: unquotedValue };
26
- })
27
- .filter(function (env) {
28
- return env !== null && env.name !== '' && env.value !== '';
29
- });
30
- }
31
14
  test('parses simple key=value pairs', function () {
32
15
  var content = "FOO=bar\nBAZ=qux";
33
- var result = parseEnvContent(content);
16
+ var result = (0, utils_1.parseEnvContent)(content);
34
17
  expect(result).toEqual([
35
18
  { name: 'FOO', value: 'bar' },
36
19
  { name: 'BAZ', value: 'qux' },
@@ -38,7 +21,7 @@ describe('loadEnvVariables', function () {
38
21
  });
39
22
  test('handles values with = signs (like base64)', function () {
40
23
  var content = "API_KEY=sk_test_abc123==\nSECRET=a=b=c=d";
41
- var result = parseEnvContent(content);
24
+ var result = (0, utils_1.parseEnvContent)(content);
42
25
  expect(result).toEqual([
43
26
  { name: 'API_KEY', value: 'sk_test_abc123==' },
44
27
  { name: 'SECRET', value: 'a=b=c=d' },
@@ -46,7 +29,7 @@ describe('loadEnvVariables', function () {
46
29
  });
47
30
  test('ignores comment lines', function () {
48
31
  var content = "# This is a comment\nFOO=bar\n# Another comment\nBAZ=qux";
49
- var result = parseEnvContent(content);
32
+ var result = (0, utils_1.parseEnvContent)(content);
50
33
  expect(result).toEqual([
51
34
  { name: 'FOO', value: 'bar' },
52
35
  { name: 'BAZ', value: 'qux' },
@@ -54,7 +37,7 @@ describe('loadEnvVariables', function () {
54
37
  });
55
38
  test('ignores empty lines', function () {
56
39
  var content = "FOO=bar\n\nBAZ=qux\n\n";
57
- var result = parseEnvContent(content);
40
+ var result = (0, utils_1.parseEnvContent)(content);
58
41
  expect(result).toEqual([
59
42
  { name: 'FOO', value: 'bar' },
60
43
  { name: 'BAZ', value: 'qux' },
@@ -62,7 +45,7 @@ describe('loadEnvVariables', function () {
62
45
  });
63
46
  test('removes surrounding quotes from values', function () {
64
47
  var content = "FOO=\"bar\"\nBAZ='qux'\nSINGLE=\"quoted value\"";
65
- var result = parseEnvContent(content);
48
+ var result = (0, utils_1.parseEnvContent)(content);
66
49
  expect(result).toEqual([
67
50
  { name: 'FOO', value: 'bar' },
68
51
  { name: 'BAZ', value: 'qux' },
@@ -71,7 +54,7 @@ describe('loadEnvVariables', function () {
71
54
  });
72
55
  test('handles whitespace around = sign', function () {
73
56
  var content = " FOO = bar\n BAZ=qux";
74
- var result = parseEnvContent(content);
57
+ var result = (0, utils_1.parseEnvContent)(content);
75
58
  expect(result).toEqual([
76
59
  { name: 'FOO', value: 'bar' },
77
60
  { name: 'BAZ', value: 'qux' },
@@ -79,7 +62,7 @@ describe('loadEnvVariables', function () {
79
62
  });
80
63
  test('skips lines without = sign', function () {
81
64
  var content = "FOO=bar\nINVALID LINE\nBAZ=qux";
82
- var result = parseEnvContent(content);
65
+ var result = (0, utils_1.parseEnvContent)(content);
83
66
  expect(result).toEqual([
84
67
  { name: 'FOO', value: 'bar' },
85
68
  { name: 'BAZ', value: 'qux' },
@@ -87,14 +70,14 @@ describe('loadEnvVariables', function () {
87
70
  });
88
71
  test('skips lines with empty name or value', function () {
89
72
  var content = "=bar\nFOO=\nVALID=value";
90
- var result = parseEnvContent(content);
73
+ var result = (0, utils_1.parseEnvContent)(content);
91
74
  expect(result).toEqual([
92
75
  { name: 'VALID', value: 'value' },
93
76
  ]);
94
77
  });
95
78
  test('handles complex production-like env file', function () {
96
79
  var content = "# Production environment\nDATABASE_URL=postgresql://user:pass@host:5432/db?schema=public\nJWT_SECRET=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ\nREDIS_URL=\"redis://localhost:6379\"\nAPI_KEY='sk_live_abc123=='\n\n# Comment in the middle\nEMPTY_LINE_AFTER=true";
97
- var result = parseEnvContent(content);
80
+ var result = (0, utils_1.parseEnvContent)(content);
98
81
  expect(result).toHaveLength(5);
99
82
  expect(result[0].name).toBe('DATABASE_URL');
100
83
  expect(result[0].value).toContain('postgresql://');
@@ -4,6 +4,195 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
6
  var path_1 = __importDefault(require("path"));
7
+ /**
8
+ * Dev command tests - security, port handling, process management, file watching
9
+ */
10
+ describe('dev.ts port handling', function () {
11
+ describe('port resolution priority', function () {
12
+ function resolvePort(envPort, cliPort, defaultPort) {
13
+ if (defaultPort === void 0) { defaultPort = '2022'; }
14
+ if (envPort)
15
+ return { port: envPort, source: '.env file' };
16
+ if (cliPort)
17
+ return { port: cliPort, source: 'command line argument' };
18
+ return { port: defaultPort, source: 'default value' };
19
+ }
20
+ test('env port takes highest priority', function () {
21
+ var result = resolvePort('3000', '4000');
22
+ expect(result.port).toBe('3000');
23
+ expect(result.source).toBe('.env file');
24
+ });
25
+ test('CLI port used when no env port', function () {
26
+ var result = resolvePort(undefined, '4000');
27
+ expect(result.port).toBe('4000');
28
+ expect(result.source).toBe('command line argument');
29
+ });
30
+ test('default port used when no env or CLI port', function () {
31
+ var result = resolvePort(undefined, undefined);
32
+ expect(result.port).toBe('2022');
33
+ expect(result.source).toBe('default value');
34
+ });
35
+ });
36
+ describe('port validation', function () {
37
+ function validatePortNumber(port) {
38
+ var portNumber = parseInt(port, 10);
39
+ var valid = !isNaN(portNumber) && portNumber >= 1 && portNumber <= 65535;
40
+ return { valid: valid, parsed: portNumber };
41
+ }
42
+ test('accepts port 1', function () {
43
+ expect(validatePortNumber('1').valid).toBe(true);
44
+ });
45
+ test('accepts port 65535', function () {
46
+ expect(validatePortNumber('65535').valid).toBe(true);
47
+ });
48
+ test('accepts common ports', function () {
49
+ expect(validatePortNumber('80').valid).toBe(true);
50
+ expect(validatePortNumber('443').valid).toBe(true);
51
+ expect(validatePortNumber('3000').valid).toBe(true);
52
+ expect(validatePortNumber('8080').valid).toBe(true);
53
+ });
54
+ test('rejects port 0', function () {
55
+ expect(validatePortNumber('0').valid).toBe(false);
56
+ });
57
+ test('rejects negative ports', function () {
58
+ expect(validatePortNumber('-1').valid).toBe(false);
59
+ expect(validatePortNumber('-80').valid).toBe(false);
60
+ });
61
+ test('rejects ports above 65535', function () {
62
+ expect(validatePortNumber('65536').valid).toBe(false);
63
+ expect(validatePortNumber('70000').valid).toBe(false);
64
+ });
65
+ test('rejects non-numeric strings', function () {
66
+ expect(validatePortNumber('abc').valid).toBe(false);
67
+ expect(validatePortNumber('').valid).toBe(false);
68
+ expect(validatePortNumber('3.14').parsed).toBe(3); // parseInt truncates
69
+ });
70
+ });
71
+ });
72
+ describe('dev.ts environment variables', function () {
73
+ test('builds correct env vars object', function () {
74
+ var port = '3000';
75
+ var config = {
76
+ 'api-key': 'sk_test_123',
77
+ 'project-id': 'proj_abc',
78
+ environment: 'development',
79
+ 'out-dir': 'dist',
80
+ };
81
+ var envVars = {
82
+ PORT: port,
83
+ DAIN_API_KEY: config['api-key'],
84
+ DAIN_PROJECT_ID: config['project-id'],
85
+ DAIN_ENVIRONMENT: config.environment,
86
+ DAIN_OUT_DIR: config['out-dir'],
87
+ };
88
+ expect(envVars.PORT).toBe('3000');
89
+ expect(envVars.DAIN_API_KEY).toBe('sk_test_123');
90
+ expect(envVars.DAIN_PROJECT_ID).toBe('proj_abc');
91
+ expect(envVars.DAIN_ENVIRONMENT).toBe('development');
92
+ expect(envVars.DAIN_OUT_DIR).toBe('dist');
93
+ });
94
+ });
95
+ describe('dev.ts file watching', function () {
96
+ describe('watch paths calculation', function () {
97
+ test('includes main file directory', function () {
98
+ var mainFile = 'src/index.ts';
99
+ var watchPath = path_1.default.dirname(mainFile);
100
+ expect(watchPath).toBe('src');
101
+ });
102
+ test('includes static directory from config', function () {
103
+ var cwd = '/project';
104
+ var config = { 'static-dir': 'public' };
105
+ var staticPath = path_1.default.join(cwd, config['static-dir']);
106
+ expect(staticPath).toBe('/project/public');
107
+ });
108
+ });
109
+ describe('chokidar ignore pattern', function () {
110
+ test('ignores dotfiles and dotdirs', function () {
111
+ var pattern = /(^|[\/\\])\../;
112
+ expect(pattern.test('.git')).toBe(true);
113
+ expect(pattern.test('.env')).toBe(true);
114
+ expect(pattern.test('src/.hidden')).toBe(true);
115
+ expect(pattern.test('/path/to/.config')).toBe(true);
116
+ expect(pattern.test('src/index.ts')).toBe(false);
117
+ });
118
+ });
119
+ describe('debounce timer', function () {
120
+ beforeEach(function () {
121
+ jest.useFakeTimers();
122
+ });
123
+ afterEach(function () {
124
+ jest.useRealTimers();
125
+ });
126
+ test('debounces rapid file changes', function () {
127
+ var callCount = 0;
128
+ var debounceTimer = null;
129
+ var handleChange = function () {
130
+ if (debounceTimer)
131
+ clearTimeout(debounceTimer);
132
+ debounceTimer = setTimeout(function () {
133
+ callCount++;
134
+ }, 300);
135
+ };
136
+ // Rapid file changes
137
+ handleChange();
138
+ handleChange();
139
+ handleChange();
140
+ expect(callCount).toBe(0);
141
+ jest.advanceTimersByTime(300);
142
+ expect(callCount).toBe(1);
143
+ });
144
+ test('each new change resets debounce', function () {
145
+ var callCount = 0;
146
+ var debounceTimer = null;
147
+ var handleChange = function () {
148
+ if (debounceTimer)
149
+ clearTimeout(debounceTimer);
150
+ debounceTimer = setTimeout(function () {
151
+ callCount++;
152
+ }, 300);
153
+ };
154
+ handleChange();
155
+ jest.advanceTimersByTime(200);
156
+ handleChange(); // Reset timer
157
+ jest.advanceTimersByTime(200);
158
+ handleChange(); // Reset timer again
159
+ jest.advanceTimersByTime(300);
160
+ expect(callCount).toBe(1);
161
+ });
162
+ });
163
+ });
164
+ describe('dev.ts workers runtime', function () {
165
+ test('constructs correct output file path', function () {
166
+ var cwd = '/project';
167
+ var mainFile = 'src/index.ts';
168
+ var dainDir = path_1.default.join(cwd, '.dain');
169
+ var outFile = path_1.default.join(dainDir, path_1.default.basename(mainFile, '.ts') + '.mjs');
170
+ expect(outFile).toBe('/project/.dain/index.mjs');
171
+ });
172
+ test('handles different main file names', function () {
173
+ var getOutFile = function (mainFile) {
174
+ return path_1.default.basename(mainFile, '.ts') + '.mjs';
175
+ };
176
+ expect(getOutFile('src/index.ts')).toBe('index.mjs');
177
+ expect(getOutFile('src/server.ts')).toBe('server.mjs');
178
+ expect(getOutFile('app/main.ts')).toBe('main.mjs');
179
+ });
180
+ });
181
+ describe('dev.ts proxy requirements', function () {
182
+ test('requires api-key when proxy enabled', function () {
183
+ var noproxy = false;
184
+ var apiKey = '';
185
+ var proxyEnabled = !noproxy;
186
+ var apiKeyMissing = !apiKey;
187
+ expect(proxyEnabled && apiKeyMissing).toBe(true);
188
+ });
189
+ test('skips api-key check when proxy disabled', function () {
190
+ var noproxy = true;
191
+ var apiKey = '';
192
+ var shouldCheckApiKey = !noproxy;
193
+ expect(shouldCheckApiKey).toBe(false);
194
+ });
195
+ });
7
196
  describe('dev.ts security', function () {
8
197
  describe('path traversal protection', function () {
9
198
  // Extract the validation logic for testing