@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/dist/parseArgv.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  applyServerArgv
3
- } from "./chunk-RIVFTOTI.js";
3
+ } from "./chunk-X3OSC5W3.js";
4
4
 
5
5
  // src/parseArgv.ts
6
6
  applyServerArgv();
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/parseArgv.ts"],"sourcesContent":["//? Side-effect-only entrypoint. Import this as the FIRST line of your\r\n//? `server.ts` so the positional CLI args (`<bundles> <port>`) are parsed\r\n//? and written into `process.env.SERVER_PORT` before any other module load\r\n//? reads it (notably `config.ts` which builds `backendUrl` at top level).\r\n//?\r\n//? Usage:\r\n//? import '@luckystack/server/parseArgv';\r\n\r\nimport { applyServerArgv } from './argv';\r\n\r\napplyServerArgv();\r\n"],"mappings":";;;;;AAUA,gBAAgB;","names":[]}
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":[]}
@@ -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`