openship 0.6.8 → 0.7.0

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 (58) hide show
  1. package/dist/bare-3POKLHTW.js +29 -0
  2. package/dist/{chunk-UAWY6X7V.js → chunk-3KUW2UC6.js} +470 -59
  3. package/dist/{chunk-GNOMEMNI.js → chunk-3LWGDMJU.js} +1 -1
  4. package/dist/{chunk-ENKJ7XIF.js → chunk-5CFUYMPE.js} +11 -11
  5. package/dist/{chunk-NUT6HKK6.js → chunk-5D27WZBW.js} +5 -5
  6. package/dist/{chunk-NIYD7MD2.js → chunk-7MKZP4TW.js} +89 -45
  7. package/dist/{chunk-IAMOUDHY.js → chunk-AUBB5DLY.js} +24 -15
  8. package/dist/{chunk-7PEXM2UI.js → chunk-BOD2IQ3P.js} +6 -6
  9. package/dist/{chunk-D6HU4VUZ.js → chunk-CMGRMPRQ.js} +1 -1
  10. package/dist/{chunk-MUWVC5R4.js → chunk-DZBXOF5Q.js} +11 -8
  11. package/dist/{chunk-BO6CY4V2.js → chunk-GEHV7JBS.js} +47 -4
  12. package/dist/{chunk-R5DC5PEF.js → chunk-GYDROM4W.js} +39 -10
  13. package/dist/{chunk-TAZZZKYO.js → chunk-HBQWOJRP.js} +1 -1
  14. package/dist/{chunk-WEZFTL4V.js → chunk-IVRTIKNZ.js} +183 -0
  15. package/dist/{chunk-P7RCYELG.js → chunk-J2NDOB7D.js} +1 -1
  16. package/dist/{chunk-T6DVUX4F.js → chunk-J4SYG2LC.js} +14 -3
  17. package/dist/{chunk-VAI5VOOI.js → chunk-JAKVPLHT.js} +1 -1
  18. package/dist/{chunk-NPSRGIHV.js → chunk-JWW5YAFL.js} +5 -4
  19. package/dist/{chunk-RCMERVBL.js → chunk-KYTTPWRP.js} +9 -9
  20. package/dist/{chunk-7YBWXEM6.js → chunk-LL7M7GL4.js} +4 -4
  21. package/dist/{chunk-SK4FFCFR.js → chunk-MZYD3LFP.js} +1 -1
  22. package/dist/{chunk-6GU6JSK6.js → chunk-QNQFS37A.js} +3 -3
  23. package/dist/{chunk-T46BQOLY.js → chunk-RHHCAWW3.js} +3 -3
  24. package/dist/{chunk-5UXHT6ED.js → chunk-SPR7N2EK.js} +113 -20
  25. package/dist/{chunk-AHJYP7DG.js → chunk-SYQ3FOLE.js} +74 -17
  26. package/dist/{chunk-3MP6ACKM.js → chunk-TPGBT5UR.js} +185 -78
  27. package/dist/{chunk-5I3COAFH.js → chunk-TT7P5ZZ3.js} +8 -8
  28. package/dist/{chunk-D3OEMXYP.js → chunk-XQC6KYZD.js} +12 -7
  29. package/dist/{chunk-X53JXRS4.js → chunk-YGA52DHX.js} +3 -3
  30. package/dist/{chunk-5DJOB7IX.js → chunk-ZFA3MZ5N.js} +3 -3
  31. package/dist/{cloud-REV3DPJH.js → cloud-RFZSO6P5.js} +19 -19
  32. package/dist/{detect-BBFT5GEU.js → detect-NXD3PYBM.js} +4 -4
  33. package/dist/{docker-EG3QSE6I.js → docker-U5APUZBI.js} +10 -10
  34. package/dist/{docker-edge-executor-E5TOTLMR.js → docker-edge-executor-HUQOWFQU.js} +7 -7
  35. package/dist/{edge-import-YJVJUGYC.js → edge-import-UXD52WQR.js} +26 -26
  36. package/dist/{edge-preflight-QUW4BM7N.js → edge-preflight-3PMJ5BWM.js} +13 -13
  37. package/dist/{ensure-container-edge-FOEFQ3PI.js → ensure-container-edge-GDPY7UJD.js} +11 -11
  38. package/dist/{executor-NTWDJ3DB.js → executor-XOVSO6QZ.js} +6 -6
  39. package/dist/{host-channel-preflight-OMCZMMKR.js → host-channel-preflight-T2QUDQXU.js} +26 -26
  40. package/dist/index.js +71 -49
  41. package/dist/{local-executor-OQ2GRN7K.js → local-executor-KR7BFCJP.js} +3 -3
  42. package/dist/{nginx-4U4C2XUS.js → nginx-JMLAS7YS.js} +9 -9
  43. package/dist/{openresty-lua-LLWT5XQW.js → openresty-lua-FJH3KEC4.js} +7 -7
  44. package/dist/{privilege-MNDVMDST.js → privilege-H2QTJ5TE.js} +5 -5
  45. package/dist/server/index.js +9760 -4421
  46. package/dist/server/lua/site_logger.lua +18 -1
  47. package/dist/server/migrations/0117_github_installation_workspace_scope.sql +24 -0
  48. package/dist/server/migrations/0118_git_sources.sql +66 -0
  49. package/dist/server/migrations/0119_backup_run_batch.sql +8 -0
  50. package/dist/server/migrations/0120_domain_primary_unique.sql +24 -0
  51. package/dist/server/migrations/0121_host_port_claim_overlap.sql +14 -0
  52. package/dist/server/migrations/meta/_journal.json +35 -0
  53. package/dist/{setup-IFIMPQZJ.js → setup-ERGE5XNW.js} +12 -12
  54. package/dist/{ssh-client-2XWZCMGI.js → ssh-client-WVLLOAYK.js} +2 -2
  55. package/dist/up-EFCE6KA6.js +61 -0
  56. package/package.json +1 -1
  57. package/dist/bare-FA66N7GC.js +0 -29
  58. package/dist/up-HQS7IR7H.js +0 -61
@@ -9,7 +9,7 @@ import {
9
9
  firewallPersists,
10
10
  refused,
11
11
  shellQuote
12
- } from "./chunk-WEZFTL4V.js";
12
+ } from "./chunk-IVRTIKNZ.js";
13
13
 
14
14
  // ../../packages/adapters/src/system/environment-ops.ts
15
15
  function opScript(steps) {
@@ -7,11 +7,11 @@ import {
7
7
  resolveProjectDir,
8
8
  resolveStaticOutputPath,
9
9
  runLocalBuild
10
- } from "./chunk-5DJOB7IX.js";
10
+ } from "./chunk-ZFA3MZ5N.js";
11
11
  import {
12
12
  isArtifactPathRef,
13
13
  removeManagedArtifact
14
- } from "./chunk-SK4FFCFR.js";
14
+ } from "./chunk-MZYD3LFP.js";
15
15
  import {
16
16
  BuildCancelledError,
17
17
  BuildLogger,
@@ -24,36 +24,36 @@ import {
24
24
  splitRuntimeEnv,
25
25
  sq,
26
26
  transferLocalDirectory
27
- } from "./chunk-AHJYP7DG.js";
27
+ } from "./chunk-SYQ3FOLE.js";
28
28
  import {
29
29
  LocalExecutor
30
- } from "./chunk-T6DVUX4F.js";
30
+ } from "./chunk-J4SYG2LC.js";
31
31
  import {
32
32
  privilegedExecutor
33
- } from "./chunk-T46BQOLY.js";
33
+ } from "./chunk-RHHCAWW3.js";
34
34
  import {
35
35
  HOST_STATE_DIR,
36
36
  envOps,
37
37
  hostRefusal,
38
38
  opScript
39
- } from "./chunk-GNOMEMNI.js";
39
+ } from "./chunk-3LWGDMJU.js";
40
40
  import {
41
41
  ensureOwnedDir,
42
42
  resolveEnvironment
43
- } from "./chunk-NPSRGIHV.js";
43
+ } from "./chunk-JWW5YAFL.js";
44
44
  import {
45
45
  sq as sq2,
46
46
  wrapLocalBuildCommand
47
- } from "./chunk-P7RCYELG.js";
47
+ } from "./chunk-J2NDOB7D.js";
48
48
  import {
49
49
  portOccupantDetails,
50
50
  probeListeningPort
51
- } from "./chunk-BO6CY4V2.js";
51
+ } from "./chunk-GEHV7JBS.js";
52
52
  import {
53
53
  managedDeploymentUnitName,
54
54
  probeExec,
55
55
  withReason
56
- } from "./chunk-D6HU4VUZ.js";
56
+ } from "./chunk-CMGRMPRQ.js";
57
57
  import {
58
58
  formatDuration,
59
59
  isRemoteConnectionError,
@@ -73,7 +73,7 @@ import {
73
73
  refused,
74
74
  safeErrorMessage,
75
75
  shellQuote
76
- } from "./chunk-WEZFTL4V.js";
76
+ } from "./chunk-IVRTIKNZ.js";
77
77
 
78
78
  // ../../packages/adapters/src/system/remote-journal.ts
79
79
  var OPSH_RUN_VERSION = 1;
@@ -19,16 +19,16 @@ import {
19
19
  rollbackEdgeTakeover,
20
20
  scanImportableSites,
21
21
  unreachableStaticRoots
22
- } from "./chunk-NIYD7MD2.js";
22
+ } from "./chunk-7MKZP4TW.js";
23
23
  import {
24
24
  LocalExecutor
25
- } from "./chunk-T6DVUX4F.js";
25
+ } from "./chunk-J4SYG2LC.js";
26
26
  import {
27
27
  ourEdgeContainerRunning
28
- } from "./chunk-X53JXRS4.js";
28
+ } from "./chunk-YGA52DHX.js";
29
29
  import {
30
30
  isOurEdgeContainer
31
- } from "./chunk-D6HU4VUZ.js";
31
+ } from "./chunk-CMGRMPRQ.js";
32
32
 
33
33
  // src/lib/edge-preflight.ts
34
34
  import { spawnSync } from "child_process";
@@ -243,7 +243,7 @@ async function repairEdgeConflict(mode, apiPort, onLog) {
243
243
  }
244
244
  spawnSync("docker", ["restart", "openship-edge"], { stdio: "ignore" });
245
245
  if (mode === "migrate" && scan.sites.length > 0) {
246
- const { importMigratedSites } = await import("./edge-import-YJVJUGYC.js");
246
+ const { importMigratedSites } = await import("./edge-import-UXD52WQR.js");
247
247
  const outcome = await importMigratedSites(apiPort, scan.sites, certPems);
248
248
  if (outcome.registered.length === 0) {
249
249
  return {
@@ -9,28 +9,28 @@ import {
9
9
  readDeclaredPair,
10
10
  readMaybeInContainer,
11
11
  validateCertFor
12
- } from "./chunk-IAMOUDHY.js";
12
+ } from "./chunk-AUBB5DLY.js";
13
13
  import {
14
14
  EDGE_CHALLENGE_HOST_DIR,
15
15
  EDGE_CONTAINER_MOUNTS,
16
16
  EDGE_HOST_PATHS,
17
17
  OPENRESTY_DEFAULT_PATHS
18
- } from "./chunk-7PEXM2UI.js";
18
+ } from "./chunk-BOD2IQ3P.js";
19
19
  import {
20
20
  privilegedExecutor,
21
21
  rootChecked,
22
22
  rootOrDegrade
23
- } from "./chunk-T46BQOLY.js";
23
+ } from "./chunk-RHHCAWW3.js";
24
24
  import {
25
25
  envOps,
26
26
  opScript
27
- } from "./chunk-GNOMEMNI.js";
27
+ } from "./chunk-3LWGDMJU.js";
28
28
  import {
29
29
  resolveEnvironment
30
- } from "./chunk-NPSRGIHV.js";
30
+ } from "./chunk-JWW5YAFL.js";
31
31
  import {
32
32
  sq
33
- } from "./chunk-P7RCYELG.js";
33
+ } from "./chunk-J2NDOB7D.js";
34
34
  import {
35
35
  EdgeConflictError,
36
36
  EdgeMigrateRequested,
@@ -45,16 +45,16 @@ import {
45
45
  sanitizeEdgeVhosts,
46
46
  sq as sq2,
47
47
  stopTargetsForStatus
48
- } from "./chunk-X53JXRS4.js";
48
+ } from "./chunk-YGA52DHX.js";
49
49
  import {
50
50
  waitForPortListening
51
- } from "./chunk-BO6CY4V2.js";
51
+ } from "./chunk-GEHV7JBS.js";
52
52
  import {
53
53
  EDGE_CONTAINER_NAME,
54
54
  probeExec,
55
55
  tryExec,
56
56
  withReason
57
- } from "./chunk-D6HU4VUZ.js";
57
+ } from "./chunk-CMGRMPRQ.js";
58
58
  import {
59
59
  formatDuration,
60
60
  systemDebug
@@ -70,7 +70,7 @@ import {
70
70
  safeErrorMessage,
71
71
  sanitizeProxySettings,
72
72
  shellQuote
73
- } from "./chunk-WEZFTL4V.js";
73
+ } from "./chunk-IVRTIKNZ.js";
74
74
 
75
75
  // ../../packages/adapters/src/system/managed-image.ts
76
76
  import { posix } from "path";
@@ -433,11 +433,18 @@ function parseUpstreams(config) {
433
433
  }
434
434
  return map;
435
435
  }
436
- function resolveProxyTarget(proxyPass, upstreams) {
436
+ var HTTP_PROXY_TARGET_RE = /^(https?:\/\/)([^/?#]+)([/?#].*)?$/i;
437
+ function resolveProxyTarget(proxyPass, upstreams, opts = {}) {
437
438
  const raw = proxyPass.replace(/;$/, "").trim();
438
- if (raw.includes("$")) return { reason: `proxy_pass "${raw}" uses an nginx variable` };
439
+ const parsed = raw.match(HTTP_PROXY_TARGET_RE);
440
+ if (raw.includes("$")) {
441
+ const authority2 = parsed?.[2];
442
+ if (!opts.allowVariablesAfterAuthority || !authority2 || authority2.includes("$")) {
443
+ return { reason: `proxy_pass "${raw}" uses an nginx variable` };
444
+ }
445
+ }
439
446
  if (/\/\/unix:/i.test(raw)) return { reason: `proxy_pass "${raw}" targets a unix socket` };
440
- const m = raw.match(/^(https?:\/\/)([^/]+)(\/.*)?$/i);
447
+ const m = parsed;
441
448
  if (!m) return { reason: `unrecognized proxy_pass "${raw}"` };
442
449
  const scheme = m[1];
443
450
  const authority = m[2];
@@ -460,13 +467,14 @@ function strictLoopbackUpstreamPorts(config) {
460
467
  for (const match of normalized.matchAll(/(?:^|[;{}\s])proxy_pass\s+([^;]+);/g)) {
461
468
  const raw = match[1]?.trim();
462
469
  if (!raw) continue;
463
- const resolved = resolveProxyTarget(raw, upstreams);
470
+ const resolved = resolveProxyTarget(raw, upstreams, { allowVariablesAfterAuthority: true });
464
471
  if ("reason" in resolved) {
465
472
  if (/\/\/unix:/i.test(raw)) continue;
466
473
  throw new Error(`Cannot inventory Openship edge routes: ${resolved.reason}`);
467
474
  }
468
- const authority = raw.match(/^https?:\/\/([^/]+)/i)?.[1];
469
- const scheme = raw.match(/^(https?:\/\/)/i)?.[1];
475
+ const parsed = raw.match(HTTP_PROXY_TARGET_RE);
476
+ const authority = parsed?.[2];
477
+ const scheme = parsed?.[1];
470
478
  const targets = authority && scheme && upstreams.has(authority) ? upstreams.get(authority).map((target) => `${scheme}${target}`) : [resolved.url];
471
479
  for (const target of targets) {
472
480
  let url;
@@ -511,12 +519,30 @@ function locationBlocks(serverBody) {
511
519
  return out;
512
520
  }
513
521
  function extractLocationProxies(serverBody) {
514
- const out = [];
522
+ const routes = [];
523
+ const unsupported = [];
524
+ let sawProxyLocation = false;
515
525
  for (const loc of locationBlocks(serverBody)) {
516
526
  const pp = firstDirective(loc.body, "proxy_pass");
517
- if (pp) out.push({ path: loc.path, proxyPass: pp });
527
+ if (!pp) continue;
528
+ sawProxyLocation = true;
529
+ const exact = loc.path.match(/^=\s+(.+)$/);
530
+ if (exact) {
531
+ routes.push({ path: exact[1].trim(), proxyPass: pp, exact: true });
532
+ continue;
533
+ }
534
+ const strongPrefix = loc.path.match(/^\^~\s+(.+)$/);
535
+ if (strongPrefix) {
536
+ routes.push({ path: strongPrefix[1].trim(), proxyPass: pp });
537
+ continue;
538
+ }
539
+ if (/^~\*?(?:\s|$)/.test(loc.path) || loc.path.startsWith("@")) {
540
+ unsupported.push(loc.path);
541
+ continue;
542
+ }
543
+ routes.push({ path: loc.path, proxyPass: pp });
518
544
  }
519
- return out;
545
+ return { routes, unsupported, sawProxyLocation };
520
546
  }
521
547
  function withoutQuotedValues(body) {
522
548
  return body.replace(/'[^'\n]*'/g, "''").replace(/"[^"\n]*"/g, '""');
@@ -591,8 +617,14 @@ function parseServer(body, source, upstreams, prefix) {
591
617
  const certPath = firstDirective(body, "ssl_certificate");
592
618
  const keyPath = firstDirective(body, "ssl_certificate_key");
593
619
  if (names.length === 0) return { warnings: [] };
594
- const rawTargets = extractLocationProxies(body);
595
- if (rawTargets.length === 0) {
620
+ const extracted = extractLocationProxies(body);
621
+ const rawTargets = extracted.routes;
622
+ for (const selector of extracted.unsupported) {
623
+ warnings.push(
624
+ `nginx: ${names[0]} location "${selector}" uses an unsupported regex or named selector (skipped)`
625
+ );
626
+ }
627
+ if (rawTargets.length === 0 && !extracted.sawProxyLocation) {
596
628
  const serverLevel = firstDirective(body, "proxy_pass");
597
629
  if (serverLevel) rawTargets.push({ path: "/", proxyPass: serverLevel });
598
630
  }
@@ -600,12 +632,12 @@ function parseServer(body, source, upstreams, prefix) {
600
632
  for (const t of rawTargets) {
601
633
  const r = resolveProxyTarget(t.proxyPass, upstreams);
602
634
  if ("reason" in r) warnings.push(`nginx: ${names[0]} ${t.path} \u2014 ${r.reason} (skipped)`);
603
- else resolved.push({ path: t.path, url: r.url });
635
+ else resolved.push({ path: t.path, url: r.url, ...t.exact ? { exact: true } : {} });
604
636
  }
605
637
  let target;
606
638
  let routes;
607
639
  if (resolved.length > 0) {
608
- const primary = resolved.find((r) => r.path === "/") ?? resolved[0];
640
+ const primary = resolved.find((r) => r.path === "/" && !r.exact) ?? resolved[0];
609
641
  target = { kind: "proxy", url: primary.url };
610
642
  routes = resolved;
611
643
  } else if (isHttpsUpgradeForSelf(body, names)) {
@@ -1589,7 +1621,9 @@ function buildProxyRouteIndex(sites) {
1589
1621
  }
1590
1622
  if (!Number.isFinite(port) || port <= 0) continue;
1591
1623
  const list = byPort.get(port) ?? [];
1592
- const existing = list.find((r) => r.path === up.path);
1624
+ const existing = list.find(
1625
+ (r) => r.path === up.path && Boolean(r.exact) === Boolean(up.exact)
1626
+ );
1593
1627
  if (existing) {
1594
1628
  existing.domains = [.../* @__PURE__ */ new Set([...existing.domains, ...site.serverNames])];
1595
1629
  if (site.ssl) {
@@ -1604,6 +1638,7 @@ function buildProxyRouteIndex(sites) {
1604
1638
  list.push({
1605
1639
  port,
1606
1640
  path: up.path,
1641
+ ...up.exact ? { exact: true } : {},
1607
1642
  domains: [...site.serverNames],
1608
1643
  ...site.proxy ? { proxy: site.proxy } : {},
1609
1644
  ssl: site.ssl ? { enabled: true, certPath: site.tls?.certPath, keyPath: site.tls?.keyPath } : { enabled: false },
@@ -2404,7 +2439,7 @@ function edgeHostExecutor(executor) {
2404
2439
  });
2405
2440
  }
2406
2441
  async function containerEdgeProvider(executor, container, opts) {
2407
- const { NginxProvider: Provider } = await import("./nginx-4U4C2XUS.js");
2442
+ const { NginxProvider: Provider } = await import("./nginx-JMLAS7YS.js");
2408
2443
  return new Provider({
2409
2444
  ...opts,
2410
2445
  // Vhosts go to the BIND-MOUNTED HOST dir (so every host-side reader — the
@@ -2428,8 +2463,8 @@ async function containerEdgeProvider(executor, container, opts) {
2428
2463
  });
2429
2464
  }
2430
2465
  async function localContainerEdgeProvider(container, opts) {
2431
- const { DockerEdgeExecutor } = await import("./docker-edge-executor-E5TOTLMR.js");
2432
- const { NginxProvider: Provider } = await import("./nginx-4U4C2XUS.js");
2466
+ const { DockerEdgeExecutor } = await import("./docker-edge-executor-HUQOWFQU.js");
2467
+ const { NginxProvider: Provider } = await import("./nginx-JMLAS7YS.js");
2433
2468
  return new Provider({
2434
2469
  ...opts,
2435
2470
  paths: OPENRESTY_DEFAULT_PATHS,
@@ -2462,25 +2497,34 @@ async function startEdgeContainer(executor, container, image, onLog, mounts) {
2462
2497
  invalidateEdgeContainer(executor);
2463
2498
  return run.code === 0;
2464
2499
  }
2500
+ var MOUNT_RESOLVE_MARKER = "__OPENSHIP_MOUNT__";
2465
2501
  async function resolveEdgeContainerMounts(executor) {
2466
- return Promise.all(
2467
- EDGE_CONTAINER_MOUNTS.map(async (mount) => {
2468
- let host;
2469
- try {
2470
- host = (await executor.exec(`cd ${sq(mount.host)} && pwd -P`)).trim();
2471
- } catch (err) {
2472
- throw new Error(
2473
- `Could not resolve edge bind-mount source ${mount.host} on the target host: ${safeErrorMessage(err)}`
2474
- );
2475
- }
2476
- if (!host.startsWith("/") || host.includes("\n")) {
2477
- throw new Error(
2478
- `Could not resolve edge bind-mount source ${mount.host} on the target host: expected one absolute path, received ${JSON.stringify(host)}.`
2479
- );
2480
- }
2481
- return { host, container: mount.container };
2482
- })
2483
- );
2502
+ const script = EDGE_CONTAINER_MOUNTS.map(
2503
+ (mount) => `echo ${MOUNT_RESOLVE_MARKER}; (cd ${sq(mount.host)} && pwd -P) 2>&1`
2504
+ ).join("; ") + "; true";
2505
+ let raw;
2506
+ try {
2507
+ raw = await executor.exec(script);
2508
+ } catch (err) {
2509
+ throw new Error(
2510
+ `Could not resolve edge bind-mount sources on the target host: ${safeErrorMessage(err)}`
2511
+ );
2512
+ }
2513
+ const segments = raw.split(MOUNT_RESOLVE_MARKER).slice(1);
2514
+ if (segments.length !== EDGE_CONTAINER_MOUNTS.length) {
2515
+ throw new Error(
2516
+ `Could not resolve edge bind-mount sources on the target host: expected ${EDGE_CONTAINER_MOUNTS.length} paths, received ${JSON.stringify(raw)}.`
2517
+ );
2518
+ }
2519
+ return EDGE_CONTAINER_MOUNTS.map((mount, i) => {
2520
+ const host = segments[i].trim();
2521
+ if (!host.startsWith("/") || host.includes("\n")) {
2522
+ throw new Error(
2523
+ `Could not resolve edge bind-mount source ${mount.host} on the target host: expected one absolute path, received ${JSON.stringify(host)}.`
2524
+ );
2525
+ }
2526
+ return { host, container: mount.container };
2527
+ });
2484
2528
  }
2485
2529
  function buildEdgeRunCommand(container, image, edgeMounts = EDGE_CONTAINER_MOUNTS) {
2486
2530
  const mounts = edgeMounts.map(
@@ -19,7 +19,7 @@ import {
19
19
  isCloudFrontedHost,
20
20
  luaSourceAvailable,
21
21
  reloadBareOpenResty
22
- } from "./chunk-7PEXM2UI.js";
22
+ } from "./chunk-BOD2IQ3P.js";
23
23
  import {
24
24
  edgeDownExplanation,
25
25
  explainEdgeDown,
@@ -30,17 +30,17 @@ import {
30
30
  } from "./chunk-R4VDPFLS.js";
31
31
  import {
32
32
  EXECUTOR_DELEGATE
33
- } from "./chunk-NPSRGIHV.js";
33
+ } from "./chunk-JWW5YAFL.js";
34
34
  import {
35
35
  logEntry,
36
36
  sq
37
- } from "./chunk-P7RCYELG.js";
37
+ } from "./chunk-J2NDOB7D.js";
38
38
  import {
39
39
  edgeFailureReason
40
- } from "./chunk-X53JXRS4.js";
40
+ } from "./chunk-YGA52DHX.js";
41
41
  import {
42
42
  probeListeningPort
43
- } from "./chunk-BO6CY4V2.js";
43
+ } from "./chunk-GEHV7JBS.js";
44
44
  import {
45
45
  PROXY_DIRECTIVES,
46
46
  parseProxyValue,
@@ -48,7 +48,7 @@ import {
48
48
  resolveRedirectStatus,
49
49
  safeErrorMessage,
50
50
  sanitizeProxySettings
51
- } from "./chunk-WEZFTL4V.js";
51
+ } from "./chunk-IVRTIKNZ.js";
52
52
 
53
53
  // ../../packages/adapters/src/infra/nginx.ts
54
54
  import {
@@ -92,8 +92,9 @@ async function readMaybeInContainer(exec, path, container) {
92
92
  if (!name) return "";
93
93
  return exec.exec(containerCommand(name, `cat ${sq(path)}`)).catch(() => "");
94
94
  }
95
- async function writeEdgeFileIn(inner, container, path, content) {
96
- await inner.writeFile(path, content);
95
+ async function writeEdgeFileIn(inner, container, path, content, opts) {
96
+ if (opts === void 0) await inner.writeFile(path, content);
97
+ else await inner.writeFile(path, content, opts);
97
98
  if (!container) return;
98
99
  const visible = await inner.exec(containerCommand(container, `test -e ${sq(path)} && echo visible`)).catch(() => "");
99
100
  if (visible.trim() === "visible") return;
@@ -149,7 +150,7 @@ ${result.output}` };
149
150
  if (opts?.files === "auto") {
150
151
  Object.assign(overrides, {
151
152
  readFile: (path) => readMaybeInContainer(inner, path, container),
152
- writeFile: (path, content) => writeEdgeFileIn(inner, container, path, content),
153
+ writeFile: (path, content, writeOpts) => writeEdgeFileIn(inner, container, path, content, writeOpts),
153
154
  exists: async (path) => {
154
155
  if (await inner.exists(path).catch(() => false)) return true;
155
156
  const { code } = await inner.streamExec(inContainer(`test -e ${sq(path)}`), () => {
@@ -381,7 +382,13 @@ var WEBHOOK_LOCATION_PREFIX = "/_openship/hooks/";
381
382
  function renderProxyLocations(route, opts) {
382
383
  if (!route.proxyLocations || route.proxyLocations.length === 0) return "";
383
384
  const head = `${opts.httpsUpgrade ? HTTPS_UPGRADE : ""}${renderRedirectRules(route, " ")}`;
384
- const claimed = /* @__PURE__ */ new Set(["/", WEBHOOK_LOCATION_PREFIX]);
385
+ const claimed = /* @__PURE__ */ new Set([
386
+ "prefix:/",
387
+ `prefix:${WEBHOOK_LOCATION_PREFIX}`,
388
+ // An exact webhook match would not be a duplicate, but it would outrank and
389
+ // hijack the edge-owned delivery endpoint just as surely as a prefix would.
390
+ `exact:${WEBHOOK_LOCATION_PREFIX}`
391
+ ]);
385
392
  const blocks = [];
386
393
  for (const loc of route.proxyLocations) {
387
394
  assertValidUpstream(loc.targetUrl);
@@ -389,10 +396,12 @@ function renderProxyLocations(route, opts) {
389
396
  const headers = loc.external ? externalProxyHeaders(loc.targetUrl) : PROXY_HEADERS;
390
397
  if (!loc.pattern || !loc.upstreamPath) {
391
398
  assertValidLiteralPath(loc.pathPrefix, "proxy location prefix");
392
- if (claimed.has(loc.pathPrefix)) continue;
393
- claimed.add(loc.pathPrefix);
399
+ const kind = loc.exact ? "exact" : "prefix";
400
+ const key = `${kind}:${loc.pathPrefix}`;
401
+ if (claimed.has(key)) continue;
402
+ claimed.add(key);
394
403
  blocks.push(`
395
- location ^~ ${loc.pathPrefix} {
404
+ location ${loc.exact ? "=" : "^~"} ${loc.pathPrefix} {
396
405
  ${head}proxy_pass ${loc.targetUrl};
397
406
  ${headers}
398
407
  }`);
@@ -403,8 +412,8 @@ function renderProxyLocations(route, opts) {
403
412
  assertNoNginxInjection(loc.upstreamPath, "rewrite upstream path");
404
413
  assertNoRuntimeVariable(loc.upstreamPath, "rewrite upstream path", true);
405
414
  const match = `^${toNginxPattern(loc.pattern)}$`;
406
- if (claimed.has(`~ ${match}`)) continue;
407
- claimed.add(`~ ${match}`);
415
+ if (claimed.has(`regex:${match}`)) continue;
416
+ claimed.add(`regex:${match}`);
408
417
  blocks.push(`
409
418
  location ~ ${match} {
410
419
  ${head}rewrite ${match} ${loc.upstreamPath} break;