@smplcty/dev-backend 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.
- package/README.md +110 -0
- package/dist/cli.d.ts +3 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +18 -0
- package/dist/cli.js.map +1 -0
- package/dist/config.d.ts +22 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +39 -0
- package/dist/config.js.map +1 -0
- package/dist/config.test.d.ts +2 -0
- package/dist/config.test.d.ts.map +1 -0
- package/dist/config.test.js +50 -0
- package/dist/config.test.js.map +1 -0
- package/dist/env.d.ts +6 -0
- package/dist/env.d.ts.map +1 -0
- package/dist/env.js +32 -0
- package/dist/env.js.map +1 -0
- package/dist/env.test.d.ts +2 -0
- package/dist/env.test.d.ts.map +1 -0
- package/dist/env.test.js +56 -0
- package/dist/env.test.js.map +1 -0
- package/dist/event.d.ts +11 -0
- package/dist/event.d.ts.map +1 -0
- package/dist/event.js +70 -0
- package/dist/event.js.map +1 -0
- package/dist/handlers.d.ts +13 -0
- package/dist/handlers.d.ts.map +1 -0
- package/dist/handlers.js +68 -0
- package/dist/handlers.js.map +1 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +6 -0
- package/dist/index.js.map +1 -0
- package/dist/server.d.ts +6 -0
- package/dist/server.d.ts.map +1 -0
- package/dist/server.js +55 -0
- package/dist/server.js.map +1 -0
- package/package.json +53 -0
package/README.md
ADDED
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
# @smplcty/dev-backend
|
|
2
|
+
|
|
3
|
+
Local Lambda dev server. Reads a routes config, loads env vars, and
|
|
4
|
+
serves Lambda handlers over HTTP with API Gateway v2 event
|
|
5
|
+
translation.
|
|
6
|
+
|
|
7
|
+
## Install
|
|
8
|
+
|
|
9
|
+
```sh
|
|
10
|
+
pnpm dlx @smplcty/dev-backend
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
Or as a dev dependency:
|
|
14
|
+
|
|
15
|
+
```sh
|
|
16
|
+
pnpm add -D @smplcty/dev-backend
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
## Usage
|
|
20
|
+
|
|
21
|
+
```sh
|
|
22
|
+
dev-backend [config-path]
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
Default config path is `back-end.routes.json` in the current
|
|
26
|
+
directory.
|
|
27
|
+
|
|
28
|
+
## Config
|
|
29
|
+
|
|
30
|
+
`back-end.routes.json` — committed to your frontend repo:
|
|
31
|
+
|
|
32
|
+
```json
|
|
33
|
+
{
|
|
34
|
+
"port": 4000,
|
|
35
|
+
"envFile": ".env.back-end",
|
|
36
|
+
"routes": {
|
|
37
|
+
"/graphql": "../handle-graphql/dist/index.js",
|
|
38
|
+
"/sign-in": {
|
|
39
|
+
"handler": "../../mabulu-inc/sign-in/dist/index.js",
|
|
40
|
+
"envFile": ".env.sign-in"
|
|
41
|
+
},
|
|
42
|
+
"/sign-in/verify": {
|
|
43
|
+
"handler": "../../mabulu-inc/sign-in-verify/dist/index.js",
|
|
44
|
+
"envFile": ".env.sign-in"
|
|
45
|
+
},
|
|
46
|
+
"/ai-insights": "../handle-ai-insights/dist/index.js"
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
### Route formats
|
|
52
|
+
|
|
53
|
+
**String** — just a handler path. Uses the base `envFile`:
|
|
54
|
+
|
|
55
|
+
```json
|
|
56
|
+
"/graphql": "../handle-graphql/dist/index.js"
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
**Object** — handler path plus a per-route env file override:
|
|
60
|
+
|
|
61
|
+
```json
|
|
62
|
+
"/sign-in": {
|
|
63
|
+
"handler": "../../mabulu-inc/sign-in/dist/index.js",
|
|
64
|
+
"envFile": ".env.sign-in"
|
|
65
|
+
}
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
Per-route env files override the base env file's values for that
|
|
69
|
+
handler only. This solves the case where two handlers need different
|
|
70
|
+
`DATABASE_URL` values (e.g. graphql vs sign-in connect to different
|
|
71
|
+
databases).
|
|
72
|
+
|
|
73
|
+
All paths are resolved relative to the config file's location.
|
|
74
|
+
|
|
75
|
+
### Env files
|
|
76
|
+
|
|
77
|
+
`.env.back-end` — shared variables (gitignored):
|
|
78
|
+
|
|
79
|
+
```
|
|
80
|
+
DATABASE_URL=postgresql://...
|
|
81
|
+
TWILIO_ACCOUNT_SID=AC...
|
|
82
|
+
TWILIO_AUTH_TOKEN=...
|
|
83
|
+
TWILIO_VERIFY_SERVICE_SID=VA...
|
|
84
|
+
LOG_LEVEL=debug
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
`.env.sign-in` — overrides for sign-in handlers (gitignored):
|
|
88
|
+
|
|
89
|
+
```
|
|
90
|
+
DATABASE_URL=postgresql://...different-db
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
Env files use standard `KEY=VALUE` format. Comments (`#`) and blank
|
|
94
|
+
lines are skipped. Surrounding quotes are stripped. Existing env vars
|
|
95
|
+
are not overridden.
|
|
96
|
+
|
|
97
|
+
## How it works
|
|
98
|
+
|
|
99
|
+
1. Loads the base env file into `process.env`.
|
|
100
|
+
2. For each route, if it has a per-route `envFile`, temporarily
|
|
101
|
+
overrides the relevant env vars, imports the handler module
|
|
102
|
+
(so module-level init like Pool creation uses the right values),
|
|
103
|
+
then restores the originals.
|
|
104
|
+
3. Starts an HTTP server that translates incoming requests into
|
|
105
|
+
API Gateway v2 proxy events and calls the matching handler.
|
|
106
|
+
4. CORS headers are set on all responses.
|
|
107
|
+
|
|
108
|
+
## License
|
|
109
|
+
|
|
110
|
+
MIT
|
package/dist/cli.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":""}
|
package/dist/cli.js
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { loadConfig } from './config.js';
|
|
3
|
+
import { loadEnvFile } from './env.js';
|
|
4
|
+
import { loadHandlers } from './handlers.js';
|
|
5
|
+
import { startServer } from './server.js';
|
|
6
|
+
const configPath = process.argv[2] ?? 'back-end.routes.json';
|
|
7
|
+
console.log(`Loading config from ${configPath}`);
|
|
8
|
+
const config = loadConfig(configPath);
|
|
9
|
+
console.log(`Loading env from ${config.envFile}`);
|
|
10
|
+
loadEnvFile(config.envFile);
|
|
11
|
+
console.log('Loading handlers:');
|
|
12
|
+
const handlers = await loadHandlers(config.routes);
|
|
13
|
+
if (Object.keys(handlers).length === 0) {
|
|
14
|
+
console.error('No handlers loaded — exiting');
|
|
15
|
+
process.exit(1);
|
|
16
|
+
}
|
|
17
|
+
startServer(handlers, config.port);
|
|
18
|
+
//# sourceMappingURL=cli.js.map
|
package/dist/cli.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";AAEA,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACzC,OAAO,EAAE,WAAW,EAAE,MAAM,UAAU,CAAC;AACvC,OAAO,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAC7C,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAE1C,MAAM,UAAU,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,sBAAsB,CAAC;AAE7D,OAAO,CAAC,GAAG,CAAC,uBAAuB,UAAU,EAAE,CAAC,CAAC;AACjD,MAAM,MAAM,GAAG,UAAU,CAAC,UAAU,CAAC,CAAC;AAEtC,OAAO,CAAC,GAAG,CAAC,oBAAoB,MAAM,CAAC,OAAO,EAAE,CAAC,CAAC;AAClD,WAAW,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;AAE5B,OAAO,CAAC,GAAG,CAAC,mBAAmB,CAAC,CAAC;AACjC,MAAM,QAAQ,GAAG,MAAM,YAAY,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;AAEnD,IAAI,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;IACvC,OAAO,CAAC,KAAK,CAAC,8BAA8B,CAAC,CAAC;IAC9C,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAClB,CAAC;AAED,WAAW,CAAC,QAAQ,EAAE,MAAM,CAAC,IAAI,CAAC,CAAC"}
|
package/dist/config.d.ts
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
export interface RouteConfig {
|
|
2
|
+
/** Absolute path to the handler module. */
|
|
3
|
+
handler: string;
|
|
4
|
+
/** Optional env file to load before importing this handler.
|
|
5
|
+
* Overrides values from the base envFile. Useful when handlers
|
|
6
|
+
* need different DATABASE_URL values. */
|
|
7
|
+
envFile?: string;
|
|
8
|
+
}
|
|
9
|
+
export interface Config {
|
|
10
|
+
/** HTTP port. Default 4000. */
|
|
11
|
+
port: number;
|
|
12
|
+
/** Path to the base .env file. Default ".env.back-end". */
|
|
13
|
+
envFile: string;
|
|
14
|
+
/** Route → handler config. */
|
|
15
|
+
routes: Record<string, RouteConfig>;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Load and validate the config file. Paths are resolved relative
|
|
19
|
+
* to the config file's directory.
|
|
20
|
+
*/
|
|
21
|
+
export declare function loadConfig(configPath: string): Config;
|
|
22
|
+
//# sourceMappingURL=config.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAGA,MAAM,WAAW,WAAW;IAC1B,2CAA2C;IAC3C,OAAO,EAAE,MAAM,CAAC;IAChB;;8CAE0C;IAC1C,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,MAAM;IACrB,+BAA+B;IAC/B,IAAI,EAAE,MAAM,CAAC;IACb,2DAA2D;IAC3D,OAAO,EAAE,MAAM,CAAC;IAChB,8BAA8B;IAC9B,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,WAAW,CAAC,CAAC;CACrC;AAUD;;;GAGG;AACH,wBAAgB,UAAU,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,CAiCrD"}
|
package/dist/config.js
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { readFileSync } from 'node:fs';
|
|
2
|
+
import { resolve, dirname } from 'node:path';
|
|
3
|
+
/**
|
|
4
|
+
* Load and validate the config file. Paths are resolved relative
|
|
5
|
+
* to the config file's directory.
|
|
6
|
+
*/
|
|
7
|
+
export function loadConfig(configPath) {
|
|
8
|
+
const absPath = resolve(configPath);
|
|
9
|
+
const dir = dirname(absPath);
|
|
10
|
+
let raw;
|
|
11
|
+
try {
|
|
12
|
+
raw = JSON.parse(readFileSync(absPath, 'utf-8'));
|
|
13
|
+
}
|
|
14
|
+
catch (err) {
|
|
15
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
16
|
+
throw new Error(`Failed to read config ${absPath}: ${msg}`, { cause: err });
|
|
17
|
+
}
|
|
18
|
+
if (!raw.routes || typeof raw.routes !== 'object') {
|
|
19
|
+
throw new Error('Config must have a "routes" object');
|
|
20
|
+
}
|
|
21
|
+
const routes = {};
|
|
22
|
+
for (const [path, entry] of Object.entries(raw.routes)) {
|
|
23
|
+
if (typeof entry === 'string') {
|
|
24
|
+
routes[path] = { handler: resolve(dir, entry) };
|
|
25
|
+
}
|
|
26
|
+
else {
|
|
27
|
+
routes[path] = {
|
|
28
|
+
handler: resolve(dir, entry.handler),
|
|
29
|
+
envFile: entry.envFile ? resolve(dir, entry.envFile) : undefined,
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
return {
|
|
34
|
+
port: raw.port ?? 4000,
|
|
35
|
+
envFile: resolve(dir, raw.envFile ?? '.env.back-end'),
|
|
36
|
+
routes,
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
//# sourceMappingURL=config.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACvC,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AA4B7C;;;GAGG;AACH,MAAM,UAAU,UAAU,CAAC,UAAkB;IAC3C,MAAM,OAAO,GAAG,OAAO,CAAC,UAAU,CAAC,CAAC;IACpC,MAAM,GAAG,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IAE7B,IAAI,GAAc,CAAC;IACnB,IAAI,CAAC;QACH,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,OAAO,EAAE,OAAO,CAAC,CAAc,CAAC;IAChE,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,MAAM,GAAG,GAAG,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;QAC7D,MAAM,IAAI,KAAK,CAAC,yBAAyB,OAAO,KAAK,GAAG,EAAE,EAAE,EAAE,KAAK,EAAE,GAAG,EAAE,CAAC,CAAC;IAC9E,CAAC;IAED,IAAI,CAAC,GAAG,CAAC,MAAM,IAAI,OAAO,GAAG,CAAC,MAAM,KAAK,QAAQ,EAAE,CAAC;QAClD,MAAM,IAAI,KAAK,CAAC,oCAAoC,CAAC,CAAC;IACxD,CAAC;IAED,MAAM,MAAM,GAAgC,EAAE,CAAC;IAC/C,KAAK,MAAM,CAAC,IAAI,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC;QACvD,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE,CAAC;YAC9B,MAAM,CAAC,IAAI,CAAC,GAAG,EAAE,OAAO,EAAE,OAAO,CAAC,GAAG,EAAE,KAAK,CAAC,EAAE,CAAC;QAClD,CAAC;aAAM,CAAC;YACN,MAAM,CAAC,IAAI,CAAC,GAAG;gBACb,OAAO,EAAE,OAAO,CAAC,GAAG,EAAE,KAAK,CAAC,OAAO,CAAC;gBACpC,OAAO,EAAE,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,OAAO,CAAC,GAAG,EAAE,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,SAAS;aACjE,CAAC;QACJ,CAAC;IACH,CAAC;IAED,OAAO;QACL,IAAI,EAAE,GAAG,CAAC,IAAI,IAAI,IAAI;QACtB,OAAO,EAAE,OAAO,CAAC,GAAG,EAAE,GAAG,CAAC,OAAO,IAAI,eAAe,CAAC;QACrD,MAAM;KACP,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.test.d.ts","sourceRoot":"","sources":["../src/config.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest';
|
|
2
|
+
import { writeFileSync, mkdtempSync, rmSync } from 'node:fs';
|
|
3
|
+
import { join } from 'node:path';
|
|
4
|
+
import { tmpdir } from 'node:os';
|
|
5
|
+
import { loadConfig } from './config.js';
|
|
6
|
+
describe('loadConfig', () => {
|
|
7
|
+
it('loads a minimal config with string routes', () => {
|
|
8
|
+
const dir = mkdtempSync(join(tmpdir(), 'dev-backend-test-'));
|
|
9
|
+
const configPath = join(dir, 'routes.json');
|
|
10
|
+
writeFileSync(configPath, JSON.stringify({
|
|
11
|
+
routes: { '/graphql': './handler.js' },
|
|
12
|
+
}));
|
|
13
|
+
const config = loadConfig(configPath);
|
|
14
|
+
expect(config.port).toBe(4000);
|
|
15
|
+
expect(config.envFile).toBe(join(dir, '.env.back-end'));
|
|
16
|
+
expect(config.routes['/graphql']?.handler).toBe(join(dir, 'handler.js'));
|
|
17
|
+
expect(config.routes['/graphql']?.envFile).toBeUndefined();
|
|
18
|
+
rmSync(dir, { recursive: true });
|
|
19
|
+
});
|
|
20
|
+
it('loads object routes with envFile', () => {
|
|
21
|
+
const dir = mkdtempSync(join(tmpdir(), 'dev-backend-test-'));
|
|
22
|
+
const configPath = join(dir, 'routes.json');
|
|
23
|
+
writeFileSync(configPath, JSON.stringify({
|
|
24
|
+
port: 5000,
|
|
25
|
+
envFile: '.env.custom',
|
|
26
|
+
routes: {
|
|
27
|
+
'/api': {
|
|
28
|
+
handler: '../api/dist/index.js',
|
|
29
|
+
envFile: '.env.api',
|
|
30
|
+
},
|
|
31
|
+
},
|
|
32
|
+
}));
|
|
33
|
+
const config = loadConfig(configPath);
|
|
34
|
+
expect(config.port).toBe(5000);
|
|
35
|
+
expect(config.envFile).toBe(join(dir, '.env.custom'));
|
|
36
|
+
expect(config.routes['/api']?.envFile).toBe(join(dir, '.env.api'));
|
|
37
|
+
rmSync(dir, { recursive: true });
|
|
38
|
+
});
|
|
39
|
+
it('throws on missing routes', () => {
|
|
40
|
+
const dir = mkdtempSync(join(tmpdir(), 'dev-backend-test-'));
|
|
41
|
+
const configPath = join(dir, 'routes.json');
|
|
42
|
+
writeFileSync(configPath, JSON.stringify({}));
|
|
43
|
+
expect(() => loadConfig(configPath)).toThrow('routes');
|
|
44
|
+
rmSync(dir, { recursive: true });
|
|
45
|
+
});
|
|
46
|
+
it('throws on missing file', () => {
|
|
47
|
+
expect(() => loadConfig('/nonexistent/routes.json')).toThrow('Failed to read');
|
|
48
|
+
});
|
|
49
|
+
});
|
|
50
|
+
//# sourceMappingURL=config.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.test.js","sourceRoot":"","sources":["../src/config.test.ts"],"names":[],"mappings":"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"}
|
package/dist/env.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"env.d.ts","sourceRoot":"","sources":["../src/env.ts"],"names":[],"mappings":"AAEA;;;GAGG;AACH,wBAAgB,WAAW,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI,CA+BlD"}
|
package/dist/env.js
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { readFileSync, existsSync } from 'node:fs';
|
|
2
|
+
/**
|
|
3
|
+
* Parse a .env file and load its variables into process.env.
|
|
4
|
+
* Does not override existing env vars.
|
|
5
|
+
*/
|
|
6
|
+
export function loadEnvFile(filePath) {
|
|
7
|
+
if (!existsSync(filePath)) {
|
|
8
|
+
console.warn(`env file not found: ${filePath} — skipping`);
|
|
9
|
+
return;
|
|
10
|
+
}
|
|
11
|
+
const content = readFileSync(filePath, 'utf-8');
|
|
12
|
+
for (const line of content.split('\n')) {
|
|
13
|
+
const trimmed = line.trim();
|
|
14
|
+
if (trimmed.length === 0 || trimmed.startsWith('#'))
|
|
15
|
+
continue;
|
|
16
|
+
const eqIndex = trimmed.indexOf('=');
|
|
17
|
+
if (eqIndex === -1)
|
|
18
|
+
continue;
|
|
19
|
+
const key = trimmed.slice(0, eqIndex).trim();
|
|
20
|
+
let value = trimmed.slice(eqIndex + 1).trim();
|
|
21
|
+
// Strip surrounding quotes
|
|
22
|
+
if ((value.startsWith('"') && value.endsWith('"')) ||
|
|
23
|
+
(value.startsWith("'") && value.endsWith("'"))) {
|
|
24
|
+
value = value.slice(1, -1);
|
|
25
|
+
}
|
|
26
|
+
// Don't override existing env vars
|
|
27
|
+
if (process.env[key] === undefined) {
|
|
28
|
+
process.env[key] = value;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
//# sourceMappingURL=env.js.map
|
package/dist/env.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"env.js","sourceRoot":"","sources":["../src/env.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AAEnD;;;GAGG;AACH,MAAM,UAAU,WAAW,CAAC,QAAgB;IAC1C,IAAI,CAAC,UAAU,CAAC,QAAQ,CAAC,EAAE,CAAC;QAC1B,OAAO,CAAC,IAAI,CAAC,uBAAuB,QAAQ,aAAa,CAAC,CAAC;QAC3D,OAAO;IACT,CAAC;IAED,MAAM,OAAO,GAAG,YAAY,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;IAEhD,KAAK,MAAM,IAAI,IAAI,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC;QACvC,MAAM,OAAO,GAAG,IAAI,CAAC,IAAI,EAAE,CAAC;QAC5B,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC,IAAI,OAAO,CAAC,UAAU,CAAC,GAAG,CAAC;YAAE,SAAS;QAE9D,MAAM,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;QACrC,IAAI,OAAO,KAAK,CAAC,CAAC;YAAE,SAAS;QAE7B,MAAM,GAAG,GAAG,OAAO,CAAC,KAAK,CAAC,CAAC,EAAE,OAAO,CAAC,CAAC,IAAI,EAAE,CAAC;QAC7C,IAAI,KAAK,GAAG,OAAO,CAAC,KAAK,CAAC,OAAO,GAAG,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;QAE9C,2BAA2B;QAC3B,IACE,CAAC,KAAK,CAAC,UAAU,CAAC,GAAG,CAAC,IAAI,KAAK,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC;YAC9C,CAAC,KAAK,CAAC,UAAU,CAAC,GAAG,CAAC,IAAI,KAAK,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,EAC9C,CAAC;YACD,KAAK,GAAG,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;QAC7B,CAAC;QAED,mCAAmC;QACnC,IAAI,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,KAAK,SAAS,EAAE,CAAC;YACnC,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,KAAK,CAAC;QAC3B,CAAC;IACH,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"env.test.d.ts","sourceRoot":"","sources":["../src/env.test.ts"],"names":[],"mappings":""}
|
package/dist/env.test.js
ADDED
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { afterEach, describe, expect, it } from 'vitest';
|
|
2
|
+
import { writeFileSync, mkdtempSync, rmSync } from 'node:fs';
|
|
3
|
+
import { join } from 'node:path';
|
|
4
|
+
import { tmpdir } from 'node:os';
|
|
5
|
+
import { loadEnvFile } from './env.js';
|
|
6
|
+
describe('loadEnvFile', () => {
|
|
7
|
+
const dirs = [];
|
|
8
|
+
afterEach(() => {
|
|
9
|
+
for (const d of dirs)
|
|
10
|
+
rmSync(d, { recursive: true });
|
|
11
|
+
dirs.length = 0;
|
|
12
|
+
});
|
|
13
|
+
function tmpEnv(content) {
|
|
14
|
+
const dir = mkdtempSync(join(tmpdir(), 'dev-backend-env-'));
|
|
15
|
+
dirs.push(dir);
|
|
16
|
+
const p = join(dir, '.env');
|
|
17
|
+
writeFileSync(p, content);
|
|
18
|
+
return p;
|
|
19
|
+
}
|
|
20
|
+
it('loads key=value pairs into process.env', () => {
|
|
21
|
+
const key = `TEST_ENV_${Date.now()}`;
|
|
22
|
+
const path = tmpEnv(`${key}=hello`);
|
|
23
|
+
loadEnvFile(path);
|
|
24
|
+
expect(process.env[key]).toBe('hello');
|
|
25
|
+
delete process.env[key];
|
|
26
|
+
});
|
|
27
|
+
it('does not override existing env vars', () => {
|
|
28
|
+
const key = `TEST_ENV_EXISTING_${Date.now()}`;
|
|
29
|
+
process.env[key] = 'original';
|
|
30
|
+
const path = tmpEnv(`${key}=override`);
|
|
31
|
+
loadEnvFile(path);
|
|
32
|
+
expect(process.env[key]).toBe('original');
|
|
33
|
+
delete process.env[key];
|
|
34
|
+
});
|
|
35
|
+
it('strips surrounding quotes', () => {
|
|
36
|
+
const key1 = `TEST_DQ_${Date.now()}`;
|
|
37
|
+
const key2 = `TEST_SQ_${Date.now()}`;
|
|
38
|
+
const path = tmpEnv(`${key1}="double"\n${key2}='single'`);
|
|
39
|
+
loadEnvFile(path);
|
|
40
|
+
expect(process.env[key1]).toBe('double');
|
|
41
|
+
expect(process.env[key2]).toBe('single');
|
|
42
|
+
delete process.env[key1];
|
|
43
|
+
delete process.env[key2];
|
|
44
|
+
});
|
|
45
|
+
it('skips comments and blank lines', () => {
|
|
46
|
+
const key = `TEST_COMMENT_${Date.now()}`;
|
|
47
|
+
const path = tmpEnv(`# comment\n\n${key}=yes`);
|
|
48
|
+
loadEnvFile(path);
|
|
49
|
+
expect(process.env[key]).toBe('yes');
|
|
50
|
+
delete process.env[key];
|
|
51
|
+
});
|
|
52
|
+
it('warns on missing file without throwing', () => {
|
|
53
|
+
expect(() => loadEnvFile('/nonexistent/.env')).not.toThrow();
|
|
54
|
+
});
|
|
55
|
+
});
|
|
56
|
+
//# sourceMappingURL=env.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"env.test.js","sourceRoot":"","sources":["../src/env.test.ts"],"names":[],"mappings":"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"}
|
package/dist/event.d.ts
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { IncomingMessage } from 'node:http';
|
|
2
|
+
/**
|
|
3
|
+
* Build an API Gateway v2 (HTTP API) proxy event from an incoming
|
|
4
|
+
* Node.js HTTP request.
|
|
5
|
+
*/
|
|
6
|
+
export declare function buildEvent(req: IncomingMessage, path: string, query: string | undefined): Promise<Record<string, unknown>>;
|
|
7
|
+
/**
|
|
8
|
+
* Decompress a gzipped response body returned by a handler.
|
|
9
|
+
*/
|
|
10
|
+
export declare function decompressResponse(body: string): string;
|
|
11
|
+
//# sourceMappingURL=event.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"event.d.ts","sourceRoot":"","sources":["../src/event.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,WAAW,CAAC;AAGjD;;;GAGG;AACH,wBAAsB,UAAU,CAC9B,GAAG,EAAE,eAAe,EACpB,IAAI,EAAE,MAAM,EACZ,KAAK,EAAE,MAAM,GAAG,SAAS,GACxB,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CA0BlC;AAoCD;;GAEG;AACH,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAEvD"}
|
package/dist/event.js
ADDED
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import { gunzipSync } from 'node:zlib';
|
|
2
|
+
/**
|
|
3
|
+
* Build an API Gateway v2 (HTTP API) proxy event from an incoming
|
|
4
|
+
* Node.js HTTP request.
|
|
5
|
+
*/
|
|
6
|
+
export async function buildEvent(req, path, query) {
|
|
7
|
+
const body = await readBody(req);
|
|
8
|
+
const method = req.method ?? 'GET';
|
|
9
|
+
return {
|
|
10
|
+
version: '2.0',
|
|
11
|
+
headers: req.headers,
|
|
12
|
+
method,
|
|
13
|
+
queryStringParameters: parseQuery(query),
|
|
14
|
+
isBase64Encoded: false,
|
|
15
|
+
rawQueryString: query ?? '',
|
|
16
|
+
requestContext: {
|
|
17
|
+
http: {
|
|
18
|
+
method,
|
|
19
|
+
path,
|
|
20
|
+
protocol: 'HTTP/1.1',
|
|
21
|
+
sourceIp: '127.0.0.1',
|
|
22
|
+
userAgent: req.headers['user-agent'] ?? '',
|
|
23
|
+
},
|
|
24
|
+
requestId: crypto.randomUUID(),
|
|
25
|
+
time: new Date().toISOString(),
|
|
26
|
+
},
|
|
27
|
+
rawPath: path,
|
|
28
|
+
routeKey: `${method} ${path}`,
|
|
29
|
+
body,
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
function parseQuery(query) {
|
|
33
|
+
if (!query)
|
|
34
|
+
return undefined;
|
|
35
|
+
const params = {};
|
|
36
|
+
for (const part of query.split('&')) {
|
|
37
|
+
const eqIndex = part.indexOf('=');
|
|
38
|
+
if (eqIndex === -1) {
|
|
39
|
+
params[decodeURIComponent(part)] = '';
|
|
40
|
+
}
|
|
41
|
+
else {
|
|
42
|
+
params[decodeURIComponent(part.slice(0, eqIndex))] =
|
|
43
|
+
decodeURIComponent(part.slice(eqIndex + 1));
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
return params;
|
|
47
|
+
}
|
|
48
|
+
function readBody(req) {
|
|
49
|
+
return new Promise((resolve, reject) => {
|
|
50
|
+
const chunks = [];
|
|
51
|
+
req.on('data', (chunk) => chunks.push(chunk));
|
|
52
|
+
req.on('error', reject);
|
|
53
|
+
req.on('end', () => {
|
|
54
|
+
const buf = Buffer.concat(chunks);
|
|
55
|
+
if (req.headers['content-encoding'] === 'gzip') {
|
|
56
|
+
resolve(buf.toString('base64'));
|
|
57
|
+
}
|
|
58
|
+
else {
|
|
59
|
+
resolve(buf.toString());
|
|
60
|
+
}
|
|
61
|
+
});
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Decompress a gzipped response body returned by a handler.
|
|
66
|
+
*/
|
|
67
|
+
export function decompressResponse(body) {
|
|
68
|
+
return gunzipSync(Buffer.from(body, 'base64')).toString();
|
|
69
|
+
}
|
|
70
|
+
//# sourceMappingURL=event.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"event.js","sourceRoot":"","sources":["../src/event.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,UAAU,EAAE,MAAM,WAAW,CAAC;AAEvC;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,UAAU,CAC9B,GAAoB,EACpB,IAAY,EACZ,KAAyB;IAEzB,MAAM,IAAI,GAAG,MAAM,QAAQ,CAAC,GAAG,CAAC,CAAC;IACjC,MAAM,MAAM,GAAG,GAAG,CAAC,MAAM,IAAI,KAAK,CAAC;IAEnC,OAAO;QACL,OAAO,EAAE,KAAK;QACd,OAAO,EAAE,GAAG,CAAC,OAAO;QACpB,MAAM;QACN,qBAAqB,EAAE,UAAU,CAAC,KAAK,CAAC;QACxC,eAAe,EAAE,KAAK;QACtB,cAAc,EAAE,KAAK,IAAI,EAAE;QAC3B,cAAc,EAAE;YACd,IAAI,EAAE;gBACJ,MAAM;gBACN,IAAI;gBACJ,QAAQ,EAAE,UAAU;gBACpB,QAAQ,EAAE,WAAW;gBACrB,SAAS,EAAE,GAAG,CAAC,OAAO,CAAC,YAAY,CAAC,IAAI,EAAE;aAC3C;YACD,SAAS,EAAE,MAAM,CAAC,UAAU,EAAE;YAC9B,IAAI,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;SAC/B;QACD,OAAO,EAAE,IAAI;QACb,QAAQ,EAAE,GAAG,MAAM,IAAI,IAAI,EAAE;QAC7B,IAAI;KACL,CAAC;AACJ,CAAC;AAED,SAAS,UAAU,CACjB,KAAyB;IAEzB,IAAI,CAAC,KAAK;QAAE,OAAO,SAAS,CAAC;IAE7B,MAAM,MAAM,GAA2B,EAAE,CAAC;IAC1C,KAAK,MAAM,IAAI,IAAI,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,EAAE,CAAC;QACpC,MAAM,OAAO,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;QAClC,IAAI,OAAO,KAAK,CAAC,CAAC,EAAE,CAAC;YACnB,MAAM,CAAC,kBAAkB,CAAC,IAAI,CAAC,CAAC,GAAG,EAAE,CAAC;QACxC,CAAC;aAAM,CAAC;YACN,MAAM,CAAC,kBAAkB,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,OAAO,CAAC,CAAC,CAAC;gBAChD,kBAAkB,CAAC,IAAI,CAAC,KAAK,CAAC,OAAO,GAAG,CAAC,CAAC,CAAC,CAAC;QAChD,CAAC;IACH,CAAC;IACD,OAAO,MAAM,CAAC;AAChB,CAAC;AAED,SAAS,QAAQ,CAAC,GAAoB;IACpC,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;QACrC,MAAM,MAAM,GAAa,EAAE,CAAC;QAC5B,GAAG,CAAC,EAAE,CAAC,MAAM,EAAE,CAAC,KAAa,EAAE,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC;QACtD,GAAG,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;QACxB,GAAG,CAAC,EAAE,CAAC,KAAK,EAAE,GAAG,EAAE;YACjB,MAAM,GAAG,GAAG,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;YAClC,IAAI,GAAG,CAAC,OAAO,CAAC,kBAAkB,CAAC,KAAK,MAAM,EAAE,CAAC;gBAC/C,OAAO,CAAC,GAAG,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC,CAAC;YAClC,CAAC;iBAAM,CAAC;gBACN,OAAO,CAAC,GAAG,CAAC,QAAQ,EAAE,CAAC,CAAC;YAC1B,CAAC;QACH,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;AACL,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,kBAAkB,CAAC,IAAY;IAC7C,OAAO,UAAU,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAC,CAAC,QAAQ,EAAE,CAAC;AAC5D,CAAC"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { RouteConfig } from './config.js';
|
|
2
|
+
export type Handler = (event: Record<string, unknown>) => Promise<{
|
|
3
|
+
statusCode: number;
|
|
4
|
+
body?: string;
|
|
5
|
+
headers?: Record<string, string>;
|
|
6
|
+
}>;
|
|
7
|
+
/**
|
|
8
|
+
* Import all handler modules. For routes with a per-route envFile,
|
|
9
|
+
* temporarily override process.env before importing so the handler's
|
|
10
|
+
* module-level init (e.g. Pool creation) picks up the right values.
|
|
11
|
+
*/
|
|
12
|
+
export declare function loadHandlers(routes: Record<string, RouteConfig>): Promise<Record<string, Handler>>;
|
|
13
|
+
//# sourceMappingURL=handlers.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"handlers.d.ts","sourceRoot":"","sources":["../src/handlers.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAE/C,MAAM,MAAM,OAAO,GAAG,CAAC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,OAAO,CAAC;IAChE,UAAU,EAAE,MAAM,CAAC;IACnB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAClC,CAAC,CAAC;AAEH;;;;GAIG;AACH,wBAAsB,YAAY,CAChC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,WAAW,CAAC,GAClC,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CA6ClC"}
|
package/dist/handlers.js
ADDED
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import { readFileSync, existsSync } from 'node:fs';
|
|
2
|
+
import { pathToFileURL } from 'node:url';
|
|
3
|
+
import { loadEnvFile } from './env.js';
|
|
4
|
+
/**
|
|
5
|
+
* Import all handler modules. For routes with a per-route envFile,
|
|
6
|
+
* temporarily override process.env before importing so the handler's
|
|
7
|
+
* module-level init (e.g. Pool creation) picks up the right values.
|
|
8
|
+
*/
|
|
9
|
+
export async function loadHandlers(routes) {
|
|
10
|
+
const handlers = {};
|
|
11
|
+
for (const [path, route] of Object.entries(routes)) {
|
|
12
|
+
// Snapshot env vars that the route's envFile might override
|
|
13
|
+
const overrides = new Map();
|
|
14
|
+
if (route.envFile) {
|
|
15
|
+
const keys = readEnvKeys(route.envFile);
|
|
16
|
+
for (const key of keys) {
|
|
17
|
+
overrides.set(key, process.env[key]);
|
|
18
|
+
delete process.env[key];
|
|
19
|
+
}
|
|
20
|
+
loadEnvFile(route.envFile);
|
|
21
|
+
}
|
|
22
|
+
try {
|
|
23
|
+
const mod = (await import(pathToFileURL(route.handler).href));
|
|
24
|
+
const handler = mod.handler;
|
|
25
|
+
if (typeof handler !== 'function') {
|
|
26
|
+
console.error(` ${path}: ${route.handler} does not export a handler function — skipping`);
|
|
27
|
+
continue;
|
|
28
|
+
}
|
|
29
|
+
handlers[path] = handler;
|
|
30
|
+
console.log(` ${path} → ${route.handler}`);
|
|
31
|
+
}
|
|
32
|
+
catch (err) {
|
|
33
|
+
console.error(` ${path}: failed to import ${route.handler}:`, err);
|
|
34
|
+
continue;
|
|
35
|
+
}
|
|
36
|
+
finally {
|
|
37
|
+
for (const [key, original] of overrides) {
|
|
38
|
+
if (original === undefined) {
|
|
39
|
+
delete process.env[key];
|
|
40
|
+
}
|
|
41
|
+
else {
|
|
42
|
+
process.env[key] = original;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
return handlers;
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Parse an env file to extract just the key names.
|
|
51
|
+
*/
|
|
52
|
+
function readEnvKeys(filePath) {
|
|
53
|
+
if (!existsSync(filePath))
|
|
54
|
+
return [];
|
|
55
|
+
const keys = [];
|
|
56
|
+
const content = readFileSync(filePath, 'utf-8');
|
|
57
|
+
for (const line of content.split('\n')) {
|
|
58
|
+
const trimmed = line.trim();
|
|
59
|
+
if (trimmed.length === 0 || trimmed.startsWith('#'))
|
|
60
|
+
continue;
|
|
61
|
+
const eqIndex = trimmed.indexOf('=');
|
|
62
|
+
if (eqIndex === -1)
|
|
63
|
+
continue;
|
|
64
|
+
keys.push(trimmed.slice(0, eqIndex).trim());
|
|
65
|
+
}
|
|
66
|
+
return keys;
|
|
67
|
+
}
|
|
68
|
+
//# sourceMappingURL=handlers.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"handlers.js","sourceRoot":"","sources":["../src/handlers.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export { loadConfig, type Config, type RouteConfig } from './config.js';
|
|
2
|
+
export { loadEnvFile } from './env.js';
|
|
3
|
+
export { loadHandlers, type Handler } from './handlers.js';
|
|
4
|
+
export { startServer } from './server.js';
|
|
5
|
+
export { buildEvent, decompressResponse } from './event.js';
|
|
6
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,KAAK,MAAM,EAAE,KAAK,WAAW,EAAE,MAAM,aAAa,CAAC;AACxE,OAAO,EAAE,WAAW,EAAE,MAAM,UAAU,CAAC;AACvC,OAAO,EAAE,YAAY,EAAE,KAAK,OAAO,EAAE,MAAM,eAAe,CAAC;AAC3D,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAC1C,OAAO,EAAE,UAAU,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export { loadConfig } from './config.js';
|
|
2
|
+
export { loadEnvFile } from './env.js';
|
|
3
|
+
export { loadHandlers } from './handlers.js';
|
|
4
|
+
export { startServer } from './server.js';
|
|
5
|
+
export { buildEvent, decompressResponse } from './event.js';
|
|
6
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAiC,MAAM,aAAa,CAAC;AACxE,OAAO,EAAE,WAAW,EAAE,MAAM,UAAU,CAAC;AACvC,OAAO,EAAE,YAAY,EAAgB,MAAM,eAAe,CAAC;AAC3D,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAC1C,OAAO,EAAE,UAAU,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAC"}
|
package/dist/server.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,eAAe,CAAC;AAE7C;;GAEG;AACH,wBAAgB,WAAW,CACzB,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EACjC,IAAI,EAAE,MAAM,GACX,IAAI,CAkDN"}
|
package/dist/server.js
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { createServer } from 'node:http';
|
|
2
|
+
import { buildEvent, decompressResponse } from './event.js';
|
|
3
|
+
/**
|
|
4
|
+
* Start the HTTP server that routes requests to Lambda handlers.
|
|
5
|
+
*/
|
|
6
|
+
export function startServer(handlers, port) {
|
|
7
|
+
const server = createServer(async (req, res) => {
|
|
8
|
+
const url = req.url ?? '/';
|
|
9
|
+
const [rawPath, query] = url.replace('//', '/').split('?');
|
|
10
|
+
console.log(`${req.method} ${rawPath}${query ? `?${query}` : ''}`);
|
|
11
|
+
// CORS preflight
|
|
12
|
+
if (req.method === 'OPTIONS') {
|
|
13
|
+
setCorsHeaders(res);
|
|
14
|
+
res.statusCode = 204;
|
|
15
|
+
res.end();
|
|
16
|
+
return;
|
|
17
|
+
}
|
|
18
|
+
const handler = handlers[rawPath];
|
|
19
|
+
if (!handler) {
|
|
20
|
+
setCorsHeaders(res);
|
|
21
|
+
res.statusCode = 404;
|
|
22
|
+
res.setHeader('Content-Type', 'application/json');
|
|
23
|
+
res.end(JSON.stringify({ message: `No handler for ${rawPath}` }));
|
|
24
|
+
return;
|
|
25
|
+
}
|
|
26
|
+
try {
|
|
27
|
+
const event = await buildEvent(req, rawPath, query);
|
|
28
|
+
const response = await handler(event);
|
|
29
|
+
setCorsHeaders(res);
|
|
30
|
+
res.statusCode = response.statusCode;
|
|
31
|
+
res.setHeader('Content-Type', 'application/json');
|
|
32
|
+
let body = response.body ?? '';
|
|
33
|
+
if (response.headers?.['Content-Encoding'] === 'gzip') {
|
|
34
|
+
body = decompressResponse(body);
|
|
35
|
+
}
|
|
36
|
+
res.end(body);
|
|
37
|
+
}
|
|
38
|
+
catch (err) {
|
|
39
|
+
console.error(`Error handling ${rawPath}:`, err);
|
|
40
|
+
setCorsHeaders(res);
|
|
41
|
+
res.statusCode = 500;
|
|
42
|
+
res.setHeader('Content-Type', 'application/json');
|
|
43
|
+
res.end(JSON.stringify({ message: 'Internal server error' }));
|
|
44
|
+
}
|
|
45
|
+
});
|
|
46
|
+
server.listen(port, () => {
|
|
47
|
+
console.log(`\ndev-backend listening on http://localhost:${port}/`);
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
function setCorsHeaders(res) {
|
|
51
|
+
res.setHeader('Access-Control-Allow-Origin', '*');
|
|
52
|
+
res.setHeader('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, PATCH, OPTIONS');
|
|
53
|
+
res.setHeader('Access-Control-Allow-Headers', 'Content-Type, Authorization');
|
|
54
|
+
}
|
|
55
|
+
//# sourceMappingURL=server.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"server.js","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"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"}
|
package/package.json
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@smplcty/dev-backend",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Local Lambda dev server — reads a routes config, loads .env, serves Lambda handlers over HTTP with API Gateway v2 event translation.",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"main": "./dist/index.js",
|
|
8
|
+
"bin": {
|
|
9
|
+
"dev-backend": "./dist/cli.js"
|
|
10
|
+
},
|
|
11
|
+
"files": [
|
|
12
|
+
"dist",
|
|
13
|
+
"README.md",
|
|
14
|
+
"LICENSE"
|
|
15
|
+
],
|
|
16
|
+
"publishConfig": {
|
|
17
|
+
"access": "public",
|
|
18
|
+
"registry": "https://registry.npmjs.org/"
|
|
19
|
+
},
|
|
20
|
+
"repository": {
|
|
21
|
+
"type": "git",
|
|
22
|
+
"url": "git+https://github.com/mabulu-inc/dev-backend.git"
|
|
23
|
+
},
|
|
24
|
+
"homepage": "https://github.com/mabulu-inc/dev-backend#readme",
|
|
25
|
+
"bugs": {
|
|
26
|
+
"url": "https://github.com/mabulu-inc/dev-backend/issues"
|
|
27
|
+
},
|
|
28
|
+
"keywords": [
|
|
29
|
+
"lambda",
|
|
30
|
+
"dev-server",
|
|
31
|
+
"api-gateway",
|
|
32
|
+
"local"
|
|
33
|
+
],
|
|
34
|
+
"engines": {
|
|
35
|
+
"node": ">=20"
|
|
36
|
+
},
|
|
37
|
+
"devDependencies": {
|
|
38
|
+
"@eslint/js": "^10.0.1",
|
|
39
|
+
"@types/node": "^25.5.2",
|
|
40
|
+
"eslint": "^10.2.0",
|
|
41
|
+
"typescript": "^6.0.2",
|
|
42
|
+
"typescript-eslint": "^8.58.1",
|
|
43
|
+
"vitest": "^4.1.4"
|
|
44
|
+
},
|
|
45
|
+
"scripts": {
|
|
46
|
+
"build": "tsc -p tsconfig.build.json",
|
|
47
|
+
"clean": "rm -rf dist",
|
|
48
|
+
"lint": "eslint src",
|
|
49
|
+
"typecheck": "tsc --noEmit",
|
|
50
|
+
"test": "vitest run",
|
|
51
|
+
"test:watch": "vitest"
|
|
52
|
+
}
|
|
53
|
+
}
|