wrangler 0.0.0-ece06ea → 0.0.0-ecfbb0c

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 (190) hide show
  1. package/Cloudflare_CA.pem +18 -0
  2. package/README.md +33 -14
  3. package/bin/wrangler.js +123 -25
  4. package/kv-asset-handler.js +1 -0
  5. package/miniflare-dist/index.mjs +4893 -0
  6. package/package.json +157 -116
  7. package/src/__tests__/config-cache.test.ts +36 -0
  8. package/src/__tests__/configuration.test.ts +3829 -0
  9. package/src/__tests__/dev.test.tsx +1159 -0
  10. package/src/__tests__/guess-worker-format.test.ts +85 -0
  11. package/src/__tests__/{clipboardy-mock.js → helpers/clipboardy-mock.js} +0 -0
  12. package/src/__tests__/helpers/cmd-shim.d.ts +11 -0
  13. package/src/__tests__/helpers/faye-websocket.d.ts +6 -0
  14. package/src/__tests__/helpers/mock-account-id.ts +48 -0
  15. package/src/__tests__/helpers/mock-bin.ts +36 -0
  16. package/src/__tests__/helpers/mock-cfetch.ts +211 -0
  17. package/src/__tests__/helpers/mock-console.ts +97 -0
  18. package/src/__tests__/helpers/mock-dialogs.ts +153 -0
  19. package/src/__tests__/helpers/mock-http-server.ts +46 -0
  20. package/src/__tests__/helpers/mock-istty.ts +74 -0
  21. package/src/__tests__/helpers/mock-kv.ts +33 -0
  22. package/src/__tests__/helpers/mock-oauth-flow.ts +250 -0
  23. package/src/__tests__/helpers/mock-process.ts +39 -0
  24. package/src/__tests__/helpers/mock-stdin.ts +108 -0
  25. package/src/__tests__/helpers/mock-web-socket.ts +29 -0
  26. package/src/__tests__/helpers/run-in-tmp.ts +39 -0
  27. package/src/__tests__/helpers/run-wrangler.ts +16 -0
  28. package/src/__tests__/helpers/write-worker-source.ts +31 -0
  29. package/src/__tests__/helpers/write-wrangler-toml.ts +17 -0
  30. package/src/__tests__/https-options.test.ts +130 -0
  31. package/src/__tests__/index.test.ts +255 -148
  32. package/src/__tests__/init.test.ts +1992 -0
  33. package/src/__tests__/jest.setup.ts +107 -0
  34. package/src/__tests__/kv.test.ts +1619 -0
  35. package/src/__tests__/logger.test.ts +141 -0
  36. package/src/__tests__/package-manager.test.ts +353 -0
  37. package/src/__tests__/pages.test.ts +1254 -0
  38. package/src/__tests__/parse.test.ts +330 -0
  39. package/src/__tests__/publish.test.ts +6513 -0
  40. package/src/__tests__/pubsub.test.ts +276 -0
  41. package/src/__tests__/r2.test.ts +233 -0
  42. package/src/__tests__/route.test.ts +45 -0
  43. package/src/__tests__/secret.test.ts +486 -0
  44. package/src/__tests__/tail.test.ts +690 -0
  45. package/src/__tests__/user.test.ts +143 -0
  46. package/src/__tests__/whoami.test.tsx +220 -0
  47. package/src/abort.d.ts +3 -0
  48. package/src/api/dev.ts +39 -0
  49. package/src/api/index.ts +1 -0
  50. package/src/bundle-reporter.tsx +29 -0
  51. package/src/bundle.ts +212 -0
  52. package/src/cfetch/index.ts +121 -0
  53. package/src/cfetch/internal.ts +149 -0
  54. package/src/cli.ts +25 -8
  55. package/src/config/README.md +107 -0
  56. package/src/config/config.ts +243 -0
  57. package/src/config/diagnostics.ts +80 -0
  58. package/src/config/environment.ts +430 -0
  59. package/src/config/index.ts +225 -0
  60. package/src/config/validation-helpers.ts +584 -0
  61. package/src/config/validation.ts +1693 -0
  62. package/src/config-cache.ts +44 -0
  63. package/src/create-worker-preview.ts +199 -0
  64. package/src/create-worker-upload-form.ts +254 -0
  65. package/src/dev/dev-vars.ts +36 -0
  66. package/src/dev/dev.tsx +417 -0
  67. package/src/dev/local.tsx +350 -0
  68. package/src/dev/remote.tsx +294 -0
  69. package/src/dev/use-esbuild.ts +150 -0
  70. package/src/dev.tsx +517 -727
  71. package/src/dialogs.tsx +116 -66
  72. package/src/durable.ts +102 -0
  73. package/src/entry.ts +328 -0
  74. package/src/environment-variables.ts +35 -0
  75. package/src/errors.ts +11 -0
  76. package/src/generate.ts +33 -0
  77. package/src/git-client.ts +42 -0
  78. package/src/https-options.ts +123 -0
  79. package/src/index.tsx +1845 -1828
  80. package/src/init.ts +549 -0
  81. package/src/inspect.ts +662 -0
  82. package/src/intl-polyfill.d.ts +139 -0
  83. package/src/kv.ts +408 -0
  84. package/src/logger.ts +73 -0
  85. package/src/miniflare-cli/README.md +30 -0
  86. package/src/miniflare-cli/enum-keys.ts +17 -0
  87. package/src/miniflare-cli/index.ts +52 -0
  88. package/src/miniflare-cli/request-context.ts +40 -0
  89. package/src/module-collection.ts +258 -0
  90. package/src/open-in-browser.ts +17 -0
  91. package/src/package-manager.ts +174 -0
  92. package/src/pages/build.tsx +202 -0
  93. package/src/pages/constants.ts +7 -0
  94. package/src/pages/deployments.tsx +101 -0
  95. package/src/pages/dev.tsx +964 -0
  96. package/src/pages/functions/buildPlugin.ts +105 -0
  97. package/src/pages/functions/buildWorker.ts +151 -0
  98. package/src/pages/functions/filepath-routing.test.ts +234 -0
  99. package/src/pages/functions/filepath-routing.ts +189 -0
  100. package/src/pages/functions/identifiers.ts +78 -0
  101. package/src/pages/functions/routes.ts +151 -0
  102. package/src/pages/index.tsx +84 -0
  103. package/src/pages/projects.tsx +157 -0
  104. package/src/pages/publish.tsx +335 -0
  105. package/src/pages/types.ts +40 -0
  106. package/src/pages/upload.tsx +384 -0
  107. package/src/pages/utils.ts +12 -0
  108. package/src/parse.ts +286 -0
  109. package/src/paths.ts +26 -0
  110. package/src/preview.ts +31 -0
  111. package/src/proxy.ts +476 -0
  112. package/src/publish.ts +755 -343
  113. package/src/pubsub/index.ts +286 -0
  114. package/src/pubsub/pubsub-commands.tsx +594 -0
  115. package/src/r2.ts +50 -0
  116. package/src/selfsigned.d.ts +29 -0
  117. package/src/sites.tsx +357 -100
  118. package/src/tail/filters.ts +279 -0
  119. package/src/tail/index.ts +299 -0
  120. package/src/tail/printing.ts +67 -0
  121. package/src/update-check.ts +19 -0
  122. package/src/user/choose-account.tsx +60 -0
  123. package/src/user/env-vars.ts +46 -0
  124. package/src/user/generate-auth-url.ts +33 -0
  125. package/src/user/generate-random-state.ts +16 -0
  126. package/src/user/index.ts +3 -0
  127. package/src/user/user.tsx +1130 -0
  128. package/src/whoami.tsx +90 -0
  129. package/src/worker.ts +185 -0
  130. package/src/zones.ts +76 -0
  131. package/templates/checked-fetch.js +17 -0
  132. package/templates/gitignore +171 -0
  133. package/templates/new-worker-scheduled.js +17 -0
  134. package/templates/new-worker-scheduled.ts +32 -0
  135. package/templates/new-worker.js +15 -0
  136. package/templates/new-worker.ts +30 -0
  137. package/templates/no-op-worker.js +10 -0
  138. package/templates/pages-template-plugin.ts +155 -0
  139. package/templates/pages-template-worker.ts +161 -0
  140. package/templates/static-asset-facade.js +43 -0
  141. package/templates/tsconfig.json +105 -0
  142. package/wrangler-dist/cli.js +104118 -113381
  143. package/src/__tests__/fixtures/init/.gitkeep +0 -0
  144. package/src/__tests__/mock-cfetch.js +0 -46
  145. package/src/api/form_data.ts +0 -158
  146. package/src/api/inspect.ts +0 -441
  147. package/src/api/preview.ts +0 -123
  148. package/src/api/worker.ts +0 -161
  149. package/src/cfetch.ts +0 -72
  150. package/src/config.ts +0 -124
  151. package/src/kv.tsx +0 -211
  152. package/src/pages.tsx +0 -344
  153. package/src/tail.tsx +0 -71
  154. package/src/user.tsx +0 -1029
  155. package/src/util/fetch.ts +0 -74
  156. package/static-asset-facade.js +0 -47
  157. package/vendor/@cloudflare/kv-asset-handler/CHANGELOG.md +0 -332
  158. package/vendor/@cloudflare/kv-asset-handler/LICENSE_APACHE +0 -176
  159. package/vendor/@cloudflare/kv-asset-handler/LICENSE_MIT +0 -25
  160. package/vendor/@cloudflare/kv-asset-handler/README.md +0 -245
  161. package/vendor/@cloudflare/kv-asset-handler/dist/index.d.ts +0 -32
  162. package/vendor/@cloudflare/kv-asset-handler/dist/index.js +0 -354
  163. package/vendor/@cloudflare/kv-asset-handler/dist/mocks.d.ts +0 -13
  164. package/vendor/@cloudflare/kv-asset-handler/dist/mocks.js +0 -148
  165. package/vendor/@cloudflare/kv-asset-handler/dist/test/getAssetFromKV.d.ts +0 -1
  166. package/vendor/@cloudflare/kv-asset-handler/dist/test/getAssetFromKV.js +0 -436
  167. package/vendor/@cloudflare/kv-asset-handler/dist/test/mapRequestToAsset.d.ts +0 -1
  168. package/vendor/@cloudflare/kv-asset-handler/dist/test/mapRequestToAsset.js +0 -40
  169. package/vendor/@cloudflare/kv-asset-handler/dist/test/serveSinglePageApp.d.ts +0 -1
  170. package/vendor/@cloudflare/kv-asset-handler/dist/test/serveSinglePageApp.js +0 -42
  171. package/vendor/@cloudflare/kv-asset-handler/dist/types.d.ts +0 -26
  172. package/vendor/@cloudflare/kv-asset-handler/dist/types.js +0 -31
  173. package/vendor/@cloudflare/kv-asset-handler/package.json +0 -52
  174. package/vendor/@cloudflare/kv-asset-handler/src/index.ts +0 -296
  175. package/vendor/@cloudflare/kv-asset-handler/src/mocks.ts +0 -136
  176. package/vendor/@cloudflare/kv-asset-handler/src/test/getAssetFromKV.ts +0 -464
  177. package/vendor/@cloudflare/kv-asset-handler/src/test/mapRequestToAsset.ts +0 -33
  178. package/vendor/@cloudflare/kv-asset-handler/src/test/serveSinglePageApp.ts +0 -42
  179. package/vendor/@cloudflare/kv-asset-handler/src/types.ts +0 -39
  180. package/vendor/wrangler-mime/CHANGELOG.md +0 -289
  181. package/vendor/wrangler-mime/LICENSE +0 -21
  182. package/vendor/wrangler-mime/Mime.js +0 -97
  183. package/vendor/wrangler-mime/README.md +0 -187
  184. package/vendor/wrangler-mime/cli.js +0 -46
  185. package/vendor/wrangler-mime/index.js +0 -4
  186. package/vendor/wrangler-mime/lite.js +0 -4
  187. package/vendor/wrangler-mime/package.json +0 -52
  188. package/vendor/wrangler-mime/types/other.js +0 -1
  189. package/vendor/wrangler-mime/types/standard.js +0 -1
  190. package/wrangler-dist/cli.js.map +0 -7
File without changes
@@ -1,46 +0,0 @@
1
- // This file mocks ../cfetch.ts
2
- // so we can insert whatever responses we want from it
3
-
4
- const pathToRegexp = require("path-to-regexp");
5
- // TODO: add jsdoc style types here
6
-
7
- // type MockHandler = (resource: string, init?: RequestInit) => any; // TODO: use a generic here
8
-
9
- let mocks = [];
10
-
11
- function mockCfetch(resource, init) {
12
- for (const { regexp, handler } of mocks) {
13
- if (regexp.test(resource)) {
14
- return handler(resource, init); // should we have some kind of fallthrough system? we'll see.
15
- }
16
- }
17
- throw new Error(`no mocks found for ${resource}`);
18
- }
19
-
20
- function setMock(resource, handler) {
21
- const mock = {
22
- resource,
23
- handler,
24
- regexp: pathToRegexp(resource),
25
- };
26
- mocks.push(mock);
27
- return () => {
28
- mocks = mocks.filter((x) => x !== mock);
29
- };
30
- }
31
-
32
- function unsetAllMocks() {
33
- mocks = [];
34
- }
35
-
36
- const CF_API_BASE_URL =
37
- process.env.CF_API_BASE_URL || "https://api.cloudflare.com/client/v4";
38
-
39
- Object.assign(module.exports, {
40
- __esModule: true,
41
- default: mockCfetch,
42
- mockCfetch,
43
- setMock,
44
- unsetAllMocks,
45
- CF_API_BASE_URL,
46
- });
@@ -1,158 +0,0 @@
1
- import type {
2
- CfWorkerInit,
3
- CfModuleType,
4
- CfVariable,
5
- CfModule,
6
- } from "./worker.js";
7
- import { FormData, Blob } from "formdata-node";
8
-
9
- // Credit: https://stackoverflow.com/a/9458996
10
- function toBase64(source: BufferSource): string {
11
- let result = "";
12
- const buffer = source instanceof ArrayBuffer ? source : source.buffer;
13
- const bytes = new Uint8Array(buffer);
14
- for (let i = 0; i < bytes.byteLength; i++) {
15
- result += String.fromCharCode(bytes[i]);
16
- }
17
- return btoa(result);
18
- }
19
-
20
- function toBinding(
21
- name: string,
22
- variable: CfVariable
23
- ): Record<string, unknown> {
24
- if (typeof variable === "string") {
25
- return { name, type: "plain_text", text: variable };
26
- }
27
-
28
- if ("namespaceId" in variable) {
29
- return {
30
- name,
31
- type: "kv_namespace",
32
- namespace_id: variable.namespaceId,
33
- };
34
- }
35
-
36
- if ("class_name" in variable) {
37
- return {
38
- name,
39
- type: "durable_object_namespace",
40
- class_name: variable.class_name,
41
- ...(variable.script_name && {
42
- script_name: variable.script_name,
43
- }),
44
- };
45
- }
46
-
47
- const { format, algorithm, usages, data } = variable;
48
- if (format) {
49
- let key_base64;
50
- let key_jwk;
51
- if (data instanceof ArrayBuffer || ArrayBuffer.isView(data)) {
52
- key_base64 = toBase64(data);
53
- } else {
54
- key_jwk = data;
55
- }
56
- return {
57
- name,
58
- type: "secret_key",
59
- format,
60
- algorithm,
61
- usages,
62
- key_base64,
63
- key_jwk,
64
- };
65
- }
66
-
67
- throw new TypeError("Unsupported variable: " + variable);
68
- }
69
-
70
- export function toMimeType(type: CfModuleType): string {
71
- switch (type) {
72
- case "esm":
73
- return "application/javascript+module";
74
- case "commonjs":
75
- return "application/javascript";
76
- case "compiled-wasm":
77
- return "application/wasm";
78
- case "buffer":
79
- return "application/octet-stream";
80
- case "text":
81
- return "text/plain";
82
- default:
83
- throw new TypeError("Unsupported module: " + type);
84
- }
85
- }
86
-
87
- function toModule(module: CfModule, entryType?: CfModuleType): Blob {
88
- const { type: moduleType, content } = module;
89
- const type = toMimeType(moduleType ?? entryType);
90
-
91
- return new Blob([content], { type });
92
- }
93
-
94
- /**
95
- * Creates a `FormData` upload from a `CfWorkerInit`.
96
- */
97
- export function toFormData(worker: CfWorkerInit): FormData {
98
- const formData = new FormData();
99
- const {
100
- main,
101
- modules,
102
- variables,
103
- migrations,
104
- usage_model,
105
- compatibility_date,
106
- compatibility_flags,
107
- } = worker;
108
- const { name, type: mainType } = main;
109
-
110
- const bindings = [];
111
- for (const [name, variable] of Object.entries(variables ?? {})) {
112
- const binding = toBinding(name, variable);
113
- bindings.push(binding);
114
- }
115
-
116
- const metadata =
117
- mainType !== "commonjs"
118
- ? {
119
- main_module: name,
120
- bindings,
121
- }
122
- : {
123
- body_part: name,
124
- bindings,
125
- };
126
- if (compatibility_date) {
127
- // @ts-expect-error - we should type metadata
128
- metadata.compatibility_date = compatibility_date;
129
- }
130
- if (compatibility_flags) {
131
- // @ts-expect-error - we should type metadata
132
- metadata.compatibility_flags = compatibility_flags;
133
- }
134
- if (usage_model) {
135
- // @ts-expect-error - we should type metadata
136
- metadata.usage_model = usage_model;
137
- }
138
- if (migrations) {
139
- // @ts-expect-error - we should type metadata
140
- metadata.migrations = migrations;
141
- }
142
-
143
- formData.set("metadata", JSON.stringify(metadata));
144
-
145
- if (mainType === "commonjs" && modules && modules.length > 0) {
146
- throw new TypeError(
147
- "More than one module can only be specified when type = 'esm'"
148
- );
149
- }
150
-
151
- for (const module of [main].concat(modules || [])) {
152
- const { name } = module;
153
- const blob = toModule(module, mainType ?? "esm");
154
- formData.set(name, blob, name);
155
- }
156
-
157
- return formData;
158
- }
@@ -1,441 +0,0 @@
1
- import { Response, Request, Headers } from "node-fetch";
2
- import type { MessageEvent } from "ws";
3
- import WebSocket, { WebSocketServer } from "ws";
4
- import type { IncomingMessage, ServerResponse } from "http";
5
- import { createServer } from "http";
6
-
7
- /**
8
- * A call frame.
9
- *
10
- * @link https://chromedevtools.github.io/devtools-protocol/1-3/Runtime/#type-CallFrame
11
- */
12
- export interface DtCallFrame {
13
- /**
14
- * The module path.
15
- *
16
- * @example
17
- * 'worker.js'
18
- */
19
- url: string;
20
- /**
21
- * The function name.
22
- */
23
- functionName?: string;
24
- /**
25
- * The line number. (0-based)
26
- */
27
- lineNumber: number;
28
- /**
29
- * The column number. (0-based)
30
- */
31
- columnNumber: number;
32
- }
33
-
34
- /**
35
- * A stack trace.
36
- *
37
- * @link https://chromedevtools.github.io/devtools-protocol/1-3/Runtime/#type-StackTrace
38
- */
39
- export interface DtStackTrace {
40
- /**
41
- * A description of the stack, only present in certain contexts.
42
- */
43
- description?: string;
44
- /**
45
- * The call frames.
46
- */
47
- callFrames: DtCallFrame[];
48
- /**
49
- * The parent stack trace.
50
- */
51
- parent?: DtStackTrace;
52
- }
53
-
54
- /**
55
- * A JavaScript object type.
56
- */
57
- export type DtRemoteObjectType =
58
- | "object"
59
- | "function"
60
- | "undefined"
61
- | "string"
62
- | "number"
63
- | "boolean"
64
- | "symbol"
65
- | "bigint";
66
-
67
- /**
68
- * A view of a remote JavaScript object.
69
- *
70
- * @link https://chromedevtools.github.io/devtools-protocol/1-3/Runtime/#type-RemoteObject
71
- */
72
- export interface DtRemoteObject<T> {
73
- /**
74
- * The object type.
75
- *
76
- * @example
77
- * 'string'
78
- */
79
- type: DtRemoteObjectType;
80
- /**
81
- * The specific object type, if the type is `object`.
82
- *
83
- * @example
84
- * 'arraybuffer'
85
- */
86
- subtype?: string;
87
- /**
88
- * The class name, if the type if `object`.
89
- */
90
- className?: string;
91
- /**
92
- * The object as a string.
93
- *
94
- * @example
95
- * 'Array(1)'
96
- * 'TypeError: Oops!\n at worker.js:5:15'
97
- */
98
- description?: string;
99
- /**
100
- * The object.
101
- */
102
- value?: T;
103
- // TODO(soon): add a preview field for more complex types
104
- }
105
-
106
- /**
107
- * An event when `console.log()` is invoked.
108
- *
109
- * @link https://chromedevtools.github.io/devtools-protocol/1-3/Runtime/#event-consoleAPICalled
110
- */
111
- export interface DtLogEvent {
112
- timestamp: number;
113
- type: string;
114
- args: DtRemoteObject<unknown>[];
115
- stackTrace?: DtStackTrace;
116
- }
117
-
118
- /**
119
- * An event when an uncaught `Error` is thrown.
120
- *
121
- * @link https://chromedevtools.github.io/devtools-protocol/1-3/Runtime/#event-exceptionThrown
122
- */
123
- export interface DtExceptionEvent {
124
- timestamp: number;
125
- exceptionDetails: {
126
- lineNumber: number;
127
- columnNumber: number;
128
- exception: DtRemoteObject<Error>;
129
- stackTrace: DtStackTrace;
130
- };
131
- }
132
-
133
- /**
134
- * A DevTools event.
135
- */
136
- export type DtEvent = DtLogEvent | DtExceptionEvent;
137
-
138
- /**
139
- * A listener that receives DevTools events.
140
- */
141
- export type DtListener = (event: DtEvent) => void;
142
-
143
- interface DtProtocolRequest<T> {
144
- id: number;
145
- method: string;
146
- params?: T;
147
- }
148
-
149
- interface DtProtocolResponse<T> {
150
- id: number;
151
- result: T;
152
- }
153
-
154
- /**
155
- * A DevTools inspector that listens to logs and debug events from a Worker.
156
- *
157
- * @example
158
- * const worker: CfWorker
159
- * const inspector: DtInspector = await worker.inspect()
160
- *
161
- * @link https://chromedevtools.github.io/devtools-protocol/
162
- */
163
- export class DtInspector {
164
- #webSocket: WebSocket;
165
- #keepAlive?: NodeJS.Timer;
166
-
167
- constructor(url: string) {
168
- // this.#events = [];
169
- // this.#listeners = [];
170
- this.#webSocket = new WebSocket(url);
171
- this.#webSocket.onopen = () => {
172
- this.enable();
173
- };
174
- this.#webSocket.onclose = () => {
175
- this.disable();
176
- };
177
- this.#webSocket.on("unexpected-response", () => {
178
- console.log("504??"); // TODO: refactor this class to start again
179
- });
180
- this.#webSocket.onmessage = (event: MessageEvent) => {
181
- // TODO: this seems unnecessary, unless we're planning
182
- // on logging to console. We'll see.
183
- if (typeof event.data === "string") {
184
- // this.recv(JSON.parse(event.data));
185
- } else {
186
- // ??
187
- }
188
- };
189
- }
190
-
191
- /**
192
- * Exposes a websocket proxy on a localhost port.
193
- */
194
- proxyTo(port: number): AbortController {
195
- return bind(new DtInspectorBridge(this.#webSocket, port), port);
196
- }
197
-
198
- /**
199
- * If the inspector is closed.
200
- */
201
- get closed(): boolean {
202
- return this.#webSocket.readyState === WebSocket.CLOSED;
203
- }
204
-
205
- /**
206
- * Closes the inspector.
207
- */
208
- close(): void {
209
- if (!this.closed) {
210
- try {
211
- this.#webSocket.close();
212
- } catch (err) {
213
- // Closing before the websocket is ready will throw an error.
214
- }
215
- }
216
- // this.#events = [];
217
- }
218
-
219
- private send(event: Record<string, unknown>): void {
220
- if (!this.closed) {
221
- this.#webSocket.send(JSON.stringify(event));
222
- }
223
- }
224
-
225
- private enable(): void {
226
- let id = 1;
227
- this.send({ method: "Runtime.enable", id });
228
- this.#keepAlive = setInterval(() => {
229
- this.send({ method: "Runtime.getIsolateId", id: id++ });
230
- }, 10_000);
231
- }
232
-
233
- private disable(): void {
234
- if (this.#keepAlive) {
235
- clearInterval(this.#keepAlive);
236
- this.#keepAlive = undefined;
237
- }
238
- }
239
- }
240
-
241
- /**
242
- * A HTTP server that responds to `fetch()` requests.
243
- */
244
- interface FetchServer {
245
- /**
246
- * Responds to a request.
247
- */
248
- fetch(request: Request): Promise<Response>;
249
- /**
250
- * Accepts a websocket connection.
251
- */
252
- upgrade?(webSocket: WebSocket): void;
253
- }
254
-
255
- /**
256
- * A bridge between a remote DevTools inspector and Chrome.
257
- *
258
- * Exposes a localhost HTTP server that responds to informational requests
259
- * from Chrome about the DevTools inspector. Then, when it receives a
260
- * WebSocket upgrade, forwards the connection to the remote inspector.
261
- */
262
- class DtInspectorBridge implements FetchServer {
263
- #webSocket: WebSocket;
264
- #localPort: number;
265
- #connected: boolean;
266
-
267
- constructor(webSocket: WebSocket, localPort?: number) {
268
- this.#webSocket = webSocket;
269
- this.#localPort = localPort;
270
- this.#connected = false;
271
- }
272
-
273
- async fetch(request: Request): Promise<Response> {
274
- const { url } = request;
275
- const { pathname } = new URL(url);
276
- switch (pathname) {
277
- case "/json/version":
278
- return this.version;
279
- case "/json":
280
- case "/json/list":
281
- return this.location;
282
- default:
283
- break;
284
- }
285
- return new Response("Not Found", { status: 404 });
286
- }
287
-
288
- upgrade(webSocket: WebSocket): void {
289
- if (this.#connected) {
290
- webSocket.close(
291
- 1013,
292
- "Too many clients; only one can be connected at a time"
293
- );
294
- } else {
295
- this.#connected = true;
296
- webSocket.addEventListener("close", () => (this.#connected = false));
297
- proxyWebSocket(webSocket, this.#webSocket);
298
- }
299
- }
300
-
301
- private get version(): Response {
302
- const headers = { "Content-Type": "application/json" };
303
- const body = {
304
- Browser: "workers-run/v0.0.0", // TODO: this should pick up version from package.json
305
- // TODO: (someday): The DevTools protocol should match that of edgeworker.
306
- // This could be exposed by the preview API.
307
- "Protocol-Version": "1.3",
308
- };
309
- return new Response(JSON.stringify(body), { headers });
310
- }
311
-
312
- private get location(): Response {
313
- const headers = { "Content-Type": "application/json" };
314
- const localHost = `localhost:${this.#localPort}/ws`;
315
- const devtoolsFrontendUrl = `devtools://devtools/bundled/js_app.html?experiments=true&v8only=true&ws=${localHost}`;
316
- const devtoolsFrontendUrlCompat = `devtools://devtools/bundled/inspector.html?experiments=true&v8only=true&ws=${localHost}`;
317
- const body = [
318
- {
319
- id: randomId(),
320
- type: "node",
321
- description: "workers",
322
- webSocketDebuggerUrl: `ws://${localHost}`,
323
- devtoolsFrontendUrl,
324
- devtoolsFrontendUrlCompat,
325
- // Below are fields that are visible in the DevTools UI.
326
- title: "Cloudflare Worker",
327
- faviconUrl: "https://workers.cloudflare.com/favicon.ico",
328
- url: "https://" + new URL(this.#webSocket.url).host,
329
- },
330
- ];
331
- return new Response(JSON.stringify(body), { headers });
332
- }
333
- }
334
-
335
- function bind(fetcher: FetchServer, port?: number): AbortController {
336
- const controller = new AbortController();
337
-
338
- const server = createServer(
339
- (input: IncomingMessage, output: ServerResponse) =>
340
- toRequest(input)
341
- .then((request) => fetcher.fetch(request))
342
- .then((response) => toResponse(response, output))
343
- .catch((error) => console.error(error))
344
- );
345
-
346
- if (fetcher.upgrade) {
347
- const webSocket = new WebSocketServer({ server });
348
- webSocket.on("connection", (ws: WebSocket) => fetcher.upgrade(ws));
349
- }
350
-
351
- const socket = server.listen({ port });
352
- controller.signal.onabort = () => {
353
- server.close();
354
- socket.close();
355
- };
356
- return controller;
357
- }
358
-
359
- /**
360
- * Converts a Node.js request to a Web standard `Request`.
361
- */
362
- async function toRequest(request: IncomingMessage): Promise<Request> {
363
- const host = request.headers.host ?? "localhost";
364
- const { href } = new URL(request.url, "http://" + host);
365
-
366
- const { rawHeaders, method } = request;
367
- const headers = new Headers();
368
- for (let i = 0; i < rawHeaders.length; i += 2) {
369
- headers.append(rawHeaders[i], rawHeaders[i + 1]);
370
- }
371
-
372
- return new Promise((resolve, reject) => {
373
- const chunks = [];
374
- request.on("data", (chunk) => chunks.push(chunk));
375
- request.on("error", (error) => reject(error));
376
- request.on("end", () => {
377
- const buffer = Buffer.concat(chunks);
378
- const body = buffer.length === 0 ? undefined : buffer;
379
- resolve(new Request(href, { method, headers, body }));
380
- });
381
- });
382
- }
383
-
384
- /**
385
- * Converts a Web standard `Response` into a Node.js response.
386
- */
387
- async function toResponse(
388
- input: Response,
389
- response: ServerResponse
390
- ): Promise<void> {
391
- const { status, statusText, headers, body: hasBody } = input;
392
-
393
- for (const [name, value] of headers.entries()) {
394
- response.setHeader(name, value);
395
- }
396
-
397
- let body: Uint8Array;
398
- if (hasBody) {
399
- body = new Uint8Array(await input.arrayBuffer());
400
- response.setHeader("Content-Length", body.byteLength);
401
- }
402
-
403
- response.writeHead(status, statusText);
404
- response.write(body);
405
- }
406
-
407
- /**
408
- * Creates a proxy bridge between two websockets.
409
- */
410
- function proxyWebSocket(webSocket: WebSocket, otherSocket: WebSocket): void {
411
- webSocket.addEventListener("message", (event: MessageEvent) => {
412
- try {
413
- otherSocket.send(event.data);
414
- } catch (e) {
415
- if (e.message !== "WebSocket is not open: readyState 0 (CONNECTING)") {
416
- // this just means we haven't opened a websocket yet
417
- // usually happens until there's at least one request
418
- // which is weird, because we may miss something that happens on
419
- // the first request
420
- console.error(e);
421
- }
422
- }
423
- });
424
- otherSocket.addEventListener("message", (event: MessageEvent) =>
425
- webSocket.send(event.data)
426
- );
427
-
428
- // Some close codes are marked as 'reserved' and will throw an error if used.
429
- // Therefore, it's not worth the effort to passthrough the close code and reason.
430
- webSocket.addEventListener("close", () => otherSocket.close());
431
- otherSocket.addEventListener("close", () => webSocket.close());
432
- }
433
-
434
- // Credit: https://stackoverflow.com/a/2117523
435
- function randomId(): string {
436
- return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, function (c) {
437
- const r = (Math.random() * 16) | 0,
438
- v = c == "x" ? r : (r & 0x3) | 0x8;
439
- return v.toString(16);
440
- });
441
- }