vmlive 1.0.7 → 1.0.9

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/README.md CHANGED
@@ -33,11 +33,7 @@ Starts the local emulation environment.
33
33
  - Provides local proxy bindings for your SQL Database (`env.DB`), Global KV (`env.KV`), and Object Storage (`env.BUCKET`) using the `.vmlive/` directory for persistent storage.
34
34
  - Supports hot-reloading for `.js` and `.ts` files.
35
35
 
36
- ### `npx vmlive test`
37
- Executes your Vitest suites against a strictly ephemeral **in-memory** engine.
38
- - Spins up `VirtualMachine` in memory-only mode without touching `.vmlive/` disk databases.
39
- - Runs `npx vitest run` and gracefully destroys the engine upon exit.
40
- - Allows test suites to deeply mutate state predictably without sandbox bleeding.
36
+
41
37
 
42
38
  ### `npx vmlive deploy`
43
39
  Uploads the local code to the vm.live edge platform.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vmlive",
3
- "version": "1.0.7",
3
+ "version": "1.0.9",
4
4
  "description": "Local development VM for custom Serverless PaaS",
5
5
  "type": "module",
6
6
  "bin": {
package/src/cli.js CHANGED
@@ -40,8 +40,9 @@ const buildProxyDispatcher = (functions, workspaceId, projectId, includeDashboar
40
40
  return new Response(null, { status: 204 });
41
41
  }
42
42
 
43
+ const explicitTarget = request.headers.get("x-vm-target");
43
44
  const hostHeader = request.headers.get("Host") || url.hostname;
44
- let targetName = hostHeader.split('.')[0];
45
+ let targetName = explicitTarget || hostHeader.split('.')[0];
45
46
 
46
47
  let res;
47
48
 
@@ -61,7 +62,10 @@ const buildProxyDispatcher = (functions, workspaceId, projectId, includeDashboar
61
62
  const status = res.status;
62
63
  const color = status >= 500 ? "\\x1b[31m" : status >= 400 ? "\\x1b[33m" : "\\x1b[32m";
63
64
 
65
+ const isMuted = path.startsWith('/.internal/') || path.includes('devtools');
66
+ if (!isMuted) {
64
67
  console.log(\`\\x1b[90m[\${targetName}]\\x1b[0m \${method} \${path} \${color}\${status}\\x1b[0m\`);
68
+ }
65
69
  return res;
66
70
  }
67
71
  }
@@ -93,7 +97,7 @@ const runInit = async () => {
93
97
  process.exit(1);
94
98
  }
95
99
 
96
- const GATEKEEPER_URL = process.env.GATEKEEPER_URL || 'http://localhost:8787';
100
+ const GATEKEEPER_URL = process.env.GATEKEEPER_URL || 'https://api.vm.live';
97
101
 
98
102
  // 2. Extract Workspace Architecture
99
103
  console.log('\x1b[36mFetching workspaces...\x1b[0m');
@@ -526,100 +530,6 @@ const runDev = async () => {
526
530
  });
527
531
  };
528
532
 
529
- const runTest = async () => {
530
- if (!fs.existsSync(CONFIG_PATH)) {
531
- throw new Error("Missing vm.json in project root. Run 'vm init' to scaffold a new project workspace.");
532
- }
533
-
534
- const testArgs = process.argv.slice(3);
535
- console.log('\x1b[36mInitializing Ephemeral Target Engine for Integration Tests...\x1b[0m');
536
-
537
- const config = JSON.parse(fs.readFileSync(CONFIG_PATH, 'utf-8'));
538
- if (!fs.existsSync(WORK_DIR)) fs.mkdirSync(WORK_DIR, { recursive: true });
539
-
540
- const sharedBindings = fs.existsSync(path.resolve('.env'))
541
- ? dotenv.parse(fs.readFileSync(path.resolve('.env')))
542
- : {};
543
-
544
- const resourcesConfig = buildResourcesConfig();
545
-
546
- const entryPoints = config.functions.reduce((acc, fn) => {
547
- acc[`${fn.name}-out`] = path.resolve(fn.entry);
548
- return acc;
549
- }, {});
550
-
551
- const workspaceId = config.workspaceId || "ws_local";
552
- const projectId = config.projectId || "prj_local";
553
-
554
- config.functions.forEach(fn => {
555
- const relativeTarget = `./${fn.name}-out.mjs`;
556
- fs.writeFileSync(path.join(WORK_DIR, `${fn.name}-shim.mjs`), generateShim(relativeTarget, workspaceId, projectId, fn.name));
557
- });
558
-
559
- const builder = await esbuild.context({
560
- entryPoints,
561
- bundle: true,
562
- format: 'esm',
563
- outdir: WORK_DIR,
564
- outExtension: { '.js': '.mjs' },
565
- external: ['cloudflare:*'],
566
- logLevel: 'silent'
567
- });
568
-
569
- await builder.rebuild();
570
-
571
- const shadowSource = fs.readFileSync(path.join(__dirname, 'shadow-dos.js'), 'utf-8');
572
- fs.writeFileSync(path.join(WORK_DIR, 'shadow-dos.mjs'), shadowSource);
573
-
574
- const mfPort = process.env.PORT ? parseInt(process.env.PORT) : 8787;
575
-
576
- // Exact parity workers but omitting the Dashboard intentionally
577
- const miniflareWorkers = [
578
- buildProxyDispatcher(config.functions, workspaceId, projectId, false),
579
- {
580
- name: "vm-shadow-worker",
581
- modules: true,
582
- scriptPath: path.join(WORK_DIR, 'shadow-dos.mjs'),
583
- bindings: { PORT: mfPort },
584
- durableObjects: { LocalTaskManagerDO: "LocalTaskManagerDO", LocalChannelRoomDO: "LocalChannelRoomDO" }
585
- },
586
- ...config.functions.map(fn => ({
587
- name: fn.name,
588
- modules: true,
589
- scriptPath: path.join(WORK_DIR, `${fn.name}-shim.mjs`),
590
- bindings: sharedBindings,
591
- ...resourcesConfig,
592
- durableObjects: {
593
- TASK_DO: { className: "LocalTaskManagerDO", scriptName: "vm-shadow-worker" },
594
- CHANNEL_DO: { className: "LocalChannelRoomDO", scriptName: "vm-shadow-worker" }
595
- }
596
- }))
597
- ];
598
-
599
- // IN-MEMORY EMULATOR (NO PERSIST PATHS)
600
- const mf = new Emulator({
601
- workers: miniflareWorkers,
602
- port: mfPort
603
- });
604
-
605
- await mf.ready;
606
- console.log('\x1b[32m✔ Ephemeral environment completely provisioned and bound.\x1b[0m\\n');
607
-
608
- const { spawn } = await import('child_process');
609
-
610
- const testProcess = spawn('npx', ['vitest', 'run', ...testArgs], {
611
- stdio: 'inherit',
612
- env: { ...process.env, VMLIVE_TESTING: "true" }
613
- });
614
-
615
- testProcess.on('exit', async (code) => {
616
- console.log('\\n\x1b[36mTerminating ephemeral engine execution context...\x1b[0m');
617
- await builder.dispose();
618
- await mf.dispose();
619
- process.exit(code || 0);
620
- });
621
- };
622
-
623
533
  const runDeploy = async () => {
624
534
  console.log('\x1b[36mDeploying...\x1b[0m');
625
535
 
@@ -669,7 +579,7 @@ const runDeploy = async () => {
669
579
 
670
580
  const code = fs.readFileSync(outPath, 'utf8');
671
581
 
672
- const GATEKEEPER_URL = process.env.GATEKEEPER_URL || 'http://localhost:8787';
582
+ const GATEKEEPER_URL = process.env.GATEKEEPER_URL || 'https://api.vm.live';
673
583
  const target = `${GATEKEEPER_URL}/api/${workspaceId}/projects/${projectId}/functions/${fn.name}/deploy`;
674
584
 
675
585
  console.log(`\x1b[36m[POST]\x1b[0m Uploading...`);
@@ -700,7 +610,7 @@ const runLogin = async () => {
700
610
  const codeVerifier = crypto.randomBytes(32).toString('base64url');
701
611
  const codeChallenge = crypto.createHash('sha256').update(codeVerifier).digest('base64url');
702
612
 
703
- const GATEKEEPER_URL = process.env.GATEKEEPER_URL || 'http://localhost:8787';
613
+ const GATEKEEPER_URL = process.env.GATEKEEPER_URL || 'https://api.vm.live';
704
614
 
705
615
  const server = http.createServer(async (req, res) => {
706
616
  const url = new URL(req.url, `http://127.0.0.1:${server.address().port}`);
@@ -778,8 +688,6 @@ const main = async () => {
778
688
  await runAdd();
779
689
  } else if (command === 'dev') {
780
690
  await runDev();
781
- } else if (command === 'test') {
782
- await runTest();
783
691
  } else if (command === 'login') {
784
692
  await runLogin();
785
693
  } else if (command === 'deploy') {
@@ -789,7 +697,7 @@ const main = async () => {
789
697
  console.log(`vmlive CLI v${pkg.version}`);
790
698
  } else {
791
699
  console.error(`\x1b[31m❌ Unknown command: ${command || 'missing'}\x1b[0m`);
792
- console.log('Usage: vm init | vm add | vm dev | vm test | vm login | vm deploy | vm which');
700
+ console.log('Usage: vmlive init | vmlive add | vmlive dev | vmlive login | vmlive deploy | vmlive which');
793
701
  process.exit(1);
794
702
  }
795
703
  };
package/src/shadow-dos.js CHANGED
@@ -49,7 +49,7 @@ export class LocalTaskManagerDO extends DurableObject {
49
49
  const res = await fetch(invokeUrl, {
50
50
  method: 'POST',
51
51
  headers: {
52
- 'Host': `${task.function_slug}.localhost`,
52
+ 'x-vm-target': `${task.workspace_id}-${task.project_id}-${task.function_slug}`,
53
53
  'Content-Type': 'application/json',
54
54
  'x-vm-system-signature': 'local-bypass'
55
55
  },