wrangler 0.0.0-e043479 → 0.0.0-e0621809

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