@luckystack/server 0.8.0 → 0.8.2

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,18 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [0.8.2] - 2026-07-27
11
+
12
+ ### Fixed
13
+
14
+ - Processes can compose non-overlapping atomic presets whose generated maps repeat the same full function registry; equivalent function entries deduplicate while conflicting function implementations and duplicate API/sync ownership still fail closed.
15
+
16
+ ## [0.8.1] - 2026-07-27
17
+
18
+ ### Fixed
19
+
20
+ - Production route maps, dev-tool startup, cookie security, bootstrap checks and port auto-increment now follow `NODE_ENV`; named topology environments remain available for health and routing identity.
21
+
10
22
  ## [0.8.0] - 2026-07-27
11
23
 
12
24
  ### Added
package/CLAUDE.md CHANGED
@@ -36,7 +36,7 @@ One-call server bootstrap for a LuckyStack project. Wires together a raw Node.js
36
36
  | `getParsedBundles()` | Returns the preset list parsed by `applyServerArgv()` (empty array before first call). | -> docs/argv-parsing.md |
37
37
  | `getParsedPort()` | Returns the port parsed by `applyServerArgv()` (`null` if argv omitted it). | -> docs/argv-parsing.md |
38
38
  | `@luckystack/server/parseArgv` (side-effect import) | First-line import that runs `applyServerArgv()` before any module reads `process.env.SERVER_PORT` (notably `config.ts`). | -> docs/argv-parsing.md |
39
- | `createProdRuntimeMapsProvider(options)` | Build a `RuntimeMapsProvider` that loads generated maps in prod and delegates to devkit discovery in dev. Returns the provider without registering. | -> docs/runtime-maps.md |
39
+ | `createProdRuntimeMapsProvider(options)` | Build a `RuntimeMapsProvider` that loads generated maps in prod and delegates to devkit discovery in dev. Composed presets reject duplicate API/sync ownership, deduplicate equivalent full-registry function entries, and reject differing function implementations. Returns the provider without registering. | -> docs/runtime-maps.md |
40
40
  | `registerProdRuntimeMapsProvider(options)` | Convenience wrapper: builds the provider AND calls `registerRuntimeMapsProvider`. Most consumers want this. | -> docs/runtime-maps.md |
41
41
  | `registerCustomRoute(handler, options?)` | Append a custom HTTP route handler. `options.phase`: `'post-params'` (default, runs after body parse) or `'pre-params'` (runs before `getParams`, raw `req` stream intact — webhooks + streaming uploads). | -> /docs/ARCHITECTURE_HTTP.md |
42
42
  | `getCustomRoutes()` | Read the `'post-params'` registry snapshot (the default phase). | -> /docs/ARCHITECTURE_HTTP.md |
package/dist/index.d.ts CHANGED
@@ -110,7 +110,7 @@ interface CreateLuckyStackServerOptions {
110
110
  customRoutes?: CustomRouteHandler;
111
111
  /**
112
112
  * Enable dev-mode tooling (devkit hot reload, REPL, console init).
113
- * Defaults to `resolveEnvKey() !== 'production'`.
113
+ * Defaults to `resolveRuntimeMode() !== 'production'` (NODE_ENV-based).
114
114
  */
115
115
  enableDevTools?: boolean;
116
116
  /**
package/dist/index.js CHANGED
@@ -9,7 +9,7 @@ import {
9
9
 
10
10
  // src/createServer.ts
11
11
  import http from "http";
12
- import { registerBindAddress, registerBoundAddress, writeBootUuid, startBootUuidHeartbeat, getLogger as getLogger13, getProjectConfig as getProjectConfig15, tryCatch as tryCatch10, tryCatchSync as tryCatchSync3, resolveEnvKey as resolveEnvKey5, dispatchHook as dispatchHook8 } from "@luckystack/core";
12
+ import { registerBindAddress, registerBoundAddress, writeBootUuid, startBootUuidHeartbeat, getLogger as getLogger13, getProjectConfig as getProjectConfig15, tryCatch as tryCatch10, tryCatchSync as tryCatchSync3, resolveRuntimeMode, dispatchHook as dispatchHook8 } from "@luckystack/core";
13
13
 
14
14
  // src/httpHandler.ts
15
15
  import { randomUUID } from "crypto";
@@ -475,11 +475,11 @@ import {
475
475
  } from "@luckystack/core";
476
476
 
477
477
  // src/httpRoutes/sessionCookie.ts
478
- import { resolveEnvKey as resolveEnvKey2 } from "@luckystack/core";
478
+ import { isProductionRuntime } from "@luckystack/core";
479
479
  var resolveCookieSecure = (sessionCookieSecure, secureEnv) => {
480
480
  if (sessionCookieSecure !== void 0) return sessionCookieSecure;
481
481
  if (secureEnv === "true") return true;
482
- return resolveEnvKey2() === "production";
482
+ return isProductionRuntime();
483
483
  };
484
484
 
485
485
  // src/httpRoutes/authApiRoute.ts
@@ -2020,7 +2020,7 @@ import {
2020
2020
  isLocalizedNormalizerRegistered,
2021
2021
  isProjectConfigRegistered,
2022
2022
  isRuntimeMapsProviderRegistered,
2023
- resolveEnvKey as resolveEnvKey3
2023
+ isProductionRuntime as isProductionRuntime2
2024
2024
  } from "@luckystack/core";
2025
2025
  var verifyBootstrap = async (requirements = {}) => {
2026
2026
  const missing = [];
@@ -2059,7 +2059,7 @@ var verifyBootstrap = async (requirements = {}) => {
2059
2059
  }
2060
2060
  }
2061
2061
  if (!isRuntimeMapsProviderRegistered()) {
2062
- if (resolveEnvKey3() === "production") {
2062
+ if (isProductionRuntime2()) {
2063
2063
  missing.push(
2064
2064
  "RuntimeMapsProvider \u2014 call `registerRuntimeMapsProvider({...})` from `server/prod/runtimeMaps.ts`. Without it, every api/sync request returns notFound."
2065
2065
  );
@@ -2070,7 +2070,7 @@ var verifyBootstrap = async (requirements = {}) => {
2070
2070
  }
2071
2071
  }
2072
2072
  if (!isLocalizedNormalizerRegistered()) {
2073
- if (resolveEnvKey3() === "production") {
2073
+ if (isProductionRuntime2()) {
2074
2074
  missing.push(
2075
2075
  "LocalizedNormalizer \u2014 call `registerLocalizedNormalizer({...})` from your bootstrap. Without it, error response messages will be the raw errorCode (no i18n)."
2076
2076
  );
@@ -2113,7 +2113,7 @@ var verifyBootstrap = async (requirements = {}) => {
2113
2113
  import {
2114
2114
  getLogger as getLogger10,
2115
2115
  registerRuntimeMapsProvider,
2116
- resolveEnvKey as resolveEnvKey4
2116
+ isProductionRuntime as isProductionRuntime3
2117
2117
  } from "@luckystack/core";
2118
2118
  var emptyRuntimeMaps = {
2119
2119
  apisObject: {},
@@ -2151,10 +2151,20 @@ var resolvePresets = (options) => {
2151
2151
  }
2152
2152
  return ["default"];
2153
2153
  };
2154
+ var areEquivalentFunctionEntries = (left, right) => {
2155
+ if (Object.is(left, right)) return true;
2156
+ if (!isRuntimeMapRecord(left) || !isRuntimeMapRecord(right)) return false;
2157
+ const leftKeys = Object.keys(left);
2158
+ const rightKeys = Object.keys(right);
2159
+ return leftKeys.length === rightKeys.length && leftKeys.every((key) => Object.hasOwn(right, key) && Object.is(left[key], right[key]));
2160
+ };
2154
2161
  var mergeInto = (target, source, kind, fromPreset, keyOrigin) => {
2155
2162
  for (const key of Object.keys(source)) {
2156
2163
  const previousPreset = keyOrigin.get(key);
2157
2164
  if (previousPreset !== void 0 && previousPreset !== fromPreset) {
2165
+ if (kind === "function" && areEquivalentFunctionEntries(target[key], source[key])) {
2166
+ continue;
2167
+ }
2158
2168
  throw new Error(
2159
2169
  `[luckystack:runtimeMaps] ${kind} key collision: "${key}" present in both preset "${previousPreset}" and preset "${fromPreset}". Services must belong to exactly one preset (see docs/ARCHITECTURE_PACKAGING.md \xA710).`
2160
2170
  );
@@ -2163,7 +2173,7 @@ var mergeInto = (target, source, kind, fromPreset, keyOrigin) => {
2163
2173
  target[key] = source[key];
2164
2174
  }
2165
2175
  };
2166
- var isProduction = () => resolveEnvKey4() === "production";
2176
+ var isProduction = () => isProductionRuntime3();
2167
2177
  var createProdRuntimeMapsProvider = (options) => {
2168
2178
  let prodMapsPromise = null;
2169
2179
  let devkitModulePromise = null;
@@ -2371,9 +2381,9 @@ var initDevTools = async () => {
2371
2381
  };
2372
2382
  var listenLuckyStackServer = (httpServer, ip, port, callback) => new Promise((resolve, reject) => {
2373
2383
  const startPort = normalizeServerPort(port);
2374
- const environmentKey = resolveEnvKey5();
2375
- const production = environmentKey === "production";
2376
- const test = environmentKey === "test";
2384
+ const runtimeMode = resolveRuntimeMode();
2385
+ const production = runtimeMode === "production";
2386
+ const test = runtimeMode === "test";
2377
2387
  const autoIncrementEnv = (process.env.SERVER_PORT_AUTO_INCREMENT ?? "").toLowerCase();
2378
2388
  let autoIncrement;
2379
2389
  if (["0", "false"].includes(autoIncrementEnv)) autoIncrement = false;
@@ -2456,7 +2466,7 @@ var createLuckyStackServer = async (options = {}) => {
2456
2466
  envPort: process.env.SERVER_PORT
2457
2467
  });
2458
2468
  const ip = options.ip ?? process.env.SERVER_IP ?? "127.0.0.1";
2459
- const enableDevTools = options.enableDevTools ?? resolveEnvKey5() !== "production";
2469
+ const enableDevTools = options.enableDevTools ?? resolveRuntimeMode() !== "production";
2460
2470
  registerBindAddress({ ip, port });
2461
2471
  if (enableDevTools) {
2462
2472
  await initDevTools();