@tamyla/clodo-framework 4.0.13 → 4.0.14

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.
Files changed (62) hide show
  1. package/CHANGELOG.md +11 -0
  2. package/README.md +7 -0
  3. package/dist/cli/commands/create.js +2 -1
  4. package/dist/middleware/Composer.js +38 -0
  5. package/dist/middleware/Registry.js +14 -0
  6. package/dist/middleware/index.js +3 -0
  7. package/dist/middleware/shared/basicAuth.js +21 -0
  8. package/dist/middleware/shared/cors.js +28 -0
  9. package/dist/middleware/shared/index.js +3 -0
  10. package/dist/middleware/shared/logging.js +14 -0
  11. package/dist/service-management/GenerationEngine.js +13 -2
  12. package/dist/service-management/ServiceOrchestrator.js +6 -2
  13. package/dist/service-management/generators/code/ServiceMiddlewareGenerator.js +156 -10
  14. package/dist/service-management/generators/code/WorkerIndexGenerator.js +75 -9
  15. package/dist/simple-api.js +32 -1
  16. package/docs/MIDDLEWARE_MIGRATION_SUMMARY.md +121 -0
  17. package/package.json +4 -1
  18. package/scripts/DEPLOY_COMMAND_NEW.js +128 -0
  19. package/scripts/README-automated-testing-suite.md +356 -0
  20. package/scripts/README-test-clodo-deployment.md +157 -0
  21. package/scripts/README.md +50 -0
  22. package/scripts/analyze-imports.ps1 +104 -0
  23. package/scripts/analyze-mixed-code.js +163 -0
  24. package/scripts/analyze-mixed-rationale.js +149 -0
  25. package/scripts/automated-testing-suite.js +776 -0
  26. package/scripts/deployment/README.md +31 -0
  27. package/scripts/deployment/deploy-domain.ps1 +449 -0
  28. package/scripts/deployment/deploy-staging.js +120 -0
  29. package/scripts/deployment/validate-staging.js +166 -0
  30. package/scripts/diagnose-imports.js +362 -0
  31. package/scripts/framework-diagnostic.js +368 -0
  32. package/scripts/migration/migrate-middleware-legacy-to-contract.js +47 -0
  33. package/scripts/post-publish-test.js +663 -0
  34. package/scripts/scan-worker-issues.js +52 -0
  35. package/scripts/service-management/README.md +27 -0
  36. package/scripts/service-management/setup-interactive.ps1 +693 -0
  37. package/scripts/test-clodo-deployment.js +588 -0
  38. package/scripts/test-downstream-install.js +237 -0
  39. package/scripts/test-local-package.ps1 +126 -0
  40. package/scripts/test-local-package.sh +166 -0
  41. package/scripts/test-package.js +339 -0
  42. package/scripts/testing/README.md +49 -0
  43. package/scripts/testing/test-first.ps1 +0 -0
  44. package/scripts/testing/test-first50.ps1 +0 -0
  45. package/scripts/testing/test.ps1 +0 -0
  46. package/scripts/utilities/README.md +61 -0
  47. package/scripts/utilities/check-bin.js +8 -0
  48. package/scripts/utilities/check-bundle.js +23 -0
  49. package/scripts/utilities/check-dist-imports.js +65 -0
  50. package/scripts/utilities/check-import-paths.js +191 -0
  51. package/scripts/utilities/cleanup-cli.js +159 -0
  52. package/scripts/utilities/deployment-helpers.ps1 +199 -0
  53. package/scripts/utilities/fix-dist-imports.js +135 -0
  54. package/scripts/utilities/generate-secrets.js +159 -0
  55. package/scripts/utilities/safe-push.ps1 +51 -0
  56. package/scripts/utilities/setup-helpers.ps1 +206 -0
  57. package/scripts/utilities/test-packaged-artifact.js +92 -0
  58. package/scripts/utilities/validate-dist-imports.js +189 -0
  59. package/scripts/utilities/validate-schema.js +102 -0
  60. package/scripts/verify-exports.js +193 -0
  61. package/scripts/verify-worker-safety.js +73 -0
  62. package/types/middleware.d.ts +1 -0
@@ -0,0 +1,339 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Comprehensive Package Testing Script
4
+ * Tests the built package as a downstream developer would use it
5
+ *
6
+ * This script:
7
+ * 1. Creates a temporary test project
8
+ * 2. Packs the current package (creates a .tgz file)
9
+ * 3. Installs it in the test project like a real user would
10
+ * 4. Tests all exported modules and APIs
11
+ * 5. Checks for import errors, circular dependencies, and runtime issues
12
+ */
13
+
14
+ import { execSync } from 'child_process';
15
+ import { mkdirSync, writeFileSync, rmSync, existsSync, readFileSync, readdirSync, statSync } from 'fs';
16
+ import { join } from 'path';
17
+ import { fileURLToPath } from 'url';
18
+ import { dirname } from 'path';
19
+
20
+ const __filename = fileURLToPath(import.meta.url);
21
+ const __dirname = dirname(__filename);
22
+ const projectRoot = join(__dirname, '..');
23
+ const testDir = join(projectRoot, 'tmp', 'package-test');
24
+
25
+ console.log('\n🧪 Starting Comprehensive Package Test\n');
26
+ console.log('━'.repeat(60));
27
+
28
+ // Clean up previous test
29
+ if (existsSync(testDir)) {
30
+ console.log('🧹 Cleaning up previous test directory...');
31
+ rmSync(testDir, { recursive: true, force: true });
32
+ }
33
+
34
+ // Step 1: Build the package
35
+ console.log('\nšŸ“¦ Step 1: Building package...');
36
+ try {
37
+ execSync('npm run build', { cwd: projectRoot, stdio: 'inherit' });
38
+ console.log('āœ… Build successful');
39
+ } catch (error) {
40
+ console.error('āŒ Build failed');
41
+ process.exit(1);
42
+ }
43
+
44
+ // Step 2: Pack the package (creates .tgz)
45
+ console.log('\nšŸ“¦ Step 2: Packing package...');
46
+ let tarballPath;
47
+ try {
48
+ const output = execSync('npm pack', { cwd: projectRoot, encoding: 'utf8' });
49
+ tarballPath = output.trim().split('\n').pop();
50
+ console.log(`āœ… Package created: ${tarballPath}`);
51
+ } catch (error) {
52
+ console.error('āŒ Pack failed');
53
+ process.exit(1);
54
+ }
55
+
56
+ // Step 3: Create test project
57
+ console.log('\nšŸ“ Step 3: Creating test project...');
58
+ mkdirSync(testDir, { recursive: true });
59
+
60
+ // Create package.json for test project
61
+ const testPackageJson = {
62
+ name: 'test-downstream-project',
63
+ version: '1.0.0',
64
+ type: 'module',
65
+ private: true
66
+ };
67
+ writeFileSync(
68
+ join(testDir, 'package.json'),
69
+ JSON.stringify(testPackageJson, null, 2)
70
+ );
71
+ console.log('āœ… Test project created');
72
+
73
+ // Step 4: Install the packed package
74
+ console.log('\nšŸ“„ Step 4: Installing package in test project...');
75
+ try {
76
+ const tarballFullPath = join(projectRoot, tarballPath);
77
+ execSync(`npm install "${tarballFullPath}"`, {
78
+ cwd: testDir,
79
+ stdio: 'inherit'
80
+ });
81
+ console.log('āœ… Package installed');
82
+ } catch (error) {
83
+ console.error('āŒ Installation failed');
84
+ process.exit(1);
85
+ }
86
+
87
+ // Step 5: Test all exports
88
+ console.log('\n🧪 Step 5: Testing package exports...');
89
+
90
+ const tests = [
91
+ {
92
+ name: 'Main Entry Point',
93
+ code: `import * as framework from '@tamyla/clodo-framework';
94
+ console.log('āœ“ Main export loaded');
95
+ if (!framework.initializeService) throw new Error('Missing initializeService');
96
+ console.log('āœ“ initializeService exists');`
97
+ },
98
+ {
99
+ name: 'Worker Integration',
100
+ code: `import { initializeService, configManager } from '@tamyla/clodo-framework/worker';
101
+ console.log('āœ“ Worker exports loaded');
102
+ if (typeof initializeService !== 'function') throw new Error('initializeService is not a function');
103
+ console.log('āœ“ initializeService is a function');`
104
+ },
105
+ {
106
+ name: 'Config Module',
107
+ code: `import * as config from '@tamyla/clodo-framework/config';
108
+ console.log('āœ“ Config exports loaded');
109
+ if (!config.getDomainFromEnv) throw new Error('Missing getDomainFromEnv');
110
+ console.log('āœ“ getDomainFromEnv exists');`
111
+ },
112
+ {
113
+ name: 'Services Module',
114
+ code: `import { GenericDataService, createDataService } from '@tamyla/clodo-framework/services';
115
+ console.log('āœ“ Services exports loaded');
116
+ if (typeof createDataService !== 'function') throw new Error('createDataService is not a function');
117
+ console.log('āœ“ createDataService is a function');`
118
+ },
119
+ {
120
+ name: 'Schema Module',
121
+ code: `import { SchemaManager, schemaManager } from '@tamyla/clodo-framework/schema';
122
+ console.log('āœ“ Schema exports loaded');
123
+ if (!SchemaManager) throw new Error('Missing SchemaManager');
124
+ console.log('āœ“ SchemaManager exists');`
125
+ },
126
+ {
127
+ name: 'Routing Module',
128
+ code: `import { EnhancedRouter, createEnhancedRouter } from '@tamyla/clodo-framework/routing';
129
+ console.log('āœ“ Routing exports loaded');
130
+ if (typeof createEnhancedRouter !== 'function') throw new Error('createEnhancedRouter is not a function');
131
+ console.log('āœ“ createEnhancedRouter is a function');`
132
+ },
133
+ {
134
+ name: 'Security Module',
135
+ code: `import { SecurityCLI, SecretGenerator } from '@tamyla/clodo-framework/security';
136
+ console.log('āœ“ Security exports loaded');
137
+ if (!SecurityCLI) throw new Error('Missing SecurityCLI');
138
+ console.log('āœ“ SecurityCLI exists');`
139
+ },
140
+ {
141
+ name: 'Service Management',
142
+ code: `import { ServiceCreator, ServiceOrchestrator } from '@tamyla/clodo-framework/service-management';
143
+ console.log('āœ“ Service management exports loaded');
144
+ if (!ServiceCreator) throw new Error('Missing ServiceCreator');
145
+ console.log('āœ“ ServiceCreator exists');`
146
+ },
147
+ {
148
+ name: 'Database Module',
149
+ code: `import * as db from '@tamyla/clodo-framework/database';
150
+ console.log('āœ“ Database exports loaded');
151
+ if (!db.DatabaseOrchestrator) throw new Error('Missing DatabaseOrchestrator');
152
+ console.log('āœ“ DatabaseOrchestrator exists');`
153
+ },
154
+ {
155
+ name: 'Deployment Module',
156
+ code: `import { DeploymentValidator, DeploymentAuditor } from '@tamyla/clodo-framework/deployment';
157
+ console.log('āœ“ Deployment exports loaded');
158
+ if (!DeploymentValidator) throw new Error('Missing DeploymentValidator');
159
+ console.log('āœ“ DeploymentValidator exists');`
160
+ },
161
+ {
162
+ name: 'Cloudflare Utils',
163
+ code: `import * as cf from '@tamyla/clodo-framework/utils/cloudflare';
164
+ console.log('āœ“ Cloudflare utils exports loaded');
165
+ if (!cf.CloudflareAPI) throw new Error('Missing CloudflareAPI');
166
+ console.log('āœ“ CloudflareAPI exists');`
167
+ },
168
+ {
169
+ name: 'Dynamic Import Test (frameworkConfig)',
170
+ code: `import { initializeService } from '@tamyla/clodo-framework/worker';
171
+ // Test that dynamic imports work at runtime
172
+ const env = { DOMAIN: 'test' };
173
+ const domains = { test: { name: 'test', accountId: '123' } };
174
+ try {
175
+ const result = initializeService(env, domains);
176
+ console.log('āœ“ initializeService executed without import errors');
177
+ } catch (error) {
178
+ if (error.message.includes('Cannot find module')) {
179
+ throw new Error('Dynamic import path broken: ' + error.message);
180
+ }
181
+ // Other errors are OK (we're not testing full functionality, just imports)
182
+ console.log('āœ“ No import path errors (functional errors are OK for this test)');
183
+ }`
184
+ },
185
+ {
186
+ name: 'Circular Dependency Check',
187
+ code: `import * as framework from '@tamyla/clodo-framework';
188
+ import * as worker from '@tamyla/clodo-framework/worker';
189
+ import * as config from '@tamyla/clodo-framework/config';
190
+ console.log('āœ“ No circular dependency errors on import');
191
+ // Check that we can access nested properties without infinite loops
192
+ if (config.getDomainFromEnv) {
193
+ console.log('āœ“ Config module accessible');
194
+ }
195
+ if (worker.initializeService) {
196
+ console.log('āœ“ Worker module accessible');
197
+ }
198
+ console.log('āœ“ No circular reference runtime errors');`
199
+ }
200
+ ];
201
+
202
+ let passed = 0;
203
+ let failed = 0;
204
+ const failures = [];
205
+
206
+ for (const test of tests) {
207
+ const testFile = join(testDir, `test-${passed + failed}.mjs`);
208
+ writeFileSync(testFile, test.code);
209
+
210
+ try {
211
+ console.log(`\n Testing: ${test.name}`);
212
+ execSync(`node "${testFile}"`, { cwd: testDir, encoding: 'utf8', stdio: 'pipe' });
213
+ console.log(` āœ… ${test.name} passed`);
214
+ passed++;
215
+ } catch (error) {
216
+ console.error(` āŒ ${test.name} failed`);
217
+ console.error(` ${error.message}`);
218
+ failed++;
219
+ failures.push({ name: test.name, error: error.message });
220
+ }
221
+ }
222
+
223
+ // Step 6: Check for common issues in dist/
224
+ console.log('\nšŸ” Step 6: Checking for common packaging issues...');
225
+
226
+ const distPath = join(projectRoot, 'dist');
227
+ const issuesFound = [];
228
+
229
+ // Check for references to src/ or ../lib/ that escaped outside dist/
230
+ function checkFileForBadImports(filePath, relativePath) {
231
+ const content = readFileSync(filePath, 'utf8');
232
+ const lines = content.split('\n');
233
+
234
+ //Skip re-export wrappers in dist/utils/ that are intended to re-export from dist/lib/
235
+ // Normalize path separators to forward slashes for consistent matching
236
+ const normalizedPath = relativePath.replace(/\\/g, '/');
237
+ const isReExportWrapper = normalizedPath.match(/^utils\/(file-manager|formatters|logger|index|cloudflare\/ops)\.js$/);
238
+ const isServiceManagementGenerator = normalizedPath.match(/^service-management\/generators\/testing\//);
239
+
240
+ lines.forEach((line, index) => {
241
+ // Check for imports that go outside dist/
242
+ // Skip if this is within lib/ going to another lib location or to config/ (which is also in dist/)
243
+ const isLibToLib = normalizedPath.startsWith('lib/') && line.match(/from\s+['"]\.\.\/\.\.\//);
244
+ const isLibToConfig = normalizedPath.startsWith('lib/shared/config/') && line.includes('../../../config/');
245
+
246
+ if (line.match(/from\s+['"]\.\.\/\.\.\/\.\.\//)) {
247
+ if (!isLibToLib && !isLibToConfig) {
248
+ issuesFound.push({
249
+ file: relativePath,
250
+ line: index + 1,
251
+ issue: 'Import path goes too far up (../../../)',
252
+ code: line.trim()
253
+ });
254
+ }
255
+ }
256
+
257
+ // Check for absolute imports to src/ or lib/
258
+ if (line.match(/from\s+['"].*\/src\//) && !isServiceManagementGenerator) {
259
+ issuesFound.push({
260
+ file: relativePath,
261
+ line: index + 1,
262
+ issue: 'Absolute import to src/ (should be relative within dist/)',
263
+ code: line.trim()
264
+ });
265
+ }
266
+
267
+ // Check for imports that try to access lib/ from project root
268
+ if (line.match(/from\s+['"]\.\.\/lib\//) && !relativePath.includes('lib\\') && !relativePath.includes('lib/') && !isReExportWrapper) {
269
+ issuesFound.push({
270
+ file: relativePath,
271
+ line: index + 1,
272
+ issue: 'Import tries to access lib/ outside dist/',
273
+ code: line.trim()
274
+ });
275
+ }
276
+ });
277
+ }
278
+
279
+ function walkDir(dir, baseDir = dir) {
280
+ const files = readdirSync(dir, { withFileTypes: true });
281
+ files.forEach(file => {
282
+ const fullPath = join(dir, file.name);
283
+ if (file.isDirectory()) {
284
+ walkDir(fullPath, baseDir);
285
+ } else if (file.name.endsWith('.js')) {
286
+ const relativePath = fullPath.substring(baseDir.length + 1);
287
+ checkFileForBadImports(fullPath, relativePath);
288
+ }
289
+ });
290
+ }
291
+
292
+ walkDir(distPath);
293
+
294
+ if (issuesFound.length > 0) {
295
+ console.log('\nāš ļø Found potential packaging issues:');
296
+ issuesFound.forEach(issue => {
297
+ console.log(`\n File: ${issue.file}:${issue.line}`);
298
+ console.log(` Issue: ${issue.issue}`);
299
+ console.log(` Code: ${issue.code}`);
300
+ });
301
+ } else {
302
+ console.log('āœ… No obvious packaging issues found');
303
+ }
304
+
305
+ // Final Report
306
+ console.log('\n' + '━'.repeat(60));
307
+ console.log('\nšŸ“Š TEST SUMMARY');
308
+ console.log('━'.repeat(60));
309
+ console.log(`\n āœ… Passed: ${passed}`);
310
+ console.log(` āŒ Failed: ${failed}`);
311
+
312
+ if (issuesFound.length > 0) {
313
+ console.log(` āš ļø Packaging issues: ${issuesFound.length}`);
314
+ }
315
+
316
+ if (failed > 0) {
317
+ console.log('\nāŒ FAILURES:');
318
+ failures.forEach(f => {
319
+ console.log(`\n ${f.name}:`);
320
+ console.log(` ${f.error}`);
321
+ });
322
+ }
323
+
324
+ console.log('\n' + '━'.repeat(60));
325
+
326
+ // Cleanup
327
+ console.log('\n🧹 Cleaning up...');
328
+ const tarballFullPath = join(projectRoot, tarballPath);
329
+ if (existsSync(tarballFullPath)) {
330
+ rmSync(tarballFullPath);
331
+ }
332
+
333
+ if (failed > 0 || issuesFound.length > 0) {
334
+ console.log('\nāŒ Package has issues that need to be fixed before publishing!\n');
335
+ process.exit(1);
336
+ } else {
337
+ console.log('\nāœ… Package is ready for publication!\n');
338
+ process.exit(0);
339
+ }
@@ -0,0 +1,49 @@
1
+ # Testing Scripts
2
+
3
+ Scripts for testing Clodo Framework services and functionality.
4
+
5
+ ## Scripts
6
+
7
+ ### test.ps1
8
+ Basic test runner for services.
9
+
10
+ **Usage:**
11
+ ```powershell
12
+ .\scripts\testing\test.ps1
13
+ ```
14
+
15
+ ### test-first.ps1
16
+ Test the first service or item in a collection.
17
+
18
+ **Features:**
19
+ - Focused testing on initial items
20
+ - Quick validation of basic functionality
21
+ - Useful for CI/CD pipelines
22
+
23
+ **Usage:**
24
+ ```powershell
25
+ .\scripts\testing\test-first.ps1 -ServiceName my-service
26
+ ```
27
+
28
+ ### test-first50.ps1
29
+ Test the first 50 services or items.
30
+
31
+ **Features:**
32
+ - Batch testing of multiple items
33
+ - Performance validation
34
+ - Scalability testing
35
+
36
+ **Usage:**
37
+ ```powershell
38
+ .\scripts\testing\test-first50.ps1 -ServiceType api-gateway
39
+ ```
40
+
41
+ ## Common Parameters
42
+
43
+ All testing scripts support:
44
+ - `ServiceName` - Name of the service to test
45
+ - `ServiceType` - Type of service to test
46
+ - `DomainName` - Domain to test against
47
+ - `NonInteractive` - Run without user interaction
48
+ - `TemplatePath` - Path to test templates
49
+ - `OutputPath` - Output directory for test results
Binary file
Binary file
Binary file
@@ -0,0 +1,61 @@
1
+ # Utility Scripts
2
+
3
+ General utility scripts for maintenance, validation, and operations.
4
+
5
+ ## Scripts
6
+
7
+ ### check-bundle.js
8
+ Validate and check bundle integrity for deployments.
9
+
10
+ **Features:**
11
+ - Bundle size validation
12
+ - Dependency checking
13
+ - Asset verification
14
+ - Performance metrics
15
+
16
+ **Usage:**
17
+ ```bash
18
+ node scripts/utilities/check-bundle.js
19
+ ```
20
+
21
+ ### cleanup-cli.js
22
+ Clean up CLI tools and temporary files.
23
+
24
+ **Features:**
25
+ - Remove temporary files
26
+ - Clean build artifacts
27
+ - Clear cache directories
28
+ - Disk space optimization
29
+
30
+ **Usage:**
31
+ ```bash
32
+ node scripts/utilities/cleanup-cli.js
33
+ ```
34
+
35
+ ### generate-secrets.js
36
+ Generate secure secrets and tokens for services.
37
+
38
+ **Features:**
39
+ - Cryptographically secure random generation
40
+ - Multiple secret types (API keys, JWT secrets, etc.)
41
+ - Configurable entropy levels
42
+ - Secure storage recommendations
43
+
44
+ **Usage:**
45
+ ```bash
46
+ node scripts/utilities/generate-secrets.js --type jwt --length 256
47
+ ```
48
+
49
+ ### validate-schema.js
50
+ Validate configuration schemas and data structures.
51
+
52
+ **Features:**
53
+ - JSON schema validation
54
+ - Configuration file checking
55
+ - Data structure verification
56
+ - Error reporting and suggestions
57
+
58
+ **Usage:**
59
+ ```bash
60
+ node scripts/utilities/validate-schema.js --file config/domains.js --schema domain-schema.json
61
+ ```
@@ -0,0 +1,8 @@
1
+ import fs from 'fs';
2
+ const content = fs.readFileSync('src/service-management/GenerationEngine.js', 'utf8');
3
+ const lines = content.split('\n');
4
+ lines.forEach((line, i) => {
5
+ if (line.includes('bin/')) {
6
+ console.log(`${i+1}: ${line.trim()}`);
7
+ }
8
+ });
@@ -0,0 +1,23 @@
1
+ import fs from 'fs';
2
+ import path from 'path';
3
+ import { fileURLToPath } from 'url';
4
+
5
+ const __filename = fileURLToPath(import.meta.url);
6
+ const __dirname = path.dirname(__filename);
7
+
8
+ // Go up two levels from scripts/utilities/ to project root, then to dist/
9
+ const distDir = path.join(__dirname, '..', '..', 'dist');
10
+
11
+ if (!fs.existsSync(distDir)) {
12
+ console.error('dist/ directory does not exist. Run npm run build first.');
13
+ process.exit(1);
14
+ }
15
+
16
+ const files = fs.readdirSync(distDir);
17
+ if (files.length === 0) {
18
+ console.error('dist/ directory is empty. Build may have failed.');
19
+ process.exit(1);
20
+ }
21
+
22
+ console.log(`Bundle check passed: ${files.length} files in dist/`);
23
+ files.forEach(file => console.log(`- ${file}`));
@@ -0,0 +1,65 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * Check Dist Imports
5
+ *
6
+ * Validates that all relative imports in `dist/` resolve to paths inside `dist/`.
7
+ * This prevents published packages from including relative imports that escape
8
+ * the `dist/` tree and point to non-existent files at package root.
9
+ */
10
+ import fs from 'fs';
11
+ import path from 'path';
12
+ import { fileURLToPath } from 'url';
13
+
14
+ const __dirname = path.dirname(fileURLToPath(import.meta.url));
15
+ const projectRoot = path.join(__dirname, '../../');
16
+ const distRoot = path.join(projectRoot, 'dist');
17
+
18
+ if (!fs.existsSync(distRoot)) {
19
+ console.error('dist/ directory not found. Run: npm run build');
20
+ process.exit(1);
21
+ }
22
+
23
+ function walk(dir) {
24
+ const entries = fs.readdirSync(dir, { withFileTypes: true });
25
+ const files = [];
26
+ for (const e of entries) {
27
+ const full = path.join(dir, e.name);
28
+ if (e.isDirectory()) files.push(...walk(full));
29
+ else if (e.isFile() && full.endsWith('.js')) files.push(full);
30
+ }
31
+ return files;
32
+ }
33
+
34
+ const importRegex = /(?:from\s+['"]([^'"\n]+)['"])|(?:import\(['"]([^'"\n]+)['"]\))/g;
35
+ let failures = [];
36
+ const files = walk(distRoot);
37
+ for (const file of files) {
38
+ const content = fs.readFileSync(file, 'utf8');
39
+ let m;
40
+ while ((m = importRegex.exec(content)) !== null) {
41
+ const imp = m[1] || m[2];
42
+ if (!imp) continue;
43
+ // only check relative imports
44
+ if (!imp.startsWith('.')) continue;
45
+
46
+ // Resolve the import target
47
+ const resolved = path.resolve(path.dirname(file), imp);
48
+ const normalizedDist = path.resolve(distRoot) + path.sep;
49
+ if (!resolved.startsWith(normalizedDist)) {
50
+ failures.push({ file, imp, resolved });
51
+ }
52
+ }
53
+ }
54
+
55
+ if (failures.length > 0) {
56
+ console.error('\nāŒ Dist import validation failed. The following relative imports resolve outside dist/:\n');
57
+ for (const f of failures) {
58
+ console.error(` - ${path.relative(projectRoot, f.file)} imports '${f.imp}' → resolves to ${f.resolved}`);
59
+ }
60
+ console.error(`\nRecommendation: adjust relative imports so they point into 'dist/' (e.g. '../lib/...' from dist/<subdir>/ files).`);
61
+ process.exit(1);
62
+ }
63
+
64
+ console.log('\nāœ… All relative imports in dist/ resolve inside dist/.');
65
+ process.exit(0);