@luckystack/server 0.6.7 → 0.7.1
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 +120 -87
- package/CLAUDE.md +87 -87
- package/LICENSE +21 -21
- package/README.md +198 -198
- package/dist/{chunk-RIVFTOTI.js → chunk-X3OSC5W3.js} +1 -1
- package/dist/chunk-X3OSC5W3.js.map +1 -0
- package/dist/index.js +44 -6
- package/dist/index.js.map +1 -1
- package/dist/parseArgv.js +1 -1
- package/dist/parseArgv.js.map +1 -1
- package/docs/argv-parsing.md +256 -256
- package/docs/create-server.md +295 -295
- package/docs/http-routes.md +318 -318
- package/docs/request-pipeline.md +198 -198
- package/docs/runtime-maps.md +206 -206
- package/docs/security-defaults.md +236 -236
- package/package.json +10 -10
- package/dist/chunk-RIVFTOTI.js.map +0 -1
package/dist/parseArgv.js
CHANGED
package/dist/parseArgv.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/parseArgv.ts"],"sourcesContent":["//? Side-effect-only entrypoint. Import this as the FIRST line of your\
|
|
1
|
+
{"version":3,"sources":["../src/parseArgv.ts"],"sourcesContent":["//? Side-effect-only entrypoint. Import this as the FIRST line of your\n//? `server.ts` so the positional CLI args (`<bundles> <port>`) are parsed\n//? and written into `process.env.SERVER_PORT` before any other module load\n//? reads it (notably `config.ts` which builds `backendUrl` at top level).\n//?\n//? Usage:\n//? import '@luckystack/server/parseArgv';\n\nimport { applyServerArgv } from './argv';\n\napplyServerArgv();\n"],"mappings":";;;;;AAUA,gBAAgB;","names":[]}
|
package/docs/argv-parsing.md
CHANGED
|
@@ -1,256 +1,256 @@
|
|
|
1
|
-
# Argv Parsing (`parseServerArgv` + `applyServerArgv`)
|
|
2
|
-
|
|
3
|
-
> Deep specs. Bron: `packages/server/src/argv.ts`, `packages/server/src/parseArgv.ts`. Bijgewerkt: 2026-05-20.
|
|
4
|
-
|
|
5
|
-
## Overview
|
|
6
|
-
|
|
7
|
-
`@luckystack/server` accepts two positional CLI arguments on boot:
|
|
8
|
-
|
|
9
|
-
```
|
|
10
|
-
npm run server -- <bundle[,bundle...]> [port]
|
|
11
|
-
```
|
|
12
|
-
|
|
13
|
-
- Arg 0 — preset list. Comma-separated; duplicates collapsed; runtime maps from each preset are shallow-merged at boot.
|
|
14
|
-
- Arg 1 — listen port. Numeric. Optional.
|
|
15
|
-
|
|
16
|
-
Argv replaces the legacy `LUCKYSTACK_BUNDLE` + `SERVER_PORT` environment toggles with one shape consumed by `createProdRuntimeMapsProvider` (preset) and `createLuckyStackServer` (port).
|
|
17
|
-
|
|
18
|
-
The module exposes:
|
|
19
|
-
|
|
20
|
-
- A pure parser: `parseServerArgv(argv)`.
|
|
21
|
-
- A side-effect runner that reads `process.argv.slice(2)` once and caches: `applyServerArgv()`.
|
|
22
|
-
- Read accessors: `getParsedBundles()`, `getParsedPort()`.
|
|
23
|
-
- A side-effect-only entrypoint `@luckystack/server/parseArgv` that simply imports `applyServerArgv` and runs it.
|
|
24
|
-
|
|
25
|
-
The side-effect entry MUST be the FIRST import in the consumer's `server.ts` because the parsed port is written back to `process.env.SERVER_PORT`, and downstream modules read that variable at top-level evaluation time:
|
|
26
|
-
|
|
27
|
-
- `@luckystack/core` env Zod schema
|
|
28
|
-
- `@luckystack/core` `bindAddress.ts` fallback
|
|
29
|
-
- The consumer's `config.ts` `backendUrl` constant
|
|
30
|
-
- `@luckystack/login` `oauthProviders.ts` callback URL builder
|
|
31
|
-
|
|
32
|
-
Importing anything that pulls one of those four before `parseArgv` runs will lock in the wrong port.
|
|
33
|
-
|
|
34
|
-
## API Reference
|
|
35
|
-
|
|
36
|
-
### `parseServerArgv(argv: string[]): ParsedServerArgv`
|
|
37
|
-
|
|
38
|
-
**Signature:**
|
|
39
|
-
|
|
40
|
-
```typescript
|
|
41
|
-
export interface ParsedServerArgv {
|
|
42
|
-
bundles: string[];
|
|
43
|
-
port: number | null;
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
export const parseServerArgv = (argv: string[]): ParsedServerArgv;
|
|
47
|
-
```
|
|
48
|
-
|
|
49
|
-
**Parameters:**
|
|
50
|
-
|
|
51
|
-
| Field | Type | Purpose |
|
|
52
|
-
| --- | --- | --- |
|
|
53
|
-
| `argv` | `string[]` | Positional args (typically `process.argv.slice(2)`). |
|
|
54
|
-
|
|
55
|
-
**Returns:** `{ bundles, port }`:
|
|
56
|
-
|
|
57
|
-
- `bundles: string[]` — deduplicated, trimmed, non-empty entries from arg 0. Empty array when arg 0 is missing or empty.
|
|
58
|
-
- `port: number | null` — `parseInt(argv[1], 10)` when arg 1 is supplied; `null` otherwise.
|
|
59
|
-
|
|
60
|
-
**Behavior:**
|
|
61
|
-
|
|
62
|
-
- Reject more than 2 positional arguments by throwing `Error('[luckystack:argv] unexpected positional argument(s): "<rest>". Usage: npm run server -- <bundle[,bundle...]> [port]')`.
|
|
63
|
-
- For arg 0: split on `,`, `trim()` each piece, drop falsy, collapse via `Array.from(new Set(...))`.
|
|
64
|
-
- For arg 1: must match `/^\d+$/`. Otherwise throws `Error('[luckystack:argv] port argument must be numeric, got: "<value>". Usage: npm run server -- <bundle[,bundle...]> [port]')`.
|
|
65
|
-
|
|
66
|
-
**Errors / Edge cases:**
|
|
67
|
-
|
|
68
|
-
- Whitespace in arg 0 (`"billing, vehicles"`) is supported — trimmed.
|
|
69
|
-
- A trailing comma (`"billing,"`) is silently dropped.
|
|
70
|
-
- A leading `0` in the port string is accepted (`/^\d+$/`) and parsed normally.
|
|
71
|
-
- An empty arg 0 (`""`) yields `bundles: []`; the downstream resolver falls back to `['default']`.
|
|
72
|
-
|
|
73
|
-
**Example:**
|
|
74
|
-
|
|
75
|
-
```typescript
|
|
76
|
-
parseServerArgv(['billing,vehicles', '4001']);
|
|
77
|
-
// => { bundles: ['billing', 'vehicles'], port: 4001 }
|
|
78
|
-
|
|
79
|
-
parseServerArgv([]);
|
|
80
|
-
// => { bundles: [], port: null }
|
|
81
|
-
|
|
82
|
-
parseServerArgv(['billing', '4001', 'oops']);
|
|
83
|
-
// => throws (too many positionals)
|
|
84
|
-
|
|
85
|
-
parseServerArgv(['billing', 'PORT']);
|
|
86
|
-
// => throws (non-numeric port)
|
|
87
|
-
```
|
|
88
|
-
|
|
89
|
-
---
|
|
90
|
-
|
|
91
|
-
### `applyServerArgv(): void`
|
|
92
|
-
|
|
93
|
-
**Signature:**
|
|
94
|
-
|
|
95
|
-
```typescript
|
|
96
|
-
export const applyServerArgv = (): void;
|
|
97
|
-
```
|
|
98
|
-
|
|
99
|
-
**Parameters:** none. Reads `process.argv.slice(2)`.
|
|
100
|
-
|
|
101
|
-
**Returns:** `void`.
|
|
102
|
-
|
|
103
|
-
**Behavior:**
|
|
104
|
-
|
|
105
|
-
- Idempotent. Subsequent calls return immediately via the module-level `hasRun` latch.
|
|
106
|
-
- First call:
|
|
107
|
-
1. `parseServerArgv(process.argv.slice(2))` (throws on malformed input).
|
|
108
|
-
2. Caches `bundles` + `port` in module state.
|
|
109
|
-
3. When `port !== null`, writes `process.env.SERVER_PORT = String(port)`. This is the writeback that lets the four downstream env-readers (listed in Overview) see the resolved port without per-call refactoring.
|
|
110
|
-
|
|
111
|
-
**Errors / Edge cases:**
|
|
112
|
-
|
|
113
|
-
- Throwing during this call aborts boot before any other module has a chance to read `SERVER_PORT`.
|
|
114
|
-
- Calling `applyServerArgv` after another module has already read `SERVER_PORT` is too late — that consumer has already captured the old value.
|
|
115
|
-
|
|
116
|
-
**Example:**
|
|
117
|
-
|
|
118
|
-
```typescript
|
|
119
|
-
// server.ts — first line
|
|
120
|
-
import '@luckystack/server/parseArgv';
|
|
121
|
-
// rest of bootstrap...
|
|
122
|
-
```
|
|
123
|
-
|
|
124
|
-
Or call it explicitly when you control the boot timing:
|
|
125
|
-
|
|
126
|
-
```typescript
|
|
127
|
-
import { applyServerArgv } from '@luckystack/server';
|
|
128
|
-
applyServerArgv();
|
|
129
|
-
```
|
|
130
|
-
|
|
131
|
-
---
|
|
132
|
-
|
|
133
|
-
### `getParsedBundles(): string[]`
|
|
134
|
-
|
|
135
|
-
**Signature:**
|
|
136
|
-
|
|
137
|
-
```typescript
|
|
138
|
-
export const getParsedBundles = (): string[];
|
|
139
|
-
```
|
|
140
|
-
|
|
141
|
-
**Returns:** the cached `bundles` array. Empty until `applyServerArgv()` has run.
|
|
142
|
-
|
|
143
|
-
**Behavior:**
|
|
144
|
-
|
|
145
|
-
- Read-only; never throws.
|
|
146
|
-
- Used by `createProdRuntimeMapsProvider` to resolve which preset(s) to load when neither `options.preset` nor a literal string is supplied.
|
|
147
|
-
|
|
148
|
-
**Example:**
|
|
149
|
-
|
|
150
|
-
```typescript
|
|
151
|
-
import { applyServerArgv, getParsedBundles } from '@luckystack/server';
|
|
152
|
-
|
|
153
|
-
applyServerArgv();
|
|
154
|
-
console.log(getParsedBundles()); // e.g. ['billing', 'vehicles']
|
|
155
|
-
```
|
|
156
|
-
|
|
157
|
-
---
|
|
158
|
-
|
|
159
|
-
### `getParsedPort(): number | null`
|
|
160
|
-
|
|
161
|
-
**Signature:**
|
|
162
|
-
|
|
163
|
-
```typescript
|
|
164
|
-
export const getParsedPort = (): number | null;
|
|
165
|
-
```
|
|
166
|
-
|
|
167
|
-
**Returns:** the cached `port`. `null` until `applyServerArgv()` has run with a numeric arg 1.
|
|
168
|
-
|
|
169
|
-
**Behavior:**
|
|
170
|
-
|
|
171
|
-
- Read-only; never throws.
|
|
172
|
-
- Consumed by `createLuckyStackServer` as one of the port-resolution fallbacks:
|
|
173
|
-
1. `options.port`
|
|
174
|
-
2. `getParsedPort()`
|
|
175
|
-
3. `process.env.SERVER_PORT`
|
|
176
|
-
4. `80`
|
|
177
|
-
|
|
178
|
-
**Example:**
|
|
179
|
-
|
|
180
|
-
```typescript
|
|
181
|
-
import { getParsedPort } from '@luckystack/server';
|
|
182
|
-
|
|
183
|
-
const port = getParsedPort();
|
|
184
|
-
if (port !== null) {
|
|
185
|
-
console.log(`argv supplied port ${port}`);
|
|
186
|
-
}
|
|
187
|
-
```
|
|
188
|
-
|
|
189
|
-
---
|
|
190
|
-
|
|
191
|
-
### Side-effect entrypoint: `@luckystack/server/parseArgv`
|
|
192
|
-
|
|
193
|
-
**Module body (verbatim):**
|
|
194
|
-
|
|
195
|
-
```typescript
|
|
196
|
-
import { applyServerArgv } from './argv';
|
|
197
|
-
|
|
198
|
-
applyServerArgv();
|
|
199
|
-
```
|
|
200
|
-
|
|
201
|
-
**Usage:** import as the FIRST line of your `server.ts`:
|
|
202
|
-
|
|
203
|
-
```typescript
|
|
204
|
-
import '@luckystack/server/parseArgv';
|
|
205
|
-
```
|
|
206
|
-
|
|
207
|
-
Anything that depends on `process.env.SERVER_PORT` MUST be imported below this line. Common pitfalls:
|
|
208
|
-
|
|
209
|
-
- Importing `'./config'` (which evaluates `backendUrl` at top level) before `parseArgv`.
|
|
210
|
-
- Importing `@luckystack/core` modules that pull the env Zod schema before `parseArgv`.
|
|
211
|
-
|
|
212
|
-
Both will freeze the wrong port into module-level constants.
|
|
213
|
-
|
|
214
|
-
## Resolution order summary
|
|
215
|
-
|
|
216
|
-
| Consumer | Source of port |
|
|
217
|
-
| --- | --- |
|
|
218
|
-
| `createLuckyStackServer` | `options.port` -> `getParsedPort()` -> `SERVER_PORT` -> `80` |
|
|
219
|
-
| `createLuckyStackServer` IP | `options.ip` -> `SERVER_IP` -> `127.0.0.1` |
|
|
220
|
-
| `createProdRuntimeMapsProvider` | `options.preset` (string -> single-entry array; non-empty array -> dedup) -> `getParsedBundles()` -> `['default']` |
|
|
221
|
-
|
|
222
|
-
## CLI examples
|
|
223
|
-
|
|
224
|
-
```bash
|
|
225
|
-
# Default preset, port 80
|
|
226
|
-
npm run server
|
|
227
|
-
|
|
228
|
-
# Single bundle, port 80
|
|
229
|
-
npm run server -- billing
|
|
230
|
-
|
|
231
|
-
# Two bundles merged, port 4001
|
|
232
|
-
npm run server -- billing,vehicles 4001
|
|
233
|
-
|
|
234
|
-
# Whitespace in bundle list is fine
|
|
235
|
-
npm run server -- "billing , vehicles" 4001
|
|
236
|
-
|
|
237
|
-
# Invalid port — boot aborts with descriptive error
|
|
238
|
-
npm run server -- billing PORT
|
|
239
|
-
# Error: [luckystack:argv] port argument must be numeric, got: "PORT".
|
|
240
|
-
|
|
241
|
-
# Extra positional — boot aborts
|
|
242
|
-
npm run server -- billing 4001 oops
|
|
243
|
-
# Error: [luckystack:argv] unexpected positional argument(s): "oops".
|
|
244
|
-
```
|
|
245
|
-
|
|
246
|
-
## Interaction with runtime maps
|
|
247
|
-
|
|
248
|
-
`createProdRuntimeMapsProvider({ loadGenerated, preset? })` calls `getParsedBundles()` when `preset` is omitted or empty. Each resolved preset is dynamically imported via the consumer-supplied `loadGenerated` callback, then shallow-merged into one runtime view. Key collisions across presets throw at boot. See `runtime-maps.md` for the merge semantics.
|
|
249
|
-
|
|
250
|
-
## Related
|
|
251
|
-
|
|
252
|
-
- Function INDEX: `packages/server/CLAUDE.md`
|
|
253
|
-
- Runtime maps: `packages/server/docs/runtime-maps.md`
|
|
254
|
-
- Create server: `packages/server/docs/create-server.md`
|
|
255
|
-
- Architecture: `docs/ARCHITECTURE_PACKAGING.md` (preset bundles, multi-service builds)
|
|
256
|
-
- README: `packages/server/README.md`
|
|
1
|
+
# Argv Parsing (`parseServerArgv` + `applyServerArgv`)
|
|
2
|
+
|
|
3
|
+
> Deep specs. Bron: `packages/server/src/argv.ts`, `packages/server/src/parseArgv.ts`. Bijgewerkt: 2026-05-20.
|
|
4
|
+
|
|
5
|
+
## Overview
|
|
6
|
+
|
|
7
|
+
`@luckystack/server` accepts two positional CLI arguments on boot:
|
|
8
|
+
|
|
9
|
+
```
|
|
10
|
+
npm run server -- <bundle[,bundle...]> [port]
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
- Arg 0 — preset list. Comma-separated; duplicates collapsed; runtime maps from each preset are shallow-merged at boot.
|
|
14
|
+
- Arg 1 — listen port. Numeric. Optional.
|
|
15
|
+
|
|
16
|
+
Argv replaces the legacy `LUCKYSTACK_BUNDLE` + `SERVER_PORT` environment toggles with one shape consumed by `createProdRuntimeMapsProvider` (preset) and `createLuckyStackServer` (port).
|
|
17
|
+
|
|
18
|
+
The module exposes:
|
|
19
|
+
|
|
20
|
+
- A pure parser: `parseServerArgv(argv)`.
|
|
21
|
+
- A side-effect runner that reads `process.argv.slice(2)` once and caches: `applyServerArgv()`.
|
|
22
|
+
- Read accessors: `getParsedBundles()`, `getParsedPort()`.
|
|
23
|
+
- A side-effect-only entrypoint `@luckystack/server/parseArgv` that simply imports `applyServerArgv` and runs it.
|
|
24
|
+
|
|
25
|
+
The side-effect entry MUST be the FIRST import in the consumer's `server.ts` because the parsed port is written back to `process.env.SERVER_PORT`, and downstream modules read that variable at top-level evaluation time:
|
|
26
|
+
|
|
27
|
+
- `@luckystack/core` env Zod schema
|
|
28
|
+
- `@luckystack/core` `bindAddress.ts` fallback
|
|
29
|
+
- The consumer's `config.ts` `backendUrl` constant
|
|
30
|
+
- `@luckystack/login` `oauthProviders.ts` callback URL builder
|
|
31
|
+
|
|
32
|
+
Importing anything that pulls one of those four before `parseArgv` runs will lock in the wrong port.
|
|
33
|
+
|
|
34
|
+
## API Reference
|
|
35
|
+
|
|
36
|
+
### `parseServerArgv(argv: string[]): ParsedServerArgv`
|
|
37
|
+
|
|
38
|
+
**Signature:**
|
|
39
|
+
|
|
40
|
+
```typescript
|
|
41
|
+
export interface ParsedServerArgv {
|
|
42
|
+
bundles: string[];
|
|
43
|
+
port: number | null;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export const parseServerArgv = (argv: string[]): ParsedServerArgv;
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
**Parameters:**
|
|
50
|
+
|
|
51
|
+
| Field | Type | Purpose |
|
|
52
|
+
| --- | --- | --- |
|
|
53
|
+
| `argv` | `string[]` | Positional args (typically `process.argv.slice(2)`). |
|
|
54
|
+
|
|
55
|
+
**Returns:** `{ bundles, port }`:
|
|
56
|
+
|
|
57
|
+
- `bundles: string[]` — deduplicated, trimmed, non-empty entries from arg 0. Empty array when arg 0 is missing or empty.
|
|
58
|
+
- `port: number | null` — `parseInt(argv[1], 10)` when arg 1 is supplied; `null` otherwise.
|
|
59
|
+
|
|
60
|
+
**Behavior:**
|
|
61
|
+
|
|
62
|
+
- Reject more than 2 positional arguments by throwing `Error('[luckystack:argv] unexpected positional argument(s): "<rest>". Usage: npm run server -- <bundle[,bundle...]> [port]')`.
|
|
63
|
+
- For arg 0: split on `,`, `trim()` each piece, drop falsy, collapse via `Array.from(new Set(...))`.
|
|
64
|
+
- For arg 1: must match `/^\d+$/`. Otherwise throws `Error('[luckystack:argv] port argument must be numeric, got: "<value>". Usage: npm run server -- <bundle[,bundle...]> [port]')`.
|
|
65
|
+
|
|
66
|
+
**Errors / Edge cases:**
|
|
67
|
+
|
|
68
|
+
- Whitespace in arg 0 (`"billing, vehicles"`) is supported — trimmed.
|
|
69
|
+
- A trailing comma (`"billing,"`) is silently dropped.
|
|
70
|
+
- A leading `0` in the port string is accepted (`/^\d+$/`) and parsed normally.
|
|
71
|
+
- An empty arg 0 (`""`) yields `bundles: []`; the downstream resolver falls back to `['default']`.
|
|
72
|
+
|
|
73
|
+
**Example:**
|
|
74
|
+
|
|
75
|
+
```typescript
|
|
76
|
+
parseServerArgv(['billing,vehicles', '4001']);
|
|
77
|
+
// => { bundles: ['billing', 'vehicles'], port: 4001 }
|
|
78
|
+
|
|
79
|
+
parseServerArgv([]);
|
|
80
|
+
// => { bundles: [], port: null }
|
|
81
|
+
|
|
82
|
+
parseServerArgv(['billing', '4001', 'oops']);
|
|
83
|
+
// => throws (too many positionals)
|
|
84
|
+
|
|
85
|
+
parseServerArgv(['billing', 'PORT']);
|
|
86
|
+
// => throws (non-numeric port)
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
---
|
|
90
|
+
|
|
91
|
+
### `applyServerArgv(): void`
|
|
92
|
+
|
|
93
|
+
**Signature:**
|
|
94
|
+
|
|
95
|
+
```typescript
|
|
96
|
+
export const applyServerArgv = (): void;
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
**Parameters:** none. Reads `process.argv.slice(2)`.
|
|
100
|
+
|
|
101
|
+
**Returns:** `void`.
|
|
102
|
+
|
|
103
|
+
**Behavior:**
|
|
104
|
+
|
|
105
|
+
- Idempotent. Subsequent calls return immediately via the module-level `hasRun` latch.
|
|
106
|
+
- First call:
|
|
107
|
+
1. `parseServerArgv(process.argv.slice(2))` (throws on malformed input).
|
|
108
|
+
2. Caches `bundles` + `port` in module state.
|
|
109
|
+
3. When `port !== null`, writes `process.env.SERVER_PORT = String(port)`. This is the writeback that lets the four downstream env-readers (listed in Overview) see the resolved port without per-call refactoring.
|
|
110
|
+
|
|
111
|
+
**Errors / Edge cases:**
|
|
112
|
+
|
|
113
|
+
- Throwing during this call aborts boot before any other module has a chance to read `SERVER_PORT`.
|
|
114
|
+
- Calling `applyServerArgv` after another module has already read `SERVER_PORT` is too late — that consumer has already captured the old value.
|
|
115
|
+
|
|
116
|
+
**Example:**
|
|
117
|
+
|
|
118
|
+
```typescript
|
|
119
|
+
// server.ts — first line
|
|
120
|
+
import '@luckystack/server/parseArgv';
|
|
121
|
+
// rest of bootstrap...
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
Or call it explicitly when you control the boot timing:
|
|
125
|
+
|
|
126
|
+
```typescript
|
|
127
|
+
import { applyServerArgv } from '@luckystack/server';
|
|
128
|
+
applyServerArgv();
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
---
|
|
132
|
+
|
|
133
|
+
### `getParsedBundles(): string[]`
|
|
134
|
+
|
|
135
|
+
**Signature:**
|
|
136
|
+
|
|
137
|
+
```typescript
|
|
138
|
+
export const getParsedBundles = (): string[];
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
**Returns:** the cached `bundles` array. Empty until `applyServerArgv()` has run.
|
|
142
|
+
|
|
143
|
+
**Behavior:**
|
|
144
|
+
|
|
145
|
+
- Read-only; never throws.
|
|
146
|
+
- Used by `createProdRuntimeMapsProvider` to resolve which preset(s) to load when neither `options.preset` nor a literal string is supplied.
|
|
147
|
+
|
|
148
|
+
**Example:**
|
|
149
|
+
|
|
150
|
+
```typescript
|
|
151
|
+
import { applyServerArgv, getParsedBundles } from '@luckystack/server';
|
|
152
|
+
|
|
153
|
+
applyServerArgv();
|
|
154
|
+
console.log(getParsedBundles()); // e.g. ['billing', 'vehicles']
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
---
|
|
158
|
+
|
|
159
|
+
### `getParsedPort(): number | null`
|
|
160
|
+
|
|
161
|
+
**Signature:**
|
|
162
|
+
|
|
163
|
+
```typescript
|
|
164
|
+
export const getParsedPort = (): number | null;
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
**Returns:** the cached `port`. `null` until `applyServerArgv()` has run with a numeric arg 1.
|
|
168
|
+
|
|
169
|
+
**Behavior:**
|
|
170
|
+
|
|
171
|
+
- Read-only; never throws.
|
|
172
|
+
- Consumed by `createLuckyStackServer` as one of the port-resolution fallbacks:
|
|
173
|
+
1. `options.port`
|
|
174
|
+
2. `getParsedPort()`
|
|
175
|
+
3. `process.env.SERVER_PORT`
|
|
176
|
+
4. `80`
|
|
177
|
+
|
|
178
|
+
**Example:**
|
|
179
|
+
|
|
180
|
+
```typescript
|
|
181
|
+
import { getParsedPort } from '@luckystack/server';
|
|
182
|
+
|
|
183
|
+
const port = getParsedPort();
|
|
184
|
+
if (port !== null) {
|
|
185
|
+
console.log(`argv supplied port ${port}`);
|
|
186
|
+
}
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
---
|
|
190
|
+
|
|
191
|
+
### Side-effect entrypoint: `@luckystack/server/parseArgv`
|
|
192
|
+
|
|
193
|
+
**Module body (verbatim):**
|
|
194
|
+
|
|
195
|
+
```typescript
|
|
196
|
+
import { applyServerArgv } from './argv';
|
|
197
|
+
|
|
198
|
+
applyServerArgv();
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
**Usage:** import as the FIRST line of your `server.ts`:
|
|
202
|
+
|
|
203
|
+
```typescript
|
|
204
|
+
import '@luckystack/server/parseArgv';
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
Anything that depends on `process.env.SERVER_PORT` MUST be imported below this line. Common pitfalls:
|
|
208
|
+
|
|
209
|
+
- Importing `'./config'` (which evaluates `backendUrl` at top level) before `parseArgv`.
|
|
210
|
+
- Importing `@luckystack/core` modules that pull the env Zod schema before `parseArgv`.
|
|
211
|
+
|
|
212
|
+
Both will freeze the wrong port into module-level constants.
|
|
213
|
+
|
|
214
|
+
## Resolution order summary
|
|
215
|
+
|
|
216
|
+
| Consumer | Source of port |
|
|
217
|
+
| --- | --- |
|
|
218
|
+
| `createLuckyStackServer` | `options.port` -> `getParsedPort()` -> `SERVER_PORT` -> `80` |
|
|
219
|
+
| `createLuckyStackServer` IP | `options.ip` -> `SERVER_IP` -> `127.0.0.1` |
|
|
220
|
+
| `createProdRuntimeMapsProvider` | `options.preset` (string -> single-entry array; non-empty array -> dedup) -> `getParsedBundles()` -> `['default']` |
|
|
221
|
+
|
|
222
|
+
## CLI examples
|
|
223
|
+
|
|
224
|
+
```bash
|
|
225
|
+
# Default preset, port 80
|
|
226
|
+
npm run server
|
|
227
|
+
|
|
228
|
+
# Single bundle, port 80
|
|
229
|
+
npm run server -- billing
|
|
230
|
+
|
|
231
|
+
# Two bundles merged, port 4001
|
|
232
|
+
npm run server -- billing,vehicles 4001
|
|
233
|
+
|
|
234
|
+
# Whitespace in bundle list is fine
|
|
235
|
+
npm run server -- "billing , vehicles" 4001
|
|
236
|
+
|
|
237
|
+
# Invalid port — boot aborts with descriptive error
|
|
238
|
+
npm run server -- billing PORT
|
|
239
|
+
# Error: [luckystack:argv] port argument must be numeric, got: "PORT".
|
|
240
|
+
|
|
241
|
+
# Extra positional — boot aborts
|
|
242
|
+
npm run server -- billing 4001 oops
|
|
243
|
+
# Error: [luckystack:argv] unexpected positional argument(s): "oops".
|
|
244
|
+
```
|
|
245
|
+
|
|
246
|
+
## Interaction with runtime maps
|
|
247
|
+
|
|
248
|
+
`createProdRuntimeMapsProvider({ loadGenerated, preset? })` calls `getParsedBundles()` when `preset` is omitted or empty. Each resolved preset is dynamically imported via the consumer-supplied `loadGenerated` callback, then shallow-merged into one runtime view. Key collisions across presets throw at boot. See `runtime-maps.md` for the merge semantics.
|
|
249
|
+
|
|
250
|
+
## Related
|
|
251
|
+
|
|
252
|
+
- Function INDEX: `packages/server/CLAUDE.md`
|
|
253
|
+
- Runtime maps: `packages/server/docs/runtime-maps.md`
|
|
254
|
+
- Create server: `packages/server/docs/create-server.md`
|
|
255
|
+
- Architecture: `docs/ARCHITECTURE_PACKAGING.md` (preset bundles, multi-service builds)
|
|
256
|
+
- README: `packages/server/README.md`
|