@warp-drive/holodeck 0.0.0-beta.15 → 0.0.0-beta.17

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.
Binary file
Binary file
@@ -0,0 +1,4 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 324 32">
2
+ <path fill="#110819"
3
+ d="M321.58 29.54a41.7 41.7 0 0 1-7.26 1.72c-2.4.36-4.87.53-7.4.53-3.21 0-6.09-.34-8.6-1.03a18.5 18.5 0 0 1-6.34-3.03 13.07 13.07 0 0 1-3.94-4.83c-.9-1.9-1.35-4.06-1.35-6.47a14.32 14.32 0 0 1 5.22-11.19 18.14 18.14 0 0 1 6.06-3.3c2.36-.81 5-1.21 7.9-1.21 2.68 0 5.13.36 7.35 1.1 2.24.71 4.15 1.78 5.74 3.2a14.28 14.28 0 0 1 3.72 5.24 18.12 18.12 0 0 1 1.33 7.17v1.98h-29.1a7.5 7.5 0 0 0 1.28 2.56c.6.77 1.4 1.43 2.42 1.97 1.02.55 2.27.97 3.74 1.27 1.5.3 3.27.45 5.33.45a35.35 35.35 0 0 0 8.23-.96c1.22-.3 2.31-.64 3.29-1 .99-.35 1.78-.7 2.38-1.07v6.9Zm-5.5-15.96a6.73 6.73 0 0 0-.66-2.26 5.82 5.82 0 0 0-1.72-2.13 9.46 9.46 0 0 0-3.1-1.54 15.6 15.6 0 0 0-4.77-.63c-1.79 0-3.33.23-4.64.67-1.3.44-2.4 1-3.29 1.65a8.37 8.37 0 0 0-2.04 2.13c-.48.76-.8 1.46-.94 2.1h21.16ZM271.84 31h-9.28L247.63 1.48h8.9l10.74 22.22 10.72-22.22h8.9L271.83 31ZM234.8 31V1.48h7.97V31h-7.97ZM229.73 8.16l-.94-.28c-.4-.13-.88-.25-1.42-.36a17.88 17.88 0 0 0-3.8-.45c-1.44 0-2.78.18-4.02.53-1.23.35-2.39.81-3.46 1.4a19.48 19.48 0 0 0-3 1.98c-.92.73-1.77 1.48-2.54 2.25V31h-7.97V1.48h7.97v4.8c.96-.72 1.94-1.41 2.94-2.07a25.36 25.36 0 0 1 3.16-1.78 18.9 18.9 0 0 1 3.48-1.23 15.39 15.39 0 0 1 5.44-.39 25 25 0 0 1 4.16.67v6.68ZM197.05 16.22c0 1.89-.2 3.57-.62 5.05a12.7 12.7 0 0 1-1.76 3.82 11.07 11.07 0 0 1-2.7 2.77 13.77 13.77 0 0 1-3.51 1.83c-1.29.46-2.68.8-4.17 1-1.49.21-3.04.31-4.66.31H160.9V1.48h18.69c1.62 0 3.17.1 4.66.32 1.49.2 2.88.53 4.17 1 1.3.45 2.48 1.06 3.54 1.82 1.06.74 1.96 1.67 2.7 2.77a12.7 12.7 0 0 1 1.77 3.82c.42 1.46.62 3.13.62 5Zm-8.03 0c0-1.42-.19-2.65-.56-3.7a5.64 5.64 0 0 0-1.76-2.57 7.66 7.66 0 0 0-3.16-1.53c-1.3-.34-2.9-.52-4.77-.52h-9.9v16.68h9.9c1.88 0 3.47-.17 4.77-.5a7.86 7.86 0 0 0 3.16-1.54c.81-.7 1.4-1.58 1.76-2.63.37-1.04.56-2.27.56-3.7ZM153.98 10.55c0 1.47-.25 2.78-.73 3.9a6.82 6.82 0 0 1-2.26 2.86 10.95 10.95 0 0 1-4 1.77 25.6 25.6 0 0 1-5.86.58h-13.26V31h-2.68V1.48h15.94c2.3 0 4.25.2 5.87.6 1.63.39 2.96.96 4 1.72a6.67 6.67 0 0 1 2.25 2.84c.48 1.13.73 2.43.73 3.9Zm-2.77 0c0-1.4-.24-2.54-.71-3.4a4.53 4.53 0 0 0-2.15-1.97c-.96-.48-2.19-.8-3.68-.95a50.43 50.43 0 0 0-5.26-.24h-11.54v13.15h11.54l2.51.02c.9 0 1.8-.04 2.67-.12.87-.1 1.7-.27 2.5-.5.8-.24 1.5-.6 2.1-1.1a5.05 5.05 0 0 0 1.46-1.9 7 7 0 0 0 .56-3ZM119.76 3.76c-.41-.13-1-.27-1.74-.41-.73-.16-1.67-.24-2.83-.24-1.6 0-3.12.2-4.54.6a17.94 17.94 0 0 0-7.28 3.96 23.73 23.73 0 0 0-2.8 2.83V31H97.9V1.48h2.69V7.5c.96-1 2-1.9 3.11-2.73a21.54 21.54 0 0 1 7.48-3.53 17.47 17.47 0 0 1 5.6-.47c.4.02.77.04 1.1.09a16.6 16.6 0 0 1 1.9.26v2.64ZM85.52 31v-3.27c-1.03.56-2.2 1.08-3.53 1.55a40.4 40.4 0 0 1-13.13 2.3c-1.91 0-3.64-.17-5.17-.52a12.2 12.2 0 0 1-3.87-1.52A7.38 7.38 0 0 1 57.37 27a7.06 7.06 0 0 1-.86-3.52c0-1.32.33-2.48 1-3.48a8.81 8.81 0 0 1 2.85-2.64c1.23-.76 2.71-1.4 4.45-1.91 1.74-.53 3.7-.98 5.84-1.34 2.15-.37 4.47-.67 6.96-.9 2.5-.24 5.14-.44 7.9-.6v-2.26c0-.91-.16-1.72-.5-2.42a5.54 5.54 0 0 0-1.45-1.83 7.97 7.97 0 0 0-2.17-1.31c-.83-.36-1.73-.65-2.7-.86a33.97 33.97 0 0 0-13.54.21c-1.03.25-2.02.53-2.96.86-.95.33-1.9.68-2.86 1.06V2.92c1.99-.53 4.1-.99 6.36-1.38 2.26-.4 4.66-.6 7.2-.6 2.14 0 4.15.19 6.01.56 1.86.36 3.48.94 4.86 1.74A9.16 9.16 0 0 1 87 6.36a8.55 8.55 0 0 1 1.18 4.62V31h-2.66Zm0-16c-4.69.25-8.69.62-12.01 1.09-3.31.47-6.02 1.05-8.12 1.74-2.1.69-3.66 1.49-4.64 2.4a4.18 4.18 0 0 0-1.46 3.12c0 .95.23 1.78.7 2.52a5.86 5.86 0 0 0 2.09 1.84c.9.49 2 .86 3.29 1.12 1.28.24 2.73.37 4.34.37a29.36 29.36 0 0 0 5.95-.6 36.6 36.6 0 0 0 5.71-1.57c.86-.32 1.64-.66 2.34-1.01a11.3 11.3 0 0 0 1.8-1.1v-9.93ZM28.65 1.48l10.53 27.37L50.09 1.48h2.99L41.2 31h-4L26.84 4.34 16.51 31h-4L.63 1.48h2.99l10.91 27.37 10.5-27.37h3.62Z" />
4
+ </svg>
@@ -0,0 +1,4 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 324 32">
2
+ <path fill="#FFC474"
3
+ d="M321.58 29.54a41.7 41.7 0 0 1-7.26 1.72c-2.4.36-4.87.53-7.4.53-3.21 0-6.09-.34-8.6-1.03a18.5 18.5 0 0 1-6.34-3.03 13.07 13.07 0 0 1-3.94-4.83c-.9-1.9-1.35-4.06-1.35-6.47a14.32 14.32 0 0 1 5.22-11.19 18.14 18.14 0 0 1 6.06-3.3c2.36-.81 5-1.21 7.9-1.21 2.68 0 5.13.36 7.35 1.1 2.24.71 4.15 1.78 5.74 3.2a14.28 14.28 0 0 1 3.72 5.24 18.12 18.12 0 0 1 1.33 7.17v1.98h-29.1a7.5 7.5 0 0 0 1.28 2.56c.6.77 1.4 1.43 2.42 1.97 1.02.55 2.27.97 3.74 1.27 1.5.3 3.27.45 5.33.45a35.35 35.35 0 0 0 8.23-.96c1.22-.3 2.31-.64 3.29-1 .99-.35 1.78-.7 2.38-1.07v6.9Zm-5.5-15.96a6.73 6.73 0 0 0-.66-2.26 5.82 5.82 0 0 0-1.72-2.13 9.46 9.46 0 0 0-3.1-1.54 15.6 15.6 0 0 0-4.77-.63c-1.79 0-3.33.23-4.64.67-1.3.44-2.4 1-3.29 1.65a8.37 8.37 0 0 0-2.04 2.13c-.48.76-.8 1.46-.94 2.1h21.16ZM271.84 31h-9.28L247.63 1.48h8.9l10.74 22.22 10.72-22.22h8.9L271.83 31ZM234.8 31V1.48h7.97V31h-7.97ZM229.73 8.16l-.94-.28c-.4-.13-.88-.25-1.42-.36a17.88 17.88 0 0 0-3.8-.45c-1.44 0-2.78.18-4.02.53-1.23.35-2.39.81-3.46 1.4a19.48 19.48 0 0 0-3 1.98c-.92.73-1.77 1.48-2.54 2.25V31h-7.97V1.48h7.97v4.8c.96-.72 1.94-1.41 2.94-2.07a25.36 25.36 0 0 1 3.16-1.78 18.9 18.9 0 0 1 3.48-1.23 15.39 15.39 0 0 1 5.44-.39 25 25 0 0 1 4.16.67v6.68ZM197.05 16.22c0 1.89-.2 3.57-.62 5.05a12.7 12.7 0 0 1-1.76 3.82 11.07 11.07 0 0 1-2.7 2.77 13.77 13.77 0 0 1-3.51 1.83c-1.29.46-2.68.8-4.17 1-1.49.21-3.04.31-4.66.31H160.9V1.48h18.69c1.62 0 3.17.1 4.66.32 1.49.2 2.88.53 4.17 1 1.3.45 2.48 1.06 3.54 1.82 1.06.74 1.96 1.67 2.7 2.77a12.7 12.7 0 0 1 1.77 3.82c.42 1.46.62 3.13.62 5Zm-8.03 0c0-1.42-.19-2.65-.56-3.7a5.64 5.64 0 0 0-1.76-2.57 7.66 7.66 0 0 0-3.16-1.53c-1.3-.34-2.9-.52-4.77-.52h-9.9v16.68h9.9c1.88 0 3.47-.17 4.77-.5a7.86 7.86 0 0 0 3.16-1.54c.81-.7 1.4-1.58 1.76-2.63.37-1.04.56-2.27.56-3.7ZM153.98 10.55c0 1.47-.25 2.78-.73 3.9a6.82 6.82 0 0 1-2.26 2.86 10.95 10.95 0 0 1-4 1.77 25.6 25.6 0 0 1-5.86.58h-13.26V31h-2.68V1.48h15.94c2.3 0 4.25.2 5.87.6 1.63.39 2.96.96 4 1.72a6.67 6.67 0 0 1 2.25 2.84c.48 1.13.73 2.43.73 3.9Zm-2.77 0c0-1.4-.24-2.54-.71-3.4a4.53 4.53 0 0 0-2.15-1.97c-.96-.48-2.19-.8-3.68-.95a50.43 50.43 0 0 0-5.26-.24h-11.54v13.15h11.54l2.51.02c.9 0 1.8-.04 2.67-.12.87-.1 1.7-.27 2.5-.5.8-.24 1.5-.6 2.1-1.1a5.05 5.05 0 0 0 1.46-1.9 7 7 0 0 0 .56-3ZM119.76 3.76c-.41-.13-1-.27-1.74-.41-.73-.16-1.67-.24-2.83-.24-1.6 0-3.12.2-4.54.6a17.94 17.94 0 0 0-7.28 3.96 23.73 23.73 0 0 0-2.8 2.83V31H97.9V1.48h2.69V7.5c.96-1 2-1.9 3.11-2.73a21.54 21.54 0 0 1 7.48-3.53 17.47 17.47 0 0 1 5.6-.47c.4.02.77.04 1.1.09a16.6 16.6 0 0 1 1.9.26v2.64ZM85.52 31v-3.27c-1.03.56-2.2 1.08-3.53 1.55a40.4 40.4 0 0 1-13.13 2.3c-1.91 0-3.64-.17-5.17-.52a12.2 12.2 0 0 1-3.87-1.52A7.38 7.38 0 0 1 57.37 27a7.06 7.06 0 0 1-.86-3.52c0-1.32.33-2.48 1-3.48a8.81 8.81 0 0 1 2.85-2.64c1.23-.76 2.71-1.4 4.45-1.91 1.74-.53 3.7-.98 5.84-1.34 2.15-.37 4.47-.67 6.96-.9 2.5-.24 5.14-.44 7.9-.6v-2.26c0-.91-.16-1.72-.5-2.42a5.54 5.54 0 0 0-1.45-1.83 7.97 7.97 0 0 0-2.17-1.31c-.83-.36-1.73-.65-2.7-.86a33.97 33.97 0 0 0-13.54.21c-1.03.25-2.02.53-2.96.86-.95.33-1.9.68-2.86 1.06V2.92c1.99-.53 4.1-.99 6.36-1.38 2.26-.4 4.66-.6 7.2-.6 2.14 0 4.15.19 6.01.56 1.86.36 3.48.94 4.86 1.74A9.16 9.16 0 0 1 87 6.36a8.55 8.55 0 0 1 1.18 4.62V31h-2.66Zm0-16c-4.69.25-8.69.62-12.01 1.09-3.31.47-6.02 1.05-8.12 1.74-2.1.69-3.66 1.49-4.64 2.4a4.18 4.18 0 0 0-1.46 3.12c0 .95.23 1.78.7 2.52a5.86 5.86 0 0 0 2.09 1.84c.9.49 2 .86 3.29 1.12 1.28.24 2.73.37 4.34.37a29.36 29.36 0 0 0 5.95-.6 36.6 36.6 0 0 0 5.71-1.57c.86-.32 1.64-.66 2.34-1.01a11.3 11.3 0 0 0 1.8-1.1v-9.93ZM28.65 1.48l10.53 27.37L50.09 1.48h2.99L41.2 31h-4L26.84 4.34 16.51 31h-4L.63 1.48h2.99l10.91 27.37 10.5-27.37h3.62Z" />
4
+ </svg>
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@warp-drive/holodeck",
3
3
  "description": "⚡️ Simple, Fast HTTP Mocking for Tests",
4
- "version": "0.0.0-beta.15",
4
+ "version": "0.0.0-beta.17",
5
5
  "license": "MIT",
6
6
  "author": "Chris Thoburn <runspired@users.noreply.github.com>",
7
7
  "repository": {
@@ -12,7 +12,7 @@
12
12
  "homepage": "https://github.com/emberjs/data",
13
13
  "bugs": "https://github.com/emberjs/data/issues",
14
14
  "engines": {
15
- "node": ">= 18.20.4"
15
+ "node": ">= 18.20.7"
16
16
  },
17
17
  "keywords": [
18
18
  "http-mock"
@@ -21,9 +21,9 @@
21
21
  "extends": "../../package.json"
22
22
  },
23
23
  "dependencies": {
24
- "@hono/node-server": "^1.11.1",
25
24
  "chalk": "^5.3.0",
26
- "hono": "^4.6.5"
25
+ "hono": "^4.7.2",
26
+ "@hono/node-server": "^1.13.8"
27
27
  },
28
28
  "type": "module",
29
29
  "files": [
@@ -32,28 +32,25 @@
32
32
  "README.md",
33
33
  "LICENSE.md",
34
34
  "server",
35
- "NCC-1701-a.svg",
36
- "NCC-1701-a-blue.svg"
35
+ "logos"
37
36
  ],
38
37
  "bin": {
39
38
  "ensure-cert": "./server/ensure-cert.js"
40
39
  },
41
40
  "peerDependencies": {
42
- "@ember-data/request": "5.4.0-beta.15",
43
- "@warp-drive/core-types": "0.0.0-beta.15"
41
+ "@ember-data/request": "5.4.0-beta.17",
42
+ "@warp-drive/core-types": "0.0.0-beta.17"
44
43
  },
45
44
  "devDependencies": {
46
- "@babel/core": "^7.24.5",
47
- "@babel/plugin-transform-typescript": "^7.24.5",
48
- "@babel/preset-env": "^7.24.5",
49
- "@babel/preset-typescript": "^7.24.1",
50
- "@babel/runtime": "^7.24.5",
51
- "@ember-data/request": "5.4.0-beta.15",
52
- "@warp-drive/core-types": "0.0.0-beta.15",
53
- "@warp-drive/internal-config": "5.4.0-beta.15",
54
- "pnpm-sync-dependencies-meta-injected": "0.0.14",
55
- "typescript": "^5.7.2",
56
- "vite": "^5.2.11"
45
+ "@babel/core": "^7.26.9",
46
+ "@babel/plugin-transform-typescript": "^7.26.8",
47
+ "@babel/preset-env": "^7.26.9",
48
+ "@babel/preset-typescript": "^7.26.0",
49
+ "@babel/runtime": "^7.26.9",
50
+ "@ember-data/request": "5.4.0-beta.17",
51
+ "@warp-drive/core-types": "0.0.0-beta.17",
52
+ "@warp-drive/internal-config": "5.4.0-beta.17",
53
+ "vite": "^5.4.14"
57
54
  },
58
55
  "exports": {
59
56
  ".": {
@@ -72,17 +69,11 @@
72
69
  "default": "./dist/mock.js"
73
70
  }
74
71
  },
75
- "dependenciesMeta": {
76
- "@ember-data/request": {
77
- "injected": true
78
- },
79
- "@warp-drive/core-types": {
80
- "injected": true
81
- }
82
- },
83
72
  "scripts": {
84
73
  "check:pkg-types": "tsc --noEmit",
85
74
  "build:pkg": "vite build;",
86
- "sync-hardlinks": "bun run sync-dependencies-meta-injected"
75
+ "sync": "echo \"syncing\"",
76
+ "_temporarily_deactivated_lint": "eslint . --quiet --cache --cache-strategy=content",
77
+ "start": "vite"
87
78
  }
88
79
  }
package/server/index.js CHANGED
@@ -1,23 +1,22 @@
1
1
  /* global Bun */
2
- import { serve } from '@hono/node-server';
3
2
  import chalk from 'chalk';
4
3
  import { Hono } from 'hono';
5
- import { cors } from 'hono/cors';
6
- import { HTTPException } from 'hono/http-exception';
4
+ import { serve } from '@hono/node-server';
5
+ import { createSecureServer } from 'node:http2';
7
6
  import { logger } from 'hono/logger';
7
+ import { HTTPException } from 'hono/http-exception';
8
+ import { cors } from 'hono/cors';
8
9
  import crypto from 'node:crypto';
9
10
  import fs from 'node:fs';
10
- import http2 from 'node:http2';
11
11
  import zlib from 'node:zlib';
12
12
  import { homedir } from 'os';
13
13
  import path from 'path';
14
14
 
15
- /** @type {import('bun-types')} */
16
15
  const isBun = typeof Bun !== 'undefined';
17
- const DEBUG = process.env.DEBUG?.includes('holodeck') || process.env.DEBUG === '*';
18
- const CURRENT_FILE = new URL(import.meta.url).pathname;
16
+ const DEBUG =
17
+ process.env.DEBUG?.includes('wd:holodeck') || process.env.DEBUG === '*' || process.env.DEBUG?.includes('wd:*');
19
18
 
20
- function getCertInfo() {
19
+ async function getCertInfo() {
21
20
  let CERT_PATH = process.env.HOLODECK_SSL_CERT_PATH;
22
21
  let KEY_PATH = process.env.HOLODECK_SSL_KEY_PATH;
23
22
 
@@ -39,16 +38,36 @@ function getCertInfo() {
39
38
  );
40
39
  }
41
40
 
42
- if (!fs.existsSync(CERT_PATH) || !fs.existsSync(KEY_PATH)) {
43
- throw new Error('SSL certificate or key not found, you may need to run `pnpx @warp-drive/holodeck ensure-cert`');
44
- }
41
+ if (isBun) {
42
+ const CERT = Bun.file(CERT_PATH);
43
+ const KEY = Bun.file(KEY_PATH);
45
44
 
46
- return {
47
- CERT_PATH,
48
- KEY_PATH,
49
- CERT: fs.readFileSync(CERT_PATH),
50
- KEY: fs.readFileSync(KEY_PATH),
51
- };
45
+ if (!(await CERT.exists()) || !(await KEY.exists())) {
46
+ throw new Error(
47
+ 'SSL certificate or key not found, you may need to run `pnpm dlx @warp-drive/holodeck ensure-cert`'
48
+ );
49
+ }
50
+
51
+ return {
52
+ CERT_PATH,
53
+ KEY_PATH,
54
+ CERT: await CERT.text(),
55
+ KEY: await KEY.text(),
56
+ };
57
+ } else {
58
+ if (!fs.existsSync(CERT_PATH) || !fs.existsSync(KEY_PATH)) {
59
+ throw new Error(
60
+ 'SSL certificate or key not found, you may need to run `pnpm dlx @warp-drive/holodeck ensure-cert`'
61
+ );
62
+ }
63
+
64
+ return {
65
+ CERT_PATH,
66
+ KEY_PATH,
67
+ CERT: fs.readFileSync(CERT_PATH, 'utf8'),
68
+ KEY: fs.readFileSync(KEY_PATH, 'utf8'),
69
+ };
70
+ }
52
71
  }
53
72
 
54
73
  const DEFAULT_PORT = 1135;
@@ -85,7 +104,7 @@ function getNiceUrl(url) {
85
104
  */
86
105
  function generateFilepath(options) {
87
106
  const { body } = options;
88
- const bodyHash = body ? crypto.createHash('md5').update(body).digest('hex') : null;
107
+ const bodyHash = body ? crypto.createHash('md5').update(JSON.stringify(body)).digest('hex') : null;
89
108
  const cacheDir = generateFileDir(options);
90
109
  return `${cacheDir}/${bodyHash ? `${bodyHash}-` : 'res'}`;
91
110
  }
@@ -94,10 +113,14 @@ function generateFileDir(options) {
94
113
  return `${projectRoot}/.mock-cache/${testId}/${method}-${testRequestNumber}-${url}`;
95
114
  }
96
115
 
97
- function replayRequest(context, cacheKey) {
98
- let meta;
116
+ async function replayRequest(context, cacheKey) {
117
+ let metaJson;
99
118
  try {
100
- meta = fs.readFileSync(`${cacheKey}.meta.json`, 'utf-8');
119
+ if (isBun) {
120
+ metaJson = await Bun.file(`${cacheKey}.meta.json`).json();
121
+ } else {
122
+ metaJson = JSON.parse(fs.readFileSync(`${cacheKey}.meta.json`, 'utf8'));
123
+ }
101
124
  } catch (e) {
102
125
  context.header('Content-Type', 'application/vnd.api+json');
103
126
  context.status(400);
@@ -108,18 +131,23 @@ function replayRequest(context, cacheKey) {
108
131
  status: '400',
109
132
  code: 'MOCK_NOT_FOUND',
110
133
  title: 'Mock not found',
111
- detail: `No mock found for ${context.req.method} ${context.req.url}. You may need to record a mock for this request.`,
134
+ detail: `No meta was found for ${context.req.method} ${context.req.url}. You may need to record a mock for this request.`,
112
135
  },
113
136
  ],
114
137
  })
115
138
  );
116
139
  }
117
140
 
118
- const metaJson = JSON.parse(meta);
119
141
  const bodyPath = `${cacheKey}.body.br`;
142
+ const bodyInit =
143
+ metaJson.status !== 204 && metaJson.status < 500
144
+ ? isBun
145
+ ? Bun.file(bodyPath)
146
+ : fs.createReadStream(bodyPath)
147
+ : '';
120
148
 
121
149
  const headers = new Headers(metaJson.headers || {});
122
- const bodyInit = metaJson.status !== 204 && metaJson.status < 500 ? fs.createReadStream(bodyPath) : '';
150
+ // @ts-expect-error - createReadStream is supported in node
123
151
  const response = new Response(bodyInit, {
124
152
  status: metaJson.status,
125
153
  statusText: metaJson.statusText,
@@ -191,12 +219,15 @@ function createTestHandler(projectRoot) {
191
219
  body: body ? JSON.stringify(body) : null,
192
220
  testRequestNumber,
193
221
  });
222
+ const compressedResponse = compress(JSON.stringify(response));
194
223
  // allow Content-Type to be overridden
195
224
  headers['Content-Type'] = headers['Content-Type'] || 'application/vnd.api+json';
196
225
  // We always compress and chunk the response
197
226
  headers['Content-Encoding'] = 'br';
198
227
  // we don't cache since tests will often reuse similar urls for different payload
199
228
  headers['Cache-Control'] = 'no-store';
229
+ // streaming requires Content-Length
230
+ headers['Content-Length'] = compressedResponse.length;
200
231
 
201
232
  const cacheDir = generateFileDir({
202
233
  projectRoot,
@@ -207,21 +238,30 @@ function createTestHandler(projectRoot) {
207
238
  });
208
239
 
209
240
  fs.mkdirSync(cacheDir, { recursive: true });
210
- fs.writeFileSync(
211
- `${cacheKey}.meta.json`,
212
- JSON.stringify({ url, status, statusText, headers, method, requestBody: body }, null, 2)
213
- );
214
- fs.writeFileSync(`${cacheKey}.body.br`, compress(JSON.stringify(response)));
241
+
242
+ if (isBun) {
243
+ const newMetaFile = Bun.file(`${cacheKey}.meta.json`);
244
+ await newMetaFile.write(JSON.stringify({ url, status, statusText, headers, method, requestBody: body }));
245
+ const newBodyFile = Bun.file(`${cacheKey}.body.br`);
246
+ await newBodyFile.write(compressedResponse);
247
+ } else {
248
+ fs.writeFileSync(
249
+ `${cacheKey}.meta.json`,
250
+ JSON.stringify({ url, status, statusText, headers, method, requestBody: body })
251
+ );
252
+ fs.writeFileSync(`${cacheKey}.body.br`, compressedResponse);
253
+ }
254
+
215
255
  context.status(204);
216
256
  return context.body(null);
217
257
  } else {
218
- const body = await req.text();
258
+ const body = req.body;
219
259
  const cacheKey = generateFilepath({
220
260
  projectRoot,
221
261
  testId,
222
262
  url: niceUrl,
223
263
  method: req.method,
224
- body,
264
+ body: body ? JSON.stringify(body) : null,
225
265
  testRequestNumber,
226
266
  });
227
267
  return replayRequest(context, cacheKey);
@@ -250,10 +290,68 @@ function createTestHandler(projectRoot) {
250
290
  return TestHandler;
251
291
  }
252
292
 
293
+ export function startNodeServer() {
294
+ const args = process.argv.slice();
295
+
296
+ if (!isBun && args.length) {
297
+ const options = JSON.parse(args[2]);
298
+ _createServer(options);
299
+ }
300
+ }
301
+
302
+ export function startWorker() {
303
+ // listen for launch message
304
+ globalThis.onmessage = async (event) => {
305
+ const { options } = event.data;
306
+
307
+ const { server } = await _createServer(options);
308
+
309
+ // listen for messages
310
+ globalThis.onmessage = (event) => {
311
+ const message = event.data;
312
+ if (message === 'end') {
313
+ server.close();
314
+ globalThis.close();
315
+ }
316
+ };
317
+ };
318
+ }
319
+
253
320
  /*
254
321
  { port?: number, projectRoot: string }
255
322
  */
256
- export function createServer(options) {
323
+ export async function createServer(options, useBun = false) {
324
+ if (!useBun) {
325
+ const CURRENT_FILE = new URL(import.meta.url).pathname;
326
+ const START_FILE = path.join(CURRENT_FILE, '../start-node.js');
327
+ const server = Bun.spawn(['node', '--experimental-default-type=module', START_FILE, JSON.stringify(options)], {
328
+ env: process.env,
329
+ cwd: process.cwd(),
330
+ stdin: 'inherit',
331
+ stdout: 'inherit',
332
+ stderr: 'inherit',
333
+ });
334
+
335
+ return {
336
+ terminate() {
337
+ server.kill();
338
+ // server.unref();
339
+ },
340
+ };
341
+ }
342
+
343
+ const worker = new Worker(new URL('./worker.js', import.meta.url), { type: 'module' });
344
+
345
+ worker.postMessage({
346
+ type: 'launch',
347
+ options,
348
+ });
349
+
350
+ return worker;
351
+ }
352
+
353
+ async function _createServer(options) {
354
+ const { CERT, KEY } = await getCertInfo();
257
355
  const app = new Hono();
258
356
  if (DEBUG) {
259
357
  app.use('*', logger());
@@ -272,38 +370,26 @@ export function createServer(options) {
272
370
  );
273
371
  app.all('*', createTestHandler(options.projectRoot));
274
372
 
275
- const { CERT, KEY } = getCertInfo();
276
-
277
- serve({
373
+ const server = serve({
374
+ overrideGlobalObjects: !isBun,
278
375
  fetch: app.fetch,
279
- createServer: (_, requestListener) => {
280
- try {
281
- return http2.createSecureServer(
282
- {
283
- key: KEY,
284
- cert: CERT,
285
- },
286
- requestListener
287
- );
288
- } catch (e) {
289
- console.log(chalk.yellow(`Failed to create secure server, falling back to http server. Error: ${e.message}`));
290
- return http2.createServer(requestListener);
291
- }
376
+ serverOptions: {
377
+ key: KEY,
378
+ cert: CERT,
292
379
  },
380
+ createServer: createSecureServer,
293
381
  port: options.port ?? DEFAULT_PORT,
294
- hostname: 'localhost',
295
- // bun uses TLS options
296
- // tls: {
297
- // key: Bun.file(KEY_PATH),
298
- // cert: Bun.file(CERT_PATH),
299
- // },
382
+ hostname: options.hostname ?? 'localhost',
300
383
  });
301
384
 
302
385
  console.log(
303
- `\tMock server running at ${chalk.magenta('https://localhost:') + chalk.yellow(options.port ?? DEFAULT_PORT)}`
386
+ `\tMock server running at ${chalk.yellow('https://') + chalk.magenta((options.hostname ?? 'localhost') + ':') + chalk.yellow(options.port ?? DEFAULT_PORT)}`
304
387
  );
388
+
389
+ return { app, server };
305
390
  }
306
391
 
392
+ /** @type {Map<string, Awaited<ReturnType<typeof createServer>>>} */
307
393
  const servers = new Map();
308
394
 
309
395
  export default {
@@ -325,59 +411,29 @@ export default {
325
411
  )}`
326
412
  )
327
413
  );
328
- console.log(chalk.grey(`\n\tStarting Subroutines (mode:${chalk.cyan(isBun ? 'bun' : 'node')})`));
329
-
330
- if (isBun) {
331
- const serverProcess = Bun.spawn(
332
- ['node', '--experimental-default-type=module', CURRENT_FILE, JSON.stringify(options)],
333
- {
334
- env: process.env,
335
- cwd: process.cwd(),
336
- stdout: 'inherit',
337
- stderr: 'inherit',
338
- }
339
- );
340
- servers.set(projectRoot, serverProcess);
341
- return;
342
- }
414
+ console.log(chalk.grey(`\n\tStarting Holodeck Subroutines (mode:${chalk.cyan(isBun ? 'bun' : 'node')})`));
343
415
 
344
416
  if (servers.has(projectRoot)) {
345
417
  throw new Error(`Holodeck is already running for project '${name}' at '${projectRoot}'`);
346
418
  }
347
419
 
348
- servers.set(projectRoot, createServer(options));
420
+ // toggle to true if Bun fixes CORS support for HTTP/2
421
+ const project = await createServer(options, false);
422
+ servers.set(projectRoot, project);
349
423
  },
350
424
  async endProgram() {
351
- console.log(chalk.grey(`\n\tEnding Subroutines (mode:${chalk.cyan(isBun ? 'bun' : 'node')})`));
425
+ console.log(chalk.grey(`\n\tEnding Holodeck Subroutines (mode:${chalk.cyan(isBun ? 'bun' : 'node')})`));
352
426
  const projectRoot = process.cwd();
353
427
 
354
428
  if (!servers.has(projectRoot)) {
355
- const name = await import(path.join(projectRoot, 'package.json'), { with: { type: 'json' } }).then(
356
- (pkg) => pkg.name
357
- );
358
- throw new Error(`Holodeck was not running for project '${name}' at '${projectRoot}'`);
359
- }
360
-
361
- if (isBun) {
362
- const serverProcess = servers.get(projectRoot);
363
- serverProcess.kill();
429
+ const name = require(path.join(projectRoot, 'package.json')).name;
430
+ console.log(chalk.red(`\n\nHolodeck was not running for project '${name}' at '${projectRoot}'\n\n`));
364
431
  return;
365
432
  }
366
433
 
367
- servers.get(projectRoot).close();
434
+ const project = servers.get(projectRoot);
368
435
  servers.delete(projectRoot);
436
+ project.terminate();
437
+ console.log(chalk.grey(`\n\tHolodeck program ended`));
369
438
  },
370
439
  };
371
-
372
- function main() {
373
- const args = process.argv.slice();
374
- if (!isBun && args.length) {
375
- if (args[1] !== CURRENT_FILE) {
376
- return;
377
- }
378
- const options = JSON.parse(args[2]);
379
- createServer(options);
380
- }
381
- }
382
-
383
- main();
@@ -0,0 +1,3 @@
1
+ import { startNodeServer } from './index.js';
2
+
3
+ startNodeServer();
@@ -0,0 +1,12 @@
1
+ {
2
+ "include": ["."],
3
+ "compilerOptions": {
4
+ "target": "ESNext",
5
+ "module": "ESNext",
6
+ "moduleResolution": "bundler",
7
+ "types": ["bun-types"],
8
+ "allowJs": true,
9
+ "checkJs": true,
10
+ "noEmit": true
11
+ }
12
+ }
@@ -0,0 +1,3 @@
1
+ import { startWorker } from './index.js';
2
+
3
+ startWorker();
File without changes