lambder 7.3.1 → 8.1.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 +1047 -3
- package/README.md +46 -21
- package/dist/api/LambderApiAnswer.d.ts +18 -22
- package/dist/api/LambderApiAnswer.js +6 -7
- package/dist/api/LambderApiCallContext.d.ts +21 -8
- package/dist/api/LambderApiCallContext.js +22 -4
- package/dist/api/LambderApiDefinition.d.ts +4 -3
- package/dist/api/LambderApiEnvelope.d.ts +14 -9
- package/dist/api/LambderApiEnvelope.js +33 -34
- package/dist/api/LambderApiGuards.d.ts +78 -51
- package/dist/api/LambderApiGuards.js +34 -36
- package/dist/api/LambderApiIdempotency.d.ts +68 -62
- package/dist/api/LambderApiIdempotency.js +214 -151
- package/dist/api/LambderApiOutputValidationError.d.ts +32 -0
- package/dist/api/LambderApiOutputValidationError.js +50 -0
- package/dist/api/LambderApiPipeline.d.ts +47 -38
- package/dist/api/LambderApiPipeline.js +122 -63
- package/dist/api/LambderApiRateLimits.d.ts +201 -54
- package/dist/api/LambderApiRateLimits.js +185 -108
- package/dist/api/LambderApiRequest.d.ts +27 -21
- package/dist/api/LambderApiRequest.js +26 -19
- package/dist/api/LambderApiSignature.d.ts +12 -15
- package/dist/api/LambderApiSignature.js +28 -51
- package/dist/api/LambderApiValidationRefusal.d.ts +9 -9
- package/dist/api/LambderApiValidationRefusal.js +10 -10
- package/dist/build/ContractTypePrinter.d.ts +85 -0
- package/dist/build/ContractTypePrinter.js +402 -0
- package/dist/build/freshProcessVerifier.d.ts +13 -0
- package/dist/build/freshProcessVerifier.js +19 -0
- package/dist/build/moduleLocation.d.ts +11 -0
- package/dist/build/moduleLocation.js +6 -0
- package/dist/build/writeApiContract.d.ts +78 -0
- package/dist/build/writeApiContract.js +302 -0
- package/dist/build/writeApiSignatures.d.ts +114 -0
- package/dist/build/writeApiSignatures.js +217 -0
- package/dist/build/writeFileAtomically.d.ts +8 -0
- package/dist/build/writeFileAtomically.js +22 -0
- package/dist/build.d.ts +14 -0
- package/dist/build.js +11 -0
- package/dist/client/LambderCaller.d.ts +13 -44
- package/dist/client/LambderCaller.js +77 -84
- package/dist/client/LambderReloadLoopBreaker.d.ts +56 -26
- package/dist/client/LambderReloadLoopBreaker.js +90 -46
- package/dist/client/LambderUploadRunner.d.ts +96 -0
- package/dist/client/LambderUploadRunner.js +234 -0
- package/dist/client/lambderFetchTransport.d.ts +4 -1
- package/dist/client/lambderFetchTransport.js +52 -28
- package/dist/client.d.ts +9 -3
- package/dist/client.js +6 -1
- package/dist/core/Lambder.d.ts +143 -79
- package/dist/core/Lambder.js +350 -231
- package/dist/core/LambderContext.d.ts +82 -15
- package/dist/core/LambderContext.js +107 -20
- package/dist/core/LambderCors.d.ts +21 -3
- package/dist/core/LambderCors.js +35 -16
- package/dist/core/LambderCrashHandling.d.ts +40 -0
- package/dist/core/LambderCrashHandling.js +97 -0
- package/dist/core/LambderCreateOptions.d.ts +151 -75
- package/dist/core/LambderCreateOptions.js +16 -23
- package/dist/core/LambderFiles.d.ts +21 -7
- package/dist/core/LambderFiles.js +62 -34
- package/dist/core/LambderIndexHtml.js +12 -11
- package/dist/core/LambderPolicyBuilders.d.ts +17 -5
- package/dist/core/LambderPolicyBuilders.js +17 -5
- package/dist/core/LambderPublicFiles.d.ts +11 -5
- package/dist/core/LambderPublicFiles.js +32 -4
- package/dist/core/LambderRequestPath.d.ts +43 -0
- package/dist/core/LambderRequestPath.js +63 -0
- package/dist/core/LambderResponse.d.ts +26 -5
- package/dist/core/LambderResponse.js +157 -70
- package/dist/core/LambderResponseBuilder.d.ts +49 -4
- package/dist/core/LambderResponseBuilder.js +64 -3
- package/dist/core/LambderRouting.d.ts +2 -3
- package/dist/core/LambderRouting.js +22 -7
- package/dist/core/LambderTemplatingEngine.js +211 -32
- package/dist/index.d.ts +25 -8
- package/dist/index.js +13 -4
- package/dist/invoke/LambderInvokeCaller.d.ts +37 -42
- package/dist/invoke/LambderInvokeCaller.js +76 -66
- package/dist/invoke/LambderInvokeOutcome.d.ts +27 -26
- package/dist/invoke/LambderInvokeOutcome.js +9 -22
- package/dist/invoke/LambderLambdaEvent.d.ts +29 -9
- package/dist/invoke/LambderLambdaEvent.js +40 -22
- package/dist/invoke/lambderHandlerTransport.d.ts +9 -10
- package/dist/invoke/lambderHandlerTransport.js +15 -18
- package/dist/mock/LambderMockApp.d.ts +67 -83
- package/dist/mock/LambderMockApp.js +167 -153
- package/dist/mock/LambderMockBrowserCookies.d.ts +24 -28
- package/dist/mock/LambderMockBrowserCookies.js +24 -28
- package/dist/mock/LambderMockCallRecorder.d.ts +15 -22
- package/dist/mock/LambderMockCallRecorder.js +19 -28
- package/dist/mock/LambderMockCreateOptions.d.ts +42 -24
- package/dist/mock/LambderMockEntryRegistry.d.ts +11 -12
- package/dist/mock/LambderMockEntryRegistry.js +24 -29
- package/dist/mock/LambderMockFailureInjector.d.ts +3 -6
- package/dist/mock/LambderMockFailureInjector.js +3 -6
- package/dist/mock/LambderMockTypes.d.ts +78 -108
- package/dist/mock/lambderMockInvokeTransport.d.ts +11 -13
- package/dist/mock/lambderMockInvokeTransport.js +11 -10
- package/dist/mock/lambderMockMswHandler.d.ts +43 -33
- package/dist/mock/lambderMockMswHandler.js +50 -39
- package/dist/mock/lambderMockUploadMswHandler.d.ts +26 -0
- package/dist/mock/lambderMockUploadMswHandler.js +28 -0
- package/dist/mock.d.ts +4 -1
- package/dist/mock.js +6 -3
- package/dist/session/LambderSessionController.d.ts +108 -89
- package/dist/session/LambderSessionController.js +187 -168
- package/dist/session/LambderSessionCrypto.d.ts +16 -7
- package/dist/session/LambderSessionCrypto.js +26 -12
- package/dist/session/LambderSessionManager.d.ts +124 -46
- package/dist/session/LambderSessionManager.js +262 -137
- package/dist/shared/LambderHtml.d.ts +42 -3
- package/dist/shared/LambderHtml.js +127 -7
- package/dist/shared/LambderHtmlPositions.d.ts +173 -0
- package/dist/shared/LambderHtmlPositions.js +652 -0
- package/dist/shared/LambderI18n.d.ts +10 -11
- package/dist/shared/LambderI18n.js +33 -21
- package/dist/shared/contracts/LambderCache.d.ts +66 -0
- package/dist/shared/contracts/LambderCache.js +11 -0
- package/dist/shared/contracts/LambderFileSource.d.ts +6 -6
- package/dist/shared/contracts/LambderFileSource.js +5 -8
- package/dist/shared/contracts/LambderIdempotencyStore.d.ts +51 -22
- package/dist/shared/contracts/LambderIdempotencyStore.js +4 -5
- package/dist/shared/contracts/LambderRateLimiter.d.ts +27 -15
- package/dist/shared/contracts/LambderRateLimiter.js +4 -5
- package/dist/shared/contracts/LambderSessionStore.d.ts +65 -26
- package/dist/shared/contracts/LambderSessionStore.js +5 -6
- package/dist/shared/contracts/LambderUploadBucket.d.ts +154 -0
- package/dist/shared/contracts/LambderUploadBucket.js +74 -0
- package/dist/shared/transport/LambderApiTransport.d.ts +27 -27
- package/dist/shared/transport/LambderApiTransport.js +7 -7
- package/dist/shared/transport/LambderCookieJar.d.ts +28 -35
- package/dist/shared/transport/LambderCookieJar.js +54 -66
- package/dist/shared/transport/lambderCookieJarTransport.d.ts +11 -13
- package/dist/shared/transport/lambderCookieJarTransport.js +24 -23
- package/dist/shared/util/LambderCallAbort.d.ts +5 -5
- package/dist/shared/util/LambderCallAbort.js +5 -5
- package/dist/shared/util/LambderClientIp.d.ts +27 -11
- package/dist/shared/util/LambderClientIp.js +96 -13
- package/dist/shared/util/LambderContentDisposition.d.ts +10 -0
- package/dist/shared/util/LambderContentDisposition.js +13 -0
- package/dist/shared/util/LambderExpiringMap.d.ts +35 -49
- package/dist/shared/util/LambderExpiringMap.js +41 -57
- package/dist/shared/util/LambderNodeModules.js +6 -7
- package/dist/shared/util/LambderOptionChecks.d.ts +4 -4
- package/dist/shared/util/LambderOptionChecks.js +4 -4
- package/dist/shared/util/LambderResponseBrand.d.ts +5 -5
- package/dist/shared/util/LambderResponseBrand.js +5 -5
- package/dist/shared/util/LambderTextDigest.d.ts +7 -5
- package/dist/shared/util/LambderTextDigest.js +11 -5
- package/dist/shared/util/LambderTypeUtilities.d.ts +7 -8
- package/dist/shared/util/LambderTypeUtilities.js +3 -3
- package/dist/shared/util/boundKeyField.d.ts +20 -0
- package/dist/shared/util/boundKeyField.js +34 -0
- package/dist/shared/util/canonicalJson.d.ts +11 -0
- package/dist/shared/util/canonicalJson.js +28 -0
- package/dist/shared/util/joinKeyFields.d.ts +20 -0
- package/dist/shared/util/joinKeyFields.js +22 -0
- package/dist/shared/wire/LambderAnswerHeaders.d.ts +12 -16
- package/dist/shared/wire/LambderAnswerHeaders.js +12 -16
- package/dist/shared/wire/LambderApiContract.d.ts +98 -53
- package/dist/shared/wire/LambderApiOutcome.d.ts +43 -31
- package/dist/shared/wire/LambderApiOutcome.js +48 -23
- package/dist/shared/wire/LambderApiRefusal.d.ts +45 -27
- package/dist/shared/wire/LambderApiRefusal.js +42 -7
- package/dist/shared/wire/LambderApiSignature.d.ts +18 -22
- package/dist/shared/wire/LambderApiSignature.js +16 -19
- package/dist/shared/wire/LambderCallOptions.d.ts +38 -47
- package/dist/shared/wire/LambderCallOptions.js +9 -11
- package/dist/shared/wire/LambderCompressionCodec.d.ts +29 -34
- package/dist/shared/wire/LambderCompressionCodec.js +31 -36
- package/dist/shared/wire/LambderCompressionOption.d.ts +9 -9
- package/dist/shared/wire/LambderCompressionOption.js +9 -9
- package/dist/shared/wire/LambderCrashDetail.d.ts +12 -15
- package/dist/shared/wire/LambderCrashDetail.js +12 -15
- package/dist/shared/wire/LambderDefaultApiPath.d.ts +6 -0
- package/dist/shared/wire/LambderDefaultApiPath.js +6 -0
- package/dist/shared/wire/LambderHttpStatus.d.ts +6 -7
- package/dist/shared/wire/LambderIdempotencyKeyScope.d.ts +89 -0
- package/dist/shared/wire/LambderIdempotencyKeyScope.js +146 -0
- package/dist/shared/wire/LambderInvokeApiId.d.ts +27 -0
- package/dist/shared/wire/LambderInvokeApiId.js +27 -0
- package/dist/shared/wire/LambderOutcomeAssertions.d.ts +6 -7
- package/dist/shared/wire/LambderOutcomeAssertions.js +6 -7
- package/dist/shared/wire/LambderRequestPayload.d.ts +18 -20
- package/dist/shared/wire/LambderRequestPayload.js +4 -6
- package/dist/shared/wire/LambderUploadObjectFields.d.ts +10 -0
- package/dist/shared/wire/LambderUploadObjectFields.js +24 -0
- package/dist/shared/wire/LambderUploadRefusal.d.ts +9 -0
- package/dist/shared/wire/LambderUploadRefusal.js +18 -0
- package/dist/shared/wire/LambderUploadSchemas.d.ts +12 -0
- package/dist/shared/wire/LambderUploadSchemas.js +30 -0
- package/dist/stores/LambderCacheFiller.d.ts +48 -0
- package/dist/stores/LambderCacheFiller.js +119 -0
- package/dist/stores/LambderCacheKeys.d.ts +26 -0
- package/dist/stores/LambderCacheKeys.js +54 -0
- package/dist/stores/LambderCacheValues.d.ts +45 -0
- package/dist/stores/LambderCacheValues.js +74 -0
- package/dist/stores/LambderDdbCache.d.ts +121 -56
- package/dist/stores/LambderDdbCache.js +528 -225
- package/dist/stores/LambderDdbIdempotencyStore.d.ts +33 -22
- package/dist/stores/LambderDdbIdempotencyStore.js +75 -50
- package/dist/stores/LambderDdbRateLimiter.d.ts +76 -20
- package/dist/stores/LambderDdbRateLimiter.js +151 -39
- package/dist/stores/LambderDdbSdk.d.ts +43 -31
- package/dist/stores/LambderDdbSdk.js +80 -38
- package/dist/stores/LambderDdbSessionStore.d.ts +27 -14
- package/dist/stores/LambderDdbSessionStore.js +119 -47
- package/dist/stores/LambderHttpFileSource.d.ts +15 -6
- package/dist/stores/LambderHttpFileSource.js +15 -13
- package/dist/stores/LambderMemoryCache.d.ts +49 -0
- package/dist/stores/LambderMemoryCache.js +113 -0
- package/dist/stores/LambderMemoryIdempotencyStore.d.ts +13 -12
- package/dist/stores/LambderMemoryIdempotencyStore.js +31 -30
- package/dist/stores/LambderMemoryRateLimiter.d.ts +8 -9
- package/dist/stores/LambderMemoryRateLimiter.js +14 -13
- package/dist/stores/LambderMemorySessionStore.d.ts +14 -11
- package/dist/stores/LambderMemorySessionStore.js +38 -19
- package/dist/stores/LambderMemoryUploadBucket.d.ts +99 -0
- package/dist/stores/LambderMemoryUploadBucket.js +219 -0
- package/dist/stores/LambderS3FileSource.d.ts +21 -6
- package/dist/stores/LambderS3FileSource.js +12 -7
- package/dist/stores/LambderS3UploadBucket.d.ts +73 -0
- package/dist/stores/LambderS3UploadBucket.js +144 -0
- package/dist/stores/LambderSdkInstallHint.d.ts +11 -0
- package/dist/stores/LambderSdkInstallHint.js +14 -0
- package/dist/testing/LambderTestApp.d.ts +23 -25
- package/dist/testing/LambderTestApp.js +22 -24
- package/dist/testing/LambderTestVisitor.d.ts +10 -12
- package/dist/testing/LambderTestVisitor.js +15 -15
- package/dist/testing.d.ts +3 -0
- package/dist/testing.js +2 -0
- package/package.json +26 -3
- package/dist/api/LambderApiPolicyEngine.d.ts +0 -47
- package/dist/api/LambderApiPolicyEngine.js +0 -85
- package/dist/shared/util/LambderKeyFields.d.ts +0 -32
- package/dist/shared/util/LambderKeyFields.js +0 -34
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { LambderApiPipeline } from "../api/LambderApiPipeline.js";
|
|
2
2
|
import { readApiEnvelope, cookieValuesByName, lowercaseHeaderNames } from "../api/LambderApiRequest.js";
|
|
3
|
-
import { createApiCallContext } from "../api/LambderApiCallContext.js";
|
|
3
|
+
import { bindCallTools, createApiCallContext } from "../api/LambderApiCallContext.js";
|
|
4
4
|
import { toHttpAnswer } from "../api/LambderApiAnswer.js";
|
|
5
5
|
import { getAnswerHeader } from "../shared/wire/LambderAnswerHeaders.js";
|
|
6
6
|
import { buildApiEnvelope, envelopeAnswer, crashAnswer, } from "../api/LambderApiEnvelope.js";
|
|
@@ -37,13 +37,13 @@ const DEFAULT_SESSION_TTL_SECONDS = 30 * 24 * 60 * 60;
|
|
|
37
37
|
const defaultCookieHost = () => globalThis.location?.host || "localhost";
|
|
38
38
|
/**
|
|
39
39
|
* The mock runtime: the API core (LambderApiPipeline, the same class the
|
|
40
|
-
* Lambda server runs) over memory stores, with
|
|
41
|
-
*
|
|
42
|
-
*
|
|
43
|
-
*
|
|
44
|
-
*
|
|
45
|
-
*
|
|
46
|
-
*
|
|
40
|
+
* Lambda server runs) over memory stores, with typed mock handlers and mock
|
|
41
|
+
* guards where the server has app handlers and guards. Every protocol step
|
|
42
|
+
* (envelope, refusals, sessions and their cookies, guards, rate limits,
|
|
43
|
+
* idempotency, the signature gate) happens in the core; this class resolves
|
|
44
|
+
* a name to an entry, wraps the handler's return into the envelope, and adds
|
|
45
|
+
* what a mock needs: failure injection, latency, a subscription, a call log,
|
|
46
|
+
* reset.
|
|
47
47
|
*
|
|
48
48
|
* Create one with initLambderMock<Contract, SessionData>().create(...),
|
|
49
49
|
* which fixes the contract and session types first so everything else is
|
|
@@ -58,12 +58,11 @@ export class LambderMockApp {
|
|
|
58
58
|
tokenCookieKey;
|
|
59
59
|
csrfCookieKey;
|
|
60
60
|
/**
|
|
61
|
-
* The client IP a transport request carrying none is read as. Public
|
|
62
|
-
*
|
|
63
|
-
*
|
|
64
|
-
*
|
|
65
|
-
*
|
|
66
|
-
* clients where there was one.
|
|
61
|
+
* The client IP a transport request carrying none is read as. Public so
|
|
62
|
+
* every adapter reads the same default the direct transport uses: with a
|
|
63
|
+
* default of its own, an app that set defaultClientIp would show one
|
|
64
|
+
* address through the transport and another through the service worker,
|
|
65
|
+
* and a per-IP rate limit would count two clients where there is one.
|
|
67
66
|
*/
|
|
68
67
|
defaultClientIp;
|
|
69
68
|
/** The host this runtime's cookies belong to (see the cookieHost option). */
|
|
@@ -71,9 +70,8 @@ export class LambderMockApp {
|
|
|
71
70
|
/**
|
|
72
71
|
* The API core, every protocol step of it. Private: the mock's surface is
|
|
73
72
|
* the app, and a consumer reaching past it would be configuring the
|
|
74
|
-
* server's pipeline through a development tool. The
|
|
75
|
-
*
|
|
76
|
-
* requestFromTransport), which is why none of them names this.
|
|
73
|
+
* server's pipeline through a development tool. The adapters use the
|
|
74
|
+
* app's own methods (handleRequest, requestFromTransport) instead.
|
|
77
75
|
*/
|
|
78
76
|
pipeline;
|
|
79
77
|
/** The cookie scope signIn plants under, so signOut can name the same one when it clears them. */
|
|
@@ -93,12 +91,11 @@ export class LambderMockApp {
|
|
|
93
91
|
this.apiVersion = options.apiVersion ?? null;
|
|
94
92
|
this.failures = new LambderMockFailureInjector({ apiVersion: this.apiVersion, latency: options.latency ?? 0 });
|
|
95
93
|
this.recorder = new LambderMockCallRecorder({ callLogSize: options.callLogSize ?? DEFAULT_CALL_LOG_SIZE });
|
|
96
|
-
// The loopback address when nothing names a client, as a request
|
|
97
|
-
// the page itself
|
|
98
|
-
//
|
|
99
|
-
//
|
|
100
|
-
//
|
|
101
|
-
// by the MSW adapter and by every request that names no client.
|
|
94
|
+
// The loopback address when nothing names a client, as for a request
|
|
95
|
+
// from the page itself (the in-process handler transport's default
|
|
96
|
+
// too). Normalized once here because the direct transport, the MSW
|
|
97
|
+
// adapter and every request naming no client all read it, and one
|
|
98
|
+
// textual form per address keeps a per-IP limit to one counter.
|
|
102
99
|
this.defaultClientIp = normalizeClientIp(options.defaultClientIp ?? LOOPBACK_CLIENT_IP);
|
|
103
100
|
this.cookieHost = options.cookieHost ?? defaultCookieHost();
|
|
104
101
|
this.onReset = options.onReset ?? null;
|
|
@@ -121,6 +118,19 @@ export class LambderMockApp {
|
|
|
121
118
|
minApiVersion: options.minApiVersion,
|
|
122
119
|
apiSignatures: options.apiSignatures,
|
|
123
120
|
maxRequestPayloadBytes: options.maxRequestPayloadBytes,
|
|
121
|
+
// The server app's own answer to a bad input, where it has one:
|
|
122
|
+
// without it the mock would answer 422 where the server answers,
|
|
123
|
+
// say, 200 with an errorMessage, and a form's error handling would
|
|
124
|
+
// be tested against an answer production never gives.
|
|
125
|
+
onInvalidInput: options.onInvalidInput
|
|
126
|
+
? async (zodError, ctx) => {
|
|
127
|
+
const answer = await options.onInvalidInput(zodError, ctx);
|
|
128
|
+
if (!answer)
|
|
129
|
+
return null;
|
|
130
|
+
const config = answer.config ?? {};
|
|
131
|
+
return envelopeAnswer(buildApiEnvelope(this.apiVersion, answer.payload ?? null, { ...config, logList: config.logList ?? ctx.logList }), { statusCode: answer.statusCode ?? 200 });
|
|
132
|
+
}
|
|
133
|
+
: undefined,
|
|
124
134
|
sessions: sessionOptions
|
|
125
135
|
? {
|
|
126
136
|
manager: new LambderSessionManager({
|
|
@@ -133,12 +143,11 @@ export class LambderMockApp {
|
|
|
133
143
|
// crypto.subtle, and a memory-only store is nothing
|
|
134
144
|
// anyone can leak, so hashing there protects nothing.
|
|
135
145
|
// Keyed on the store's own isMemoryOnly rather than on
|
|
136
|
-
//
|
|
137
|
-
// own LambderMemorySessionStore
|
|
138
|
-
// on the first session call
|
|
139
|
-
//
|
|
140
|
-
//
|
|
141
|
-
// there to say.
|
|
146
|
+
// whether this runtime created it: an app passing its
|
|
147
|
+
// own LambderMemorySessionStore degrades the same way
|
|
148
|
+
// instead of throwing on the first session call, and a
|
|
149
|
+
// store that outlives the process still gets real
|
|
150
|
+
// hashing.
|
|
142
151
|
crypto: sessionOptions.crypto
|
|
143
152
|
?? (!isWebCryptoAvailable() && (sessionOptions.store?.isMemoryOnly ?? true) ? new LambderPlainSessionCrypto() : undefined),
|
|
144
153
|
}),
|
|
@@ -157,13 +166,12 @@ export class LambderMockApp {
|
|
|
157
166
|
defaultTtlSeconds: idempotencyOptions.defaultTtlSeconds,
|
|
158
167
|
defaultPendingTtlSeconds: idempotencyOptions.defaultPendingTtlSeconds,
|
|
159
168
|
failOpen: idempotencyOptions.failOpen,
|
|
160
|
-
// The engine
|
|
161
|
-
//
|
|
162
|
-
//
|
|
163
|
-
//
|
|
164
|
-
//
|
|
165
|
-
//
|
|
166
|
-
// knows both sides.
|
|
169
|
+
// The engine types this option for the base call context,
|
|
170
|
+
// the one thing every adapter shares, but hands the
|
|
171
|
+
// function the running adapter's context, here the mock's.
|
|
172
|
+
// So it is declared for the mock context (ctx.request is
|
|
173
|
+
// readable) and widened here, the one place that knows
|
|
174
|
+
// both sides.
|
|
167
175
|
callerIdentity: idempotencyOptions.callerIdentity,
|
|
168
176
|
}
|
|
169
177
|
: undefined,
|
|
@@ -177,11 +185,10 @@ export class LambderMockApp {
|
|
|
177
185
|
* the ones the server runs on a definition, and the mock's own "a session
|
|
178
186
|
* endpoint needs the sessions option".
|
|
179
187
|
*
|
|
180
|
-
* One place, so no registration path can skip either check.
|
|
181
|
-
* endpoint on a mock without sessions would
|
|
182
|
-
* and answer
|
|
183
|
-
*
|
|
184
|
-
* create().
|
|
188
|
+
* One place, so no registration path can skip either check. Without the
|
|
189
|
+
* second, a session endpoint on a mock without sessions would register
|
|
190
|
+
* silently and answer its first call with a 500 naming the SERVER's
|
|
191
|
+
* option, for a mistake fixed by one option at create().
|
|
185
192
|
*/
|
|
186
193
|
assertEntryRegistration(definition) {
|
|
187
194
|
this.pipeline.assertRegistration(definition);
|
|
@@ -219,14 +226,13 @@ export class LambderMockApp {
|
|
|
219
226
|
* A public endpoint deliberately left without a mock; a call answers the
|
|
220
227
|
* notMocked refusal carrying the reason.
|
|
221
228
|
*
|
|
222
|
-
* Public and session
|
|
223
|
-
*
|
|
224
|
-
*
|
|
225
|
-
*
|
|
226
|
-
*
|
|
227
|
-
*
|
|
228
|
-
*
|
|
229
|
-
* is a type, so the builder is where it has to be said.
|
|
229
|
+
* Public and session get separate builders, as publicApi and sessionApi
|
|
230
|
+
* do, because the refusal runs through the pipeline so the steps BEFORE
|
|
231
|
+
* dispatch still happen, and the session read is one of them. Were every
|
|
232
|
+
* not-mocked endpoint public, a session endpoint with no session would
|
|
233
|
+
* answer "not mocked" where the server answers sessionExpired, and its
|
|
234
|
+
* events and call log would carry the wrong mode. The contract is a type,
|
|
235
|
+
* so the mode cannot be recovered at runtime: the builder has to say it.
|
|
230
236
|
*/
|
|
231
237
|
notMocked(name, reason) {
|
|
232
238
|
return this.buildNotMockedEntry(name, "public", reason);
|
|
@@ -243,23 +249,21 @@ export class LambderMockApp {
|
|
|
243
249
|
* mockApp.register(userMocks, billingMocks, mockApp.restNotMocked("not mocked yet"));
|
|
244
250
|
* ```
|
|
245
251
|
*
|
|
246
|
-
*
|
|
247
|
-
* register() stays exhaustive by construction, and
|
|
248
|
-
* claims answer the notMocked refusal
|
|
252
|
+
* It lets mocks be adopted over a contract they do not cover yet:
|
|
253
|
+
* register() stays exhaustive by construction, and endpoints nothing
|
|
254
|
+
* claims answer the notMocked refusal with this reason instead of
|
|
249
255
|
* apiNotFound, so a screen that reaches one says "not mocked yet" rather
|
|
250
256
|
* than "unknown error". Strays and duplicates in the explicit slices are
|
|
251
|
-
* refused
|
|
252
|
-
*
|
|
253
|
-
* rest.
|
|
257
|
+
* still refused, and an entry registered later (registerPartial, or a
|
|
258
|
+
* second register) takes its endpoint back from the rest.
|
|
254
259
|
*
|
|
255
|
-
*
|
|
256
|
-
*
|
|
257
|
-
*
|
|
258
|
-
*
|
|
259
|
-
*
|
|
260
|
-
*
|
|
261
|
-
*
|
|
262
|
-
* sessionNotMocked instead.
|
|
260
|
+
* What it cannot do is the session read. The mode of an unregistered name
|
|
261
|
+
* is not knowable at runtime (the contract is a type), so a call it
|
|
262
|
+
* answers is processed as public: the protocol's pre-pass still runs, so
|
|
263
|
+
* a stale client still hears versionExpired, but a signed-out call to an
|
|
264
|
+
* unmocked session endpoint answers "not mocked" where the server answers
|
|
265
|
+
* sessionExpired. Declare an endpoint whose signed-out path a test cares
|
|
266
|
+
* about with sessionNotMocked instead.
|
|
263
267
|
*/
|
|
264
268
|
restNotMocked(reason) {
|
|
265
269
|
return { restNotMockedReason: reason };
|
|
@@ -334,11 +338,10 @@ export class LambderMockApp {
|
|
|
334
338
|
* Whether a call to this name would be answered from the registry, which
|
|
335
339
|
* is what an adapter asks before passing one on.
|
|
336
340
|
*
|
|
337
|
-
* True for every name once a rest entry is registered,
|
|
338
|
-
*
|
|
339
|
-
*
|
|
340
|
-
*
|
|
341
|
-
* answers everything itself and nothing is handed on to the network.
|
|
341
|
+
* True for every name once a rest entry is registered, since it answers
|
|
342
|
+
* whatever nothing else claimed. That makes a rest entry and the MSW
|
|
343
|
+
* adapter's `onUnmocked: "passthrough"` alternatives rather than layers:
|
|
344
|
+
* with one registered, nothing is handed on to the network.
|
|
342
345
|
*/
|
|
343
346
|
hasRegisteredEntry(apiName) {
|
|
344
347
|
return this.entryFor(apiName) !== null || this.registry.restNotMockedReason !== null;
|
|
@@ -349,13 +352,10 @@ export class LambderMockApp {
|
|
|
349
352
|
/**
|
|
350
353
|
* The entry that answers a name nothing registered, when register() was
|
|
351
354
|
* given a rest entry: the notMocked refusal carrying its reason, run
|
|
352
|
-
* through the pipeline as a public endpoint
|
|
353
|
-
*
|
|
354
|
-
*
|
|
355
|
-
*
|
|
356
|
-
* still runs (the signature gate, the payload restore); the session read is
|
|
357
|
-
* the one step this answer cannot have, which is the fidelity limit
|
|
358
|
-
* restNotMocked documents.
|
|
355
|
+
* through the pipeline as a public endpoint, since the mode of an
|
|
356
|
+
* unregistered name cannot be recovered at runtime. Everything before
|
|
357
|
+
* dispatch still runs (the signature gate, the payload restore); the
|
|
358
|
+
* missing session read is the fidelity limit restNotMocked documents.
|
|
359
359
|
*/
|
|
360
360
|
restNotMockedEntry(apiName) {
|
|
361
361
|
const reason = this.registry.restNotMockedReason;
|
|
@@ -387,20 +387,17 @@ export class LambderMockApp {
|
|
|
387
387
|
* latency, the call log and its numbering, the cookies its own transports
|
|
388
388
|
* hold, then onReset, so the app rewinds its own data too.
|
|
389
389
|
*
|
|
390
|
-
* The cookies matter as much as the sessions
|
|
391
|
-
*
|
|
392
|
-
*
|
|
393
|
-
*
|
|
394
|
-
* for itself is emptied, and the cookies a "document" transport mirrored
|
|
395
|
-
* are expired again.
|
|
390
|
+
* The cookies matter as much as the sessions: a jar still holding the
|
|
391
|
+
* token of an emptied store's session reads as signed in until an answer
|
|
392
|
+
* says sessionExpired. So every jar transport() built for itself is
|
|
393
|
+
* emptied, and the cookies a "document" transport mirrored are expired.
|
|
396
394
|
*
|
|
397
|
-
* The registry survives, being
|
|
398
|
-
*
|
|
399
|
-
*
|
|
400
|
-
*
|
|
401
|
-
*
|
|
402
|
-
*
|
|
403
|
-
* does not know what else holds it.
|
|
395
|
+
* The registry survives, being configuration rather than accumulated
|
|
396
|
+
* state. Subscriptions survive too, being how a test watches the runtime;
|
|
397
|
+
* a listener muted for throwing is unmuted, so one bad call does not
|
|
398
|
+
* silence it for the rest of the run. A session store or cookie jar the
|
|
399
|
+
* app supplied survives: the runtime did not create it and does not know
|
|
400
|
+
* what else holds it.
|
|
404
401
|
*/
|
|
405
402
|
reset() {
|
|
406
403
|
this.sessionStore?.reset();
|
|
@@ -417,12 +414,9 @@ export class LambderMockApp {
|
|
|
417
414
|
// -----------------------------------------------------------------------
|
|
418
415
|
/**
|
|
419
416
|
* The four session members refuse in the mock's own words, naming the
|
|
420
|
-
* option a mock is created with.
|
|
421
|
-
*
|
|
422
|
-
*
|
|
423
|
-
* which is the SERVER's option name: the mock's is `sessions`, and a
|
|
424
|
-
* reader who goes looking for `session` on create() does not find it.
|
|
425
|
-
* The registration path was fixed for exactly this one method over.
|
|
417
|
+
* option a mock is created with. The pipeline's guard names the SERVER's
|
|
418
|
+
* option (`session`), which a reader does not find on the mock's create()
|
|
419
|
+
* (`sessions`); assertEntryRegistration does the same for registration.
|
|
426
420
|
*/
|
|
427
421
|
assertSessionsConfigured(member) {
|
|
428
422
|
if (!this.pipeline.hasSessions)
|
|
@@ -453,17 +447,16 @@ export class LambderMockApp {
|
|
|
453
447
|
const headers = {};
|
|
454
448
|
ctx.responseHeaders.applyInto(headers);
|
|
455
449
|
const setCookies = getAnswerHeader(headers, "Set-Cookie") ?? [];
|
|
456
|
-
//
|
|
457
|
-
//
|
|
458
|
-
//
|
|
459
|
-
//
|
|
460
|
-
// cookie domain, silently.
|
|
450
|
+
// Planted for the host the controller wrote them for: a jar checks
|
|
451
|
+
// every Domain against the sending host and refuses one it cannot
|
|
452
|
+
// check, so unscoped, the session cookie of an app that configures a
|
|
453
|
+
// cookie domain would be dropped silently.
|
|
461
454
|
options.jar?.storeSetCookies(setCookies, { host });
|
|
462
455
|
// Through the same mirror every other cookie writer uses. Behind the
|
|
463
|
-
// MSW adapter the
|
|
464
|
-
//
|
|
465
|
-
//
|
|
466
|
-
//
|
|
456
|
+
// MSW adapter the page's caller reads its CSRF token from
|
|
457
|
+
// document.cookie, not the jar, so a signIn that only filled a jar
|
|
458
|
+
// would leave the token empty and every session call would answer
|
|
459
|
+
// sessionExpired.
|
|
467
460
|
this.browserCookies.mirrorSetCookies(setCookies);
|
|
468
461
|
return created;
|
|
469
462
|
}
|
|
@@ -532,16 +525,27 @@ export class LambderMockApp {
|
|
|
532
525
|
// idempotency, and on one that does not it is a value nothing
|
|
533
526
|
// reads, so handing it over typed as a string would be the lie.
|
|
534
527
|
idempotencyKey: typeof request.idempotencyKey === "string" ? request.idempotencyKey : undefined,
|
|
535
|
-
sessions: undefined,
|
|
536
528
|
});
|
|
537
|
-
// The controller reads and writes this very context, so it is
|
|
538
|
-
// after it
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
529
|
+
// The controller reads and writes this very context, so it is bound
|
|
530
|
+
// after it, the way the server binds its own. Without sessions
|
|
531
|
+
// configured, touching it says why.
|
|
532
|
+
const chargeRateLimit = async (policy, key, refuse) => {
|
|
533
|
+
const { checkResult, refusal } = await pipeline.chargeRateLimit(policy, { apiName: request.apiName, ip: request.ip, session: ctx.session, key });
|
|
534
|
+
if (refuse && refusal)
|
|
535
|
+
throw refusal;
|
|
536
|
+
return checkResult;
|
|
537
|
+
};
|
|
538
|
+
bindCallTools(ctx, {
|
|
539
|
+
getters: {
|
|
540
|
+
sessionController: () => {
|
|
541
|
+
if (!pipeline.hasSessions)
|
|
542
|
+
throw new Error(`LambderMockApp: ctx.sessionController on "${request.apiName}" needs the sessions option at creation.`);
|
|
543
|
+
return pipeline.sessionController(ctx, LambderApiPipeline.sessionInfoOf(request));
|
|
544
|
+
},
|
|
545
|
+
},
|
|
546
|
+
methods: {
|
|
547
|
+
rateLimit: async (policy, key) => { await chargeRateLimit(policy, key, true); },
|
|
548
|
+
isRateLimited: (policy, key) => chargeRateLimit(policy, key, false),
|
|
545
549
|
},
|
|
546
550
|
});
|
|
547
551
|
return ctx;
|
|
@@ -595,24 +599,23 @@ export class LambderMockApp {
|
|
|
595
599
|
let outcome;
|
|
596
600
|
let error;
|
|
597
601
|
try {
|
|
598
|
-
// The protocol's pre-pass, run before the name is resolved,
|
|
599
|
-
//
|
|
600
|
-
//
|
|
601
|
-
//
|
|
602
|
-
//
|
|
603
|
-
//
|
|
604
|
-
// the
|
|
605
|
-
//
|
|
606
|
-
// for. run() calls prepare again, which is safe by construction.
|
|
602
|
+
// The protocol's pre-pass, run before the name is resolved, as on
|
|
603
|
+
// the server. Otherwise an unknown name would reach the notFound
|
|
604
|
+
// refusal without the signature gate or the payload restore (a
|
|
605
|
+
// stale client or a malformed compressed payload answered unlike
|
|
606
|
+
// the server), and the request event would carry the wire fields
|
|
607
|
+
// instead of the payload, so a dev panel would show nothing for
|
|
608
|
+
// exactly the compressed calls. run() calls prepare again, which
|
|
609
|
+
// is safe by construction.
|
|
607
610
|
const prepared = await this.pipeline.prepare(request);
|
|
608
611
|
this.emit(this.requestEvent(id, request, mode, startedAt));
|
|
609
612
|
await this.failures.wait(this.failures.latencyFor(request.apiName), request.signal);
|
|
610
613
|
if (this.failures.offline)
|
|
611
614
|
throw new LambderMockTransportError("offline");
|
|
612
|
-
//
|
|
613
|
-
// before it reaches a handler:
|
|
614
|
-
//
|
|
615
|
-
//
|
|
615
|
+
// Taken after the wait and the offline check, both of which end
|
|
616
|
+
// the call before it reaches a handler: taken first, a queued
|
|
617
|
+
// failNext would be spent on a call it never got to fail, and the
|
|
618
|
+
// next call, the one the test arranged it for, would answer
|
|
616
619
|
// normally.
|
|
617
620
|
const failure = this.failures.take(request.apiName);
|
|
618
621
|
if (failure) {
|
|
@@ -623,7 +626,7 @@ export class LambderMockApp {
|
|
|
623
626
|
answer = prepared;
|
|
624
627
|
}
|
|
625
628
|
else if (!entry) {
|
|
626
|
-
answer = this.pipeline.answerUnknownApi(
|
|
629
|
+
answer = this.pipeline.answerUnknownApi(ctx);
|
|
627
630
|
outcome = "unknownApi";
|
|
628
631
|
}
|
|
629
632
|
else {
|
|
@@ -639,11 +642,10 @@ export class LambderMockApp {
|
|
|
639
642
|
}));
|
|
640
643
|
}
|
|
641
644
|
// A notMocked entry refuses where a handler would run, not
|
|
642
|
-
// ahead of the pipeline
|
|
643
|
-
//
|
|
644
|
-
//
|
|
645
|
-
//
|
|
646
|
-
// the events at all.
|
|
645
|
+
// ahead of the pipeline, so the steps that precede
|
|
646
|
+
// dispatch still happen: a stale client hears
|
|
647
|
+
// versionExpired as it would from the server, and a
|
|
648
|
+
// compressed payload reaches the events.
|
|
647
649
|
: async () => {
|
|
648
650
|
throw new LambderApiRefusal(`Not mocked: ${entry.notMockedReason}`, {
|
|
649
651
|
errorMessage: { type: "warning", code: LAMBDER_REFUSAL_CODES.notMocked, content: `"${request.apiName}" is not mocked: ${entry.notMockedReason}` },
|
|
@@ -670,14 +672,12 @@ export class LambderMockApp {
|
|
|
670
672
|
: crashAnswer(this.apiVersion);
|
|
671
673
|
outcome = "crash";
|
|
672
674
|
}
|
|
673
|
-
// Every header written during the call belongs on the answer,
|
|
674
|
-
//
|
|
675
|
-
//
|
|
676
|
-
//
|
|
677
|
-
//
|
|
678
|
-
//
|
|
679
|
-
// applyInto is idempotent, so the answers the pipeline already handled
|
|
680
|
-
// are unaffected.
|
|
675
|
+
// Every header written during the call belongs on the answer, however
|
|
676
|
+
// the call ended. The pipeline applies them to the answers it produces,
|
|
677
|
+
// but a crash unwinds past it and an injected failure never reaches
|
|
678
|
+
// it: a handler that created a session and then threw would otherwise
|
|
679
|
+
// leave the jar with no cookie and nothing to explain it. applyInto is
|
|
680
|
+
// idempotent, so answers the pipeline already handled are unaffected.
|
|
681
681
|
ctx.responseHeaders.applyInto(answer.headers);
|
|
682
682
|
this.recorder.settle(facts, { answer, guardsRun: trace.guardsRun, ...(outcome ? { outcome } : {}), ...(error ? { error } : {}) });
|
|
683
683
|
return answer;
|
|
@@ -699,7 +699,14 @@ export class LambderMockApp {
|
|
|
699
699
|
host: transportRequest.siteHost || this.cookieHost,
|
|
700
700
|
...(transportRequest.signal ? { signal: transportRequest.signal } : {}),
|
|
701
701
|
};
|
|
702
|
-
|
|
702
|
+
// Through the wire's own serialization, as every server-bound
|
|
703
|
+
// transport sends it: the handler gets a parse of the JSON, never the
|
|
704
|
+
// page's object. Handed over by reference, a handler that stores the
|
|
705
|
+
// payload would share it with the page's form (later edits changing
|
|
706
|
+
// the "saved" record with no call), a Date would stay a Date and a
|
|
707
|
+
// key set to undefined keep its place, none of which a server sees.
|
|
708
|
+
const envelope = JSON.parse(JSON.stringify(buildTransportEnvelope(transportRequest)));
|
|
709
|
+
const request = readApiEnvelope(envelope, info);
|
|
703
710
|
if (!request)
|
|
704
711
|
throw new Error("LambderMockApp: the transport request names no api.");
|
|
705
712
|
return request;
|
|
@@ -732,12 +739,19 @@ export class LambderMockApp {
|
|
|
732
739
|
if (jar !== cookies)
|
|
733
740
|
this.browserCookies.adoptJar(jar);
|
|
734
741
|
// The runtime's one cookie host, so the jar scopes what it sends the
|
|
735
|
-
// way the browser this transport stands in for would
|
|
736
|
-
//
|
|
737
|
-
//
|
|
742
|
+
// way the browser this transport stands in for would; scoped to
|
|
743
|
+
// whatever host a caller names, cookies signIn planted would go
|
|
744
|
+
// unsent.
|
|
738
745
|
const withJar = lambderCookieJarTransport(direct, { jar, csrfCookieKey: this.csrfCookieKey, host: this.cookieHost });
|
|
746
|
+
// Where the page's readable cookies live, one answer per mode. In
|
|
747
|
+
// memory mode it is this jar alone, so the token the caller read is
|
|
748
|
+
// dropped and the jar fills in its own CSRF cookie. A page's caller
|
|
749
|
+
// reading document.cookie would find what signIn mirrors for the MSW
|
|
750
|
+
// adapter's sake and no later answer of this transport writes, so
|
|
751
|
+
// after a logout and a login it would post the first session's token
|
|
752
|
+
// and be signed out.
|
|
739
753
|
if (cookies !== "document")
|
|
740
|
-
return transportCarrying(withJar, jar);
|
|
754
|
+
return transportCarrying(async (request) => await withJar({ ...request, token: "" }), jar);
|
|
741
755
|
// The page's own cookie storage sees the non-HttpOnly cookies (the
|
|
742
756
|
// CSRF token), so the caller's cookie read and clear paths run for
|
|
743
757
|
// real; the HttpOnly session cookie stays in the jar, as a browser
|
|
@@ -760,9 +774,9 @@ export class LambderMockApp {
|
|
|
760
774
|
/**
|
|
761
775
|
* Takes a jar an adapter built for itself as the runtime's own, so reset()
|
|
762
776
|
* empties it with the rest. The MSW adapter's jar holds the session
|
|
763
|
-
* cookies of calls that never touch transport()
|
|
764
|
-
*
|
|
765
|
-
*
|
|
777
|
+
* cookies of calls that never touch transport(); left full after a reset,
|
|
778
|
+
* the next request would carry a token for a session the emptied store no
|
|
779
|
+
* longer has.
|
|
766
780
|
*/
|
|
767
781
|
adoptCookieJar(jar) {
|
|
768
782
|
this.browserCookies.adoptJar(jar);
|
|
@@ -804,12 +818,12 @@ export const initLambderMock = () => ({
|
|
|
804
818
|
* The mock app, with the guard map and the rate-limit policies inferred
|
|
805
819
|
* from the options.
|
|
806
820
|
*
|
|
807
|
-
* `const` on each of them
|
|
808
|
-
*
|
|
809
|
-
*
|
|
810
|
-
*
|
|
811
|
-
*
|
|
812
|
-
*
|
|
821
|
+
* `const` on each of them pins a restatement to the contract, at a cost:
|
|
822
|
+
* inferring a generic from an object literal switches excess-property
|
|
823
|
+
* checking off for the whole literal, nested objects included, so a typo
|
|
824
|
+
* inside `rateLimits.policies` or `idempotency` would compile and be
|
|
825
|
+
* dropped. `I` exists for the same reason `P` does, and
|
|
826
|
+
* LambderMockSurplusKeys puts the error back on the key.
|
|
813
827
|
*/
|
|
814
828
|
create(options) {
|
|
815
829
|
return new LambderMockApp(options);
|
|
@@ -1,21 +1,17 @@
|
|
|
1
1
|
import { type LambderCookieJar } from "../shared/transport/LambderCookieJar.js";
|
|
2
2
|
/**
|
|
3
3
|
* Where the runtime's cookies live outside its own answers: the jars it built
|
|
4
|
-
* for itself, and the copies it planted in the page's
|
|
4
|
+
* for itself, and the copies it planted in the page's cookie storage. A
|
|
5
|
+
* collaborator of LambderMockApp that owns state nothing else touches, meets
|
|
6
|
+
* the runtime at two calls (an answer coming back, reset()), and holds the
|
|
7
|
+
* one outside dependency, `document`, keeping the app free of browser
|
|
8
|
+
* conditionals.
|
|
5
9
|
*
|
|
6
|
-
* The
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
* Both the direct transport's "document" mode and the MSW adapter come through
|
|
13
|
-
* one instance, so there is one mirror implementation and one record of what
|
|
14
|
-
* was planted. They carried a copy each before: the MSW copy dropped `Secure`
|
|
15
|
-
* on a page that is not a secure context and the transport's did not, so on
|
|
16
|
-
* plain http (device testing on a LAN address) the browser silently refused
|
|
17
|
-
* the CSRF cookie and every session call failed its CSRF check with nothing in
|
|
18
|
-
* any log to say why.
|
|
10
|
+
* The direct transport's "document" mode and the MSW adapter share one
|
|
11
|
+
* instance, so they cannot disagree on dropping `Secure`: on plain http
|
|
12
|
+
* (device testing on a LAN address) a kept `Secure` makes the browser
|
|
13
|
+
* silently refuse the CSRF cookie, failing every session call's CSRF check
|
|
14
|
+
* with nothing in any log.
|
|
19
15
|
*/
|
|
20
16
|
export declare class LambderMockBrowserCookies {
|
|
21
17
|
/** The jars transport() and the adapters built for themselves, which reset() is therefore free to empty. */
|
|
@@ -24,32 +20,32 @@ export declare class LambderMockBrowserCookies {
|
|
|
24
20
|
private readonly mirroredCookies;
|
|
25
21
|
/**
|
|
26
22
|
* Takes a jar built for the runtime as the runtime's own, so reset()
|
|
27
|
-
* empties it with the rest. A jar the app passed in stays the app's,
|
|
28
|
-
*
|
|
29
|
-
*
|
|
23
|
+
* empties it with the rest. A jar the app passed in stays the app's, like
|
|
24
|
+
* an app-supplied session store: the runtime does not know what else
|
|
25
|
+
* holds it.
|
|
30
26
|
*/
|
|
31
27
|
adoptJar(jar: LambderCookieJar): void;
|
|
32
28
|
/**
|
|
33
29
|
* Mirrors an answer's non-HttpOnly cookies into document.cookie and
|
|
34
30
|
* remembers them for reset().
|
|
35
31
|
*
|
|
36
|
-
* HttpOnly cookies are skipped
|
|
37
|
-
*
|
|
38
|
-
*
|
|
39
|
-
*
|
|
40
|
-
* `__Host-` or `__Secure-`
|
|
41
|
-
*
|
|
42
|
-
*
|
|
32
|
+
* HttpOnly cookies are skipped as a real browser skips them; the jar is
|
|
33
|
+
* the store no script can reach. `Secure` is dropped where the page is not
|
|
34
|
+
* a secure context, since the browser would refuse the write and plain
|
|
35
|
+
* http on a LAN address has to keep working. The browser then discards a
|
|
36
|
+
* `__Host-` or `__Secure-` name for breaking its prefix rule, which is
|
|
37
|
+
* correct: such a name cannot work on plain http, and localhost is a
|
|
38
|
+
* secure context.
|
|
43
39
|
*/
|
|
44
40
|
mirrorSetCookies(setCookies: readonly string[]): void;
|
|
45
41
|
/**
|
|
46
42
|
* Empties the jars the runtime owns and expires what it mirrored, which is
|
|
47
43
|
* what clearing the page's cookie storage would do.
|
|
48
44
|
*
|
|
49
|
-
*
|
|
50
|
-
* while a jar still holds
|
|
51
|
-
*
|
|
52
|
-
*
|
|
45
|
+
* Part of a rewind as much as the session store is: emptying the store
|
|
46
|
+
* while a jar still holds one of its session tokens leaves the next call
|
|
47
|
+
* carrying a dead session, which reads as signed in until the answer says
|
|
48
|
+
* sessionExpired.
|
|
53
49
|
*/
|
|
54
50
|
reset(): void;
|
|
55
51
|
}
|