aws-lambda-devkit 0.1.0

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 (183) hide show
  1. package/CHANGELOG.md +72 -0
  2. package/LICENSE +21 -0
  3. package/README.md +214 -0
  4. package/dist/aws/batch-response.d.ts +8 -0
  5. package/dist/aws/batch-response.d.ts.map +1 -0
  6. package/dist/aws/batch-response.js +12 -0
  7. package/dist/aws/batch-response.js.map +1 -0
  8. package/dist/aws/clients.d.ts +6 -0
  9. package/dist/aws/clients.d.ts.map +1 -0
  10. package/dist/aws/clients.js +30 -0
  11. package/dist/aws/clients.js.map +1 -0
  12. package/dist/aws/sns-publish.d.ts +10 -0
  13. package/dist/aws/sns-publish.d.ts.map +1 -0
  14. package/dist/aws/sns-publish.js +17 -0
  15. package/dist/aws/sns-publish.js.map +1 -0
  16. package/dist/aws/sqs-listen.d.ts +17 -0
  17. package/dist/aws/sqs-listen.d.ts.map +1 -0
  18. package/dist/aws/sqs-listen.js +113 -0
  19. package/dist/aws/sqs-listen.js.map +1 -0
  20. package/dist/aws/sqs-process.d.ts +39 -0
  21. package/dist/aws/sqs-process.d.ts.map +1 -0
  22. package/dist/aws/sqs-process.js +95 -0
  23. package/dist/aws/sqs-process.js.map +1 -0
  24. package/dist/aws/sqs-send.d.ts +15 -0
  25. package/dist/aws/sqs-send.d.ts.map +1 -0
  26. package/dist/aws/sqs-send.js +27 -0
  27. package/dist/aws/sqs-send.js.map +1 -0
  28. package/dist/aws/sqs-visibility.d.ts +10 -0
  29. package/dist/aws/sqs-visibility.d.ts.map +1 -0
  30. package/dist/aws/sqs-visibility.js +29 -0
  31. package/dist/aws/sqs-visibility.js.map +1 -0
  32. package/dist/cli.d.ts +3 -0
  33. package/dist/cli.d.ts.map +1 -0
  34. package/dist/cli.js +187 -0
  35. package/dist/cli.js.map +1 -0
  36. package/dist/commands/config-cmd.d.ts +5 -0
  37. package/dist/commands/config-cmd.d.ts.map +1 -0
  38. package/dist/commands/config-cmd.js +8 -0
  39. package/dist/commands/config-cmd.js.map +1 -0
  40. package/dist/commands/config.d.ts +2 -0
  41. package/dist/commands/config.d.ts.map +1 -0
  42. package/dist/commands/config.js +24 -0
  43. package/dist/commands/config.js.map +1 -0
  44. package/dist/commands/init.d.ts +7 -0
  45. package/dist/commands/init.d.ts.map +1 -0
  46. package/dist/commands/init.js +59 -0
  47. package/dist/commands/init.js.map +1 -0
  48. package/dist/commands/list.d.ts +4 -0
  49. package/dist/commands/list.d.ts.map +1 -0
  50. package/dist/commands/list.js +23 -0
  51. package/dist/commands/list.js.map +1 -0
  52. package/dist/commands/listen.d.ts +20 -0
  53. package/dist/commands/listen.d.ts.map +1 -0
  54. package/dist/commands/listen.js +36 -0
  55. package/dist/commands/listen.js.map +1 -0
  56. package/dist/commands/send.d.ts +17 -0
  57. package/dist/commands/send.d.ts.map +1 -0
  58. package/dist/commands/send.js +51 -0
  59. package/dist/commands/send.js.map +1 -0
  60. package/dist/commands/test.d.ts +23 -0
  61. package/dist/commands/test.d.ts.map +1 -0
  62. package/dist/commands/test.js +123 -0
  63. package/dist/commands/test.js.map +1 -0
  64. package/dist/config/env.d.ts +8 -0
  65. package/dist/config/env.d.ts.map +1 -0
  66. package/dist/config/env.js +16 -0
  67. package/dist/config/env.js.map +1 -0
  68. package/dist/config/load.d.ts +9 -0
  69. package/dist/config/load.d.ts.map +1 -0
  70. package/dist/config/load.js +72 -0
  71. package/dist/config/load.js.map +1 -0
  72. package/dist/config/merge.d.ts +36 -0
  73. package/dist/config/merge.d.ts.map +1 -0
  74. package/dist/config/merge.js +89 -0
  75. package/dist/config/merge.js.map +1 -0
  76. package/dist/config/project-env.d.ts +34 -0
  77. package/dist/config/project-env.d.ts.map +1 -0
  78. package/dist/config/project-env.js +90 -0
  79. package/dist/config/project-env.js.map +1 -0
  80. package/dist/config/schema.d.ts +27 -0
  81. package/dist/config/schema.d.ts.map +1 -0
  82. package/dist/config/schema.js +123 -0
  83. package/dist/config/schema.js.map +1 -0
  84. package/dist/config/types.d.ts +167 -0
  85. package/dist/config/types.d.ts.map +1 -0
  86. package/dist/config/types.js +2 -0
  87. package/dist/config/types.js.map +1 -0
  88. package/dist/events/apigw.d.ts +41 -0
  89. package/dist/events/apigw.d.ts.map +1 -0
  90. package/dist/events/apigw.js +43 -0
  91. package/dist/events/apigw.js.map +1 -0
  92. package/dist/events/eventbridge.d.ts +16 -0
  93. package/dist/events/eventbridge.d.ts.map +1 -0
  94. package/dist/events/eventbridge.js +19 -0
  95. package/dist/events/eventbridge.js.map +1 -0
  96. package/dist/events/index.d.ts +17 -0
  97. package/dist/events/index.d.ts.map +1 -0
  98. package/dist/events/index.js +50 -0
  99. package/dist/events/index.js.map +1 -0
  100. package/dist/events/s3.d.ts +45 -0
  101. package/dist/events/s3.d.ts.map +1 -0
  102. package/dist/events/s3.js +44 -0
  103. package/dist/events/s3.js.map +1 -0
  104. package/dist/events/schedule.d.ts +12 -0
  105. package/dist/events/schedule.d.ts.map +1 -0
  106. package/dist/events/schedule.js +15 -0
  107. package/dist/events/schedule.js.map +1 -0
  108. package/dist/events/sns.d.ts +24 -0
  109. package/dist/events/sns.d.ts.map +1 -0
  110. package/dist/events/sns.js +31 -0
  111. package/dist/events/sns.js.map +1 -0
  112. package/dist/events/sqs-record.d.ts +44 -0
  113. package/dist/events/sqs-record.d.ts.map +1 -0
  114. package/dist/events/sqs-record.js +75 -0
  115. package/dist/events/sqs-record.js.map +1 -0
  116. package/dist/events/sqs.d.ts +21 -0
  117. package/dist/events/sqs.d.ts.map +1 -0
  118. package/dist/events/sqs.js +28 -0
  119. package/dist/events/sqs.js.map +1 -0
  120. package/dist/events/util.d.ts +2 -0
  121. package/dist/events/util.d.ts.map +1 -0
  122. package/dist/events/util.js +7 -0
  123. package/dist/events/util.js.map +1 -0
  124. package/dist/index.d.ts +15 -0
  125. package/dist/index.d.ts.map +1 -0
  126. package/dist/index.js +14 -0
  127. package/dist/index.js.map +1 -0
  128. package/dist/peer-resolve.d.ts +5 -0
  129. package/dist/peer-resolve.d.ts.map +1 -0
  130. package/dist/peer-resolve.js +44 -0
  131. package/dist/peer-resolve.js.map +1 -0
  132. package/dist/runtime/asset-links.d.ts +15 -0
  133. package/dist/runtime/asset-links.d.ts.map +1 -0
  134. package/dist/runtime/asset-links.js +42 -0
  135. package/dist/runtime/asset-links.js.map +1 -0
  136. package/dist/runtime/clear-caches.d.ts +5 -0
  137. package/dist/runtime/clear-caches.d.ts.map +1 -0
  138. package/dist/runtime/clear-caches.js +8 -0
  139. package/dist/runtime/clear-caches.js.map +1 -0
  140. package/dist/runtime/context.d.ts +10 -0
  141. package/dist/runtime/context.d.ts.map +1 -0
  142. package/dist/runtime/context.js +42 -0
  143. package/dist/runtime/context.js.map +1 -0
  144. package/dist/runtime/handler-cache.d.ts +6 -0
  145. package/dist/runtime/handler-cache.d.ts.map +1 -0
  146. package/dist/runtime/handler-cache.js +14 -0
  147. package/dist/runtime/handler-cache.js.map +1 -0
  148. package/dist/runtime/invoke.d.ts +32 -0
  149. package/dist/runtime/invoke.d.ts.map +1 -0
  150. package/dist/runtime/invoke.js +111 -0
  151. package/dist/runtime/invoke.js.map +1 -0
  152. package/dist/runtime/loader.d.ts +17 -0
  153. package/dist/runtime/loader.d.ts.map +1 -0
  154. package/dist/runtime/loader.js +76 -0
  155. package/dist/runtime/loader.js.map +1 -0
  156. package/dist/runtime/logs.d.ts +19 -0
  157. package/dist/runtime/logs.d.ts.map +1 -0
  158. package/dist/runtime/logs.js +125 -0
  159. package/dist/runtime/logs.js.map +1 -0
  160. package/dist/util/payload.d.ts +13 -0
  161. package/dist/util/payload.d.ts.map +1 -0
  162. package/dist/util/payload.js +40 -0
  163. package/dist/util/payload.js.map +1 -0
  164. package/dist/util/tsx-register.d.ts +4 -0
  165. package/dist/util/tsx-register.d.ts.map +1 -0
  166. package/dist/util/tsx-register.js +27 -0
  167. package/dist/util/tsx-register.js.map +1 -0
  168. package/dist/version.d.ts +2 -0
  169. package/dist/version.d.ts.map +1 -0
  170. package/dist/version.js +2 -0
  171. package/dist/version.js.map +1 -0
  172. package/docs/README.md +23 -0
  173. package/docs/commands.md +294 -0
  174. package/docs/configuration.md +652 -0
  175. package/docs/getting-started.md +400 -0
  176. package/docs/recipes.md +743 -0
  177. package/docs/troubleshooting.md +393 -0
  178. package/package.json +76 -0
  179. package/templates/.env.example +5 -0
  180. package/templates/.vscode/launch.json +14 -0
  181. package/templates/events/sample.json +4 -0
  182. package/templates/lamkit.config.js +38 -0
  183. package/templates/lamkit.config.ts +27 -0
@@ -0,0 +1,743 @@
1
+ # Recipes
2
+
3
+ Copy-paste **end-to-end** setups. Each recipe includes folder layout, config, handler snippets, commands, and what to expect.
4
+
5
+ Replace generic names (`worker`, `WORKER_QUEUE_URL`, etc.) with your own.
6
+
7
+ **Index**
8
+
9
+ | # | Scenario |
10
+ |---|----------|
11
+ | [1](#recipe-1--single-sqs-worker-javascript) | Single SQS worker, plain JavaScript |
12
+ | [2](#recipe-2--compiled-typescript-dist) | Compiled TypeScript (`dist/`) |
13
+ | [3](#recipe-3--typescript-with-tsx-no-build) | TypeScript live via `tsx` |
14
+ | [4](#recipe-4--default-payload-in-config) | Default payload in config |
15
+ | [5](#recipe-5--payload-from-a-file) | Payload from JSON file |
16
+ | [6](#recipe-6--raw-captured-aws-event) | Raw captured AWS event |
17
+ | [7](#recipe-7--sqs-batch-multiple-records) | SQS batch (multiple records) |
18
+ | [8](#recipe-8--http-api-gateway-handler) | HTTP / API Gateway handler |
19
+ | [9](#recipe-9--sns-trigger-simulated) | SNS trigger (simulated) |
20
+ | [10](#recipe-10--monorepo-shared-root-env) | Monorepo + shared root `.env` |
21
+ | [11](#recipe-11--asset-links-for-contractsjson) | Asset links for `contracts/*.json` |
22
+ | [12](#recipe-12--real-sqs-send--listen-loop) | Real SQS send + listen loop |
23
+ | [13](#recipe-13--fifo-queue) | FIFO queue |
24
+ | [14](#recipe-14--sns-publish-real-aws) | SNS publish (real AWS) |
25
+ | [15](#recipe-15--json-structured-logs) | JSON structured logs |
26
+ | [16](#recipe-16--debug-with-breakpoints) | Debug with breakpoints |
27
+ | [17](#recipe-17--ci-smoke-test) | CI smoke test (no AWS) |
28
+ | [18](#recipe-18--sqs-partial-batch-failures) | SQS partial batch failures |
29
+ | [19](#recipe-19--multiple-functions) | Multiple functions |
30
+ | [20](#recipe-20--custom-aws-endpoint) | Custom AWS-compatible endpoint |
31
+ | [21](#recipe-21--per-invoke-env-overrides) | Per-invoke env overrides |
32
+ | [22](#recipe-22--reload-after-code-change) | Reload after code change |
33
+
34
+ ---
35
+
36
+ ## Recipe 1 — Single SQS worker (JavaScript)
37
+
38
+ **Goal:** Fastest possible setup — no TypeScript, no build step.
39
+
40
+ **Layout:**
41
+
42
+ ```
43
+ order-worker/
44
+ ├── package.json
45
+ ├── lamkit.config.js
46
+ ├── .env
47
+ └── src/
48
+ └── handler.js
49
+ ```
50
+
51
+ **`package.json`:**
52
+
53
+ ```json
54
+ {
55
+ "name": "order-worker",
56
+ "type": "module",
57
+ "scripts": {
58
+ "test:lambda": "lamkit test"
59
+ },
60
+ "devDependencies": {
61
+ "aws-lambda-devkit": "^0.1.0"
62
+ }
63
+ }
64
+ ```
65
+
66
+ **`src/handler.js`:**
67
+
68
+ ```js
69
+ export const handler = async (event) => {
70
+ for (const record of event.Records) {
71
+ const order = JSON.parse(record.body);
72
+ console.log('Processing order', order.orderId);
73
+ if (!order.orderId) throw new Error('orderId required');
74
+ }
75
+ };
76
+ ```
77
+
78
+ **`lamkit.config.js`:**
79
+
80
+ ```js
81
+ export default {
82
+ functions: [
83
+ {
84
+ name: 'worker',
85
+ entry: './src/handler.js',
86
+ trigger: 'sqs',
87
+ test: { data: { orderId: 'ord_local_1', amount: 10 } },
88
+ },
89
+ ],
90
+ };
91
+ ```
92
+
93
+ **Commands:**
94
+
95
+ ```bash
96
+ npm install
97
+ npx lamkit init # optional if you already have config
98
+ npx lamkit test
99
+ npx lamkit test --data '{"orderId":"ord_999","amount":50}'
100
+ ```
101
+
102
+ **Expect:** `✓ worker (sqs)` and logs showing your `orderId`.
103
+
104
+ ---
105
+
106
+ ## Recipe 2 — Compiled TypeScript (`dist/`)
107
+
108
+ **Goal:** Match production — deploy `dist/`, test `dist/`.
109
+
110
+ **Layout:**
111
+
112
+ ```
113
+ order-worker/
114
+ ├── lamkit.config.js
115
+ ├── src/handler.ts
116
+ ├── dist/handler.js ← npm run build
117
+ └── tsconfig.json
118
+ ```
119
+
120
+ **`src/handler.ts`:**
121
+
122
+ ```ts
123
+ import type { SQSEvent, SQSHandler } from 'aws-lambda';
124
+
125
+ export const handler: SQSHandler = async (event: SQSEvent) => {
126
+ for (const record of event.Records) {
127
+ const body = JSON.parse(record.body);
128
+ console.log('Typed handler:', body);
129
+ }
130
+ };
131
+ ```
132
+
133
+ **`lamkit.config.js`:**
134
+
135
+ ```js
136
+ export default {
137
+ functions: [
138
+ {
139
+ name: 'worker',
140
+ entry: './dist/handler.js',
141
+ trigger: 'sqs',
142
+ },
143
+ ],
144
+ };
145
+ ```
146
+
147
+ **Commands:**
148
+
149
+ ```bash
150
+ npm run build
151
+ npx lamkit test --data '{"orderId":"1"}'
152
+ ```
153
+
154
+ **Tip:** Add `"test:lambda": "npm run build && lamkit test"` to run build before every test.
155
+
156
+ ---
157
+
158
+ ## Recipe 3 — TypeScript with `tsx` (no build)
159
+
160
+ **Goal:** Iterate on `.ts` without compiling each time.
161
+
162
+ ```bash
163
+ npm install -D aws-lambda-devkit tsx
164
+ ```
165
+
166
+ **`lamkit.config.js`:**
167
+
168
+ ```js
169
+ export default {
170
+ functions: [
171
+ {
172
+ name: 'worker',
173
+ entry: './src/handler.ts',
174
+ trigger: 'sqs',
175
+ },
176
+ ],
177
+ };
178
+ ```
179
+
180
+ ```bash
181
+ npx lamkit test --data '{"id":"1"}'
182
+ ```
183
+
184
+ Lamkit uses the `tsx` peer to import `.ts` directly when `dist/` is missing.
185
+
186
+ ---
187
+
188
+ ## Recipe 4 — Default payload in config
189
+
190
+ **Goal:** Run `lamkit test` with no CLI arguments.
191
+
192
+ ```js
193
+ export default {
194
+ functions: [
195
+ {
196
+ name: 'worker',
197
+ entry: './src/handler.js',
198
+ trigger: 'sqs',
199
+ test: {
200
+ data: {
201
+ type: 'PING',
202
+ timestamp: '2026-01-15T12:00:00Z',
203
+ },
204
+ },
205
+ },
206
+ ],
207
+ };
208
+ ```
209
+
210
+ ```bash
211
+ npx lamkit test
212
+ ```
213
+
214
+ CLI `--data` still overrides config when provided.
215
+
216
+ ---
217
+
218
+ ## Recipe 5 — Payload from a file
219
+
220
+ **`events/order-created.json`:**
221
+
222
+ ```json
223
+ {
224
+ "orderId": "ord_123",
225
+ "customerId": "cust_456",
226
+ "lines": [
227
+ { "sku": "WIDGET-A", "quantity": 2, "price": 19.99 }
228
+ ]
229
+ }
230
+ ```
231
+
232
+ ```bash
233
+ npx lamkit test --data-file events/order-created.json
234
+ npx lamkit test --data @events/order-created.json
235
+ ```
236
+
237
+ Good for large payloads you do not want inline in the shell.
238
+
239
+ ---
240
+
241
+ ## Recipe 6 — Raw captured AWS event
242
+
243
+ **Goal:** Test with the **exact** event shape from production.
244
+
245
+ 1. Copy an event from CloudWatch Logs or AWS documentation.
246
+ 2. Save as **`events/sqs-production-sample.json`** (full `SQSEvent` with `Records` array).
247
+ 3. Run:
248
+
249
+ ```bash
250
+ npx lamkit test --event events/sqs-production-sample.json
251
+ ```
252
+
253
+ This skips the event builder — useful when field names or attributes must match production exactly.
254
+
255
+ ---
256
+
257
+ ## Recipe 7 — SQS batch (multiple records)
258
+
259
+ **Goal:** Test batch handling and partial failure logic.
260
+
261
+ **Handler example:**
262
+
263
+ ```js
264
+ export const handler = async (event) => {
265
+ const failures = [];
266
+ for (const record of event.Records) {
267
+ try {
268
+ const body = JSON.parse(record.body);
269
+ if (body.shouldFail) throw new Error('simulated failure');
270
+ } catch {
271
+ failures.push({ itemIdentifier: record.messageId });
272
+ }
273
+ }
274
+ return { batchItemFailures: failures };
275
+ };
276
+ ```
277
+
278
+ **Commands:**
279
+
280
+ ```bash
281
+ # One payload → 5 records
282
+ npx lamkit test --data '{"id":"1"}' --batch-size 5
283
+
284
+ # Array → one record per element
285
+ npx lamkit test --data '[{"id":"1"},{"id":"2"},{"id":"3"}]'
286
+
287
+ # Fail CI if batchItemFailures returned
288
+ npx lamkit test --strict-batch --data '[{"shouldFail":true}]'
289
+ ```
290
+
291
+ ---
292
+
293
+ ## Recipe 8 — HTTP (API Gateway) handler
294
+
295
+ **`src/http.js`:**
296
+
297
+ ```js
298
+ export const handler = async (event) => {
299
+ const body = event.body ? JSON.parse(event.body) : {};
300
+ return {
301
+ statusCode: 200,
302
+ headers: { 'Content-Type': 'application/json' },
303
+ body: JSON.stringify({ greeting: `Hello, ${body.name ?? 'world'}` }),
304
+ };
305
+ };
306
+ ```
307
+
308
+ **`lamkit.config.js`:**
309
+
310
+ ```js
311
+ export default {
312
+ functions: [
313
+ {
314
+ name: 'api',
315
+ entry: './src/http.js',
316
+ trigger: 'http',
317
+ test: { data: { name: 'Ada' } },
318
+ },
319
+ ],
320
+ };
321
+ ```
322
+
323
+ ```bash
324
+ npx lamkit test api
325
+ npx lamkit test api --data '{"name":"Bob"}'
326
+ ```
327
+
328
+ ---
329
+
330
+ ## Recipe 9 — SNS trigger (simulated)
331
+
332
+ **`lamkit.config.js`:**
333
+
334
+ ```js
335
+ export default {
336
+ functions: [
337
+ {
338
+ name: 'subscriber',
339
+ entry: './dist/handler.js',
340
+ trigger: 'sns',
341
+ test: { data: { metric: 'cpu', value: 95, threshold: 90 } },
342
+ },
343
+ ],
344
+ };
345
+ ```
346
+
347
+ **Handler:**
348
+
349
+ ```js
350
+ export const handler = async (event) => {
351
+ for (const record of event.Records) {
352
+ const message = JSON.parse(record.Sns.Message);
353
+ console.log('Alert:', message);
354
+ }
355
+ };
356
+ ```
357
+
358
+ ```bash
359
+ npx lamkit test subscriber
360
+ ```
361
+
362
+ ---
363
+
364
+ ## Recipe 10 — Monorepo with shared root `.env`
365
+
366
+ **Goal:** Lambda package in `packages/worker/` but secrets in repo root `.env`.
367
+
368
+ **Layout:**
369
+
370
+ ```
371
+ acme-platform/
372
+ ├── .env
373
+ └── packages/
374
+ └── order-worker/
375
+ ├── lamkit.config.mjs
376
+ ├── dist/handler.js
377
+ └── package.json
378
+ ```
379
+
380
+ **Root `.env`:**
381
+
382
+ ```env
383
+ AWS_REGION=eu-west-1
384
+ WORKER_QUEUE_URL=https://sqs.eu-west-1.amazonaws.com/111222333/acme-dev-orders
385
+ APP_DB_NAME=orders_dev
386
+ APP_DB_PORT=5432
387
+ ```
388
+
389
+ **`packages/order-worker/lamkit.config.mjs`:**
390
+
391
+ ```js
392
+ import { loadProjectEnv } from 'aws-lambda-devkit';
393
+
394
+ loadProjectEnv({
395
+ files: ['../../.env'],
396
+ skipDotenv: true,
397
+ aliases: {
398
+ APP_DB_NAME: 'DB_NAME',
399
+ APP_DB_PORT: 'DB_PORT',
400
+ },
401
+ });
402
+
403
+ const region = process.env.AWS_REGION ?? 'eu-west-1';
404
+
405
+ export default {
406
+ defaults: { aws: { region } },
407
+ functions: [
408
+ {
409
+ name: 'worker',
410
+ entry: './dist/handler.js',
411
+ trigger: 'sqs',
412
+ aws: { queueUrl: process.env.WORKER_QUEUE_URL, region },
413
+ },
414
+ ],
415
+ };
416
+ ```
417
+
418
+ ```bash
419
+ cd packages/order-worker
420
+ npm install -D aws-lambda-devkit
421
+ npx lamkit test --data '{"orderId":"1"}'
422
+ ```
423
+
424
+ ---
425
+
426
+ ## Recipe 11 — Asset links for `contracts/*.json`
427
+
428
+ **Goal:** `dist/` code does `require('../../contracts/abi.json')` but files live in `src/contracts/`.
429
+
430
+ **Layout:**
431
+
432
+ ```
433
+ worker/
434
+ ├── src/contracts/abi.json
435
+ ├── dist/handler.js
436
+ └── lamkit.config.js
437
+ ```
438
+
439
+ **`lamkit.config.js`:**
440
+
441
+ ```js
442
+ export default {
443
+ assetLinks: [{ path: 'contracts', target: 'src/contracts' }],
444
+ functions: [
445
+ { name: 'worker', entry: './dist/handler.js', trigger: 'sqs' },
446
+ ],
447
+ };
448
+ ```
449
+
450
+ Lamkit creates `contracts → src/contracts` symlink before invoke if `contracts/` is missing.
451
+
452
+ ---
453
+
454
+ ## Recipe 12 — Real SQS: send then listen locally
455
+
456
+ **Goal:** End-to-end with a real dev queue; handler runs on your machine.
457
+
458
+ **Install:**
459
+
460
+ ```bash
461
+ npm install -D @aws-sdk/client-sqs
462
+ ```
463
+
464
+ **`.env`:**
465
+
466
+ ```env
467
+ AWS_REGION=us-east-1
468
+ AWS_ACCESS_KEY_ID=AKIA...
469
+ AWS_SECRET_ACCESS_KEY=...
470
+ WORKER_QUEUE_URL=https://sqs.us-east-1.amazonaws.com/123456789012/acme-dev-orders
471
+ ```
472
+
473
+ **`lamkit.config.js`:**
474
+
475
+ ```js
476
+ export default {
477
+ defaults: { aws: { region: process.env.AWS_REGION } },
478
+ functions: [
479
+ {
480
+ name: 'worker',
481
+ entry: './dist/handler.js',
482
+ trigger: 'sqs',
483
+ aws: { queueUrl: process.env.WORKER_QUEUE_URL },
484
+ },
485
+ ],
486
+ };
487
+ ```
488
+
489
+ **Terminal 1 — listener:**
490
+
491
+ ```bash
492
+ npx lamkit listen worker
493
+ ```
494
+
495
+ **Terminal 2 — sender:**
496
+
497
+ ```bash
498
+ npx lamkit send sqs worker --data '{"orderId":"live-1"}'
499
+ ```
500
+
501
+ **Expect:** Terminal 1 prints `message <id>: success` and your handler logs.
502
+
503
+ **Safety:** Use a dev-only queue. Disable the deployed Lambda event source mapping while listening.
504
+
505
+ **One-shot:**
506
+
507
+ ```bash
508
+ npx lamkit send sqs worker --data '{"orderId":"1"}'
509
+ npx lamkit listen worker --once --expect-messages
510
+ ```
511
+
512
+ ---
513
+
514
+ ## Recipe 13 — FIFO queue
515
+
516
+ Same as Recipe 12, but `WORKER_QUEUE_URL` ends with `.fifo`:
517
+
518
+ ```env
519
+ WORKER_QUEUE_URL=https://sqs.us-east-1.amazonaws.com/123456789012/acme-orders.fifo
520
+ ```
521
+
522
+ ```bash
523
+ npx lamkit send sqs worker --data '{"orderId":"1","group":"customer-A"}'
524
+ ```
525
+
526
+ Lamkit sets `MessageGroupId` and `MessageDeduplicationId` automatically.
527
+
528
+ ---
529
+
530
+ ## Recipe 14 — SNS publish (real AWS)
531
+
532
+ ```bash
533
+ npm install -D @aws-sdk/client-sns
534
+ ```
535
+
536
+ ```env
537
+ EVENTS_TOPIC_ARN=arn:aws:sns:us-east-1:123456789012:acme-dev-events
538
+ ```
539
+
540
+ ```js
541
+ export default {
542
+ functions: [
543
+ {
544
+ name: 'notifier',
545
+ entry: './dist/handler.js',
546
+ trigger: 'sns',
547
+ aws: { topicArn: process.env.EVENTS_TOPIC_ARN },
548
+ },
549
+ ],
550
+ };
551
+ ```
552
+
553
+ ```bash
554
+ npx lamkit send sns notifier --data '{"event":"ORDER_PLACED","orderId":"1"}'
555
+ ```
556
+
557
+ To invoke the handler offline with SNS event shape, use `lamkit test` with `trigger: 'sns'`.
558
+
559
+ ---
560
+
561
+ ## Recipe 15 — JSON structured logs
562
+
563
+ ```js
564
+ export default {
565
+ defaults: { logFormat: 'json' },
566
+ functions: [
567
+ { name: 'worker', entry: './src/handler.js', trigger: 'sqs' },
568
+ ],
569
+ };
570
+ ```
571
+
572
+ ```bash
573
+ npx lamkit test --data '{"id":"1"}' | jq .
574
+ ```
575
+
576
+ Each log line is a JSON object — easier for scripts and log aggregators locally.
577
+
578
+ ---
579
+
580
+ ## Recipe 16 — Debug with breakpoints
581
+
582
+ **VS Code:** Run **"lamkit test (inspect)"** from `.vscode/launch.json` (created by `lamkit init`).
583
+
584
+ **Terminal:**
585
+
586
+ ```bash
587
+ npx lamkit test worker --inspect-brk --data '{"orderId":"1"}'
588
+ ```
589
+
590
+ Set breakpoints in your handler, attach debugger, step through code.
591
+
592
+ ---
593
+
594
+ ## Recipe 17 — CI smoke test
595
+
596
+ **Goal:** GitHub Actions / GitLab CI runs handlers without AWS credentials.
597
+
598
+ **`package.json`:**
599
+
600
+ ```json
601
+ {
602
+ "scripts": {
603
+ "test:lambda": "lamkit test --all"
604
+ }
605
+ }
606
+ ```
607
+
608
+ **Config** — each function needs `test.data` or handlers must tolerate empty events:
609
+
610
+ ```js
611
+ export default {
612
+ functions: [
613
+ {
614
+ name: 'worker',
615
+ entry: './dist/handler.js',
616
+ trigger: 'sqs',
617
+ test: { data: { type: 'CI_PING' } },
618
+ },
619
+ ],
620
+ };
621
+ ```
622
+
623
+ **CI step:**
624
+
625
+ ```yaml
626
+ - run: npm ci
627
+ - run: npm run build
628
+ - run: npm run test:lambda
629
+ ```
630
+
631
+ Handlers that call AWS/DB in CI may need mocks or `--dry-run` only.
632
+
633
+ ---
634
+
635
+ ## Recipe 18 — SQS partial batch failures
636
+
637
+ ```bash
638
+ npx lamkit test worker --strict-batch --data '[{"id":"ok"},{"id":"bad"}]'
639
+ ```
640
+
641
+ Exit code `1` if handler returns:
642
+
643
+ ```js
644
+ return {
645
+ batchItemFailures: [{ itemIdentifier: 'message-id-here' }],
646
+ };
647
+ ```
648
+
649
+ Useful in shell scripts and CI gates.
650
+
651
+ ---
652
+
653
+ ## Recipe 19 — Multiple functions
654
+
655
+ ```js
656
+ export default {
657
+ functions: [
658
+ {
659
+ name: 'orders',
660
+ entry: './dist/orders.js',
661
+ trigger: 'sqs',
662
+ test: { data: { type: 'ORDER' } },
663
+ },
664
+ {
665
+ name: 'billing',
666
+ entry: './dist/billing.js',
667
+ trigger: 'sqs',
668
+ test: { data: { type: 'INVOICE' } },
669
+ },
670
+ ],
671
+ };
672
+ ```
673
+
674
+ ```bash
675
+ npx lamkit list
676
+ npx lamkit test orders
677
+ npx lamkit test billing
678
+ npx lamkit test --all
679
+ ```
680
+
681
+ ---
682
+
683
+ ## Recipe 20 — Custom AWS-compatible endpoint
684
+
685
+ ```env
686
+ AWS_ENDPOINT_URL=https://internal-gateway.example.com
687
+ AWS_REGION=us-east-1
688
+ ```
689
+
690
+ ```js
691
+ export default {
692
+ defaults: {
693
+ aws: {
694
+ region: process.env.AWS_REGION,
695
+ endpoint: process.env.AWS_ENDPOINT_URL,
696
+ },
697
+ },
698
+ functions: [/* ... */],
699
+ };
700
+ ```
701
+
702
+ When using real IAM keys (`AKIA*`), `loadProjectEnv()` strips endpoint overrides by default so SDK hits real AWS. Set `stripCustomEndpointForRealAws: false` to keep custom endpoints with real keys.
703
+
704
+ ---
705
+
706
+ ## Recipe 21 — Per-invoke env overrides
707
+
708
+ ```bash
709
+ npx lamkit test worker \
710
+ --env FEATURE_NEW_PARSER=true \
711
+ --env LOG_LEVEL=debug \
712
+ --data '{"orderId":"1"}'
713
+ ```
714
+
715
+ Does not modify `.env` — only the current process.
716
+
717
+ ---
718
+
719
+ ## Recipe 22 — Reload after code change
720
+
721
+ During a tight edit-test loop:
722
+
723
+ ```bash
724
+ # terminal 1: edit src/handler.js
725
+ npx lamkit test worker --reload --data '{"orderId":"1"}'
726
+ ```
727
+
728
+ `--reload` bypasses the handler module cache so you see fresh code without restarting the shell.
729
+
730
+ ---
731
+
732
+ ## Picking a recipe
733
+
734
+ | Your situation | Start with |
735
+ |----------------|------------|
736
+ | Brand new project | Recipe 1 |
737
+ | TypeScript + `tsc` | Recipe 2 |
738
+ | Monorepo | Recipe 10 |
739
+ | `require('../../contracts/...')` fails | Recipe 11 |
740
+ | Test against real dev queue | Recipe 12 |
741
+ | GitHub Actions | Recipe 17 |
742
+
743
+ More detail: [Configuration](configuration.md) · [Commands](commands.md) · [Troubleshooting](troubleshooting.md)