@luckystack/server 0.7.2 → 0.7.3

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.
package/CHANGELOG.md CHANGED
@@ -7,6 +7,27 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [0.7.3] - 2026-07-20
11
+
12
+ ### Fixed
13
+
14
+ - **Stale-port bug class after a dev auto-increment hop.** When the dev server
15
+ auto-incremented off a busy port (`:80` → `:81`), the bind-address registry
16
+ still held the INTENDED port, so `checkOrigin`'s same-origin CORS entry pointed
17
+ at a port nothing listened on. `listenLuckyStackServer` now re-registers the
18
+ ACTUALLY-bound port inside the listen callback, so every `getBindAddress()`
19
+ reader (CORS foremost) sees reality.
20
+ - **OAuth now targets the actually-bound port after a hop.** The `/auth/api/<provider>`
21
+ authorize route rewrites a `localhost` OAuth `redirect_uri` to the port the
22
+ server bound (via core's `resolveDevCallbackUrl`), matching the token-exchange
23
+ side in `@luckystack/login` — so the OAuth round-trip reaches the live dev
24
+ server even after an auto-increment hop, instead of a frozen dead port.
25
+ - **Loud OAuth port-drift warning on a hop.** The framework auto-targets the bound
26
+ port for OAuth, but a provider console still exact-matches its registered
27
+ redirect URI — so the server now warns (naming both ports) to add the bound port
28
+ to the provider's authorized redirect URIs, or pin the port with
29
+ `SERVER_PORT_AUTO_INCREMENT=0`.
30
+
10
31
  ## [0.7.1] - 2026-07-18
11
32
 
12
33
  ### Fixed
package/dist/index.js CHANGED
@@ -7,7 +7,7 @@ import {
7
7
 
8
8
  // src/createServer.ts
9
9
  import http from "http";
10
- import { registerBindAddress, writeBootUuid, getLogger as getLogger13, getProjectConfig as getProjectConfig15, tryCatch as tryCatch10, isProduction as isProduction2, resolveEnvKey as resolveEnvKey5, dispatchHook as dispatchHook8 } from "@luckystack/core";
10
+ import { registerBindAddress, writeBootUuid, getLogger as getLogger13, getProjectConfig as getProjectConfig15, tryCatch as tryCatch10, tryCatchSync as tryCatchSync2, isProduction as isProduction2, resolveEnvKey as resolveEnvKey5, dispatchHook as dispatchHook8 } from "@luckystack/core";
11
11
 
12
12
  // src/httpHandler.ts
13
13
  import { randomUUID } from "crypto";
@@ -457,7 +457,8 @@ import {
457
457
  dispatchHook as dispatchHook2,
458
458
  getLogger,
459
459
  getProjectConfig as getProjectConfig5,
460
- resolveClientIp
460
+ resolveClientIp,
461
+ resolveDevCallbackUrl
461
462
  } from "@luckystack/core";
462
463
 
463
464
  // src/httpRoutes/sessionCookie.ts
@@ -561,7 +562,11 @@ var handleAuthApiRoute = async ({
561
562
  );
562
563
  const authParams = new URLSearchParams({
563
564
  client_id: provider.clientID,
564
- redirect_uri: provider.callbackURL,
565
+ //? In dev, target the port the server ACTUALLY bound (auto-increment may
566
+ //? have moved it off the frozen `oauthCallbackBase` port). The token
567
+ //? exchange applies the SAME rewrite, so the two redirect_uri values stay
568
+ //? byte-identical as OAuth requires. Prod / non-localhost: unchanged.
569
+ redirect_uri: resolveDevCallbackUrl(provider.callbackURL),
565
570
  scope: provider.scope.join(" "),
566
571
  response_type: "code",
567
572
  prompt: "select_account"
@@ -2365,10 +2370,20 @@ var listenLuckyStackServer = (httpServer, ip, port, callback) => new Promise((re
2365
2370
  httpServer.once("error", onError);
2366
2371
  httpServer.listen(attemptPort, ip, () => {
2367
2372
  httpServer.off("error", onError);
2373
+ registerBindAddress({ ip, port: attemptPort });
2368
2374
  if (!isProduction2 && process.env.NODE_ENV !== "test") {
2369
2375
  writeDevServerInfo(ip, attemptPort);
2370
2376
  process.once("exit", clearDevServerInfo);
2371
2377
  }
2378
+ if (attemptPort !== startPort && !isProduction2) {
2379
+ const configuredCallbackBase = tryCatchSync2(() => getProjectConfig15().oauthCallbackBase)[1] ?? "";
2380
+ const callbackPort = /:(\d+)(?:\/|$)/.exec(configuredCallbackBase)?.[1];
2381
+ if (callbackPort && callbackPort !== String(attemptPort)) {
2382
+ getLogger13().warn(
2383
+ `OAuth port drift: the server bound :${String(attemptPort)} but your OAuth callback base is configured for :${callbackPort} (${configuredCallbackBase}). The framework now auto-targets :${String(attemptPort)} for OAuth so the callback reaches THIS server \u2014 but your provider (Google/GitHub/\u2026) still exact-matches its registered redirect URI, so add :${String(attemptPort)} to the authorized redirect URIs, OR set SERVER_PORT_AUTO_INCREMENT=0 to pin :${callbackPort} (stop whatever holds it) instead of hopping.`
2384
+ );
2385
+ }
2386
+ }
2372
2387
  const config = getProjectConfig15();
2373
2388
  if (config.logging.socketStartup || config.logging.devLogs) {
2374
2389
  getLogger13().info(`Server is running on http://${ip}:${String(attemptPort)}/`);