lambda-pipe 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 (64) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +761 -0
  3. package/build/cjs/cli/index.js +1 -0
  4. package/build/cjs/index.js +1 -0
  5. package/build/cjs/jobs/index.js +1 -0
  6. package/build/cjs/logger/index.js +1 -0
  7. package/build/cjs/runtime/index.js +1 -0
  8. package/build/cjs/websocket/index.js +1 -0
  9. package/build/cli/dev.d.ts +10 -0
  10. package/build/core/cache/index.d.ts +19 -0
  11. package/build/core/context/createRequestContext.d.ts +2 -0
  12. package/build/core/context/index.d.ts +2 -0
  13. package/build/core/context/types.d.ts +52 -0
  14. package/build/core/context/utils.d.ts +6 -0
  15. package/build/core/errors/HttpError.d.ts +6 -0
  16. package/build/core/errors/handleError.d.ts +2 -0
  17. package/build/core/errors/http.d.ts +13 -0
  18. package/build/core/errors/index.d.ts +3 -0
  19. package/build/core/http/helpers.d.ts +6 -0
  20. package/build/core/http/index.d.ts +3 -0
  21. package/build/core/http/serialize.d.ts +2 -0
  22. package/build/core/http/types.d.ts +6 -0
  23. package/build/core/index.d.ts +8 -0
  24. package/build/core/lifecycle/hooks.d.ts +4 -0
  25. package/build/core/lifecycle/index.d.ts +1 -0
  26. package/build/core/middleware/index.d.ts +2 -0
  27. package/build/core/middleware/run.d.ts +4 -0
  28. package/build/core/middleware/types.d.ts +11 -0
  29. package/build/core/plugins/index.d.ts +1 -0
  30. package/build/core/plugins/types.d.ts +15 -0
  31. package/build/core/routing/defineHandler.d.ts +14 -0
  32. package/build/core/routing/defineRoute.d.ts +20 -0
  33. package/build/core/routing/execution.d.ts +4 -0
  34. package/build/core/routing/index.d.ts +3 -0
  35. package/build/core/routing/types.d.ts +55 -0
  36. package/build/core/validation/index.d.ts +2 -0
  37. package/build/core/validation/types.d.ts +8 -0
  38. package/build/core/validation/validate.d.ts +2 -0
  39. package/build/esm/cli/index.js +1 -0
  40. package/build/esm/index.js +1 -0
  41. package/build/esm/jobs/index.js +1 -0
  42. package/build/esm/logger/index.js +1 -0
  43. package/build/esm/runtime/index.js +1 -0
  44. package/build/esm/websocket/index.js +1 -0
  45. package/build/index.d.ts +1 -0
  46. package/build/jobs/defineJob.d.ts +5 -0
  47. package/build/jobs/index.d.ts +2 -0
  48. package/build/jobs/sqs.d.ts +7 -0
  49. package/build/logger/index.d.ts +2 -0
  50. package/build/logger/logger.d.ts +2 -0
  51. package/build/logger/types.d.ts +6 -0
  52. package/build/routes/health.d.ts +2 -0
  53. package/build/routes/user.d.ts +2 -0
  54. package/build/runtime/aws/createAWSHandler.d.ts +3 -0
  55. package/build/runtime/aws/index.d.ts +2 -0
  56. package/build/runtime/aws/request.d.ts +4 -0
  57. package/build/runtime/fastify/createFastifyHandler.d.ts +3 -0
  58. package/build/runtime/fastify/index.d.ts +2 -0
  59. package/build/runtime/fastify/request.d.ts +4 -0
  60. package/build/runtime/index.d.ts +2 -0
  61. package/build/websocket/index.d.ts +16 -0
  62. package/docs/dev-server.md +278 -0
  63. package/docs/websocket.md +118 -0
  64. package/package.json +135 -0
@@ -0,0 +1,278 @@
1
+ # Dev Server (lambda-pipe)
2
+
3
+ Local development server that simulates AWS Lambda execution using a Fastify-based runtime.
4
+
5
+ It converts HTTP requests into Lambda-like events and executes routes with full middleware + plugin lifecycle.
6
+
7
+ ---
8
+
9
+ # Features
10
+
11
+ - File-based routing (`src/routes`)
12
+ - Lambda-like request context
13
+ - Fastify-based dev runtime
14
+ - Cookie support
15
+ - JSON-safe response serialization
16
+ - Dynamic route loading
17
+ - Optional hot reload (tsx / nodemon)
18
+
19
+ ---
20
+
21
+ # Installation
22
+
23
+ ```bash
24
+ npm install fastify @fastify/cookie
25
+ ```
26
+
27
+ ---
28
+
29
+ # Quick Start
30
+
31
+ ```ts
32
+ import { createDevApp } from 'lambda-pipe/cli'
33
+
34
+ await createDevApp({
35
+ port: 3000,
36
+ routesDir: 'src/routes',
37
+ })
38
+ ```
39
+
40
+ ```bash
41
+ npm run dev
42
+ ```
43
+
44
+ ---
45
+
46
+ # Project Structure
47
+
48
+ ```bash
49
+ src/
50
+ routes/
51
+ health.ts
52
+ user.ts
53
+ ```
54
+
55
+ ---
56
+
57
+ # Health Route
58
+
59
+ ```ts
60
+ import { defineRoute, ok } from 'lambda-pipe'
61
+
62
+ export default defineRoute({
63
+ method: 'GET',
64
+ path: '/health',
65
+
66
+ handler: async () => {
67
+ return ok({
68
+ status: 'ok',
69
+ uptime: process.uptime(),
70
+ })
71
+ },
72
+ })
73
+ ```
74
+
75
+ ---
76
+
77
+ # Full User Route Example
78
+
79
+ ```ts
80
+ import {
81
+ defineRoute,
82
+ ok,
83
+ HttpError,
84
+ ValidationError,
85
+ } from 'lambda-pipe'
86
+
87
+ import type { Middleware, Plugin, Validator } from 'lambda-pipe'
88
+
89
+ type User = {
90
+ id: string
91
+ teamId: string
92
+ }
93
+
94
+ type PluginContext = {
95
+ log: {
96
+ info: (...args: any[]) => void
97
+ error: (...args: any[]) => void
98
+ }
99
+
100
+ auth: {
101
+ can: (scope: string) => boolean
102
+ }
103
+
104
+ db: {
105
+ findUserById: (id: string) => Promise<any>
106
+ }
107
+ }
108
+
109
+ const paramsValidator: Validator<{ id: string }> = async (input) => {
110
+ const params = input as any
111
+ if (!params.id) throw new ValidationError('missing id')
112
+ return { id: params.id }
113
+ }
114
+
115
+ const queryValidator: Validator<{ expand?: string }> = async (input) => {
116
+ const query = input as any
117
+ if (query.expand && typeof query.expand !== 'string') {
118
+ throw new ValidationError('expand must be string')
119
+ }
120
+ return { expand: query.expand }
121
+ }
122
+
123
+ const loggerPlugin: Plugin = {
124
+ name: 'logger',
125
+ extend: () => ({
126
+ log: {
127
+ info: console.log,
128
+ error: console.error,
129
+ },
130
+ }),
131
+ }
132
+
133
+ const authPlugin: Plugin = {
134
+ name: 'auth',
135
+ extend: () => ({
136
+ auth: {
137
+ can: (scope: string) => scope === 'read:user',
138
+ },
139
+ }),
140
+ }
141
+
142
+ const dbPlugin: Plugin = {
143
+ name: 'db',
144
+ extend: () => ({
145
+ db: {
146
+ findUserById: async (id: string) => ({
147
+ id,
148
+ username: 'john_doe',
149
+ createdAt: new Date().toISOString(),
150
+ }),
151
+ },
152
+ }),
153
+ }
154
+
155
+ const requestMiddleware: Middleware<any, User, PluginContext> = {
156
+ onRequest: async (context, next) => {
157
+ context.state.startedAt = Date.now()
158
+
159
+ context.log.info('request start', {
160
+ requestId: context.req.requestId,
161
+ ip: context.req.ip,
162
+ })
163
+
164
+ if (!context.req.user) {
165
+ throw new HttpError(401, 'Unauthorized')
166
+ }
167
+
168
+ if (!context.auth.can('read:user')) {
169
+ throw new HttpError(403, 'Forbidden')
170
+ }
171
+
172
+ await next()
173
+ },
174
+
175
+ onResponse: async (context) => {
176
+ const ms = Date.now() - context.state.startedAt
177
+ context.log.info('response done', { duration: ms })
178
+ },
179
+
180
+ onError: async (err, context) => {
181
+ context.log.error('request failed', {
182
+ err,
183
+ requestId: context.req.requestId,
184
+ })
185
+ },
186
+ }
187
+
188
+ export default defineRoute<
189
+ {
190
+ params: { id: string }
191
+ query: { expand?: string }
192
+ },
193
+ User,
194
+ PluginContext
195
+ >({
196
+ method: 'GET',
197
+ path: '/user/:id',
198
+
199
+ plugins: [loggerPlugin, authPlugin, dbPlugin],
200
+ middleware: [requestMiddleware],
201
+
202
+ validate: {
203
+ params: paramsValidator,
204
+ query: queryValidator,
205
+ },
206
+
207
+ authenticate: async (token) => {
208
+ if (token !== 'valid-token') return null
209
+
210
+ return {
211
+ user: {
212
+ id: 'u1',
213
+ teamId: 'team-1',
214
+ },
215
+ scope: ['read:user'],
216
+ metadata: {
217
+ role: 'admin',
218
+ },
219
+ }
220
+ },
221
+
222
+ handler: async (context) => {
223
+ const { id } = context.params
224
+ const { expand } = context.query
225
+
226
+ const cacheKey = `user:${id}`
227
+ const cached = context.exec.cache.get(cacheKey)
228
+
229
+ if (cached) {
230
+ return ok({
231
+ source: 'cache',
232
+ data: cached,
233
+ })
234
+ }
235
+
236
+ const user = await context.db.findUserById(id)
237
+
238
+ const result = {
239
+ id: user.id,
240
+ username: user.username,
241
+ teamId: context.req.user?.teamId,
242
+ expand,
243
+ createdAt: user.createdAt,
244
+ }
245
+
246
+ context.exec.cache.set(cacheKey, result, 10000)
247
+
248
+ return ok({
249
+ source: 'database',
250
+ data: result,
251
+ })
252
+ },
253
+ })
254
+ ```
255
+
256
+ ---
257
+
258
+ # Execution Flow
259
+
260
+ ```
261
+ HTTP Request
262
+
263
+ Fastify server
264
+
265
+ Route loader
266
+
267
+ Lambda-like context
268
+
269
+ Middleware + Auth
270
+
271
+ Validation
272
+
273
+ Plugins injection
274
+
275
+ Handler
276
+
277
+ Response
278
+ ```
@@ -0,0 +1,118 @@
1
+ # WebSocket (lambda-pipe)
2
+
3
+ lambda-pipe supports AWS API Gateway WebSocket APIs with a simple handler abstraction.
4
+
5
+ It handles:
6
+ - $connect
7
+ - $disconnect
8
+ - custom route messages
9
+
10
+ ---
11
+
12
+ # Installation
13
+
14
+ ```bash
15
+ npm install @aws-sdk/client-apigatewaymanagementapi
16
+ ```
17
+
18
+ ---
19
+
20
+ # Basic Usage
21
+
22
+ ```ts
23
+ import { wsHandler } from 'lambda-pipe/websocket'
24
+
25
+ export const handler = wsHandler({
26
+ connect: async () => {
27
+ console.log('client connected')
28
+ },
29
+
30
+ message: async (ctx) => {
31
+ return {
32
+ statusCode: 200,
33
+ body: JSON.stringify({ ok: true }),
34
+ }
35
+ },
36
+
37
+ disconnect: async () => {
38
+ console.log('client disconnected')
39
+ },
40
+ })
41
+ ```
42
+
43
+ ---
44
+
45
+ # Context
46
+
47
+ ```
48
+ {
49
+ event: APIGatewayProxyWebsocketEventV2
50
+ }
51
+ ```
52
+
53
+ ---
54
+
55
+ # Important fields
56
+
57
+ ```
58
+ const connectionId =
59
+ context.event.requestContext.connectionId
60
+
61
+ const routeKey =
62
+ context.event.requestContext.routeKey
63
+ ```
64
+
65
+ | Field | Description |
66
+ |------|-------------|
67
+ | connectionId | unique connection |
68
+ | routeKey | $connect / message / disconnect |
69
+
70
+ ---
71
+
72
+ # Chat Example
73
+
74
+ ```ts
75
+ export const handler = wsHandler({
76
+ connect: async (ctx) => {
77
+ console.log('connected')
78
+ },
79
+
80
+ message: async (ctx) => {
81
+ const body = JSON.parse(ctx.event.body || '{}')
82
+
83
+ return {
84
+ statusCode: 200,
85
+ body: JSON.stringify({
86
+ received: true,
87
+ }),
88
+ }
89
+ },
90
+
91
+ disconnect: async () => {
92
+ console.log('disconnected')
93
+ },
94
+ })
95
+ ```
96
+
97
+ ---
98
+
99
+ # Metal model
100
+
101
+ ```bash
102
+ API Gateway WebSocket
103
+
104
+ lambda-pipe wsHandler
105
+
106
+ routeKey dispatcher
107
+
108
+ handler
109
+
110
+ response
111
+ ```
112
+
113
+ ---
114
+
115
+ # Notes
116
+
117
+ - Works only with AWS API Gateway WebSocket
118
+ - Stateless execution model
package/package.json ADDED
@@ -0,0 +1,135 @@
1
+ {
2
+ "name": "lambda-pipe",
3
+ "version": "0.1.0",
4
+ "description": "Type-safe plugin & middleware runtime for AWS Lambda and serverless APIs.",
5
+ "license": "MIT",
6
+ "author": "Delpi.Kye",
7
+ "sideEffects": false,
8
+ "type": "module",
9
+ "main": "./build/cjs/index.cjs",
10
+ "types": "./build/index.d.ts",
11
+ "exports": {
12
+ ".": {
13
+ "types": "./build/index.d.ts",
14
+ "import": "./build/esm/index.js",
15
+ "require": "./build/cjs/index.cjs"
16
+ },
17
+ "./jobs": {
18
+ "types": "./build/jobs/index.d.ts",
19
+ "import": "./build/esm/jobs/index.js",
20
+ "require": "./build/cjs/jobs/index.js"
21
+ },
22
+ "./logger": {
23
+ "types": "./build/logger/index.d.ts",
24
+ "import": "./build/esm/logger/index.js",
25
+ "require": "./build/cjs/logger/index.js"
26
+ },
27
+ "./runtime": {
28
+ "types": "./build/runtime/index.d.ts",
29
+ "import": "./build/esm/runtime/index.js",
30
+ "require": "./build/cjs/runtime/index.js"
31
+ },
32
+ "./websocket": {
33
+ "types": "./build/websocket/index.d.ts",
34
+ "import": "./build/esm/websocket/index.js",
35
+ "require": "./build/cjs/websocket/index.js"
36
+ },
37
+ "./cli": {
38
+ "types": "./build/cli/dev.d.ts",
39
+ "import": "./build/esm/cli/index.js",
40
+ "require": "./build/cjs/cli/index.js"
41
+ }
42
+ },
43
+ "files": [
44
+ "build",
45
+ "docs"
46
+ ],
47
+ "scripts": {
48
+ "clean": "rimraf build",
49
+ "dev": "nodemon --watch src --ext ts --exec tsx src/cli/dev.ts",
50
+ "build": "rollup -c",
51
+ "cb": "npm run clean && npm run build",
52
+ "lint": "eslint \"src/**/*.{ts,tsx}\"",
53
+ "lint:fix": "eslint \"src/**/*.{ts,tsx}\" --fix",
54
+ "typecheck": "tsc --noEmit",
55
+ "prepublishOnly": "npm run clean && npm run lint && npm run typecheck && npm run build"
56
+ },
57
+ "repository": {
58
+ "type": "git",
59
+ "url": "git+https://github.com/delpikye-v/lambda-pipe.git"
60
+ },
61
+ "homepage": "https://github.com/delpikye-v/lambda-pipe#readme",
62
+ "bugs": {
63
+ "url": "https://github.com/delpikye-v/lambda-pipe/issues"
64
+ },
65
+ "funding": {
66
+ "type": "github",
67
+ "url": "https://github.com/sponsors/delpikye-v"
68
+ },
69
+ "keywords": [
70
+ "lambda",
71
+ "aws-lambda",
72
+ "serverless",
73
+ "middleware",
74
+ "plugin-system",
75
+ "typescript",
76
+ "runtime",
77
+ "execution-engine",
78
+ "request-context",
79
+ "validation",
80
+ "fastify",
81
+ "lambda-adapter",
82
+ "api-runtime",
83
+ "backend",
84
+ "nodejs",
85
+ "http",
86
+ "router",
87
+ "framework-agnostic",
88
+ "typed-context",
89
+ "serverless-framework"
90
+ ],
91
+ "peerDependencies": {
92
+ "fastify": ">=5"
93
+ },
94
+
95
+ "peerDependenciesMeta": {
96
+ "fastify": {
97
+ "optional": true
98
+ }
99
+ },
100
+
101
+ "dependencies": {
102
+ "@aws-sdk/client-apigatewaymanagementapi": "^3.1045.0",
103
+ "aws-lambda": "^1.0.7"
104
+ },
105
+ "devDependencies": {
106
+ "@fastify/cookie": "^11.0.2",
107
+ "@rollup/plugin-commonjs": "^25.0.7",
108
+ "@rollup/plugin-json": "^6.1.0",
109
+ "@rollup/plugin-node-resolve": "^15.2.3",
110
+ "@rollup/plugin-terser": "^0.4.4",
111
+ "@types/aws-lambda": "^8.10.161",
112
+ "@types/node": "^25.5.0",
113
+ "@typescript-eslint/eslint-plugin": "^6.21.0",
114
+ "@typescript-eslint/parser": "^6.21.0",
115
+ "eslint": "^8.57.1",
116
+ "eslint-config-prettier": "^10.1.8",
117
+ "eslint-plugin-import": "^2.32.0",
118
+ "eslint-plugin-prettier": "^5.5.5",
119
+ "eslint-plugin-simple-import-sort": "^13.0.0",
120
+ "eslint-plugin-unused-imports": "^4.4.1",
121
+ "fastify": "^5.8.5",
122
+ "nodemon": "^3.1.14",
123
+ "prettier": "^3.8.1",
124
+ "rimraf": "^5.0.5",
125
+ "rollup": "^4.12.0",
126
+ "rollup-plugin-peer-deps-external": "^2.2.4",
127
+ "rollup-plugin-typescript2": "^0.37.0",
128
+ "tslib": "^2.8.1",
129
+ "tsx": "^4.21.0",
130
+ "typescript": "^5.3.3"
131
+ },
132
+ "engines": {
133
+ "node": ">=18"
134
+ }
135
+ }