wrangler 0.0.0-e978986 → 0.0.0-ea7ee45

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 (181) hide show
  1. package/README.md +7 -1
  2. package/bin/wrangler.js +111 -57
  3. package/kv-asset-handler.js +1 -0
  4. package/miniflare-dist/index.mjs +9 -2
  5. package/package.json +156 -152
  6. package/src/__tests__/config-cache.test.ts +24 -24
  7. package/src/__tests__/configuration.test.ts +3818 -3363
  8. package/src/__tests__/dev.test.tsx +1128 -949
  9. package/src/__tests__/guess-worker-format.test.ts +68 -68
  10. package/src/__tests__/helpers/cmd-shim.d.ts +6 -6
  11. package/src/__tests__/helpers/faye-websocket.d.ts +4 -4
  12. package/src/__tests__/helpers/mock-account-id.ts +24 -24
  13. package/src/__tests__/helpers/mock-bin.ts +20 -20
  14. package/src/__tests__/helpers/mock-cfetch.ts +92 -92
  15. package/src/__tests__/helpers/mock-console.ts +49 -39
  16. package/src/__tests__/helpers/mock-dialogs.ts +94 -71
  17. package/src/__tests__/helpers/mock-http-server.ts +30 -30
  18. package/src/__tests__/helpers/mock-istty.ts +65 -18
  19. package/src/__tests__/helpers/mock-kv.ts +26 -26
  20. package/src/__tests__/helpers/mock-oauth-flow.ts +223 -228
  21. package/src/__tests__/helpers/mock-process.ts +39 -0
  22. package/src/__tests__/helpers/mock-stdin.ts +82 -77
  23. package/src/__tests__/helpers/mock-web-socket.ts +21 -21
  24. package/src/__tests__/helpers/run-in-tmp.ts +27 -27
  25. package/src/__tests__/helpers/run-wrangler.ts +8 -8
  26. package/src/__tests__/helpers/write-worker-source.ts +16 -16
  27. package/src/__tests__/helpers/write-wrangler-toml.ts +9 -9
  28. package/src/__tests__/https-options.test.ts +104 -104
  29. package/src/__tests__/index.test.ts +239 -233
  30. package/src/__tests__/init.test.ts +1605 -1180
  31. package/src/__tests__/jest.setup.ts +63 -33
  32. package/src/__tests__/kv.test.ts +1128 -990
  33. package/src/__tests__/logger.test.ts +100 -74
  34. package/src/__tests__/package-manager.test.ts +303 -303
  35. package/src/__tests__/pages.test.ts +1160 -563
  36. package/src/__tests__/parse.test.ts +252 -252
  37. package/src/__tests__/publish.test.ts +6344 -5299
  38. package/src/__tests__/pubsub.test.ts +276 -0
  39. package/src/__tests__/r2.test.ts +133 -133
  40. package/src/__tests__/route.test.ts +18 -18
  41. package/src/__tests__/secret.test.ts +382 -377
  42. package/src/__tests__/tail.test.ts +530 -530
  43. package/src/__tests__/user.test.ts +123 -111
  44. package/src/__tests__/whoami.test.tsx +198 -83
  45. package/src/__tests__/worker-namespace.test.ts +327 -0
  46. package/src/abort.d.ts +1 -1
  47. package/src/api/dev.ts +39 -0
  48. package/src/api/index.ts +1 -0
  49. package/src/bundle-reporter.tsx +29 -0
  50. package/src/bundle.ts +158 -146
  51. package/src/cfetch/index.ts +80 -80
  52. package/src/cfetch/internal.ts +92 -88
  53. package/src/cli.ts +21 -7
  54. package/src/config/config.ts +204 -195
  55. package/src/config/diagnostics.ts +61 -61
  56. package/src/config/environment.ts +367 -357
  57. package/src/config/index.ts +193 -193
  58. package/src/config/validation-helpers.ts +366 -357
  59. package/src/config/validation.ts +1521 -1367
  60. package/src/config-cache.ts +24 -25
  61. package/src/create-worker-preview.ts +206 -136
  62. package/src/create-worker-upload-form.ts +238 -238
  63. package/src/dev/dev-vars.ts +13 -13
  64. package/src/dev/dev.tsx +334 -336
  65. package/src/dev/local.tsx +304 -272
  66. package/src/dev/remote.tsx +351 -211
  67. package/src/dev/use-esbuild.ts +126 -94
  68. package/src/dev.tsx +526 -0
  69. package/src/dialogs.tsx +97 -97
  70. package/src/durable.ts +87 -87
  71. package/src/entry.ts +234 -228
  72. package/src/environment-variables.ts +23 -23
  73. package/src/errors.ts +6 -6
  74. package/src/generate.ts +33 -0
  75. package/src/git-client.ts +42 -0
  76. package/src/https-options.ts +79 -79
  77. package/src/index.tsx +1775 -2752
  78. package/src/init.ts +549 -0
  79. package/src/inspect.ts +593 -593
  80. package/src/intl-polyfill.d.ts +123 -123
  81. package/src/kv.ts +277 -277
  82. package/src/logger.ts +46 -39
  83. package/src/miniflare-cli/enum-keys.ts +8 -8
  84. package/src/miniflare-cli/index.ts +42 -31
  85. package/src/miniflare-cli/request-context.ts +18 -18
  86. package/src/module-collection.ts +212 -212
  87. package/src/open-in-browser.ts +4 -6
  88. package/src/package-manager.ts +123 -123
  89. package/src/pages/build.tsx +202 -0
  90. package/src/pages/constants.ts +7 -0
  91. package/src/pages/deployments.tsx +101 -0
  92. package/src/pages/dev.tsx +964 -0
  93. package/src/pages/functions/buildPlugin.ts +105 -0
  94. package/src/pages/functions/buildWorker.ts +151 -0
  95. package/{pages → src/pages}/functions/filepath-routing.test.ts +113 -113
  96. package/src/pages/functions/filepath-routing.ts +189 -0
  97. package/src/pages/functions/identifiers.ts +78 -0
  98. package/src/pages/functions/routes.ts +151 -0
  99. package/src/pages/index.tsx +84 -0
  100. package/src/pages/projects.tsx +157 -0
  101. package/src/pages/publish.tsx +335 -0
  102. package/src/pages/types.ts +40 -0
  103. package/src/pages/upload.tsx +384 -0
  104. package/src/pages/utils.ts +12 -0
  105. package/src/parse.ts +209 -125
  106. package/src/paths.ts +6 -6
  107. package/src/preview.ts +31 -0
  108. package/src/proxy.ts +400 -389
  109. package/src/publish.ts +688 -505
  110. package/src/pubsub/index.ts +286 -0
  111. package/src/pubsub/pubsub-commands.tsx +594 -0
  112. package/src/r2.ts +19 -19
  113. package/src/selfsigned.d.ts +23 -23
  114. package/src/sites.tsx +278 -201
  115. package/src/tail/filters.ts +108 -108
  116. package/src/tail/index.ts +217 -217
  117. package/src/tail/printing.ts +45 -45
  118. package/src/update-check.ts +11 -11
  119. package/src/user/choose-account.tsx +60 -0
  120. package/src/user/env-vars.ts +46 -0
  121. package/src/user/generate-auth-url.ts +33 -0
  122. package/src/user/generate-random-state.ts +16 -0
  123. package/src/user/index.ts +3 -0
  124. package/src/user/user.tsx +1130 -0
  125. package/src/whoami.tsx +61 -41
  126. package/src/worker-namespace.ts +190 -0
  127. package/src/worker.ts +104 -99
  128. package/src/zones.ts +76 -0
  129. package/templates/checked-fetch.js +17 -0
  130. package/templates/new-worker-scheduled.js +4 -4
  131. package/templates/new-worker-scheduled.ts +16 -16
  132. package/templates/new-worker.js +3 -3
  133. package/templates/new-worker.ts +15 -15
  134. package/templates/no-op-worker.js +10 -0
  135. package/templates/pages-template-plugin.ts +155 -0
  136. package/templates/pages-template-worker.ts +161 -0
  137. package/templates/static-asset-facade.js +32 -36
  138. package/templates/tsconfig.json +95 -95
  139. package/wrangler-dist/cli.js +40620 -39223
  140. package/pages/functions/buildPlugin.ts +0 -105
  141. package/pages/functions/buildWorker.ts +0 -151
  142. package/pages/functions/filepath-routing.ts +0 -189
  143. package/pages/functions/identifiers.ts +0 -78
  144. package/pages/functions/routes.ts +0 -156
  145. package/pages/functions/template-plugin.ts +0 -147
  146. package/pages/functions/template-worker.ts +0 -143
  147. package/src/pages.tsx +0 -2027
  148. package/src/user.tsx +0 -1203
  149. package/vendor/@cloudflare/kv-asset-handler/CHANGELOG.md +0 -332
  150. package/vendor/@cloudflare/kv-asset-handler/LICENSE_APACHE +0 -176
  151. package/vendor/@cloudflare/kv-asset-handler/LICENSE_MIT +0 -25
  152. package/vendor/@cloudflare/kv-asset-handler/README.md +0 -245
  153. package/vendor/@cloudflare/kv-asset-handler/dist/index.d.ts +0 -32
  154. package/vendor/@cloudflare/kv-asset-handler/dist/index.js +0 -354
  155. package/vendor/@cloudflare/kv-asset-handler/dist/mocks.d.ts +0 -13
  156. package/vendor/@cloudflare/kv-asset-handler/dist/mocks.js +0 -148
  157. package/vendor/@cloudflare/kv-asset-handler/dist/test/getAssetFromKV.d.ts +0 -1
  158. package/vendor/@cloudflare/kv-asset-handler/dist/test/getAssetFromKV.js +0 -436
  159. package/vendor/@cloudflare/kv-asset-handler/dist/test/mapRequestToAsset.d.ts +0 -1
  160. package/vendor/@cloudflare/kv-asset-handler/dist/test/mapRequestToAsset.js +0 -40
  161. package/vendor/@cloudflare/kv-asset-handler/dist/test/serveSinglePageApp.d.ts +0 -1
  162. package/vendor/@cloudflare/kv-asset-handler/dist/test/serveSinglePageApp.js +0 -42
  163. package/vendor/@cloudflare/kv-asset-handler/dist/types.d.ts +0 -26
  164. package/vendor/@cloudflare/kv-asset-handler/dist/types.js +0 -31
  165. package/vendor/@cloudflare/kv-asset-handler/package.json +0 -52
  166. package/vendor/@cloudflare/kv-asset-handler/src/index.ts +0 -296
  167. package/vendor/@cloudflare/kv-asset-handler/src/mocks.ts +0 -136
  168. package/vendor/@cloudflare/kv-asset-handler/src/test/getAssetFromKV.ts +0 -464
  169. package/vendor/@cloudflare/kv-asset-handler/src/test/mapRequestToAsset.ts +0 -33
  170. package/vendor/@cloudflare/kv-asset-handler/src/test/serveSinglePageApp.ts +0 -42
  171. package/vendor/@cloudflare/kv-asset-handler/src/types.ts +0 -39
  172. package/vendor/wrangler-mime/CHANGELOG.md +0 -289
  173. package/vendor/wrangler-mime/LICENSE +0 -21
  174. package/vendor/wrangler-mime/Mime.js +0 -97
  175. package/vendor/wrangler-mime/README.md +0 -187
  176. package/vendor/wrangler-mime/cli.js +0 -46
  177. package/vendor/wrangler-mime/index.js +0 -4
  178. package/vendor/wrangler-mime/lite.js +0 -4
  179. package/vendor/wrangler-mime/package.json +0 -52
  180. package/vendor/wrangler-mime/types/other.js +0 -1
  181. package/vendor/wrangler-mime/types/standard.js +0 -1
@@ -1,227 +1,367 @@
1
- import assert from "node:assert";
2
1
  import { readFile } from "node:fs/promises";
3
2
  import path from "node:path";
4
- import { useState, useEffect, useRef } from "react";
5
- import { createWorkerPreview } from "../create-worker-preview";
3
+ import React, { useState, useEffect, useRef } from "react";
4
+ import { useErrorHandler } from "react-error-boundary";
5
+ import { printBundleSize } from "../bundle-reporter";
6
+ import {
7
+ createPreviewSession,
8
+ createWorkerPreview,
9
+ } from "../create-worker-preview";
6
10
  import useInspector from "../inspect";
7
11
  import { logger } from "../logger";
8
12
  import { usePreviewServer } from "../proxy";
9
13
  import { syncAssets } from "../sites";
10
- import type { CfPreviewToken } from "../create-worker-preview";
14
+ import { ChooseAccount, getAccountChoices, requireApiToken } from "../user";
15
+ import type { Route } from "../config/environment";
16
+ import type {
17
+ CfPreviewToken,
18
+ CfPreviewSession,
19
+ } from "../create-worker-preview";
11
20
  import type { AssetPaths } from "../sites";
12
- import type { CfModule, CfWorkerInit, CfScriptFormat } from "../worker";
21
+ import type { ChooseAccountItem } from "../user";
22
+ import type {
23
+ CfModule,
24
+ CfWorkerInit,
25
+ CfScriptFormat,
26
+ CfAccount,
27
+ CfWorkerContext,
28
+ } from "../worker";
13
29
  import type { EsbuildBundle } from "./use-esbuild";
14
30
 
15
31
  export function Remote(props: {
16
- name: string | undefined;
17
- bundle: EsbuildBundle | undefined;
18
- format: CfScriptFormat | undefined;
19
- public: string | undefined;
20
- assetPaths: AssetPaths | undefined;
21
- port: number;
22
- ip: string;
23
- localProtocol: "https" | "http";
24
- inspectorPort: number;
25
- accountId: string | undefined;
26
- apiToken: string | undefined;
27
- bindings: CfWorkerInit["bindings"];
28
- compatibilityDate: string;
29
- compatibilityFlags: string[] | undefined;
30
- usageModel: "bundled" | "unbound" | undefined;
31
- env: string | undefined;
32
- legacyEnv: boolean | undefined;
33
- zone: { id: string; host: string } | undefined;
32
+ name: string | undefined;
33
+ bundle: EsbuildBundle | undefined;
34
+ format: CfScriptFormat | undefined;
35
+ isWorkersSite: boolean;
36
+ assetPaths: AssetPaths | undefined;
37
+ port: number;
38
+ ip: string;
39
+ localProtocol: "https" | "http";
40
+ inspectorPort: number;
41
+ accountId: string | undefined;
42
+ bindings: CfWorkerInit["bindings"];
43
+ compatibilityDate: string;
44
+ compatibilityFlags: string[] | undefined;
45
+ usageModel: "bundled" | "unbound" | undefined;
46
+ env: string | undefined;
47
+ legacyEnv: boolean | undefined;
48
+ zone: string | undefined;
49
+ host: string | undefined;
50
+ routes: Route[] | undefined;
51
+ onReady?: () => void;
34
52
  }) {
35
- assert(props.accountId, "accountId is required");
36
- assert(props.apiToken, "apiToken is required");
37
- const previewToken = useWorker({
38
- name: props.name,
39
- bundle: props.bundle,
40
- format: props.format,
41
- modules: props.bundle ? props.bundle.modules : [],
42
- accountId: props.accountId,
43
- apiToken: props.apiToken,
44
- bindings: props.bindings,
45
- assetPaths: props.assetPaths,
46
- port: props.port,
47
- compatibilityDate: props.compatibilityDate,
48
- compatibilityFlags: props.compatibilityFlags,
49
- usageModel: props.usageModel,
50
- env: props.env,
51
- legacyEnv: props.legacyEnv,
52
- zone: props.zone,
53
- });
54
-
55
- usePreviewServer({
56
- previewToken,
57
- publicRoot: props.public,
58
- localProtocol: props.localProtocol,
59
- localPort: props.port,
60
- ip: props.ip,
61
- });
62
-
63
- useInspector({
64
- inspectorUrl: previewToken ? previewToken.inspectorUrl.href : undefined,
65
- port: props.inspectorPort,
66
- logToTerminal: true,
67
- });
68
- return null;
53
+ const [accountId, setAccountId] = useState(props.accountId);
54
+ const accountChoicesRef = useRef<Promise<ChooseAccountItem[]>>();
55
+ const [accountChoices, setAccountChoices] = useState<ChooseAccountItem[]>();
56
+
57
+ const previewToken = useWorker({
58
+ name: props.name,
59
+ bundle: props.bundle,
60
+ format: props.format,
61
+ modules: props.bundle ? props.bundle.modules : [],
62
+ accountId,
63
+ bindings: props.bindings,
64
+ assetPaths: props.assetPaths,
65
+ isWorkersSite: props.isWorkersSite,
66
+ port: props.port,
67
+ compatibilityDate: props.compatibilityDate,
68
+ compatibilityFlags: props.compatibilityFlags,
69
+ usageModel: props.usageModel,
70
+ env: props.env,
71
+ legacyEnv: props.legacyEnv,
72
+ zone: props.zone,
73
+ host: props.host,
74
+ routes: props.routes,
75
+ onReady: props.onReady,
76
+ });
77
+
78
+ usePreviewServer({
79
+ previewToken,
80
+ assetDirectory: props.isWorkersSite
81
+ ? undefined
82
+ : props.assetPaths?.assetDirectory,
83
+ localProtocol: props.localProtocol,
84
+ localPort: props.port,
85
+ ip: props.ip,
86
+ });
87
+
88
+ useInspector({
89
+ inspectorUrl: previewToken ? previewToken.inspectorUrl.href : undefined,
90
+ port: props.inspectorPort,
91
+ logToTerminal: true,
92
+ });
93
+
94
+ const errorHandler = useErrorHandler();
95
+
96
+ // This effect handles the async step of fetching the available accounts for the current user.
97
+ // If only one account is available then it is just used by calling `setAccountId()`.
98
+ useEffect(() => {
99
+ if (accountChoicesRef.current !== undefined) {
100
+ return;
101
+ }
102
+ accountChoicesRef.current = getAccountChoices();
103
+ accountChoicesRef.current.then(
104
+ (accounts) => {
105
+ if (accounts.length === 1) {
106
+ setAccountId(accounts[0].id);
107
+ } else {
108
+ setAccountChoices(accounts);
109
+ }
110
+ },
111
+ (err) => {
112
+ errorHandler(err);
113
+ }
114
+ );
115
+ });
116
+
117
+ // If we have not already chosen an account and there are multiple accounts available
118
+ // allow the users to select one.
119
+ return accountId === undefined && accountChoices !== undefined ? (
120
+ <ChooseAccount
121
+ accounts={accountChoices}
122
+ onSelect={(selectedAccountId) => setAccountId(selectedAccountId)}
123
+ onError={(err) => errorHandler(err)}
124
+ ></ChooseAccount>
125
+ ) : null;
69
126
  }
70
127
 
71
128
  export function useWorker(props: {
72
- name: string | undefined;
73
- bundle: EsbuildBundle | undefined;
74
- format: CfScriptFormat | undefined;
75
- modules: CfModule[];
76
- accountId: string;
77
- apiToken: string;
78
- bindings: CfWorkerInit["bindings"];
79
- assetPaths: AssetPaths | undefined;
80
- port: number;
81
- compatibilityDate: string | undefined;
82
- compatibilityFlags: string[] | undefined;
83
- usageModel: "bundled" | "unbound" | undefined;
84
- env: string | undefined;
85
- legacyEnv: boolean | undefined;
86
- zone: { id: string; host: string } | undefined;
129
+ name: string | undefined;
130
+ bundle: EsbuildBundle | undefined;
131
+ format: CfScriptFormat | undefined;
132
+ modules: CfModule[];
133
+ accountId: string | undefined;
134
+ bindings: CfWorkerInit["bindings"];
135
+ assetPaths: AssetPaths | undefined;
136
+ isWorkersSite: boolean;
137
+ port: number;
138
+ compatibilityDate: string | undefined;
139
+ compatibilityFlags: string[] | undefined;
140
+ usageModel: "bundled" | "unbound" | undefined;
141
+ env: string | undefined;
142
+ legacyEnv: boolean | undefined;
143
+ zone: string | undefined;
144
+ host: string | undefined;
145
+ routes: Route[] | undefined;
146
+ onReady?: () => void;
87
147
  }): CfPreviewToken | undefined {
88
- const {
89
- name,
90
- bundle,
91
- format,
92
- modules,
93
- accountId,
94
- apiToken,
95
- bindings,
96
- assetPaths,
97
- compatibilityDate,
98
- compatibilityFlags,
99
- usageModel,
100
- port,
101
- } = props;
102
- const [token, setToken] = useState<CfPreviewToken | undefined>();
103
-
104
- // This is the most reliable way to detect whether
105
- // something's "happened" in our system; We make a ref and
106
- // mark it once we log our initial message. Refs are vars!
107
- const startedRef = useRef(false);
108
-
109
- useEffect(() => {
110
- const abortController = new AbortController();
111
- async function start() {
112
- setToken(undefined); // reset token in case we're re-running
113
-
114
- if (!bundle || !format) return;
115
-
116
- if (!startedRef.current) {
117
- startedRef.current = true;
118
- } else {
119
- logger.log("⎔ Detected changes, restarted server.");
120
- }
121
-
122
- const assets = await syncAssets(
123
- accountId,
124
- // When we're using the newer service environments, we wouldn't
125
- // have added the env name on to the script name. However, we must
126
- // include it in the kv namespace name regardless (since there's no
127
- // concept of service environments for kv namespaces yet).
128
- name + (!props.legacyEnv && props.env ? `-${props.env}` : ""),
129
- assetPaths,
130
- true,
131
- false
132
- ); // TODO: cancellable?
133
-
134
- const content = await readFile(bundle.path, "utf-8");
135
-
136
- const init: CfWorkerInit = {
137
- name,
138
- main: {
139
- name: path.basename(bundle.path),
140
- type: format === "modules" ? "esm" : "commonjs",
141
- content,
142
- },
143
- modules: modules.concat(
144
- assets.manifest
145
- ? {
146
- name: "__STATIC_CONTENT_MANIFEST",
147
- content: JSON.stringify(assets.manifest),
148
- type: "text",
149
- }
150
- : []
151
- ),
152
- bindings: {
153
- ...bindings,
154
- kv_namespaces: (bindings.kv_namespaces || []).concat(
155
- assets.namespace
156
- ? { binding: "__STATIC_CONTENT", id: assets.namespace }
157
- : []
158
- ),
159
- text_blobs: {
160
- ...bindings.text_blobs,
161
- ...(assets.manifest &&
162
- format === "service-worker" && {
163
- __STATIC_CONTENT_MANIFEST: "__STATIC_CONTENT_MANIFEST",
164
- }),
165
- },
166
- },
167
- migrations: undefined, // no migrations in dev
168
- compatibility_date: compatibilityDate,
169
- compatibility_flags: compatibilityFlags,
170
- usage_model: usageModel,
171
- };
172
- setToken(
173
- await createWorkerPreview(
174
- init,
175
- {
176
- accountId,
177
- apiToken,
178
- },
179
- { env: props.env, legacyEnv: props.legacyEnv, zone: props.zone },
180
- abortController.signal
181
- )
182
- );
183
- }
184
- start().catch((err) => {
185
- // we want to log the error, but not end the process
186
- // since it could recover after the developer fixes whatever's wrong
187
- if ((err as { code: string }).code !== "ABORT_ERR") {
188
- // instead of logging the raw API error to the user,
189
- // give them friendly instructions
190
- // for error 10063 (workers.dev subdomain required)
191
- if (err.code === 10063) {
192
- const errorMessage =
193
- "Error: You need to register a workers.dev subdomain before running the dev command in remote mode";
194
- const solutionMessage =
195
- "You can either enable local mode by pressing l, or register a workers.dev subdomain here:";
196
- const onboardingLink = `https://dash.cloudflare.com/${accountId}/workers/onboarding`;
197
- logger.error(
198
- `${errorMessage}\n${solutionMessage}\n${onboardingLink}`
199
- );
200
- } else {
201
- logger.error("Error on remote worker:", err);
202
- }
203
- }
204
- });
205
-
206
- return () => {
207
- abortController.abort();
208
- };
209
- }, [
210
- name,
211
- bundle,
212
- format,
213
- accountId,
214
- apiToken,
215
- port,
216
- assetPaths,
217
- compatibilityDate,
218
- compatibilityFlags,
219
- usageModel,
220
- bindings,
221
- modules,
222
- props.env,
223
- props.legacyEnv,
224
- props.zone,
225
- ]);
226
- return token;
148
+ const {
149
+ name,
150
+ bundle,
151
+ format,
152
+ modules,
153
+ accountId,
154
+ bindings,
155
+ assetPaths,
156
+ compatibilityDate,
157
+ compatibilityFlags,
158
+ usageModel,
159
+ port,
160
+ onReady,
161
+ } = props;
162
+ const [session, setSession] = useState<CfPreviewSession | undefined>();
163
+ const [token, setToken] = useState<CfPreviewToken | undefined>();
164
+
165
+ // This is the most reliable way to detect whether
166
+ // something's "happened" in our system; We make a ref and
167
+ // mark it once we log our initial message. Refs are vars!
168
+ const startedRef = useRef(false);
169
+
170
+ // This effect sets up the preview session
171
+ useEffect(() => {
172
+ const abortController = new AbortController();
173
+ async function start() {
174
+ if (accountId === undefined) {
175
+ return;
176
+ }
177
+
178
+ const workerAccount: CfAccount = {
179
+ accountId,
180
+ apiToken: requireApiToken(),
181
+ };
182
+
183
+ const workerCtx: CfWorkerContext = {
184
+ env: props.env,
185
+ legacyEnv: props.legacyEnv,
186
+ zone: props.zone,
187
+ host: props.host,
188
+ routes: props.routes,
189
+ };
190
+
191
+ setSession(
192
+ await createPreviewSession(
193
+ workerAccount,
194
+ workerCtx,
195
+ abortController.signal
196
+ )
197
+ );
198
+ }
199
+ start().catch((err) => {
200
+ // we want to log the error, but not end the process
201
+ // since it could recover after the developer fixes whatever's wrong
202
+ if ((err as { code: string }).code !== "ABORT_ERR") {
203
+ logger.error("Error while creating remote dev session:", err);
204
+ }
205
+ });
206
+
207
+ return () => {
208
+ abortController.abort();
209
+ };
210
+ }, [
211
+ accountId,
212
+ props.env,
213
+ props.host,
214
+ props.legacyEnv,
215
+ props.routes,
216
+ props.zone,
217
+ ]);
218
+
219
+ // This effect uses the session to upload the worker and create a preview
220
+ useEffect(() => {
221
+ const abortController = new AbortController();
222
+ async function start() {
223
+ if (accountId === undefined) {
224
+ return;
225
+ }
226
+ if (session === undefined) {
227
+ return;
228
+ }
229
+ setToken(undefined); // reset token in case we're re-running
230
+
231
+ if (!bundle || !format) return;
232
+
233
+ if (!startedRef.current) {
234
+ startedRef.current = true;
235
+ } else {
236
+ logger.log("⎔ Detected changes, restarted server.");
237
+ }
238
+
239
+ const content = await readFile(bundle.path, "utf-8");
240
+
241
+ // TODO: For Dev we could show the reporter message in the interactive box.
242
+ void printBundleSize(
243
+ { name: path.basename(bundle.path), content: content },
244
+ modules
245
+ );
246
+
247
+ const assets = await syncAssets(
248
+ accountId,
249
+ // When we're using the newer service environments, we wouldn't
250
+ // have added the env name on to the script name. However, we must
251
+ // include it in the kv namespace name regardless (since there's no
252
+ // concept of service environments for kv namespaces yet).
253
+ name + (!props.legacyEnv && props.env ? `-${props.env}` : ""),
254
+ props.isWorkersSite ? assetPaths : undefined,
255
+ true,
256
+ false
257
+ ); // TODO: cancellable?
258
+
259
+ const init: CfWorkerInit = {
260
+ name,
261
+ main: {
262
+ name: path.basename(bundle.path),
263
+ type: format === "modules" ? "esm" : "commonjs",
264
+ content,
265
+ },
266
+ modules: modules.concat(
267
+ assets.manifest
268
+ ? {
269
+ name: "__STATIC_CONTENT_MANIFEST",
270
+ content: JSON.stringify(assets.manifest),
271
+ type: "text",
272
+ }
273
+ : []
274
+ ),
275
+ bindings: {
276
+ ...bindings,
277
+ kv_namespaces: (bindings.kv_namespaces || []).concat(
278
+ assets.namespace
279
+ ? { binding: "__STATIC_CONTENT", id: assets.namespace }
280
+ : []
281
+ ),
282
+ text_blobs: {
283
+ ...bindings.text_blobs,
284
+ ...(assets.manifest &&
285
+ format === "service-worker" && {
286
+ __STATIC_CONTENT_MANIFEST: "__STATIC_CONTENT_MANIFEST",
287
+ }),
288
+ },
289
+ },
290
+ migrations: undefined, // no migrations in dev
291
+ compatibility_date: compatibilityDate,
292
+ compatibility_flags: compatibilityFlags,
293
+ usage_model: usageModel,
294
+ };
295
+
296
+ const workerAccount: CfAccount = {
297
+ accountId,
298
+ apiToken: requireApiToken(),
299
+ };
300
+
301
+ const workerCtx: CfWorkerContext = {
302
+ env: props.env,
303
+ legacyEnv: props.legacyEnv,
304
+ zone: props.zone,
305
+ host: props.host,
306
+ routes: props.routes,
307
+ };
308
+
309
+ setToken(
310
+ await createWorkerPreview(
311
+ init,
312
+ workerAccount,
313
+ workerCtx,
314
+ session,
315
+ abortController.signal
316
+ )
317
+ );
318
+ onReady?.();
319
+ }
320
+ start().catch((err) => {
321
+ // we want to log the error, but not end the process
322
+ // since it could recover after the developer fixes whatever's wrong
323
+ if ((err as { code: string }).code !== "ABORT_ERR") {
324
+ // instead of logging the raw API error to the user,
325
+ // give them friendly instructions
326
+ // for error 10063 (workers.dev subdomain required)
327
+ if (err.code === 10063) {
328
+ const errorMessage =
329
+ "Error: You need to register a workers.dev subdomain before running the dev command in remote mode";
330
+ const solutionMessage =
331
+ "You can either enable local mode by pressing l, or register a workers.dev subdomain here:";
332
+ const onboardingLink = `https://dash.cloudflare.com/${accountId}/workers/onboarding`;
333
+ logger.error(
334
+ `${errorMessage}\n${solutionMessage}\n${onboardingLink}`
335
+ );
336
+ } else {
337
+ logger.error("Error on remote worker:", err);
338
+ }
339
+ }
340
+ });
341
+
342
+ return () => {
343
+ abortController.abort();
344
+ };
345
+ }, [
346
+ name,
347
+ bundle,
348
+ format,
349
+ accountId,
350
+ port,
351
+ assetPaths,
352
+ props.isWorkersSite,
353
+ compatibilityDate,
354
+ compatibilityFlags,
355
+ usageModel,
356
+ bindings,
357
+ modules,
358
+ props.env,
359
+ props.legacyEnv,
360
+ props.zone,
361
+ props.host,
362
+ props.routes,
363
+ session,
364
+ onReady,
365
+ ]);
366
+ return token;
227
367
  }