lambder 9.0.2 → 10.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (84) hide show
  1. package/CHANGELOG.md +195 -0
  2. package/README.md +29 -14
  3. package/dist/api/LambderApiDefinition.d.ts +9 -0
  4. package/dist/api/LambderApiEnvelope.d.ts +22 -10
  5. package/dist/api/LambderApiEnvelope.js +57 -22
  6. package/dist/api/LambderApiGuards.d.ts +77 -22
  7. package/dist/api/LambderApiGuards.js +13 -1
  8. package/dist/api/LambderApiIdempotency.js +4 -4
  9. package/dist/api/LambderApiOutputValidationError.d.ts +10 -4
  10. package/dist/api/LambderApiOutputValidationError.js +14 -6
  11. package/dist/api/LambderApiPipeline.d.ts +2 -1
  12. package/dist/api/LambderApiPipeline.js +10 -2
  13. package/dist/api/LambderApiRateLimits.d.ts +15 -11
  14. package/dist/api/LambderApiRateLimits.js +31 -7
  15. package/dist/api/LambderApiRefusals.d.ts +204 -0
  16. package/dist/api/LambderApiRefusals.js +223 -0
  17. package/dist/api/LambderApiSignature.d.ts +13 -4
  18. package/dist/api/LambderApiSignature.js +21 -4
  19. package/dist/build/writeApiOptions.js +7 -5
  20. package/dist/client/LambderCaller.d.ts +28 -17
  21. package/dist/client/LambderCaller.js +19 -15
  22. package/dist/client/LambderUploadRunner.d.ts +5 -5
  23. package/dist/client/LambderUploadRunner.js +25 -16
  24. package/dist/client.d.ts +6 -5
  25. package/dist/client.js +2 -1
  26. package/dist/core/Lambder.d.ts +121 -23
  27. package/dist/core/Lambder.js +105 -51
  28. package/dist/core/LambderContext.d.ts +1 -1
  29. package/dist/core/LambderContext.js +5 -0
  30. package/dist/core/LambderCreateOptions.d.ts +58 -15
  31. package/dist/core/LambderCreateOptions.js +10 -0
  32. package/dist/core/LambderPolicyBuilders.d.ts +11 -8
  33. package/dist/core/LambderPolicyBuilders.js +10 -8
  34. package/dist/core/LambderResolver.d.ts +1 -1
  35. package/dist/core/LambderResolver.js +1 -1
  36. package/dist/core/LambderResponseBuilder.d.ts +11 -9
  37. package/dist/core/LambderResponseBuilder.js +12 -11
  38. package/dist/index.d.ts +13 -9
  39. package/dist/index.js +6 -2
  40. package/dist/invoke/LambderInvokeCaller.d.ts +5 -6
  41. package/dist/invoke/LambderInvokeCaller.js +13 -12
  42. package/dist/invoke/LambderInvokeOutcome.d.ts +32 -23
  43. package/dist/invoke/LambderInvokeOutcome.js +4 -4
  44. package/dist/mock/LambderMockApp.d.ts +86 -24
  45. package/dist/mock/LambderMockApp.js +108 -16
  46. package/dist/mock/LambderMockCallRecorder.js +6 -3
  47. package/dist/mock/LambderMockCreateOptions.d.ts +4 -5
  48. package/dist/mock/LambderMockFailureInjector.d.ts +9 -2
  49. package/dist/mock/LambderMockFailureInjector.js +18 -4
  50. package/dist/mock/LambderMockTypes.d.ts +34 -11
  51. package/dist/mock.d.ts +4 -3
  52. package/dist/mock.js +4 -3
  53. package/dist/shared/contracts/LambderUploadBucket.d.ts +18 -3
  54. package/dist/shared/contracts/LambderUploadBucket.js +2 -2
  55. package/dist/shared/util/LambderTypeUtilities.d.ts +11 -0
  56. package/dist/shared/wire/LambderApiContract.d.ts +96 -31
  57. package/dist/shared/wire/LambderApiOptionEntries.d.ts +19 -12
  58. package/dist/shared/wire/LambderApiOptionValues.d.ts +15 -6
  59. package/dist/shared/wire/LambderApiOptionValues.js +3 -3
  60. package/dist/shared/wire/LambderApiOutcome.d.ts +49 -27
  61. package/dist/shared/wire/LambderApiOutcome.js +28 -11
  62. package/dist/shared/wire/LambderApiRefusal.d.ts +127 -42
  63. package/dist/shared/wire/LambderApiRefusal.js +15 -7
  64. package/dist/shared/wire/LambderCrashDetail.d.ts +1 -1
  65. package/dist/shared/wire/LambderCrashDetail.js +1 -1
  66. package/dist/shared/wire/LambderHttpStatus.d.ts +6 -0
  67. package/dist/shared/wire/LambderIdempotencyKeyScope.d.ts +3 -2
  68. package/dist/shared/wire/LambderIdempotencyKeyScope.js +5 -5
  69. package/dist/shared/wire/LambderObjectPayload.d.ts +15 -0
  70. package/dist/shared/wire/LambderObjectPayload.js +24 -0
  71. package/dist/shared/wire/LambderOutcomeAssertions.d.ts +52 -7
  72. package/dist/shared/wire/LambderOutcomeAssertions.js +34 -8
  73. package/dist/shared/wire/LambderUploadObjectFields.d.ts +10 -1
  74. package/dist/shared/wire/LambderUploadObjectFields.js +22 -1
  75. package/dist/shared/wire/LambderUploadSchemas.d.ts +8 -2
  76. package/dist/shared/wire/LambderUploadSchemas.js +18 -7
  77. package/dist/stores/LambderMemoryUploadBucket.d.ts +21 -7
  78. package/dist/stores/LambderMemoryUploadBucket.js +60 -12
  79. package/dist/stores/LambderS3UploadBucket.d.ts +22 -3
  80. package/dist/stores/LambderS3UploadBucket.js +51 -4
  81. package/dist/testing/LambderTestApp.d.ts +1 -1
  82. package/dist/testing.d.ts +1 -1
  83. package/dist/testing.js +1 -1
  84. package/package.json +1 -1
@@ -1,5 +1,5 @@
1
1
  import { LambderResponse } from "./LambderResponse.js";
2
- import { buildApiEnvelope } from "../api/LambderApiEnvelope.js";
2
+ import { plainRefusalEnvelope } from "../api/LambderApiEnvelope.js";
3
3
  /**
4
4
  * Builds the responses of routes, hooks and error handlers. An API handler
5
5
  * never holds one: it returns its output or refuses, and writes headers and
@@ -92,7 +92,7 @@ export default class LambderResponseBuilder {
92
92
  }
93
93
  ;
94
94
  versionExpired(options) {
95
- return this.api(null, { versionExpired: true }, options);
95
+ return this.apiRefusal({ versionExpired: true }, options);
96
96
  }
97
97
  ;
98
98
  fileBase64(fileBase64, mimeType, options) {
@@ -135,16 +135,17 @@ export default class LambderResponseBuilder {
135
135
  }
136
136
  ;
137
137
  /**
138
- * An API envelope written by hand: what a hook, an input validation
139
- * handler or a global error handler answers an API call with (a refusal
140
- * flag, an errorMessage, a crash). The payload goes out as given; an API
141
- * handler's own output is parsed through its schema by the instance
142
- * instead. The logList channel is what the request accumulated unless the
143
- * config names its own.
138
+ * An API call answered from outside its handler: what a hook, a fallback,
139
+ * the input validation handler or the global error handler answers with.
140
+ * It is always a refusal (a refusal message, or one of the versionExpired,
141
+ * sessionExpired and notAuthorized flags) with a null payload, so a
142
+ * caller's success is only ever the handler's parsed output. Its message
143
+ * carries a framework code or none, and no data, since it answers outside
144
+ * any one endpoint's declared refusals. The logList channel is what the
145
+ * request accumulated unless the config names its own.
144
146
  */
145
- api(payload, config = {}, options) {
146
- const envelope = buildApiEnvelope(this.apiVersion, payload, { ...config, logList: config.logList || this.ctx?.logList });
147
- return this.json(envelope, options);
147
+ apiRefusal(config, options) {
148
+ return this.json(plainRefusalEnvelope(this.apiVersion, config, "res.apiRefusal()", this.ctx?.logList), options);
148
149
  }
149
150
  ;
150
151
  }
package/dist/index.d.ts CHANGED
@@ -38,18 +38,20 @@ export { apiNameKeyOf, lookupApiSignature, readApiSignature, API_SIGNATURE_HEX_L
38
38
  export type { LambderApiSignatureMap } from "./shared/wire/LambderApiSignatureMap.js";
39
39
  export { RELOAD_LOOP_WINDOW_MS } from "./client/LambderReloadLoopBreaker.js";
40
40
  export { compareDottedVersions, isDottedVersion } from "./shared/wire/LambderVersionOrder.js";
41
- export { buildApiEnvelope, envelopeAnswer, refusalAnswer, validationAnswer, apiNotFoundAnswer, sessionExpiredAnswer, versionExpiredAnswer, invalidPayloadAnswer, crashAnswer, API_ANSWER_CONTENT_TYPE, } from "./api/LambderApiEnvelope.js";
42
- export type { LambderApiEnvelopeConfig, LambderValidationAnswerBody } from "./api/LambderApiEnvelope.js";
41
+ export { successEnvelope, refusalEnvelope, plainRefusalEnvelope, envelopeAnswer, refusalAnswer, validationAnswer, apiNotFoundAnswer, sessionExpiredAnswer, versionExpiredAnswer, invalidPayloadAnswer, crashAnswer, API_ANSWER_CONTENT_TYPE, } from "./api/LambderApiEnvelope.js";
42
+ export type { LambderValidationAnswerBody } from "./api/LambderApiEnvelope.js";
43
43
  export { LambderApiValidationRefusal, isLambderApiValidationRefusal } from "./api/LambderApiValidationRefusal.js";
44
44
  export { LambderApiOutputValidationError } from "./api/LambderApiOutputValidationError.js";
45
+ export { checkedRefusal } from "./api/LambderApiRefusals.js";
45
46
  export { LambderInvokeError, isLambderInvokeError } from "./invoke/LambderInvokeOutcome.js";
46
47
  export type { LambderInvokeOutcome, LambderInvokeFailure, LambderInvokeValidationFailure, LambderInvokeCrashFailure, LambderInvokePayloadTooLargeFailure, LambderInvokeEnvelopeFailure, LambderInvokeDeliveryFailure, LambderInvokeFailureReason, LambderInvokeFunctionError, } from "./invoke/LambderInvokeOutcome.js";
47
48
  export { default as LambderInvokeCaller, LAMBDER_INVOKE_MAX_EVENT_BYTES, } from "./invoke/LambderInvokeCaller.js";
48
49
  export type { LambderInvokeCallerOptions, LambderInvokeCallOptions, LambderInvokeFailureHandler, LambderInvokeLogListHandler, LambderInvokeTransport, LambderInvokeTransportResult, LambderInvokeRequestInit, LambderInvokeEventInit, } from "./invoke/LambderInvokeCaller.js";
49
50
  export { describeCrash, errorFromCrashDetail } from "./shared/wire/LambderCrashDetail.js";
50
51
  export type { LambderCrashDetail, LambderCrashCause } from "./shared/wire/LambderCrashDetail.js";
51
- export { LambderApiRefusal, isLambderApiRefusal, refuse, refusalMessageOf, LAMBDER_REFUSAL_CODES } from "./shared/wire/LambderApiRefusal.js";
52
- export type { LambderApiRefusalOptions, LambderRefusalMessage, LambderAppRefusalMessage, LambderRefusalCode, LambderRefuseOptions } from "./shared/wire/LambderApiRefusal.js";
52
+ export { LambderApiRefusal, isLambderApiRefusal, isLambderRefusalCode, refuse, refusalMessageOf, LAMBDER_REFUSAL_CODES } from "./shared/wire/LambderApiRefusal.js";
53
+ export type { LambderApiRefusalOptions, LambderRefusalMessage, LambderPlainRefusalMessage, LambderUncheckedRefusalMessage, LambderRefusalCode, LambderRefuseOptions, LambderDeclaredRefuseOptions, LambderDeclaredRefuse, } from "./shared/wire/LambderApiRefusal.js";
54
+ export { isObjectPayload } from "./shared/wire/LambderObjectPayload.js";
53
55
  export { default as LambderResponseBuilder } from "./core/LambderResponseBuilder.js";
54
56
  export { default as LambderResolver } from "./core/LambderResolver.js";
55
57
  export { default as LambderSessionManager } from "./session/LambderSessionManager.js";
@@ -64,7 +66,7 @@ export type { LambderSessionCrypto } from "./session/LambderSessionCrypto.js";
64
66
  export { LambderDdbSessionStore } from "./stores/LambderDdbSessionStore.js";
65
67
  export type { LambderDdbSessionStoreOptions } from "./stores/LambderDdbSessionStore.js";
66
68
  export { LambderResponse, finalizeResponse, answerFromResponse, responseFromAnswer, type LambderHttpResponse, type LambderHeadersInput, type LambderFinalizeOptions, type LambderResponseCompressionSettings, type LambderResponseCompressionOption, } from "./core/LambderResponse.js";
67
- export type { LambderHttpStatusCode } from "./shared/wire/LambderHttpStatus.js";
69
+ export type { LambderHttpStatusCode, LambderRefusalStatusCode } from "./shared/wire/LambderHttpStatus.js";
68
70
  export { html, xml, raw, jsonScript, escapeHtml, renderHtmlValue, LambderSafeHtml, type LambderHtmlValue } from "./shared/LambderHtml.js";
69
71
  export { LambderTemplatingEngine } from "./core/LambderTemplatingEngine.js";
70
72
  export type { LambderTemplateData, LambderTemplatingEngineOptions } from "./core/LambderTemplatingEngine.js";
@@ -88,7 +90,7 @@ export type { LambderS3UploadBucketOptions } from "./stores/LambderS3UploadBucke
88
90
  export { LambderMemoryUploadBucket } from "./stores/LambderMemoryUploadBucket.js";
89
91
  export type { LambderMemoryUploadBucketOptions, LambderMemoryUploadObject } from "./stores/LambderMemoryUploadBucket.js";
90
92
  export { checkUploadRule } from "./shared/contracts/LambderUploadBucket.js";
91
- export type { LambderUploadBucket, LambderUploadRule, LambderUploadFileFacts, LambderUploadTicket, LambderUploadVerdict, LambderUploadRuleVerdict, LambderUploadObjectOptions, LambderUploadContentDisposition, } from "./shared/contracts/LambderUploadBucket.js";
93
+ export type { LambderUploadBucket, LambderUploadRule, LambderUploadFileFacts, LambderUploadTicket, LambderUploadMethod, LambderUploadVerdict, LambderUploadRuleVerdict, LambderUploadObjectOptions, LambderUploadContentDisposition, } from "./shared/contracts/LambderUploadBucket.js";
92
94
  export { LambderUploadFileFactsSchema, LambderUploadTicketSchema } from "./shared/wire/LambderUploadSchemas.js";
93
95
  export { refuseUnacceptedUpload } from "./shared/wire/LambderUploadRefusal.js";
94
96
  export { LambderUploadRunner, LambderUploadError } from "./client/LambderUploadRunner.js";
@@ -123,17 +125,19 @@ export type { LambderDdbIdempotencyStoreOptions } from "./stores/LambderDdbIdemp
123
125
  export { LambderMemoryIdempotencyStore } from "./stores/LambderMemoryIdempotencyStore.js";
124
126
  export { lambderGuard, lambderRateLimitKey } from "./core/LambderPolicyBuilders.js";
125
127
  export { lambderGuardBuilder } from "./api/LambderApiGuards.js";
126
- export type { LambderGuardBuilder, LambderApiGuard, LambderGuardMeta, LambderGuardMetaMap, LambderGuardRunAt, LambderAllowedGuardNames, LambderParamlessGuardNames, LambderGuardsOption, LambderGuardDataOf, LambderGuardInputsOf, } from "./api/LambderApiGuards.js";
128
+ export type { LambderGuardBuilder, LambderApiGuard, LambderGuardMeta, LambderGuardMetaMap, LambderGuardRunAt, LambderAllowedGuardNames, LambderParamlessGuardNames, LambderGuardsOption, LambderGuardDataOf, LambderGuardInputsOf, LambderGuardRefusals, LambderGuardRefusalNamesOf, } from "./api/LambderApiGuards.js";
129
+ export { LambderApiRefusalValidationError } from "./api/LambderApiRefusals.js";
130
+ export type { LambderRefusalDeclaration, LambderRefusalVocabulary, LambderApiAllowedRefusal, LambderApiAllowedRefusals, LambderEndpointRefusals, LambderRefusalsOption, LambderRefusalNamesIn, LambderHandlerRefusalsOf, LambderWireRefusalsOf, } from "./api/LambderApiRefusals.js";
127
131
  export { lambderRateLimitKeyBuilder, rateLimitRefusal, DEFAULT_RATE_LIMIT_REFUSAL } from "./api/LambderApiRateLimits.js";
128
132
  export type { LambderRateLimitKeyBuilder } from "./api/LambderApiRateLimits.js";
129
133
  export type { LambderContextRateLimit, LambderContextRateLimitCheck, LambderRateLimitCheckResult, LambderChargeablePolicyNames, LambderChargeKeyArgs, LambderRateLimitKeyFn, LambderRateLimitPer, LambderRateLimitBudget, LambderRateLimitChargeAt, LambderApiRateLimitPolicyConfig, LambderApiRateLimitsConfig, LambderAllowedPolicyNames, LambderRateLimitOption, } from "./api/LambderApiRateLimits.js";
130
134
  export type { LambderApiIdempotencyConfig } from "./api/LambderApiIdempotency.js";
131
- export type { LambderGuardsOptionValue, LambderRateLimitOverride, LambderRateLimitOptionValue, LambderApiIdempotencyOption, } from "./shared/wire/LambderApiOptionValues.js";
135
+ export type { LambderGuardsOptionValue, LambderRateLimitMessage, LambderRateLimitOverride, LambderRateLimitOptionValue, LambderRefusalsOptionValue, LambderApiIdempotencyOption, } from "./shared/wire/LambderApiOptionValues.js";
132
136
  export { apiGuardParam } from "./shared/wire/LambderApiOptionEntries.js";
133
137
  export type { LambderApiOptionEntries, LambderApiOptionEntry, LambderRateLimitPolicyEntry, LambderGuardDeclarationEntry, LambderApisWithGuard, LambderApisGuardedBy, LambderApisWithMode, LambderGuardParamOf, } from "./shared/wire/LambderApiOptionEntries.js";
134
138
  export { createLambderI18n } from "./shared/LambderI18n.js";
135
139
  export type { LambderLanguageMeta, LambderI18nConfig, LambderI18nInstance, LambderI18nTranslator, LambderI18nExtractParams, LambderI18nDictionaryLoader, LambderI18nCodes, LambderI18nKeys, LambderI18nTranslatorFor, } from "./shared/LambderI18n.js";
136
- export type { LambderApiContractShape, LambderApiMode, LambderApiEnvelopeBody, LambderApiResponseConfig, LambderContractEntry, LambderMergeContract, LambderGuardNamesIn, LambderContractMode, LambderContractKeysWithMode, LambderContractKeysWithGuard, LambderJsonOf, LambderJsonOutputOf, LambderContractGuardsOf, LambderContractGuardNames, LambderContractGuardInputsOf, LambderContractGuardInput, LambderContractGuardInputNames, LambderContractRateLimitOf, LambderContractRateLimitNames, LambderContractIdempotencyOf, } from "./shared/wire/LambderApiContract.js";
140
+ export type { LambderApiContractShape, LambderApiMode, LambderApiEnvelopeBody, LambderApiSuccessEnvelope, LambderApiRefusalEnvelope, LambderRefusalEnvelopeFields, LambderApiRefusalConfig, LambderContractEntry, LambderMergeContract, LambderGuardNamesIn, LambderContractMode, LambderContractKeysWithMode, LambderContractKeysWithGuard, LambderJsonOf, LambderContractRefusalsOf, LambderContractRefusalMessage, LambderContractRefusals, LambderContractAnyRefusalMessage, LambderContractGuardsOf, LambderContractGuardNames, LambderContractGuardInputsOf, LambderContractGuardInput, LambderContractGuardInputNames, LambderContractRateLimitOf, LambderContractRateLimitNames, LambderContractIdempotencyOf, } from "./shared/wire/LambderApiContract.js";
137
141
  export type { LambderRenderContext, LambderSessionRenderContext, LambderHttpEvent, LambderHttpEventFormat } from "./core/LambderContext.js";
138
142
  export type { LambderApiAnswerOutcome, LambderApiSuccessOutcome, LambderApiCallFailure, LambderApiValidationFailure, LambderApiEnvelopeFailure, LambderApiHttpAnswer, } from "./shared/wire/LambderApiOutcome.js";
139
143
  export { resolveApiOutcome } from "./shared/wire/LambderApiOutcome.js";
package/dist/index.js CHANGED
@@ -27,16 +27,18 @@ export { apiSignatureOf } from "./api/LambderApiSignature.js";
27
27
  export { apiNameKeyOf, lookupApiSignature, readApiSignature, API_SIGNATURE_HEX_LENGTH, extensibleEnum } from "./shared/wire/LambderApiSignatureMap.js";
28
28
  export { RELOAD_LOOP_WINDOW_MS } from "./client/LambderReloadLoopBreaker.js";
29
29
  export { compareDottedVersions, isDottedVersion } from "./shared/wire/LambderVersionOrder.js";
30
- export { buildApiEnvelope, envelopeAnswer, refusalAnswer, validationAnswer, apiNotFoundAnswer, sessionExpiredAnswer, versionExpiredAnswer, invalidPayloadAnswer, crashAnswer, API_ANSWER_CONTENT_TYPE, } from "./api/LambderApiEnvelope.js";
30
+ export { successEnvelope, refusalEnvelope, plainRefusalEnvelope, envelopeAnswer, refusalAnswer, validationAnswer, apiNotFoundAnswer, sessionExpiredAnswer, versionExpiredAnswer, invalidPayloadAnswer, crashAnswer, API_ANSWER_CONTENT_TYPE, } from "./api/LambderApiEnvelope.js";
31
31
  export { LambderApiValidationRefusal, isLambderApiValidationRefusal } from "./api/LambderApiValidationRefusal.js";
32
32
  export { LambderApiOutputValidationError } from "./api/LambderApiOutputValidationError.js";
33
+ export { checkedRefusal } from "./api/LambderApiRefusals.js";
33
34
  // Calling a Lambder app from another lambda (server-only: the Lambda SDK, zlib)
34
35
  export { LambderInvokeError, isLambderInvokeError } from "./invoke/LambderInvokeOutcome.js";
35
36
  export { default as LambderInvokeCaller, LAMBDER_INVOKE_MAX_EVENT_BYTES, } from "./invoke/LambderInvokeCaller.js";
36
37
  // A crash described for a caller allowed to see it (the envelope's `crash` field)
37
38
  export { describeCrash, errorFromCrashDetail } from "./shared/wire/LambderCrashDetail.js";
38
39
  // Typed API refusals (isomorphic: shared code may throw them from anywhere)
39
- export { LambderApiRefusal, isLambderApiRefusal, refuse, refusalMessageOf, LAMBDER_REFUSAL_CODES } from "./shared/wire/LambderApiRefusal.js";
40
+ export { LambderApiRefusal, isLambderApiRefusal, isLambderRefusalCode, refuse, refusalMessageOf, LAMBDER_REFUSAL_CODES } from "./shared/wire/LambderApiRefusal.js";
41
+ export { isObjectPayload } from "./shared/wire/LambderObjectPayload.js";
40
42
  export { default as LambderResponseBuilder } from "./core/LambderResponseBuilder.js";
41
43
  export { default as LambderResolver } from "./core/LambderResolver.js";
42
44
  export { default as LambderSessionManager } from "./session/LambderSessionManager.js";
@@ -86,6 +88,8 @@ export { LambderMemoryIdempotencyStore } from "./stores/LambderMemoryIdempotency
86
88
  // Declarative per-API policies: guards
87
89
  export { lambderGuard, lambderRateLimitKey } from "./core/LambderPolicyBuilders.js";
88
90
  export { lambderGuardBuilder } from "./api/LambderApiGuards.js";
91
+ // Declarative per-API policies: declared refusals
92
+ export { LambderApiRefusalValidationError } from "./api/LambderApiRefusals.js";
89
93
  // Declarative per-API policies: rate limits
90
94
  export { lambderRateLimitKeyBuilder, rateLimitRefusal, DEFAULT_RATE_LIMIT_REFUSAL } from "./api/LambderApiRateLimits.js";
91
95
  // The declared options as plain data: what apiOptionEntries() reports and
@@ -24,7 +24,7 @@ import type { APIGatewayProxyEventV2, Context } from "aws-lambda";
24
24
  import { type LambderInvokeFailure, type LambderInvokeOutcome } from "./LambderInvokeOutcome.js";
25
25
  import { type LambderApiSignatureMap } from "../shared/wire/LambderApiSignatureMap.js";
26
26
  import type { LambdaClient, LambdaClientConfig } from "@aws-sdk/client-lambda";
27
- import type { LambderApiContractShape } from "../shared/wire/LambderApiContract.js";
27
+ import type { LambderApiContractShape, LambderContractRefusalMessage } from "../shared/wire/LambderApiContract.js";
28
28
  import { type LambderCallArgs, type LambderContractOutputOf, type LambderGuardInputsProviderOption, type LambderSharedCallOptions } from "../shared/wire/LambderCallOptions.js";
29
29
  import { type LambderCompressionOption } from "../shared/wire/LambderCompressionOption.js";
30
30
  import { type LambderInvokeSession, type LambderLambdaHttpResult } from "./LambderLambdaEvent.js";
@@ -214,14 +214,13 @@ export default class LambderInvokeCaller<TContract extends LambderApiContractSha
214
214
  * comes from the contract rather than a type parameter, so a call site
215
215
  * cannot replace it by annotating what it assigns to.
216
216
  */
217
- apiOutcome<TApiName extends keyof TContract & string = string>(apiName: TApiName, ...rest: LambderCallArgs<TContract, TApiName, TProvidedGuards, LambderInvokeCallOptions>): Promise<LambderInvokeOutcome<LambderContractOutputOf<TContract, TApiName>>>;
217
+ apiOutcome<TApiName extends keyof TContract & string = string>(apiName: TApiName, ...rest: LambderCallArgs<TContract, TApiName, TProvidedGuards, LambderInvokeCallOptions>): Promise<LambderInvokeOutcome<LambderContractOutputOf<TContract, TApiName>, LambderContractRefusalMessage<TContract, TApiName>>>;
218
218
  /**
219
219
  * The declared output, or a thrown LambderInvokeError carrying the
220
220
  * outcome. A failed dependency is a failed request: the throw reaches the
221
- * app's global error handler with the callee's error as its cause. The
222
- * resolver lets a handler answer null only when the output allows it or
223
- * beside a reason (LambderApiAnswer), so null arrives only for a
224
- * nullable output.
221
+ * app's global error handler with the callee's error as its cause. Only
222
+ * the callee handler's own output reads as a success, so what this
223
+ * returns is always the contract's output.
225
224
  */
226
225
  api<TApiName extends keyof TContract & string = string>(apiName: TApiName, ...rest: LambderCallArgs<TContract, TApiName, TProvidedGuards, LambderInvokeCallOptions>): Promise<LambderContractOutputOf<TContract, TApiName>>;
227
226
  /**
@@ -227,7 +227,7 @@ export default class LambderInvokeCaller {
227
227
  apiName,
228
228
  functionName: this.functionName,
229
229
  status: init.status,
230
- errorMessage: init.errorMessage,
230
+ refusal: init.refusal,
231
231
  crash: init.crash,
232
232
  functionError: init.functionError,
233
233
  logList,
@@ -246,7 +246,7 @@ export default class LambderInvokeCaller {
246
246
  logList,
247
247
  cookies: init.cookies ?? [],
248
248
  ...(init.status !== undefined ? { status: init.status } : {}),
249
- ...(init.errorMessage !== undefined ? { errorMessage: init.errorMessage } : {}),
249
+ ...(init.refusal !== undefined ? { refusal: init.refusal } : {}),
250
250
  ...(init.retryAfterSeconds !== undefined ? { retryAfterSeconds: init.retryAfterSeconds } : {}),
251
251
  ...(init.zodError !== undefined ? { zodError: init.zodError } : {}),
252
252
  ...(init.crash !== undefined ? { crash: init.crash } : {}),
@@ -288,6 +288,9 @@ export default class LambderInvokeCaller {
288
288
  */
289
289
  async dispatch(apiName, payload, options = {}) {
290
290
  const idempotentAttempt = beginIdempotentAttempt(options.idempotencyKey);
291
+ // The reader cannot check a refusal's code against declarations it
292
+ // does not have; the callee never sends one its endpoint did not
293
+ // declare, which is what the endpoint's message type stands for.
291
294
  const outcome = await this.dispatchAttempt(apiName, payload, options, idempotentAttempt);
292
295
  // Only the first settle counts: an attempt that never left settled
293
296
  // itself as not sent.
@@ -378,11 +381,10 @@ export default class LambderInvokeCaller {
378
381
  // The answer's Set-Cookie values, so a session the callee rotated or
379
382
  // cleared is visible to whoever is carrying it.
380
383
  const cookies = http.cookies;
381
- // The declared output, by the callee's own typing: a handler returns
382
- // its output, so a success payload is null only where the output
383
- // allows null.
384
+ // The callee handler's output: only a handler's answer reads as a
385
+ // success, and an output is always an object or an array.
384
386
  if (outcome.ok)
385
- return { ok: true, payload: (outcome.payload ?? null), response: outcome.response, logList, cookies };
387
+ return { ok: true, payload: outcome.payload, response: outcome.response, logList, cookies };
386
388
  const shared = { status: outcome.status, retryAfterSeconds: outcome.retryAfterSeconds, logList, cookies };
387
389
  // Each failure reason carries different evidence, and the outcome
388
390
  // union says which: a rejected input has its issues and no envelope,
@@ -399,7 +401,7 @@ export default class LambderInvokeCaller {
399
401
  return await this.failureOutcome(apiName, {
400
402
  ...shared,
401
403
  reason: 'server',
402
- errorMessage: outcome.errorMessage,
404
+ refusal: outcome.refusal,
403
405
  response: outcome.response,
404
406
  crash: outcome.response?.crash,
405
407
  cause: outcome.error,
@@ -408,7 +410,7 @@ export default class LambderInvokeCaller {
408
410
  }
409
411
  return await this.failureOutcome(apiName, {
410
412
  ...shared,
411
- ...(outcome.reason === 'errorMessage' ? { reason: outcome.reason, errorMessage: outcome.errorMessage } : { reason: outcome.reason }),
413
+ ...(outcome.reason === 'refusal' ? { reason: outcome.reason, refusal: outcome.refusal } : { reason: outcome.reason }),
412
414
  response: outcome.response,
413
415
  crash: outcome.response.crash,
414
416
  });
@@ -429,10 +431,9 @@ export default class LambderInvokeCaller {
429
431
  /**
430
432
  * The declared output, or a thrown LambderInvokeError carrying the
431
433
  * outcome. A failed dependency is a failed request: the throw reaches the
432
- * app's global error handler with the callee's error as its cause. The
433
- * resolver lets a handler answer null only when the output allows it or
434
- * beside a reason (LambderApiAnswer), so null arrives only for a
435
- * nullable output.
434
+ * app's global error handler with the callee's error as its cause. Only
435
+ * the callee handler's own output reads as a success, so what this
436
+ * returns is always the contract's output.
436
437
  */
437
438
  async api(apiName, ...rest) {
438
439
  const [payload, options] = rest;
@@ -10,10 +10,10 @@
10
10
  * should not have to read the whole class to find it. None of these functions
11
11
  * touch the caller's state.
12
12
  */
13
- import type { LambderApiEnvelopeBody } from "../shared/wire/LambderApiContract.js";
13
+ import type { LambderApiRefusalEnvelope, LambderApiSuccessEnvelope } from "../shared/wire/LambderApiContract.js";
14
14
  import type { LambderApiFailureReason, LambderValidationError } from "../shared/wire/LambderApiOutcome.js";
15
15
  import type { LambderCrashDetail } from "../shared/wire/LambderCrashDetail.js";
16
- import type { LambderAppRefusalMessage } from "../shared/wire/LambderApiRefusal.js";
16
+ import type { LambderUncheckedRefusalMessage } from "../shared/wire/LambderApiRefusal.js";
17
17
  export type LambderInvokeFailureReason = LambderApiFailureReason | 'crash' | 'protocol' | 'payloadTooLarge';
18
18
  /** Lambda's own error payload for a FunctionError invocation. */
19
19
  export type LambderInvokeFunctionError = {
@@ -21,13 +21,17 @@ export type LambderInvokeFunctionError = {
21
21
  errorMessage?: string;
22
22
  trace?: string[];
23
23
  };
24
- /** What every invoke failure carries, whatever went wrong. */
25
- type LambderInvokeFailureFields = {
24
+ /**
25
+ * What every invoke failure carries, whatever went wrong. TMessage is the
26
+ * refusal message the endpoint can answer with (LambderContractRefusalMessage),
27
+ * on every arm, as on the browser caller's outcome.
28
+ */
29
+ type LambderInvokeFailureFields<TMessage extends LambderUncheckedRefusalMessage> = {
26
30
  ok: false;
27
31
  /** HTTP status, when the callee answered. */
28
32
  status?: number;
29
- /** Envelope errorMessage, when the callee provided one: always the message object, a plain string having been read as one (refusalMessageOf). */
30
- errorMessage?: LambderAppRefusalMessage;
33
+ /** The envelope's refusal, when the callee provided one: always the message object, a plain string having been read as one (refusalMessageOf). */
34
+ refusal?: TMessage;
31
35
  /** Seconds to wait before retrying, from the answer's Retry-After header. */
32
36
  retryAfterSeconds?: number;
33
37
  /** Always present: the error api() throws for this failure, with the callee's error as its cause when one is known. */
@@ -38,31 +42,31 @@ type LambderInvokeFailureFields = {
38
42
  cookies: string[];
39
43
  };
40
44
  /** HTTP 422: the callee rejected the input against the API's schema. Always carries the issues. */
41
- export type LambderInvokeValidationFailure = LambderInvokeFailureFields & {
45
+ export type LambderInvokeValidationFailure<TMessage extends LambderUncheckedRefusalMessage = LambderUncheckedRefusalMessage> = LambderInvokeFailureFields<TMessage> & {
42
46
  reason: 'validation';
43
47
  zodError: LambderValidationError;
44
48
  };
45
49
  /** Lambda reported a FunctionError: the callee failed outside the framework (an init failure, a timeout, out of memory). Always carries the runtime's error payload. */
46
- export type LambderInvokeCrashFailure = LambderInvokeFailureFields & {
50
+ export type LambderInvokeCrashFailure<TMessage extends LambderUncheckedRefusalMessage = LambderUncheckedRefusalMessage> = LambderInvokeFailureFields<TMessage> & {
47
51
  reason: 'crash';
48
52
  functionError: LambderInvokeFunctionError;
49
53
  };
50
54
  /** Refused before sending: the serialized event is over the invoke cap. Always carries its size. */
51
- export type LambderInvokePayloadTooLargeFailure = LambderInvokeFailureFields & {
55
+ export type LambderInvokePayloadTooLargeFailure<TMessage extends LambderUncheckedRefusalMessage = LambderUncheckedRefusalMessage> = LambderInvokeFailureFields<TMessage> & {
52
56
  reason: 'payloadTooLarge';
53
57
  /** The event's byte size, so a caller can say by how much it is over. */
54
58
  bytes: number;
55
59
  };
56
- /** The callee answered, and the envelope itself says the call is refused. Always carries that envelope, and an `errorMessage` refusal always carries its message. */
57
- export type LambderInvokeEnvelopeFailure = LambderInvokeFailureFields & {
58
- response: LambderApiEnvelopeBody<any>;
60
+ /** The callee answered, and the envelope itself says the call is refused. Always carries that envelope, and a `refusal` failure always carries the refusal. */
61
+ export type LambderInvokeEnvelopeFailure<TMessage extends LambderUncheckedRefusalMessage = LambderUncheckedRefusalMessage> = LambderInvokeFailureFields<TMessage> & {
62
+ response: LambderApiRefusalEnvelope;
59
63
  /** The callee's crash detail, when its global error handler sent one (the envelope's `crash` field). */
60
64
  crash?: LambderCrashDetail;
61
65
  } & ({
62
66
  reason: 'versionExpired' | 'sessionExpired' | 'notAuthorized';
63
67
  } | {
64
- reason: 'errorMessage';
65
- errorMessage: LambderAppRefusalMessage;
68
+ reason: 'refusal';
69
+ refusal: TMessage;
66
70
  });
67
71
  /**
68
72
  * Nothing usable came back: the invoke never arrived or was given up on, the
@@ -71,9 +75,9 @@ export type LambderInvokeEnvelopeFailure = LambderInvokeFailureFields & {
71
75
  * answered with Lambder's own envelope, which is how a crash detail and a
72
76
  * logList arrive with it.
73
77
  */
74
- export type LambderInvokeDeliveryFailure = LambderInvokeFailureFields & {
78
+ export type LambderInvokeDeliveryFailure<TMessage extends LambderUncheckedRefusalMessage = LambderUncheckedRefusalMessage> = LambderInvokeFailureFields<TMessage> & {
75
79
  reason: 'network' | 'timeout' | 'server' | 'protocol' | 'unknown';
76
- response?: LambderApiEnvelopeBody<any>;
80
+ response?: LambderApiRefusalEnvelope;
77
81
  /** The callee's crash detail, when its global error handler sent one (the envelope's `crash` field). */
78
82
  crash?: LambderCrashDetail;
79
83
  };
@@ -85,14 +89,19 @@ export type LambderInvokeDeliveryFailure = LambderInvokeFailureFields & {
85
89
  * caller's LambderApiOutcome is discriminated the same way.
86
90
  *
87
91
  * `error`, `logList` and `cookies` are on every arm, and `status`,
88
- * `errorMessage` and `retryAfterSeconds` are there whenever an answer came
92
+ * `refusal` and `retryAfterSeconds` are there whenever an answer came
89
93
  * back to read them from.
90
94
  */
91
- export type LambderInvokeFailure = LambderInvokeValidationFailure | LambderInvokeCrashFailure | LambderInvokePayloadTooLargeFailure | LambderInvokeEnvelopeFailure | LambderInvokeDeliveryFailure;
92
- export type LambderInvokeOutcome<T> = {
95
+ export type LambderInvokeFailure<TMessage extends LambderUncheckedRefusalMessage = LambderUncheckedRefusalMessage> = LambderInvokeValidationFailure<TMessage> | LambderInvokeCrashFailure<TMessage> | LambderInvokePayloadTooLargeFailure<TMessage> | LambderInvokeEnvelopeFailure<TMessage> | LambderInvokeDeliveryFailure<TMessage>;
96
+ /**
97
+ * An invoke's result: the callee handler's output on success (never null,
98
+ * an output being an object or an array), or a failure whose refusal
99
+ * narrows on the codes the endpoint declares.
100
+ */
101
+ export type LambderInvokeOutcome<T, TMessage extends LambderUncheckedRefusalMessage = LambderUncheckedRefusalMessage> = {
93
102
  ok: true;
94
103
  payload: T;
95
- response: LambderApiEnvelopeBody<T>;
104
+ response: LambderApiSuccessEnvelope<T>;
96
105
  logList: unknown[];
97
106
  /**
98
107
  * The answer's Set-Cookie values. A caller carrying a user's session
@@ -102,14 +111,14 @@ export type LambderInvokeOutcome<T> = {
102
111
  * tokens themselves.
103
112
  */
104
113
  cookies: string[];
105
- } | LambderInvokeFailure;
114
+ } | LambderInvokeFailure<TMessage>;
106
115
  export type LambderInvokeErrorInit = {
107
116
  message: string;
108
117
  reason: LambderInvokeFailureReason;
109
118
  apiName: string;
110
119
  functionName: string;
111
120
  status?: number;
112
- errorMessage?: LambderAppRefusalMessage;
121
+ refusal?: LambderUncheckedRefusalMessage;
113
122
  crash?: LambderCrashDetail;
114
123
  functionError?: LambderInvokeFunctionError;
115
124
  logList: unknown[];
@@ -132,7 +141,7 @@ export declare class LambderInvokeError extends Error {
132
141
  readonly apiName: string;
133
142
  readonly functionName: string;
134
143
  readonly status?: number;
135
- readonly errorMessage?: LambderAppRefusalMessage;
144
+ readonly refusal?: LambderUncheckedRefusalMessage;
136
145
  readonly crash?: LambderCrashDetail;
137
146
  readonly functionError?: LambderInvokeFunctionError;
138
147
  readonly logList: unknown[];
@@ -25,7 +25,7 @@ export class LambderInvokeError extends Error {
25
25
  apiName;
26
26
  functionName;
27
27
  status;
28
- errorMessage;
28
+ refusal;
29
29
  crash;
30
30
  functionError;
31
31
  logList;
@@ -41,7 +41,7 @@ export class LambderInvokeError extends Error {
41
41
  this.apiName = init.apiName;
42
42
  this.functionName = init.functionName;
43
43
  this.status = init.status;
44
- this.errorMessage = init.errorMessage;
44
+ this.refusal = init.refusal;
45
45
  this.crash = init.crash;
46
46
  this.functionError = init.functionError;
47
47
  this.logList = init.logList;
@@ -100,8 +100,8 @@ export const describeFailure = (init) => {
100
100
  return init.crash.message;
101
101
  if (init.functionError)
102
102
  return `${init.functionError.errorType ?? "FunctionError"}: ${init.functionError.errorMessage ?? "the function failed"}`;
103
- if (init.errorMessage !== undefined)
104
- return init.errorMessage.content;
103
+ if (init.refusal !== undefined)
104
+ return init.refusal.content;
105
105
  if (init.reason === 'validation')
106
106
  return "the callee rejected the input";
107
107
  if (init.reason === 'versionExpired')
@@ -2,6 +2,8 @@ import type { z } from "zod";
2
2
  import type { LambderApiContractShape } from "../shared/wire/LambderApiContract.js";
3
3
  import { type LambderApiRequest } from "../api/LambderApiRequest.js";
4
4
  import { type LambderApiAnswer } from "../api/LambderApiAnswer.js";
5
+ import { type LambderDeclaredRefuse, type LambderRefusalMessage } from "../shared/wire/LambderApiRefusal.js";
6
+ import { type LambderDeclaredVocabulary, type LambderHandlerRefusalsOf, type LambderRefusalVocabulary, type LambderRefusalVocabularyChecks } from "../api/LambderApiRefusals.js";
5
7
  import { type LambderApiTransport, type LambderApiTransportRequest } from "../shared/transport/LambderApiTransport.js";
6
8
  import { LambderCookieJar } from "../shared/transport/LambderCookieJar.js";
7
9
  import { type LambderApiGuard, type LambderGuardBuilder } from "../api/LambderApiGuards.js";
@@ -11,7 +13,7 @@ import { LambderMemorySessionStore } from "../stores/LambderMemorySessionStore.j
11
13
  import LambderSessionManager, { type LambderCreatedSession } from "../session/LambderSessionManager.js";
12
14
  import type { LambderMockAppOptions, LambderMockIdempotencyOptions, LambderMockTransport, LambderMockTransportOptions } from "./LambderMockCreateOptions.js";
13
15
  import type { LambderApiOptionEntry, LambderGuardDeclarationEntry } from "../shared/wire/LambderApiOptionEntries.js";
14
- import type { LambderMockCallContext, LambderMockCallRecord, LambderMockEntry, LambderMockEntryInput, LambderMockFailure, LambderMockFailureReason, LambderMockHandler, LambderMockLatency, LambderMockListener, LambderMockPublicNames, LambderMockRateLimitPolicies, LambderMockRegistryCheck, LambderMockRestEntry, LambderMockSessionCallContext, LambderMockSessionNames, LambderMockSlice, LambderMockOverride } from "./LambderMockTypes.js";
16
+ import type { LambderMockCallContext, LambderMockCallRecord, LambderMockEntry, LambderMockEntryInput, LambderMockFailure, LambderMockFailureReason, LambderMockHandler, LambderMockLatency, LambderMockListener, LambderMockPublicNames, LambderMockRateLimitPolicies, LambderMockRegistryCheck, LambderMockRestEntry, LambderMockSessionCallContext, LambderMockSessionNames, LambderMockSlice, LambderMockOverride, LambderMockRefusalsOf } from "./LambderMockTypes.js";
15
17
  /**
16
18
  * The mock runtime: the API core (LambderApiPipeline, the same class the
17
19
  * Lambda server runs) over memory stores, with typed mock handlers and mock
@@ -26,8 +28,10 @@ import type { LambderMockCallContext, LambderMockCallRecord, LambderMockEntry, L
26
28
  * which fixes the contract and session types first so everything else is
27
29
  * inferred from the options. `TDerived` is true for a mock created with the
28
30
  * generated `apiOptions` table, whose entries are their handlers alone.
31
+ * `TVocabulary` and `TCodesRequired` are the refusal vocabulary the init
32
+ * declared (declareRefusals), which types every handler's ctx.refuse.
29
33
  */
30
- export declare class LambderMockApp<C extends LambderApiContractShape, S = any, G extends Record<string, LambderApiGuard<any, any, any>> = {}, TDerived extends boolean = false> {
34
+ export declare class LambderMockApp<C extends LambderApiContractShape, S = any, G extends Record<string, LambderApiGuard<any, any, any>> = {}, TDerived extends boolean = false, TVocabulary extends LambderRefusalVocabulary = never, TCodesRequired extends boolean = false> {
31
35
  readonly apiVersion: string | null;
32
36
  /** The memory stores, for assertions and reset; null for a subsystem that is off or backed by a store of yours. */
33
37
  readonly sessionStore: LambderMemorySessionStore<S> | null;
@@ -65,9 +69,17 @@ export declare class LambderMockApp<C extends LambderApiContractShape, S = any,
65
69
  private readonly registry;
66
70
  /** The server's declared options per API, when create() was given the generated table; the entries' declarations come from here. */
67
71
  private readonly apiOptions;
72
+ /** The server's guard declarations, when create() was given the generated table: the refusal codes a declared guard adds to an entry. */
73
+ private readonly guardDeclarations;
74
+ /** The server's refusal vocabulary, when create() was given the generated table: whether each code an entry may refuse with carries data. */
75
+ private readonly refusalVocabulary;
76
+ /** Whether every refusal an entry answers with has to name a code, as the server's declareRefusals() requireCodes. */
77
+ private readonly requireRefusalCodes;
68
78
  /** The jars the runtime owns and what it planted in document.cookie (see LambderMockBrowserCookies). */
69
79
  private readonly browserCookies;
70
- constructor(options: LambderMockAppOptions<C, S, G, LambderMockRateLimitPolicies<S>, boolean | LambderMockIdempotencyOptions<S>, {}, Record<string, LambderApiOptionEntry> | undefined>);
80
+ constructor(options: LambderMockAppOptions<C, S, G, LambderMockRateLimitPolicies<S>, boolean | LambderMockIdempotencyOptions<S>, {}, Record<string, LambderApiOptionEntry> | undefined>,
81
+ /** The vocabulary the init declared, or null: what initLambderMock().declareRefusals() passes on. */
82
+ declared?: LambderDeclaredVocabulary<TVocabulary, TCodesRequired> | null);
71
83
  /**
72
84
  * The registration-time checks every entry goes through, mocked or not:
73
85
  * the ones the server runs on a definition, and the mock's own "a session
@@ -87,13 +99,23 @@ export declare class LambderMockApp<C extends LambderApiContractShape, S = any,
87
99
  * the compiler did not see.
88
100
  */
89
101
  private declaredOptionsOf;
102
+ /**
103
+ * The codes an entry may refuse with, read off the generated tables (its
104
+ * own from apiOptions, its guards' from guardDeclarations), each resolved
105
+ * through the vocabulary the init declared, exactly as the server
106
+ * resolves its own: schema, status and flag. An entry that declares
107
+ * guards needs the guardDeclarations table, and one with any code needs
108
+ * the vocabulary; a code the tables name that the vocabulary lacks means
109
+ * the two describe different servers.
110
+ */
111
+ private declaredRefusalsOf;
90
112
  /** The mode the apiOptions table gives a name, or null without a table or for a name it does not hold. */
91
113
  private declaredModeOf;
92
114
  private buildEntry;
93
115
  /** A mock for a public endpoint: a handler, or the handler with the endpoint's declarations restated. */
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>;
116
+ publicApi<K extends LambderMockPublicNames<C>, TInputSchema extends z.ZodType = z.ZodType>(name: K, entry: LambderMockEntryInput<C, K, S, G, TInputSchema, TDerived, TVocabulary, TCodesRequired>): LambderMockEntry<C, K>;
95
117
  /** A mock for a session endpoint: the pipeline fetches the session before the handler runs, and refuses without one. */
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>;
118
+ sessionApi<K extends LambderMockSessionNames<C>, TInputSchema extends z.ZodType = z.ZodType>(name: K, entry: LambderMockEntryInput<C, K, S, G, TInputSchema, TDerived, TVocabulary, TCodesRequired>): LambderMockEntry<C, K>;
97
119
  /**
98
120
  * A public endpoint deliberately left without a mock; a call answers the
99
121
  * notMocked refusal carrying the reason.
@@ -162,7 +184,7 @@ export declare class LambderMockApp<C extends LambderApiContractShape, S = any,
162
184
  * an override one `it` scoped cannot drop the one a describe put in place
163
185
  * around it.
164
186
  */
165
- override<K extends keyof C & string>(name: K, handler: LambderMockHandler<C, K, S, G>): LambderMockOverride;
187
+ override<K extends keyof C & string>(name: K, handler: LambderMockHandler<C, K, S, G, TVocabulary, TCodesRequired>): LambderMockOverride;
166
188
  /** Puts every overridden handler back, however deeply they were stacked. */
167
189
  restoreOverrides(): void;
168
190
  /** The registered endpoint names. */
@@ -188,10 +210,10 @@ export declare class LambderMockApp<C extends LambderApiContractShape, S = any,
188
210
  * restore, and for a session endpoint the session read).
189
211
  */
190
212
  private restNotMockedEntry;
191
- /** The next call to the endpoint fails this way; several calls queue in order. */
192
- failNext(apiName: keyof C & string, failure: LambderMockFailure | LambderMockFailureReason): void;
213
+ /** The next call to the endpoint fails this way; several calls queue in order. An injected refusal names one of the endpoint's declared codes, its data as a handler raises it (see LambderMockRefusalsOf), and is checked and sent as a real one is. */
214
+ failNext<K extends keyof C & string>(apiName: K, failure: LambderMockFailure<LambderRefusalMessage<LambderMockRefusalsOf<C, K, TVocabulary>>> | LambderMockFailureReason): void;
193
215
  /** Every call to the endpoint fails this way until cleared with null. */
194
- setFailure(apiName: keyof C & string, failure: LambderMockFailure | LambderMockFailureReason | null): void;
216
+ setFailure<K extends keyof C & string>(apiName: K, failure: LambderMockFailure<LambderRefusalMessage<LambderMockRefusalsOf<C, K, TVocabulary>>> | LambderMockFailureReason | null): void;
195
217
  /** Every call rejects at the transport, as with no network at all. */
196
218
  setOffline(offline: boolean): void;
197
219
  setLatency(latency: LambderMockLatency): void;
@@ -313,22 +335,55 @@ export declare class LambderMockApp<C extends LambderApiContractShape, S = any,
313
335
  }, options?: LambderMockTransportOptions): LambderMockTransport;
314
336
  }
315
337
  /**
316
- * Fixes the contract and session data types, then hands out the guard
317
- * builder bound to the mock's contexts and the create() that infers
318
- * everything else (the guard map, the rate-limit policies) from its options.
319
- * Curried for the same reason initLambder is: TypeScript type arguments are
320
- * all-or-nothing per call.
321
- *
322
- * ```ts
323
- * const mock = initLambderMock<ApiContractType, SessionData>();
324
- * const mockApp = mock.create({
325
- * apiVersion: "1.4.0",
326
- * sessions: true,
327
- * guards: { tenant: mock.guard({ guardInput: z.object({ tenantId: z.uuid() }), session: true, handler: ... }) },
328
- * });
329
- * ```
338
+ * The mock's entry point: fixes the contract and session types, and hands
339
+ * out the builders and create() that share them. `declareRefusals()` binds
340
+ * the server's refusal vocabulary too, the same object the server's init
341
+ * declares, so a mock handler's ctx.refuse takes a code's data in the
342
+ * schema's input form and the mock parses it exactly as the server does.
330
343
  */
331
344
  export declare const initLambderMock: <C extends LambderApiContractShape, S = any>() => {
345
+ /**
346
+ * Declares the server's refusal vocabulary on the mock: the same object,
347
+ * and the same `requireCodes`, the server's init declares, imported from
348
+ * shared code (codes, zod schemas, statuses and flags hold nothing
349
+ * secret). Given, every refusal an entry answers with is checked and sent
350
+ * as the server would send it: the code among the codes the tables give
351
+ * the entry, its data parsed through the code's schema from the input
352
+ * form, and the declaration's status and flag. An entry whose tables name
353
+ * a code needs it.
354
+ */
355
+ declareRefusals<const TVocabulary extends LambderRefusalVocabulary, const TRequireCodes extends boolean = false>(refusals: TVocabulary & LambderRefusalVocabularyChecks<TVocabulary>, options?: {
356
+ requireCodes?: TRequireCodes;
357
+ }): {
358
+ /** Builds a mock guard: the server guard's shape, the handler seeing the mock's contexts. */
359
+ guard: LambderGuardBuilder<LambderMockCallContext<S>, LambderMockSessionCallContext<S>>;
360
+ /**
361
+ * Builds a mock rate-limit key, the counterpart of `guard`. Bound to the
362
+ * mock's own call context, because the server's lambderRateLimitKey() is
363
+ * bound to the render context and a handler written with it compiles here
364
+ * while reading fields the mock context does not have.
365
+ */
366
+ rateLimitKey: import("../api/LambderApiRateLimits.js").LambderRateLimitKeyBuilder<LambderMockCallContext<S>>;
367
+ /**
368
+ * refuse() typed to the vocabulary the mock declared, for a mock guard or
369
+ * a helper of the mock with no endpoint in hand: `code` is one of its
370
+ * codes and `data` the schema's input form, parsed as the server parses
371
+ * it. The free refuse() where the mock declared none.
372
+ */
373
+ refuse: [TVocabulary] extends [never] ? (content: string, options?: import("../shared/wire/LambderApiRefusal.js").LambderRefuseOptions) => never : LambderDeclaredRefuse<LambderHandlerRefusalsOf<TVocabulary, keyof TVocabulary & string>, TRequireCodes>;
374
+ /**
375
+ * The mock app, with the guard map and the rate-limit policies inferred
376
+ * from the options.
377
+ *
378
+ * `const` on each of them pins a restatement to the contract, at a cost:
379
+ * inferring a generic from an object literal switches excess-property
380
+ * checking off for the whole literal, nested objects included, so a typo
381
+ * inside `rateLimits.policies` or `idempotency` would compile and be
382
+ * dropped. `I` exists for the same reason `P` does, and
383
+ * LambderMockSurplusKeys puts the error back on the key.
384
+ */
385
+ 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, TVocabulary, TRequireCodes>;
386
+ };
332
387
  /** Builds a mock guard: the server guard's shape, the handler seeing the mock's contexts. */
333
388
  guard: LambderGuardBuilder<LambderMockCallContext<S>, LambderMockSessionCallContext<S>>;
334
389
  /**
@@ -338,6 +393,13 @@ export declare const initLambderMock: <C extends LambderApiContractShape, S = an
338
393
  * while reading fields the mock context does not have.
339
394
  */
340
395
  rateLimitKey: import("../api/LambderApiRateLimits.js").LambderRateLimitKeyBuilder<LambderMockCallContext<S>>;
396
+ /**
397
+ * refuse() typed to the vocabulary the mock declared, for a mock guard or
398
+ * a helper of the mock with no endpoint in hand: `code` is one of its
399
+ * codes and `data` the schema's input form, parsed as the server parses
400
+ * it. The free refuse() where the mock declared none.
401
+ */
402
+ refuse: (content: string, options?: import("../shared/wire/LambderApiRefusal.js").LambderRefuseOptions) => never;
341
403
  /**
342
404
  * The mock app, with the guard map and the rate-limit policies inferred
343
405
  * from the options.
@@ -349,5 +411,5 @@ export declare const initLambderMock: <C extends LambderApiContractShape, S = an
349
411
  * dropped. `I` exists for the same reason `P` does, and
350
412
  * LambderMockSurplusKeys puts the error back on the key.
351
413
  */
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>;
414
+ 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, never, false>;
353
415
  };