@playcademy/vite-plugin 1.1.3-beta.4 → 1.1.3-beta.6

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 (2) hide show
  1. package/dist/index.js +73 -19
  2. package/package.json +4 -4
package/dist/index.js CHANGED
@@ -24314,7 +24314,7 @@ import path2 from "node:path";
24314
24314
  // package.json
24315
24315
  var package_default = {
24316
24316
  name: "@playcademy/vite-plugin",
24317
- version: "1.1.3-beta.4",
24317
+ version: "1.1.3-beta.6",
24318
24318
  type: "module",
24319
24319
  exports: {
24320
24320
  ".": {
@@ -25845,7 +25845,7 @@ var package_default2;
25845
25845
  var init_package = __esm(() => {
25846
25846
  package_default2 = {
25847
25847
  name: "@playcademy/sandbox",
25848
- version: "0.6.1-beta.5",
25848
+ version: "0.6.1-beta.6",
25849
25849
  description: "Local development server for Playcademy game development",
25850
25850
  type: "module",
25851
25851
  exports: {
@@ -30547,6 +30547,14 @@ var init_schema = __esm(() => {
30547
30547
  platformServiceJwt: platformServiceJwtConfigSchema.optional(),
30548
30548
  uploadBucket: exports_external.string().optional(),
30549
30549
  queueIngressSecret: exports_external.string().optional()
30550
+ }).superRefine((config2, ctx) => {
30551
+ if (config2.isLocal && !config2.baseUrl) {
30552
+ ctx.addIssue({
30553
+ code: exports_external.ZodIssueCode.custom,
30554
+ path: ["baseUrl"],
30555
+ message: "baseUrl is required when isLocal is true"
30556
+ });
30557
+ }
30550
30558
  });
30551
30559
  });
30552
30560
  var init_config2 = __esm(() => {
@@ -51998,27 +52006,73 @@ var init_playcademy = __esm(() => {
51998
52006
  init_workers2();
51999
52007
  init_infra();
52000
52008
  });
52001
- async function getTunnelUrl() {
52002
- let response;
52009
+ function servicePort(service) {
52010
+ if (!service) {
52011
+ return null;
52012
+ }
52003
52013
  try {
52004
- response = await fetch(`${METRICS_BASE}/config`);
52014
+ const url = new URL(service);
52015
+ return url.port || (url.protocol === "https:" ? "443" : "80");
52005
52016
  } catch {
52006
- throw new Error("Local tunnel is not running. Start it with `bun dev` or `bun scripts/infra/tunnel.ts`.");
52017
+ return null;
52007
52018
  }
52008
- if (!response.ok) {
52009
- throw new Error(`Tunnel metrics endpoint returned ${response.status}`);
52019
+ }
52020
+ async function readTunnelIngress(port) {
52021
+ try {
52022
+ const response = await fetch(`http://127.0.0.1:${port}/config`, {
52023
+ signal: AbortSignal.timeout(1000)
52024
+ });
52025
+ if (!response.ok) {
52026
+ return null;
52027
+ }
52028
+ const data = await response.json();
52029
+ return data.config?.ingress ?? [];
52030
+ } catch {
52031
+ return null;
52032
+ }
52033
+ }
52034
+ function matchingHostname(rules, expectedPort) {
52035
+ for (const rule of rules ?? []) {
52036
+ if (rule.hostname && servicePort(rule.service) === expectedPort) {
52037
+ return rule.hostname;
52038
+ }
52039
+ }
52040
+ return null;
52041
+ }
52042
+ async function getTunnelUrl(platformBaseUrl) {
52043
+ const expectedPort = servicePort(platformBaseUrl);
52044
+ if (!expectedPort) {
52045
+ throw new Error(`Cannot resolve a tunnel for unparseable platform URL '${platformBaseUrl}'`);
52046
+ }
52047
+ const cachedPort = matchedPorts.get(expectedPort);
52048
+ if (cachedPort !== undefined) {
52049
+ const hostname = matchingHostname(await readTunnelIngress(cachedPort), expectedPort);
52050
+ if (hostname) {
52051
+ return `https://${hostname}`;
52052
+ }
52053
+ matchedPorts.delete(expectedPort);
52010
52054
  }
52011
- const data = await response.json();
52012
- const hostname = data.config.ingress.find((r) => r.hostname)?.hostname;
52013
- if (!hostname) {
52014
- throw new Error("Tunnel is running but no hostname found in ingress config");
52055
+ const ports = Array.from({ length: TUNNEL_METRICS_PORT_RANGE }, (_2, offset) => TUNNEL_METRICS_PORT + offset);
52056
+ const results = await Promise.all(ports.map(async (port) => ({ port, rules: await readTunnelIngress(port) })));
52057
+ for (const { port, rules } of results) {
52058
+ const hostname = matchingHostname(rules, expectedPort);
52059
+ if (hostname) {
52060
+ matchedPorts.set(expectedPort, port);
52061
+ return `https://${hostname}`;
52062
+ }
52015
52063
  }
52016
- return `https://${hostname}`;
52064
+ const foreign = results.flatMap(({ rules }) => (rules ?? []).filter((rule) => rule.hostname).map((rule) => `${rule.hostname} -> ${rule.service}`));
52065
+ if (foreign.length > 0) {
52066
+ throw new Error(`A local tunnel is running, but none route to the platform at ${platformBaseUrl} (found: ${foreign.join(", ")}). ${TUNNEL_START_HINT}`);
52067
+ }
52068
+ throw new Error(`Local tunnel is not running. ${TUNNEL_START_HINT}`);
52017
52069
  }
52018
52070
  var TUNNEL_METRICS_PORT = 20241;
52019
- var METRICS_BASE;
52071
+ var TUNNEL_METRICS_PORT_RANGE = 10;
52072
+ var TUNNEL_START_HINT = "Start this platform's tunnel with `bun dev` or `bun run tunnel`.";
52073
+ var matchedPorts;
52020
52074
  var init_tunnel = __esm(() => {
52021
- METRICS_BASE = `http://127.0.0.1:${TUNNEL_METRICS_PORT}`;
52075
+ matchedPorts = new Map;
52022
52076
  });
52023
52077
  function isPreviewStage(stage) {
52024
52078
  return PREVIEW_STAGE_PATTERN.test(stage);
@@ -52655,13 +52709,13 @@ class DeployService {
52655
52709
  return this.deps.config.baseUrl;
52656
52710
  }
52657
52711
  try {
52658
- return await getTunnelUrl();
52659
- } catch {
52712
+ return await getTunnelUrl(this.deps.config.baseUrl);
52713
+ } catch (error) {
52660
52714
  setAttributes({
52661
52715
  "app.deploy.tunnel_available": false,
52662
52716
  "app.deploy.failure_kind": "local_tunnel_unavailable"
52663
52717
  });
52664
- throw new ValidationError("Local tunnel is not running. Ensure cloudflared is installed (`brew install cloudflared`) and the tunnel DevCommand started successfully.");
52718
+ throw new ValidationError(errorMessage2(error));
52665
52719
  }
52666
52720
  }
52667
52721
  async deployToCloudflare(args2) {
@@ -169007,7 +169061,7 @@ var init_session_controller = __esm(() => {
169007
169061
  let baseUrl;
169008
169062
  if (ctx.config.isLocal) {
169009
169063
  try {
169010
- baseUrl = await getTunnelUrl();
169064
+ baseUrl = await getTunnelUrl(ctx.config.baseUrl);
169011
169065
  } catch {}
169012
169066
  }
169013
169067
  return { token, exp, baseUrl };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@playcademy/vite-plugin",
3
- "version": "1.1.3-beta.4",
3
+ "version": "1.1.3-beta.6",
4
4
  "type": "module",
5
5
  "exports": {
6
6
  ".": {
@@ -19,14 +19,14 @@
19
19
  "dependencies": {
20
20
  "archiver": "^7.0.1",
21
21
  "picocolors": "^1.1.1",
22
- "playcademy": "0.27.1-beta.4"
22
+ "playcademy": "0.27.1-beta.6"
23
23
  },
24
24
  "devDependencies": {
25
25
  "@electric-sql/pglite": "^0.3.16",
26
26
  "@inquirer/prompts": "^7.8.6",
27
27
  "@playcademy/constants": "0.0.1",
28
- "@playcademy/sandbox": "0.6.1-beta.5",
29
- "@playcademy/sdk": "0.15.1-beta.3",
28
+ "@playcademy/sandbox": "0.6.1-beta.6",
29
+ "@playcademy/sdk": "0.15.1-beta.4",
30
30
  "@playcademy/types": "0.0.1",
31
31
  "@playcademy/utils": "0.0.1",
32
32
  "@types/archiver": "^6.0.3",