lambder 7.2.5 → 8.0.2
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 +1021 -3
- package/README.md +43 -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 +74 -61
- package/dist/api/LambderApiIdempotency.js +226 -151
- package/dist/api/LambderApiOutputValidationError.d.ts +32 -0
- package/dist/api/LambderApiOutputValidationError.js +50 -0
- package/dist/api/LambderApiPipeline.d.ts +77 -39
- package/dist/api/LambderApiPipeline.js +135 -62
- package/dist/api/LambderApiRateLimits.d.ts +208 -54
- package/dist/api/LambderApiRateLimits.js +197 -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/freshProcessVerifier.d.ts +13 -0
- package/dist/build/freshProcessVerifier.js +19 -0
- package/dist/build/writeApiSignatures.d.ts +109 -0
- package/dist/build/writeApiSignatures.js +222 -0
- package/dist/build.d.ts +9 -0
- package/dist/build.js +8 -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/lambderFetchTransport.d.ts +4 -1
- package/dist/client/lambderFetchTransport.js +52 -28
- package/dist/client.d.ts +5 -3
- package/dist/client.js +2 -1
- package/dist/core/Lambder.d.ts +161 -69
- package/dist/core/Lambder.js +370 -226
- 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 +28 -7
- package/dist/core/LambderFiles.js +73 -33
- 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 +15 -8
- package/dist/index.js +5 -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 +44 -10
- package/dist/invoke/LambderLambdaEvent.js +80 -37
- package/dist/invoke/lambderHandlerTransport.d.ts +12 -10
- package/dist/invoke/lambderHandlerTransport.js +16 -19
- 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 +33 -29
- package/dist/mock/lambderMockMswHandler.js +50 -39
- package/dist/mock.d.ts +3 -1
- package/dist/mock.js +5 -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 +136 -47
- package/dist/session/LambderSessionManager.js +280 -139
- 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/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/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/LambderTestingDoors.d.ts +29 -0
- package/dist/shared/util/LambderTestingDoors.js +29 -0
- 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 +107 -32
- package/dist/shared/wire/LambderApiOutcome.d.ts +43 -31
- package/dist/shared/wire/LambderApiOutcome.js +48 -23
- package/dist/shared/wire/LambderApiRefusal.d.ts +39 -27
- package/dist/shared/wire/LambderApiRefusal.js +36 -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 +79 -0
- package/dist/shared/wire/LambderOutcomeAssertions.js +112 -0
- package/dist/shared/wire/LambderRequestPayload.d.ts +18 -20
- package/dist/shared/wire/LambderRequestPayload.js +4 -6
- 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 +79 -33
- 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/LambderS3FileSource.d.ts +21 -6
- package/dist/stores/LambderS3FileSource.js +12 -7
- package/dist/testing/LambderTestApp.d.ts +176 -0
- package/dist/testing/LambderTestApp.js +204 -0
- package/dist/testing/LambderTestVisitor.d.ts +153 -0
- package/dist/testing/LambderTestVisitor.js +154 -0
- package/dist/testing.d.ts +27 -0
- package/dist/testing.js +24 -0
- package/package.json +20 -3
- package/dist/api/LambderApiPolicyEngine.d.ts +0 -36
- package/dist/api/LambderApiPolicyEngine.js +0 -77
- package/dist/shared/util/LambderKeyFields.d.ts +0 -32
- package/dist/shared/util/LambderKeyFields.js +0 -34
|
@@ -106,17 +106,16 @@ export interface LambderI18nInstance<TLanguages extends Record<string, LambderLa
|
|
|
106
106
|
[D in TDefault]: Partial<Record<keyof TContract, never>>;
|
|
107
107
|
} & TExt): LambderI18nInstance<TLanguages, TDefault, TEnforced, TContract & TExt[TDefault]>;
|
|
108
108
|
/**
|
|
109
|
-
* Run the loaders a language has in this instance and
|
|
110
|
-
* sharing its root,
|
|
111
|
-
* Defaults to the active language
|
|
112
|
-
*
|
|
113
|
-
*
|
|
114
|
-
*
|
|
115
|
-
*
|
|
116
|
-
*
|
|
117
|
-
*
|
|
118
|
-
*
|
|
119
|
-
* what is still missing.
|
|
109
|
+
* Run the loaders a language has in this instance and every instance
|
|
110
|
+
* sharing its root, resolving once their dictionaries are merged.
|
|
111
|
+
* Defaults to the active language. Until then `t` falls back per key to
|
|
112
|
+
* the default language, so await it before the first render, and before
|
|
113
|
+
* `setLanguage` to switch without a flash of the default language.
|
|
114
|
+
* Change listeners fire once per load, however many calls share it. A
|
|
115
|
+
* loader that answered never runs again; one that rejected rejects this
|
|
116
|
+
* call and is retried on the next. Creating an extension loads nothing:
|
|
117
|
+
* one created after its language was loaded needs its own
|
|
118
|
+
* `loadLanguage()`, which runs only what is still missing.
|
|
120
119
|
*/
|
|
121
120
|
loadLanguage(code?: keyof TLanguages & string): Promise<void>;
|
|
122
121
|
/** Merge additional translations at runtime (e.g. fetched from an API). Notifies change listeners. */
|
|
@@ -9,9 +9,14 @@
|
|
|
9
9
|
// ---------------------------------------------------------------------------
|
|
10
10
|
// Implementation
|
|
11
11
|
// ---------------------------------------------------------------------------
|
|
12
|
-
/**
|
|
12
|
+
/**
|
|
13
|
+
* Browser detection: ordered prefs, full code then primary subtag. Only in a
|
|
14
|
+
* page, where there is a document: Node 21 and later, Deno and Bun define
|
|
15
|
+
* `navigator.languages` too, from the process locale, and a server's locale
|
|
16
|
+
* is not its reader's.
|
|
17
|
+
*/
|
|
13
18
|
const detectBrowserLanguage = (isCode) => {
|
|
14
|
-
if (typeof navigator === "undefined")
|
|
19
|
+
if (typeof document === "undefined" || typeof navigator === "undefined")
|
|
15
20
|
return null;
|
|
16
21
|
const prefs = navigator.languages?.length ? navigator.languages : [navigator.language];
|
|
17
22
|
for (const pref of prefs ?? []) {
|
|
@@ -30,33 +35,37 @@ class LanguageState {
|
|
|
30
35
|
defaultLanguage;
|
|
31
36
|
customDetect;
|
|
32
37
|
override = null;
|
|
33
|
-
|
|
38
|
+
/** A throwing detector is reported once rather than on every t() call that runs it. */
|
|
39
|
+
detectorFailureReported = false;
|
|
34
40
|
listeners = new Set();
|
|
35
41
|
constructor(isCode, defaultLanguage, customDetect) {
|
|
36
42
|
this.isCode = isCode;
|
|
37
43
|
this.defaultLanguage = defaultLanguage;
|
|
38
44
|
this.customDetect = customDetect;
|
|
39
45
|
}
|
|
46
|
+
/**
|
|
47
|
+
* The active language: the one set, else detected afresh on every read.
|
|
48
|
+
* Detection is cheap, and what it reads lives outside this instance (the
|
|
49
|
+
* path an SPA navigates, the browser's languages), so remembering its
|
|
50
|
+
* first answer would keep a page on /en/ after it moved to /tr/.
|
|
51
|
+
*/
|
|
40
52
|
resolve() {
|
|
41
53
|
if (this.override)
|
|
42
54
|
return this.override;
|
|
43
|
-
if (this.detected)
|
|
44
|
-
return this.detected;
|
|
45
55
|
// Fail-open: a broken app detector must not take down every t() call.
|
|
46
56
|
let custom = null;
|
|
47
57
|
try {
|
|
48
58
|
custom = this.customDetect?.();
|
|
49
59
|
}
|
|
50
60
|
catch (err) {
|
|
51
|
-
|
|
61
|
+
if (!this.detectorFailureReported) {
|
|
62
|
+
this.detectorFailureReported = true;
|
|
63
|
+
console.error("LambderI18n: detectLanguage threw; continuing detection chain.", err);
|
|
64
|
+
}
|
|
52
65
|
}
|
|
53
|
-
if (custom && this.isCode(custom))
|
|
54
|
-
this.detected = custom;
|
|
66
|
+
if (custom && this.isCode(custom))
|
|
55
67
|
return custom;
|
|
56
|
-
|
|
57
|
-
const browser = detectBrowserLanguage(this.isCode);
|
|
58
|
-
this.detected = browser ?? this.defaultLanguage;
|
|
59
|
-
return this.detected;
|
|
68
|
+
return detectBrowserLanguage(this.isCode) ?? this.defaultLanguage;
|
|
60
69
|
}
|
|
61
70
|
set(code) {
|
|
62
71
|
if (!this.isCode(code))
|
|
@@ -68,7 +77,6 @@ class LanguageState {
|
|
|
68
77
|
}
|
|
69
78
|
reset() {
|
|
70
79
|
this.override = null;
|
|
71
|
-
this.detected = null;
|
|
72
80
|
this.notify(this.resolve());
|
|
73
81
|
}
|
|
74
82
|
/** Re-notify listeners without a language change (e.g. dictionaries changed). */
|
|
@@ -91,14 +99,18 @@ class LanguageState {
|
|
|
91
99
|
}
|
|
92
100
|
}
|
|
93
101
|
}
|
|
102
|
+
/**
|
|
103
|
+
* Fills `{name}` tokens in one pass over the template, so a value is inserted
|
|
104
|
+
* as it is: a display name "Eve {org}" stays that, rather than having its own
|
|
105
|
+
* `{org}` filled by the next parameter. A token with no parameter stays.
|
|
106
|
+
* Own properties only, through Object.prototype.hasOwnProperty rather than
|
|
107
|
+
* Object.hasOwn: this runs in the browser bundle, and a browser without
|
|
108
|
+
* ES2022 (Safari before 15.4) would throw on the first parameterised text.
|
|
109
|
+
*/
|
|
94
110
|
const interpolate = (text, params) => {
|
|
95
111
|
if (!params)
|
|
96
112
|
return text;
|
|
97
|
-
|
|
98
|
-
for (const [token, value] of Object.entries(params)) {
|
|
99
|
-
out = out.split(`{${token}}`).join(String(value));
|
|
100
|
-
}
|
|
101
|
-
return out;
|
|
113
|
+
return text.replace(/\{([^{}]+)\}/g, (token, name) => Object.prototype.hasOwnProperty.call(params, name) ? String(params[name]) : token);
|
|
102
114
|
};
|
|
103
115
|
const layerLookup = (layer, lang, key) => {
|
|
104
116
|
for (let node = layer; node; node = node.parent) {
|
|
@@ -151,9 +163,9 @@ const startLayerLoad = (core, layer, lang, loader) => {
|
|
|
151
163
|
const load = Promise.resolve()
|
|
152
164
|
.then(loader)
|
|
153
165
|
.then((loaded) => {
|
|
154
|
-
// A loader that answered is spent even when its answer is refused
|
|
155
|
-
// running it again would
|
|
156
|
-
//
|
|
166
|
+
// A loader that answered is spent even when its answer is refused,
|
|
167
|
+
// since running it again would fail the same way. Only a loader
|
|
168
|
+
// that rejected stays, to be retried.
|
|
157
169
|
layer.loaders.delete(lang);
|
|
158
170
|
if (layer.loaders.size === 0)
|
|
159
171
|
core.lazyLayers.delete(layer);
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The cache vocabulary every cache in Lambder shares: how an entry is
|
|
3
|
+
* addressed, what writing and listing take, and the methods an app calls.
|
|
4
|
+
*
|
|
5
|
+
* Kept apart from LambderDdbCache for the reason the rate-limit and
|
|
6
|
+
* idempotency contracts are: an app that types its caches against this
|
|
7
|
+
* interface can hold a LambderMemoryCache in a test and a LambderDdbCache in
|
|
8
|
+
* production without the test's import graph ever reaching the DynamoDB
|
|
9
|
+
* store. Pure and dependency-free.
|
|
10
|
+
*/
|
|
11
|
+
/**
|
|
12
|
+
* Where a value lives. A plain string addresses one entry. `{ pk, sk }` puts
|
|
13
|
+
* the entry in a partition it can share with others, so a group can be
|
|
14
|
+
* listed or dropped in one call: `{ pk: "store:nyc-01", sk: "1700:1800" }`
|
|
15
|
+
* keeps every cached window of one store together.
|
|
16
|
+
*/
|
|
17
|
+
export type LambderCacheKey = string | {
|
|
18
|
+
pk: string;
|
|
19
|
+
sk: string;
|
|
20
|
+
};
|
|
21
|
+
export interface LambderCacheSetOptions {
|
|
22
|
+
/** How long the entry lives. Default: the cache's own defaultTtlSeconds. */
|
|
23
|
+
ttlSeconds?: number;
|
|
24
|
+
}
|
|
25
|
+
export interface LambderCacheListOptions {
|
|
26
|
+
/** Only sort keys starting with this prefix. */
|
|
27
|
+
prefix?: string;
|
|
28
|
+
/** Cap on RESULTS, not on entries read. */
|
|
29
|
+
limit?: number;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* What an app asks of a cache. LambderDdbCache and LambderMemoryCache
|
|
33
|
+
* implement it with the same rules (the same key limits, the same JSON round
|
|
34
|
+
* trip, the same expiry), so code written against this interface behaves the
|
|
35
|
+
* same over either.
|
|
36
|
+
*
|
|
37
|
+
* Values are JSON: what `set` is handed is stored as its JSON text and `get`
|
|
38
|
+
* hands back a parse of it, never the object that was stored.
|
|
39
|
+
*/
|
|
40
|
+
export interface LambderCache {
|
|
41
|
+
/** The value, or undefined when it is absent or past its TTL. */
|
|
42
|
+
get<T>(key: LambderCacheKey): Promise<T | undefined>;
|
|
43
|
+
has(key: LambderCacheKey): Promise<boolean>;
|
|
44
|
+
/** Stores the value's JSON; throws for a value JSON cannot represent (undefined, a function). */
|
|
45
|
+
set<T>(key: LambderCacheKey, value: T, options?: LambderCacheSetOptions): Promise<void>;
|
|
46
|
+
/** True when there was a live entry to delete: a load in progress for the key, or an entry past its TTL, is none. */
|
|
47
|
+
delete(key: LambderCacheKey): Promise<boolean>;
|
|
48
|
+
/** Drops every entry stored under one `pk` and answers how many live ones there were. */
|
|
49
|
+
deletePartition(partition: string): Promise<number>;
|
|
50
|
+
/** The live sort keys stored under one `pk`, in sort-key order. Plain-string entries have none, so they never appear. */
|
|
51
|
+
listSortKeys(partition: string, options?: LambderCacheListOptions): Promise<string[]>;
|
|
52
|
+
/**
|
|
53
|
+
* The cached value, or the loader's once it has been stored, as the
|
|
54
|
+
* stored JSON either way, so the call that filled the entry answers what
|
|
55
|
+
* every later one does. Concurrent calls for one key in one process share
|
|
56
|
+
* a single load, each handed a parse of its own, so one caller's change to
|
|
57
|
+
* its answer never shows in another's. A loader's undefined is not cached
|
|
58
|
+
* (return null to cache "not found"), and a value the cache fails to store
|
|
59
|
+
* is handed back uncached, as the same JSON. So is the value of a load
|
|
60
|
+
* that a `set`, `delete` or `deletePartition` of the key overtook: the
|
|
61
|
+
* loader may have read its source before that write, so its value never
|
|
62
|
+
* lands over it. An invalid option throws before anything is read or
|
|
63
|
+
* loaded.
|
|
64
|
+
*/
|
|
65
|
+
getOrSet<T>(key: LambderCacheKey, loader: () => Promise<T>, options?: LambderCacheSetOptions): Promise<T>;
|
|
66
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The cache vocabulary every cache in Lambder shares: how an entry is
|
|
3
|
+
* addressed, what writing and listing take, and the methods an app calls.
|
|
4
|
+
*
|
|
5
|
+
* Kept apart from LambderDdbCache for the reason the rate-limit and
|
|
6
|
+
* idempotency contracts are: an app that types its caches against this
|
|
7
|
+
* interface can hold a LambderMemoryCache in a test and a LambderDdbCache in
|
|
8
|
+
* production without the test's import graph ever reaching the DynamoDB
|
|
9
|
+
* store. Pure and dependency-free.
|
|
10
|
+
*/
|
|
11
|
+
export {};
|
|
@@ -5,12 +5,12 @@ export type LambderFile = {
|
|
|
5
5
|
};
|
|
6
6
|
/**
|
|
7
7
|
* Where an app's files come from: the `files` option at creation, read by
|
|
8
|
-
* servePublicFiles, serveIndexHtml, res.file and res.templateFile alike
|
|
9
|
-
* through the instance's one reader (LambderFiles)
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
8
|
+
* servePublicFiles, serveIndexHtml, res.file and res.templateFile alike
|
|
9
|
+
* through the instance's one reader (LambderFiles), which adds the path rule,
|
|
10
|
+
* memory cache and mime fallback for every source. Implement `read` over any
|
|
11
|
+
* backing store, or use LambderLocalFileSource (a folder),
|
|
12
|
+
* LambderS3FileSource (S3, R2 and other S3-compatible stores) or
|
|
13
|
+
* LambderHttpFileSource (any origin serving files by path).
|
|
14
14
|
*/
|
|
15
15
|
export interface LambderFileSource {
|
|
16
16
|
/**
|
|
@@ -2,14 +2,11 @@
|
|
|
2
2
|
* Where an app's files come from: the interface, and the one helper its
|
|
3
3
|
* remote implementations share.
|
|
4
4
|
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
* What reads through a source is the instance's reader, LambderFiles: it owns
|
|
12
|
-
* the path rule, the memory cache and the mime fallback.
|
|
5
|
+
* Laid out like the other store families (LambderSessionStore,
|
|
6
|
+
* LambderRateLimiter, LambderIdempotencyStore): the interface here, the
|
|
7
|
+
* implementations in stores/. core/ names only the interface. The instance's
|
|
8
|
+
* reader, LambderFiles, reads through a source and owns the path rule, the
|
|
9
|
+
* memory cache and the mime fallback.
|
|
13
10
|
*/
|
|
14
11
|
/**
|
|
15
12
|
* A file a remote store returned: the store's Content-Type unless it is a
|
|
@@ -3,11 +3,10 @@
|
|
|
3
3
|
* answer looks like, what claiming a scope reports, and the four methods the
|
|
4
4
|
* idempotency engine asks of a store.
|
|
5
5
|
*
|
|
6
|
-
* Kept apart from the engine
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
* browser entry can resolve it.
|
|
6
|
+
* Kept apart from the engine, like LambderRateLimiter: a store implements this
|
|
7
|
+
* and nothing else, and importing it from the engine would pull the engine
|
|
8
|
+
* and its refusal machinery into every store's import graph. Dependency-free,
|
|
9
|
+
* so the mock runtime and the browser entry can resolve it.
|
|
11
10
|
*/
|
|
12
11
|
/** A stored answer: what a completed record replays. */
|
|
13
12
|
export type LambderIdempotencyDoneRecord = {
|
|
@@ -15,12 +14,22 @@ export type LambderIdempotencyDoneRecord = {
|
|
|
15
14
|
/** Response headers stored with the record (normalized multi-value map). */
|
|
16
15
|
headers: Record<string, string[]>;
|
|
17
16
|
body: string;
|
|
17
|
+
/**
|
|
18
|
+
* The request the answer belongs to, as a digest of its payload: a key
|
|
19
|
+
* reused for a different request is refused rather than handed this
|
|
20
|
+
* answer. The engine's fingerprints are never empty, so a store that
|
|
21
|
+
* holds a record it cannot tie to a request (one another writer left in
|
|
22
|
+
* its table) reports the empty string, which matches no request and has
|
|
23
|
+
* the key refused as reused.
|
|
24
|
+
*/
|
|
25
|
+
fingerprint: string;
|
|
18
26
|
};
|
|
19
27
|
export type LambderIdempotencyBeginResult = {
|
|
20
28
|
state: "new";
|
|
21
29
|
ownerToken: string;
|
|
22
30
|
} | {
|
|
23
31
|
state: "pending";
|
|
32
|
+
fingerprint: string;
|
|
24
33
|
} | ({
|
|
25
34
|
state: "done";
|
|
26
35
|
} & LambderIdempotencyDoneRecord);
|
|
@@ -29,38 +38,58 @@ export type LambderIdempotencyBeginResult = {
|
|
|
29
38
|
* atomically, settled by the claim's owner. LambderDdbIdempotencyStore and
|
|
30
39
|
* LambderMemoryIdempotencyStore implement it; an app may bring its own.
|
|
31
40
|
*
|
|
32
|
-
*
|
|
33
|
-
*
|
|
34
|
-
* wrong are worth naming here.
|
|
41
|
+
* tests/store-conformance asserts the rules against every implementation.
|
|
42
|
+
* Four are easy to get wrong:
|
|
35
43
|
*
|
|
36
44
|
* A read hands back a COPY of the record, never the stored object, because a
|
|
37
45
|
* caller applies its own headers onto what it gets back.
|
|
38
46
|
*
|
|
39
|
-
* A write takes a copy too:
|
|
40
|
-
*
|
|
41
|
-
*
|
|
42
|
-
*
|
|
43
|
-
*
|
|
44
|
-
* over the wire gets this for free, since serializing IS the copy; one that
|
|
45
|
-
* keeps the record in the process has to make it.
|
|
47
|
+
* A write takes a copy too: the pipeline keeps writing the call's own headers
|
|
48
|
+
* into the record after complete() returns, so a store that retained it
|
|
49
|
+
* would let one request's Set-Cookie join the stored answer and replay to
|
|
50
|
+
* everybody else. A store that serializes over the wire copies for free; one
|
|
51
|
+
* that keeps the record in the process has to copy it.
|
|
46
52
|
*
|
|
47
|
-
* Size decides before ownership
|
|
53
|
+
* Size decides before ownership, so an answer too big to store reports
|
|
48
54
|
* "too-large" even when the claim has meanwhile been lost. The engine
|
|
49
|
-
* releases the claim either way
|
|
50
|
-
*
|
|
51
|
-
*
|
|
55
|
+
* releases the claim either way and only the reported reason differs, but an
|
|
56
|
+
* implementation with the order inverted would disagree with every other.
|
|
57
|
+
*
|
|
58
|
+
* abandon() releases a PENDING claim and nothing else: a stored record stays,
|
|
59
|
+
* even when the owner that stored it asks. The engine abandons after any
|
|
60
|
+
* complete() that throws, and one whose response was lost may have landed;
|
|
61
|
+
* deleting what it stored would run the operation again on the retry that
|
|
62
|
+
* should have replayed it.
|
|
52
63
|
*/
|
|
53
64
|
export interface LambderIdempotencyStore {
|
|
54
65
|
/** The stored answer when a completed, unexpired record exists, null otherwise (absent, pending, or expired). */
|
|
55
66
|
peek(scopeKey: string): Promise<LambderIdempotencyDoneRecord | null>;
|
|
56
|
-
/**
|
|
67
|
+
/**
|
|
68
|
+
* Claims the scope: "new" with the ownerToken to settle with, "pending"
|
|
69
|
+
* when another request owns it, "done" with the answer to replay. The
|
|
70
|
+
* claim keeps `fingerprint`, and "pending" and "done" report the one the
|
|
71
|
+
* scope holds, so the engine can tell a retry from a different request.
|
|
72
|
+
* A store that refuses the claim with no record to show for it (no room
|
|
73
|
+
* to hold one) reports the caller's own: the engine then answers the
|
|
74
|
+
* in-flight 409, which a client retries under the same key, rather than
|
|
75
|
+
* the key-reused one, which would move its key on.
|
|
76
|
+
*/
|
|
57
77
|
begin(scopeKey: string, options: {
|
|
58
78
|
pendingTtlSeconds: number;
|
|
79
|
+
fingerprint: string;
|
|
59
80
|
}): Promise<LambderIdempotencyBeginResult>;
|
|
60
|
-
/**
|
|
81
|
+
/**
|
|
82
|
+
* Stores the answer over the claim, with the fingerprint of the request
|
|
83
|
+
* it answers: "stored", "too-large" (nothing written; release the claim),
|
|
84
|
+
* or "lost" (the claim expired and a retry took the scope).
|
|
85
|
+
*/
|
|
61
86
|
complete(scopeKey: string, ownerToken: string, record: LambderIdempotencyDoneRecord & {
|
|
62
87
|
ttlSeconds: number;
|
|
63
88
|
}): Promise<"stored" | "too-large" | "lost">;
|
|
64
|
-
/**
|
|
89
|
+
/**
|
|
90
|
+
* Releases the owner's claim while it is still pending, so a retry can
|
|
91
|
+
* execute. A lost claim, or one already settled by complete(), makes
|
|
92
|
+
* this a silent no-op.
|
|
93
|
+
*/
|
|
65
94
|
abandon(scopeKey: string, ownerToken: string): Promise<void>;
|
|
66
95
|
}
|
|
@@ -3,10 +3,9 @@
|
|
|
3
3
|
* answer looks like, what claiming a scope reports, and the four methods the
|
|
4
4
|
* idempotency engine asks of a store.
|
|
5
5
|
*
|
|
6
|
-
* Kept apart from the engine
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
* browser entry can resolve it.
|
|
6
|
+
* Kept apart from the engine, like LambderRateLimiter: a store implements this
|
|
7
|
+
* and nothing else, and importing it from the engine would pull the engine
|
|
8
|
+
* and its refusal machinery into every store's import graph. Dependency-free,
|
|
9
|
+
* so the mock runtime and the browser entry can resolve it.
|
|
11
10
|
*/
|
|
12
11
|
export {};
|
|
@@ -3,11 +3,10 @@
|
|
|
3
3
|
* policy may cap, the policy shape, what an exceeded check reports, and the
|
|
4
4
|
* one method the rate-limit engine asks of a limiter.
|
|
5
5
|
*
|
|
6
|
-
* Kept apart from the DynamoDB limiter on purpose
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
* mock runtime and the browser entry can resolve it.
|
|
6
|
+
* Kept apart from the DynamoDB limiter on purpose: importing it from the
|
|
7
|
+
* store would pull the DynamoDB SDK loader into the engine's import graph and
|
|
8
|
+
* keep the policy layer from running anywhere but inside a Lambda. Pure and
|
|
9
|
+
* dependency-free, so the mock runtime and the browser entry can resolve it.
|
|
11
10
|
*/
|
|
12
11
|
/**
|
|
13
12
|
* The fixed windows a policy may cap, smallest first (the evaluation order),
|
|
@@ -53,19 +52,32 @@ export type LambderRateLimitResult = false | LambderRateLimitExceeded;
|
|
|
53
52
|
* DynamoDB limiter and the in-memory one implement it; an app may bring its
|
|
54
53
|
* own (Redis, a database) by implementing this one method.
|
|
55
54
|
*
|
|
56
|
-
* The engine validates a policy before it
|
|
57
|
-
* window arrives as a non-negative whole number
|
|
58
|
-
*
|
|
59
|
-
*
|
|
60
|
-
*
|
|
61
|
-
* it) because neither was ever meant to be asked.
|
|
55
|
+
* The engine validates a policy before it reaches here, so every capped
|
|
56
|
+
* window arrives as a non-negative whole number. A caller reaching
|
|
57
|
+
* isRateLimited directly owes the same precondition: the two shipped
|
|
58
|
+
* implementations disagree on a negative cap (one refuses the first attempt,
|
|
59
|
+
* the other allows it), since neither is meant to be asked.
|
|
62
60
|
*
|
|
63
61
|
* The tracker key is bounded there too: the variable half of it (a custom
|
|
64
|
-
* key handler's return, a session key) is replaced by its own sha256
|
|
65
|
-
* 1024 UTF-8 bytes
|
|
66
|
-
* meets a key it has to refuse. That matters
|
|
67
|
-
* throw, and a throw is what failOpen
|
|
62
|
+
* key handler's return, a session key) is replaced by its own sha256 once it
|
|
63
|
+
* passes 1024 UTF-8 bytes as written into the key, so an implementation with
|
|
64
|
+
* a key limit of its own never meets a key it has to refuse. That matters
|
|
65
|
+
* because a limiter's refusal is a throw, and a throw is what failOpen
|
|
66
|
+
* swallows into no limit at all.
|
|
67
|
+
*
|
|
68
|
+
* A limiter that answers a run of requests with one continuing failure (the
|
|
69
|
+
* DynamoDB limiter, for a flooded partition it cannot size) may throw the
|
|
70
|
+
* same error object for each of them: the engine logs a failure once, not
|
|
71
|
+
* once per request that meets it.
|
|
68
72
|
*/
|
|
69
73
|
export interface LambderRateLimiter {
|
|
70
74
|
isRateLimited(trackerKey: string, policy: LambderRateLimitPolicy): Promise<LambderRateLimitResult>;
|
|
75
|
+
/**
|
|
76
|
+
* The clock the windows are computed against, in epoch milliseconds, for
|
|
77
|
+
* a limiter that keeps one of its own (an injected test clock). A
|
|
78
|
+
* refusal's `resetAt` is a second on this clock, so the engine reads the
|
|
79
|
+
* Retry-After against it. Optional: without it, the engine reads
|
|
80
|
+
* Date.now().
|
|
81
|
+
*/
|
|
82
|
+
clockMilliseconds?(): number;
|
|
71
83
|
}
|
|
@@ -3,11 +3,10 @@
|
|
|
3
3
|
* policy may cap, the policy shape, what an exceeded check reports, and the
|
|
4
4
|
* one method the rate-limit engine asks of a limiter.
|
|
5
5
|
*
|
|
6
|
-
* Kept apart from the DynamoDB limiter on purpose
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
* mock runtime and the browser entry can resolve it.
|
|
6
|
+
* Kept apart from the DynamoDB limiter on purpose: importing it from the
|
|
7
|
+
* store would pull the DynamoDB SDK loader into the engine's import graph and
|
|
8
|
+
* keep the policy layer from running anywhere but inside a Lambda. Pure and
|
|
9
|
+
* dependency-free, so the mock runtime and the browser entry can resolve it.
|
|
11
10
|
*/
|
|
12
11
|
/**
|
|
13
12
|
* The fixed windows a policy may cap, smallest first (the evaluation order),
|
|
@@ -3,12 +3,11 @@
|
|
|
3
3
|
* it rests in.
|
|
4
4
|
*
|
|
5
5
|
* The manager owns the model (token format, hashing, expiry, sliding
|
|
6
|
-
* writes, dataRefresh, regeneration); a store owns
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
* the same shape, because the shape is the manager's, not the store's.
|
|
6
|
+
* writes, dataRefresh, regeneration); a store owns only the operations
|
|
7
|
+
* below, keyed by the two hashes. LambderDdbSessionStore and
|
|
8
|
+
* LambderMemorySessionStore implement it, and an app may bring its own
|
|
9
|
+
* (Redis, a database). The record shape is the manager's, so records written
|
|
10
|
+
* by one store read back through another.
|
|
12
11
|
*/
|
|
13
12
|
/**
|
|
14
13
|
* A session record. The two hashes are the record's identity: the partition
|
|
@@ -17,7 +16,7 @@
|
|
|
17
16
|
* stored, so a read of the store yields no usable credentials.
|
|
18
17
|
*/
|
|
19
18
|
export type LambderSessionRecord<SessionData = unknown> = {
|
|
20
|
-
/**
|
|
19
|
+
/** HMAC-SHA256 of the sessionKey keyed by the salt: the partition every session of one subject shares. */
|
|
21
20
|
sessionKeyHash: string;
|
|
22
21
|
/** sha256 of the sort-key secret the client's session cookie carries. */
|
|
23
22
|
secretHash: string;
|
|
@@ -37,36 +36,76 @@ export type LambderSessionRecord<SessionData = unknown> = {
|
|
|
37
36
|
* session's own expiresAt.
|
|
38
37
|
*/
|
|
39
38
|
dataExpiresAt?: number;
|
|
39
|
+
/**
|
|
40
|
+
* How many writes `data` and `dataExpiresAt` have had: 0 when the record
|
|
41
|
+
* is created, and one more on every update that writes either of them,
|
|
42
|
+
* which the store adds in the same atomic write. A conditioned update
|
|
43
|
+
* names the version it read (see LambderSessionStore.update).
|
|
44
|
+
*/
|
|
45
|
+
dataVersion: number;
|
|
40
46
|
};
|
|
47
|
+
/**
|
|
48
|
+
* The fields of a stored session a write may change: the data and its
|
|
49
|
+
* refresh deadline, and the sliding expiry. A session's identity, its CSRF
|
|
50
|
+
* hash, its subject and its creation are fixed when it is created, and its
|
|
51
|
+
* dataVersion is the store's to advance, never the caller's to set.
|
|
52
|
+
*/
|
|
53
|
+
export type LambderSessionChanges<SessionData = unknown> = Partial<Pick<LambderSessionRecord<SessionData>, "data" | "dataExpiresAt" | "lastAccessedAt" | "expiresAt">>;
|
|
54
|
+
/**
|
|
55
|
+
* What an update found: `"updated"` when it applied, `"missing"` when the
|
|
56
|
+
* record is gone (a logout, a password change, an expiry), and `"stale"`
|
|
57
|
+
* when the record exists but its dataVersion is no longer the one the
|
|
58
|
+
* update was conditioned on, so something else wrote its data or its
|
|
59
|
+
* refresh deadline in between.
|
|
60
|
+
*/
|
|
61
|
+
export type LambderSessionUpdateResult = "updated" | "missing" | "stale";
|
|
41
62
|
export interface LambderSessionStore<SessionData = unknown> {
|
|
42
63
|
/**
|
|
43
|
-
* Whether records live only
|
|
44
|
-
*
|
|
45
|
-
*
|
|
46
|
-
*
|
|
47
|
-
* rest, where the records would be usable credentials.
|
|
64
|
+
* Whether records live only as long as this process. The session manager
|
|
65
|
+
* reads it to refuse a non-cryptographic LambderSessionCrypto over a store
|
|
66
|
+
* that outlives the process: fake hashing is tolerable in memory for a
|
|
67
|
+
* development run, but at rest the records would be usable credentials.
|
|
48
68
|
*/
|
|
49
69
|
readonly isMemoryOnly: boolean;
|
|
50
70
|
/**
|
|
51
71
|
* The record under the two hashes, or null. A failing read throws; the
|
|
52
72
|
* manager wraps it as a LambderSessionReadError.
|
|
53
73
|
*
|
|
54
|
-
* A store MAY hand back a record
|
|
55
|
-
*
|
|
56
|
-
*
|
|
57
|
-
*
|
|
58
|
-
*
|
|
74
|
+
* A store MAY hand back a record past its expiresAt: a DynamoDB TTL
|
|
75
|
+
* deletes within days rather than at the second, and a plain table has
|
|
76
|
+
* nothing that sweeps at all. The manager enforces expiry on every read;
|
|
77
|
+
* a store that drops expired records itself is doing housekeeping, not
|
|
78
|
+
* policy.
|
|
59
79
|
*/
|
|
60
80
|
get(sessionKeyHash: string, secretHash: string): Promise<LambderSessionRecord<SessionData> | null>;
|
|
61
|
-
/** Writes
|
|
62
|
-
|
|
63
|
-
delete(sessionKeyHash: string, secretHash: string): Promise<void>;
|
|
64
|
-
/** Every secretHash stored under the partition: the sessions of one subject. */
|
|
65
|
-
listSecretHashes(sessionKeyHash: string): Promise<string[]>;
|
|
81
|
+
/** Writes a new record, and throws when one already exists under the two hashes: a session is minted once and never overwritten. */
|
|
82
|
+
create(record: LambderSessionRecord<SessionData>): Promise<void>;
|
|
66
83
|
/**
|
|
67
|
-
*
|
|
68
|
-
*
|
|
69
|
-
*
|
|
84
|
+
* Changes the named fields of an existing record and no others, and only
|
|
85
|
+
* while it exists: an update never brings back a session deleted in
|
|
86
|
+
* between, which is what keeps a logout, "log out everywhere" or a
|
|
87
|
+
* password change from being undone by a write already in flight.
|
|
88
|
+
*
|
|
89
|
+
* An update whose changes carry `data` or `dataExpiresAt` also adds one
|
|
90
|
+
* to the record's dataVersion, in the same atomic write (DynamoDB's
|
|
91
|
+
* `ADD`), even when the value written equals the one stored. With
|
|
92
|
+
* `condition`, the update applies only while the record's dataVersion is
|
|
93
|
+
* still `condition.dataVersion`, and answers "stale" otherwise, so data
|
|
94
|
+
* derived from an earlier read cannot land over a concurrent refresh,
|
|
95
|
+
* data write or expireSessionDataAllByKey. The version is what makes
|
|
96
|
+
* that hold: a deadline compared in whole seconds cannot show a mark
|
|
97
|
+
* that wrote the value already stored, or two marks in one second.
|
|
70
98
|
*/
|
|
71
|
-
|
|
99
|
+
update(sessionKeyHash: string, secretHash: string, changes: LambderSessionChanges<SessionData>, condition?: {
|
|
100
|
+
dataVersion: number;
|
|
101
|
+
}): Promise<LambderSessionUpdateResult>;
|
|
102
|
+
/**
|
|
103
|
+
* Removes the record and hands back what it held at that moment, or null
|
|
104
|
+
* when there was none: rotation carries the stored record over, not the
|
|
105
|
+
* one a request read earlier, and mints nothing when a logout got there
|
|
106
|
+
* first.
|
|
107
|
+
*/
|
|
108
|
+
delete(sessionKeyHash: string, secretHash: string): Promise<LambderSessionRecord<SessionData> | null>;
|
|
109
|
+
/** Every secretHash stored under the partition, read consistently: the sessions of one subject, including one created a moment ago. */
|
|
110
|
+
listSecretHashes(sessionKeyHash: string): Promise<string[]>;
|
|
72
111
|
}
|
|
@@ -3,11 +3,10 @@
|
|
|
3
3
|
* it rests in.
|
|
4
4
|
*
|
|
5
5
|
* The manager owns the model (token format, hashing, expiry, sliding
|
|
6
|
-
* writes, dataRefresh, regeneration); a store owns
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
* the same shape, because the shape is the manager's, not the store's.
|
|
6
|
+
* writes, dataRefresh, regeneration); a store owns only the operations
|
|
7
|
+
* below, keyed by the two hashes. LambderDdbSessionStore and
|
|
8
|
+
* LambderMemorySessionStore implement it, and an app may bring its own
|
|
9
|
+
* (Redis, a database). The record shape is the manager's, so records written
|
|
10
|
+
* by one store read back through another.
|
|
12
11
|
*/
|
|
13
12
|
export {};
|