wrangler 0.0.0-e7a0cd5b → 0.0.0-e87ad068

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 (255) hide show
  1. package/README.md +3 -3
  2. package/bin/wrangler.js +30 -10
  3. package/miniflare-dist/index.mjs +254 -128
  4. package/package.json +27 -19
  5. package/src/__tests__/api-dev.test.ts +65 -36
  6. package/src/__tests__/api-devregistry.test.ts +121 -0
  7. package/src/__tests__/api.test.ts +27 -5
  8. package/src/__tests__/configuration.test.ts +797 -98
  9. package/src/__tests__/constellation.test.ts +371 -0
  10. package/src/__tests__/d1/d1.test.ts +82 -0
  11. package/src/__tests__/d1/execute.test.ts +66 -0
  12. package/src/__tests__/d1/migrate.test.ts +257 -0
  13. package/src/__tests__/d1/splitter.test.ts +255 -0
  14. package/src/__tests__/delete.test.ts +91 -49
  15. package/src/__tests__/deployments.test.ts +330 -56
  16. package/src/__tests__/dev.test.tsx +198 -90
  17. package/src/__tests__/generate.test.ts +12 -10
  18. package/src/__tests__/guess-worker-format.test.ts +35 -0
  19. package/src/__tests__/helpers/end-event-loop.ts +6 -0
  20. package/src/__tests__/helpers/mock-auth-domain.ts +20 -0
  21. package/src/__tests__/helpers/mock-console.ts +6 -0
  22. package/src/__tests__/helpers/mock-dialogs.ts +71 -85
  23. package/src/__tests__/helpers/mock-get-pages-upload-token.ts +25 -0
  24. package/src/__tests__/helpers/mock-get-zone-from-host.ts +8 -5
  25. package/src/__tests__/helpers/mock-known-routes.ts +7 -2
  26. package/src/__tests__/helpers/mock-kv.ts +29 -16
  27. package/src/__tests__/helpers/mock-oauth-flow.ts +103 -92
  28. package/src/__tests__/helpers/mock-process.ts +8 -13
  29. package/src/__tests__/helpers/mock-set-timeout.ts +16 -0
  30. package/src/__tests__/helpers/msw/blob-worker.cjs +19 -0
  31. package/src/__tests__/helpers/msw/handlers/deployments.ts +128 -27
  32. package/src/__tests__/helpers/msw/handlers/namespaces.ts +18 -41
  33. package/src/__tests__/helpers/msw/handlers/oauth.ts +13 -18
  34. package/src/__tests__/helpers/msw/handlers/r2.ts +14 -34
  35. package/src/__tests__/helpers/msw/handlers/script.ts +9 -28
  36. package/src/__tests__/helpers/msw/handlers/user.ts +13 -24
  37. package/src/__tests__/helpers/msw/handlers/zones.ts +6 -8
  38. package/src/__tests__/helpers/msw/index.ts +32 -1
  39. package/src/__tests__/helpers/msw/read-file-sync.js +61 -0
  40. package/src/__tests__/helpers/string-dynamic-values-matcher.ts +28 -0
  41. package/src/__tests__/helpers/worker-scripts/parent-worker.js +4 -1
  42. package/src/__tests__/index.test.ts +92 -72
  43. package/src/__tests__/init.test.ts +1033 -631
  44. package/src/__tests__/jest.setup.ts +19 -42
  45. package/src/__tests__/kv.test.ts +355 -231
  46. package/src/__tests__/logger.test.ts +68 -2
  47. package/src/__tests__/logout.test.ts +47 -0
  48. package/src/__tests__/metrics.test.ts +94 -51
  49. package/src/__tests__/middleware.scheduled.test.ts +42 -32
  50. package/src/__tests__/middleware.test.ts +212 -122
  51. package/src/__tests__/mtls-certificates.test.ts +589 -0
  52. package/src/__tests__/pages/deployment-list.test.ts +80 -0
  53. package/src/__tests__/pages/functions-build.test.ts +529 -0
  54. package/src/__tests__/pages/pages.test.ts +81 -0
  55. package/src/__tests__/pages/project-create.test.ts +63 -0
  56. package/src/__tests__/pages/project-list.test.ts +110 -0
  57. package/src/__tests__/pages/project-upload.test.ts +500 -0
  58. package/src/__tests__/pages/publish.test.ts +2864 -0
  59. package/src/__tests__/pages-deployment-tail.test.ts +957 -0
  60. package/src/__tests__/parse.test.ts +106 -0
  61. package/src/__tests__/publish.test.ts +2227 -1005
  62. package/src/__tests__/pubsub.test.ts +226 -100
  63. package/src/__tests__/queues.test.ts +238 -77
  64. package/src/__tests__/r2.test.ts +55 -57
  65. package/src/__tests__/secret.test.ts +179 -130
  66. package/src/__tests__/tail.test.ts +313 -113
  67. package/src/__tests__/test-old-node-version.js +3 -3
  68. package/src/__tests__/traverse-module-graph.test.ts +220 -0
  69. package/src/__tests__/tsconfig-sanity.ts +12 -0
  70. package/src/__tests__/tsconfig.json +8 -0
  71. package/src/__tests__/tsconfig.tsbuildinfo +1 -0
  72. package/src/__tests__/type-generation.test.ts +43 -26
  73. package/src/__tests__/unstableDev.test.ts +61 -0
  74. package/src/__tests__/user.test.ts +35 -72
  75. package/src/__tests__/whoami.test.tsx +8 -114
  76. package/src/__tests__/worker-namespace.test.ts +83 -69
  77. package/src/api/dev.ts +180 -60
  78. package/src/api/index.ts +10 -1
  79. package/src/api/mtls-certificate.ts +148 -0
  80. package/src/api/pages/create-worker-bundle-contents.ts +80 -0
  81. package/src/api/pages/index.ts +5 -0
  82. package/src/api/pages/publish.tsx +371 -0
  83. package/src/bundle.ts +315 -148
  84. package/src/cfetch/index.ts +0 -2
  85. package/src/cfetch/internal.ts +33 -33
  86. package/src/cli.ts +4 -4
  87. package/src/config/config.ts +14 -7
  88. package/src/config/environment.ts +120 -24
  89. package/src/config/index.ts +117 -35
  90. package/src/config/validation-helpers.ts +1 -1
  91. package/src/config/validation.ts +430 -19
  92. package/src/constellation/createProject.tsx +51 -0
  93. package/src/constellation/deleteProject.ts +51 -0
  94. package/src/constellation/deleteProjectModel.ts +76 -0
  95. package/src/constellation/index.ts +75 -0
  96. package/src/constellation/listCatalog.tsx +35 -0
  97. package/src/constellation/listModel.tsx +49 -0
  98. package/src/constellation/listProject.tsx +28 -0
  99. package/src/constellation/listRuntime.tsx +28 -0
  100. package/src/constellation/options.ts +17 -0
  101. package/src/constellation/types.ts +17 -0
  102. package/src/constellation/uploadModel.tsx +84 -0
  103. package/src/constellation/utils.ts +90 -0
  104. package/src/create-worker-preview.ts +19 -8
  105. package/src/create-worker-upload-form.ts +81 -18
  106. package/src/d1/backups.tsx +47 -46
  107. package/src/d1/constants.ts +5 -0
  108. package/src/d1/create.tsx +85 -37
  109. package/src/d1/{delete.tsx → delete.ts} +12 -15
  110. package/src/d1/execute.tsx +228 -156
  111. package/src/d1/index.ts +31 -7
  112. package/src/d1/list.tsx +27 -13
  113. package/src/d1/migrations/apply.tsx +235 -0
  114. package/src/d1/migrations/create.tsx +79 -0
  115. package/src/d1/migrations/helpers.ts +171 -0
  116. package/src/d1/migrations/index.ts +3 -0
  117. package/src/d1/migrations/list.tsx +95 -0
  118. package/src/d1/migrations/options.ts +23 -0
  119. package/src/d1/options.ts +12 -2
  120. package/src/d1/splitter.ts +161 -0
  121. package/src/d1/types.ts +32 -0
  122. package/src/d1/utils.ts +12 -2
  123. package/src/delete.ts +8 -16
  124. package/src/deployments.ts +340 -46
  125. package/src/deprecated/index.ts +8 -302
  126. package/src/dev/dev.tsx +66 -46
  127. package/src/dev/local.tsx +339 -104
  128. package/src/dev/remote.tsx +36 -3
  129. package/src/dev/start-server.ts +146 -101
  130. package/src/dev/use-esbuild.ts +82 -56
  131. package/src/dev/validate-dev-props.ts +9 -0
  132. package/src/{dev-registry.tsx → dev-registry.ts} +13 -1
  133. package/src/dev.tsx +202 -125
  134. package/src/dialogs.ts +136 -0
  135. package/src/dispatch-namespace.ts +21 -17
  136. package/src/docs/helpers.ts +50 -0
  137. package/src/docs/index.ts +54 -0
  138. package/src/entry.ts +26 -11
  139. package/src/environment-variables/factory.ts +89 -0
  140. package/src/environment-variables/misc-variables.ts +30 -0
  141. package/src/generate/index.ts +303 -0
  142. package/src/git-client.ts +15 -4
  143. package/src/{index.tsx → index.ts} +148 -63
  144. package/src/init.ts +301 -197
  145. package/src/inspect.ts +122 -6
  146. package/src/jest.d.ts +4 -0
  147. package/src/kv/helpers.ts +7 -4
  148. package/src/kv/index.ts +15 -17
  149. package/src/logger.ts +36 -5
  150. package/src/metrics/metrics-config.ts +3 -3
  151. package/src/metrics/send-event.ts +6 -1
  152. package/src/miniflare-cli/assets.ts +64 -28
  153. package/src/miniflare-cli/index.ts +6 -9
  154. package/src/miniflare-cli/tsconfig.json +9 -0
  155. package/src/miniflare-cli/tsconfig.tsbuildinfo +1 -0
  156. package/src/miniflare-cli/types.ts +11 -0
  157. package/src/module-collection.ts +92 -26
  158. package/src/mtls-certificate/cli.ts +155 -0
  159. package/src/pages/build.ts +423 -0
  160. package/src/pages/buildFunctions.ts +140 -0
  161. package/src/pages/deployment-tails.ts +281 -0
  162. package/src/pages/deployments.tsx +11 -33
  163. package/src/pages/{dev.tsx → dev.ts} +192 -126
  164. package/src/pages/errors.ts +1 -0
  165. package/src/pages/functions/buildPlugin.ts +73 -69
  166. package/src/pages/functions/buildWorker.ts +342 -117
  167. package/src/pages/functions/tsconfig.json +8 -0
  168. package/src/pages/functions/tsconfig.tsbuildinfo +1 -0
  169. package/src/pages/{functions.tsx → functions.ts} +8 -4
  170. package/src/pages/{index.tsx → index.ts} +11 -3
  171. package/src/pages/projects.tsx +12 -22
  172. package/src/pages/prompt-select-project.tsx +31 -0
  173. package/src/pages/publish.tsx +45 -214
  174. package/src/pages/types.ts +6 -9
  175. package/src/pages/upload.tsx +58 -14
  176. package/src/pages/utils.ts +14 -3
  177. package/src/parse.ts +23 -1
  178. package/src/paths.ts +1 -1
  179. package/src/proxy.ts +5 -0
  180. package/src/publish/index.ts +20 -16
  181. package/src/publish/publish.ts +81 -48
  182. package/src/pubsub/{pubsub-commands.tsx → pubsub-commands.ts} +19 -20
  183. package/src/queues/cli/commands/consumer/add.ts +24 -24
  184. package/src/queues/cli/commands/consumer/index.ts +3 -6
  185. package/src/queues/cli/commands/consumer/remove.ts +11 -18
  186. package/src/queues/cli/commands/create.ts +8 -8
  187. package/src/queues/cli/commands/delete.ts +8 -8
  188. package/src/queues/cli/commands/index.ts +6 -4
  189. package/src/queues/cli/commands/list.ts +8 -8
  190. package/src/queues/client.ts +1 -0
  191. package/src/queues/utils.ts +18 -0
  192. package/src/r2/constants.ts +4 -0
  193. package/src/r2/helpers.ts +1 -0
  194. package/src/r2/index.ts +40 -28
  195. package/src/secret/index.ts +18 -17
  196. package/src/{sites.tsx → sites.ts} +165 -53
  197. package/src/tail/createTail.ts +103 -3
  198. package/src/tail/filters.ts +1 -1
  199. package/src/tail/index.ts +8 -11
  200. package/src/tail/printing.ts +27 -0
  201. package/src/traverse-module-graph.ts +54 -0
  202. package/src/tsconfig-sanity.ts +16 -0
  203. package/src/type-generation.ts +85 -57
  204. package/src/user/access.ts +0 -1
  205. package/src/user/auth-variables.ts +113 -0
  206. package/src/user/choose-account.tsx +1 -31
  207. package/src/user/index.ts +0 -1
  208. package/src/user/{user.tsx → user.ts} +124 -82
  209. package/src/utils/render.ts +93 -0
  210. package/src/{whoami.tsx → whoami.ts} +37 -71
  211. package/src/worker.ts +61 -3
  212. package/src/yargs-types.ts +22 -5
  213. package/templates/__tests__/tsconfig-sanity.ts +12 -0
  214. package/templates/__tests__/tsconfig.json +8 -0
  215. package/templates/__tests__/tsconfig.tsbuildinfo +1 -0
  216. package/templates/checked-fetch.js +9 -3
  217. package/templates/d1-beta-facade.js +95 -25
  218. package/templates/facade.d.ts +14 -0
  219. package/templates/first-party-worker-module-facade.ts +4 -3
  220. package/templates/format-dev-errors.ts +8 -6
  221. package/templates/gitignore +1 -0
  222. package/templates/init-tests/test-jest-new-worker.js +3 -5
  223. package/templates/init-tests/test-vitest-new-worker.js +3 -5
  224. package/templates/init-tests/test-vitest-new-worker.ts +25 -0
  225. package/templates/middleware/loader-modules.ts +2 -2
  226. package/templates/middleware/loader-sw.ts +51 -31
  227. package/templates/middleware/middleware-miniflare3-json-error.ts +32 -0
  228. package/templates/new-worker.ts +3 -0
  229. package/templates/pages-dev-pipeline.ts +4 -1
  230. package/templates/pages-dev-util.ts +4 -1
  231. package/templates/pages-shim.ts +4 -1
  232. package/templates/pages-template-plugin.ts +19 -10
  233. package/templates/serve-static-assets.ts +17 -14
  234. package/templates/service-bindings-module-facade.js +1 -0
  235. package/templates/tsconfig-sanity.ts +11 -0
  236. package/templates/tsconfig.init.json +105 -0
  237. package/templates/tsconfig.json +5 -103
  238. package/templates/tsconfig.tsbuildinfo +1 -0
  239. package/wrangler-dist/cli.d.ts +902 -77
  240. package/wrangler-dist/cli.js +131685 -140615
  241. package/wrangler-dist/wasm-sync.wasm +0 -0
  242. package/src/__tests__/api-devregistry.test.js +0 -57
  243. package/src/__tests__/d1.test.ts +0 -72
  244. package/src/__tests__/dialogs.test.tsx +0 -40
  245. package/src/__tests__/helpers/mock-cfetch.ts +0 -333
  246. package/src/__tests__/pages.test.ts +0 -2223
  247. package/src/d1/types.tsx +0 -14
  248. package/src/dialogs.tsx +0 -163
  249. package/src/environment-variables.ts +0 -50
  250. package/src/pages/build.tsx +0 -238
  251. package/src/user/env-vars.ts +0 -46
  252. package/templates/experimental-local-cache-stubs.js +0 -27
  253. /package/src/{bundle-reporter.tsx → bundle-reporter.ts} +0 -0
  254. /package/src/dev/{get-local-persistence-path.tsx → get-local-persistence-path.ts} +0 -0
  255. /package/src/pages/{hash.tsx → hash.ts} +0 -0
@@ -0,0 +1,148 @@
1
+ import { fetchResult } from "../cfetch";
2
+ import { readFileSync } from "../parse";
3
+
4
+ /**
5
+ * the representation of an mTLS certificate in the account certificate store
6
+ */
7
+ export interface MTlsCertificateResponse {
8
+ id: string;
9
+ name?: string;
10
+ ca: boolean;
11
+ certificates: string;
12
+ expires_on: string;
13
+ issuer: string;
14
+ serial_number: string;
15
+ signature: string;
16
+ uploaded_on: string;
17
+ }
18
+
19
+ /**
20
+ * details for uploading an mTLS certificate from disk
21
+ */
22
+ export interface MTlsCertificateUploadDetails {
23
+ certificateChainFilename: string;
24
+ privateKeyFilename: string;
25
+ name?: string;
26
+ }
27
+
28
+ /**
29
+ * details for uploading an mTLS certificate via the ssl api
30
+ */
31
+ export interface MTlsCertificateBody {
32
+ certificateChain: string;
33
+ privateKey: string;
34
+ name?: string;
35
+ }
36
+
37
+ /**
38
+ * supported filters for listing mTLS certificates via the ssl api
39
+ */
40
+ export interface MTlsCertificateListFilter {
41
+ name?: string;
42
+ }
43
+
44
+ /**
45
+ * indicates that looking up a certificate by name failed due to zero matching results
46
+ */
47
+ export class ErrorMTlsCertificateNameNotFound extends Error {}
48
+
49
+ /**
50
+ * indicates that looking up a certificate by name failed due to more than one matching results
51
+ */
52
+ export class ErrorMTlsCertificateManyNamesMatch extends Error {}
53
+
54
+ /**
55
+ * reads an mTLS certificate and private key pair from disk and uploads it to the account mTLS certificate store
56
+ */
57
+ export async function uploadMTlsCertificateFromFs(
58
+ accountId: string,
59
+ details: MTlsCertificateUploadDetails
60
+ ): Promise<MTlsCertificateResponse> {
61
+ return await uploadMTlsCertificate(accountId, {
62
+ certificateChain: readFileSync(details.certificateChainFilename),
63
+ privateKey: readFileSync(details.privateKeyFilename),
64
+ name: details.name,
65
+ });
66
+ }
67
+
68
+ /**
69
+ * uploads an mTLS certificate and private key pair to the account mTLS certificate store
70
+ */
71
+ export async function uploadMTlsCertificate(
72
+ accountId: string,
73
+ body: MTlsCertificateBody
74
+ ): Promise<MTlsCertificateResponse> {
75
+ return await fetchResult(`/accounts/${accountId}/mtls_certificates`, {
76
+ method: "POST",
77
+ body: JSON.stringify({
78
+ name: body.name,
79
+ certificates: body.certificateChain,
80
+ private_key: body.privateKey,
81
+ ca: false,
82
+ }),
83
+ });
84
+ }
85
+
86
+ /**
87
+ * fetches an mTLS certificate from the account mTLS certificate store by ID
88
+ */
89
+ export async function getMTlsCertificate(
90
+ accountId: string,
91
+ id: string
92
+ ): Promise<MTlsCertificateResponse> {
93
+ return await fetchResult(
94
+ `/accounts/${accountId}/mtls_certificates/${id}`,
95
+ {}
96
+ );
97
+ }
98
+
99
+ /**
100
+ * lists mTLS certificates for an account. filtering by name is supported
101
+ */
102
+ export async function listMTlsCertificates(
103
+ accountId: string,
104
+ filter: MTlsCertificateListFilter
105
+ ): Promise<MTlsCertificateResponse[]> {
106
+ const params = new URLSearchParams();
107
+ params.append("ca", "false");
108
+ if (filter.name) {
109
+ params.append("name", filter.name);
110
+ }
111
+ return await fetchResult(
112
+ `/accounts/${accountId}/mtls_certificates`,
113
+ {},
114
+ params
115
+ );
116
+ }
117
+
118
+ /**
119
+ * fetches an mTLS certificate from the account mTLS certificate store by name. will throw an error if no certificates are found, or multiple are found with that name
120
+ */
121
+ export async function getMTlsCertificateByName(
122
+ accountId: string,
123
+ name: string
124
+ ): Promise<MTlsCertificateResponse> {
125
+ const certificates = await listMTlsCertificates(accountId, { name });
126
+ if (certificates.length === 0) {
127
+ throw new ErrorMTlsCertificateNameNotFound(
128
+ `certificate not found with name "${name}"`
129
+ );
130
+ }
131
+ if (certificates.length > 1) {
132
+ throw new ErrorMTlsCertificateManyNamesMatch(
133
+ `multiple certificates found with name "${name}"`
134
+ );
135
+ }
136
+ const certificate = certificates[0];
137
+ return certificate;
138
+ }
139
+
140
+ export async function deleteMTlsCertificate(
141
+ accountId: string,
142
+ certificateId: string
143
+ ) {
144
+ return await fetchResult(
145
+ `/accounts/${accountId}/mtls_certificates/${certificateId}`,
146
+ { method: "DELETE" }
147
+ );
148
+ }
@@ -0,0 +1,80 @@
1
+ import { readFileSync } from "node:fs";
2
+ import path from "node:path";
3
+ import { Response } from "undici";
4
+ import { createWorkerUploadForm } from "../../create-worker-upload-form";
5
+ import type { BundleResult } from "../../bundle";
6
+ import type { CfWorkerInit } from "../../worker";
7
+ import type { Blob } from "node:buffer";
8
+ import type { FormData } from "undici";
9
+
10
+ /**
11
+ * Takes a Worker bundle - `BundleResult` - and generates the _worker.bundle
12
+ * contents
13
+ */
14
+ export async function createUploadWorkerBundleContents(
15
+ workerBundle: BundleResult
16
+ ): Promise<Blob> {
17
+ const workerBundleFormData = createWorkerBundleFormData(workerBundle);
18
+ const metadata = JSON.parse(workerBundleFormData.get("metadata") as string);
19
+
20
+ /**
21
+ * Pages doesn't need the metadata bindings returned by
22
+ * `createWorkerBundleFormData`. Let's strip them out and return only
23
+ * the contents we need
24
+ */
25
+ workerBundleFormData.set(
26
+ "metadata",
27
+ JSON.stringify({ main_module: metadata.main_module })
28
+ );
29
+
30
+ return await new Response(workerBundleFormData).blob();
31
+ }
32
+
33
+ /**
34
+ * Creates a `FormData` upload from a `BundleResult`
35
+ */
36
+ function createWorkerBundleFormData(workerBundle: BundleResult): FormData {
37
+ const mainModule = {
38
+ name: path.basename(workerBundle.resolvedEntryPointPath),
39
+ content: readFileSync(workerBundle.resolvedEntryPointPath, {
40
+ encoding: "utf-8",
41
+ }),
42
+ type: workerBundle.bundleType || "esm",
43
+ };
44
+
45
+ const worker: CfWorkerInit = {
46
+ name: mainModule.name,
47
+ main: mainModule,
48
+ modules: workerBundle.modules,
49
+ bindings: {
50
+ vars: undefined,
51
+ kv_namespaces: undefined,
52
+ send_email: undefined,
53
+ wasm_modules: undefined,
54
+ text_blobs: undefined,
55
+ browser: undefined,
56
+ data_blobs: undefined,
57
+ durable_objects: undefined,
58
+ queues: undefined,
59
+ r2_buckets: undefined,
60
+ d1_databases: undefined,
61
+ constellation: undefined,
62
+ services: undefined,
63
+ analytics_engine_datasets: undefined,
64
+ dispatch_namespaces: undefined,
65
+ mtls_certificates: undefined,
66
+ logfwdr: undefined,
67
+ unsafe: undefined,
68
+ },
69
+ migrations: undefined,
70
+ compatibility_date: undefined,
71
+ compatibility_flags: undefined,
72
+ usage_model: undefined,
73
+ keepVars: undefined,
74
+ logpush: undefined,
75
+ placement: undefined,
76
+ tail_consumers: undefined,
77
+ };
78
+
79
+ return createWorkerUploadForm(worker);
80
+ }
@@ -0,0 +1,5 @@
1
+ import { publish } from "./publish";
2
+
3
+ export const unstable_pages = {
4
+ publish,
5
+ };
@@ -0,0 +1,371 @@
1
+ import { existsSync, lstatSync, readFileSync } from "node:fs";
2
+ import { tmpdir } from "node:os";
3
+ import { join, resolve as resolvePath } from "node:path";
4
+ import { cwd } from "node:process";
5
+ import { File, FormData } from "undici";
6
+ import { fetchResult } from "../../cfetch";
7
+ import { FatalError } from "../../errors";
8
+ import { logger } from "../../logger";
9
+ import { buildFunctions } from "../../pages/buildFunctions";
10
+ import {
11
+ FunctionsNoRoutesError,
12
+ getFunctionsNoRoutesWarning,
13
+ } from "../../pages/errors";
14
+ import {
15
+ buildRawWorker,
16
+ checkRawWorker,
17
+ traverseAndBuildWorkerJSDirectory,
18
+ } from "../../pages/functions/buildWorker";
19
+ import { validateRoutes } from "../../pages/functions/routes-validation";
20
+ import { upload } from "../../pages/upload";
21
+ import { createUploadWorkerBundleContents } from "./create-worker-bundle-contents";
22
+ import type { BundleResult } from "../../bundle";
23
+ import type { Project, Deployment } from "@cloudflare/types";
24
+
25
+ interface PagesPublishOptions {
26
+ /**
27
+ * Path to static assets to publish to Pages
28
+ */
29
+ directory: string;
30
+ /**
31
+ * The Cloudflare Account ID that owns the project that's
32
+ * being published
33
+ */
34
+ accountId: string;
35
+ /**
36
+ * The name of the project to be published
37
+ */
38
+ projectName: string;
39
+ /**
40
+ * Branch name to use. Defaults to production branch
41
+ */
42
+ branch?: string;
43
+ /**
44
+ * Whether or not to skip local file upload result caching
45
+ */
46
+ skipCaching?: boolean;
47
+ /**
48
+ * Commit message associated to deployment
49
+ */
50
+ commitMessage?: string;
51
+ /**
52
+ * Commit hash associated to deployment
53
+ */
54
+ commitHash?: string;
55
+ /**
56
+ * Whether or not the deployment should be considered to be
57
+ * in a dirty commit state
58
+ */
59
+ commitDirty?: boolean;
60
+ /**
61
+ * Path to the project's functions directory. Default uses
62
+ * the current working directory + /functions since this is
63
+ * typically called in a CLI
64
+ */
65
+ functionsDirectory?: string;
66
+
67
+ /**
68
+ * Whether to run bundling on `_worker.js` before deploying.
69
+ * Default: true
70
+ */
71
+ bundle?: boolean;
72
+
73
+ // TODO: Allow passing in the API key and plumb it through
74
+ // to the API calls so that the publish function does not
75
+ // rely on the `CLOUDFLARE_API_KEY` environment variable
76
+ }
77
+
78
+ /**
79
+ * Publish a directory to an account/project.
80
+ * NOTE: You will need the `CLOUDFLARE_API_KEY` environment
81
+ * variable set
82
+ */
83
+ export async function publish({
84
+ directory,
85
+ accountId,
86
+ projectName,
87
+ branch,
88
+ skipCaching,
89
+ commitMessage,
90
+ commitHash,
91
+ commitDirty,
92
+ functionsDirectory: customFunctionsDirectory,
93
+ bundle,
94
+ }: PagesPublishOptions) {
95
+ let _headers: string | undefined,
96
+ _redirects: string | undefined,
97
+ _routesGenerated: string | undefined,
98
+ _routesCustom: string | undefined,
99
+ _workerJSIsDirectory = false,
100
+ _workerJS: string | undefined;
101
+
102
+ bundle = bundle ?? true;
103
+
104
+ const _workerPath = resolvePath(directory, "_worker.js");
105
+
106
+ try {
107
+ _headers = readFileSync(join(directory, "_headers"), "utf-8");
108
+ } catch {}
109
+
110
+ try {
111
+ _redirects = readFileSync(join(directory, "_redirects"), "utf-8");
112
+ } catch {}
113
+
114
+ try {
115
+ /**
116
+ * Developers can specify a custom _routes.json file, for projects with Pages
117
+ * Functions or projects in Advanced Mode
118
+ */
119
+ _routesCustom = readFileSync(join(directory, "_routes.json"), "utf-8");
120
+ } catch {}
121
+
122
+ try {
123
+ _workerJSIsDirectory = lstatSync(_workerPath).isDirectory();
124
+ if (!_workerJSIsDirectory) {
125
+ _workerJS = readFileSync(_workerPath, "utf-8");
126
+ }
127
+ } catch {}
128
+
129
+ // Grab the bindings from the API, we need these for shims and other such hacky inserts
130
+ const project = await fetchResult<Project>(
131
+ `/accounts/${accountId}/pages/projects/${projectName}`
132
+ );
133
+ let isProduction = true;
134
+ if (branch) {
135
+ isProduction = project.production_branch === branch;
136
+ }
137
+
138
+ const deploymentConfig =
139
+ project.deployment_configs[isProduction ? "production" : "preview"];
140
+ const nodejsCompat =
141
+ deploymentConfig.compatibility_flags?.includes("nodejs_compat");
142
+
143
+ /**
144
+ * Evaluate if this is an Advanced Mode or Pages Functions project. If Advanced Mode, we'll
145
+ * go ahead and upload `_worker.js` as is, but if Pages Functions, we need to attempt to build
146
+ * Functions first and exit if it failed
147
+ */
148
+ let builtFunctions: string | undefined = undefined;
149
+ let workerBundle: BundleResult | undefined = undefined;
150
+
151
+ const functionsDirectory =
152
+ customFunctionsDirectory || join(cwd(), "functions");
153
+ const routesOutputPath = !existsSync(join(directory, "_routes.json"))
154
+ ? join(tmpdir(), `_routes-${Math.random()}.json`)
155
+ : undefined;
156
+
157
+ // Routing configuration displayed in the Functions tab of a deployment in Dash
158
+ let filepathRoutingConfig: string | undefined;
159
+
160
+ const d1Databases = Object.keys(
161
+ project.deployment_configs[isProduction ? "production" : "preview"]
162
+ .d1_databases ?? {}
163
+ );
164
+
165
+ if (!_workerJS && existsSync(functionsDirectory)) {
166
+ const outputConfigPath = join(
167
+ tmpdir(),
168
+ `functions-filepath-routing-config-${Math.random()}.json`
169
+ );
170
+
171
+ try {
172
+ workerBundle = await buildFunctions({
173
+ outputConfigPath,
174
+ functionsDirectory,
175
+ onEnd: () => {},
176
+ buildOutputDirectory: directory,
177
+ routesOutputPath,
178
+ local: false,
179
+ d1Databases,
180
+ nodejsCompat,
181
+ });
182
+
183
+ builtFunctions = readFileSync(
184
+ workerBundle.resolvedEntryPointPath,
185
+ "utf-8"
186
+ );
187
+ filepathRoutingConfig = readFileSync(outputConfigPath, "utf-8");
188
+ } catch (e) {
189
+ if (e instanceof FunctionsNoRoutesError) {
190
+ logger.warn(
191
+ getFunctionsNoRoutesWarning(functionsDirectory, "skipping")
192
+ );
193
+ } else {
194
+ throw e;
195
+ }
196
+ }
197
+ }
198
+
199
+ const manifest = await upload({
200
+ directory,
201
+ accountId,
202
+ projectName,
203
+ skipCaching: skipCaching ?? false,
204
+ });
205
+
206
+ const formData = new FormData();
207
+
208
+ formData.append("manifest", JSON.stringify(manifest));
209
+
210
+ if (branch) {
211
+ formData.append("branch", branch);
212
+ }
213
+
214
+ if (commitMessage) {
215
+ formData.append("commit_message", commitMessage);
216
+ }
217
+
218
+ if (commitHash) {
219
+ formData.append("commit_hash", commitHash);
220
+ }
221
+
222
+ if (commitDirty !== undefined) {
223
+ formData.append("commit_dirty", commitDirty);
224
+ }
225
+
226
+ if (_headers) {
227
+ formData.append("_headers", new File([_headers], "_headers"));
228
+ logger.log(`✨ Uploading _headers`);
229
+ }
230
+
231
+ if (_redirects) {
232
+ formData.append("_redirects", new File([_redirects], "_redirects"));
233
+ logger.log(`✨ Uploading _redirects`);
234
+ }
235
+
236
+ if (filepathRoutingConfig) {
237
+ formData.append(
238
+ "functions-filepath-routing-config.json",
239
+ new File(
240
+ [filepathRoutingConfig],
241
+ "functions-filepath-routing-config.json"
242
+ )
243
+ );
244
+ }
245
+
246
+ /**
247
+ * Advanced Mode
248
+ * https://developers.cloudflare.com/pages/platform/functions/#advanced-mode
249
+ *
250
+ * When using a _worker.js file or _worker.js/ directory, the entire /functions directory is ignored
251
+ * – this includes its routing and middleware characteristics.
252
+ */
253
+ if (_workerJSIsDirectory) {
254
+ workerBundle = await traverseAndBuildWorkerJSDirectory({
255
+ workerJSDirectory: _workerPath,
256
+ buildOutputDirectory: directory,
257
+ d1Databases,
258
+ nodejsCompat,
259
+ });
260
+ } else if (_workerJS) {
261
+ if (bundle) {
262
+ const outfile = join(tmpdir(), `./bundledWorker-${Math.random()}.mjs`);
263
+ workerBundle = await buildRawWorker({
264
+ workerScriptPath: _workerPath,
265
+ outfile,
266
+ directory,
267
+ local: false,
268
+ sourcemap: true,
269
+ watch: false,
270
+ onEnd: () => {},
271
+ betaD1Shims: d1Databases,
272
+ nodejsCompat,
273
+ });
274
+ } else {
275
+ await checkRawWorker(_workerPath, () => {});
276
+ // TODO: Let users configure this in the future.
277
+ workerBundle = {
278
+ modules: [],
279
+ dependencies: {},
280
+ stop: undefined,
281
+ resolvedEntryPointPath: _workerPath,
282
+ bundleType: "esm",
283
+ };
284
+ }
285
+ }
286
+
287
+ if (_workerJS || _workerJSIsDirectory) {
288
+ const workerBundleContents = await createUploadWorkerBundleContents(
289
+ workerBundle as BundleResult
290
+ );
291
+
292
+ formData.append(
293
+ "_worker.bundle",
294
+ new File([workerBundleContents], "_worker.bundle")
295
+ );
296
+ logger.log(`✨ Uploading Worker bundle`);
297
+
298
+ if (_routesCustom) {
299
+ // user provided a custom _routes.json file
300
+ try {
301
+ const routesCustomJSON = JSON.parse(_routesCustom);
302
+ validateRoutes(routesCustomJSON, join(directory, "_routes.json"));
303
+
304
+ formData.append(
305
+ "_routes.json",
306
+ new File([_routesCustom], "_routes.json")
307
+ );
308
+ logger.log(`✨ Uploading _routes.json`);
309
+ } catch (err) {
310
+ if (err instanceof FatalError) {
311
+ throw err;
312
+ }
313
+ }
314
+ }
315
+ }
316
+
317
+ /**
318
+ * Pages Functions
319
+ * https://developers.cloudflare.com/pages/platform/functions/
320
+ */
321
+ if (builtFunctions && !_workerJS && !_workerJSIsDirectory) {
322
+ const workerBundleContents = await createUploadWorkerBundleContents(
323
+ workerBundle as BundleResult
324
+ );
325
+
326
+ formData.append(
327
+ "_worker.bundle",
328
+ new File([workerBundleContents], "_worker.bundle")
329
+ );
330
+ logger.log(`✨ Uploading Functions bundle`);
331
+
332
+ if (_routesCustom) {
333
+ // user provided a custom _routes.json file
334
+ try {
335
+ const routesCustomJSON = JSON.parse(_routesCustom);
336
+ validateRoutes(routesCustomJSON, join(directory, "_routes.json"));
337
+
338
+ formData.append(
339
+ "_routes.json",
340
+ new File([_routesCustom], "_routes.json")
341
+ );
342
+ logger.log(`✨ Uploading _routes.json`);
343
+ } catch (err) {
344
+ if (err instanceof FatalError) {
345
+ throw err;
346
+ }
347
+ }
348
+ } else if (routesOutputPath) {
349
+ // no custom _routes.json file found, so fallback to the generated one
350
+ try {
351
+ _routesGenerated = readFileSync(routesOutputPath, "utf-8");
352
+
353
+ if (_routesGenerated) {
354
+ formData.append(
355
+ "_routes.json",
356
+ new File([_routesGenerated], "_routes.json")
357
+ );
358
+ }
359
+ } catch {}
360
+ }
361
+ }
362
+
363
+ const deploymentResponse = await fetchResult<Deployment>(
364
+ `/accounts/${accountId}/pages/projects/${projectName}/deployments`,
365
+ {
366
+ method: "POST",
367
+ body: formData,
368
+ }
369
+ );
370
+ return deploymentResponse;
371
+ }