lambder 11.1.1 → 13.0.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.
Files changed (87) hide show
  1. package/CHANGELOG.md +217 -0
  2. package/README.md +25 -15
  3. package/dist/api/LambderApiAnswer.d.ts +7 -0
  4. package/dist/api/LambderApiDeclarations.d.ts +194 -0
  5. package/dist/api/LambderApiDeclarations.js +52 -0
  6. package/dist/api/LambderApiDefinition.d.ts +1 -1
  7. package/dist/api/LambderApiEnvelope.js +4 -1
  8. package/dist/api/LambderApiGuards.d.ts +21 -22
  9. package/dist/api/LambderApiGuards.js +1 -1
  10. package/dist/api/LambderApiRateLimits.d.ts +19 -19
  11. package/dist/api/LambderApiRateLimits.js +1 -1
  12. package/dist/api/LambderApiRefusals.d.ts +22 -0
  13. package/dist/api/LambderApiRefusals.js +13 -0
  14. package/dist/api/LambderApiRequest.d.ts +15 -6
  15. package/dist/api/LambderApiRequest.js +8 -9
  16. package/dist/build/generateApiFiles.d.ts +60 -0
  17. package/dist/build/generateApiFiles.js +85 -0
  18. package/dist/build/generatedTables.d.ts +2 -1
  19. package/dist/build/generatedTables.js +1 -1
  20. package/dist/build.d.ts +4 -1
  21. package/dist/build.js +3 -1
  22. package/dist/client/LambderCaller.d.ts +36 -23
  23. package/dist/client/LambderCaller.js +88 -67
  24. package/dist/client/lambderFetchTransport.js +2 -1
  25. package/dist/client.d.ts +3 -1
  26. package/dist/client.js +2 -0
  27. package/dist/core/Lambder.d.ts +232 -92
  28. package/dist/core/Lambder.js +400 -103
  29. package/dist/core/LambderCallSummary.d.ts +43 -0
  30. package/dist/core/LambderCallSummary.js +10 -0
  31. package/dist/core/LambderContext.d.ts +15 -2
  32. package/dist/core/LambderContext.js +32 -9
  33. package/dist/core/LambderCreateOptions.d.ts +78 -64
  34. package/dist/core/LambderCreateOptions.js +39 -21
  35. package/dist/core/LambderInvocationScope.d.ts +25 -0
  36. package/dist/core/LambderInvocationScope.js +21 -0
  37. package/dist/core/LambderResponse.d.ts +11 -0
  38. package/dist/core/LambderResponse.js +12 -0
  39. package/dist/core/LambderResponseBuilder.js +5 -1
  40. package/dist/index.d.ts +13 -7
  41. package/dist/index.js +3 -1
  42. package/dist/invoke/LambderInvokeCaller.d.ts +48 -31
  43. package/dist/invoke/LambderInvokeCaller.js +110 -94
  44. package/dist/invoke/LambderLambdaEvent.d.ts +2 -1
  45. package/dist/invoke/LambderLambdaEvent.js +8 -3
  46. package/dist/invoke/lambderHandlerTransport.js +2 -1
  47. package/dist/mock/LambderMockApp.d.ts +39 -33
  48. package/dist/mock/LambderMockApp.js +66 -55
  49. package/dist/mock/LambderMockCreateOptions.d.ts +8 -10
  50. package/dist/mock/LambderMockTypes.d.ts +44 -7
  51. package/dist/mock/lambderMockInvokeTransport.d.ts +9 -1
  52. package/dist/mock/lambderMockInvokeTransport.js +15 -7
  53. package/dist/mock/lambderMockMswHandler.js +8 -4
  54. package/dist/mock.d.ts +2 -2
  55. package/dist/session/LambderSessionManager.d.ts +46 -3
  56. package/dist/session/LambderSessionManager.js +62 -11
  57. package/dist/shared/LambderI18n.js +49 -4
  58. package/dist/shared/contracts/LambderRateLimiter.d.ts +2 -2
  59. package/dist/shared/contracts/LambderRateLimiter.js +2 -2
  60. package/dist/shared/transport/LambderApiTransport.d.ts +3 -2
  61. package/dist/shared/transport/LambderApiTransport.js +2 -3
  62. package/dist/shared/transport/lambderCookieJarTransport.js +2 -1
  63. package/dist/shared/util/LambderInvocationScope.d.ts +10 -0
  64. package/dist/shared/util/LambderInvocationScope.js +5 -0
  65. package/dist/shared/util/LambderNamedMaps.d.ts +22 -0
  66. package/dist/shared/util/LambderNamedMaps.js +19 -0
  67. package/dist/shared/util/LambderTypeUtilities.d.ts +1 -1
  68. package/dist/shared/wire/LambderApiContract.d.ts +4 -18
  69. package/dist/shared/wire/LambderApiContract.js +1 -1
  70. package/dist/shared/wire/LambderApiGroupCalls.d.ts +31 -0
  71. package/dist/shared/wire/LambderApiGroupCalls.js +68 -0
  72. package/dist/shared/wire/LambderApiNames.d.ts +55 -0
  73. package/dist/shared/wire/LambderApiNames.js +81 -0
  74. package/dist/shared/wire/LambderCallOutcome.d.ts +27 -0
  75. package/dist/shared/wire/LambderCallOutcome.js +47 -0
  76. package/dist/shared/wire/LambderInvokeApiId.d.ts +10 -0
  77. package/dist/shared/wire/LambderInvokeApiId.js +10 -0
  78. package/dist/shared/wire/LambderUploadSchemas.js +2 -1
  79. package/dist/stores/LambderDdbRateLimiter.d.ts +24 -16
  80. package/dist/stores/LambderDdbRateLimiter.js +64 -45
  81. package/dist/stores/LambderMemoryRateLimiter.d.ts +9 -4
  82. package/dist/stores/LambderMemoryRateLimiter.js +20 -15
  83. package/dist/testing/LambderTestApp.d.ts +18 -5
  84. package/dist/testing/LambderTestApp.js +21 -5
  85. package/dist/testing/LambderTestVisitor.d.ts +16 -8
  86. package/dist/testing/LambderTestVisitor.js +33 -25
  87. package/package.json +1 -1
@@ -3,10 +3,15 @@ import { LambderExpiringMap } from "../shared/util/LambderExpiringMap.js";
3
3
  import { joinKeyFields } from "../shared/util/joinKeyFields.js";
4
4
  /**
5
5
  * Fixed-window rate limiter held in memory: the same windows, the same
6
- * attempts-count semantics and the same evaluation order as
7
- * LambderDdbRateLimiter, with a Map in place of the table. For tests and
8
- * for the mock runtime; a single-process server could use it too, with the
9
- * caveat that its counters are per process and vanish with it.
6
+ * attempts-count semantics and the same refusals as LambderDdbRateLimiter,
7
+ * with a Map in place of the table. For tests and for the mock runtime; a
8
+ * single-process server could use it too, with the caveat that its counters
9
+ * are per process and vanish with it.
10
+ *
11
+ * Every counter is in hand, so an attempt a window refuses is counted in no
12
+ * window: what the DynamoDB limiter reaches once it remembers the refusing
13
+ * window, which the first refused attempt in each of its processes still
14
+ * counts in the others.
10
15
  *
11
16
  * `now` is injectable so a test can move time forward and watch a window
12
17
  * reset without waiting for it.
@@ -31,24 +36,24 @@ export class LambderMemoryRateLimiter {
31
36
  }
32
37
  async isRateLimited(trackerKey, policy) {
33
38
  const nowSeconds = Math.floor(this.now() / 1000);
34
- for (const { key, seconds } of RATE_LIMIT_WINDOWS) {
35
- const limit = policy[key];
36
- if (!limit)
37
- continue;
39
+ const windows = RATE_LIMIT_WINDOWS.filter(({ key }) => policy[key]).map(({ key, seconds }) => {
38
40
  const windowStart = Math.floor(nowSeconds / seconds) * seconds;
39
41
  // The table keeps the tracker key and the window in separate
40
42
  // attributes; one string has to keep them as distinct, hence the
41
43
  // escaping join.
42
44
  const counterKey = joinKeyFields(trackerKey, key, String(windowStart));
43
- const counter = this.counters.get(counterKey) ?? { count: 0 };
44
- // The DynamoDB limiter's conditional ADD: allowed while the count
45
- // before this attempt is under the limit, and the refused attempt
46
- // leaves the counter where it was.
47
- if (counter.count >= limit)
48
- return { window: key, limit, resetAt: windowStart + seconds };
45
+ return { key, limit: policy[key], resetAt: windowStart + seconds, counterKey, counter: this.counters.get(counterKey) ?? { count: 0 } };
46
+ });
47
+ // The DynamoDB limiter's conditional ADD: allowed while the count
48
+ // before this attempt is under the limit, and the smallest window at
49
+ // its limit names the refusal.
50
+ const refusing = windows.find(({ counter, limit }) => counter.count >= limit);
51
+ if (refusing)
52
+ return { window: refusing.key, limit: refusing.limit, resetAt: refusing.resetAt };
53
+ for (const { counterKey, counter, resetAt } of windows) {
49
54
  counter.count += 1;
50
55
  // The counter dies with its window, which is also the table's TTL.
51
- this.counters.set(counterKey, counter, windowStart + seconds);
56
+ this.counters.set(counterKey, counter, resetAt);
52
57
  }
53
58
  return false;
54
59
  }
@@ -7,7 +7,9 @@ import type { LambderFileSource } from "../shared/contracts/LambderFileSource.js
7
7
  import type { LambderIdempotencyStore } from "../shared/contracts/LambderIdempotencyStore.js";
8
8
  import type { LambderRateLimiter } from "../shared/contracts/LambderRateLimiter.js";
9
9
  import type { LambderSessionStore } from "../shared/contracts/LambderSessionStore.js";
10
+ import { LAMBDER_BACKEND_SWAP, LAMBDER_CRASH_WATCH } from "../shared/util/LambderTestingDoors.js";
10
11
  import type { LambderApiContractShape } from "../shared/wire/LambderApiContract.js";
12
+ import type { LambderCallSummary } from "../core/LambderCallSummary.js";
11
13
  import { LambderTestVisitor, type LambderTestVisitorArgs, type LambderTestVisitorOptions } from "./LambderTestVisitor.js";
12
14
  /**
13
15
  * What a test app may be told. Nothing is required: `lambderTestApp(lambder)`
@@ -51,7 +53,10 @@ export type LambderTestAppOptions = {
51
53
  * (`lambderTestApp<SessionData, ApiContractType>(lambder)`), so the tests type
52
54
  * their calls against plain members rather than the chained intersection.
53
55
  */
54
- export type LambderTestedInstance<TSessionData, TContract> = Lambder<TSessionData, any, any, any, any, any, any, any, any, any> & {
56
+ export type LambderTestedInstance<TSessionData, TContract> = Pick<Lambder<any, any>, "apiPath" | "getHandler" | "getSessionController" | "getSessionManager" | typeof LAMBDER_BACKEND_SWAP | typeof LAMBDER_CRASH_WATCH> & {
57
+ readonly AppTypes: {
58
+ session: TSessionData;
59
+ };
55
60
  readonly ApiContract: TContract;
56
61
  };
57
62
  /**
@@ -96,6 +101,8 @@ export declare class LambderTestApp<TContract extends LambderApiContractShape =
96
101
  private visitorCount;
97
102
  private resetCount;
98
103
  private readonly crashList;
104
+ /** Every call summary the app wrote since the last reset; see callSummaries. */
105
+ private readonly callSummaryList;
99
106
  /**
100
107
  * The call a crash happened under. The app's 500 says nothing about the
101
108
  * crash, so the error travels beside the answer, and with concurrent
@@ -113,6 +120,12 @@ export declare class LambderTestApp<TContract extends LambderApiContractShape =
113
120
  * error an `event()` rejects with, which the test already holds.
114
121
  */
115
122
  get crashes(): readonly Error[];
123
+ /**
124
+ * The summary of every API call the app answered since the last reset,
125
+ * in order (see LambderCallSummary): what its callSummary option would
126
+ * have been handed, collected here instead of written to stdout.
127
+ */
128
+ get callSummaries(): readonly LambderCallSummary[];
116
129
  /** The session manager, for tests that inspect or manipulate sessions directly. Throws when the app has no sessions. */
117
130
  get sessionManager(): LambderSessionManager<TSessionData>;
118
131
  /**
@@ -155,10 +168,10 @@ export declare class LambderTestApp<TContract extends LambderApiContractShape =
155
168
  event(event: unknown, context?: Partial<Context>): Promise<unknown>;
156
169
  /**
157
170
  * Rewinds what accumulated: sessions, rate-limit counters and replay
158
- * records in the stores this test app made, the crashes it recorded, and
159
- * the cookies of every visitor it created (each empties its jar the next
160
- * time it is used). For a beforeEach. The app's own data (its database)
161
- * is the app's to rewind.
171
+ * records in the stores this test app made, the crashes and call
172
+ * summaries it recorded, and the cookies of every visitor it created
173
+ * (each empties its jar the next time it is used). For a beforeEach. The
174
+ * app's own data (its database) is the app's to rewind.
162
175
  */
163
176
  reset(): void;
164
177
  }
@@ -49,6 +49,8 @@ export class LambderTestApp {
49
49
  visitorCount = 0;
50
50
  resetCount = 0;
51
51
  crashList = [];
52
+ /** Every call summary the app wrote since the last reset; see callSummaries. */
53
+ callSummaryList = [];
52
54
  /**
53
55
  * The call a crash happened under. The app's 500 says nothing about the
54
56
  * crash, so the error travels beside the answer, and with concurrent
@@ -66,7 +68,12 @@ export class LambderTestApp {
66
68
  const sessionStore = options.session?.store ?? own(new LambderMemorySessionStore());
67
69
  const rateLimiter = options.rateLimits?.limiter ?? own(new LambderMemoryRateLimiter());
68
70
  const idempotencyStore = options.idempotency?.store ?? own(new LambderMemoryIdempotencyStore());
69
- const swap = lambder[LAMBDER_BACKEND_SWAP]({ sessionStore, rateLimiter, idempotencyStore, fileSource: options.files });
71
+ // The summaries are collected rather than written, so a suite's
72
+ // output stays its own and a test can read what a call recorded.
73
+ const swap = lambder[LAMBDER_BACKEND_SWAP]({
74
+ sessionStore, rateLimiter, idempotencyStore, fileSource: options.files,
75
+ callSummary: (summary) => { this.callSummaryList.push(summary); },
76
+ });
70
77
  if (options.files && !swap.files) {
71
78
  throw new Error("lambderTestApp: the files option was given, but the app was created without one, so nothing reads files to put a source under.");
72
79
  }
@@ -116,6 +123,14 @@ export class LambderTestApp {
116
123
  get crashes() {
117
124
  return this.crashList;
118
125
  }
126
+ /**
127
+ * The summary of every API call the app answered since the last reset,
128
+ * in order (see LambderCallSummary): what its callSummary option would
129
+ * have been handed, collected here instead of written to stdout.
130
+ */
131
+ get callSummaries() {
132
+ return this.callSummaryList;
133
+ }
119
134
  /** The session manager, for tests that inspect or manipulate sessions directly. Throws when the app has no sessions. */
120
135
  get sessionManager() {
121
136
  return this.lambder.getSessionManager();
@@ -178,15 +193,16 @@ export class LambderTestApp {
178
193
  }
179
194
  /**
180
195
  * Rewinds what accumulated: sessions, rate-limit counters and replay
181
- * records in the stores this test app made, the crashes it recorded, and
182
- * the cookies of every visitor it created (each empties its jar the next
183
- * time it is used). For a beforeEach. The app's own data (its database)
184
- * is the app's to rewind.
196
+ * records in the stores this test app made, the crashes and call
197
+ * summaries it recorded, and the cookies of every visitor it created
198
+ * (each empties its jar the next time it is used). For a beforeEach. The
199
+ * app's own data (its database) is the app's to rewind.
185
200
  */
186
201
  reset() {
187
202
  for (const store of this.ownStores)
188
203
  store.reset();
189
204
  this.crashList.length = 0;
205
+ this.callSummaryList.length = 0;
190
206
  this.resetCount += 1;
191
207
  }
192
208
  }
@@ -1,4 +1,4 @@
1
- import LambderCaller from "../client/LambderCaller.js";
1
+ import LambderCaller, { type LambderCallerGroupCalls, type LambderCallerMembers } from "../client/LambderCaller.js";
2
2
  import type { LambderHttpEventFormat } from "../core/LambderContext.js";
3
3
  import type { LambderHandler } from "../core/LambderCreateOptions.js";
4
4
  import { type LambderLambdaHttpResult } from "../invoke/LambderLambdaEvent.js";
@@ -84,7 +84,8 @@ export type LambderTestVisitorArgs<TOptions, TProvidedGuards extends string> = [
84
84
  *
85
85
  * Created by `LambderTestApp.visitor()` and `signIn()`, not constructed.
86
86
  */
87
- export declare class LambderTestVisitor<TContract extends LambderApiContractShape = any, TSessionData = any, TProvidedGuards extends string = never> {
87
+ declare class LambderTestVisitorCore<TContract extends LambderApiContractShape = any, TSessionData = any, TProvidedGuards extends string = never> {
88
+ #private;
88
89
  readonly host: string;
89
90
  readonly clientIp: string;
90
91
  /**
@@ -95,7 +96,7 @@ export declare class LambderTestVisitor<TContract extends LambderApiContractShap
95
96
  */
96
97
  readonly caller: LambderCaller<TContract, TProvidedGuards>;
97
98
  /** The payload on success, `undefined` on a failure: LambderCaller.api, through this visitor. */
98
- readonly api: LambderCaller<TContract, TProvidedGuards>["api"];
99
+ readonly api: LambderCallerMembers<TContract, TProvidedGuards>["api"];
99
100
  /**
100
101
  * The full outcome, never throwing: LambderCaller.apiOutcome, through
101
102
  * this visitor. Pair it with assertApiSuccess / assertApiFailure.
@@ -105,11 +106,7 @@ export declare class LambderTestVisitor<TContract extends LambderApiContractShap
105
106
  * 500"; here that error's `cause` is what the app actually threw, stack
106
107
  * included, so a failing test points at the line in the handler.
107
108
  */
108
- readonly apiOutcome: LambderCaller<TContract, TProvidedGuards>["apiOutcome"];
109
- private readonly wiring;
110
- private readonly headers;
111
- private readonly cookieJar;
112
- private seenResetCount;
109
+ readonly apiOutcome: LambderCallerMembers<TContract, TProvidedGuards>["apiOutcome"];
113
110
  constructor(wiring: LambderTestVisitorWiring<TSessionData>, options: LambderTestVisitorOptions<TContract, TProvidedGuards> & {
114
111
  host: string;
115
112
  clientIp: string;
@@ -151,3 +148,14 @@ export declare class LambderTestVisitor<TContract extends LambderApiContractShap
151
148
  ttlSeconds?: number;
152
149
  }): Promise<LambderCreatedSession<TSessionData>>;
153
150
  }
151
+ /**
152
+ * One simulated browser in front of a real Lambder app, with the app's
153
+ * endpoints by group as a caller has them: `visitor.orders.place(input)`,
154
+ * `visitor.orders.place.outcome(input)`.
155
+ */
156
+ export type LambderTestVisitor<TContract extends LambderApiContractShape = any, TSessionData = any, TProvidedGuards extends string = never> = LambderTestVisitorCore<TContract, TSessionData, TProvidedGuards> & LambderCallerGroupCalls<TContract, TProvidedGuards>;
157
+ export declare const LambderTestVisitor: {
158
+ new <TContract extends LambderApiContractShape = any, TSessionData = any, TProvidedGuards extends string = never>(...args: ConstructorParameters<typeof LambderTestVisitorCore<TContract, TSessionData, TProvidedGuards>>): LambderTestVisitor<TContract, TSessionData, TProvidedGuards>;
159
+ readonly prototype: LambderTestVisitorCore<any, any, any>;
160
+ };
161
+ export {};
@@ -1,4 +1,5 @@
1
1
  import LambderCaller from "../client/LambderCaller.js";
2
+ import { withApiGroupCalls } from "../shared/wire/LambderApiGroupCalls.js";
2
3
  import { lambderHandlerTransport } from "../invoke/lambderHandlerTransport.js";
3
4
  import { decodeLambdaHttpResult, localLambdaContext, synthesizeLambdaHttpEvent, } from "../invoke/LambderLambdaEvent.js";
4
5
  import { LambderCookieJar } from "../shared/transport/LambderCookieJar.js";
@@ -15,7 +16,7 @@ import { DEFAULT_MAX_RESTORED_PAYLOAD_BYTES } from "../shared/wire/LambderReques
15
16
  *
16
17
  * Created by `LambderTestApp.visitor()` and `signIn()`, not constructed.
17
18
  */
18
- export class LambderTestVisitor {
19
+ class LambderTestVisitorCore {
19
20
  host;
20
21
  clientIp;
21
22
  /**
@@ -37,19 +38,19 @@ export class LambderTestVisitor {
37
38
  * included, so a failing test points at the line in the handler.
38
39
  */
39
40
  apiOutcome;
40
- wiring;
41
- headers;
42
- cookieJar;
43
- seenResetCount;
41
+ #wiring;
42
+ #headers;
43
+ #cookieJar;
44
+ #seenResetCount;
44
45
  constructor(wiring, options) {
45
- this.wiring = wiring;
46
+ this.#wiring = wiring;
46
47
  this.host = options.host;
47
48
  this.clientIp = options.clientIp;
48
- this.headers = options.headers ?? {};
49
- this.cookieJar = new LambderCookieJar({ host: this.host });
50
- this.seenResetCount = wiring.resetCount();
49
+ this.#headers = options.headers ?? {};
50
+ this.#cookieJar = new LambderCookieJar({ host: this.host });
51
+ this.#seenResetCount = wiring.resetCount();
51
52
  const handlerTransport = lambderHandlerTransport(wiring.handler, { host: this.host, clientIp: this.clientIp, eventFormat: wiring.eventFormat });
52
- const withVisitorHeaders = (request) => handlerTransport({ ...request, headers: { ...this.headers, ...request.headers } });
53
+ const withVisitorHeaders = (request) => handlerTransport({ ...request, headers: { ...this.#headers, ...request.headers } });
53
54
  this.caller = new LambderCaller({
54
55
  apiPath: wiring.apiPath,
55
56
  apiVersion: options.apiVersion,
@@ -69,13 +70,19 @@ export class LambderTestVisitor {
69
70
  if (wiring.sessionCookieNames) {
70
71
  this.caller.setSessionCookieKey(wiring.sessionCookieNames.tokenCookieKey, wiring.sessionCookieNames.csrfCookieKey);
71
72
  }
72
- this.api = this.caller.api.bind(this.caller);
73
- this.apiOutcome = (async (...callArgs) => {
74
- const { result: outcome, crash } = await wiring.watchCrash(() => this.caller.apiOutcome(...callArgs));
73
+ const caller = this.caller;
74
+ const apiOutcome = async (apiName, ...callArgs) => {
75
+ const { result: outcome, crash } = await wiring.watchCrash(() => caller.apiOutcome(apiName, ...callArgs));
75
76
  if (crash && !outcome.ok && "error" in outcome && outcome.error.cause === undefined)
76
77
  outcome.error.cause = crash;
77
78
  return outcome;
78
- });
79
+ };
80
+ this.api = ((apiName, ...callArgs) => caller.api(apiName, ...callArgs));
81
+ this.apiOutcome = apiOutcome;
82
+ // Each group of the contract, as on a caller, through this visitor's
83
+ // own two calls: visitor.orders.place.outcome(input) carries the crash
84
+ // cause as visitor.apiOutcome does.
85
+ return withApiGroupCalls(this, (apiName, args) => caller.api(apiName, ...args), (apiName, args) => apiOutcome(apiName, ...args));
79
86
  }
80
87
  /**
81
88
  * This visitor's cookies, to inspect or clear; every call and request
@@ -84,12 +91,12 @@ export class LambderTestVisitor {
84
91
  * emptied store would read as signed in until an answer said otherwise.
85
92
  */
86
93
  get jar() {
87
- const resetCount = this.wiring.resetCount();
88
- if (resetCount !== this.seenResetCount) {
89
- this.cookieJar.clear();
90
- this.seenResetCount = resetCount;
94
+ const resetCount = this.#wiring.resetCount();
95
+ if (resetCount !== this.#seenResetCount) {
96
+ this.#cookieJar.clear();
97
+ this.#seenResetCount = resetCount;
91
98
  }
92
- return this.cookieJar;
99
+ return this.#cookieJar;
93
100
  }
94
101
  /**
95
102
  * One HTTP request to the app that is not an API call, answered by
@@ -117,12 +124,12 @@ export class LambderTestVisitor {
117
124
  path: pathname,
118
125
  query,
119
126
  host: this.host,
120
- headers: { ...this.headers, ...init.headers },
127
+ headers: { ...this.#headers, ...init.headers },
121
128
  clientIp: this.clientIp,
122
129
  cookies: this.jar.cookiePairs(cookieScope),
123
130
  body: init.body,
124
- }, { invoke: false, eventFormat: this.wiring.eventFormat });
125
- const result = await decodeLambdaHttpResult(await this.wiring.handler(event, localLambdaContext("lambder-test")), DEFAULT_MAX_RESTORED_PAYLOAD_BYTES);
131
+ }, { invoke: false, eventFormat: this.#wiring.eventFormat });
132
+ const result = await decodeLambdaHttpResult(await this.#wiring.handler(event, localLambdaContext("lambder-test")), DEFAULT_MAX_RESTORED_PAYLOAD_BYTES);
126
133
  if (result.cookies.length)
127
134
  this.jar.storeSetCookies(result.cookies, cookieScope);
128
135
  return result;
@@ -140,11 +147,11 @@ export class LambderTestVisitor {
140
147
  * answer sessionExpired with nothing to say why.
141
148
  */
142
149
  async signIn(sessionKey, data, options = {}) {
143
- if (!this.wiring.sessionCookieNames)
150
+ if (!this.#wiring.sessionCookieNames)
144
151
  throw new Error("LambderTestVisitor: signIn() needs an app with sessions. Pass the session option to create().");
145
- const { created, setCookies } = await this.wiring.issueSession(this.host, sessionKey, data, options.ttlSeconds);
152
+ const { created, setCookies } = await this.#wiring.issueSession(this.host, sessionKey, data, options.ttlSeconds);
146
153
  this.jar.storeSetCookies(setCookies, { host: this.host });
147
- if (this.jar.get(this.wiring.sessionCookieNames.tokenCookieKey, { host: this.host, includeHttpOnly: true }) === undefined) {
154
+ if (this.jar.get(this.#wiring.sessionCookieNames.tokenCookieKey, { host: this.host, includeHttpOnly: true }) === undefined) {
148
155
  throw new Error(`LambderTestVisitor: the session cookie did not stick for host "${this.host}", the way a browser on that host would drop it. ` +
149
156
  "The app most likely scopes its session cookie to a domain this host is not under: " +
150
157
  "give the test app or this visitor a `host` the cookie domain covers.");
@@ -152,3 +159,4 @@ export class LambderTestVisitor {
152
159
  return created;
153
160
  }
154
161
  }
162
+ export const LambderTestVisitor = LambderTestVisitorCore;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lambder",
3
- "version": "11.1.1",
3
+ "version": "13.0.1",
4
4
  "sideEffects": false,
5
5
  "description": "Opinionated serverless web framework for TypeScript on AWS Lambda: type-safe APIs from Zod schemas, DynamoDB sessions, and declarative rate limits, authorization guards and idempotency.",
6
6
  "keywords": [