lambder 8.1.2 → 8.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (65) hide show
  1. package/CHANGELOG.md +136 -0
  2. package/README.md +8 -7
  3. package/dist/api/LambderApiGuards.d.ts +2 -17
  4. package/dist/api/LambderApiRateLimits.d.ts +2 -29
  5. package/dist/build/generatedTables.d.ts +72 -0
  6. package/dist/build/generatedTables.js +99 -0
  7. package/dist/build/writeApiGuardParams.d.ts +60 -0
  8. package/dist/build/writeApiGuardParams.js +85 -0
  9. package/dist/build/writeApiOptions.d.ts +68 -0
  10. package/dist/build/writeApiOptions.js +102 -0
  11. package/dist/build.d.ts +10 -4
  12. package/dist/build.js +7 -4
  13. package/dist/client/LambderUploadRunner.d.ts +7 -7
  14. package/dist/client/LambderUploadRunner.js +12 -21
  15. package/dist/client.d.ts +7 -0
  16. package/dist/client.js +11 -0
  17. package/dist/core/Lambder.d.ts +21 -0
  18. package/dist/core/Lambder.js +69 -0
  19. package/dist/index.d.ts +13 -0
  20. package/dist/index.js +13 -0
  21. package/dist/mock/LambderMockApp.d.ts +34 -17
  22. package/dist/mock/LambderMockApp.js +67 -21
  23. package/dist/mock/LambderMockCreateOptions.d.ts +68 -5
  24. package/dist/mock/LambderMockTypes.d.ts +29 -10
  25. package/dist/mock/lambderMockPoliciesFrom.d.ts +51 -0
  26. package/dist/mock/lambderMockPoliciesFrom.js +46 -0
  27. package/dist/mock.d.ts +3 -0
  28. package/dist/mock.js +3 -0
  29. package/dist/secrets/LambderOneShotSecrets.d.ts +166 -0
  30. package/dist/secrets/LambderOneShotSecrets.js +217 -0
  31. package/dist/session/LambderSessionCrypto.js +6 -16
  32. package/dist/shared/contracts/LambderIdempotencyStore.d.ts +3 -2
  33. package/dist/shared/contracts/LambderOneShotSecretStore.d.ts +122 -0
  34. package/dist/shared/contracts/LambderOneShotSecretStore.js +38 -0
  35. package/dist/shared/util/LambderBackoffTimer.d.ts +82 -0
  36. package/dist/shared/util/LambderBackoffTimer.js +86 -0
  37. package/dist/shared/util/LambderBase64.d.ts +14 -0
  38. package/dist/shared/util/LambderBase64.js +17 -0
  39. package/dist/shared/util/LambderSignedClaims.d.ts +78 -0
  40. package/dist/shared/util/LambderSignedClaims.js +109 -0
  41. package/dist/shared/util/LambderTextDigest.d.ts +19 -5
  42. package/dist/shared/util/LambderTextDigest.js +30 -5
  43. package/dist/shared/util/assertPlainData.d.ts +9 -0
  44. package/dist/shared/util/assertPlainData.js +41 -0
  45. package/dist/shared/wire/LambderApiOptionEntries.d.ts +148 -0
  46. package/dist/shared/wire/LambderApiOptionEntries.js +35 -0
  47. package/dist/stores/LambderDdbOneShotSecretStore.d.ts +64 -0
  48. package/dist/stores/LambderDdbOneShotSecretStore.js +266 -0
  49. package/dist/stores/LambderMemoryIdempotencyStore.d.ts +3 -2
  50. package/dist/stores/LambderMemoryIdempotencyStore.js +3 -2
  51. package/dist/stores/LambderMemoryOneShotSecretStore.d.ts +36 -0
  52. package/dist/stores/LambderMemoryOneShotSecretStore.js +93 -0
  53. package/dist/testing/LambderConformanceRunner.d.ts +46 -0
  54. package/dist/testing/LambderConformanceRunner.js +21 -0
  55. package/dist/testing/lambderIdempotencyStoreConformance.d.ts +33 -0
  56. package/dist/testing/lambderIdempotencyStoreConformance.js +237 -0
  57. package/dist/testing/lambderOneShotSecretStoreConformance.d.ts +43 -0
  58. package/dist/testing/lambderOneShotSecretStoreConformance.js +224 -0
  59. package/dist/testing/lambderRateLimiterConformance.d.ts +20 -0
  60. package/dist/testing/lambderRateLimiterConformance.js +72 -0
  61. package/dist/testing/lambderSessionStoreConformance.d.ts +27 -0
  62. package/dist/testing/lambderSessionStoreConformance.js +165 -0
  63. package/dist/testing.d.ts +14 -0
  64. package/dist/testing.js +12 -0
  65. package/package.json +1 -1
@@ -0,0 +1,102 @@
1
+ import { existsSync, readFileSync } from "fs";
2
+ import { resolve } from "path";
3
+ import { headerLinesOf, loadApiOptionEntries, nameChangesOf, readGeneratedTable, settleGeneratedFile, tableStatementLines, } from "./generatedTables.js";
4
+ /*
5
+ * The declared options of an app's APIs as a generated module of plain data:
6
+ * every API's mode and its guards, rateLimit and idempotency options as
7
+ * written, every rate-limit policy less its key handler, and every guard's
8
+ * input mode, session flag and place in the call. The contract already
9
+ * carries the options as types; this is the same fact as a value, for the
10
+ * code that decides something at runtime with it: a mock restating the
11
+ * server's declarations, a test walking the public surface. Neither imports
12
+ * the server for it. The file names every endpoint, so a production bundle
13
+ * imports none of it: a screen that gates on a guard reads that guard's
14
+ * parameters alone (writeApiGuardParams).
15
+ *
16
+ * Plain data by construction: the instance refuses to report a guard
17
+ * parameter that is code or a class instance (see Lambder.apiOptionEntries),
18
+ * a policy's key handler is reduced to `per: "custom"`, and a guard's schema
19
+ * to its input mode. No secret can reach the file, because nothing that could
20
+ * hold one is written.
21
+ */
22
+ const DEFAULT_HEADER = [
23
+ "Generated by writeApiOptions() from lambder/build. Do not edit.",
24
+ "",
25
+ "Every API the server registers with its declared options, every rate-limit",
26
+ "policy less its key handler, and every guard's input mode: the server's",
27
+ "declarations as plain data, for the code that decides something with them",
28
+ "without importing the server.",
29
+ ].join("\n");
30
+ /** The three tables the module exports, in the order they are written. */
31
+ const TABLES = [
32
+ { name: "apiOptions", key: "apis", type: "LambderApiOptionEntry", doc: "Every API the server registers: its mode and its guards, rateLimit and idempotency options as written." },
33
+ { name: "rateLimitPolicies", key: "rateLimitPolicies", type: "LambderRateLimitPolicyEntry", doc: "Every rate-limit policy the server declares, its key reduced to what it counts: an address, a session, or a key the app derives (\"custom\")." },
34
+ { name: "guardDeclarations", key: "guards", type: "LambderGuardDeclarationEntry", doc: "Every guard the server declares: how it is fed, whether it needs a session, and when it runs." },
35
+ ];
36
+ /** The tables a generated file holds, read back as data, or null for a file that does not hold all three as written (never written, or rewritten by hand). */
37
+ export const readOptionTables = (contents) => {
38
+ const tables = {};
39
+ for (const table of TABLES) {
40
+ const read = readGeneratedTable(contents, table.name);
41
+ if (!read)
42
+ return null;
43
+ tables[table.key] = read;
44
+ }
45
+ return tables;
46
+ };
47
+ const renderOptionsFile = (entries, options) => {
48
+ const semicolon = options.semicolons === false ? "" : ";";
49
+ return [
50
+ ...headerLinesOf(options.header ?? DEFAULT_HEADER),
51
+ `import type { ${TABLES.map((table) => table.type).join(", ")} } from "lambder/client"${semicolon}`,
52
+ ...TABLES.flatMap((table) => [
53
+ "",
54
+ ...tableStatementLines({ name: table.name, doc: table.doc, value: entries[table.key], satisfies: `Record<string, ${table.type}>`, semicolon }),
55
+ ]),
56
+ "",
57
+ ].join("\n");
58
+ };
59
+ /**
60
+ * Writes the declared options of an app's APIs as a module of plain data, or
61
+ * checks the one on disk, and says which APIs, policies and guards moved.
62
+ *
63
+ * Call it from a generator script beside writeApiSignatures, naming the
64
+ * module that exports the app's instance:
65
+ *
66
+ * ```ts
67
+ * import { writeApiOptions } from "lambder/build";
68
+ *
69
+ * const result = await writeApiOptions({
70
+ * module: "server/src/index.ts", // export const lambder = initLambder()...
71
+ * exportName: "lambder",
72
+ * file: "shared/generated/apiOptions.generated.ts",
73
+ * check: process.argv.includes("--check"),
74
+ * });
75
+ * console.log(result.lines.join("\n"));
76
+ * process.exit(result.ok ? 0 : 1);
77
+ * ```
78
+ *
79
+ * The tables are compared as the data the file holds, so a checkout that
80
+ * rewrote its line endings or a formatter that re-indented it is neither
81
+ * stale nor rewritten, and a file whose data is current is left as it is.
82
+ * There is no fresh-process check: nothing here is digested, so nothing can
83
+ * differ per process. A module that does not load, an export that is not an
84
+ * instance, or a guard parameter that is not plain data throws.
85
+ */
86
+ export const writeApiOptions = async (options) => {
87
+ const file = resolve(options.file);
88
+ const entries = await loadApiOptionEntries(options, "writeApiOptions");
89
+ const previousText = existsSync(file) ? readFileSync(file, "utf8") : null;
90
+ const previous = previousText === null ? null : readOptionTables(previousText);
91
+ const changes = Object.fromEntries(TABLES.map((table) => [table.key, nameChangesOf(entries[table.key], previous?.[table.key] ?? {})]));
92
+ const counts = Object.fromEntries(TABLES.map((table) => [table.key, Object.keys(entries[table.key]).length]));
93
+ const settled = settleGeneratedFile({
94
+ name: options.file, file, check: options.check, previousText, readBack: previous !== null,
95
+ tables: TABLES.map((table) => ({ name: table.name, count: counts[table.key], changes: changes[table.key] })),
96
+ held: `${counts.apis} APIs, ${counts.rateLimitPolicies} policies, ${counts.guards} guards`,
97
+ tablesNoun: "the three tables",
98
+ unmovedLine: "no options changed",
99
+ render: () => renderOptionsFile(entries, options),
100
+ });
101
+ return { ...settled, file, counts, changes };
102
+ };
package/dist/build.d.ts CHANGED
@@ -2,13 +2,19 @@
2
2
  * Build entry point (`import ... from "lambder/build"`).
3
3
  *
4
4
  * What a generator script runs at build time to write the files a deployment
5
- * ships: the signature file both sides read, from the app's own instance, and
6
- * the contract a client compiles against, from the server's sources. Node-only
7
- * and imported by nothing else in the package, so no deployment or bundle
8
- * carries it.
5
+ * ships: the signature file both sides read, the declared options as plain
6
+ * data, and one guard's parameters as the least a browser needs of them, all
7
+ * from the app's own instance; and the contract a client compiles against,
8
+ * from the server's sources. Node-only and imported by nothing else
9
+ * in the package, so no deployment or bundle carries it.
9
10
  */
10
11
  export { writeApiSignatures } from "./build/writeApiSignatures.js";
11
12
  export type { LambderApiSignatureSource, LambderApiSignatureFileOptions, LambderApiSignatureFileResult, } from "./build/writeApiSignatures.js";
12
13
  export { writeApiContract } from "./build/writeApiContract.js";
13
14
  export type { LambderApiContractFileOptions, LambderApiContractFileResult, } from "./build/writeApiContract.js";
15
+ export { writeApiOptions } from "./build/writeApiOptions.js";
16
+ export type { LambderApiOptionsFileOptions, LambderApiOptionsFileResult, } from "./build/writeApiOptions.js";
17
+ export { writeApiGuardParams } from "./build/writeApiGuardParams.js";
18
+ export type { LambderApiGuardParamsFileOptions, LambderApiGuardParamsFileResult, } from "./build/writeApiGuardParams.js";
19
+ export type { LambderApiOptionsSource, LambderNameChanges } from "./build/generatedTables.js";
14
20
  export type { LambderModuleLocation } from "./build/moduleLocation.js";
package/dist/build.js CHANGED
@@ -2,10 +2,13 @@
2
2
  * Build entry point (`import ... from "lambder/build"`).
3
3
  *
4
4
  * What a generator script runs at build time to write the files a deployment
5
- * ships: the signature file both sides read, from the app's own instance, and
6
- * the contract a client compiles against, from the server's sources. Node-only
7
- * and imported by nothing else in the package, so no deployment or bundle
8
- * carries it.
5
+ * ships: the signature file both sides read, the declared options as plain
6
+ * data, and one guard's parameters as the least a browser needs of them, all
7
+ * from the app's own instance; and the contract a client compiles against,
8
+ * from the server's sources. Node-only and imported by nothing else
9
+ * in the package, so no deployment or bundle carries it.
9
10
  */
10
11
  export { writeApiSignatures } from "./build/writeApiSignatures.js";
11
12
  export { writeApiContract } from "./build/writeApiContract.js";
13
+ export { writeApiOptions } from "./build/writeApiOptions.js";
14
+ export { writeApiGuardParams } from "./build/writeApiGuardParams.js";
@@ -48,10 +48,11 @@ export type LambderUploadRunnerOptions<Reference, Receipt> = {
48
48
  /**
49
49
  * How storage is tried again when it cannot be reached, stalls, or answers
50
50
  * a failure a retry can cure (a 5xx, RequestTimeout, SlowDown). Each wait
51
- * is a random time between `baseDelayMs` and a ceiling of twice that,
52
- * doubling with every failed attempt and never past `maxDelayMs`, so many
53
- * browsers dropped together do not come back in step, not even the first
54
- * time. Default: 4 attempts, waits from one second to 15.
51
+ * is `baseDelayMs` plus a random share of a ceiling that starts at
52
+ * `baseDelayMs` and doubles with every failed attempt, the whole never
53
+ * past `maxDelayMs` (the ladder of LambderBackoffTimer), so many browsers
54
+ * dropped together do not come back in step, not even the first time.
55
+ * Default: 4 attempts, waits from one second to 15.
55
56
  */
56
57
  storageRetry?: {
57
58
  attempts?: number;
@@ -69,8 +70,8 @@ export type LambderUploadRunnerOptions<Reference, Receipt> = {
69
70
  export declare class LambderUploadRunner<Reference, Receipt> {
70
71
  private readonly options;
71
72
  private readonly attempts;
72
- private readonly baseDelayMs;
73
- private readonly maxDelayMs;
73
+ /** The ladder one upload's waits climb; each upload() builds a timer of its own from it, so two uploads never share a count. */
74
+ private readonly backoff;
74
75
  private readonly stallTimeoutMs;
75
76
  constructor(options: LambderUploadRunnerOptions<Reference, Receipt>);
76
77
  /** For a file input's `accept`, so the picker only offers what the rule takes. */
@@ -90,7 +91,6 @@ export declare class LambderUploadRunner<Reference, Receipt> {
90
91
  }): Promise<Receipt>;
91
92
  /** Forgets a confirmed upload through the app's endpoint, when it declared one. */
92
93
  discard(receipt: Receipt): Promise<void>;
93
- private waitBeforeRetry;
94
94
  /** One post of the file to storage. Never throws: every ending is an outcome. */
95
95
  private post;
96
96
  }
@@ -1,4 +1,5 @@
1
1
  import { checkUploadRule, } from "../shared/contracts/LambderUploadBucket.js";
2
+ import { LambderBackoffTimer } from "../shared/util/LambderBackoffTimer.js";
2
3
  import { sha256Base64Of } from "../shared/util/LambderTextDigest.js";
3
4
  /** How an upload failed: `reason` for a screen to word, the underlying error as `cause`. */
4
5
  export class LambderUploadError extends Error {
@@ -21,14 +22,16 @@ const TRANSIENT_STORAGE_CODES = new Set(["RequestTimeout", "SlowDown", "Internal
21
22
  export class LambderUploadRunner {
22
23
  options;
23
24
  attempts;
24
- baseDelayMs;
25
- maxDelayMs;
25
+ /** The ladder one upload's waits climb; each upload() builds a timer of its own from it, so two uploads never share a count. */
26
+ backoff;
26
27
  stallTimeoutMs;
27
28
  constructor(options) {
28
29
  this.options = options;
29
30
  this.attempts = Math.max(1, options.storageRetry?.attempts ?? 4);
30
- this.baseDelayMs = options.storageRetry?.baseDelayMs ?? 1_000;
31
- this.maxDelayMs = options.storageRetry?.maxDelayMs ?? 15_000;
31
+ const baseDelayMs = options.storageRetry?.baseDelayMs ?? 1_000;
32
+ const maxDelayMs = options.storageRetry?.maxDelayMs ?? 15_000;
33
+ // A longest wait below the shortest is every wait at the shortest.
34
+ this.backoff = { baseMs: baseDelayMs, maxMs: Math.max(baseDelayMs, maxDelayMs) };
32
35
  this.stallTimeoutMs = options.stallTimeoutMs ?? 60_000;
33
36
  }
34
37
  /** For a file input's `accept`, so the picker only offers what the rule takes. */
@@ -81,6 +84,7 @@ export class LambderUploadRunner {
81
84
  }
82
85
  };
83
86
  let issued = await requestTicket();
87
+ const backoff = new LambderBackoffTimer(this.backoff);
84
88
  let failedAttempts = 0;
85
89
  let ticketRenewals = 0;
86
90
  for (;;) {
@@ -103,7 +107,10 @@ export class LambderUploadRunner {
103
107
  // connection does not leave the app a record per attempt.
104
108
  if (++failedAttempts >= this.attempts)
105
109
  throw new LambderUploadError("networkFailed");
106
- await this.waitBeforeRetry(failedAttempts, signal);
110
+ // The wait rejects only for the signal: nothing else cancels it.
111
+ await backoff.wait(signal).catch((cause) => {
112
+ throw new LambderUploadError("cancelled", { cause });
113
+ });
107
114
  }
108
115
  report("confirming", file.size);
109
116
  try {
@@ -117,22 +124,6 @@ export class LambderUploadRunner {
117
124
  async discard(receipt) {
118
125
  await this.options.discardUpload?.(receipt);
119
126
  }
120
- waitBeforeRetry(failedAttempts, signal) {
121
- const ceiling = Math.max(this.baseDelayMs, Math.min(this.baseDelayMs * 2 ** failedAttempts, this.maxDelayMs));
122
- return new Promise((resolve, reject) => {
123
- if (signal?.aborted)
124
- return reject(new LambderUploadError("cancelled"));
125
- const cancel = () => {
126
- clearTimeout(timer);
127
- reject(new LambderUploadError("cancelled"));
128
- };
129
- const timer = setTimeout(() => {
130
- signal?.removeEventListener("abort", cancel);
131
- resolve();
132
- }, this.baseDelayMs + Math.random() * (ceiling - this.baseDelayMs));
133
- signal?.addEventListener("abort", cancel, { once: true });
134
- });
135
- }
136
127
  /** One post of the file to storage. Never throws: every ending is an outcome. */
137
128
  post(ticket, file, signal, onSent) {
138
129
  if (signal?.aborted)
package/dist/client.d.ts CHANGED
@@ -26,6 +26,8 @@ export type { LambderIdempotencyKeyScope } from "./shared/wire/LambderIdempotenc
26
26
  export { LambderApiRefusal, isLambderApiRefusal, refuse, refusalMessageOf, LAMBDER_REFUSAL_CODES } from "./shared/wire/LambderApiRefusal.js";
27
27
  export type { LambderApiRefusalOptions, LambderRefusalMessage, LambderAppRefusalMessage, LambderRefusalCode, LambderRefuseOptions } from "./shared/wire/LambderApiRefusal.js";
28
28
  export type { LambderApiContractShape, LambderApiMode, LambderApiEnvelopeBody, LambderApiResponseConfig, LambderGuardNamesIn, LambderContractMode, LambderContractKeysWithMode, LambderContractKeysWithGuard, LambderJsonOf, LambderJsonOutputOf, LambderContractGuardsOf, LambderContractGuardNames, LambderContractGuardInputsOf, LambderContractGuardInput, LambderContractGuardInputNames, LambderContractRateLimitOf, LambderContractRateLimitNames, LambderContractIdempotencyOf, } from "./shared/wire/LambderApiContract.js";
29
+ export { apiGuardParam } from "./shared/wire/LambderApiOptionEntries.js";
30
+ export type { LambderApiOptionEntries, LambderApiOptionEntry, LambderRateLimitPolicyEntry, LambderGuardDeclarationEntry, LambderApisWithGuard, LambderApisGuardedBy, LambderApisWithMode, LambderGuardParamOf, } from "./shared/wire/LambderApiOptionEntries.js";
29
31
  export { describeCrash, errorFromCrashDetail } from "./shared/wire/LambderCrashDetail.js";
30
32
  export type { LambderCrashDetail, LambderCrashCause } from "./shared/wire/LambderCrashDetail.js";
31
33
  export { compressPayloadGzip, isRequestCompressionAvailable, COMPRESSED_PAYLOAD_GZ_FIELD, COMPRESSED_PAYLOAD_BR_FIELD, COMPRESSED_PAYLOAD_BYTES_FIELD, DEFAULT_REQUEST_COMPRESSION_SETTINGS, } from "./shared/wire/LambderRequestPayload.js";
@@ -36,6 +38,11 @@ export { html, xml, raw, jsonScript, escapeHtml, renderHtmlValue, LambderSafeHtm
36
38
  export { createLambderI18n } from "./shared/LambderI18n.js";
37
39
  export type { LambderLanguageMeta, LambderI18nConfig, LambderI18nInstance, LambderI18nTranslator, LambderI18nExtractParams, LambderI18nDictionaryLoader, LambderI18nCodes, LambderI18nKeys, LambderI18nTranslatorFor, } from "./shared/LambderI18n.js";
38
40
  export type { LambderHttpStatusCode } from "./shared/wire/LambderHttpStatus.js";
41
+ export { LambderBackoffTimer } from "./shared/util/LambderBackoffTimer.js";
42
+ export type { LambderBackoffTimerOptions } from "./shared/util/LambderBackoffTimer.js";
43
+ export { LambderSignedClaims, keyedDigest, randomSecret } from "./shared/util/LambderSignedClaims.js";
44
+ export type { LambderSignedClaimsOptions } from "./shared/util/LambderSignedClaims.js";
45
+ export { constantTimeEquals } from "./shared/util/LambderTextDigest.js";
39
46
  export { LambderUploadRunner, LambderUploadError } from "./client/LambderUploadRunner.js";
40
47
  export type { LambderUploadRunnerOptions, LambderUploadProgress, LambderUploadPhase, LambderUploadFailureReason } from "./client/LambderUploadRunner.js";
41
48
  export { checkUploadRule } from "./shared/contracts/LambderUploadBucket.js";
package/dist/client.js CHANGED
@@ -23,6 +23,9 @@ export { createIdempotencyKey, createIdempotencyKeyScope } from "./shared/wire/L
23
23
  // Typed API refusals (isomorphic: shared code may throw them from anywhere;
24
24
  // in the browser they are plain Errors).
25
25
  export { LambderApiRefusal, isLambderApiRefusal, refuse, refusalMessageOf, LAMBDER_REFUSAL_CODES } from "./shared/wire/LambderApiRefusal.js";
26
+ // The server's declared options as plain data (the generated options module's
27
+ // entry types) and the readers a client derives its own facts from.
28
+ export { apiGuardParam } from "./shared/wire/LambderApiOptionEntries.js";
26
29
  // A crash described for a caller allowed to see it (the envelope's `crash` field; pure, no Node built-ins).
27
30
  export { describeCrash, errorFromCrashDetail } from "./shared/wire/LambderCrashDetail.js";
28
31
  // Request payload compression (browser-safe: gzip via CompressionStream, no Node built-ins).
@@ -33,6 +36,14 @@ export { resolveCompressionOption } from "./shared/wire/LambderCompressionOption
33
36
  export { html, xml, raw, jsonScript, escapeHtml, renderHtmlValue, LambderSafeHtml } from "./shared/LambderHtml.js";
34
37
  // Typed translations (standalone, isomorphic)
35
38
  export { createLambderI18n } from "./shared/LambderI18n.js";
39
+ // Waiting longer after each failure, once: for a reconnecting client or a
40
+ // screen that has to come back by itself.
41
+ export { LambderBackoffTimer } from "./shared/util/LambderBackoffTimer.js";
42
+ // Signed claims tokens, for the isomorphic code that verifies them where a
43
+ // server's secret is at hand (an edge Worker, a shared backend package);
44
+ // never in a page, which holds no secret to verify with.
45
+ export { LambderSignedClaims, keyedDigest, randomSecret } from "./shared/util/LambderSignedClaims.js";
46
+ export { constantTimeEquals } from "./shared/util/LambderTextDigest.js";
36
47
  // Direct uploads: the runner that takes a file from the browser straight to
37
48
  // storage, and the vocabulary it shares with the server's bucket.
38
49
  export { LambderUploadRunner, LambderUploadError } from "./client/LambderUploadRunner.js";
@@ -14,6 +14,7 @@ import type { LambderFileSource } from "../shared/contracts/LambderFileSource.js
14
14
  import { LAMBDER_BACKEND_SWAP, LAMBDER_CRASH_WATCH } from "../shared/util/LambderTestingDoors.js";
15
15
  import { type LambderApiSignatureEntry } from "../api/LambderApiSignature.js";
16
16
  import { type LambderApiSignatureMap } from "../shared/wire/LambderApiSignature.js";
17
+ import type { LambderApiOptionEntries } from "../shared/wire/LambderApiOptionEntries.js";
17
18
  import type { LambderApiIdempotencyOption } from "../shared/wire/LambderApiOptionValues.js";
18
19
  import type { LambderApiGuard, LambderGuardMetaMap, LambderGuardsOption, LambderGuardDataOf, LambderGuardInputsOf } from "../api/LambderApiGuards.js";
19
20
  import type { LambderApiRateLimitPolicyConfig, LambderRateLimitOption } from "../api/LambderApiRateLimits.js";
@@ -91,6 +92,8 @@ export default class Lambder<TSessionData = any, _TContract extends Record<strin
91
92
  private readonly apiDefinitions;
92
93
  /** The guards map given at creation, kept for apiSignatures(): a guard's schema is part of the signature of every endpoint declaring it. */
93
94
  private readonly guards;
95
+ /** The rate-limit policies given at creation, kept for apiOptionEntries(), which records each one less its key handler. */
96
+ private readonly rateLimitPolicies;
94
97
  private hookList;
95
98
  private createdHooks;
96
99
  private initPromise;
@@ -246,6 +249,24 @@ export default class Lambder<TSessionData = any, _TContract extends Record<strin
246
249
  * unless the generator writes it there.
247
250
  */
248
251
  apiSignatureEntries(): Promise<LambderApiSignatureEntry[]>;
252
+ /**
253
+ * Every registered API's mode and declared options as plain data, with
254
+ * the rate-limit policies and guards they name reduced to what is not
255
+ * code: what writeApiOptions (lambder/build) writes to a module a client,
256
+ * a mock or a test imports instead of the server. The contract carries
257
+ * the same options as types; this is the same fact as a value, for code
258
+ * that decides something at runtime with it.
259
+ *
260
+ * Nothing here is a secret or a handler by construction. A guard's
261
+ * parameter is written as it was declared, so it has to be plain data
262
+ * (a permission string, a list, a reason); one that is not fails by API
263
+ * and guard name. A policy's key handler is never written: its `per`
264
+ * says "custom" and no more. A guard's input schema is never written
265
+ * either; its declaration says only which of the three input modes it
266
+ * has. Every table is sorted by name, so the module diffs by endpoint
267
+ * and never moves when registrations are reordered.
268
+ */
269
+ apiOptionEntries(): LambderApiOptionEntries;
249
270
  getResponseBuilder(ctx?: LambderRenderContext): LambderResponseBuilder<any>;
250
271
  private getResolver;
251
272
  getHandler(): LambderHandler;
@@ -15,6 +15,7 @@ import { LambderApiPipeline } from "../api/LambderApiPipeline.js";
15
15
  import { LAMBDER_BACKEND_SWAP, LAMBDER_CRASH_WATCH } from "../shared/util/LambderTestingDoors.js";
16
16
  import { apiSignatureOf } from "../api/LambderApiSignature.js";
17
17
  import { apiNameKeyOf } from "../shared/wire/LambderApiSignature.js";
18
+ import { assertPlainData } from "../shared/util/assertPlainData.js";
18
19
  import { apiNotFoundAnswer, refusalAnswer, sessionExpiredAnswer, } from "../api/LambderApiEnvelope.js";
19
20
  import { bindContextTools, createContext, isV2HttpEvent, } from "./LambderContext.js";
20
21
  import { COMPRESSED_PAYLOAD_GZ_FIELD, COMPRESSED_PAYLOAD_BR_FIELD, COMPRESSED_PAYLOAD_BYTES_FIELD } from "../shared/wire/LambderRequestPayload.js";
@@ -80,6 +81,8 @@ export default class Lambder {
80
81
  apiDefinitions = new Map();
81
82
  /** The guards map given at creation, kept for apiSignatures(): a guard's schema is part of the signature of every endpoint declaring it. */
82
83
  guards;
84
+ /** The rate-limit policies given at creation, kept for apiOptionEntries(), which records each one less its key handler. */
85
+ rateLimitPolicies;
83
86
  hookList = { "beforeRender": [], "afterRender": [], "fallback": [] };
84
87
  createdHooks = [];
85
88
  initPromise = null;
@@ -122,6 +125,7 @@ export default class Lambder {
122
125
  }
123
126
  const session = options.session;
124
127
  this.guards = options.guards;
128
+ this.rateLimitPolicies = options.rateLimits?.policies;
125
129
  this.pipeline = new LambderApiPipeline({
126
130
  apiVersion: this.apiVersion,
127
131
  minApiVersion: options.minApiVersion,
@@ -413,6 +417,71 @@ export default class Lambder {
413
417
  entries.sort((a, b) => (a.key < b.key ? -1 : a.key > b.key ? 1 : 0));
414
418
  return entries;
415
419
  }
420
+ /**
421
+ * Every registered API's mode and declared options as plain data, with
422
+ * the rate-limit policies and guards they name reduced to what is not
423
+ * code: what writeApiOptions (lambder/build) writes to a module a client,
424
+ * a mock or a test imports instead of the server. The contract carries
425
+ * the same options as types; this is the same fact as a value, for code
426
+ * that decides something at runtime with it.
427
+ *
428
+ * Nothing here is a secret or a handler by construction. A guard's
429
+ * parameter is written as it was declared, so it has to be plain data
430
+ * (a permission string, a list, a reason); one that is not fails by API
431
+ * and guard name. A policy's key handler is never written: its `per`
432
+ * says "custom" and no more. A guard's input schema is never written
433
+ * either; its declaration says only which of the three input modes it
434
+ * has. Every table is sorted by name, so the module diffs by endpoint
435
+ * and never moves when registrations are reordered.
436
+ */
437
+ apiOptionEntries() {
438
+ const apis = {};
439
+ for (const name of [...this.apiDefinitions.keys()].sort()) {
440
+ const { mode, guards, rateLimit, idempotency } = this.apiDefinitions.get(name);
441
+ const entry = { mode };
442
+ if (guards !== undefined) {
443
+ assertPlainData(guards, `the guards option of API "${name}"`);
444
+ entry.guards = guards;
445
+ }
446
+ if (rateLimit !== undefined) {
447
+ assertPlainData(rateLimit, `the rateLimit option of API "${name}"`);
448
+ entry.rateLimit = rateLimit;
449
+ }
450
+ if (idempotency !== undefined)
451
+ entry.idempotency = idempotency;
452
+ apis[name] = entry;
453
+ }
454
+ const rateLimitPolicies = {};
455
+ for (const name of Object.keys(this.rateLimitPolicies ?? {}).sort()) {
456
+ const { per, budget, chargeAt, errorMessage, ...windows } = this.rateLimitPolicies[name];
457
+ const entry = {};
458
+ for (const [window, limit] of Object.entries(windows)) {
459
+ if (limit !== undefined)
460
+ entry[window] = limit;
461
+ }
462
+ if (per !== undefined)
463
+ entry.per = per === "ip" || per === "session" ? per : "custom";
464
+ if (budget !== undefined)
465
+ entry.budget = budget;
466
+ if (chargeAt !== undefined)
467
+ entry.chargeAt = chargeAt;
468
+ if (errorMessage !== undefined) {
469
+ assertPlainData(errorMessage, `the errorMessage of rate-limit policy "${name}"`);
470
+ entry.errorMessage = errorMessage;
471
+ }
472
+ rateLimitPolicies[name] = entry;
473
+ }
474
+ const guards = {};
475
+ for (const name of Object.keys(this.guards ?? {}).sort()) {
476
+ const guard = this.guards[name];
477
+ guards[name] = {
478
+ input: guard.apiInput ? "apiInput" : guard.guardInput ? "guardInput" : "none",
479
+ session: guard.session === true,
480
+ runAt: guard.runAt ?? "beforeInputValidation",
481
+ };
482
+ }
483
+ return { apis, rateLimitPolicies, guards };
484
+ }
416
485
  getResponseBuilder(ctx) {
417
486
  return new LambderResponseBuilder({
418
487
  files: this.files,
package/dist/index.d.ts CHANGED
@@ -12,6 +12,11 @@ export { lambderCookieJarTransport } from "./shared/transport/lambderCookieJarTr
12
12
  export type { LambderApiTransport, LambderApiTransportRequest, LambderTransportFailureReason } from "./shared/transport/LambderApiTransport.js";
13
13
  export { LambderCookieJar, parseSetCookie } from "./shared/transport/LambderCookieJar.js";
14
14
  export { LambderExpiringMap, LambderExpiringMapFullError } from "./shared/util/LambderExpiringMap.js";
15
+ export { LambderBackoffTimer } from "./shared/util/LambderBackoffTimer.js";
16
+ export type { LambderBackoffTimerOptions } from "./shared/util/LambderBackoffTimer.js";
17
+ export { LambderSignedClaims, keyedDigest, randomSecret } from "./shared/util/LambderSignedClaims.js";
18
+ export type { LambderSignedClaimsOptions } from "./shared/util/LambderSignedClaims.js";
19
+ export { constantTimeEquals } from "./shared/util/LambderTextDigest.js";
15
20
  export type { LambderStoredCookie } from "./shared/transport/LambderCookieJar.js";
16
21
  export { lambderHandlerTransport } from "./invoke/lambderHandlerTransport.js";
17
22
  export type { LambderHandlerTransportOptions } from "./invoke/lambderHandlerTransport.js";
@@ -106,6 +111,12 @@ export type { LambderRateLimiter, LambderRateLimitWindow, LambderRateLimitPolicy
106
111
  export { LambderDdbRateLimiter } from "./stores/LambderDdbRateLimiter.js";
107
112
  export type { LambderDdbRateLimiterOptions } from "./stores/LambderDdbRateLimiter.js";
108
113
  export { LambderMemoryRateLimiter } from "./stores/LambderMemoryRateLimiter.js";
114
+ export { LambderOneShotSecrets } from "./secrets/LambderOneShotSecrets.js";
115
+ export type { LambderOneShotSecretKind, LambderOneShotSecretsOptions, LambderOneShotIssueResult, LambderOneShotRedeemResult, LambderOneShotCodeKindNames, LambderOneShotTokenKindNames, } from "./secrets/LambderOneShotSecrets.js";
116
+ export type { LambderOneShotSecretStore, LambderOneShotSecretRecord, LambderOneShotSecretDraft, LambderOneShotSecretShape, LambderOneShotIssueOutcome, } from "./shared/contracts/LambderOneShotSecretStore.js";
117
+ export { LambderDdbOneShotSecretStore } from "./stores/LambderDdbOneShotSecretStore.js";
118
+ export type { LambderDdbOneShotSecretStoreOptions } from "./stores/LambderDdbOneShotSecretStore.js";
119
+ export { LambderMemoryOneShotSecretStore } from "./stores/LambderMemoryOneShotSecretStore.js";
109
120
  export type { LambderIdempotencyStore, LambderIdempotencyBeginResult, LambderIdempotencyDoneRecord, } from "./shared/contracts/LambderIdempotencyStore.js";
110
121
  export { LambderDdbIdempotencyStore } from "./stores/LambderDdbIdempotencyStore.js";
111
122
  export type { LambderDdbIdempotencyStoreOptions } from "./stores/LambderDdbIdempotencyStore.js";
@@ -118,6 +129,8 @@ export type { LambderRateLimitKeyBuilder } from "./api/LambderApiRateLimits.js";
118
129
  export type { LambderContextRateLimit, LambderContextRateLimitCheck, LambderRateLimitCheckResult, LambderChargeablePolicyNames, LambderChargeKeyArgs, LambderRateLimitKeyFn, LambderRateLimitPer, LambderRateLimitBudget, LambderRateLimitChargeAt, LambderApiRateLimitPolicyConfig, LambderApiRateLimitsConfig, LambderAllowedPolicyNames, LambderRateLimitOption, } from "./api/LambderApiRateLimits.js";
119
130
  export type { LambderApiIdempotencyConfig } from "./api/LambderApiIdempotency.js";
120
131
  export type { LambderGuardsOptionValue, LambderRateLimitOverride, LambderRateLimitOptionValue, LambderApiIdempotencyOption, } from "./shared/wire/LambderApiOptionValues.js";
132
+ export { apiGuardParam } from "./shared/wire/LambderApiOptionEntries.js";
133
+ export type { LambderApiOptionEntries, LambderApiOptionEntry, LambderRateLimitPolicyEntry, LambderGuardDeclarationEntry, LambderApisWithGuard, LambderApisGuardedBy, LambderApisWithMode, LambderGuardParamOf, } from "./shared/wire/LambderApiOptionEntries.js";
121
134
  export { createLambderI18n } from "./shared/LambderI18n.js";
122
135
  export type { LambderLanguageMeta, LambderI18nConfig, LambderI18nInstance, LambderI18nTranslator, LambderI18nExtractParams, LambderI18nDictionaryLoader, LambderI18nCodes, LambderI18nKeys, LambderI18nTranslatorFor, } from "./shared/LambderI18n.js";
123
136
  export type { LambderApiContractShape, LambderApiMode, LambderApiEnvelopeBody, LambderApiResponseConfig, LambderApiNullAnswerConfig, LambderContractEntry, LambderMergeContract, LambderGuardNamesIn, LambderContractMode, LambderContractKeysWithMode, LambderContractKeysWithGuard, LambderJsonOf, LambderJsonOutputOf, LambderContractGuardsOf, LambderContractGuardNames, LambderContractGuardInputsOf, LambderContractGuardInput, LambderContractGuardInputNames, LambderContractRateLimitOf, LambderContractRateLimitNames, LambderContractIdempotencyOf, } from "./shared/wire/LambderApiContract.js";
package/dist/index.js CHANGED
@@ -9,6 +9,11 @@ export { buildTransportEnvelope, LambderTransportFailure, isLambderTransportFail
9
9
  export { lambderCookieJarTransport } from "./shared/transport/lambderCookieJarTransport.js";
10
10
  export { LambderCookieJar, parseSetCookie } from "./shared/transport/LambderCookieJar.js";
11
11
  export { LambderExpiringMap, LambderExpiringMapFullError } from "./shared/util/LambderExpiringMap.js";
12
+ // Waiting longer after each failure, once: what the upload runner and an app's own retrying code climb.
13
+ export { LambderBackoffTimer } from "./shared/util/LambderBackoffTimer.js";
14
+ // Signed claims tokens, and the keyed digest and random secret for the secrets an app stores.
15
+ export { LambderSignedClaims, keyedDigest, randomSecret } from "./shared/util/LambderSignedClaims.js";
16
+ export { constantTimeEquals } from "./shared/util/LambderTextDigest.js";
12
17
  export { lambderHandlerTransport } from "./invoke/lambderHandlerTransport.js";
13
18
  export { synthesizeLambdaHttpEvent, decodeLambdaHttpResult, localLambdaContext, LAMBDER_INVOKE_HEADER, LAMBDER_INVOKED_BY_HEADER, LAMBDER_INVOKE_PROTOCOL, } from "./invoke/LambderLambdaEvent.js";
14
19
  // The API core: the request, answer, envelope and pipeline both the server and the mock runtime run
@@ -71,6 +76,11 @@ export { LambderMemoryCache } from "./stores/LambderMemoryCache.js";
71
76
  export { RATE_LIMIT_WINDOWS } from "./shared/contracts/LambderRateLimiter.js";
72
77
  export { LambderDdbRateLimiter } from "./stores/LambderDdbRateLimiter.js";
73
78
  export { LambderMemoryRateLimiter } from "./stores/LambderMemoryRateLimiter.js";
79
+ // One-shot secrets: codes and tokens handed out once and taken back once, the
80
+ // store interface they live in, and the DynamoDB and in-memory stores
81
+ export { LambderOneShotSecrets } from "./secrets/LambderOneShotSecrets.js";
82
+ export { LambderDdbOneShotSecretStore } from "./stores/LambderDdbOneShotSecretStore.js";
83
+ export { LambderMemoryOneShotSecretStore } from "./stores/LambderMemoryOneShotSecretStore.js";
74
84
  export { LambderDdbIdempotencyStore } from "./stores/LambderDdbIdempotencyStore.js";
75
85
  export { LambderMemoryIdempotencyStore } from "./stores/LambderMemoryIdempotencyStore.js";
76
86
  // Declarative per-API policies: guards
@@ -78,6 +88,9 @@ export { lambderGuard, lambderRateLimitKey } from "./core/LambderPolicyBuilders.
78
88
  export { lambderGuardBuilder } from "./api/LambderApiGuards.js";
79
89
  // Declarative per-API policies: rate limits
80
90
  export { lambderRateLimitKeyBuilder, rateLimitRefusal, DEFAULT_RATE_LIMIT_REFUSAL } from "./api/LambderApiRateLimits.js";
91
+ // The declared options as plain data: what apiOptionEntries() reports and
92
+ // writeApiOptions writes, and the readers over the generated tables.
93
+ export { apiGuardParam } from "./shared/wire/LambderApiOptionEntries.js";
81
94
  // Typed translations (standalone, isomorphic)
82
95
  export { createLambderI18n } from "./shared/LambderI18n.js";
83
96
  export { resolveApiOutcome } from "./shared/wire/LambderApiOutcome.js";
@@ -10,6 +10,7 @@ import { LambderMemoryIdempotencyStore } from "../stores/LambderMemoryIdempotenc
10
10
  import { LambderMemorySessionStore } from "../stores/LambderMemorySessionStore.js";
11
11
  import LambderSessionManager, { type LambderCreatedSession } from "../session/LambderSessionManager.js";
12
12
  import type { LambderMockAppOptions, LambderMockIdempotencyOptions, LambderMockTransport, LambderMockTransportOptions } from "./LambderMockCreateOptions.js";
13
+ import type { LambderApiOptionEntry, LambderGuardDeclarationEntry } from "../shared/wire/LambderApiOptionEntries.js";
13
14
  import type { LambderMockCallContext, LambderMockCallRecord, LambderMockEntry, LambderMockEntryInput, LambderMockFailure, LambderMockFailureReason, LambderMockHandler, LambderMockLatency, LambderMockListener, LambderMockPublicNames, LambderMockRateLimitPolicies, LambderMockRegistryCheck, LambderMockRestEntry, LambderMockSessionCallContext, LambderMockSessionNames, LambderMockSlice, LambderMockOverride } from "./LambderMockTypes.js";
14
15
  /**
15
16
  * The mock runtime: the API core (LambderApiPipeline, the same class the
@@ -23,9 +24,10 @@ import type { LambderMockCallContext, LambderMockCallRecord, LambderMockEntry, L
23
24
  *
24
25
  * Create one with initLambderMock<Contract, SessionData>().create(...),
25
26
  * which fixes the contract and session types first so everything else is
26
- * inferred from the options.
27
+ * inferred from the options. `TDerived` is true for a mock created with the
28
+ * generated `apiOptions` table, whose entries are their handlers alone.
27
29
  */
28
- export declare class LambderMockApp<C extends LambderApiContractShape, S = any, G extends Record<string, LambderApiGuard<any, any, any>> = {}> {
30
+ export declare class LambderMockApp<C extends LambderApiContractShape, S = any, G extends Record<string, LambderApiGuard<any, any, any>> = {}, TDerived extends boolean = false> {
29
31
  readonly apiVersion: string | null;
30
32
  /** The memory stores, for assertions and reset; null for a subsystem that is off or backed by a store of yours. */
31
33
  readonly sessionStore: LambderMemorySessionStore<S> | null;
@@ -61,9 +63,11 @@ export declare class LambderMockApp<C extends LambderApiContractShape, S = any,
61
63
  private readonly recorder;
62
64
  /** Registered entries and the overrides over them (see LambderMockEntryRegistry). */
63
65
  private readonly registry;
66
+ /** The server's declared options per API, when create() was given the generated table; the entries' declarations come from here. */
67
+ private readonly apiOptions;
64
68
  /** The jars the runtime owns and what it planted in document.cookie (see LambderMockBrowserCookies). */
65
69
  private readonly browserCookies;
66
- constructor(options: LambderMockAppOptions<C, S, G>);
70
+ constructor(options: LambderMockAppOptions<C, S, G, LambderMockRateLimitPolicies<S>, boolean | LambderMockIdempotencyOptions<S>, {}, Record<string, LambderApiOptionEntry> | undefined>);
67
71
  /**
68
72
  * The registration-time checks every entry goes through, mocked or not:
69
73
  * the ones the server runs on a definition, and the mock's own "a session
@@ -75,11 +79,21 @@ export declare class LambderMockApp<C extends LambderApiContractShape, S = any,
75
79
  * option, for a mistake fixed by one option at create().
76
80
  */
77
81
  private assertEntryRegistration;
82
+ /**
83
+ * The table's entry for an endpoint, when create() was given one: null
84
+ * without a table, and a throw for a name the table does not hold or an
85
+ * entry registered under the other mode. The compiler already refuses
86
+ * both against the contract; this is where a stale table meets a caller
87
+ * the compiler did not see.
88
+ */
89
+ private declaredOptionsOf;
90
+ /** The mode the apiOptions table gives a name, or null without a table or for a name it does not hold. */
91
+ private declaredModeOf;
78
92
  private buildEntry;
79
93
  /** A mock for a public endpoint: a handler, or the handler with the endpoint's declarations restated. */
80
- publicApi<K extends LambderMockPublicNames<C>, TInputSchema extends z.ZodType = z.ZodType>(name: K, entry: LambderMockEntryInput<C, K, S, G, TInputSchema>): LambderMockEntry<C, K>;
94
+ publicApi<K extends LambderMockPublicNames<C>, TInputSchema extends z.ZodType = z.ZodType>(name: K, entry: LambderMockEntryInput<C, K, S, G, TInputSchema, TDerived>): LambderMockEntry<C, K>;
81
95
  /** A mock for a session endpoint: the pipeline fetches the session before the handler runs, and refuses without one. */
82
- sessionApi<K extends LambderMockSessionNames<C>, TInputSchema extends z.ZodType = z.ZodType>(name: K, entry: LambderMockEntryInput<C, K, S, G, TInputSchema>): LambderMockEntry<C, K>;
96
+ sessionApi<K extends LambderMockSessionNames<C>, TInputSchema extends z.ZodType = z.ZodType>(name: K, entry: LambderMockEntryInput<C, K, S, G, TInputSchema, TDerived>): LambderMockEntry<C, K>;
83
97
  /**
84
98
  * A public endpoint deliberately left without a mock; a call answers the
85
99
  * notMocked refusal carrying the reason.
@@ -111,13 +125,15 @@ export declare class LambderMockApp<C extends LambderApiContractShape, S = any,
111
125
  * still refused, and an entry registered later (registerPartial, or a
112
126
  * second register) takes its endpoint back from the rest.
113
127
  *
114
- * What it cannot do is the session read. The mode of an unregistered name
115
- * is not knowable at runtime (the contract is a type), so a call it
116
- * answers is processed as public: the protocol's pre-pass still runs, so
117
- * a stale client still hears versionExpired, but a signed-out call to an
118
- * unmocked session endpoint answers "not mocked" where the server answers
119
- * sessionExpired. Declare an endpoint whose signed-out path a test cares
120
- * about with sessionNotMocked instead.
128
+ * The mode of an unregistered name comes from the apiOptions table, when
129
+ * create() was given one, so an unmocked session endpoint still reads the
130
+ * session and a signed-out call answers sessionExpired as on the server.
131
+ * Without the table the mode is not knowable at runtime (the contract is
132
+ * a type), and a call it answers is processed as public: the protocol's
133
+ * pre-pass still runs, so a stale client still hears versionExpired, but
134
+ * a signed-out call to an unmocked session endpoint answers "not mocked"
135
+ * where the server answers sessionExpired. Declare an endpoint whose
136
+ * signed-out path a test cares about with sessionNotMocked there.
121
137
  */
122
138
  restNotMocked(reason: string): LambderMockRestEntry;
123
139
  private buildNotMockedEntry;
@@ -165,10 +181,11 @@ export declare class LambderMockApp<C extends LambderApiContractShape, S = any,
165
181
  /**
166
182
  * The entry that answers a name nothing registered, when register() was
167
183
  * given a rest entry: the notMocked refusal carrying its reason, run
168
- * through the pipeline as a public endpoint, since the mode of an
169
- * unregistered name cannot be recovered at runtime. Everything before
170
- * dispatch still runs (the signature gate, the payload restore); the
171
- * missing session read is the fidelity limit restNotMocked documents.
184
+ * through the pipeline under the mode the apiOptions table gives the
185
+ * name, and as a public endpoint where there is no table to say (the
186
+ * mode of an unregistered name cannot otherwise be recovered at runtime).
187
+ * Everything before dispatch still runs (the signature gate, the payload
188
+ * restore, and for a session endpoint the session read).
172
189
  */
173
190
  private restNotMockedEntry;
174
191
  /** The next call to the endpoint fails this way; several calls queue in order. */
@@ -332,5 +349,5 @@ export declare const initLambderMock: <C extends LambderApiContractShape, S = an
332
349
  * dropped. `I` exists for the same reason `P` does, and
333
350
  * LambderMockSurplusKeys puts the error back on the key.
334
351
  */
335
- create<const G extends Record<string, LambderApiGuard<any, any, any, LambderMockCallContext<S>, LambderMockSessionCallContext<S>>> = {}, const P extends LambderMockRateLimitPolicies<S> = {}, I extends boolean | LambderMockIdempotencyOptions<S> = boolean | LambderMockIdempotencyOptions<S>>(options: LambderMockAppOptions<C, S, G, P, I>): LambderMockApp<C, S, G>;
352
+ create<const G extends Record<string, LambderApiGuard<any, any, any, LambderMockCallContext<S>, LambderMockSessionCallContext<S>>> = {}, const P extends LambderMockRateLimitPolicies<S> = {}, I extends boolean | LambderMockIdempotencyOptions<S> = boolean | LambderMockIdempotencyOptions<S>, const D extends Record<string, LambderGuardDeclarationEntry> = {}, const A extends Record<string, LambderApiOptionEntry> | undefined = undefined>(options: LambderMockAppOptions<C, S, G, P, I, D, A>): LambderMockApp<C, S, G, [A] extends [undefined] ? false : true>;
336
353
  };