devflare 1.0.0-next.59 → 1.0.0-next.60

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 (45) hide show
  1. package/dist/_chunks/{build-IR-IaQN1.js → build-Dp8k2SWa.js} +1 -1
  2. package/dist/_chunks/{build-artifacts-C7JfIRnw.js → build-artifacts-DLEYfdVL.js} +2 -2
  3. package/dist/_chunks/{cli-yOO5yCaf.js → cli-BlVetiEh.js} +5 -5
  4. package/dist/_chunks/{compiler-okj8qvCh.js → compiler-CgIFPfRA.js} +23 -1
  5. package/dist/_chunks/{config-C8Qux0NR.js → config-BwRb2_vD.js} +1 -1
  6. package/dist/_chunks/{deploy-BG2PHaWu.js → deploy-Blrdr3Gv.js} +2 -2
  7. package/dist/_chunks/{dev-BgQl56Bd.js → dev-DRP6LR2H.js} +42 -7
  8. package/dist/_chunks/{previews-fTFp1WkQ.js → previews-D-GPVCeq.js} +1 -1
  9. package/dist/_chunks/r2-presign-runtime-zTOLS0Ea.js +291 -0
  10. package/dist/_chunks/{runtime-DL-Nz3k8.js → runtime-RekfD_2X.js} +325 -1
  11. package/dist/_chunks/{vite-De7bGxC6.js → vite-CtvgOi1c.js} +2 -2
  12. package/dist/bridge/miniflare-gateway.d.ts +45 -0
  13. package/dist/bridge/miniflare-gateway.d.ts.map +1 -0
  14. package/dist/bridge/miniflare.d.ts +8 -1
  15. package/dist/bridge/miniflare.d.ts.map +1 -1
  16. package/dist/bridge/r2-presign-runtime.d.ts +9 -0
  17. package/dist/bridge/r2-presign-runtime.d.ts.map +1 -0
  18. package/dist/cli/index.js +1 -1
  19. package/dist/config/compiler.d.ts.map +1 -1
  20. package/dist/dev-server/gateway-script.d.ts +2 -1
  21. package/dist/dev-server/gateway-script.d.ts.map +1 -1
  22. package/dist/dev-server/miniflare-dev-config.d.ts +13 -0
  23. package/dist/dev-server/miniflare-dev-config.d.ts.map +1 -1
  24. package/dist/dev-server/miniflare-worker-config.d.ts +7 -0
  25. package/dist/dev-server/miniflare-worker-config.d.ts.map +1 -1
  26. package/dist/dev-server/server.d.ts.map +1 -1
  27. package/dist/dev-server/vite-process.d.ts +9 -0
  28. package/dist/dev-server/vite-process.d.ts.map +1 -1
  29. package/dist/index.js +2 -2
  30. package/dist/runtime/index.d.ts +1 -0
  31. package/dist/runtime/index.d.ts.map +1 -1
  32. package/dist/runtime/index.js +2 -2
  33. package/dist/runtime/r2-presign.d.ts +164 -0
  34. package/dist/runtime/r2-presign.d.ts.map +1 -0
  35. package/dist/test/index.js +41 -4
  36. package/dist/test/simple-context-gateway-script.d.ts.map +1 -1
  37. package/dist/test/simple-context-mfconfig.d.ts +8 -0
  38. package/dist/test/simple-context-mfconfig.d.ts.map +1 -1
  39. package/dist/test/simple-context-multi-worker.d.ts.map +1 -1
  40. package/dist/test/simple-context-runtime.d.ts.map +1 -1
  41. package/dist/test/simple-context-startup.d.ts.map +1 -1
  42. package/dist/test/simple-context.d.ts.map +1 -1
  43. package/dist/vite/index.js +1 -1
  44. package/package.json +3 -2
  45. package/dist/_chunks/local-workflow-entrypoints-ZIL7apIa.js +0 -90
@@ -1,5 +1,5 @@
1
1
  import { a as cyanBold, f as logLine, o as dim, r as createCliTheme } from "./ui-BUoZApvE.js";
2
- import { t as prepareBuildArtifacts } from "./build-artifacts-C7JfIRnw.js";
2
+ import { t as prepareBuildArtifacts } from "./build-artifacts-DLEYfdVL.js";
3
3
  //#region src/cli/commands/build.ts
4
4
  async function runBuildCommand(parsed, logger, options) {
5
5
  const theme = createCliTheme(parsed.options);
@@ -1,10 +1,10 @@
1
1
  import { d as resolveConfigEnvVars } from "./preview-C3Cdr8an.js";
2
2
  import { r as loadConfig, u as resolveConfigForEnvironment } from "./loader-E_UGyTrJ.js";
3
- import { c as writeWranglerConfig, i as isolateViteBuildOutputPaths$1, o as rebaseWranglerConfigPaths, t as compileBuildConfig } from "./compiler-okj8qvCh.js";
3
+ import { c as writeWranglerConfig, i as isolateViteBuildOutputPaths$1, o as rebaseWranglerConfigPaths, t as compileBuildConfig } from "./compiler-CgIFPfRA.js";
4
4
  import { f as logLine } from "./ui-BUoZApvE.js";
5
5
  import { n as getPackageVersion } from "./package-metadata-DjHBLB-z.js";
6
6
  import { t as resolvePackageSpecifier } from "./resolve-package-qizBfCIm.js";
7
- import { i as writeGeneratedViteConfig, l as prepareComposedWorkerEntrypoint, n as resolveEffectiveViteProject } from "./vite-De7bGxC6.js";
7
+ import { i as writeGeneratedViteConfig, l as prepareComposedWorkerEntrypoint, n as resolveEffectiveViteProject } from "./vite-CtvgOi1c.js";
8
8
  import { t as bundleWorkerEntry } from "./worker-bundler-dTTnYU3n.js";
9
9
  import { t as detectViteProject } from "./vite-utils-OVLMV605.js";
10
10
  import { t as getDependencies } from "./dependencies-C2oxFjU_.js";
@@ -1101,15 +1101,15 @@ async function runInit(parsed, logger, options) {
1101
1101
  return runInitCommand(parsed, logger, options);
1102
1102
  }
1103
1103
  async function runDev(parsed, logger, options) {
1104
- const { runDevCommand } = await import("./dev-BgQl56Bd.js");
1104
+ const { runDevCommand } = await import("./dev-DRP6LR2H.js");
1105
1105
  return runDevCommand(parsed, logger, options);
1106
1106
  }
1107
1107
  async function runBuild(parsed, logger, options) {
1108
- const { runBuildCommand } = await import("./build-IR-IaQN1.js");
1108
+ const { runBuildCommand } = await import("./build-Dp8k2SWa.js");
1109
1109
  return runBuildCommand(parsed, logger, options);
1110
1110
  }
1111
1111
  async function runDeploy(parsed, logger, options) {
1112
- const { runDeployCommand } = await import("./deploy-BG2PHaWu.js");
1112
+ const { runDeployCommand } = await import("./deploy-Blrdr3Gv.js");
1113
1113
  return runDeployCommand(parsed, logger, {
1114
1114
  ...options,
1115
1115
  requireExplicitDeployTarget: true
@@ -1124,7 +1124,7 @@ async function runDoctor(parsed, logger, options) {
1124
1124
  return runDoctorCommand(parsed, logger, options);
1125
1125
  }
1126
1126
  async function runConfig(parsed, logger, options) {
1127
- const { runConfigCommand } = await import("./config-C8Qux0NR.js");
1127
+ const { runConfigCommand } = await import("./config-BwRb2_vD.js");
1128
1128
  return runConfigCommand(parsed, logger, options);
1129
1129
  }
1130
1130
  async function runAccount(parsed, logger, options) {
@@ -1136,7 +1136,7 @@ async function runLogin(parsed, logger, options) {
1136
1136
  return runLoginCommand(parsed, logger, options);
1137
1137
  }
1138
1138
  async function runPreviews(parsed, logger, options) {
1139
- const { runPreviewsCommand } = await import("./previews-fTFp1WkQ.js");
1139
+ const { runPreviewsCommand } = await import("./previews-D-GPVCeq.js");
1140
1140
  return runPreviewsCommand(parsed, logger, options);
1141
1141
  }
1142
1142
  async function runProductions(parsed, logger, options) {
@@ -458,7 +458,11 @@ function compileConfigInternal(config, environment, options = {}) {
458
458
  ...consumer.environment && { environment: consumer.environment }
459
459
  });
460
460
  if (mergedConfig.streamingTailConsumers && mergedConfig.streamingTailConsumers.length > 0) result.streaming_tail_consumers = mergedConfig.streamingTailConsumers.map((consumer) => typeof consumer === "string" ? { service: consumer } : { service: consumer.service });
461
- if (mergedConfig.vars && Object.keys(mergedConfig.vars).length > 0) result.vars = mergedConfig.vars;
461
+ const r2BucketsVar = buildR2BucketsVar(mergedConfig.bindings?.r2);
462
+ if (mergedConfig.vars && Object.keys(mergedConfig.vars).length > 0 || r2BucketsVar) result.vars = {
463
+ ...r2BucketsVar && { DEVFLARE_R2_BUCKETS: r2BucketsVar },
464
+ ...mergedConfig.vars
465
+ };
462
466
  const secrets = toWranglerSecretsConfig(mergedConfig.secrets);
463
467
  if (secrets) result.secrets = secrets;
464
468
  if (mergedConfig.routes && mergedConfig.routes.length > 0) result.routes = mergedConfig.routes.map((route) => ({
@@ -485,6 +489,24 @@ function compileConfigInternal(config, environment, options = {}) {
485
489
  return result;
486
490
  }
487
491
  /**
492
+ * Build the JSON value of the injected `DEVFLARE_R2_BUCKETS` var: a mapping
493
+ * of R2 binding name → `{ bucketName, jurisdiction? }` that `presignR2Put`/
494
+ * `presignR2Get` read at runtime to presign against the right bucket without
495
+ * the app hardcoding bucket names. Returns `undefined` when the config has
496
+ * no R2 bindings.
497
+ */
498
+ function buildR2BucketsVar(r2Bindings) {
499
+ if (!r2Bindings || Object.keys(r2Bindings).length === 0) return;
500
+ const mapping = Object.fromEntries(Object.entries(r2Bindings).map(([bindingName, bindingConfig]) => {
501
+ const normalized = normalizeR2Binding(bindingConfig);
502
+ return [bindingName, {
503
+ bucketName: normalized.bucketName,
504
+ ...normalized.jurisdiction && { jurisdiction: normalized.jurisdiction }
505
+ }];
506
+ }));
507
+ return JSON.stringify(mapping);
508
+ }
509
+ /**
488
510
  * Compile DevflareConfig to programmatic config for @cloudflare/vite-plugin.
489
511
  * This is used instead of wrangler.jsonc in dev mode.
490
512
  *
@@ -1,6 +1,6 @@
1
1
  import { d as resolveConfigEnvVars } from "./preview-C3Cdr8an.js";
2
2
  import { a as ConfigResourceResolutionError, o as loadResolvedConfig, r as loadConfig, s as resolveResources } from "./loader-E_UGyTrJ.js";
3
- import { n as compileConfig, t as compileBuildConfig } from "./compiler-okj8qvCh.js";
3
+ import { n as compileConfig, t as compileBuildConfig } from "./compiler-CgIFPfRA.js";
4
4
  //#region src/cli/commands/config.ts
5
5
  function isSupportedFormat(value) {
6
6
  return value === "devflare" || value === "wrangler";
@@ -1,13 +1,13 @@
1
1
  import { a as resolvePreviewIdentifier, d as resolveConfigEnvVars } from "./preview-C3Cdr8an.js";
2
2
  import { c as prepareConfigResourcesForDeploy, r as loadConfig, u as resolveConfigForEnvironment } from "./loader-E_UGyTrJ.js";
3
- import { a as readWranglerConfig, c as writeWranglerConfig, n as compileConfig, o as rebaseWranglerConfigPaths, s as stringifyConfig, t as compileBuildConfig } from "./compiler-okj8qvCh.js";
3
+ import { a as readWranglerConfig, c as writeWranglerConfig, n as compileConfig, o as rebaseWranglerConfigPaths, s as stringifyConfig, t as compileBuildConfig } from "./compiler-CgIFPfRA.js";
4
4
  import { d as getPrimaryAccount, n as getEffectiveAccountId } from "./preferences-BKp_7XJx.js";
5
5
  import { D as listWorkerVersions, E as listWorkerDeployments, O as listWorkers, T as getWorkersSubdomain, c as reconcilePreviewRegistry, w as getWorkerVersionDetail, x as formatWorkersDevUrl } from "./preview-registry-ClAbLTHH.js";
6
6
  import { _ as yellowBold, d as green, f as logLine, g as yellow, o as dim, r as createCliTheme } from "./ui-BUoZApvE.js";
7
7
  import { n as getPackageVersion } from "./package-metadata-DjHBLB-z.js";
8
8
  import { t as resolvePackageSpecifier } from "./resolve-package-qizBfCIm.js";
9
9
  import { t as asOptionalString } from "./command-utils-CSnoFdxM.js";
10
- import { a as createBuildManifest, i as compareManifests, n as applyDeploymentStrategy, o as formatDriftWarning, r as describeDeploymentStrategy, s as readBuildManifest, t as prepareBuildArtifacts } from "./build-artifacts-C7JfIRnw.js";
10
+ import { a as createBuildManifest, i as compareManifests, n as applyDeploymentStrategy, o as formatDriftWarning, r as describeDeploymentStrategy, s as readBuildManifest, t as prepareBuildArtifacts } from "./build-artifacts-DLEYfdVL.js";
11
11
  import { t as getDependencies } from "./dependencies-C2oxFjU_.js";
12
12
  import { n as preparePreviewScopedResourcesForDeploy } from "./preview-resources-D_aFwtZR.js";
13
13
  import { mkdir, open, readFile, rm, writeFile } from "node:fs/promises";
@@ -7,9 +7,9 @@ import { clearLocalSendEmailBindings, setLocalSendEmailBindings } from "../utils
7
7
  import { C as buildSendEmailConfig, D as buildVersionMetadataConfig, E as buildTailConsumersConfig, O as buildWorkerLoadersConfig, S as buildSecretsStoreConfig, T as buildStreamingTailConsumersConfig, _ as buildMtlsCertificatesConfig, b as buildQueueProducers, c as buildAiSearchInstancesConfig, d as buildArtifactsConfig, f as buildDispatchNamespacesConfig, g as buildMediaConfig, h as buildImagesConfig, k as buildWorkflowsConfig, l as buildAiSearchNamespacesConfig, m as buildHyperdrivesConfig, n as getRouteDirectoryCandidate, p as buildFlagshipConfig, s as resolveServiceBindings, t as discoverRoutes, u as buildAnalyticsEngineConfig, v as buildPipelinesConfig, w as buildStreamConfig, x as buildRateLimitsConfig, y as buildQueueConsumers } from "./routes-BMU7ga_l.js";
8
8
  import { r as transformDurableObject } from "./durable-object-DMtH0XYX.js";
9
9
  import { i as findFiles } from "./glob-CmQOvunB.js";
10
- import { d as DEFAULT_FETCH_ENTRY_FILES, f as DEFAULT_QUEUE_ENTRY_FILES, g as discoverDurableObjectFiles, h as resolveWorkerSurfacePaths, i as writeGeneratedViteConfig, l as prepareComposedWorkerEntrypoint, m as hasWorkerSurfacePaths, n as resolveEffectiveViteProject, p as DEFAULT_SCHEDULED_ENTRY_FILES, u as DEFAULT_EMAIL_ENTRY_FILES } from "./vite-De7bGxC6.js";
10
+ import { d as DEFAULT_FETCH_ENTRY_FILES, f as DEFAULT_QUEUE_ENTRY_FILES, g as discoverDurableObjectFiles, h as resolveWorkerSurfacePaths, i as writeGeneratedViteConfig, l as prepareComposedWorkerEntrypoint, m as hasWorkerSurfacePaths, n as resolveEffectiveViteProject, p as DEFAULT_SCHEDULED_ENTRY_FILES, u as DEFAULT_EMAIL_ENTRY_FILES } from "./vite-CtvgOi1c.js";
11
11
  import { a as createWorkerdBundlerDefaults, i as writeWorkerCompatibleBundle, n as ensureDebugShim, r as resolveWorkerCompatibleRolldownConfig, t as bundleWorkerEntry } from "./worker-bundler-dTTnYU3n.js";
12
- import { t as bundleWorkflowEntrypointScript } from "./local-workflow-entrypoints-ZIL7apIa.js";
12
+ import { n as bundleWorkflowEntrypointScript, t as R2_PRESIGN_RUNTIME_JS } from "./r2-presign-runtime-zTOLS0Ea.js";
13
13
  import { n as buildLocalSecretWrappedBindingConfig } from "./local-secrets-6sMstHXw.js";
14
14
  import { t as buildLocalBindingShimServiceConfig } from "./local-media-bindings-DsAW4AMF.js";
15
15
  import { i as waitForViteReady, n as resolveViteMode, r as stopSpawnedProcessTree, t as detectViteProject } from "./vite-utils-OVLMV605.js";
@@ -1592,7 +1592,8 @@ async function handleDevToolsWebSocket(request, url) {
1592
1592
  * All in-sandbox RPC behavior (method dispatch, error envelope, serialization,
1593
1593
  * WebSocket bridge, HTTP transfer) lives in `GATEWAY_RUNTIME_JS` and is shared
1594
1594
  * with `src/bridge/miniflare.ts`. The canonical TypeScript equivalent lives in
1595
- * `src/bridge/server.ts`.
1595
+ * `src/bridge/server.ts`. The local presigned-R2 endpoint handler is shared
1596
+ * via `R2_PRESIGN_RUNTIME_JS` (`src/bridge/r2-presign-runtime.ts`).
1596
1597
  *
1597
1598
  * This file only owns the pieces that are genuinely dev-server-specific:
1598
1599
  * - WebSocket route matching & DO WebSocket forwarding (`WS_ROUTES`)
@@ -1608,6 +1609,8 @@ function getGatewayScript(wsRoutes = [], debug = false, appServiceBindingName =
1608
1609
  return `
1609
1610
  ${GATEWAY_RUNTIME_JS}
1610
1611
 
1612
+ ${R2_PRESIGN_RUNTIME_JS}
1613
+
1611
1614
  // Bridge Gateway Worker — Dev Server
1612
1615
  // Dev-server-specific overlay on top of the shared GATEWAY_RUNTIME_JS:
1613
1616
  // WS route DO forwarding, D1 migration, email ingest, app-worker fallthrough.
@@ -1635,6 +1638,11 @@ export default {
1635
1638
  return handleHttpTransfer(request, env, url)
1636
1639
  }
1637
1640
 
1641
+ const presignResponse = await __devflareR2PresignHandle(request, env, url)
1642
+ if (presignResponse) {
1643
+ return presignResponse
1644
+ }
1645
+
1638
1646
  if (url.pathname === '/_devflare/migrate' && request.method === 'POST') {
1639
1647
  return handleMigration(request, env)
1640
1648
  }
@@ -1909,7 +1917,10 @@ function makeMiniflareWorker(context, options) {
1909
1917
  const { cwd, loadedConfig, bindings, sendEmailConfig, rateLimitsConfig, versionMetadataConfig, workerLoadersConfig, mtlsCertificatesConfig, dispatchNamespacesConfig, workflowsConfig, pipelinesConfig, hyperdrivesConfig, imagesConfig, mediaConfig, streamConfig, flagshipConfig, analyticsEngineConfig, tailConsumersConfig, streamingTailConsumersConfig, artifactsConfig, aiSearchNamespacesConfig, aiSearchInstancesConfig, secretsStoreConfig, localSecretWrappedBindingConfig, queueProducers } = context;
1910
1918
  const baseFlags = loadedConfig.compatibilityFlags ?? [];
1911
1919
  const compatFlags = baseFlags.includes("nodejs_compat") ? baseFlags : [...baseFlags, "nodejs_compat"];
1912
- const workerBindings = loadedConfig.vars ?? {};
1920
+ const workerBindings = {
1921
+ ...loadedConfig.vars ?? {},
1922
+ ...context.injectedVars ?? {}
1923
+ };
1913
1924
  const localWrappedBindings = { ...localSecretWrappedBindingConfig?.wrappedBindings ?? {} };
1914
1925
  const workerConfig = {
1915
1926
  name: options.name,
@@ -1995,6 +2006,16 @@ function collectWorkerWatchRoots(cwd, config, mainWorkerSurfacePaths) {
1995
2006
  //#region src/dev-server/miniflare-dev-config.ts
1996
2007
  const INTERNAL_APP_SERVICE_BINDING = "__DEVFLARE_APP";
1997
2008
  /**
2009
+ * Resolve the browser-facing origin of the local dev runtime, used inside
2010
+ * locally-presigned R2 URLs. Honors `server.publicUrl` (reverse proxy /
2011
+ * tunnel setups); otherwise derives `http(s)://<host>:<port>` from the
2012
+ * Miniflare listen address, normalizing wildcard hosts to `localhost`.
2013
+ */
2014
+ function resolveR2PresignOrigin(serverConfig, miniflareHost, miniflarePort) {
2015
+ if (serverConfig?.publicUrl) return serverConfig.publicUrl.replace(/\/$/, "");
2016
+ return `${serverConfig?.https ? "https" : "http"}://${miniflareHost === "0.0.0.0" || miniflareHost === "::" ? "localhost" : miniflareHost}:${miniflarePort}`;
2017
+ }
2018
+ /**
1998
2019
  * Build the complete Miniflare configuration for the dev server.
1999
2020
  *
2000
2021
  * IMPORTANT: When using multi-worker setup, ALL workers must go in the
@@ -2003,7 +2024,7 @@ const INTERNAL_APP_SERVICE_BINDING = "__DEVFLARE_APP";
2003
2024
  * workers array is present.
2004
2025
  */
2005
2026
  function buildMiniflareDevConfig(input) {
2006
- const { config: loadedConfig, cwd, miniflarePort, miniflareHost = "127.0.0.1", persist, enableVite, debug, mainWorkerSurfacePaths, mainWorkerRoutes, mainWorkerScriptPath, bundledMainWorkerScriptPath, workflowEntrypointScript, browserShimPort, doResult, serviceBindingResolution, logger } = input;
2027
+ const { config: loadedConfig, cwd, miniflarePort, miniflareHost = "127.0.0.1", persist, enableVite, debug, mainWorkerSurfacePaths, mainWorkerRoutes, mainWorkerScriptPath, bundledMainWorkerScriptPath, workflowEntrypointScript, browserShimPort, doResult, serviceBindingResolution, r2PresignSecret, logger } = input;
2007
2028
  const bindings = loadedConfig.bindings ?? {};
2008
2029
  const persistPath = resolve(cwd, ".devflare/data");
2009
2030
  const appWorkerName = loadedConfig.name;
@@ -2086,7 +2107,11 @@ function buildMiniflareDevConfig(input) {
2086
2107
  aiSearchInstancesConfig,
2087
2108
  secretsStoreConfig: buildSecretsStoreConfig(bindings, loadedConfig.secretsStoreId, localSecretBindingNames),
2088
2109
  localSecretWrappedBindingConfig,
2089
- queueProducers
2110
+ queueProducers,
2111
+ injectedVars: bindings.r2 && r2PresignSecret ? {
2112
+ DEVFLARE_R2_PRESIGN_SECRET: r2PresignSecret,
2113
+ DEVFLARE_R2_PRESIGN_ORIGIN: resolveR2PresignOrigin(serverConfig, miniflareHost, miniflarePort)
2114
+ } : void 0
2090
2115
  };
2091
2116
  const createWorkerConfig = (options) => makeMiniflareWorker(workerContext, options);
2092
2117
  const gatewayWorker = createWorkerConfig({
@@ -2902,7 +2927,7 @@ async function resolveViteIntegration(options) {
2902
2927
  * Start the Vite dev server process.
2903
2928
  */
2904
2929
  async function startViteProcess(options) {
2905
- const { cwd, configPath, vitePort, miniflarePort, generatedViteConfigPath, logger } = options;
2930
+ const { cwd, configPath, vitePort, miniflarePort, generatedViteConfigPath, r2Presign, logger } = options;
2906
2931
  const args = [
2907
2932
  "vite",
2908
2933
  "dev",
@@ -2923,6 +2948,10 @@ async function startViteProcess(options) {
2923
2948
  DEVFLARE_DEV: "true",
2924
2949
  DEVFLARE_BRIDGE_PORT: String(miniflarePort),
2925
2950
  ...configPath ? { DEVFLARE_CONFIG_PATH: configPath } : {},
2951
+ ...r2Presign ? {
2952
+ DEVFLARE_R2_PRESIGN_SECRET: r2Presign.secret,
2953
+ DEVFLARE_R2_PRESIGN_ORIGIN: r2Presign.origin
2954
+ } : {},
2926
2955
  FORCE_COLOR: "1"
2927
2956
  }
2928
2957
  });
@@ -3050,6 +3079,7 @@ async function applyWatcherTargetDiff(watcher, currentTargets, nextTargets) {
3050
3079
  function createDevServer(options) {
3051
3080
  const { cwd, configPath, vitePort = 5173, miniflarePort = 8787, miniflareHost = "127.0.0.1", enableVite: enableViteRequested = true, persist = true, logger, verbose = false, debug = process.env.DEVFLARE_DEBUG === "true" } = options;
3052
3081
  const state = createDevServerState({ enableVite: enableViteRequested });
3082
+ const r2PresignSecret = `${crypto.randomUUID()}${crypto.randomUUID()}`;
3053
3083
  const reloadQueue = createReloadQueue({
3054
3084
  reload: async () => {
3055
3085
  if (!state.miniflare) return;
@@ -3098,6 +3128,7 @@ function createDevServer(options) {
3098
3128
  browserShimPort: state.browserShimPort,
3099
3129
  doResult,
3100
3130
  serviceBindingResolution: state.serviceBindingResolution,
3131
+ r2PresignSecret,
3101
3132
  logger
3102
3133
  });
3103
3134
  }
@@ -3269,6 +3300,10 @@ function createDevServer(options) {
3269
3300
  vitePort,
3270
3301
  miniflarePort,
3271
3302
  generatedViteConfigPath: state.generatedViteConfigPath,
3303
+ r2Presign: state.config.bindings?.r2 ? {
3304
+ secret: r2PresignSecret,
3305
+ origin: resolveR2PresignOrigin(state.config.server, miniflareHost, miniflarePort)
3306
+ } : null,
3272
3307
  logger
3273
3308
  });
3274
3309
  else logger?.info("Vite startup skipped (no effective Vite config found for this package)");
@@ -1,6 +1,6 @@
1
1
  import { a as resolvePreviewIdentifier } from "./preview-C3Cdr8an.js";
2
2
  import { i as resolveConfigPath, o as loadResolvedConfig, r as loadConfig, t as ConfigNotFoundError } from "./loader-E_UGyTrJ.js";
3
- import { t as compileBuildConfig } from "./compiler-okj8qvCh.js";
3
+ import { t as compileBuildConfig } from "./compiler-CgIFPfRA.js";
4
4
  import { t as account } from "./cloudflare-BaloqI3H.js";
5
5
  import { _ as yellowBold, a as cyanBold, d as green, f as logLine, g as yellow, h as whiteDim, m as red, n as bold, o as dim, r as createCliTheme, u as formatTableLine } from "./ui-BUoZApvE.js";
6
6
  import { i as resolveNamedSelection, r as resolveCloudflareAccountId, t as asOptionalString } from "./command-utils-CSnoFdxM.js";
@@ -0,0 +1,291 @@
1
+ import { x as normalizeWorkflowBinding } from "./schema-normalization-Zgm4W6O6.js";
2
+ import { i as findFiles, r as DEFAULT_WORKFLOW_PATTERN } from "./glob-CmQOvunB.js";
3
+ import { t as bundleWorkerEntry } from "./worker-bundler-dTTnYU3n.js";
4
+ import { mkdir, readFile, writeFile } from "node:fs/promises";
5
+ import { join, relative, resolve } from "pathe";
6
+ //#region src/workflows/local-workflow-entrypoints.ts
7
+ function findExportedClasses(code) {
8
+ const classes = [];
9
+ const classPattern = /export\s+class\s+(\w+)/g;
10
+ let match = classPattern.exec(code);
11
+ while (match !== null) {
12
+ classes.push(match[1]);
13
+ match = classPattern.exec(code);
14
+ }
15
+ return classes;
16
+ }
17
+ function toImportSpecifier(fromDir, filePath) {
18
+ const relativePath = relative(fromDir, filePath).replace(/\\/g, "/");
19
+ return relativePath.startsWith(".") ? relativePath : `./${relativePath}`;
20
+ }
21
+ async function discoverWorkflowClasses(config, configDir) {
22
+ const classToFilePath = /* @__PURE__ */ new Map();
23
+ const workflowPatternConfig = config.files?.workflows;
24
+ const workflowPattern = typeof workflowPatternConfig === "string" ? workflowPatternConfig : DEFAULT_WORKFLOW_PATTERN;
25
+ if (workflowPatternConfig === false) return classToFilePath;
26
+ const files = await findFiles(workflowPattern, { cwd: configDir });
27
+ for (const filePath of files) try {
28
+ const code = await readFile(filePath, "utf-8");
29
+ for (const className of findExportedClasses(code)) classToFilePath.set(className, filePath);
30
+ } catch {}
31
+ return classToFilePath;
32
+ }
33
+ async function resolveLocalWorkflowEntrypoints(config, configDir) {
34
+ const workflows = config.bindings?.workflows;
35
+ if (!workflows || Object.keys(workflows).length === 0) return [];
36
+ const classToFilePath = await discoverWorkflowClasses(config, configDir);
37
+ const entrypoints = [];
38
+ for (const [bindingName, binding] of Object.entries(workflows)) {
39
+ const normalized = normalizeWorkflowBinding(binding);
40
+ if (normalized.scriptName) continue;
41
+ const scriptPath = classToFilePath.get(normalized.className);
42
+ if (!scriptPath) throw new Error(`Workflow binding ${bindingName} (className: '${normalized.className}') not found.\nEither set files.workflows to match the workflow source file, or set scriptName when the workflow lives in another worker.`);
43
+ entrypoints.push({
44
+ bindingName,
45
+ className: normalized.className,
46
+ scriptPath
47
+ });
48
+ }
49
+ return entrypoints;
50
+ }
51
+ function buildWorkflowVirtualEntry(entrypoints, entryDir) {
52
+ const imports = entrypoints.map((entrypoint, index) => {
53
+ const importName = `__DevflareWorkflow${index}`;
54
+ const importPath = toImportSpecifier(entryDir, entrypoint.scriptPath);
55
+ return {
56
+ importName,
57
+ className: entrypoint.className,
58
+ line: `import { ${entrypoint.className} as ${importName} } from '${importPath}'`
59
+ };
60
+ });
61
+ const exports = imports.map((entrypoint) => {
62
+ return `export { ${entrypoint.importName} as ${entrypoint.className} }`;
63
+ });
64
+ return [
65
+ ...imports.map((entrypoint) => entrypoint.line),
66
+ "",
67
+ ...exports
68
+ ].join("\n");
69
+ }
70
+ async function bundleWorkflowEntrypointScript(config, configDir, options = {}) {
71
+ const entrypoints = await resolveLocalWorkflowEntrypoints(config, configDir);
72
+ if (entrypoints.length === 0) return "";
73
+ const entryDir = resolve(configDir, ".devflare", "workflow-entrypoints");
74
+ const entryPath = join(entryDir, "__entry.ts");
75
+ const outFile = join(entryDir, "index.js");
76
+ await mkdir(entryDir, { recursive: true });
77
+ await writeFile(entryPath, buildWorkflowVirtualEntry(entrypoints, entryDir));
78
+ await bundleWorkerEntry({
79
+ cwd: configDir,
80
+ inputFile: entryPath,
81
+ outFile,
82
+ rolldownOptions: config.rolldown?.options,
83
+ sourcemap: config.rolldown?.sourcemap,
84
+ minify: config.rolldown?.minify,
85
+ logger: options.logger
86
+ });
87
+ return await readFile(outFile, "utf-8");
88
+ }
89
+ //#endregion
90
+ //#region src/bridge/r2-presign-runtime.ts
91
+ /**
92
+ * Inline JS defining `__devflareR2PresignHandle(request, env, url)`, which
93
+ * returns a `Response` when the request targets the local presign endpoint
94
+ * and `null` otherwise. Enforces signature, expiry, method, content type and
95
+ * size against the per-boot secret in `env.DEVFLARE_R2_PRESIGN_SECRET`, then
96
+ * executes the object operation on the local R2 binding.
97
+ */
98
+ const R2_PRESIGN_RUNTIME_JS = `
99
+ const __devflareR2PresignPrefix = '/_devflare/r2/presigned/'
100
+
101
+ const __devflareR2PresignCors = {
102
+ 'Access-Control-Allow-Origin': '*',
103
+ 'Access-Control-Allow-Methods': 'GET, HEAD, PUT, OPTIONS',
104
+ 'Access-Control-Allow-Headers': 'Content-Type',
105
+ 'Access-Control-Expose-Headers': 'ETag, Content-Type, Content-Length'
106
+ }
107
+
108
+ function __devflareR2PresignError(status, code, message) {
109
+ return new Response(JSON.stringify({ error: { code, message } }), {
110
+ status,
111
+ headers: { 'Content-Type': 'application/json', ...__devflareR2PresignCors }
112
+ })
113
+ }
114
+
115
+ async function __devflareR2PresignHmacHex(secret, message) {
116
+ const encoder = new TextEncoder()
117
+ const key = await crypto.subtle.importKey(
118
+ 'raw',
119
+ encoder.encode(secret),
120
+ { name: 'HMAC', hash: 'SHA-256' },
121
+ false,
122
+ ['sign']
123
+ )
124
+ const signature = await crypto.subtle.sign('HMAC', key, encoder.encode(message))
125
+ return [...new Uint8Array(signature)].map((b) => b.toString(16).padStart(2, '0')).join('')
126
+ }
127
+
128
+ // Constant-time hex comparison (uses workerd's timingSafeEqual when present).
129
+ function __devflareR2PresignEqual(expectedHex, actualHex) {
130
+ if (typeof actualHex !== 'string' || actualHex.length !== expectedHex.length) return false
131
+ const encoder = new TextEncoder()
132
+ const expected = encoder.encode(expectedHex)
133
+ const actual = encoder.encode(actualHex)
134
+ const subtle = crypto.subtle
135
+ if (subtle && typeof subtle.timingSafeEqual === 'function') {
136
+ return subtle.timingSafeEqual(expected, actual)
137
+ }
138
+ let diff = 0
139
+ for (let i = 0; i < expected.length; i++) diff |= expected[i] ^ actual[i]
140
+ return diff === 0
141
+ }
142
+
143
+ // MUST match buildLocalCanonicalString in src/runtime/r2-presign.ts.
144
+ function __devflareR2PresignCanonical(method, binding, key, expires, contentType, contentLength, maxSize) {
145
+ return [
146
+ 'devflare:r2-presign:v1',
147
+ method,
148
+ binding,
149
+ key,
150
+ expires,
151
+ contentType || '',
152
+ contentLength || '',
153
+ maxSize || ''
154
+ ].join('\\n')
155
+ }
156
+
157
+ /**
158
+ * Handle a request to the local presigned R2 endpoint; returns null when the
159
+ * path does not match so gateways can fall through to their other routes.
160
+ */
161
+ async function __devflareR2PresignHandle(request, env, url) {
162
+ if (!url.pathname.startsWith(__devflareR2PresignPrefix)) return null
163
+
164
+ if (request.method === 'OPTIONS') {
165
+ return new Response(null, {
166
+ status: 204,
167
+ headers: { ...__devflareR2PresignCors, 'Access-Control-Max-Age': '86400' }
168
+ })
169
+ }
170
+
171
+ const rest = url.pathname.slice(__devflareR2PresignPrefix.length)
172
+ const slashIndex = rest.indexOf('/')
173
+ if (slashIndex <= 0 || slashIndex === rest.length - 1) {
174
+ return __devflareR2PresignError(400, 'bad_request', 'Expected /_devflare/r2/presigned/<binding>/<key>.')
175
+ }
176
+ const binding = decodeURIComponent(rest.slice(0, slashIndex))
177
+ const key = rest
178
+ .slice(slashIndex + 1)
179
+ .split('/')
180
+ .map((segment) => decodeURIComponent(segment))
181
+ .join('/')
182
+
183
+ const secret = env.DEVFLARE_R2_PRESIGN_SECRET
184
+ if (typeof secret !== 'string' || secret.length === 0) {
185
+ return __devflareR2PresignError(
186
+ 500,
187
+ 'presign_unconfigured',
188
+ 'DEVFLARE_R2_PRESIGN_SECRET is not configured on the devflare gateway. '
189
+ + 'This endpoint is only served for configs with R2 bindings.'
190
+ )
191
+ }
192
+
193
+ const params = url.searchParams
194
+ const signedMethod = params.get('X-Devflare-Method')
195
+ const expires = params.get('X-Devflare-Expires')
196
+ const providedSignature = params.get('X-Devflare-Signature')
197
+ const signedContentType = params.get('X-Devflare-Content-Type')
198
+ const signedContentLength = params.get('X-Devflare-Content-Length')
199
+ const signedMaxSize = params.get('X-Devflare-Max-Size')
200
+
201
+ if (!signedMethod || !expires || !providedSignature) {
202
+ return __devflareR2PresignError(403, 'missing_parameters', 'Missing presign query parameters.')
203
+ }
204
+
205
+ const expectedSignature = await __devflareR2PresignHmacHex(
206
+ secret,
207
+ __devflareR2PresignCanonical(
208
+ signedMethod, binding, key, expires, signedContentType, signedContentLength, signedMaxSize
209
+ )
210
+ )
211
+ if (!__devflareR2PresignEqual(expectedSignature, providedSignature)) {
212
+ return __devflareR2PresignError(403, 'invalid_signature', 'Presigned URL signature mismatch.')
213
+ }
214
+
215
+ if (Math.floor(Date.now() / 1000) > Number(expires)) {
216
+ return __devflareR2PresignError(403, 'expired', 'Presigned URL has expired.')
217
+ }
218
+
219
+ const methodAllowed = signedMethod === 'GET'
220
+ ? request.method === 'GET' || request.method === 'HEAD'
221
+ : request.method === signedMethod
222
+ if (!methodAllowed) {
223
+ return __devflareR2PresignError(
224
+ 403,
225
+ 'method_mismatch',
226
+ 'URL was presigned for ' + signedMethod + ', got ' + request.method + '.'
227
+ )
228
+ }
229
+
230
+ const bucket = env[binding]
231
+ if (!bucket || typeof bucket.put !== 'function' || typeof bucket.head !== 'function') {
232
+ return __devflareR2PresignError(404, 'unknown_binding', 'R2 binding not found: ' + binding)
233
+ }
234
+
235
+ if (request.method === 'PUT') {
236
+ const sentContentType = request.headers.get('Content-Type')
237
+ if (signedContentType !== null && sentContentType !== signedContentType) {
238
+ return __devflareR2PresignError(
239
+ 403,
240
+ 'content_type_mismatch',
241
+ 'URL was presigned for Content-Type "' + signedContentType + '", got "' + sentContentType + '".'
242
+ )
243
+ }
244
+
245
+ const body = await request.arrayBuffer()
246
+ if (signedContentLength !== null && body.byteLength !== Number(signedContentLength)) {
247
+ return __devflareR2PresignError(
248
+ 403,
249
+ 'content_length_mismatch',
250
+ 'URL was presigned for exactly ' + signedContentLength + ' bytes, got ' + body.byteLength + '.'
251
+ )
252
+ }
253
+ if (signedMaxSize !== null && body.byteLength > Number(signedMaxSize)) {
254
+ return __devflareR2PresignError(
255
+ 413,
256
+ 'too_large',
257
+ 'Body of ' + body.byteLength + ' bytes exceeds the presigned maximum of ' + signedMaxSize + '.'
258
+ )
259
+ }
260
+
261
+ const putContentType = sentContentType || signedContentType || undefined
262
+ const result = await bucket.put(key, body, {
263
+ ...(putContentType && { httpMetadata: { contentType: putContentType } })
264
+ })
265
+ return new Response(null, {
266
+ status: 200,
267
+ headers: { ...__devflareR2PresignCors, ...(result?.httpEtag && { ETag: result.httpEtag }) }
268
+ })
269
+ }
270
+
271
+ // GET / HEAD
272
+ const objectHeaders = (object) => ({
273
+ ...__devflareR2PresignCors,
274
+ 'Content-Type': object.httpMetadata?.contentType || 'application/octet-stream',
275
+ 'Content-Length': String(object.size),
276
+ ...(object.httpEtag && { ETag: object.httpEtag })
277
+ })
278
+
279
+ if (request.method === 'HEAD') {
280
+ const object = await bucket.head(key)
281
+ if (!object) return __devflareR2PresignError(404, 'not_found', 'Object not found: ' + key)
282
+ return new Response(null, { status: 200, headers: objectHeaders(object) })
283
+ }
284
+
285
+ const object = await bucket.get(key)
286
+ if (!object) return __devflareR2PresignError(404, 'not_found', 'Object not found: ' + key)
287
+ return new Response(object.body, { status: 200, headers: objectHeaders(object) })
288
+ }
289
+ `;
290
+ //#endregion
291
+ export { bundleWorkflowEntrypointScript as n, R2_PRESIGN_RUNTIME_JS as t };