@niledatabase/express 5.0.0-alpha.15 → 5.0.0-alpha.16

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@niledatabase/express",
3
- "version": "5.0.0-alpha.15",
3
+ "version": "5.0.0-alpha.16",
4
4
  "license": "MIT",
5
5
  "main": "./dist/index.js",
6
6
  "module": "./dist/index.mjs",
@@ -17,6 +17,9 @@
17
17
  "singleQuote": true,
18
18
  "trailingComma": "es5"
19
19
  },
20
+ "files": [
21
+ "dist"
22
+ ],
20
23
  "scripts": {
21
24
  "build": "tsup src/index.ts",
22
25
  "test": "jest"
@@ -40,5 +43,5 @@
40
43
  "ts-jest": "^29.3.4",
41
44
  "tsup": "^8.5.0"
42
45
  },
43
- "gitHead": "5838393fd8739e388754a43b0efcd8430d6d6027"
46
+ "gitHead": "b96e22d67b78a2a0d6d8027dea4973272a1ce94e"
44
47
  }
package/CHANGELOG.md DELETED
@@ -1,58 +0,0 @@
1
- # Change Log
2
-
3
- All notable changes to this project will be documented in this file.
4
- See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
5
-
6
- # [5.0.0-alpha.15](https://github.com/niledatabase/nile-js/compare/v5.0.0-alpha.14...v5.0.0-alpha.15) (2025-06-25)
7
-
8
- ### Bug Fixes
9
-
10
- - **server:** add better extensions ([2dd7417](https://github.com/niledatabase/nile-js/commit/2dd741726e2336b042d06a36ad091e6749098454))
11
-
12
- # [5.0.0-alpha.14](https://github.com/niledatabase/nile-js/compare/v5.0.0-alpha.13...v5.0.0-alpha.14) (2025-06-16)
13
-
14
- **Note:** Version bump only for package @niledatabase/express
15
-
16
- # [5.0.0-alpha.13](https://github.com/niledatabase/nile-js/compare/v5.0.0-alpha.12...v5.0.0-alpha.13) (2025-06-16)
17
-
18
- **Note:** Version bump only for package @niledatabase/express
19
-
20
- # [5.0.0-alpha.12](https://github.com/niledatabase/nile-js/compare/v5.0.0-alpha.11...v5.0.0-alpha.12) (2025-06-16)
21
-
22
- **Note:** Version bump only for package @niledatabase/express
23
-
24
- # [5.0.0-alpha.11](https://github.com/niledatabase/nile-js/compare/v5.0.0-alpha.10...v5.0.0-alpha.11) (2025-06-16)
25
-
26
- **Note:** Version bump only for package @niledatabase/express
27
-
28
- # [5.0.0-alpha.10](https://github.com/niledatabase/nile-js/compare/v5.0.0-alpha.9...v5.0.0-alpha.10) (2025-06-16)
29
-
30
- **Note:** Version bump only for package @niledatabase/express
31
-
32
- # [5.0.0-alpha.9](https://github.com/niledatabase/nile-js/compare/v5.0.0-alpha.8...v5.0.0-alpha.9) (2025-06-16)
33
-
34
- **Note:** Version bump only for package @niledatabase/express
35
-
36
- # [5.0.0-alpha.8](https://github.com/niledatabase/nile-js/compare/v5.0.0-alpha.7...v5.0.0-alpha.8) (2025-06-12)
37
-
38
- **Note:** Version bump only for package @niledatabase/express
39
-
40
- # [5.0.0-alpha.7](https://github.com/niledatabase/nile-js/compare/v5.0.0-alpha.6...v5.0.0-alpha.7) (2025-06-11)
41
-
42
- **Note:** Version bump only for package @niledatabase/express
43
-
44
- # [5.0.0-alpha.6](https://github.com/niledatabase/nile-js/compare/v5.0.0-alpha.5...v5.0.0-alpha.6) (2025-06-11)
45
-
46
- ### Bug Fixes
47
-
48
- - **server:** move logging to the config object ([6e3e380](https://github.com/niledatabase/nile-js/commit/6e3e38014f5b9795552af0a7b97e2d3fe6cd1a88))
49
-
50
- # [5.0.0-alpha.5](https://github.com/niledatabase/nile-js/compare/v5.0.0-alpha.4...v5.0.0-alpha.5) (2025-06-10)
51
-
52
- **Note:** Version bump only for package @niledatabase/express
53
-
54
- # [5.0.0-alpha.4](https://github.com/niledatabase/nile-js/compare/v5.0.0-alpha.3...v5.0.0-alpha.4) (2025-06-10)
55
-
56
- ### Features
57
-
58
- - **server:** invites ([c10f598](https://github.com/niledatabase/nile-js/commit/c10f5980bcbc55ed436ce8cceca4111aa8e6a276))
package/jest.config.js DELETED
@@ -1,6 +0,0 @@
1
- // eslint-disable-next-line @typescript-eslint/no-var-requires
2
-
3
- module.exports = {
4
- preset: 'ts-jest',
5
- testEnvironment: 'node',
6
- };
@@ -1,166 +0,0 @@
1
- import express from 'express';
2
- import { ExtensionState, Server } from '@niledatabase/server';
3
- import type {
4
- Request as ExpressRequest,
5
- Response as ExpressResponse,
6
- } from 'express';
7
-
8
- import { express as expressExtension, cleaner } from '.';
9
-
10
- describe('express extension', () => {
11
- let app;
12
- let instance: Server;
13
-
14
- beforeEach(() => {
15
- app = express();
16
- app.use(express.json());
17
-
18
- instance = {
19
- logger: () => ({
20
- error: jest.fn(),
21
- debug: jest.fn(),
22
- warn: jest.fn(),
23
- info: jest.fn(),
24
- }),
25
- handlers: {
26
- GET: jest
27
- .fn()
28
- .mockResolvedValue(
29
- new Response(JSON.stringify({ msg: 'GET OK' }), { status: 200 })
30
- ),
31
- POST: jest
32
- .fn()
33
- .mockResolvedValue(
34
- new Response(JSON.stringify({ msg: 'POST OK' }), { status: 201 })
35
- ),
36
- },
37
- setContext: jest.fn(),
38
- paths: {
39
- get: ['/test/{id}'],
40
- post: ['/submit/{formId}'],
41
- put: [],
42
- delete: [],
43
- },
44
- } as unknown as Server;
45
- });
46
-
47
- describe('cleaner()', () => {
48
- it('replaces {param} with :param', () => {
49
- expect(cleaner('/api/{tenantId}/resource')).toBe(
50
- '/api/:tenantId/resource'
51
- );
52
- });
53
- });
54
-
55
- describe('onSetContext', () => {
56
- it('sets context from params and headers', () => {
57
- const ext = expressExtension(instance);
58
- const req = {
59
- params: { tenantId: 'abc' },
60
- headers: { 'x-foo': 'bar' },
61
- } as unknown as ExpressRequest;
62
- const res = {} as unknown as ExpressResponse;
63
- const next = jest.fn();
64
-
65
- ext.onSetContext(req, res, next);
66
-
67
- expect(instance.setContext).toHaveBeenCalledWith({ tenantId: 'abc' });
68
- expect(instance.setContext).toHaveBeenCalledWith(req.headers);
69
- expect(next).toHaveBeenCalled();
70
- });
71
- });
72
-
73
- describe('onConfigure', () => {
74
- it('cleans up path params and updates instance.paths', () => {
75
- const ext = expressExtension(instance);
76
- ext.onConfigure();
77
- expect(instance.paths).toEqual({
78
- get: ['/test/:id'],
79
- post: ['/submit/:formId'],
80
- put: [],
81
- delete: [],
82
- });
83
- });
84
- });
85
-
86
- describe('onHandleRequest', () => {
87
- it('proxies GET request with JSON response', async () => {
88
- const ext = expressExtension(instance);
89
- const req = {
90
- method: 'GET',
91
- protocol: 'http',
92
- get: () => 'localhost',
93
- originalUrl: '/api/test',
94
- headers: { cookie: 'a=b' },
95
- body: {},
96
- } as unknown as ExpressRequest;
97
- const res = {
98
- headersSent: false,
99
- status: jest.fn().mockReturnThis(),
100
- set: jest.fn().mockReturnThis(),
101
- json: jest.fn(),
102
- send: jest.fn(),
103
- } as unknown as ExpressResponse;
104
-
105
- const result = await ext.onHandleRequest(req, res);
106
-
107
- expect(instance.setContext).toHaveBeenCalledWith(req.headers);
108
- expect(instance.handlers.GET).toHaveBeenCalled();
109
- expect(res.status).toHaveBeenCalledWith(200);
110
- expect(res.json).toHaveBeenCalledWith({ msg: 'GET OK' });
111
- expect(result).toBe(ExtensionState.onHandleRequest);
112
- });
113
-
114
- it('handles non-JSON response fallback', async () => {
115
- (instance.handlers.GET as jest.Mock).mockResolvedValue(
116
- new Response('Plain text body', { status: 200 })
117
- );
118
- const ext = expressExtension(instance);
119
-
120
- const req = {
121
- method: 'GET',
122
- protocol: 'http',
123
- get: () => 'localhost',
124
- originalUrl: '/api/test',
125
- headers: {},
126
- body: {},
127
- } as unknown as ExpressRequest;
128
- const res = {
129
- headersSent: false,
130
- status: jest.fn().mockReturnThis(),
131
- set: jest.fn().mockReturnThis(),
132
- json: jest.fn(),
133
- send: jest.fn(),
134
- } as unknown as ExpressResponse;
135
-
136
- await ext.onHandleRequest(req, res);
137
-
138
- expect(res.send).toHaveBeenCalledWith('Plain text body');
139
- });
140
-
141
- it('does not re-send headers if already sent', async () => {
142
- const ext = expressExtension(instance);
143
-
144
- const req = {
145
- method: 'GET',
146
- protocol: 'http',
147
- get: () => 'localhost',
148
- originalUrl: '/api/test',
149
- headers: {},
150
- body: {},
151
- } as unknown as ExpressRequest;
152
- const res = {
153
- headersSent: true,
154
- status: jest.fn(),
155
- set: jest.fn(),
156
- json: jest.fn(),
157
- send: jest.fn(),
158
- } as unknown as ExpressResponse;
159
-
160
- const result = await ext.onHandleRequest(req, res);
161
-
162
- expect(res.status).not.toHaveBeenCalled();
163
- expect(result).toBe(ExtensionState.onHandleRequest);
164
- });
165
- });
166
- });
package/src/index.ts DELETED
@@ -1,147 +0,0 @@
1
- import { ExtensionState, Server } from '@niledatabase/server';
2
- import type {
3
- Request as ExpressRequest,
4
- Response as ExpressResponse,
5
- NextFunction,
6
- } from 'express';
7
-
8
- export function cleaner(val: string) {
9
- return val.replaceAll(/\{([^}]+)\}/g, ':$1');
10
- }
11
-
12
- export const express = (instance: Server) => {
13
- const { error, debug } = instance.logger('[EXTENSION][express]');
14
- return {
15
- id: 'express',
16
- onSetContext: (
17
- req: ExpressRequest,
18
- res: ExpressResponse,
19
- next: NextFunction
20
- ) => {
21
- // do this first, since `cookies` does not persist across context set
22
- if (req.params.tenantId) {
23
- instance.setContext({ tenantId: req.params.tenantId });
24
- }
25
-
26
- if (req.headers) {
27
- instance.setContext(req.headers);
28
- }
29
- if (req instanceof Headers) {
30
- instance.setContext(req);
31
- }
32
-
33
- if (typeof next === 'function') {
34
- next();
35
- }
36
- },
37
- onConfigure: () => {
38
- const { paths: nilePaths } = instance;
39
- const paths = {
40
- get: nilePaths.get.map(cleaner),
41
- post: nilePaths.post.map(cleaner),
42
- put: nilePaths.put.map(cleaner),
43
- delete: nilePaths.delete.map(cleaner),
44
- };
45
- debug(`paths configured ${JSON.stringify(paths)}`);
46
- instance.paths = paths;
47
- },
48
- onHandleRequest: async (req: ExpressRequest, res: ExpressResponse) => {
49
- // handle standard request objects
50
- if (req instanceof Request) {
51
- debug('using default response');
52
- const response = await instance.handlers[
53
- req.method as 'GET' | 'POST' | 'PUT' | 'DELETE'
54
- // disable the extension so we don't re-handle
55
- ](req, { disableExtensions: ['express'] });
56
- return response;
57
- }
58
-
59
- debug('handling response');
60
- instance.setContext(req.headers);
61
-
62
- const reqUrl = req.protocol + '://' + req.get('host') + req.originalUrl;
63
-
64
- // be sure its a valid url
65
- try {
66
- new URL(reqUrl);
67
- } catch (e) {
68
- throw new Error(
69
- 'Invalid URL sent for handle request. Are you running express?'
70
- );
71
- }
72
- const { method } = req;
73
- const init: RequestInit = { method, headers: new Headers() };
74
- // seems like this should work without this, since `get/set context should do the things we think it should.
75
- if (
76
- 'headers' in req &&
77
- typeof req.headers === 'object' &&
78
- req.headers &&
79
- 'cookie' in req.headers &&
80
- typeof req.headers.cookie === 'string'
81
- ) {
82
- (init.headers as Headers).set('cookie', req.headers.cookie);
83
- }
84
-
85
- if ('body' in req) {
86
- if (method === 'POST' || method === 'PUT') {
87
- init.body = JSON.stringify(req.body);
88
- }
89
- }
90
-
91
- const proxyRequest = new Request(reqUrl, init);
92
- debug(
93
- `[${method.toUpperCase()}]proxy request converted to ${reqUrl} with ${JSON.stringify(
94
- init
95
- )}`
96
- );
97
-
98
- let response: Response = null as unknown as Response;
99
-
100
- try {
101
- response = (await instance.handlers[
102
- req.method as 'GET' | 'POST' | 'PUT' | 'DELETE'
103
- ](proxyRequest, { disableExtensions: ['express'] })) as Response;
104
- } catch (e) {
105
- error(e);
106
- }
107
-
108
- let body;
109
- try {
110
- const tryJson = await response.clone();
111
- body = await tryJson.json();
112
- } catch (e) {
113
- body = await response.text();
114
- }
115
-
116
- const newHeaders: Record<string, string | string[]> = {};
117
- response.headers.forEach((value, key) => {
118
- if (
119
- !['content-length', 'transfer-encoding'].includes(key.toLowerCase())
120
- ) {
121
- if (newHeaders[key]) {
122
- const prev = newHeaders[key];
123
- if (Array.isArray(prev)) {
124
- newHeaders[key] = [...prev, value];
125
- } else {
126
- newHeaders[key] = [prev, value];
127
- }
128
- } else {
129
- newHeaders[key] = value;
130
- }
131
- }
132
- });
133
-
134
- if (!res.headersSent) {
135
- debug('sending response');
136
- res.status(response.status).set(newHeaders);
137
- if (typeof body === 'string') {
138
- res.send(body);
139
- } else {
140
- res.json(body ?? {});
141
- }
142
- }
143
- // always return to prevent infinite loop
144
- return ExtensionState.onHandleRequest;
145
- },
146
- };
147
- };
package/tsup.config.js DELETED
@@ -1,9 +0,0 @@
1
- import { defineConfig } from 'tsup';
2
-
3
- export default defineConfig({
4
- minify: true,
5
- target: 'es2022',
6
- sourcemap: true,
7
- dts: true,
8
- format: ['esm', 'cjs'],
9
- });