@getstrata/bootstrap 2.2.0 → 2.2.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 CHANGED
@@ -1,5 +1,12 @@
1
1
  # @getstrata/bootstrap changelog
2
2
 
3
+ ## 2.2.1
4
+
5
+ F02 — complete queue durability. Queues have leased reservations, recovery and fenced acknowledgement, but still use Redis lists. Persisted retry schedules, execution deadlines, cancellation and the planned Streams migration remain unfinished. Retries currently wait inside the running worker.
6
+ F10 — production public-route tenancy. Separate anonymous access from development tenant headers, support a trusted-host resolver, reject unknown hosts, and accept forwarded hosts only through configured trusted proxies. The shop still lacks the approved-domain mapping.
7
+ F12 — initial administrator provisioning and migration guidance. Automatic demo seeding is fixed. Secure initial-admin provisioning and a consolidated upgrade/operations guide remain.
8
+ Distributed qualification. Cache generations/fill leases, scheduler ownership, OpenTelemetry, body limits and lifecycle controls now exist. Their implementation should no longer be described as entirely pending, but broader failure and capacity qualification is still required.
9
+
3
10
  ## 2.2.0
4
11
 
5
12
  Consistent model scopes.
@@ -4,7 +4,7 @@ declare const CACHE_MAX_ENTRIES_CONFIG_KEY = "cache.maxEntries";
4
4
  declare const CACHE_DRIVER_CONFIG_KEY = "cache.driver";
5
5
  declare const REDIS_URL_CONFIG_KEY = "cache.redisUrl";
6
6
  declare const DATABASE_URL_CONFIG_KEY = "database.url";
7
- export { CORE_ABILITY_CHECKER_TOKEN, CORE_AUTH_TOKEN, CORE_AUTH_USER_DIRECTORY_TOKEN, CORE_CACHE_TOKEN, CORE_CONFIG_TOKEN, CORE_EVENT_BUS_TOKEN, CORE_HTTP_CLEANUP_TOKEN, CORE_POLICY_GATE_TOKEN, CORE_QUEUE_TOKEN, CORE_TENANT_RESOLVER_TOKEN, CORE_THROTTLE_QUOTA_POLICY_TOKEN, CORE_TOKEN_SERVICE_TOKEN, } from "@getstrata/core/contracts/serviceTokens";
7
+ export { CORE_ABILITY_CHECKER_TOKEN, CORE_AUTH_TOKEN, CORE_AUTH_USER_DIRECTORY_TOKEN, CORE_CACHE_TOKEN, CORE_CONFIG_TOKEN, CORE_EVENT_BUS_TOKEN, CORE_HTTP_CLEANUP_TOKEN, CORE_POLICY_GATE_TOKEN, CORE_PUBLIC_TENANCY_TOKEN, CORE_QUEUE_TOKEN, CORE_TENANT_RESOLVER_TOKEN, CORE_THROTTLE_QUOTA_POLICY_TOKEN, CORE_TOKEN_SERVICE_TOKEN, } from "@getstrata/core/contracts/serviceTokens";
8
8
  declare const AUTH_DEV_HEADERS_CONFIG_KEY = "auth.allowDevHeaders";
9
9
  declare const DEFAULT_APP_PORT = 3000;
10
10
  declare const DEFAULT_CACHE_TTL_MS = 3600000;
@@ -19,7 +19,9 @@ declare class HttpKernel {
19
19
  wrapWeb<TRequest extends Request = Request>(handler: RouteHandler<TRequest>): RouteHandler<TRequest>;
20
20
  /** Signed-in HTML users redirect to `home` instead of seeing guest pages. */
21
21
  wrapWebGuest<TRequest extends Request = Request>(handler: RouteHandler<TRequest>, home?: WebGuestHome): RouteHandler<TRequest>;
22
- wrapWebPublicRead<TRequest extends Request = Request>(handler: RouteHandler<TRequest>): RouteHandler<TRequest>;
22
+ wrapWebPublicRead<TRequest extends Request = Request>(handler: RouteHandler<TRequest>, options?: {
23
+ allowAnonymous?: boolean;
24
+ }): RouteHandler<TRequest>;
23
25
  wrapWebAuthenticated<TRequest extends Request = Request>(handler: RouteHandler<TRequest>): RouteHandler<TRequest>;
24
26
  /** Signed-in HTML without the email-verified gate (logout, verification notice). */
25
27
  wrapWebAuthenticatedAllowUnverified<TRequest extends Request = Request>(handler: RouteHandler<TRequest>): RouteHandler<TRequest>;
@@ -31,7 +33,9 @@ declare class HttpKernel {
31
33
  wrapAuthenticated<TRequest extends Request = Request>(handler: RouteHandler<TRequest>): RouteHandler<TRequest>;
32
34
  /** JSON routes that require a verified email. No-op when `FEATURE_EMAIL_VERIFICATION` is off. */
33
35
  wrapVerified<TRequest extends Request = Request>(handler: RouteHandler<TRequest>): RouteHandler<TRequest>;
34
- wrapPublicRead<TRequest extends Request = Request>(handler: RouteHandler<TRequest>): RouteHandler<TRequest>;
36
+ wrapPublicRead<TRequest extends Request = Request>(handler: RouteHandler<TRequest>, options?: {
37
+ allowAnonymous?: boolean;
38
+ }): RouteHandler<TRequest>;
35
39
  wrapGlobalAdmin<TRequest extends Request = Request>(handler: RouteHandler<TRequest>): RouteHandler<TRequest>;
36
40
  wrapAbility<TRequest extends Request = Request>(ability: string, handler: RouteHandler<TRequest>): RouteHandler<TRequest>;
37
41
  wrapPolicy<TRequest extends Request = Request>(resource: string, action: keyof Policy, handler: RouteHandler<TRequest>): RouteHandler<TRequest>;
@@ -52,6 +52,7 @@ import {
52
52
  CORE_EVENT_BUS_TOKEN,
53
53
  CORE_HTTP_CLEANUP_TOKEN,
54
54
  CORE_POLICY_GATE_TOKEN,
55
+ CORE_PUBLIC_TENANCY_TOKEN,
55
56
  CORE_QUEUE_TOKEN,
56
57
  CORE_TENANT_RESOLVER_TOKEN,
57
58
  CORE_THROTTLE_QUOTA_POLICY_TOKEN,
@@ -168,6 +169,7 @@ class HttpKernel {
168
169
  createAuthMiddleware(auth),
169
170
  createMembershipMiddleware(),
170
171
  createTenantMiddleware({
172
+ publicTenancy: this.dependencies.container.has(CORE_PUBLIC_TENANCY_TOKEN) ? this.dependencies.container.resolve(CORE_PUBLIC_TENANCY_TOKEN) : undefined,
171
173
  resolveTenant: this.dependencies.container.has(CORE_TENANT_RESOLVER_TOKEN) ? this.dependencies.container.resolve(CORE_TENANT_RESOLVER_TOKEN) : undefined
172
174
  })
173
175
  ];
@@ -242,8 +244,8 @@ class HttpKernel {
242
244
  return handler(request);
243
245
  });
244
246
  }
245
- wrapWebPublicRead(handler) {
246
- if (isPublicReadsEnabled()) {
247
+ wrapWebPublicRead(handler, options = {}) {
248
+ if (options.allowAnonymous ?? isPublicReadsEnabled()) {
247
249
  return this.wrapWeb(handler);
248
250
  }
249
251
  return this.wrapWebAuthenticated(handler);
@@ -289,8 +291,8 @@ class HttpKernel {
289
291
  const middleware = [...this.group("authenticated"), ...this.verifiedMiddleware()];
290
292
  return withMiddleware(...middleware)(handler);
291
293
  }
292
- wrapPublicRead(handler) {
293
- if (isPublicReadsEnabled()) {
294
+ wrapPublicRead(handler, options = {}) {
295
+ if (options.allowAnonymous ?? isPublicReadsEnabled()) {
294
296
  return handler;
295
297
  }
296
298
  return this.wrapAuthenticated(handler);
@@ -55,6 +55,7 @@ import {
55
55
  CORE_EVENT_BUS_TOKEN,
56
56
  CORE_HTTP_CLEANUP_TOKEN,
57
57
  CORE_POLICY_GATE_TOKEN,
58
+ CORE_PUBLIC_TENANCY_TOKEN,
58
59
  CORE_QUEUE_TOKEN,
59
60
  CORE_TENANT_RESOLVER_TOKEN,
60
61
  CORE_THROTTLE_QUOTA_POLICY_TOKEN,
@@ -171,6 +172,7 @@ class HttpKernel {
171
172
  createAuthMiddleware(auth),
172
173
  createMembershipMiddleware(),
173
174
  createTenantMiddleware({
175
+ publicTenancy: this.dependencies.container.has(CORE_PUBLIC_TENANCY_TOKEN) ? this.dependencies.container.resolve(CORE_PUBLIC_TENANCY_TOKEN) : undefined,
174
176
  resolveTenant: this.dependencies.container.has(CORE_TENANT_RESOLVER_TOKEN) ? this.dependencies.container.resolve(CORE_TENANT_RESOLVER_TOKEN) : undefined
175
177
  })
176
178
  ];
@@ -245,8 +247,8 @@ class HttpKernel {
245
247
  return handler(request);
246
248
  });
247
249
  }
248
- wrapWebPublicRead(handler) {
249
- if (isPublicReadsEnabled()) {
250
+ wrapWebPublicRead(handler, options = {}) {
251
+ if (options.allowAnonymous ?? isPublicReadsEnabled()) {
250
252
  return this.wrapWeb(handler);
251
253
  }
252
254
  return this.wrapWebAuthenticated(handler);
@@ -292,8 +294,8 @@ class HttpKernel {
292
294
  const middleware = [...this.group("authenticated"), ...this.verifiedMiddleware()];
293
295
  return withMiddleware(...middleware)(handler);
294
296
  }
295
- wrapPublicRead(handler) {
296
- if (isPublicReadsEnabled()) {
297
+ wrapPublicRead(handler, options = {}) {
298
+ if (options.allowAnonymous ?? isPublicReadsEnabled()) {
297
299
  return handler;
298
300
  }
299
301
  return this.wrapAuthenticated(handler);
@@ -9,6 +9,7 @@ import {
9
9
  CORE_EVENT_BUS_TOKEN,
10
10
  CORE_HTTP_CLEANUP_TOKEN,
11
11
  CORE_POLICY_GATE_TOKEN,
12
+ CORE_PUBLIC_TENANCY_TOKEN,
12
13
  CORE_QUEUE_TOKEN,
13
14
  CORE_TENANT_RESOLVER_TOKEN,
14
15
  CORE_THROTTLE_QUOTA_POLICY_TOKEN,
@@ -41,6 +42,7 @@ export {
41
42
  CORE_EVENT_BUS_TOKEN,
42
43
  CORE_HTTP_CLEANUP_TOKEN,
43
44
  CORE_POLICY_GATE_TOKEN,
45
+ CORE_PUBLIC_TENANCY_TOKEN,
44
46
  CORE_QUEUE_TOKEN,
45
47
  CORE_TENANT_RESOLVER_TOKEN,
46
48
  CORE_THROTTLE_QUOTA_POLICY_TOKEN,
@@ -232,6 +232,7 @@ import {
232
232
  CORE_EVENT_BUS_TOKEN,
233
233
  CORE_HTTP_CLEANUP_TOKEN,
234
234
  CORE_POLICY_GATE_TOKEN,
235
+ CORE_PUBLIC_TENANCY_TOKEN,
235
236
  CORE_QUEUE_TOKEN,
236
237
  CORE_TENANT_RESOLVER_TOKEN,
237
238
  CORE_THROTTLE_QUOTA_POLICY_TOKEN,
@@ -61,6 +61,7 @@ import {
61
61
  CORE_EVENT_BUS_TOKEN,
62
62
  CORE_HTTP_CLEANUP_TOKEN,
63
63
  CORE_POLICY_GATE_TOKEN,
64
+ CORE_PUBLIC_TENANCY_TOKEN,
64
65
  CORE_QUEUE_TOKEN,
65
66
  CORE_TENANT_RESOLVER_TOKEN,
66
67
  CORE_THROTTLE_QUOTA_POLICY_TOKEN,
@@ -177,6 +178,7 @@ class HttpKernel {
177
178
  createAuthMiddleware(auth),
178
179
  createMembershipMiddleware(),
179
180
  createTenantMiddleware({
181
+ publicTenancy: this.dependencies.container.has(CORE_PUBLIC_TENANCY_TOKEN) ? this.dependencies.container.resolve(CORE_PUBLIC_TENANCY_TOKEN) : undefined,
180
182
  resolveTenant: this.dependencies.container.has(CORE_TENANT_RESOLVER_TOKEN) ? this.dependencies.container.resolve(CORE_TENANT_RESOLVER_TOKEN) : undefined
181
183
  })
182
184
  ];
@@ -251,8 +253,8 @@ class HttpKernel {
251
253
  return handler(request);
252
254
  });
253
255
  }
254
- wrapWebPublicRead(handler) {
255
- if (isPublicReadsEnabled()) {
256
+ wrapWebPublicRead(handler, options = {}) {
257
+ if (options.allowAnonymous ?? isPublicReadsEnabled()) {
256
258
  return this.wrapWeb(handler);
257
259
  }
258
260
  return this.wrapWebAuthenticated(handler);
@@ -298,8 +300,8 @@ class HttpKernel {
298
300
  const middleware = [...this.group("authenticated"), ...this.verifiedMiddleware()];
299
301
  return withMiddleware(...middleware)(handler);
300
302
  }
301
- wrapPublicRead(handler) {
302
- if (isPublicReadsEnabled()) {
303
+ wrapPublicRead(handler, options = {}) {
304
+ if (options.allowAnonymous ?? isPublicReadsEnabled()) {
303
305
  return handler;
304
306
  }
305
307
  return this.wrapAuthenticated(handler);
@@ -232,6 +232,7 @@ import {
232
232
  CORE_EVENT_BUS_TOKEN,
233
233
  CORE_HTTP_CLEANUP_TOKEN,
234
234
  CORE_POLICY_GATE_TOKEN,
235
+ CORE_PUBLIC_TENANCY_TOKEN,
235
236
  CORE_QUEUE_TOKEN,
236
237
  CORE_TENANT_RESOLVER_TOKEN,
237
238
  CORE_THROTTLE_QUOTA_POLICY_TOKEN,
@@ -16,6 +16,7 @@ import {
16
16
  CORE_EVENT_BUS_TOKEN,
17
17
  CORE_HTTP_CLEANUP_TOKEN,
18
18
  CORE_POLICY_GATE_TOKEN,
19
+ CORE_PUBLIC_TENANCY_TOKEN,
19
20
  CORE_QUEUE_TOKEN,
20
21
  CORE_TENANT_RESOLVER_TOKEN,
21
22
  CORE_THROTTLE_QUOTA_POLICY_TOKEN,
@@ -48,6 +48,7 @@ import {
48
48
  CORE_EVENT_BUS_TOKEN,
49
49
  CORE_HTTP_CLEANUP_TOKEN,
50
50
  CORE_POLICY_GATE_TOKEN,
51
+ CORE_PUBLIC_TENANCY_TOKEN,
51
52
  CORE_QUEUE_TOKEN,
52
53
  CORE_TENANT_RESOLVER_TOKEN,
53
54
  CORE_THROTTLE_QUOTA_POLICY_TOKEN,
@@ -164,6 +165,7 @@ class HttpKernel {
164
165
  createAuthMiddleware(auth),
165
166
  createMembershipMiddleware(),
166
167
  createTenantMiddleware({
168
+ publicTenancy: this.dependencies.container.has(CORE_PUBLIC_TENANCY_TOKEN) ? this.dependencies.container.resolve(CORE_PUBLIC_TENANCY_TOKEN) : undefined,
167
169
  resolveTenant: this.dependencies.container.has(CORE_TENANT_RESOLVER_TOKEN) ? this.dependencies.container.resolve(CORE_TENANT_RESOLVER_TOKEN) : undefined
168
170
  })
169
171
  ];
@@ -238,8 +240,8 @@ class HttpKernel {
238
240
  return handler(request);
239
241
  });
240
242
  }
241
- wrapWebPublicRead(handler) {
242
- if (isPublicReadsEnabled()) {
243
+ wrapWebPublicRead(handler, options = {}) {
244
+ if (options.allowAnonymous ?? isPublicReadsEnabled()) {
243
245
  return this.wrapWeb(handler);
244
246
  }
245
247
  return this.wrapWebAuthenticated(handler);
@@ -285,8 +287,8 @@ class HttpKernel {
285
287
  const middleware = [...this.group("authenticated"), ...this.verifiedMiddleware()];
286
288
  return withMiddleware(...middleware)(handler);
287
289
  }
288
- wrapPublicRead(handler) {
289
- if (isPublicReadsEnabled()) {
290
+ wrapPublicRead(handler, options = {}) {
291
+ if (options.allowAnonymous ?? isPublicReadsEnabled()) {
290
292
  return handler;
291
293
  }
292
294
  return this.wrapAuthenticated(handler);
@@ -27,6 +27,7 @@ import {
27
27
  CORE_EVENT_BUS_TOKEN,
28
28
  CORE_HTTP_CLEANUP_TOKEN,
29
29
  CORE_POLICY_GATE_TOKEN,
30
+ CORE_PUBLIC_TENANCY_TOKEN,
30
31
  CORE_QUEUE_TOKEN,
31
32
  CORE_TENANT_RESOLVER_TOKEN,
32
33
  CORE_THROTTLE_QUOTA_POLICY_TOKEN,
@@ -126,8 +126,8 @@ function assertFeatureProductionSecrets(env) {
126
126
  throw new Error("Production startup blocked: set explicit CORS_ALLOWED_ORIGINS instead of wildcard.");
127
127
  }
128
128
  }
129
- if (envFlagEnabled(env.FEATURE_PUBLIC_READS)) {
130
- throw new Error("Production startup blocked: set FEATURE_PUBLIC_READS=false for authenticated-only reads.");
129
+ if (envFlagEnabled(env.TENANT_DEV_HEADERS)) {
130
+ throw new Error("Production startup blocked: set TENANT_DEV_HEADERS=false.");
131
131
  }
132
132
  if (!env.SIEM_EXPORT_URL?.trim() && envFlagEnabled(env.FEATURE_SIEM_EXPORT)) {
133
133
  console.warn("[secrets] SIEM_EXPORT_URL is not configured; audit logs remain database-only.");
@@ -58,6 +58,7 @@ import {
58
58
  CORE_EVENT_BUS_TOKEN,
59
59
  CORE_HTTP_CLEANUP_TOKEN,
60
60
  CORE_POLICY_GATE_TOKEN,
61
+ CORE_PUBLIC_TENANCY_TOKEN,
61
62
  CORE_QUEUE_TOKEN,
62
63
  CORE_TENANT_RESOLVER_TOKEN,
63
64
  CORE_THROTTLE_QUOTA_POLICY_TOKEN,
@@ -174,6 +175,7 @@ class HttpKernel {
174
175
  createAuthMiddleware(auth),
175
176
  createMembershipMiddleware(),
176
177
  createTenantMiddleware({
178
+ publicTenancy: this.dependencies.container.has(CORE_PUBLIC_TENANCY_TOKEN) ? this.dependencies.container.resolve(CORE_PUBLIC_TENANCY_TOKEN) : undefined,
177
179
  resolveTenant: this.dependencies.container.has(CORE_TENANT_RESOLVER_TOKEN) ? this.dependencies.container.resolve(CORE_TENANT_RESOLVER_TOKEN) : undefined
178
180
  })
179
181
  ];
@@ -248,8 +250,8 @@ class HttpKernel {
248
250
  return handler(request);
249
251
  });
250
252
  }
251
- wrapWebPublicRead(handler) {
252
- if (isPublicReadsEnabled()) {
253
+ wrapWebPublicRead(handler, options = {}) {
254
+ if (options.allowAnonymous ?? isPublicReadsEnabled()) {
253
255
  return this.wrapWeb(handler);
254
256
  }
255
257
  return this.wrapWebAuthenticated(handler);
@@ -295,8 +297,8 @@ class HttpKernel {
295
297
  const middleware = [...this.group("authenticated"), ...this.verifiedMiddleware()];
296
298
  return withMiddleware(...middleware)(handler);
297
299
  }
298
- wrapPublicRead(handler) {
299
- if (isPublicReadsEnabled()) {
300
+ wrapPublicRead(handler, options = {}) {
301
+ if (options.allowAnonymous ?? isPublicReadsEnabled()) {
300
302
  return handler;
301
303
  }
302
304
  return this.wrapAuthenticated(handler);
@@ -19,6 +19,7 @@ function wrapRouteHandler(handler, path, onRequest, track = (work) => work()) {
19
19
  request,
20
20
  routeTemplate: path,
21
21
  ipAddress: socketAddress(server, request),
22
+ peerAddress: socketAddress(server, request),
22
23
  userAgent: request.headers.get("user-agent")
23
24
  }, async () => {
24
25
  const intercepted = await onRequest?.(request, server);
@@ -94,6 +95,7 @@ function createWebServer(options) {
94
95
  ...currentRequestMeta(),
95
96
  request,
96
97
  ipAddress: socketAddress(server, request),
98
+ peerAddress: socketAddress(server, request),
97
99
  userAgent: request.headers.get("user-agent")
98
100
  }, async () => {
99
101
  const intercepted = await onRequest(request, server);
package/dist/index.js CHANGED
@@ -73,6 +73,7 @@ import {
73
73
  CORE_EVENT_BUS_TOKEN,
74
74
  CORE_HTTP_CLEANUP_TOKEN,
75
75
  CORE_POLICY_GATE_TOKEN,
76
+ CORE_PUBLIC_TENANCY_TOKEN,
76
77
  CORE_QUEUE_TOKEN,
77
78
  CORE_TENANT_RESOLVER_TOKEN,
78
79
  CORE_THROTTLE_QUOTA_POLICY_TOKEN,
@@ -188,6 +189,7 @@ class HttpKernel {
188
189
  createAuthMiddleware(auth),
189
190
  createMembershipMiddleware(),
190
191
  createTenantMiddleware({
192
+ publicTenancy: this.dependencies.container.has(CORE_PUBLIC_TENANCY_TOKEN) ? this.dependencies.container.resolve(CORE_PUBLIC_TENANCY_TOKEN) : undefined,
191
193
  resolveTenant: this.dependencies.container.has(CORE_TENANT_RESOLVER_TOKEN) ? this.dependencies.container.resolve(CORE_TENANT_RESOLVER_TOKEN) : undefined
192
194
  })
193
195
  ];
@@ -262,8 +264,8 @@ class HttpKernel {
262
264
  return handler(request);
263
265
  });
264
266
  }
265
- wrapWebPublicRead(handler) {
266
- if (isPublicReadsEnabled()) {
267
+ wrapWebPublicRead(handler, options = {}) {
268
+ if (options.allowAnonymous ?? isPublicReadsEnabled()) {
267
269
  return this.wrapWeb(handler);
268
270
  }
269
271
  return this.wrapWebAuthenticated(handler);
@@ -309,8 +311,8 @@ class HttpKernel {
309
311
  const middleware = [...this.group("authenticated"), ...this.verifiedMiddleware()];
310
312
  return withMiddleware(...middleware)(handler);
311
313
  }
312
- wrapPublicRead(handler) {
313
- if (isPublicReadsEnabled()) {
314
+ wrapPublicRead(handler, options = {}) {
315
+ if (options.allowAnonymous ?? isPublicReadsEnabled()) {
314
316
  return handler;
315
317
  }
316
318
  return this.wrapAuthenticated(handler);
@@ -1743,8 +1745,8 @@ function assertFeatureProductionSecrets(env) {
1743
1745
  throw new Error("Production startup blocked: set explicit CORS_ALLOWED_ORIGINS instead of wildcard.");
1744
1746
  }
1745
1747
  }
1746
- if (envFlagEnabled3(env.FEATURE_PUBLIC_READS)) {
1747
- throw new Error("Production startup blocked: set FEATURE_PUBLIC_READS=false for authenticated-only reads.");
1748
+ if (envFlagEnabled3(env.TENANT_DEV_HEADERS)) {
1749
+ throw new Error("Production startup blocked: set TENANT_DEV_HEADERS=false.");
1748
1750
  }
1749
1751
  if (!env.SIEM_EXPORT_URL?.trim() && envFlagEnabled3(env.FEATURE_SIEM_EXPORT)) {
1750
1752
  console.warn("[secrets] SIEM_EXPORT_URL is not configured; audit logs remain database-only.");
@@ -1913,6 +1915,7 @@ function wrapRouteHandler(handler, path, onRequest, track = (work) => work()) {
1913
1915
  request,
1914
1916
  routeTemplate: path,
1915
1917
  ipAddress: socketAddress(server, request),
1918
+ peerAddress: socketAddress(server, request),
1916
1919
  userAgent: request.headers.get("user-agent")
1917
1920
  }, async () => {
1918
1921
  const intercepted = await onRequest?.(request, server);
@@ -1988,6 +1991,7 @@ function createWebServer(options) {
1988
1991
  ...currentRequestMeta2(),
1989
1992
  request,
1990
1993
  ipAddress: socketAddress(server, request),
1994
+ peerAddress: socketAddress(server, request),
1991
1995
  userAgent: request.headers.get("user-agent")
1992
1996
  }, async () => {
1993
1997
  const intercepted = await onRequest(request, server);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@getstrata/bootstrap",
3
- "version": "2.2.0",
3
+ "version": "2.2.1",
4
4
  "description": "Strata application bootstrap: HttpKernel, DI, web session helpers",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -190,7 +190,7 @@
190
190
  "access": "public"
191
191
  },
192
192
  "peerDependencies": {
193
- "@getstrata/core": "^2.2.0",
193
+ "@getstrata/core": "^2.2.1",
194
194
  "typescript": "^5.9.0 || ^6.0.0 || ^7.0.0"
195
195
  },
196
196
  "engines": {