wrangler 0.0.0-e64812e → 0.0.0-e8e684c7

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 (214) hide show
  1. package/README.md +27 -3
  2. package/bin/wrangler.js +150 -62
  3. package/kv-asset-handler.js +1 -0
  4. package/miniflare-dist/index.mjs +663 -7
  5. package/package.json +173 -152
  6. package/src/__tests__/config-cache-without-cache-dir.test.ts +38 -0
  7. package/src/__tests__/config-cache.test.ts +30 -24
  8. package/src/__tests__/configuration.test.ts +4015 -3363
  9. package/src/__tests__/dev.test.tsx +1290 -952
  10. package/src/__tests__/generate.test.ts +91 -0
  11. package/src/__tests__/guess-worker-format.test.ts +68 -68
  12. package/src/__tests__/helpers/cmd-shim.d.ts +6 -6
  13. package/src/__tests__/helpers/mock-account-id.ts +24 -24
  14. package/src/__tests__/helpers/mock-bin.ts +20 -20
  15. package/src/__tests__/helpers/mock-cfetch.ts +179 -94
  16. package/src/__tests__/helpers/mock-console.ts +49 -39
  17. package/src/__tests__/helpers/mock-dialogs.ts +94 -71
  18. package/src/__tests__/helpers/mock-get-zone-from-host.ts +8 -0
  19. package/src/__tests__/helpers/mock-http-server.ts +30 -30
  20. package/src/__tests__/helpers/mock-istty.ts +65 -18
  21. package/src/__tests__/helpers/mock-known-routes.ts +7 -0
  22. package/src/__tests__/helpers/mock-kv.ts +26 -26
  23. package/src/__tests__/helpers/mock-oauth-flow.ts +223 -228
  24. package/src/__tests__/helpers/mock-process.ts +39 -0
  25. package/src/__tests__/helpers/mock-stdin.ts +82 -77
  26. package/src/__tests__/helpers/mock-web-socket.ts +21 -21
  27. package/src/__tests__/helpers/run-in-tmp.ts +27 -28
  28. package/src/__tests__/helpers/run-wrangler.ts +8 -8
  29. package/src/__tests__/helpers/write-worker-source.ts +16 -16
  30. package/src/__tests__/helpers/write-wrangler-toml.ts +9 -9
  31. package/src/__tests__/https-options.test.ts +142 -109
  32. package/src/__tests__/index.test.ts +222 -233
  33. package/src/__tests__/init.test.ts +2139 -1603
  34. package/src/__tests__/jest.setup.ts +114 -34
  35. package/src/__tests__/kv.test.ts +1222 -1032
  36. package/src/__tests__/logger.test.ts +100 -74
  37. package/src/__tests__/metrics.test.ts +450 -0
  38. package/src/__tests__/package-manager.test.ts +303 -303
  39. package/src/__tests__/pages.test.ts +1173 -652
  40. package/src/__tests__/parse.test.ts +252 -252
  41. package/src/__tests__/publish.test.ts +6788 -5301
  42. package/src/__tests__/pubsub.test.ts +367 -0
  43. package/src/__tests__/r2.test.ts +328 -225
  44. package/src/__tests__/route.test.ts +18 -18
  45. package/src/__tests__/secret.test.ts +470 -459
  46. package/src/__tests__/tail.test.ts +676 -564
  47. package/src/__tests__/test-old-node-version.js +31 -0
  48. package/src/__tests__/user.test.ts +136 -112
  49. package/src/__tests__/validate-dev-props.test.ts +56 -0
  50. package/src/__tests__/version.test.ts +35 -0
  51. package/src/__tests__/whoami.test.tsx +267 -83
  52. package/src/__tests__/worker-namespace.test.ts +329 -0
  53. package/src/abort.d.ts +1 -1
  54. package/src/api/dev.ts +93 -0
  55. package/src/api/index.ts +1 -0
  56. package/src/bundle-reporter.tsx +29 -0
  57. package/src/bundle.ts +430 -157
  58. package/src/cfetch/index.ts +80 -80
  59. package/src/cfetch/internal.ts +244 -91
  60. package/src/cli.ts +21 -7
  61. package/src/config/config.ts +224 -195
  62. package/src/config/diagnostics.ts +61 -61
  63. package/src/config/environment.ts +422 -357
  64. package/src/config/index.ts +219 -193
  65. package/src/config/validation-helpers.ts +379 -357
  66. package/src/config/validation.ts +1690 -1367
  67. package/src/config-cache.ts +81 -41
  68. package/src/create-worker-preview.ts +208 -136
  69. package/src/create-worker-upload-form.ts +275 -238
  70. package/src/dev/dev-vars.ts +13 -13
  71. package/src/dev/dev.tsx +477 -351
  72. package/src/dev/local.tsx +426 -295
  73. package/src/dev/remote.tsx +406 -211
  74. package/src/dev/use-esbuild.ts +154 -94
  75. package/src/dev/validate-dev-props.ts +31 -0
  76. package/src/dev-registry.tsx +160 -0
  77. package/src/dev.tsx +696 -0
  78. package/src/dialogs.tsx +97 -97
  79. package/src/dispatch-namespace.ts +206 -0
  80. package/src/durable.ts +87 -87
  81. package/src/entry.ts +234 -228
  82. package/src/environment-variables.ts +23 -23
  83. package/src/errors.ts +6 -6
  84. package/src/faye-websocket.d.ts +6 -0
  85. package/src/generate.ts +131 -0
  86. package/src/git-client.ts +42 -0
  87. package/src/global-wrangler-config-path.ts +26 -0
  88. package/src/https-options.ts +84 -80
  89. package/src/index.tsx +2164 -2768
  90. package/src/init.ts +622 -0
  91. package/src/inspect.ts +678 -593
  92. package/src/intl-polyfill.d.ts +123 -123
  93. package/src/is-ci.ts +14 -0
  94. package/src/is-interactive.ts +12 -0
  95. package/src/kv.ts +299 -277
  96. package/src/logger.ts +46 -39
  97. package/src/metrics/index.ts +5 -0
  98. package/src/metrics/metrics-config.ts +239 -0
  99. package/src/metrics/metrics-dispatcher.ts +96 -0
  100. package/src/metrics/metrics-usage-headers.ts +24 -0
  101. package/src/metrics/send-event.ts +92 -0
  102. package/src/miniflare-cli/assets.ts +554 -0
  103. package/src/miniflare-cli/enum-keys.ts +8 -8
  104. package/src/miniflare-cli/index.ts +198 -32
  105. package/src/miniflare-cli/request-context.ts +18 -18
  106. package/src/module-collection.ts +212 -212
  107. package/src/open-in-browser.ts +4 -6
  108. package/src/package-manager.ts +173 -128
  109. package/src/pages/build.tsx +238 -0
  110. package/src/pages/constants.ts +12 -0
  111. package/src/pages/deployments.tsx +106 -0
  112. package/src/pages/dev.tsx +528 -0
  113. package/src/pages/errors.ts +22 -0
  114. package/src/pages/functions/buildPlugin.ts +109 -0
  115. package/src/pages/functions/buildWorker.ts +155 -0
  116. package/{pages → src/pages}/functions/filepath-routing.test.ts +113 -113
  117. package/src/pages/functions/filepath-routing.ts +189 -0
  118. package/src/pages/functions/identifiers.ts +78 -0
  119. package/src/pages/functions/routes-consolidation.test.ts +250 -0
  120. package/src/pages/functions/routes-consolidation.ts +73 -0
  121. package/src/pages/functions/routes-transformation.test.ts +271 -0
  122. package/src/pages/functions/routes-transformation.ts +122 -0
  123. package/src/pages/functions/routes.ts +151 -0
  124. package/src/pages/functions.tsx +96 -0
  125. package/src/pages/index.tsx +94 -0
  126. package/src/pages/projects.tsx +169 -0
  127. package/src/pages/publish.tsx +391 -0
  128. package/src/pages/types.ts +49 -0
  129. package/src/pages/upload.tsx +413 -0
  130. package/src/pages/utils.ts +12 -0
  131. package/src/parse.ts +209 -125
  132. package/src/paths.ts +6 -6
  133. package/src/preview.ts +31 -0
  134. package/src/proxy.ts +410 -389
  135. package/src/publish.ts +773 -534
  136. package/src/pubsub/index.ts +286 -0
  137. package/src/pubsub/pubsub-commands.tsx +623 -0
  138. package/src/r2.ts +100 -19
  139. package/src/selfsigned.d.ts +23 -23
  140. package/src/sites.tsx +278 -201
  141. package/src/tail/filters.ts +111 -109
  142. package/src/tail/index.ts +230 -217
  143. package/src/tail/printing.ts +86 -46
  144. package/src/update-check.ts +11 -11
  145. package/src/user/choose-account.tsx +69 -0
  146. package/src/user/env-vars.ts +46 -0
  147. package/src/user/generate-auth-url.ts +33 -0
  148. package/src/user/generate-random-state.ts +16 -0
  149. package/src/user/index.ts +3 -0
  150. package/src/user/user.tsx +1191 -0
  151. package/src/whoami.tsx +141 -42
  152. package/src/worker.ts +123 -99
  153. package/src/zones.ts +167 -0
  154. package/templates/checked-fetch.js +17 -0
  155. package/templates/first-party-worker-module-facade.ts +18 -0
  156. package/templates/format-dev-errors.ts +32 -0
  157. package/templates/new-worker-scheduled.js +3 -3
  158. package/templates/new-worker-scheduled.ts +15 -15
  159. package/templates/new-worker.js +3 -3
  160. package/templates/new-worker.ts +15 -15
  161. package/templates/no-op-worker.js +10 -0
  162. package/templates/pages-shim.ts +9 -0
  163. package/templates/pages-template-plugin.ts +167 -0
  164. package/templates/pages-template-worker.ts +172 -0
  165. package/templates/serve-static-assets.ts +63 -0
  166. package/templates/service-bindings-module-facade.js +54 -0
  167. package/templates/service-bindings-sw-facade.js +42 -0
  168. package/templates/tsconfig.json +95 -95
  169. package/wrangler-dist/cli.d.ts +338 -0
  170. package/wrangler-dist/cli.js +95449 -67524
  171. package/pages/functions/buildPlugin.ts +0 -105
  172. package/pages/functions/buildWorker.ts +0 -151
  173. package/pages/functions/filepath-routing.ts +0 -189
  174. package/pages/functions/identifiers.ts +0 -78
  175. package/pages/functions/routes.ts +0 -156
  176. package/pages/functions/template-plugin.ts +0 -147
  177. package/pages/functions/template-worker.ts +0 -143
  178. package/src/__tests__/helpers/faye-websocket.d.ts +0 -6
  179. package/src/pages.tsx +0 -2038
  180. package/src/user.tsx +0 -1203
  181. package/templates/static-asset-facade.js +0 -47
  182. package/vendor/@cloudflare/kv-asset-handler/CHANGELOG.md +0 -332
  183. package/vendor/@cloudflare/kv-asset-handler/LICENSE_APACHE +0 -176
  184. package/vendor/@cloudflare/kv-asset-handler/LICENSE_MIT +0 -25
  185. package/vendor/@cloudflare/kv-asset-handler/README.md +0 -245
  186. package/vendor/@cloudflare/kv-asset-handler/dist/index.d.ts +0 -32
  187. package/vendor/@cloudflare/kv-asset-handler/dist/index.js +0 -354
  188. package/vendor/@cloudflare/kv-asset-handler/dist/mocks.d.ts +0 -13
  189. package/vendor/@cloudflare/kv-asset-handler/dist/mocks.js +0 -148
  190. package/vendor/@cloudflare/kv-asset-handler/dist/test/getAssetFromKV.d.ts +0 -1
  191. package/vendor/@cloudflare/kv-asset-handler/dist/test/getAssetFromKV.js +0 -436
  192. package/vendor/@cloudflare/kv-asset-handler/dist/test/mapRequestToAsset.d.ts +0 -1
  193. package/vendor/@cloudflare/kv-asset-handler/dist/test/mapRequestToAsset.js +0 -40
  194. package/vendor/@cloudflare/kv-asset-handler/dist/test/serveSinglePageApp.d.ts +0 -1
  195. package/vendor/@cloudflare/kv-asset-handler/dist/test/serveSinglePageApp.js +0 -42
  196. package/vendor/@cloudflare/kv-asset-handler/dist/types.d.ts +0 -26
  197. package/vendor/@cloudflare/kv-asset-handler/dist/types.js +0 -31
  198. package/vendor/@cloudflare/kv-asset-handler/package.json +0 -52
  199. package/vendor/@cloudflare/kv-asset-handler/src/index.ts +0 -296
  200. package/vendor/@cloudflare/kv-asset-handler/src/mocks.ts +0 -136
  201. package/vendor/@cloudflare/kv-asset-handler/src/test/getAssetFromKV.ts +0 -464
  202. package/vendor/@cloudflare/kv-asset-handler/src/test/mapRequestToAsset.ts +0 -33
  203. package/vendor/@cloudflare/kv-asset-handler/src/test/serveSinglePageApp.ts +0 -42
  204. package/vendor/@cloudflare/kv-asset-handler/src/types.ts +0 -39
  205. package/vendor/wrangler-mime/CHANGELOG.md +0 -289
  206. package/vendor/wrangler-mime/LICENSE +0 -21
  207. package/vendor/wrangler-mime/Mime.js +0 -97
  208. package/vendor/wrangler-mime/README.md +0 -187
  209. package/vendor/wrangler-mime/cli.js +0 -46
  210. package/vendor/wrangler-mime/index.js +0 -4
  211. package/vendor/wrangler-mime/lite.js +0 -4
  212. package/vendor/wrangler-mime/package.json +0 -52
  213. package/vendor/wrangler-mime/types/other.js +0 -1
  214. package/vendor/wrangler-mime/types/standard.js +0 -1
package/src/dev/dev.tsx CHANGED
@@ -1,5 +1,6 @@
1
1
  import { spawn } from "node:child_process";
2
2
  import * as path from "node:path";
3
+ import * as util from "node:util";
3
4
  import { watch } from "chokidar";
4
5
  import clipboardy from "clipboardy";
5
6
  import commandExists from "command-exists";
@@ -9,276 +10,383 @@ import { withErrorBoundary, useErrorHandler } from "react-error-boundary";
9
10
  import onExit from "signal-exit";
10
11
  import tmp from "tmp-promise";
11
12
  import { fetch } from "undici";
13
+ import {
14
+ getRegisteredWorkers,
15
+ startWorkerRegistry,
16
+ stopWorkerRegistry,
17
+ unregisterWorker,
18
+ } from "../dev-registry";
12
19
  import { runCustomBuild } from "../entry";
13
20
  import { openInspector } from "../inspect";
14
21
  import { logger } from "../logger";
15
22
  import openInBrowser from "../open-in-browser";
16
- import { getAPIToken } from "../user";
17
23
  import { Local } from "./local";
18
24
  import { Remote } from "./remote";
19
25
  import { useEsbuild } from "./use-esbuild";
26
+ import { validateDevProps } from "./validate-dev-props";
20
27
  import type { Config } from "../config";
28
+ import type { Route } from "../config/environment";
29
+ import type { WorkerRegistry } from "../dev-registry";
21
30
  import type { Entry } from "../entry";
31
+ import type { EnablePagesAssetsServiceBindingOptions } from "../miniflare-cli";
22
32
  import type { AssetPaths } from "../sites";
23
33
  import type { CfWorkerInit } from "../worker";
24
- import type { EsbuildBundle } from "./use-esbuild";
34
+
35
+ /**
36
+ * This hooks establishes a connection with the dev registry,
37
+ * and periodically updates itself with details of workers currently
38
+ * running a dev session on this system.
39
+ */
40
+ function useDevRegistry(
41
+ name: string | undefined,
42
+ services: Config["services"] | undefined,
43
+ durableObjects: Config["durable_objects"] | undefined,
44
+ mode: "local" | "remote"
45
+ ): WorkerRegistry {
46
+ const [workers, setWorkers] = useState<WorkerRegistry>({});
47
+
48
+ useEffect(() => {
49
+ // Let's try to start registry
50
+ // TODO: we should probably call this in a loop
51
+ // in case the registry dies elsewhere
52
+ startWorkerRegistry().catch((err) => {
53
+ logger.error("failed to start worker registry", err);
54
+ });
55
+
56
+ const serviceNames = (services || []).map(
57
+ (serviceBinding) => serviceBinding.service
58
+ );
59
+ const durableObjectServices = (
60
+ durableObjects || { bindings: [] }
61
+ ).bindings.map((durableObjectBinding) => durableObjectBinding.script_name);
62
+
63
+ const interval =
64
+ // TODO: enable this for remote mode as well
65
+ // https://github.com/cloudflare/wrangler2/issues/1182
66
+ mode === "local"
67
+ ? setInterval(() => {
68
+ getRegisteredWorkers().then(
69
+ (workerDefinitions: WorkerRegistry | undefined) => {
70
+ // We only want the workers that we're bound to
71
+ // so let's filter out the others
72
+ const filteredWorkers = Object.fromEntries(
73
+ Object.entries(workerDefinitions || {}).filter(
74
+ ([key, _value]) =>
75
+ serviceNames.includes(key) ||
76
+ durableObjectServices.includes(key)
77
+ )
78
+ );
79
+ setWorkers((prevWorkers) => {
80
+ if (!util.isDeepStrictEqual(filteredWorkers, prevWorkers)) {
81
+ return filteredWorkers;
82
+ }
83
+ return prevWorkers;
84
+ });
85
+ },
86
+ (err) => {
87
+ logger.warn("Failed to get worker definitions", err);
88
+ }
89
+ );
90
+ }, 300)
91
+ : undefined;
92
+
93
+ return () => {
94
+ interval && clearInterval(interval);
95
+ Promise.allSettled([
96
+ name ? unregisterWorker(name) : Promise.resolve(),
97
+ stopWorkerRegistry(),
98
+ ]).then(
99
+ ([unregisterResult, stopRegistryResult]) => {
100
+ if (unregisterResult.status === "rejected") {
101
+ logger.error(
102
+ "Failed to unregister worker",
103
+ unregisterResult.reason
104
+ );
105
+ }
106
+ if (stopRegistryResult.status === "rejected") {
107
+ logger.error(
108
+ "Failed to stop worker registry",
109
+ stopRegistryResult.reason
110
+ );
111
+ }
112
+ },
113
+ (err) => {
114
+ logger.error("Failed to clear dev registry effect", err);
115
+ }
116
+ );
117
+ };
118
+ }, [name, services, durableObjects, mode]);
119
+
120
+ return workers;
121
+ }
25
122
 
26
123
  export type DevProps = {
27
- name?: string;
28
- entry: Entry;
29
- port: number;
30
- ip: string;
31
- inspectorPort: number;
32
- rules: Config["rules"];
33
- accountId: string | undefined;
34
- initialMode: "local" | "remote";
35
- jsxFactory: string | undefined;
36
- jsxFragment: string | undefined;
37
- tsconfig: string | undefined;
38
- upstreamProtocol: "https" | "http";
39
- localProtocol: "https" | "http";
40
- enableLocalPersistence: boolean;
41
- bindings: CfWorkerInit["bindings"];
42
- crons: Config["triggers"]["crons"];
43
- public: string | undefined;
44
- assetPaths: AssetPaths | undefined;
45
- compatibilityDate: string;
46
- compatibilityFlags: string[] | undefined;
47
- usageModel: "bundled" | "unbound" | undefined;
48
- minify: boolean | undefined;
49
- nodeCompat: boolean | undefined;
50
- build: {
51
- command?: string | undefined;
52
- cwd?: string | undefined;
53
- watch_dir?: string | undefined;
54
- };
55
- env: string | undefined;
56
- legacyEnv: boolean;
57
- zone:
58
- | {
59
- id: string;
60
- host: string;
61
- }
62
- | undefined;
124
+ name: string | undefined;
125
+ noBundle: boolean;
126
+ entry: Entry;
127
+ port: number;
128
+ ip: string;
129
+ inspectorPort: number;
130
+ rules: Config["rules"];
131
+ accountId: string | undefined;
132
+ initialMode: "local" | "remote";
133
+ jsxFactory: string | undefined;
134
+ jsxFragment: string | undefined;
135
+ tsconfig: string | undefined;
136
+ upstreamProtocol: "https" | "http";
137
+ localProtocol: "https" | "http";
138
+ localUpstream: string | undefined;
139
+ enableLocalPersistence: boolean;
140
+ liveReload: boolean;
141
+ bindings: CfWorkerInit["bindings"];
142
+ define: Config["define"];
143
+ crons: Config["triggers"]["crons"];
144
+ isWorkersSite: boolean;
145
+ assetPaths: AssetPaths | undefined;
146
+ assetsConfig: Config["assets"];
147
+ compatibilityDate: string;
148
+ compatibilityFlags: string[] | undefined;
149
+ usageModel: "bundled" | "unbound" | undefined;
150
+ minify: boolean | undefined;
151
+ nodeCompat: boolean | undefined;
152
+ build: Config["build"];
153
+ env: string | undefined;
154
+ legacyEnv: boolean;
155
+ zone: string | undefined;
156
+ host: string | undefined;
157
+ routes: Route[] | undefined;
158
+ inspect: boolean;
159
+ logLevel: "none" | "error" | "log" | "warn" | "debug" | undefined;
160
+ logPrefix?: string;
161
+ onReady: (() => void) | undefined;
162
+ showInteractiveDevSession: boolean | undefined;
163
+ forceLocal: boolean | undefined;
164
+ enablePagesAssetsServiceBinding?: EnablePagesAssetsServiceBindingOptions;
165
+ firstPartyWorker: boolean | undefined;
166
+ sendMetrics: boolean | undefined;
63
167
  };
64
168
 
65
169
  export function DevImplementation(props: DevProps): JSX.Element {
66
- const apiToken = props.initialMode === "remote" ? getAPIToken() : undefined;
67
- const directory = useTmpDir();
68
-
69
- useCustomBuild(props.entry, props.build);
70
-
71
- if (props.public && props.entry.format === "service-worker") {
72
- throw new Error(
73
- "You cannot use the service-worker format with a `public` directory."
74
- );
75
- }
76
-
77
- if (props.bindings.wasm_modules && props.entry.format === "modules") {
78
- throw new Error(
79
- "You cannot configure [wasm_modules] with an ES module worker. Instead, import the .wasm module directly in your code"
80
- );
81
- }
82
-
83
- if (props.bindings.text_blobs && props.entry.format === "modules") {
84
- throw new Error(
85
- "You cannot configure [text_blobs] with an ES module worker. Instead, import the file directly in your code, and optionally configure `[rules]` in your wrangler.toml"
86
- );
87
- }
88
-
89
- if (props.bindings.data_blobs && props.entry.format === "modules") {
90
- throw new Error(
91
- "You cannot configure [data_blobs] with an ES module worker. Instead, import the file directly in your code, and optionally configure `[rules]` in your wrangler.toml"
92
- );
93
- }
94
-
95
- const bundle = useEsbuild({
96
- entry: props.entry,
97
- destination: directory,
98
- staticRoot: props.public,
99
- jsxFactory: props.jsxFactory,
100
- rules: props.rules,
101
- jsxFragment: props.jsxFragment,
102
- serveAssetsFromWorker: !!props.public,
103
- tsconfig: props.tsconfig,
104
- minify: props.minify,
105
- nodeCompat: props.nodeCompat,
106
- });
107
-
108
- // only load the UI if we're running in a supported environment
109
- const { isRawModeSupported } = useStdin();
110
- return isRawModeSupported ? (
111
- <InteractiveDevSession {...props} bundle={bundle} apiToken={apiToken} />
112
- ) : (
113
- <DevSession
114
- {...props}
115
- bundle={bundle}
116
- apiToken={apiToken}
117
- local={props.initialMode === "local"}
118
- />
119
- );
170
+ validateDevProps(props);
171
+
172
+ // only load the UI if we're running in a supported environment
173
+ const { isRawModeSupported } = useStdin();
174
+
175
+ return props.showInteractiveDevSession ?? isRawModeSupported ? (
176
+ <InteractiveDevSession {...props} />
177
+ ) : (
178
+ <DevSession {...props} local={props.initialMode === "local"} />
179
+ );
120
180
  }
121
181
 
122
- type InteractiveDevSessionProps = DevProps & {
123
- apiToken: string | undefined;
124
- bundle: EsbuildBundle | undefined;
125
- };
182
+ function InteractiveDevSession(props: DevProps) {
183
+ const toggles = useHotkeys({
184
+ initial: {
185
+ local: props.initialMode === "local",
186
+ tunnel: false,
187
+ },
188
+ port: props.port,
189
+ ip: props.ip,
190
+ inspectorPort: props.inspectorPort,
191
+ inspect: props.inspect,
192
+ localProtocol: props.localProtocol,
193
+ forceLocal: props.forceLocal,
194
+ });
126
195
 
127
- function InteractiveDevSession(props: InteractiveDevSessionProps) {
128
- const toggles = useHotkeys(
129
- {
130
- local: props.initialMode === "local",
131
- tunnel: false,
132
- },
133
- props.port,
134
- props.ip,
135
- props.inspectorPort,
136
- props.localProtocol
137
- );
138
-
139
- useTunnel(toggles.tunnel);
140
-
141
- return (
142
- <>
143
- <DevSession {...props} local={toggles.local} />
144
- <Box borderStyle="round" paddingLeft={1} paddingRight={1}>
145
- <Text bold={true}>[b]</Text>
146
- <Text> open a browser, </Text>
147
- <Text bold={true}>[d]</Text>
148
- <Text> open Devtools, </Text>
149
- <Text bold={true}>[l]</Text>
150
- <Text> {toggles.local ? "turn off" : "turn on"} local mode, </Text>
151
- <Text bold={true}>[c]</Text>
152
- <Text> clear console, </Text>
153
- <Text bold={true}>[x]</Text>
154
- <Text> to exit</Text>
155
- </Box>
156
- </>
157
- );
196
+ useTunnel(toggles.tunnel);
197
+
198
+ return (
199
+ <>
200
+ <DevSession {...props} local={toggles.local} />
201
+ <Box borderStyle="round" paddingLeft={1} paddingRight={1}>
202
+ <Text bold={true}>[b]</Text>
203
+ <Text> open a browser, </Text>
204
+ {props.inspect ? (
205
+ <>
206
+ <Text bold={true}>[d]</Text>
207
+ <Text> open Devtools, </Text>
208
+ </>
209
+ ) : null}
210
+ {!props.forceLocal ? (
211
+ <>
212
+ <Text bold={true}>[l]</Text>
213
+ <Text> {toggles.local ? "turn off" : "turn on"} local mode, </Text>
214
+ </>
215
+ ) : null}
216
+ <Text bold={true}>[c]</Text>
217
+ <Text> clear console, </Text>
218
+ <Text bold={true}>[x]</Text>
219
+ <Text> to exit</Text>
220
+ </Box>
221
+ </>
222
+ );
158
223
  }
159
224
 
160
- type DevSessionProps = InteractiveDevSessionProps & { local: boolean };
225
+ type DevSessionProps = DevProps & {
226
+ local: boolean;
227
+ };
161
228
 
162
229
  function DevSession(props: DevSessionProps) {
163
- return props.local ? (
164
- <Local
165
- name={props.name}
166
- bundle={props.bundle}
167
- format={props.entry.format}
168
- compatibilityDate={props.compatibilityDate}
169
- compatibilityFlags={props.compatibilityFlags}
170
- bindings={props.bindings}
171
- assetPaths={props.assetPaths}
172
- public={props.public}
173
- port={props.port}
174
- ip={props.ip}
175
- rules={props.rules}
176
- inspectorPort={props.inspectorPort}
177
- enableLocalPersistence={props.enableLocalPersistence}
178
- crons={props.crons}
179
- />
180
- ) : (
181
- <Remote
182
- name={props.name}
183
- bundle={props.bundle}
184
- format={props.entry.format}
185
- accountId={props.accountId}
186
- apiToken={props.apiToken}
187
- bindings={props.bindings}
188
- assetPaths={props.assetPaths}
189
- public={props.public}
190
- port={props.port}
191
- ip={props.ip}
192
- localProtocol={props.localProtocol}
193
- inspectorPort={props.inspectorPort}
194
- compatibilityDate={props.compatibilityDate}
195
- compatibilityFlags={props.compatibilityFlags}
196
- usageModel={props.usageModel}
197
- env={props.env}
198
- legacyEnv={props.legacyEnv}
199
- zone={props.zone}
200
- />
201
- );
230
+ useCustomBuild(props.entry, props.build);
231
+
232
+ const directory = useTmpDir();
233
+
234
+ const workerDefinitions = useDevRegistry(
235
+ props.name,
236
+ props.bindings.services,
237
+ props.bindings.durable_objects,
238
+ props.local ? "local" : "remote"
239
+ );
240
+
241
+ const bundle = useEsbuild({
242
+ entry: props.entry,
243
+ destination: directory,
244
+ jsxFactory: props.jsxFactory,
245
+ rules: props.rules,
246
+ jsxFragment: props.jsxFragment,
247
+ serveAssetsFromWorker: Boolean(
248
+ props.assetPaths && !props.isWorkersSite && props.local
249
+ ),
250
+ tsconfig: props.tsconfig,
251
+ minify: props.minify,
252
+ nodeCompat: props.nodeCompat,
253
+ define: props.define,
254
+ noBundle: props.noBundle,
255
+ assets: props.assetsConfig,
256
+ workerDefinitions,
257
+ services: props.bindings.services,
258
+ durableObjects: props.bindings.durable_objects || { bindings: [] },
259
+ firstPartyWorkerDevFacade: props.firstPartyWorker,
260
+ });
261
+
262
+ return props.local ? (
263
+ <Local
264
+ name={props.name}
265
+ bundle={bundle}
266
+ format={props.entry.format}
267
+ compatibilityDate={props.compatibilityDate}
268
+ compatibilityFlags={props.compatibilityFlags}
269
+ usageModel={props.usageModel}
270
+ bindings={props.bindings}
271
+ workerDefinitions={workerDefinitions}
272
+ assetPaths={props.assetPaths}
273
+ port={props.port}
274
+ ip={props.ip}
275
+ rules={props.rules}
276
+ inspectorPort={props.inspectorPort}
277
+ enableLocalPersistence={props.enableLocalPersistence}
278
+ liveReload={props.liveReload}
279
+ crons={props.crons}
280
+ localProtocol={props.localProtocol}
281
+ localUpstream={props.localUpstream}
282
+ logLevel={props.logLevel}
283
+ logPrefix={props.logPrefix}
284
+ inspect={props.inspect}
285
+ onReady={props.onReady}
286
+ enablePagesAssetsServiceBinding={props.enablePagesAssetsServiceBinding}
287
+ />
288
+ ) : (
289
+ <Remote
290
+ name={props.name}
291
+ bundle={bundle}
292
+ format={props.entry.format}
293
+ accountId={props.accountId}
294
+ bindings={props.bindings}
295
+ assetPaths={props.assetPaths}
296
+ isWorkersSite={props.isWorkersSite}
297
+ port={props.port}
298
+ ip={props.ip}
299
+ localProtocol={props.localProtocol}
300
+ inspectorPort={props.inspectorPort}
301
+ // TODO: @threepointone #1167
302
+ // liveReload={props.liveReload}
303
+ inspect={props.inspect}
304
+ compatibilityDate={props.compatibilityDate}
305
+ compatibilityFlags={props.compatibilityFlags}
306
+ usageModel={props.usageModel}
307
+ env={props.env}
308
+ legacyEnv={props.legacyEnv}
309
+ zone={props.zone}
310
+ host={props.host}
311
+ routes={props.routes}
312
+ onReady={props.onReady}
313
+ sourceMapPath={bundle?.sourceMapPath}
314
+ sendMetrics={props.sendMetrics}
315
+ />
316
+ );
202
317
  }
203
318
 
204
319
  export interface DirectorySyncResult {
205
- name: string;
206
- removeCallback: () => void;
320
+ name: string;
321
+ removeCallback: () => void;
207
322
  }
208
323
 
209
324
  function useTmpDir(): string | undefined {
210
- const [directory, setDirectory] = useState<DirectorySyncResult>();
211
- const handleError = useErrorHandler();
212
- useEffect(() => {
213
- let dir: DirectorySyncResult | undefined;
214
- try {
215
- dir = tmp.dirSync({ unsafeCleanup: true });
216
- setDirectory(dir);
217
- return;
218
- } catch (err) {
219
- logger.error(
220
- "Failed to create temporary directory to store built files."
221
- );
222
- handleError(err);
223
- }
224
- return () => {
225
- dir?.removeCallback();
226
- };
227
- }, [handleError]);
228
- return directory?.name;
325
+ const [directory, setDirectory] = useState<DirectorySyncResult>();
326
+ const handleError = useErrorHandler();
327
+ useEffect(() => {
328
+ let dir: DirectorySyncResult | undefined;
329
+ try {
330
+ dir = tmp.dirSync({ unsafeCleanup: true });
331
+ setDirectory(dir);
332
+ return;
333
+ } catch (err) {
334
+ logger.error(
335
+ "Failed to create temporary directory to store built files."
336
+ );
337
+ handleError(err);
338
+ }
339
+ return () => {
340
+ dir?.removeCallback();
341
+ };
342
+ }, [handleError]);
343
+ return directory?.name;
229
344
  }
230
345
 
231
- function useCustomBuild(
232
- expectedEntry: Entry,
233
- build: {
234
- command?: string | undefined;
235
- cwd?: string | undefined;
236
- watch_dir?: string | undefined;
237
- }
238
- ): void {
239
- useEffect(() => {
240
- if (!build.command) return;
241
- let watcher: ReturnType<typeof watch> | undefined;
242
- if (build.watch_dir) {
243
- watcher = watch(build.watch_dir, {
244
- persistent: true,
245
- ignoreInitial: true,
246
- }).on("all", (_event, filePath) => {
247
- const relativeFile =
248
- path.relative(expectedEntry.directory, expectedEntry.file) || ".";
249
- //TODO: we should buffer requests to the proxy until this completes
250
- logger.log(`The file ${filePath} changed, restarting build...`);
251
- runCustomBuild(expectedEntry.file, relativeFile, build).catch((err) => {
252
- logger.error("Custom build failed:", err);
253
- });
254
- });
255
- }
256
-
257
- return () => {
258
- watcher?.close();
259
- };
260
- }, [build, expectedEntry]);
346
+ function useCustomBuild(expectedEntry: Entry, build: Config["build"]): void {
347
+ useEffect(() => {
348
+ if (!build.command) return;
349
+ let watcher: ReturnType<typeof watch> | undefined;
350
+ if (build.watch_dir) {
351
+ watcher = watch(build.watch_dir, {
352
+ persistent: true,
353
+ ignoreInitial: true,
354
+ }).on("all", (_event, filePath) => {
355
+ const relativeFile =
356
+ path.relative(expectedEntry.directory, expectedEntry.file) || ".";
357
+ //TODO: we should buffer requests to the proxy until this completes
358
+ logger.log(`The file ${filePath} changed, restarting build...`);
359
+ runCustomBuild(expectedEntry.file, relativeFile, build).catch((err) => {
360
+ logger.error("Custom build failed:", err);
361
+ });
362
+ });
363
+ }
364
+
365
+ return () => {
366
+ watcher?.close();
367
+ };
368
+ }, [build, expectedEntry]);
261
369
  }
262
370
 
263
371
  function sleep(period: number) {
264
- return new Promise((resolve) => setTimeout(resolve, period));
372
+ return new Promise((resolve) => setTimeout(resolve, period));
265
373
  }
266
374
  const SLEEP_DURATION = 2000;
267
375
  // really need a first class api for this
268
376
  const hostNameRegex = /userHostname="(.*)"/g;
269
377
  async function findTunnelHostname() {
270
- let hostName: string | undefined;
271
- while (!hostName) {
272
- try {
273
- const resp = await fetch("http://localhost:8789/metrics");
274
- const data = await resp.text();
275
- const matches = Array.from(data.matchAll(hostNameRegex));
276
- hostName = matches[0][1];
277
- } catch (err) {
278
- await sleep(SLEEP_DURATION);
279
- }
280
- }
281
- return hostName;
378
+ let hostName: string | undefined;
379
+ while (!hostName) {
380
+ try {
381
+ const resp = await fetch("http://localhost:8789/metrics");
382
+ const data = await resp.text();
383
+ const matches = Array.from(data.matchAll(hostNameRegex));
384
+ hostName = matches[0][1];
385
+ } catch (err) {
386
+ await sleep(SLEEP_DURATION);
387
+ }
388
+ }
389
+ return hostName;
282
390
  }
283
391
 
284
392
  /**
@@ -286,134 +394,152 @@ async function findTunnelHostname() {
286
394
  * We've disabled this for now until we figure out a better user experience.
287
395
  */
288
396
  function useTunnel(toggle: boolean) {
289
- const tunnel = useRef<ReturnType<typeof spawn>>();
290
- const removeSignalExitListener = useRef<() => void>();
291
- // TODO: test if cloudflared is available, if not
292
- // point them to a url where they can get docs to install it
293
- useEffect(() => {
294
- async function startTunnel() {
295
- if (toggle) {
296
- try {
297
- await commandExists("cloudflared");
298
- } catch (e) {
299
- logger.warn(
300
- "To share your worker on the Internet, please install `cloudflared` from https://developers.cloudflare.com/cloudflare-one/connections/connect-apps/install-and-setup/installation"
301
- );
302
- return;
303
- }
304
- logger.log("⎔ Starting a tunnel...");
305
- tunnel.current = spawn("cloudflared", [
306
- "tunnel",
307
- "--url",
308
- "http://localhost:8787",
309
- "--metrics",
310
- "localhost:8789",
311
- ]);
312
-
313
- tunnel.current.on("close", (code) => {
314
- if (code) {
315
- logger.log(`Tunnel process exited with code ${code}`);
316
- }
317
- });
318
-
319
- removeSignalExitListener.current = onExit((_code, _signal) => {
320
- logger.log("⎔ Shutting down local tunnel.");
321
- tunnel.current?.kill();
322
- tunnel.current = undefined;
323
- });
324
-
325
- const hostName = await findTunnelHostname();
326
- await clipboardy.write(hostName);
327
- logger.log(`⬣ Sharing at ${hostName}, copied to clipboard.`);
328
- }
329
- }
330
-
331
- startTunnel().catch(async (err) => {
332
- logger.error("tunnel:", err);
333
- });
334
-
335
- return () => {
336
- if (tunnel.current) {
337
- logger.log("⎔ Shutting down tunnel.");
338
- tunnel.current?.kill();
339
- tunnel.current = undefined;
340
- removeSignalExitListener.current && removeSignalExitListener.current();
341
- removeSignalExitListener.current = undefined;
342
- }
343
- };
344
- }, [toggle]);
397
+ const tunnel = useRef<ReturnType<typeof spawn>>();
398
+ const removeSignalExitListener = useRef<() => void>();
399
+ // TODO: test if cloudflared is available, if not
400
+ // point them to a url where they can get docs to install it
401
+ useEffect(() => {
402
+ async function startTunnel() {
403
+ if (toggle) {
404
+ try {
405
+ await commandExists("cloudflared");
406
+ } catch (e) {
407
+ logger.warn(
408
+ "To share your worker on the Internet, please install `cloudflared` from https://developers.cloudflare.com/cloudflare-one/connections/connect-apps/install-and-setup/installation"
409
+ );
410
+ return;
411
+ }
412
+ logger.log("⎔ Starting a tunnel...");
413
+ tunnel.current = spawn("cloudflared", [
414
+ "tunnel",
415
+ "--url",
416
+ "http://localhost:8787",
417
+ "--metrics",
418
+ "localhost:8789",
419
+ ]);
420
+
421
+ tunnel.current.on("close", (code) => {
422
+ if (code) {
423
+ logger.log(`Tunnel process exited with code ${code}`);
424
+ }
425
+ });
426
+
427
+ removeSignalExitListener.current = onExit((_code, _signal) => {
428
+ logger.log("⎔ Shutting down local tunnel.");
429
+ tunnel.current?.kill();
430
+ tunnel.current = undefined;
431
+ });
432
+
433
+ const hostName = await findTunnelHostname();
434
+ await clipboardy.write(hostName);
435
+ logger.log(`⬣ Sharing at ${hostName}, copied to clipboard.`);
436
+ }
437
+ }
438
+
439
+ startTunnel().catch(async (err) => {
440
+ logger.error("tunnel:", err);
441
+ });
442
+
443
+ return () => {
444
+ if (tunnel.current) {
445
+ logger.log("⎔ Shutting down tunnel.");
446
+ tunnel.current?.kill();
447
+ tunnel.current = undefined;
448
+ removeSignalExitListener.current && removeSignalExitListener.current();
449
+ removeSignalExitListener.current = undefined;
450
+ }
451
+ };
452
+ }, [toggle]);
345
453
  }
346
454
 
347
455
  type useHotkeysInitialState = {
348
- local: boolean;
349
- tunnel: boolean;
456
+ local: boolean;
457
+ tunnel: boolean;
350
458
  };
351
- function useHotkeys(
352
- initial: useHotkeysInitialState,
353
- port: number,
354
- ip: string,
355
- inspectorPort: number,
356
- localProtocol: "http" | "https"
357
- ) {
358
- // UGH, we should put port in context instead
359
- const [toggles, setToggles] = useState(initial);
360
- const { exit } = useApp();
361
- useInput(
362
- async (
363
- input,
364
- // eslint-disable-next-line unused-imports/no-unused-vars
365
- key
366
- ) => {
367
- switch (input.toLowerCase()) {
368
- // clear console
369
- case "c":
370
- console.clear();
371
- // This console.log causes Ink to re-render the `DevSession` component.
372
- // Couldn't find a better way to tell it to do so...
373
- console.log();
374
- break;
375
- // open browser
376
- case "b": {
377
- await openInBrowser(`${localProtocol}://${ip}:${port}`);
378
- break;
379
- }
380
- // toggle inspector
381
- case "d": {
382
- await openInspector(inspectorPort);
383
- break;
384
- }
385
- // toggle local
386
- case "l":
387
- setToggles((previousToggles) => ({
388
- ...previousToggles,
389
- local: !previousToggles.local,
390
- }));
391
- break;
392
- // shut down
393
- case "q":
394
- case "x":
395
- exit();
396
- break;
397
- default:
398
- // nothing?
399
- break;
400
- }
401
- }
402
- );
403
- return toggles;
459
+ function useHotkeys(props: {
460
+ initial: useHotkeysInitialState;
461
+ port: number;
462
+ ip: string;
463
+ inspectorPort: number;
464
+ inspect: boolean;
465
+ localProtocol: "http" | "https";
466
+ forceLocal: boolean | undefined;
467
+ }) {
468
+ const {
469
+ initial,
470
+ port,
471
+ ip,
472
+ inspectorPort,
473
+ inspect,
474
+ localProtocol,
475
+ forceLocal,
476
+ } = props;
477
+ // UGH, we should put port in context instead
478
+ const [toggles, setToggles] = useState(initial);
479
+ const { exit } = useApp();
480
+ useInput(
481
+ async (
482
+ input,
483
+ // eslint-disable-next-line unused-imports/no-unused-vars
484
+ key
485
+ ) => {
486
+ switch (input.toLowerCase()) {
487
+ // clear console
488
+ case "c":
489
+ console.clear();
490
+ // This console.log causes Ink to re-render the `DevSession` component.
491
+ // Couldn't find a better way to tell it to do so...
492
+ console.log();
493
+ break;
494
+ // open browser
495
+ case "b": {
496
+ if (ip === "0.0.0.0") {
497
+ await openInBrowser(`${localProtocol}://127.0.0.1:${port}`);
498
+ return;
499
+ }
500
+ await openInBrowser(`${localProtocol}://${ip}:${port}`);
501
+ break;
502
+ }
503
+ // toggle inspector
504
+ case "d": {
505
+ if (inspect) {
506
+ await openInspector(inspectorPort);
507
+ }
508
+ break;
509
+ }
510
+ // toggle local
511
+ case "l":
512
+ if (forceLocal) return;
513
+ setToggles((previousToggles) => ({
514
+ ...previousToggles,
515
+ local: !previousToggles.local,
516
+ }));
517
+ break;
518
+ // shut down
519
+ case "q":
520
+ case "x":
521
+ exit();
522
+ break;
523
+ default:
524
+ // nothing?
525
+ break;
526
+ }
527
+ }
528
+ );
529
+ return toggles;
404
530
  }
405
531
 
406
532
  function ErrorFallback(props: { error: Error }) {
407
- const { exit } = useApp();
408
- useEffect(() => exit(props.error));
409
- return (
410
- <>
411
- <Text>Something went wrong:</Text>
412
- <Text>{props.error.stack}</Text>
413
- </>
414
- );
533
+ const { exit } = useApp();
534
+ useEffect(() => exit(props.error));
535
+ return (
536
+ <>
537
+ <Text>Something went wrong:</Text>
538
+ <Text>{props.error.stack}</Text>
539
+ </>
540
+ );
415
541
  }
416
542
 
417
543
  export default withErrorBoundary(DevImplementation, {
418
- FallbackComponent: ErrorFallback,
544
+ FallbackComponent: ErrorFallback,
419
545
  });