lambder 12.0.1 → 14.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.
- package/CHANGELOG.md +119 -0
- package/README.md +7 -2
- package/dist/api/LambderApiAnswer.d.ts +7 -0
- package/dist/api/LambderApiEnvelope.js +4 -1
- package/dist/api/LambderApiRefusals.d.ts +22 -0
- package/dist/api/LambderApiRefusals.js +13 -0
- package/dist/build/generateApiFiles.d.ts +60 -0
- package/dist/build/generateApiFiles.js +85 -0
- package/dist/build/generatedTables.d.ts +1 -1
- package/dist/build/writeApiGuardParams.d.ts +11 -10
- package/dist/build/writeApiGuardParams.js +135 -31
- package/dist/build.d.ts +4 -1
- package/dist/build.js +3 -1
- package/dist/client.d.ts +2 -2
- package/dist/client.js +1 -1
- package/dist/core/Lambder.d.ts +32 -8
- package/dist/core/Lambder.js +135 -17
- package/dist/core/LambderCallSummary.d.ts +43 -0
- package/dist/core/LambderCallSummary.js +10 -0
- package/dist/core/LambderContext.d.ts +14 -1
- package/dist/core/LambderContext.js +19 -4
- package/dist/core/LambderCreateOptions.d.ts +50 -25
- package/dist/core/LambderCreateOptions.js +37 -18
- package/dist/core/LambderInvocationScope.d.ts +25 -0
- package/dist/core/LambderInvocationScope.js +21 -0
- package/dist/core/LambderResponse.d.ts +11 -0
- package/dist/core/LambderResponse.js +12 -0
- package/dist/core/LambderResponseBuilder.js +5 -1
- package/dist/index.d.ts +8 -6
- package/dist/index.js +2 -2
- package/dist/invoke/LambderLambdaEvent.d.ts +2 -0
- package/dist/invoke/LambderLambdaEvent.js +8 -2
- package/dist/mock/LambderMockApp.d.ts +8 -8
- package/dist/mock/LambderMockApp.js +14 -10
- package/dist/mock/LambderMockCreateOptions.d.ts +2 -3
- package/dist/session/LambderSessionManager.d.ts +46 -3
- package/dist/session/LambderSessionManager.js +62 -11
- package/dist/shared/LambderI18n.js +49 -4
- package/dist/shared/contracts/LambderRateLimiter.d.ts +2 -2
- package/dist/shared/contracts/LambderRateLimiter.js +2 -2
- package/dist/shared/util/LambderInvocationScope.d.ts +10 -0
- package/dist/shared/util/LambderInvocationScope.js +5 -0
- package/dist/shared/util/LambderNamedMaps.d.ts +22 -0
- package/dist/shared/util/LambderNamedMaps.js +19 -0
- package/dist/shared/wire/LambderApiOptionEntries.d.ts +24 -0
- package/dist/shared/wire/LambderApiOptionEntries.js +13 -0
- package/dist/shared/wire/LambderCallOutcome.d.ts +27 -0
- package/dist/shared/wire/LambderCallOutcome.js +47 -0
- package/dist/shared/wire/LambderInvokeApiId.d.ts +10 -0
- package/dist/shared/wire/LambderInvokeApiId.js +10 -0
- package/dist/stores/LambderDdbRateLimiter.d.ts +24 -16
- package/dist/stores/LambderDdbRateLimiter.js +64 -45
- package/dist/stores/LambderMemoryRateLimiter.d.ts +9 -4
- package/dist/stores/LambderMemoryRateLimiter.js +20 -15
- package/dist/testing/LambderTestApp.d.ts +13 -4
- package/dist/testing/LambderTestApp.js +21 -5
- package/package.json +1 -1
|
@@ -1,32 +1,128 @@
|
|
|
1
1
|
import { existsSync, readFileSync } from "fs";
|
|
2
2
|
import { resolve } from "path";
|
|
3
|
-
import { apiGuardParam } from "../shared/wire/LambderApiOptionEntries.js";
|
|
4
|
-
import { headerLinesOf, loadApiOptionEntries, nameChangesOf,
|
|
3
|
+
import { apiGuardParam, apiGuardParamExportName } from "../shared/wire/LambderApiOptionEntries.js";
|
|
4
|
+
import { headerLinesOf, loadApiOptionEntries, nameChangesOf, settleGeneratedFile, } from "./generatedTables.js";
|
|
5
5
|
/*
|
|
6
|
-
* One guard's parameters as a generated module a browser may carry:
|
|
7
|
-
* API that declares the guard, the parameter it gives it,
|
|
8
|
-
* about any API.
|
|
6
|
+
* One guard's parameters as a generated module a browser may carry: one
|
|
7
|
+
* export per API that declares the guard, holding the parameter it gives it,
|
|
8
|
+
* and nothing else about any API.
|
|
9
9
|
*
|
|
10
10
|
* The file writeApiOptions writes holds every declaration, because a mock
|
|
11
11
|
* and a test need every declaration. A screen that decides whether to offer
|
|
12
12
|
* a control by the permission an endpoint's guard asks needs one guard's
|
|
13
|
-
* parameter,
|
|
14
|
-
* every
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
13
|
+
* parameter, for the endpoints it gates on. A table of them would still hand
|
|
14
|
+
* every page that imports it the name of every API behind the guard and what
|
|
15
|
+
* each asks, whichever of them its screens use. One export per API lets the
|
|
16
|
+
* bundler keep exactly the ones the client imports, and keep no name at all:
|
|
17
|
+
* the API's name sits in the export's type and doc comment, which a build
|
|
18
|
+
* strips, and in its identifier, which a minifier renames.
|
|
19
|
+
*
|
|
20
|
+
* Each export is tagged with its API's name and the guard's
|
|
21
|
+
* (LambderApiGuardParam), in types alone. A client function typed to the
|
|
22
|
+
* tagged parameters takes only generated ones, so a hand-written literal,
|
|
23
|
+
* which would restate the server's declaration and could drift from it, does
|
|
24
|
+
* not compile.
|
|
18
25
|
*/
|
|
19
|
-
|
|
26
|
+
/** The tag type every export is asserted to, imported from the client entry, which is safe in a browser. */
|
|
27
|
+
const TAG_TYPE = "LambderApiGuardParam";
|
|
20
28
|
const defaultHeader = (guard) => [
|
|
21
29
|
"Generated by writeApiGuardParams() from lambder/build. Do not edit.",
|
|
22
30
|
"",
|
|
23
31
|
`The parameter each API the server guards with "${guard}" gives that guard,`,
|
|
24
|
-
"and nothing else about any API:
|
|
25
|
-
"
|
|
32
|
+
"one export per API and nothing else about any API: a client imports the",
|
|
33
|
+
"ones its screens gate on, and its bundle carries those alone.",
|
|
26
34
|
].join("\n");
|
|
27
35
|
/**
|
|
28
|
-
*
|
|
29
|
-
*
|
|
36
|
+
* A plain-data value as the type `as const` gives it: its literals, readonly
|
|
37
|
+
* all the way down. What each export is asserted to beside its tag, so the
|
|
38
|
+
* tagged type keeps the literal the API declared.
|
|
39
|
+
*/
|
|
40
|
+
const constTypeOf = (value) => {
|
|
41
|
+
if (Array.isArray(value))
|
|
42
|
+
return `readonly [${value.map(constTypeOf).join(", ")}]`;
|
|
43
|
+
if (value !== null && typeof value === "object") {
|
|
44
|
+
const members = Object.entries(value).map(([key, member]) => `readonly ${JSON.stringify(key)}: ${constTypeOf(member)}`);
|
|
45
|
+
return members.length ? `{ ${members.join("; ")} }` : "{}";
|
|
46
|
+
}
|
|
47
|
+
return JSON.stringify(value);
|
|
48
|
+
};
|
|
49
|
+
/**
|
|
50
|
+
* One API's export as written, less the statement's semicolon: `export const
|
|
51
|
+
* <name> = <JSON> as const as LambderApiGuardParam<"<api>", "<guard>", <type>>`.
|
|
52
|
+
* A check renders each export it reads back again and compares the two, so
|
|
53
|
+
* the identifier a client imports, the guard and the literal type are held
|
|
54
|
+
* to what this writer would write, not only the value.
|
|
55
|
+
*/
|
|
56
|
+
const exportLineOf = (name, param, guard) => `export const ${apiGuardParamExportName(name)} = ${JSON.stringify(param)} as const as ${TAG_TYPE}<${JSON.stringify(name)}, ${JSON.stringify(guard)}, ${constTypeOf(param)}>`;
|
|
57
|
+
/** The start of one export, far enough to read back its parameter's JSON and its API's name. */
|
|
58
|
+
const EXPORT_LINE = new RegExp(`^export const [A-Za-z_$][\\w$]* = (.+) as const as ${TAG_TYPE}<("(?:[^"\\\\]|\\\\.)*"), `);
|
|
59
|
+
/** The tag's import, in whatever spacing and quotes a formatter gives it, since no `// prettier-ignore` sits above it. */
|
|
60
|
+
const IMPORT_LINE = new RegExp(`^import type \\{\\s*${TAG_TYPE}\\s*\\} from (["'])lambder/client\\1;?$`);
|
|
61
|
+
/** The empty module of a guard no API declares, however a formatter spaces it. */
|
|
62
|
+
const EMPTY_EXPORT_LINE = /^export \{\s*\};?$/;
|
|
63
|
+
/**
|
|
64
|
+
* The parameters a file already holds, by API name, or null for a file that
|
|
65
|
+
* does not hold them as written. As written means the tag's import and one
|
|
66
|
+
* export per API, each the line exportLineOf renders for its API and value,
|
|
67
|
+
* or the empty module alone for a guard no API declares. Anything else makes
|
|
68
|
+
* it null: another statement, an export renamed by hand or written for
|
|
69
|
+
* another guard, an API exported twice, a missing import, a file with no
|
|
70
|
+
* statement at all. Comments, blank lines, re-indentation, line endings and
|
|
71
|
+
* the semicolons change nothing.
|
|
72
|
+
*/
|
|
73
|
+
const readGuardParams = (contents, guard) => {
|
|
74
|
+
const params = {};
|
|
75
|
+
let imported = false;
|
|
76
|
+
let empty = false;
|
|
77
|
+
for (const line of contents.split(/\r?\n/).map((text) => text.trim())) {
|
|
78
|
+
if (line === "" || line.startsWith("//") || line.startsWith("/*") || line.startsWith("*"))
|
|
79
|
+
continue;
|
|
80
|
+
if (IMPORT_LINE.test(line)) {
|
|
81
|
+
imported = true;
|
|
82
|
+
continue;
|
|
83
|
+
}
|
|
84
|
+
if (EMPTY_EXPORT_LINE.test(line)) {
|
|
85
|
+
empty = true;
|
|
86
|
+
continue;
|
|
87
|
+
}
|
|
88
|
+
const match = EXPORT_LINE.exec(line);
|
|
89
|
+
if (!match)
|
|
90
|
+
return null;
|
|
91
|
+
try {
|
|
92
|
+
const param = JSON.parse(match[1]);
|
|
93
|
+
const name = JSON.parse(match[2]);
|
|
94
|
+
if (typeof name !== "string" || Object.prototype.hasOwnProperty.call(params, name))
|
|
95
|
+
return null;
|
|
96
|
+
if (line.replace(/;$/, "") !== exportLineOf(name, param, guard))
|
|
97
|
+
return null;
|
|
98
|
+
params[name] = param;
|
|
99
|
+
}
|
|
100
|
+
catch {
|
|
101
|
+
return null;
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
const held = Object.keys(params).length > 0 ? imported && !empty : empty && !imported;
|
|
105
|
+
return held ? params : null;
|
|
106
|
+
};
|
|
107
|
+
/** The file's statements after its header: the tag's import and one export per API, or an empty export for a guard no API declares. */
|
|
108
|
+
const statementLinesOf = (params, guard, semicolon) => {
|
|
109
|
+
const names = Object.keys(params);
|
|
110
|
+
if (names.length === 0)
|
|
111
|
+
return [`export {}${semicolon}`];
|
|
112
|
+
return [
|
|
113
|
+
`import type { ${TAG_TYPE} } from "lambder/client"${semicolon}`,
|
|
114
|
+
...names.flatMap((name) => [
|
|
115
|
+
"",
|
|
116
|
+
`/** What ${name} gives the "${guard}" guard. */`,
|
|
117
|
+
// Keeps a formatter off the JSON a check reads back.
|
|
118
|
+
"// prettier-ignore",
|
|
119
|
+
`${exportLineOf(name, params[name], guard)}${semicolon}`,
|
|
120
|
+
]),
|
|
121
|
+
];
|
|
122
|
+
};
|
|
123
|
+
/**
|
|
124
|
+
* Writes one guard's parameters as a module of one export per API, or checks
|
|
125
|
+
* the one on disk, and says which APIs moved.
|
|
30
126
|
*
|
|
31
127
|
* ```ts
|
|
32
128
|
* import { writeApiGuardParams } from "lambder/build";
|
|
@@ -40,11 +136,12 @@ const defaultHeader = (guard) => [
|
|
|
40
136
|
* });
|
|
41
137
|
* ```
|
|
42
138
|
*
|
|
43
|
-
*
|
|
44
|
-
*
|
|
45
|
-
*
|
|
46
|
-
*
|
|
47
|
-
* that is not plain data
|
|
139
|
+
* API `orders.list` exports `ordersListGuardParam`: the literal it declared,
|
|
140
|
+
* `true` for a guard named without a parameter (the string and list forms),
|
|
141
|
+
* typed LambderApiGuardParam<"orders.list", "store", literal>. Two APIs whose
|
|
142
|
+
* names export under one identifier fail the write, and so does a parameter
|
|
143
|
+
* that is not plain data, as it does for writeApiOptions, or that is null,
|
|
144
|
+
* which no tag can carry.
|
|
48
145
|
*/
|
|
49
146
|
export const writeApiGuardParams = async (options) => {
|
|
50
147
|
const file = resolve(options.file);
|
|
@@ -54,30 +151,37 @@ export const writeApiGuardParams = async (options) => {
|
|
|
54
151
|
throw new Error(`writeApiGuardParams: the server declares no guard "${options.guard}" (it declares ${declared.length ? declared.map((name) => `"${name}"`).join(", ") : "none"}).`);
|
|
55
152
|
}
|
|
56
153
|
const params = {};
|
|
154
|
+
const exportedBy = new Map();
|
|
57
155
|
for (const name of Object.keys(entries.apis).sort()) {
|
|
58
156
|
const param = apiGuardParam(entries.apis, name, options.guard);
|
|
59
|
-
if (param
|
|
60
|
-
|
|
157
|
+
if (param === undefined)
|
|
158
|
+
continue;
|
|
159
|
+
if (param === null) {
|
|
160
|
+
throw new Error(`writeApiGuardParams: "${name}" gives the "${options.guard}" guard null, which an export cannot carry with its tag; give the guard another value.`);
|
|
161
|
+
}
|
|
162
|
+
const exportName = apiGuardParamExportName(name);
|
|
163
|
+
const taken = exportedBy.get(exportName);
|
|
164
|
+
if (taken !== undefined) {
|
|
165
|
+
throw new Error(`writeApiGuardParams: "${taken}" and "${name}" would both export ${exportName}; rename one of them.`);
|
|
166
|
+
}
|
|
167
|
+
exportedBy.set(exportName, name);
|
|
168
|
+
params[name] = param;
|
|
61
169
|
}
|
|
62
170
|
const previousText = existsSync(file) ? readFileSync(file, "utf8") : null;
|
|
63
|
-
const previous = previousText === null ? null :
|
|
171
|
+
const previous = previousText === null ? null : readGuardParams(previousText, options.guard);
|
|
64
172
|
const changes = nameChangesOf(params, previous ?? {});
|
|
65
173
|
const count = Object.keys(params).length;
|
|
174
|
+
const semicolon = options.semicolons === false ? "" : ";";
|
|
66
175
|
const settled = settleGeneratedFile({
|
|
67
176
|
name: options.file, file, check: options.check, previousText, readBack: previous !== null,
|
|
68
|
-
tables: [{ name:
|
|
177
|
+
tables: [{ name: `${options.guard} params`, count, changes }],
|
|
69
178
|
held: `${count} APIs guarded by "${options.guard}"`,
|
|
70
|
-
tablesNoun: "the
|
|
179
|
+
tablesNoun: "the parameters",
|
|
71
180
|
unmovedLine: "no parameters changed",
|
|
72
181
|
render: () => [
|
|
73
182
|
...headerLinesOf(options.header ?? defaultHeader(options.guard)),
|
|
74
183
|
"",
|
|
75
|
-
...
|
|
76
|
-
name: TABLE_NAME,
|
|
77
|
-
doc: `The parameter each API guarded by "${options.guard}" gives it: the value as declared, or true for the guard named without one.`,
|
|
78
|
-
value: params,
|
|
79
|
-
semicolon: options.semicolons === false ? "" : ";",
|
|
80
|
-
}),
|
|
184
|
+
...statementLinesOf(params, options.guard, semicolon),
|
|
81
185
|
"",
|
|
82
186
|
].join("\n"),
|
|
83
187
|
});
|
package/dist/build.d.ts
CHANGED
|
@@ -5,7 +5,8 @@
|
|
|
5
5
|
* ships: the signature file both sides read, the declared options as plain
|
|
6
6
|
* data, and one guard's parameters as the least a browser needs of them, all
|
|
7
7
|
* from the app's own instance; and the contract a client compiles against,
|
|
8
|
-
* from the server's sources
|
|
8
|
+
* from the server's sources; and generateApiFiles, every one of them for
|
|
9
|
+
* every app a script names, in one call. Node-only and imported by nothing else
|
|
9
10
|
* in the package, so no deployment or bundle carries it.
|
|
10
11
|
*/
|
|
11
12
|
export { writeApiSignatures } from "./build/writeApiSignatures.js";
|
|
@@ -17,4 +18,6 @@ export type { LambderApiOptionsFileOptions, LambderApiOptionsFileResult, } from
|
|
|
17
18
|
export { writeApiGuardParams } from "./build/writeApiGuardParams.js";
|
|
18
19
|
export type { LambderApiGuardParamsFileOptions, LambderApiGuardParamsFileResult, } from "./build/writeApiGuardParams.js";
|
|
19
20
|
export type { LambderApiOptionsSource, LambderNameChanges } from "./build/generatedTables.js";
|
|
21
|
+
export { generateApiFiles } from "./build/generateApiFiles.js";
|
|
22
|
+
export type { LambderApiFilesConfig, LambderApiFilesApp, LambderApiFilesResult } from "./build/generateApiFiles.js";
|
|
20
23
|
export type { LambderModuleLocation } from "./build/moduleLocation.js";
|
package/dist/build.js
CHANGED
|
@@ -5,10 +5,12 @@
|
|
|
5
5
|
* ships: the signature file both sides read, the declared options as plain
|
|
6
6
|
* data, and one guard's parameters as the least a browser needs of them, all
|
|
7
7
|
* from the app's own instance; and the contract a client compiles against,
|
|
8
|
-
* from the server's sources
|
|
8
|
+
* from the server's sources; and generateApiFiles, every one of them for
|
|
9
|
+
* every app a script names, in one call. Node-only and imported by nothing else
|
|
9
10
|
* in the package, so no deployment or bundle carries it.
|
|
10
11
|
*/
|
|
11
12
|
export { writeApiSignatures } from "./build/writeApiSignatures.js";
|
|
12
13
|
export { writeApiContract } from "./build/writeApiContract.js";
|
|
13
14
|
export { writeApiOptions } from "./build/writeApiOptions.js";
|
|
14
15
|
export { writeApiGuardParams } from "./build/writeApiGuardParams.js";
|
|
16
|
+
export { generateApiFiles } from "./build/generateApiFiles.js";
|
package/dist/client.d.ts
CHANGED
|
@@ -29,8 +29,8 @@ export { LambderApiRefusal, isLambderApiRefusal, isLambderRefusalCode, refuse, r
|
|
|
29
29
|
export type { LambderApiRefusalOptions, LambderRefusalMessage, LambderPlainRefusalMessage, LambderUncheckedRefusalMessage, LambderRefusalCode, LambderRefuseOptions, LambderDeclaredRefuseOptions, LambderDeclaredRefuse, } from "./shared/wire/LambderApiRefusal.js";
|
|
30
30
|
export { isObjectPayload } from "./shared/wire/LambderObjectPayload.js";
|
|
31
31
|
export type { LambderApiContractShape, LambderApiMode, LambderApiEnvelopeBody, LambderApiSuccessEnvelope, LambderApiRefusalEnvelope, LambderRefusalEnvelopeFields, LambderApiRefusalConfig, LambderGuardNamesIn, LambderContractMode, LambderContractKeysWithMode, LambderContractKeysWithGuard, LambderJsonOf, LambderContractRefusalsOf, LambderContractRefusalMessage, LambderContractRefusals, LambderContractAnyRefusalMessage, LambderContractGuardsOf, LambderContractGuardNames, LambderContractGuardInputsOf, LambderContractGuardInput, LambderContractGuardInputNames, LambderContractRateLimitOf, LambderContractRateLimitNames, LambderContractIdempotencyOf, } from "./shared/wire/LambderApiContract.js";
|
|
32
|
-
export { apiGuardParam } from "./shared/wire/LambderApiOptionEntries.js";
|
|
33
|
-
export type { LambderApiOptionEntries, LambderApiOptionEntry, LambderRateLimitPolicyEntry, LambderGuardDeclarationEntry, LambderApisWithGuard, LambderApisGuardedBy, LambderApisWithMode, LambderGuardParamOf, } from "./shared/wire/LambderApiOptionEntries.js";
|
|
32
|
+
export { apiGuardParam, apiGuardParamExportName } from "./shared/wire/LambderApiOptionEntries.js";
|
|
33
|
+
export type { LambderApiOptionEntries, LambderApiOptionEntry, LambderRateLimitPolicyEntry, LambderGuardDeclarationEntry, LambderApisWithGuard, LambderApisGuardedBy, LambderApisWithMode, LambderGuardParamOf, LambderApiGuardParam, } from "./shared/wire/LambderApiOptionEntries.js";
|
|
34
34
|
export { describeCrash, errorFromCrashDetail } from "./shared/wire/LambderCrashDetail.js";
|
|
35
35
|
export type { LambderCrashDetail, LambderCrashCause } from "./shared/wire/LambderCrashDetail.js";
|
|
36
36
|
export { compressPayloadGzip, isRequestCompressionAvailable, COMPRESSED_PAYLOAD_GZ_FIELD, COMPRESSED_PAYLOAD_BR_FIELD, COMPRESSED_PAYLOAD_BYTES_FIELD, DEFAULT_REQUEST_COMPRESSION_SETTINGS, } from "./shared/wire/LambderRequestPayload.js";
|
package/dist/client.js
CHANGED
|
@@ -28,7 +28,7 @@ export { LambderApiRefusal, isLambderApiRefusal, isLambderRefusalCode, refuse, r
|
|
|
28
28
|
export { isObjectPayload } from "./shared/wire/LambderObjectPayload.js";
|
|
29
29
|
// The server's declared options as plain data (the generated options module's
|
|
30
30
|
// entry types) and the readers a client derives its own facts from.
|
|
31
|
-
export { apiGuardParam } from "./shared/wire/LambderApiOptionEntries.js";
|
|
31
|
+
export { apiGuardParam, apiGuardParamExportName } from "./shared/wire/LambderApiOptionEntries.js";
|
|
32
32
|
// A crash described for a caller allowed to see it (the envelope's `crash` field; pure, no Node built-ins).
|
|
33
33
|
export { describeCrash, errorFromCrashDetail } from "./shared/wire/LambderCrashDetail.js";
|
|
34
34
|
// Request payload compression (browser-safe: gzip via CompressionStream, no Node built-ins).
|
package/dist/core/Lambder.d.ts
CHANGED
|
@@ -12,7 +12,7 @@ import { LambderFiles } from "./LambderFiles.js";
|
|
|
12
12
|
import { type LambderPipelineBackends, type LambderPipelineBackendSwap } from "../api/LambderApiPipeline.js";
|
|
13
13
|
import type { LambderFileSource } from "../shared/contracts/LambderFileSource.js";
|
|
14
14
|
import { LAMBDER_BACKEND_SWAP, LAMBDER_CRASH_WATCH } from "../shared/util/LambderTestingDoors.js";
|
|
15
|
-
import { type LambderWireRefusalsOf, type LambderHandlerRefusalsOf, type LambderRefusalNamesIn, type LambderRefusalsOption, type
|
|
15
|
+
import { type LambderWireRefusalsOf, type LambderHandlerRefusalsOf, type LambderRefusalNamesIn, type LambderRefusalsOption, type LambderRefusalVocabularyOption, type LambderRefusalVocabularyOptionChecks, type LambderMergedRefusalVocabulary } from "../api/LambderApiRefusals.js";
|
|
16
16
|
import { type LambderDeclaredRefuse } from "../shared/wire/LambderApiRefusal.js";
|
|
17
17
|
import { type LambderApiSignatureEntry } from "../api/LambderApiSignature.js";
|
|
18
18
|
import { type LambderApiSignatureMap } from "../shared/wire/LambderApiSignatureMap.js";
|
|
@@ -25,10 +25,13 @@ import type { LambderContractEntry, LambderJsonOf } from "../shared/wire/Lambder
|
|
|
25
25
|
import { type LambderActionNamesCheck, type LambderApiDeclaration, type LambderApiDeclarations, type LambderApiGroup, type LambderApiModeOf, type LambderAppTypes, type LambderContractOfGroups, type LambderGroupNameCheck, type LambderGroupPartsCheck, type LambderMergedParts, type LambderLazyApiGroup, type LambderPlainAppTypes, type LambderPublicSessionPolicyCheck, type LambderRegisteredGroupsCheck, type LambderRegistrableApiGroup, type LambderSessionModeCheck } from "../api/LambderApiDeclarations.js";
|
|
26
26
|
import { type LambderHttpEvent, type LambderRenderContext, type LambderSessionRenderContext } from "./LambderContext.js";
|
|
27
27
|
import type { LambderReadonlyDeep, MaybePromise } from "../shared/util/LambderTypeUtilities.js";
|
|
28
|
+
import { type LambderCallSummary } from "./LambderCallSummary.js";
|
|
28
29
|
import { type LambderMergedNamedMaps, type LambderNamedMapsOption, type LambderRouteHandler, type LambderInputValidationHandler, type LambderFallbackHandler, type LambderGlobalErrorHandler, type LambderAfterRenderHook, type LambderBeforeRenderHook, type LambderFallbackHook, type LambderActionTools, type LambderCreateOptions, type LambderGivenOption, type LambderHandler, type LambderNestedOptionChecks, type LambderObjectOutputCheck, type LambderPayloadSliceCheck, type LambderRequirableGuardsField, type LambderSessionEnabledInstance, type LambderSessionRouteHandler } from "./LambderCreateOptions.js";
|
|
29
30
|
/** Everything `lambder/testing` may put under a built instance: the pipeline's stores, and the source its files are read from. */
|
|
30
31
|
export type LambderInstanceBackends = LambderPipelineBackends & {
|
|
31
32
|
fileSource?: LambderFileSource;
|
|
33
|
+
/** Where call summaries go instead of the app's own callSummary; null writes none. */
|
|
34
|
+
callSummary?: ((summary: LambderCallSummary) => void) | null;
|
|
32
35
|
};
|
|
33
36
|
/** What the instance had a place for; see LambderPipelineBackendSwap. `files` is false on an instance created without the files option. */
|
|
34
37
|
export type LambderInstanceBackendSwap = LambderPipelineBackendSwap & {
|
|
@@ -146,6 +149,9 @@ export default class Lambder<TApp extends LambderAppTypes = LambderPlainAppTypes
|
|
|
146
149
|
private readonly contextTools;
|
|
147
150
|
private readonly trustedClientIpHeaders;
|
|
148
151
|
private readonly trustedHostHeaders;
|
|
152
|
+
private readonly originProof;
|
|
153
|
+
/** Where each API call's summary goes (the callSummary option); null writes none. Replaceable through the backend swap alone. */
|
|
154
|
+
private callSummaryWriter;
|
|
149
155
|
constructor(given?: LambderCreateOptions<TApp["session"]>);
|
|
150
156
|
setRouteFallbackHandler(routeFallbackHandler: LambderFallbackHandler): this;
|
|
151
157
|
setApiFallbackHandler(apiFallbackHandler: LambderFallbackHandler): this;
|
|
@@ -445,7 +451,15 @@ export default class Lambder<TApp extends LambderAppTypes = LambderPlainAppTypes
|
|
|
445
451
|
*/
|
|
446
452
|
private isCorsPreflight;
|
|
447
453
|
private resolveRequest;
|
|
454
|
+
/**
|
|
455
|
+
* One HTTP invocation, from the event to the finalized response, under
|
|
456
|
+
* an invocation record of its own (see LambderInvocationScope): what an
|
|
457
|
+
* API call's summary line is written from once the response is final.
|
|
458
|
+
*/
|
|
448
459
|
render(event: LambderHttpEvent, lambdaContext: Context): Promise<LambderHttpResponse>;
|
|
460
|
+
private renderRequest;
|
|
461
|
+
/** What createContext reads this instance's requests with. */
|
|
462
|
+
private contextOptions;
|
|
449
463
|
/**
|
|
450
464
|
* A thrown value that is an answer rather than a crash, as the response;
|
|
451
465
|
* anything else is rethrown to the crash path.
|
|
@@ -470,6 +484,12 @@ export default class Lambder<TApp extends LambderAppTypes = LambderPlainAppTypes
|
|
|
470
484
|
* failed too.
|
|
471
485
|
*/
|
|
472
486
|
private answerCrash;
|
|
487
|
+
/**
|
|
488
|
+
* The summary line of an API call, once its response is final: nothing
|
|
489
|
+
* for an invocation that served no API call. A writer that throws costs
|
|
490
|
+
* the call its line and nothing else.
|
|
491
|
+
*/
|
|
492
|
+
private writeCallSummary;
|
|
473
493
|
/**
|
|
474
494
|
* An answer to a crash, carrying what the call wrote and its CORS headers.
|
|
475
495
|
* As on the success path, headers belong to the call: a call that wrote a
|
|
@@ -500,6 +520,7 @@ export default class Lambder<TApp extends LambderAppTypes = LambderPlainAppTypes
|
|
|
500
520
|
* so Lambda's retries and dead-letter queues still see the failure.
|
|
501
521
|
*/
|
|
502
522
|
renderEvent(event: unknown, lambdaContext: Context): Promise<unknown>;
|
|
523
|
+
private renderEventAction;
|
|
503
524
|
/**
|
|
504
525
|
* The answer for a rejected input: the app's
|
|
505
526
|
* setApiInputValidationErrorHandler when set, otherwise the standard 422
|
|
@@ -596,7 +617,10 @@ export declare const initLambder: <TSessionData = any>() => {
|
|
|
596
617
|
* Declares the app's refusal vocabulary: every code once, with the schema
|
|
597
618
|
* of its data (`{ data: schema }`) or none (`{}`), the status every
|
|
598
619
|
* refusal with it leaves with (`status`, 200 by default) and whether it
|
|
599
|
-
* sets the notAuthorized flag (`notAuthorized: true`).
|
|
620
|
+
* sets the notAuthorized flag (`notAuthorized: true`). One map of codes,
|
|
621
|
+
* or a list of them, as guards and rate-limit policies are declared, so
|
|
622
|
+
* each part of an app declares its own codes; a code two maps declare is
|
|
623
|
+
* refused, at compile time and at the call. Returns the init
|
|
600
624
|
* bound to it: its guard() types ctx.refuse to a guard's refusals and
|
|
601
625
|
* refuses a guard naming a code outside the vocabulary, its refuse is
|
|
602
626
|
* typed to the whole vocabulary, and its create() hands the vocabulary to
|
|
@@ -604,7 +628,7 @@ export declare const initLambder: <TSessionData = any>() => {
|
|
|
604
628
|
* handler, a guard or a helper is a crash rather than an answer, so every
|
|
605
629
|
* "no" a client reads names a code.
|
|
606
630
|
*/
|
|
607
|
-
declareRefusals<const TRefusals extends
|
|
631
|
+
declareRefusals<const TRefusals extends LambderRefusalVocabularyOption, const TRequireCodes extends boolean = false>(refusals: TRefusals & LambderRefusalVocabularyOptionChecks<TRefusals>, options?: {
|
|
608
632
|
requireCodes?: TRequireCodes;
|
|
609
633
|
}): {
|
|
610
634
|
/**
|
|
@@ -615,8 +639,8 @@ export declare const initLambder: <TSessionData = any>() => {
|
|
|
615
639
|
* refuse(). Inside an API handler ctx.refuse is narrower, that
|
|
616
640
|
* endpoint's codes alone.
|
|
617
641
|
*/
|
|
618
|
-
refuse: LambderDeclaredRefuse<LambderHandlerRefusalsOf<TRefusals
|
|
619
|
-
create<const TOptions extends LambderInitCreateOptions<TSessionData>>(options: TOptions & Record<Exclude<keyof TOptions, "session" | "guards" | "idempotency" | "apiVersion" | "cors" | "apiPath" | "apiSignatures" | "trustedClientIpHeaders" | "trustedHostHeaders" | "files" | "etag" | "rateLimits" | "minApiVersion" | "compression" | "maxResponseBytes" | "maxRequestPayloadBytes" | "requireApiGuards" | "crashes">, never> & LambderNestedOptionChecks<TSessionData, TOptions, TRefusals
|
|
642
|
+
refuse: LambderDeclaredRefuse<LambderHandlerRefusalsOf<LambderMergedRefusalVocabulary<TRefusals>, keyof LambderMergedRefusalVocabulary<TRefusals> & string>, TRequireCodes>;
|
|
643
|
+
create<const TOptions extends LambderInitCreateOptions<TSessionData>>(options: TOptions & Record<Exclude<keyof TOptions, "session" | "guards" | "idempotency" | "apiVersion" | "cors" | "apiPath" | "apiSignatures" | "trustedClientIpHeaders" | "trustedHostHeaders" | "originProof" | "files" | "etag" | "rateLimits" | "minApiVersion" | "compression" | "maxResponseBytes" | "maxRequestPayloadBytes" | "callSummary" | "requireApiGuards" | "crashes">, never> & LambderNestedOptionChecks<TSessionData, TOptions, LambderMergedRefusalVocabulary<TRefusals>>): Lambder<{
|
|
620
644
|
session: TSessionData;
|
|
621
645
|
policies: TOptions["rateLimits"] extends {
|
|
622
646
|
policies: infer TPolicies;
|
|
@@ -625,10 +649,10 @@ export declare const initLambder: <TSessionData = any>() => {
|
|
|
625
649
|
idempotency: TOptions["idempotency"] extends LambderApiIdempotencyConfig ? true : false;
|
|
626
650
|
guardsRequired: [LambderGivenOption<TOptions, "requireApiGuards">] extends [false | undefined] ? false : true;
|
|
627
651
|
sessions: [LambderGivenOption<TOptions, "session">] extends [undefined] ? false : true;
|
|
628
|
-
refusals: TRefusals
|
|
652
|
+
refusals: LambderMergedRefusalVocabulary<TRefusals>;
|
|
629
653
|
refusalCodesRequired: TRequireCodes;
|
|
630
654
|
}, {}>;
|
|
631
|
-
guard: import("../api/LambderApiGuards.js").LambderGuardBuilder<LambderRenderContext<any, Record<string, string>, {}, TSessionData>, LambderSessionRenderContext<any, TSessionData>, TRefusals
|
|
655
|
+
guard: import("../api/LambderApiGuards.js").LambderGuardBuilder<LambderRenderContext<any, Record<string, string>, {}, TSessionData>, LambderSessionRenderContext<any, TSessionData>, LambderMergedRefusalVocabulary<TRefusals>, TRequireCodes>;
|
|
632
656
|
rateLimitKey: import("../api/LambderApiRateLimits.js").LambderRateLimitKeyBuilder<LambderRenderContext<any, Record<string, string>, {}, TSessionData>>;
|
|
633
657
|
};
|
|
634
658
|
/**
|
|
@@ -651,7 +675,7 @@ export declare const initLambder: <TSessionData = any>() => {
|
|
|
651
675
|
code?: undefined;
|
|
652
676
|
data?: undefined;
|
|
653
677
|
}) | undefined) => never;
|
|
654
|
-
create<const TOptions extends LambderInitCreateOptions<TSessionData>>(options: TOptions & Record<Exclude<keyof TOptions, "session" | "guards" | "idempotency" | "apiVersion" | "cors" | "apiPath" | "apiSignatures" | "trustedClientIpHeaders" | "trustedHostHeaders" | "files" | "etag" | "rateLimits" | "minApiVersion" | "compression" | "maxResponseBytes" | "maxRequestPayloadBytes" | "requireApiGuards" | "crashes">, never> & LambderNestedOptionChecks<TSessionData, TOptions, {}>): Lambder<{
|
|
678
|
+
create<const TOptions extends LambderInitCreateOptions<TSessionData>>(options: TOptions & Record<Exclude<keyof TOptions, "session" | "guards" | "idempotency" | "apiVersion" | "cors" | "apiPath" | "apiSignatures" | "trustedClientIpHeaders" | "trustedHostHeaders" | "originProof" | "files" | "etag" | "rateLimits" | "minApiVersion" | "compression" | "maxResponseBytes" | "maxRequestPayloadBytes" | "callSummary" | "requireApiGuards" | "crashes">, never> & LambderNestedOptionChecks<TSessionData, TOptions, {}>): Lambder<{
|
|
655
679
|
session: TSessionData;
|
|
656
680
|
policies: TOptions["rateLimits"] extends {
|
|
657
681
|
policies: infer TPolicies;
|