wrangler 0.0.0-e2b7352 → 0.0.0-e305903a

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 (264) hide show
  1. package/Cloudflare_CA.pem +18 -0
  2. package/README.md +51 -14
  3. package/bin/wrangler.js +154 -31
  4. package/kv-asset-handler.js +1 -0
  5. package/miniflare-dist/index.mjs +6244 -21
  6. package/package.json +185 -141
  7. package/src/__tests__/api-dev.test.ts +209 -0
  8. package/src/__tests__/api-devregistry.test.js +56 -0
  9. package/src/__tests__/api.test.ts +80 -0
  10. package/src/__tests__/config-cache-without-cache-dir.test.ts +38 -0
  11. package/src/__tests__/config-cache.test.ts +42 -0
  12. package/src/__tests__/configuration.test.ts +4122 -2810
  13. package/src/__tests__/dev.test.tsx +1341 -527
  14. package/src/__tests__/dialogs.test.tsx +40 -0
  15. package/src/__tests__/generate.test.ts +91 -0
  16. package/src/__tests__/guess-worker-format.test.ts +74 -57
  17. package/src/__tests__/helpers/cmd-shim.d.ts +6 -6
  18. package/src/__tests__/helpers/mock-account-id.ts +24 -24
  19. package/src/__tests__/helpers/mock-bin.ts +20 -20
  20. package/src/__tests__/helpers/mock-cfetch.ts +202 -93
  21. package/src/__tests__/helpers/mock-console.ts +68 -32
  22. package/src/__tests__/helpers/mock-dialogs.ts +121 -33
  23. package/src/__tests__/helpers/mock-get-zone-from-host.ts +8 -0
  24. package/src/__tests__/helpers/mock-http-server.ts +46 -0
  25. package/src/__tests__/helpers/mock-istty.ts +74 -0
  26. package/src/__tests__/helpers/mock-known-routes.ts +7 -0
  27. package/src/__tests__/helpers/mock-kv.ts +26 -26
  28. package/src/__tests__/helpers/mock-oauth-flow.ts +252 -0
  29. package/src/__tests__/helpers/mock-process.ts +39 -0
  30. package/src/__tests__/helpers/mock-stdin.ts +82 -77
  31. package/src/__tests__/helpers/mock-web-socket.ts +21 -21
  32. package/src/__tests__/helpers/msw/handlers/index.ts +13 -0
  33. package/src/__tests__/helpers/msw/handlers/namespaces.ts +104 -0
  34. package/src/__tests__/helpers/msw/handlers/oauth.ts +36 -0
  35. package/src/__tests__/helpers/msw/handlers/r2.ts +80 -0
  36. package/src/__tests__/helpers/msw/handlers/user.ts +63 -0
  37. package/src/__tests__/helpers/msw/index.ts +4 -0
  38. package/src/__tests__/helpers/run-in-tmp.ts +28 -22
  39. package/src/__tests__/helpers/run-wrangler.ts +8 -8
  40. package/src/__tests__/helpers/worker-scripts/child-wrangler.toml +1 -0
  41. package/src/__tests__/helpers/worker-scripts/hello-world-worker.js +5 -0
  42. package/src/__tests__/helpers/worker-scripts/hello-world-wrangler.toml +1 -0
  43. package/src/__tests__/helpers/worker-scripts/parent-worker.js +8 -0
  44. package/src/__tests__/helpers/worker-scripts/parent-wrangler.toml +5 -0
  45. package/src/__tests__/helpers/write-worker-source.ts +31 -0
  46. package/src/__tests__/helpers/write-wrangler-toml.ts +9 -9
  47. package/src/__tests__/https-options.test.ts +144 -108
  48. package/src/__tests__/index.test.ts +233 -967
  49. package/src/__tests__/init.test.ts +2717 -0
  50. package/src/__tests__/jest.setup.ts +151 -34
  51. package/src/__tests__/kv.test.ts +1376 -1085
  52. package/src/__tests__/logger.test.ts +141 -0
  53. package/src/__tests__/metrics.test.ts +450 -0
  54. package/src/__tests__/middleware.scheduled.test.ts +135 -0
  55. package/src/__tests__/middleware.test.ts +726 -0
  56. package/src/__tests__/package-manager.test.ts +312 -165
  57. package/src/__tests__/pages.test.ts +1990 -77
  58. package/src/__tests__/parse.test.ts +253 -245
  59. package/src/__tests__/paths.test.ts +39 -0
  60. package/src/__tests__/publish.test.ts +6989 -3411
  61. package/src/__tests__/pubsub.test.ts +367 -0
  62. package/src/__tests__/r2.test.ts +363 -190
  63. package/src/__tests__/route.test.ts +22 -22
  64. package/src/__tests__/secret.test.ts +626 -407
  65. package/src/__tests__/tail.test.ts +653 -516
  66. package/src/__tests__/test-old-node-version.js +31 -0
  67. package/src/__tests__/user.test.ts +155 -0
  68. package/src/__tests__/validate-dev-props.test.ts +56 -0
  69. package/src/__tests__/version.test.ts +35 -0
  70. package/src/__tests__/whoami.test.tsx +261 -88
  71. package/src/__tests__/worker-namespace.test.ts +319 -0
  72. package/src/abort.d.ts +3 -0
  73. package/src/api/dev.ts +206 -0
  74. package/src/api/index.ts +2 -0
  75. package/src/bundle-reporter.tsx +29 -0
  76. package/src/bundle.ts +723 -122
  77. package/src/cfetch/index.ts +83 -66
  78. package/src/cfetch/internal.ts +267 -92
  79. package/src/cli.ts +24 -14
  80. package/src/config/README.md +1 -1
  81. package/src/config/config.ts +236 -179
  82. package/src/config/diagnostics.ts +61 -61
  83. package/src/config/environment.ts +450 -316
  84. package/src/config/index.ts +259 -38
  85. package/src/config/validation-helpers.ts +385 -342
  86. package/src/config/validation.ts +1797 -1099
  87. package/src/config-cache.ts +85 -0
  88. package/src/create-worker-preview.ts +233 -124
  89. package/src/create-worker-upload-form.ts +285 -201
  90. package/src/d1/backups.tsx +212 -0
  91. package/src/d1/create.tsx +54 -0
  92. package/src/d1/delete.tsx +56 -0
  93. package/src/d1/execute.tsx +294 -0
  94. package/src/d1/formatTimeAgo.ts +14 -0
  95. package/src/d1/index.ts +75 -0
  96. package/src/d1/list.tsx +48 -0
  97. package/src/d1/options.ts +12 -0
  98. package/src/d1/types.tsx +14 -0
  99. package/src/d1/utils.ts +39 -0
  100. package/src/dev/dev-vars.ts +36 -0
  101. package/src/dev/dev.tsx +501 -328
  102. package/src/dev/get-local-persistence-path.tsx +31 -0
  103. package/src/dev/local.tsx +629 -211
  104. package/src/dev/remote.tsx +398 -188
  105. package/src/dev/start-server.ts +443 -0
  106. package/src/dev/use-esbuild.ts +175 -82
  107. package/src/dev/validate-dev-props.ts +31 -0
  108. package/src/dev-registry.tsx +190 -0
  109. package/src/dev.tsx +869 -0
  110. package/src/dialogs.tsx +138 -57
  111. package/src/dispatch-namespace.ts +206 -0
  112. package/src/durable.ts +102 -0
  113. package/src/entry.ts +260 -171
  114. package/src/environment-variables.ts +40 -23
  115. package/src/errors.ts +11 -0
  116. package/src/faye-websocket.d.ts +6 -0
  117. package/src/generate.ts +131 -0
  118. package/src/git-client.ts +42 -0
  119. package/src/global-wrangler-config-path.ts +26 -0
  120. package/src/https-options.ts +85 -80
  121. package/src/index.tsx +2295 -2315
  122. package/src/init.ts +967 -0
  123. package/src/inspect.ts +687 -557
  124. package/src/intl-polyfill.d.ts +123 -123
  125. package/src/is-ci.ts +14 -0
  126. package/src/is-interactive.ts +12 -0
  127. package/src/kv.ts +353 -242
  128. package/src/logger.ts +80 -0
  129. package/src/metrics/index.ts +5 -0
  130. package/src/metrics/metrics-config.ts +239 -0
  131. package/src/metrics/metrics-dispatcher.ts +96 -0
  132. package/src/metrics/metrics-usage-headers.ts +24 -0
  133. package/src/metrics/send-event.ts +93 -0
  134. package/src/miniflare-cli/assets.ts +215 -0
  135. package/src/miniflare-cli/index.ts +201 -30
  136. package/src/miniflare-cli/request-context.ts +40 -0
  137. package/src/module-collection.ts +213 -213
  138. package/src/open-in-browser.ts +8 -16
  139. package/src/package-manager.ts +183 -90
  140. package/src/pages/build.tsx +238 -0
  141. package/src/pages/constants.ts +16 -0
  142. package/src/pages/deployments.tsx +106 -0
  143. package/src/pages/dev.tsx +684 -0
  144. package/src/pages/errors.ts +66 -0
  145. package/src/pages/functions/buildPlugin.ts +110 -0
  146. package/src/pages/functions/buildWorker.ts +156 -0
  147. package/src/pages/functions/filepath-routing.test.ts +234 -0
  148. package/src/pages/functions/filepath-routing.ts +189 -0
  149. package/src/pages/functions/identifiers.ts +78 -0
  150. package/src/pages/functions/routes-consolidation.test.ts +250 -0
  151. package/src/pages/functions/routes-consolidation.ts +73 -0
  152. package/src/pages/functions/routes-transformation.test.ts +282 -0
  153. package/src/pages/functions/routes-transformation.ts +115 -0
  154. package/src/pages/functions/routes-validation.test.ts +403 -0
  155. package/src/pages/functions/routes-validation.ts +202 -0
  156. package/src/pages/functions/routes.ts +151 -0
  157. package/src/pages/functions.tsx +82 -0
  158. package/src/pages/hash.tsx +13 -0
  159. package/src/pages/index.tsx +94 -0
  160. package/src/pages/projects.tsx +169 -0
  161. package/src/pages/publish.tsx +419 -0
  162. package/src/pages/types.ts +49 -0
  163. package/src/pages/upload.tsx +398 -0
  164. package/src/pages/utils.ts +12 -0
  165. package/src/parse.ts +224 -121
  166. package/src/paths.ts +53 -8
  167. package/src/preview.ts +31 -0
  168. package/src/proxy.ts +436 -370
  169. package/src/publish.ts +839 -324
  170. package/src/pubsub/index.ts +286 -0
  171. package/src/pubsub/pubsub-commands.tsx +623 -0
  172. package/src/r2.ts +100 -19
  173. package/src/selfsigned.d.ts +23 -23
  174. package/src/sites.tsx +281 -189
  175. package/src/tail/filters.ts +108 -110
  176. package/src/tail/index.ts +234 -218
  177. package/src/tail/printing.ts +92 -47
  178. package/src/update-check.ts +19 -0
  179. package/src/user/choose-account.tsx +69 -0
  180. package/src/user/env-vars.ts +46 -0
  181. package/src/user/generate-auth-url.ts +33 -0
  182. package/src/user/generate-random-state.ts +16 -0
  183. package/src/user/index.ts +3 -0
  184. package/src/user/user.tsx +1192 -0
  185. package/src/whoami.tsx +142 -42
  186. package/src/worker.ts +171 -100
  187. package/src/zones.ts +167 -0
  188. package/templates/checked-fetch.js +17 -0
  189. package/templates/d1-beta-facade.js +174 -0
  190. package/templates/experimental-local-cache-stubs.js +27 -0
  191. package/templates/first-party-worker-module-facade.ts +18 -0
  192. package/templates/format-dev-errors.ts +32 -0
  193. package/templates/gitignore +171 -0
  194. package/templates/init-tests/test-jest-new-worker.js +25 -0
  195. package/templates/init-tests/test-vitest-new-worker.js +26 -0
  196. package/templates/middleware/common.ts +62 -0
  197. package/templates/middleware/loader-modules.ts +84 -0
  198. package/templates/middleware/loader-sw.ts +213 -0
  199. package/templates/middleware/middleware-pretty-error.ts +40 -0
  200. package/templates/middleware/middleware-scheduled.ts +15 -0
  201. package/templates/new-worker-scheduled.js +17 -0
  202. package/templates/new-worker-scheduled.ts +32 -0
  203. package/templates/new-worker.js +3 -3
  204. package/templates/new-worker.ts +18 -3
  205. package/templates/no-op-worker.js +10 -0
  206. package/templates/pages-dev-pipeline.ts +35 -0
  207. package/templates/pages-shim.ts +9 -0
  208. package/templates/pages-template-plugin.ts +167 -0
  209. package/templates/pages-template-worker.ts +172 -0
  210. package/templates/serve-static-assets.ts +63 -0
  211. package/templates/service-bindings-module-facade.js +54 -0
  212. package/templates/service-bindings-sw-facade.js +42 -0
  213. package/templates/tsconfig.json +95 -95
  214. package/wrangler-dist/cli.d.ts +841 -0
  215. package/wrangler-dist/cli.js +129492 -108210
  216. package/miniflare-dist/index.mjs.map +0 -7
  217. package/pages/functions/buildWorker.ts +0 -62
  218. package/pages/functions/filepath-routing.test.ts +0 -223
  219. package/pages/functions/filepath-routing.ts +0 -181
  220. package/pages/functions/identifiers.ts +0 -78
  221. package/pages/functions/routes.ts +0 -151
  222. package/pages/functions/template-worker.ts +0 -130
  223. package/src/__tests__/helpers/faye-websocket.d.ts +0 -6
  224. package/src/__tests__/logout.test.ts +0 -52
  225. package/src/__tests__/sentry.test.ts +0 -146
  226. package/src/miniflare-cli/enum-keys.ts +0 -17
  227. package/src/pages.tsx +0 -1061
  228. package/src/reporting.ts +0 -131
  229. package/src/user.tsx +0 -1127
  230. package/templates/static-asset-facade.js +0 -47
  231. package/vendor/@cloudflare/kv-asset-handler/CHANGELOG.md +0 -332
  232. package/vendor/@cloudflare/kv-asset-handler/LICENSE_APACHE +0 -176
  233. package/vendor/@cloudflare/kv-asset-handler/LICENSE_MIT +0 -25
  234. package/vendor/@cloudflare/kv-asset-handler/README.md +0 -245
  235. package/vendor/@cloudflare/kv-asset-handler/dist/index.d.ts +0 -32
  236. package/vendor/@cloudflare/kv-asset-handler/dist/index.js +0 -354
  237. package/vendor/@cloudflare/kv-asset-handler/dist/mocks.d.ts +0 -13
  238. package/vendor/@cloudflare/kv-asset-handler/dist/mocks.js +0 -148
  239. package/vendor/@cloudflare/kv-asset-handler/dist/test/getAssetFromKV.d.ts +0 -1
  240. package/vendor/@cloudflare/kv-asset-handler/dist/test/getAssetFromKV.js +0 -436
  241. package/vendor/@cloudflare/kv-asset-handler/dist/test/mapRequestToAsset.d.ts +0 -1
  242. package/vendor/@cloudflare/kv-asset-handler/dist/test/mapRequestToAsset.js +0 -40
  243. package/vendor/@cloudflare/kv-asset-handler/dist/test/serveSinglePageApp.d.ts +0 -1
  244. package/vendor/@cloudflare/kv-asset-handler/dist/test/serveSinglePageApp.js +0 -42
  245. package/vendor/@cloudflare/kv-asset-handler/dist/types.d.ts +0 -26
  246. package/vendor/@cloudflare/kv-asset-handler/dist/types.js +0 -31
  247. package/vendor/@cloudflare/kv-asset-handler/package.json +0 -52
  248. package/vendor/@cloudflare/kv-asset-handler/src/index.ts +0 -296
  249. package/vendor/@cloudflare/kv-asset-handler/src/mocks.ts +0 -136
  250. package/vendor/@cloudflare/kv-asset-handler/src/test/getAssetFromKV.ts +0 -464
  251. package/vendor/@cloudflare/kv-asset-handler/src/test/mapRequestToAsset.ts +0 -33
  252. package/vendor/@cloudflare/kv-asset-handler/src/test/serveSinglePageApp.ts +0 -42
  253. package/vendor/@cloudflare/kv-asset-handler/src/types.ts +0 -39
  254. package/vendor/wrangler-mime/CHANGELOG.md +0 -289
  255. package/vendor/wrangler-mime/LICENSE +0 -21
  256. package/vendor/wrangler-mime/Mime.js +0 -97
  257. package/vendor/wrangler-mime/README.md +0 -187
  258. package/vendor/wrangler-mime/cli.js +0 -46
  259. package/vendor/wrangler-mime/index.js +0 -4
  260. package/vendor/wrangler-mime/lite.js +0 -4
  261. package/vendor/wrangler-mime/package.json +0 -52
  262. package/vendor/wrangler-mime/types/other.js +0 -1
  263. package/vendor/wrangler-mime/types/standard.js +0 -1
  264. package/wrangler-dist/cli.js.map +0 -7
@@ -9,22 +9,24 @@ import type { Environment, RawEnvironment } from "./environment";
9
9
  * The `fieldPath` is a dot separated property path, e.g. `"build.upload.format"`.
10
10
  */
11
11
  export function deprecated<T extends object>(
12
- diagnostics: Diagnostics,
13
- config: T,
14
- fieldPath: DeepKeyOf<T>,
15
- message: string,
16
- remove: boolean,
17
- breaking = false
12
+ diagnostics: Diagnostics,
13
+ config: T,
14
+ fieldPath: DeepKeyOf<T>,
15
+ message: string,
16
+ remove: boolean,
17
+ title = "Deprecation",
18
+ type: "warning" | "error" = "warning"
18
19
  ): void {
19
- const result = unwindPropertyPath(config, fieldPath);
20
- if (result !== undefined && result.field in result.container) {
21
- (breaking ? diagnostics.errors : diagnostics.warnings).push(
22
- `DEPRECATION: "${fieldPath}":\n${message}`
23
- );
24
- if (remove) {
25
- delete (result.container as Record<string, unknown>)[result.field];
26
- }
27
- }
20
+ const BOLD = "\x1b[1m";
21
+ const NORMAL = "\x1b[0m";
22
+ const diagnosticMessage = `${BOLD}${title}${NORMAL}: "${fieldPath}":\n${message}`;
23
+ const result = unwindPropertyPath(config, fieldPath);
24
+ if (result !== undefined && result.field in result.container) {
25
+ diagnostics[`${type}s`].push(diagnosticMessage);
26
+ if (remove) {
27
+ delete (result.container as Record<string, unknown>)[result.field];
28
+ }
29
+ }
28
30
  }
29
31
 
30
32
  /**
@@ -34,16 +36,16 @@ export function deprecated<T extends object>(
34
36
  * The `fieldPath` is a dot separated property path, e.g. `"build.upload.format"`.
35
37
  */
36
38
  export function experimental<T extends object>(
37
- diagnostics: Diagnostics,
38
- config: T,
39
- fieldPath: DeepKeyOf<T>
39
+ diagnostics: Diagnostics,
40
+ config: T,
41
+ fieldPath: DeepKeyOf<T>
40
42
  ): void {
41
- const result = unwindPropertyPath(config, fieldPath);
42
- if (result !== undefined && result.field in result.container) {
43
- diagnostics.warnings.push(
44
- `"${fieldPath}" fields are experimental and may change or break at any time.`
45
- );
46
- }
43
+ const result = unwindPropertyPath(config, fieldPath);
44
+ if (result !== undefined && result.field in result.container) {
45
+ diagnostics.warnings.push(
46
+ `"${fieldPath}" fields are experimental and may change or break at any time.`
47
+ );
48
+ }
47
49
  }
48
50
 
49
51
  /**
@@ -53,51 +55,51 @@ export function experimental<T extends object>(
53
55
  * and then the `defaultValue`.
54
56
  */
55
57
  export function inheritable<K extends keyof Environment>(
56
- diagnostics: Diagnostics,
57
- topLevelEnv: Environment | undefined,
58
- rawEnv: RawEnvironment,
59
- field: K,
60
- validate: ValidatorFn,
61
- defaultValue: Environment[K],
62
- transformFn: TransformFn<Environment[K]> = (v) => v
58
+ diagnostics: Diagnostics,
59
+ topLevelEnv: Environment | undefined,
60
+ rawEnv: RawEnvironment,
61
+ field: K,
62
+ validate: ValidatorFn,
63
+ defaultValue: Environment[K],
64
+ transformFn: TransformFn<Environment[K]> = (v) => v
63
65
  ): Environment[K] {
64
- validate(diagnostics, field, rawEnv[field], topLevelEnv);
65
- return (
66
- (rawEnv[field] as Environment[K]) ??
67
- transformFn(topLevelEnv?.[field]) ??
68
- defaultValue
69
- );
66
+ validate(diagnostics, field, rawEnv[field], topLevelEnv);
67
+ return (
68
+ (rawEnv[field] as Environment[K]) ??
69
+ transformFn(topLevelEnv?.[field]) ??
70
+ defaultValue
71
+ );
70
72
  }
71
73
 
72
74
  /**
73
75
  * Get an inheritable environment field, but only if we are in legacy environments
74
76
  */
75
77
  export function inheritableInLegacyEnvironments<K extends keyof Environment>(
76
- diagnostics: Diagnostics,
77
- isLegacyEnv: boolean | undefined,
78
- topLevelEnv: Environment | undefined,
79
- rawEnv: RawEnvironment,
80
- field: K,
81
- validate: ValidatorFn,
82
- transformFn: TransformFn<Environment[K]>,
83
- defaultValue: Environment[K]
78
+ diagnostics: Diagnostics,
79
+ isLegacyEnv: boolean | undefined,
80
+ topLevelEnv: Environment | undefined,
81
+ rawEnv: RawEnvironment,
82
+ field: K,
83
+ validate: ValidatorFn,
84
+ transformFn: TransformFn<Environment[K]> = (v) => v,
85
+ defaultValue: Environment[K]
84
86
  ): Environment[K] {
85
- return topLevelEnv === undefined || isLegacyEnv === true
86
- ? inheritable(
87
- diagnostics,
88
- topLevelEnv,
89
- rawEnv,
90
- field,
91
- validate,
92
- defaultValue,
93
- transformFn
94
- )
95
- : notAllowedInNamedServiceEnvironment(
96
- diagnostics,
97
- topLevelEnv,
98
- rawEnv,
99
- field
100
- );
87
+ return topLevelEnv === undefined || isLegacyEnv === true
88
+ ? inheritable(
89
+ diagnostics,
90
+ topLevelEnv,
91
+ rawEnv,
92
+ field,
93
+ validate,
94
+ defaultValue,
95
+ transformFn
96
+ )
97
+ : notAllowedInNamedServiceEnvironment(
98
+ diagnostics,
99
+ topLevelEnv,
100
+ rawEnv,
101
+ field
102
+ );
101
103
  }
102
104
 
103
105
  /**
@@ -109,27 +111,27 @@ type TransformFn<T> = (fieldValue: T | undefined) => T | undefined;
109
111
  * Transform an environment field by appending current environment name to it.
110
112
  */
111
113
  export const appendEnvName =
112
- (envName: string): TransformFn<string | undefined> =>
113
- (fieldValue) =>
114
- fieldValue ? `${fieldValue}-${envName}` : undefined;
114
+ (envName: string): TransformFn<string | undefined> =>
115
+ (fieldValue) =>
116
+ fieldValue ? `${fieldValue}-${envName}` : undefined;
115
117
 
116
118
  /**
117
119
  * Log an error if this named environment is trying to override the value in the top-level
118
120
  * environment, which is not allow for this field.
119
121
  */
120
122
  function notAllowedInNamedServiceEnvironment<K extends keyof Environment>(
121
- diagnostics: Diagnostics,
122
- topLevelEnv: Environment,
123
- rawEnv: RawEnvironment,
124
- field: K
123
+ diagnostics: Diagnostics,
124
+ topLevelEnv: Environment,
125
+ rawEnv: RawEnvironment,
126
+ field: K
125
127
  ): Environment[K] {
126
- if (field in rawEnv) {
127
- diagnostics.errors.push(
128
- `The "${field}" field is not allowed in named service environments.\n` +
129
- `Please remove the field from this environment.`
130
- );
131
- }
132
- return topLevelEnv[field];
128
+ if (field in rawEnv) {
129
+ diagnostics.errors.push(
130
+ `The "${field}" field is not allowed in named service environments.\n` +
131
+ `Please remove the field from this environment.`
132
+ );
133
+ }
134
+ return topLevelEnv[field];
133
135
  }
134
136
 
135
137
  /**
@@ -139,39 +141,39 @@ function notAllowedInNamedServiceEnvironment<K extends keyof Environment>(
139
141
  * then log a warning and return the `defaultValue`.
140
142
  */
141
143
  export function notInheritable<K extends keyof Environment>(
142
- diagnostics: Diagnostics,
143
- topLevelEnv: Environment | undefined,
144
- rawConfig: RawConfig | undefined,
145
- rawEnv: RawEnvironment,
146
- envName: string,
147
- field: K,
148
- validate: ValidatorFn,
149
- defaultValue: Environment[K]
144
+ diagnostics: Diagnostics,
145
+ topLevelEnv: Environment | undefined,
146
+ rawConfig: RawConfig | undefined,
147
+ rawEnv: RawEnvironment,
148
+ envName: string,
149
+ field: K,
150
+ validate: ValidatorFn,
151
+ defaultValue: Environment[K]
150
152
  ): Environment[K] {
151
- if (rawEnv[field] !== undefined) {
152
- validate(diagnostics, field, rawEnv[field], topLevelEnv);
153
- } else {
154
- if (rawConfig?.[field] !== undefined) {
155
- diagnostics.warnings.push(
156
- `"${field}" exists at the top level, but not on "env.${envName}".\n` +
157
- `This is not what you probably want, since "${field}" is not inherited by environments.\n` +
158
- `Please add "${field}" to "env.${envName}".`
159
- );
160
- }
161
- }
162
- return (rawEnv[field] as Environment[K]) ?? defaultValue;
153
+ if (rawEnv[field] !== undefined) {
154
+ validate(diagnostics, field, rawEnv[field], topLevelEnv);
155
+ } else {
156
+ if (rawConfig?.[field] !== undefined) {
157
+ diagnostics.warnings.push(
158
+ `"${field}" exists at the top level, but not on "env.${envName}".\n` +
159
+ `This is not what you probably want, since "${field}" is not inherited by environments.\n` +
160
+ `Please add "${field}" to "env.${envName}".`
161
+ );
162
+ }
163
+ }
164
+ return (rawEnv[field] as Environment[K]) ?? defaultValue;
163
165
  }
164
166
 
165
167
  // Idea taken from https://stackoverflow.com/a/66661477
166
168
  type DeepKeyOf<T> = (
167
- T extends object
168
- ? {
169
- [K in Exclude<keyof T, symbol>]: `${K}${DotPrefix<DeepKeyOf<T[K]>>}`;
170
- }[Exclude<keyof T, symbol>]
171
- : ""
169
+ T extends object
170
+ ? {
171
+ [K in Exclude<keyof T, symbol>]: `${K}${DotPrefix<DeepKeyOf<T[K]>>}`;
172
+ }[Exclude<keyof T, symbol>]
173
+ : ""
172
174
  ) extends infer D
173
- ? Extract<D, string>
174
- : never;
175
+ ? Extract<D, string>
176
+ : never;
175
177
 
176
178
  type DotPrefix<T extends string> = T extends "" ? "" : `.${T}`;
177
179
 
@@ -186,117 +188,145 @@ type DotPrefix<T extends string> = T extends "" ? "" : `.${T}`;
186
188
  * ```
187
189
  */
188
190
  function unwindPropertyPath<T extends object>(
189
- root: T,
190
- path: DeepKeyOf<T>
191
+ root: T,
192
+ path: DeepKeyOf<T>
191
193
  ): { container: object; field: string } | undefined {
192
- let container: object = root;
193
- const parts = (path as string).split(".");
194
- for (let i = 0; i < parts.length - 1; i++) {
195
- if (!hasProperty(container, parts[i])) {
196
- return;
197
- }
198
- container = container[parts[i]];
199
- }
200
- return { container, field: parts[parts.length - 1] };
194
+ let container: object = root;
195
+ const parts = (path as string).split(".");
196
+ for (let i = 0; i < parts.length - 1; i++) {
197
+ if (!hasProperty(container, parts[i])) {
198
+ return;
199
+ }
200
+ container = container[parts[i]];
201
+ }
202
+ return { container, field: parts[parts.length - 1] };
201
203
  }
202
204
 
203
205
  /**
204
206
  * The type of a function that can be used to validate a configuration field.
205
207
  */
206
208
  export type ValidatorFn = (
207
- diagnostics: Diagnostics,
208
- field: string,
209
- value: unknown,
210
- topLevelEnv: Environment | undefined
209
+ diagnostics: Diagnostics,
210
+ field: string,
211
+ value: unknown,
212
+ topLevelEnv: Environment | undefined
211
213
  ) => boolean;
212
214
 
213
215
  /**
214
216
  * Validate that the field is a string.
215
217
  */
216
218
  export const isString: ValidatorFn = (diagnostics, field, value) => {
217
- if (value !== undefined && typeof value !== "string") {
218
- diagnostics.errors.push(
219
- `Expected "${field}" to be of type string but got ${JSON.stringify(
220
- value
221
- )}.`
222
- );
223
- return false;
224
- }
225
- return true;
219
+ if (value !== undefined && typeof value !== "string") {
220
+ diagnostics.errors.push(
221
+ `Expected "${field}" to be of type string but got ${JSON.stringify(
222
+ value
223
+ )}.`
224
+ );
225
+ return false;
226
+ }
227
+ return true;
228
+ };
229
+
230
+ /**
231
+ * Validate that the `name` field is compliant with EWC constraints.
232
+ */
233
+ export const isValidName: ValidatorFn = (diagnostics, field, value) => {
234
+ if (
235
+ (typeof value === "string" && /^$|^[a-z0-9_ ][a-z0-9-_ ]*$/.test(value)) ||
236
+ value === undefined
237
+ ) {
238
+ return true;
239
+ } else {
240
+ diagnostics.errors.push(
241
+ `Expected "${field}" to be of type string, alphanumeric and lowercase with dashes only but got ${JSON.stringify(
242
+ value
243
+ )}.`
244
+ );
245
+ return false;
246
+ }
226
247
  };
227
248
 
228
249
  /**
229
250
  * Validate that the field is an array of strings.
230
251
  */
231
252
  export const isStringArray: ValidatorFn = (diagnostics, field, value) => {
232
- if (
233
- value !== undefined &&
234
- (!Array.isArray(value) || value.some((item) => typeof item !== "string"))
235
- ) {
236
- diagnostics.errors.push(
237
- `Expected "${field}" to be of type string array but got ${JSON.stringify(
238
- value
239
- )}.`
240
- );
241
- return false;
242
- }
243
- return true;
253
+ if (
254
+ value !== undefined &&
255
+ (!Array.isArray(value) || value.some((item) => typeof item !== "string"))
256
+ ) {
257
+ diagnostics.errors.push(
258
+ `Expected "${field}" to be of type string array but got ${JSON.stringify(
259
+ value
260
+ )}.`
261
+ );
262
+ return false;
263
+ }
264
+ return true;
244
265
  };
245
266
 
246
267
  /**
247
268
  * Validate that the field is an object containing the given properties.
248
269
  */
249
270
  export const isObjectWith =
250
- (...properties: string[]): ValidatorFn =>
251
- (diagnostics, field, value) => {
252
- if (
253
- value !== undefined &&
254
- (typeof value !== "object" ||
255
- value === null ||
256
- !properties.every((prop) => prop in value))
257
- ) {
258
- diagnostics.errors.push(
259
- `Expected "${field}" to be of type object, containing properties ${properties}, but got ${JSON.stringify(
260
- value
261
- )}.`
262
- );
263
- return false;
264
- }
265
- return true;
266
- };
271
+ (...properties: string[]): ValidatorFn =>
272
+ (diagnostics, field, value) => {
273
+ if (
274
+ value !== undefined &&
275
+ (typeof value !== "object" ||
276
+ value === null ||
277
+ !properties.every((prop) => prop in value))
278
+ ) {
279
+ diagnostics.errors.push(
280
+ `Expected "${field}" to be of type object, containing only properties ${properties}, but got ${JSON.stringify(
281
+ value
282
+ )}.`
283
+ );
284
+ return false;
285
+ }
286
+ // it's an object with the field as desired,
287
+ // but let's also check for unexpected fields
288
+ if (value !== undefined) {
289
+ const restFields = Object.keys(value).filter(
290
+ (key) => !properties.includes(key)
291
+ );
292
+ validateAdditionalProperties(diagnostics, field, restFields, []);
293
+ }
294
+
295
+ return true;
296
+ };
267
297
 
268
298
  /**
269
299
  * Validate that the field value is one of the given choices.
270
300
  */
271
301
  export const isOneOf =
272
- (...choices: unknown[]): ValidatorFn =>
273
- (diagnostics, field, value) => {
274
- if (value !== undefined && !choices.some((choice) => value === choice)) {
275
- diagnostics.errors.push(
276
- `Expected "${field}" field to be one of ${JSON.stringify(
277
- choices
278
- )} but got ${JSON.stringify(value)}.`
279
- );
280
- return false;
281
- }
282
- return true;
283
- };
302
+ (...choices: unknown[]): ValidatorFn =>
303
+ (diagnostics, field, value) => {
304
+ if (value !== undefined && !choices.some((choice) => value === choice)) {
305
+ diagnostics.errors.push(
306
+ `Expected "${field}" field to be one of ${JSON.stringify(
307
+ choices
308
+ )} but got ${JSON.stringify(value)}.`
309
+ );
310
+ return false;
311
+ }
312
+ return true;
313
+ };
284
314
 
285
315
  /**
286
316
  * Aggregate multiple validator functions
287
317
  */
288
318
  export const all = (...validations: ValidatorFn[]): ValidatorFn => {
289
- return (diagnostics, field, value, config) => {
290
- let passedValidations = true;
319
+ return (diagnostics, field, value, config) => {
320
+ let passedValidations = true;
291
321
 
292
- for (const validate of validations) {
293
- if (!validate(diagnostics, field, value, config)) {
294
- passedValidations = false;
295
- }
296
- }
322
+ for (const validate of validations) {
323
+ if (!validate(diagnostics, field, value, config)) {
324
+ passedValidations = false;
325
+ }
326
+ }
297
327
 
298
- return passedValidations;
299
- };
328
+ return passedValidations;
329
+ };
300
330
  };
301
331
 
302
332
  /**
@@ -306,201 +336,201 @@ export const all = (...validations: ValidatorFn[]): ValidatorFn => {
306
336
  * @param fields the names of the fields to check against.
307
337
  */
308
338
  export const isMutuallyExclusiveWith = <T extends RawEnvironment | RawConfig>(
309
- container: T,
310
- ...fields: (keyof T)[]
339
+ container: T,
340
+ ...fields: (keyof T)[]
311
341
  ): ValidatorFn => {
312
- return (diagnostics, field, value) => {
313
- if (value === undefined) {
314
- return true;
315
- }
316
-
317
- for (const exclusiveWith of fields) {
318
- if (container[exclusiveWith] !== undefined) {
319
- diagnostics.errors.push(
320
- `Expected exactly one of the following fields ${JSON.stringify([
321
- field,
322
- ...fields,
323
- ])}.`
324
- );
325
- return false;
326
- }
327
- }
328
-
329
- return true;
330
- };
342
+ return (diagnostics, field, value) => {
343
+ if (value === undefined) {
344
+ return true;
345
+ }
346
+
347
+ for (const exclusiveWith of fields) {
348
+ if (container[exclusiveWith] !== undefined) {
349
+ diagnostics.errors.push(
350
+ `Expected exactly one of the following fields ${JSON.stringify([
351
+ field,
352
+ ...fields,
353
+ ])}.`
354
+ );
355
+ return false;
356
+ }
357
+ }
358
+
359
+ return true;
360
+ };
331
361
  };
332
362
 
333
363
  /**
334
364
  * Validate that the field is a boolean.
335
365
  */
336
366
  export const isBoolean: ValidatorFn = (diagnostics, field, value) => {
337
- if (value !== undefined && typeof value !== "boolean") {
338
- diagnostics.errors.push(
339
- `Expected "${field}" to be of type boolean but got ${JSON.stringify(
340
- value
341
- )}.`
342
- );
343
- return false;
344
- }
345
- return true;
367
+ if (value !== undefined && typeof value !== "boolean") {
368
+ diagnostics.errors.push(
369
+ `Expected "${field}" to be of type boolean but got ${JSON.stringify(
370
+ value
371
+ )}.`
372
+ );
373
+ return false;
374
+ }
375
+ return true;
346
376
  };
347
377
 
348
378
  /**
349
379
  * Validate that the required field exists and has the expected type.
350
380
  */
351
381
  export const validateRequiredProperty = (
352
- diagnostics: Diagnostics,
353
- container: string,
354
- key: string,
355
- value: unknown,
356
- type: string,
357
- choices?: unknown[]
382
+ diagnostics: Diagnostics,
383
+ container: string,
384
+ key: string,
385
+ value: unknown,
386
+ type: TypeofType,
387
+ choices?: unknown[]
358
388
  ): boolean => {
359
- if (container) {
360
- container += ".";
361
- }
362
- if (value === undefined) {
363
- diagnostics.errors.push(`"${container}${key}" is a required field.`);
364
- return false;
365
- } else if (typeof value !== type) {
366
- diagnostics.errors.push(
367
- `Expected "${container}${key}" to be of type ${type} but got ${JSON.stringify(
368
- value
369
- )}.`
370
- );
371
- return false;
372
- } else if (choices) {
373
- if (
374
- !isOneOf(...choices)(diagnostics, `${container}${key}`, value, undefined)
375
- ) {
376
- return false;
377
- }
378
- }
379
- return true;
389
+ if (container) {
390
+ container += ".";
391
+ }
392
+ if (value === undefined) {
393
+ diagnostics.errors.push(`"${container}${key}" is a required field.`);
394
+ return false;
395
+ } else if (typeof value !== type) {
396
+ diagnostics.errors.push(
397
+ `Expected "${container}${key}" to be of type ${type} but got ${JSON.stringify(
398
+ value
399
+ )}.`
400
+ );
401
+ return false;
402
+ } else if (choices) {
403
+ if (
404
+ !isOneOf(...choices)(diagnostics, `${container}${key}`, value, undefined)
405
+ ) {
406
+ return false;
407
+ }
408
+ }
409
+ return true;
380
410
  };
381
411
 
382
412
  /**
383
413
  * Validate that, if the optional field exists, then it has the expected type.
384
414
  */
385
415
  export const validateOptionalProperty = (
386
- diagnostics: Diagnostics,
387
- container: string,
388
- key: string,
389
- value: unknown,
390
- type: string,
391
- choices?: unknown[]
416
+ diagnostics: Diagnostics,
417
+ container: string,
418
+ key: string,
419
+ value: unknown,
420
+ type: TypeofType,
421
+ choices?: unknown[]
392
422
  ): boolean => {
393
- if (value !== undefined) {
394
- return validateRequiredProperty(
395
- diagnostics,
396
- container,
397
- key,
398
- value,
399
- type,
400
- choices
401
- );
402
- }
403
- return true;
423
+ if (value !== undefined) {
424
+ return validateRequiredProperty(
425
+ diagnostics,
426
+ container,
427
+ key,
428
+ value,
429
+ type,
430
+ choices
431
+ );
432
+ }
433
+ return true;
404
434
  };
405
435
 
406
436
  /**
407
437
  * Validate that the field is an array of elements of the given type.
408
438
  */
409
439
  export const validateTypedArray = (
410
- diagnostics: Diagnostics,
411
- container: string,
412
- value: unknown,
413
- type: string
440
+ diagnostics: Diagnostics,
441
+ container: string,
442
+ value: unknown,
443
+ type: TypeofType
414
444
  ): boolean => {
415
- let isValid = true;
416
- if (!Array.isArray(value)) {
417
- diagnostics.errors.push(
418
- `Expected "${container}" to be an array of ${type}s but got ${JSON.stringify(
419
- value
420
- )}`
421
- );
422
- isValid = false;
423
- } else {
424
- for (let i = 0; i < value.length; i++) {
425
- isValid =
426
- validateRequiredProperty(
427
- diagnostics,
428
- container,
429
- `[${i}]`,
430
- value[i],
431
- type
432
- ) && isValid;
433
- }
434
- }
435
- return isValid;
445
+ let isValid = true;
446
+ if (!Array.isArray(value)) {
447
+ diagnostics.errors.push(
448
+ `Expected "${container}" to be an array of ${type}s but got ${JSON.stringify(
449
+ value
450
+ )}`
451
+ );
452
+ isValid = false;
453
+ } else {
454
+ for (let i = 0; i < value.length; i++) {
455
+ isValid =
456
+ validateRequiredProperty(
457
+ diagnostics,
458
+ container,
459
+ `[${i}]`,
460
+ value[i],
461
+ type
462
+ ) && isValid;
463
+ }
464
+ }
465
+ return isValid;
436
466
  };
437
467
 
438
468
  /**
439
469
  * Validate that, if the optional field exists, it is an array of elements of the given type.
440
470
  */
441
471
  export const validateOptionalTypedArray = (
442
- diagnostics: Diagnostics,
443
- container: string,
444
- value: unknown,
445
- type: string
472
+ diagnostics: Diagnostics,
473
+ container: string,
474
+ value: unknown,
475
+ type: TypeofType
446
476
  ) => {
447
- if (value !== undefined) {
448
- return validateTypedArray(diagnostics, container, value, type);
449
- }
450
- return true;
477
+ if (value !== undefined) {
478
+ return validateTypedArray(diagnostics, container, value, type);
479
+ }
480
+ return true;
451
481
  };
452
482
 
453
483
  /**
454
484
  * Test to see if `obj` has the required property `prop` of type `type`.
455
485
  */
456
486
  export const isRequiredProperty = <T extends object>(
457
- obj: object,
458
- prop: keyof T,
459
- type: string,
460
- choices?: unknown[]
487
+ obj: object,
488
+ prop: keyof T,
489
+ type: TypeofType,
490
+ choices?: unknown[]
461
491
  ): obj is T =>
462
- hasProperty<T>(obj, prop) &&
463
- typeof obj[prop] === type &&
464
- (choices === undefined || choices.includes(obj[prop]));
492
+ hasProperty<T>(obj, prop) &&
493
+ typeof obj[prop] === type &&
494
+ (choices === undefined || choices.includes(obj[prop]));
465
495
 
466
496
  /**
467
497
  * Test to see if `obj` has the optional property `prop` of type `type`.
468
498
  */
469
499
  export const isOptionalProperty = <T extends object>(
470
- obj: object,
471
- prop: keyof T,
472
- type: string
500
+ obj: object,
501
+ prop: keyof T,
502
+ type: TypeofType
473
503
  ): obj is T => !hasProperty<T>(obj, prop) || typeof obj[prop] === type;
474
504
 
475
505
  /**
476
506
  * Test to see if `obj` has the property `prop`.
477
507
  */
478
508
  export const hasProperty = <T extends object>(
479
- obj: object,
480
- property: keyof T
509
+ obj: object,
510
+ property: keyof T
481
511
  ): obj is T => property in obj;
482
512
 
483
513
  /**
484
514
  * Add warning messages about any properties in the given field that are not expected to be there.
485
515
  */
486
516
  export const validateAdditionalProperties = (
487
- diagnostics: Diagnostics,
488
- fieldPath: string,
489
- restProps: Iterable<string>,
490
- knownProps: Iterable<string>
517
+ diagnostics: Diagnostics,
518
+ fieldPath: string,
519
+ restProps: Iterable<string>,
520
+ knownProps: Iterable<string>
491
521
  ): boolean => {
492
- const restPropSet = new Set(restProps);
493
- for (const knownProp of knownProps) {
494
- restPropSet.delete(knownProp);
495
- }
496
- if (restPropSet.size > 0) {
497
- const fields = Array.from(restPropSet.keys()).map((field) => `"${field}"`);
498
- diagnostics.warnings.push(
499
- `Unexpected fields found in ${fieldPath} field: ${fields}`
500
- );
501
- return false;
502
- }
503
- return true;
522
+ const restPropSet = new Set(restProps);
523
+ for (const knownProp of knownProps) {
524
+ restPropSet.delete(knownProp);
525
+ }
526
+ if (restPropSet.size > 0) {
527
+ const fields = Array.from(restPropSet.keys()).map((field) => `"${field}"`);
528
+ diagnostics.warnings.push(
529
+ `Unexpected fields found in ${fieldPath} field: ${fields}`
530
+ );
531
+ return false;
532
+ }
533
+ return true;
504
534
  };
505
535
 
506
536
  /**
@@ -511,44 +541,57 @@ export const validateAdditionalProperties = (
511
541
  * performed externally.
512
542
  */
513
543
  export const getBindingNames = (value: unknown): string[] => {
514
- if (typeof value !== "object" || value === null) {
515
- return [];
516
- }
517
-
518
- if (isBindingList(value)) {
519
- return value.bindings.map(({ name }) => name);
520
- } else if (isNamespaceList(value)) {
521
- return value.map(({ binding }) => binding);
522
- } else if (isRecord(value)) {
523
- return Object.keys(value);
524
- } else {
525
- return [];
526
- }
544
+ if (typeof value !== "object" || value === null) {
545
+ return [];
546
+ }
547
+
548
+ if (isBindingList(value)) {
549
+ return value.bindings.map(({ name }) => name);
550
+ } else if (isNamespaceList(value)) {
551
+ return value.map(({ binding }) => binding);
552
+ } else if (isRecord(value)) {
553
+ return Object.keys(value);
554
+ } else {
555
+ return [];
556
+ }
527
557
  };
528
558
 
529
559
  const isBindingList = (
530
- value: unknown
560
+ value: unknown
531
561
  ): value is {
532
- bindings: {
533
- name: string;
534
- }[];
562
+ bindings: {
563
+ name: string;
564
+ }[];
535
565
  } =>
536
- isRecord(value) &&
537
- "bindings" in value &&
538
- Array.isArray(value.bindings) &&
539
- value.bindings.every(
540
- (binding) =>
541
- isRecord(binding) && "name" in binding && typeof binding.name === "string"
542
- );
566
+ isRecord(value) &&
567
+ "bindings" in value &&
568
+ Array.isArray(value.bindings) &&
569
+ value.bindings.every(
570
+ (binding) =>
571
+ isRecord(binding) && "name" in binding && typeof binding.name === "string"
572
+ );
543
573
 
544
574
  const isNamespaceList = (value: unknown): value is { binding: string }[] =>
545
- Array.isArray(value) &&
546
- value.every(
547
- (entry) =>
548
- isRecord(entry) && "binding" in entry && typeof entry.binding === "string"
549
- );
575
+ Array.isArray(value) &&
576
+ value.every(
577
+ (entry) =>
578
+ isRecord(entry) && "binding" in entry && typeof entry.binding === "string"
579
+ );
550
580
 
551
581
  const isRecord = (
552
- value: unknown
582
+ value: unknown
553
583
  ): value is Record<string | number | symbol, unknown> =>
554
- typeof value === "object" && value !== null && !Array.isArray(value);
584
+ typeof value === "object" && value !== null && !Array.isArray(value);
585
+
586
+ /**
587
+ * JavaScript `typeof` operator return values.
588
+ */
589
+ type TypeofType =
590
+ | "string"
591
+ | "number"
592
+ | "bigint"
593
+ | "boolean"
594
+ | "symbol"
595
+ | "undefined"
596
+ | "object"
597
+ | "function";