typed-bridge 1.0.0 → 2.0.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/.vscode/settings.json +3 -0
- package/context.md +168 -0
- package/dist/bridge/index.d.ts +19 -6
- package/dist/bridge/index.js +110 -12
- package/dist/config/index.d.ts +0 -2
- package/dist/config/index.js +1 -3
- package/dist/demo/bridge/index.d.ts +10 -1
- package/dist/demo/bridge/index.js +3 -1
- package/dist/demo/bridge/user/index.d.ts +15 -1
- package/dist/demo/bridge/user/index.js +29 -4
- package/dist/demo/bridge/user/types.d.ts +11 -11
- package/dist/demo/bridge/user/types.js +8 -8
- package/dist/demo/index.js +6 -21
- package/dist/helpers/index.d.ts +1 -0
- package/dist/helpers/index.js +24 -7
- package/dist/index.d.ts +1 -3
- package/dist/index.js +4 -6
- package/dist/scripts/cli.js +2 -1
- package/dist/scripts/typedBridgeCleaner.js +8 -3
- package/dist/server/index.js +0 -3
- package/docs/auto-bridge-sync.md +56 -0
- package/package.json +63 -61
- package/readme.md +233 -65
- package/test/bridge.ts +76 -0
- package/test/index.ts +38 -0
- package/tmp/add-middleware.md +17 -0
- package/tmp/bridge.ts +40 -0
- package/tmp/configuration.md +46 -0
- package/tmp/context-setup.md +38 -0
- package/tmp/request-validation.md +24 -0
- package/dist/app/index.d.ts +0 -3
- package/dist/app/index.js +0 -71
package/context.md
ADDED
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
# **Using `typed-bridge` to Build a Backend**
|
|
2
|
+
|
|
3
|
+
## **What is `typed-bridge`?**
|
|
4
|
+
|
|
5
|
+
`typed-bridge` is an open‑source TypeScript package that lets you define **strictly typed server functions** and automatically generate a matching **typed client** for your frontend.
|
|
6
|
+
It removes the boilerplate of REST/GraphQL by letting you call server functions like local functions — while keeping **end‑to‑end type safety** between backend and frontend.
|
|
7
|
+
It’s framework‑agnostic, works with any frontend (React, Vue, Angular, RN, etc.), supports middleware for auth/validation/logging, and has built‑in graceful shutdown and request logging.
|
|
8
|
+
|
|
9
|
+
**Why use it?**
|
|
10
|
+
|
|
11
|
+
- Prevents API spec drift between backend and frontend
|
|
12
|
+
- Strong TypeScript type checking across the stack
|
|
13
|
+
- Simple middleware for auth, validation, and context sharing
|
|
14
|
+
- No need to hand‑roll REST/GraphQL routes
|
|
15
|
+
|
|
16
|
+
---
|
|
17
|
+
|
|
18
|
+
## **How to Start a Server**
|
|
19
|
+
|
|
20
|
+
```ts
|
|
21
|
+
// src/server.ts
|
|
22
|
+
import { createBridge, tbConfig } from 'typed-bridge'
|
|
23
|
+
import bridge from './bridge/index.js'
|
|
24
|
+
import './middleware/auth.js' // optional middleware
|
|
25
|
+
|
|
26
|
+
// Optional logging
|
|
27
|
+
tbConfig.logs.request = true
|
|
28
|
+
tbConfig.logs.response = true
|
|
29
|
+
tbConfig.logs.error = true
|
|
30
|
+
|
|
31
|
+
createBridge(bridge, 8080, '/bridge')
|
|
32
|
+
console.log(`Bridge running at http://localhost:8080/bridge`)
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
---
|
|
36
|
+
|
|
37
|
+
## **How to Define Routes**
|
|
38
|
+
|
|
39
|
+
```ts
|
|
40
|
+
// src/bridge/index.ts
|
|
41
|
+
import * as user from './user/index.js'
|
|
42
|
+
|
|
43
|
+
export default {
|
|
44
|
+
'user.fetch': user.fetch,
|
|
45
|
+
'user.update': user.update,
|
|
46
|
+
'user.fetchAll': user.fetchAll
|
|
47
|
+
}
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
---
|
|
51
|
+
|
|
52
|
+
## **How to Write Handlers**
|
|
53
|
+
|
|
54
|
+
```ts
|
|
55
|
+
// src/bridge/user/index.ts
|
|
56
|
+
export interface User {
|
|
57
|
+
id: number
|
|
58
|
+
name: string
|
|
59
|
+
email: string
|
|
60
|
+
createdAt: Date
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
const users: User[] = [
|
|
64
|
+
{ id: 1, name: 'Neil', email: 'neil@example.com', createdAt: new Date() },
|
|
65
|
+
{ id: 2, name: 'John', email: 'john@example.com', createdAt: new Date() },
|
|
66
|
+
{ id: 3, name: 'Jane', email: 'jane@example.com', createdAt: new Date() }
|
|
67
|
+
]
|
|
68
|
+
|
|
69
|
+
export const fetch = async (args: { id: number }): Promise<User> => {
|
|
70
|
+
const user = users.find(u => u.id === args.id)
|
|
71
|
+
if (!user) throw new Error(`User ${args.id} not found`)
|
|
72
|
+
return user
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
export const update = async (args: { id: number; name?: string; email?: string }): Promise<User> => {
|
|
76
|
+
const user = users.find(u => u.id === args.id)
|
|
77
|
+
if (!user) throw new Error(`User ${args.id} not found`)
|
|
78
|
+
if (args.name) user.name = args.name
|
|
79
|
+
if (args.email) user.email = args.email
|
|
80
|
+
return user
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
export const fetchAll = async (): Promise<User[]> => users
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
---
|
|
87
|
+
|
|
88
|
+
## **How to Add Middleware**
|
|
89
|
+
|
|
90
|
+
```ts
|
|
91
|
+
// src/middleware/auth.ts
|
|
92
|
+
import { createMiddleware } from 'typed-bridge'
|
|
93
|
+
|
|
94
|
+
createMiddleware('user.*', async (req, res) => {
|
|
95
|
+
if (req.headers.authorization !== 'Bearer 123') {
|
|
96
|
+
res.status(401).send('Unauthorized')
|
|
97
|
+
return { next: false } // Stop processing
|
|
98
|
+
}
|
|
99
|
+
return { context: { userId: 999 } }
|
|
100
|
+
})
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
---
|
|
104
|
+
|
|
105
|
+
## **How to Validate with `$z` (Zod)**
|
|
106
|
+
|
|
107
|
+
```ts
|
|
108
|
+
// src/bridge/user/types.ts
|
|
109
|
+
import { $z } from 'typed-bridge'
|
|
110
|
+
|
|
111
|
+
export const fetch = {
|
|
112
|
+
args: $z.object({ id: $z.number().min(1) }),
|
|
113
|
+
res: $z.object({
|
|
114
|
+
id: $z.number(),
|
|
115
|
+
name: $z.string(),
|
|
116
|
+
email: $z.string().email(),
|
|
117
|
+
createdAt: $z.date()
|
|
118
|
+
})
|
|
119
|
+
}
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
```ts
|
|
123
|
+
// src/bridge/user/index.ts (validated version)
|
|
124
|
+
import { $z } from 'typed-bridge'
|
|
125
|
+
import * as types from './types.js'
|
|
126
|
+
|
|
127
|
+
export const fetch = async (
|
|
128
|
+
args: $z.infer<typeof types.fetch.args>,
|
|
129
|
+
context: { userId?: number }
|
|
130
|
+
): Promise<$z.infer<typeof types.fetch.res>> => {
|
|
131
|
+
args = types.fetch.args.parse(args)
|
|
132
|
+
const user = users.find(u => u.id === args.id)
|
|
133
|
+
if (!user) throw new Error(`User ${args.id} not found`)
|
|
134
|
+
return user
|
|
135
|
+
}
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
---
|
|
139
|
+
|
|
140
|
+
## **How to Generate a Typed Client**
|
|
141
|
+
|
|
142
|
+
```bash
|
|
143
|
+
npm run gen:typed-bridge-client
|
|
144
|
+
# => creates ./bridge.ts
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
---
|
|
148
|
+
|
|
149
|
+
## **How to Use the Generated Client (Frontend)**
|
|
150
|
+
|
|
151
|
+
```ts
|
|
152
|
+
import bridge, { bridgeConfig } from '../path-to-backend/bridge'
|
|
153
|
+
|
|
154
|
+
bridgeConfig.host = 'http://localhost:8080/bridge'
|
|
155
|
+
bridgeConfig.headers = { 'Content-Type': 'application/json', Authorization: 'Bearer 123' }
|
|
156
|
+
|
|
157
|
+
const user = await bridge['user.fetch']({ id: 1 })
|
|
158
|
+
console.log(user)
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
---
|
|
162
|
+
|
|
163
|
+
## **How to Add a New Route (Checklist)**
|
|
164
|
+
|
|
165
|
+
1. Create handler in `src/bridge/<module>/index.ts`
|
|
166
|
+
2. Add `'module.method': handler` to `src/bridge/index.ts`
|
|
167
|
+
3. (If validating) add types in `src/bridge/<module>/types.ts`
|
|
168
|
+
4. Run `npm run gen:typed-bridge-client` to refresh the client
|
package/dist/bridge/index.d.ts
CHANGED
|
@@ -1,6 +1,19 @@
|
|
|
1
|
-
import { Request, Response } from 'express';
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
}
|
|
6
|
-
|
|
1
|
+
import { Application, Request, Response } from 'express';
|
|
2
|
+
import { Server } from 'http';
|
|
3
|
+
type Bridge = {
|
|
4
|
+
[key: string]: (...args: any[]) => Promise<any>;
|
|
5
|
+
};
|
|
6
|
+
type Middleware = {
|
|
7
|
+
pattern: string;
|
|
8
|
+
handler: (req: Request, res: Response) => Promise<{
|
|
9
|
+
next?: boolean;
|
|
10
|
+
context?: any;
|
|
11
|
+
} | void>;
|
|
12
|
+
};
|
|
13
|
+
export declare const createMiddleware: (pattern: string, handler: Middleware["handler"]) => number;
|
|
14
|
+
export declare const onShutdown: (fn: () => void) => () => void;
|
|
15
|
+
export declare const createBridge: (bridge: Bridge, port: number, path?: string) => {
|
|
16
|
+
app: Application;
|
|
17
|
+
server: Server;
|
|
18
|
+
};
|
|
19
|
+
export {};
|
package/dist/bridge/index.js
CHANGED
|
@@ -1,23 +1,121 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
2
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
|
|
4
|
-
|
|
6
|
+
exports.createBridge = exports.onShutdown = exports.createMiddleware = void 0;
|
|
7
|
+
const helpers_1 = require("@/helpers");
|
|
8
|
+
const chalk_1 = __importDefault(require("chalk"));
|
|
9
|
+
const compression_1 = __importDefault(require("compression"));
|
|
10
|
+
const cors_1 = __importDefault(require("cors"));
|
|
11
|
+
const express_1 = __importDefault(require("express"));
|
|
12
|
+
const path_1 = __importDefault(require("path"));
|
|
13
|
+
const __1 = require("..");
|
|
14
|
+
const middlewares = [];
|
|
15
|
+
const createMiddleware = (pattern, handler) => middlewares.push({ pattern, handler });
|
|
16
|
+
exports.createMiddleware = createMiddleware;
|
|
17
|
+
let shutdownCallback = () => { };
|
|
18
|
+
const onShutdown = (fn) => (shutdownCallback = fn);
|
|
19
|
+
exports.onShutdown = onShutdown;
|
|
20
|
+
const createBridge = (bridge, port, path = '/bridge') => {
|
|
21
|
+
const app = (0, express_1.default)();
|
|
22
|
+
// cors
|
|
23
|
+
app.use((0, cors_1.default)());
|
|
24
|
+
// Compression
|
|
25
|
+
app.use((0, compression_1.default)());
|
|
26
|
+
// Body parser
|
|
27
|
+
app.use(express_1.default.json());
|
|
28
|
+
app.use(express_1.default.urlencoded({ extended: true }));
|
|
29
|
+
// Typed bridge middleware
|
|
30
|
+
let requestId = 1;
|
|
31
|
+
app.use((req, res, next) => {
|
|
32
|
+
const _req = req;
|
|
33
|
+
const xForwardedFor = req.headers['x-forwarded-for'];
|
|
34
|
+
let ip = Array.isArray(xForwardedFor)
|
|
35
|
+
? xForwardedFor[0]
|
|
36
|
+
: (xForwardedFor || '').split(', ')[0] || req.socket.remoteAddress || '';
|
|
37
|
+
if (ip === '::1')
|
|
38
|
+
ip = '127.0.0.1';
|
|
39
|
+
// Bind data
|
|
40
|
+
_req.bind = {
|
|
41
|
+
id: 0,
|
|
42
|
+
ts: Date.now(),
|
|
43
|
+
args: {},
|
|
44
|
+
ip
|
|
45
|
+
};
|
|
46
|
+
// Set typed bridge header
|
|
47
|
+
res.setHeader('X-Powered-By', 'typed-bridge');
|
|
48
|
+
requestId++;
|
|
49
|
+
// Log request
|
|
50
|
+
if (__1.tbConfig.logs.request) {
|
|
51
|
+
console.log(chalk_1.default.blueBright(`REQ | ${new Date().toISOString()} | ${requestId} :: ${req.method} | ${req.path} | ${ip}`));
|
|
52
|
+
}
|
|
53
|
+
// Log response
|
|
54
|
+
const startTime = Date.now();
|
|
55
|
+
res.on('finish', () => {
|
|
56
|
+
const log = `RES | ${new Date().toISOString()} | ${requestId} :: ${res.statusCode} | ${Date.now() - startTime}ms`;
|
|
57
|
+
if (__1.tbConfig.logs.response)
|
|
58
|
+
console.log(res.statusCode < 400 ? chalk_1.default.green(log) : chalk_1.default.red(log));
|
|
59
|
+
});
|
|
60
|
+
next();
|
|
61
|
+
});
|
|
62
|
+
// Handle invalid json in post request
|
|
63
|
+
app.use((error, req, res, next) => {
|
|
64
|
+
if (error?.type === 'entity.parse.failed') {
|
|
65
|
+
res.status(400).send('Can not parse request!');
|
|
66
|
+
}
|
|
67
|
+
else
|
|
68
|
+
next();
|
|
69
|
+
});
|
|
70
|
+
// Custom responseDelay
|
|
71
|
+
if (__1.tbConfig.responseDelay)
|
|
72
|
+
app.use((req, res, next) => {
|
|
73
|
+
setTimeout(next, __1.tbConfig.responseDelay);
|
|
74
|
+
});
|
|
75
|
+
// Server health
|
|
76
|
+
app.use(path_1.default.join(path, 'health'), (req, res) => {
|
|
77
|
+
res.sendStatus(200);
|
|
78
|
+
});
|
|
79
|
+
app.use(path, bridgeHandler(bridge));
|
|
80
|
+
const server = app.listen(port, () => (0, helpers_1.printStartLogs)(port));
|
|
81
|
+
let shuttingDown = false;
|
|
82
|
+
const shutdown = () => {
|
|
83
|
+
if (shuttingDown)
|
|
84
|
+
return;
|
|
85
|
+
shuttingDown = true;
|
|
86
|
+
server.close();
|
|
87
|
+
(0, helpers_1.printStopLogs)();
|
|
88
|
+
shutdownCallback();
|
|
89
|
+
};
|
|
90
|
+
process.on('SIGINT', () => shutdown());
|
|
91
|
+
process.on('SIGTERM', () => shutdown());
|
|
92
|
+
return { app, server };
|
|
93
|
+
};
|
|
94
|
+
exports.createBridge = createBridge;
|
|
95
|
+
const bridgeHandler = (bridge) => async (req, res) => {
|
|
5
96
|
try {
|
|
6
97
|
const path = req.path.split('/').pop() || '';
|
|
7
98
|
const args = req.body;
|
|
8
99
|
if (!path)
|
|
9
100
|
throw new Error('Bridge not found!');
|
|
10
101
|
const serverFunction = bridge[path];
|
|
11
|
-
if (!serverFunction)
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
102
|
+
if (!serverFunction) {
|
|
103
|
+
const error = 'Bridge not found: ' + path;
|
|
104
|
+
if (__1.tbConfig.logs.error)
|
|
105
|
+
console.error(error);
|
|
106
|
+
return res.status(404).json({ error });
|
|
107
|
+
}
|
|
108
|
+
let context = {};
|
|
109
|
+
for (const middleware of middlewares) {
|
|
110
|
+
if ((0, helpers_1.matchesPattern)(path, middleware.pattern)) {
|
|
111
|
+
const result = await middleware.handler(req, res);
|
|
112
|
+
if (result?.next === false)
|
|
113
|
+
return;
|
|
114
|
+
if (result?.context)
|
|
115
|
+
context = { ...context, ...result.context };
|
|
116
|
+
}
|
|
19
117
|
}
|
|
20
|
-
res.json(await serverFunction(args, context));
|
|
118
|
+
res.json((await serverFunction(args, context)) || {});
|
|
21
119
|
}
|
|
22
120
|
catch (error) {
|
|
23
121
|
if (Array.isArray(error.errors)) {
|
|
@@ -25,7 +123,7 @@ exports.default = (bridge, contextParser) => async (req, res) => {
|
|
|
25
123
|
const errorMessage = (keyPath ? keyPath + ': ' : '') + error.errors[0].message;
|
|
26
124
|
return res.status(400).send(errorMessage);
|
|
27
125
|
}
|
|
28
|
-
if (
|
|
126
|
+
if (__1.tbConfig.logs.error)
|
|
29
127
|
console.error(error);
|
|
30
128
|
return res.status(500).json({ error: error.message });
|
|
31
129
|
}
|
package/dist/config/index.d.ts
CHANGED
package/dist/config/index.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import * as user from './user';
|
|
1
2
|
declare const _default: {
|
|
2
3
|
'user.fetch': (args: import("zod").TypeOf<import("zod").ZodObject<{
|
|
3
4
|
id: import("zod").ZodNumber;
|
|
@@ -5,7 +6,9 @@ declare const _default: {
|
|
|
5
6
|
id: number;
|
|
6
7
|
}, {
|
|
7
8
|
id: number;
|
|
8
|
-
}>>, context:
|
|
9
|
+
}>>, context: {
|
|
10
|
+
id: number;
|
|
11
|
+
}) => Promise<import("zod").TypeOf<import("zod").ZodObject<{
|
|
9
12
|
id: import("zod").ZodNumber;
|
|
10
13
|
name: import("zod").ZodString;
|
|
11
14
|
}, "strip", import("zod").ZodTypeAny, {
|
|
@@ -15,5 +18,11 @@ declare const _default: {
|
|
|
15
18
|
id: number;
|
|
16
19
|
name: string;
|
|
17
20
|
}>>>;
|
|
21
|
+
'user.update': (args: {
|
|
22
|
+
id: number;
|
|
23
|
+
name?: string;
|
|
24
|
+
email?: string;
|
|
25
|
+
}) => Promise<user.User>;
|
|
26
|
+
'user.fetchAll': () => Promise<user.User[]>;
|
|
18
27
|
};
|
|
19
28
|
export default _default;
|
|
@@ -35,5 +35,7 @@ var __importStar = (this && this.__importStar) || (function () {
|
|
|
35
35
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
36
|
const user = __importStar(require("./user"));
|
|
37
37
|
exports.default = {
|
|
38
|
-
'user.fetch': user.fetch
|
|
38
|
+
'user.fetch': user.fetch,
|
|
39
|
+
'user.update': user.update,
|
|
40
|
+
'user.fetchAll': user.fetchAll
|
|
39
41
|
};
|
|
@@ -1,3 +1,17 @@
|
|
|
1
1
|
import { $z } from '@/index';
|
|
2
2
|
import * as types from './types';
|
|
3
|
-
export
|
|
3
|
+
export interface User {
|
|
4
|
+
id: number;
|
|
5
|
+
name: string;
|
|
6
|
+
email: string;
|
|
7
|
+
createdAt: Date;
|
|
8
|
+
}
|
|
9
|
+
export declare const fetch: (args: $z.infer<typeof types.fetch.args>, context: {
|
|
10
|
+
id: number;
|
|
11
|
+
}) => Promise<$z.infer<typeof types.fetch.res>>;
|
|
12
|
+
export declare const update: (args: {
|
|
13
|
+
id: number;
|
|
14
|
+
name?: string;
|
|
15
|
+
email?: string;
|
|
16
|
+
}) => Promise<User>;
|
|
17
|
+
export declare const fetchAll: () => Promise<User[]>;
|
|
@@ -33,11 +33,36 @@ var __importStar = (this && this.__importStar) || (function () {
|
|
|
33
33
|
};
|
|
34
34
|
})();
|
|
35
35
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
-
exports.fetch = void 0;
|
|
36
|
+
exports.fetchAll = exports.update = exports.fetch = void 0;
|
|
37
37
|
const types = __importStar(require("./types"));
|
|
38
|
+
const users = [
|
|
39
|
+
{ id: 1, name: 'Neil', email: 'neil@example.com', createdAt: new Date() },
|
|
40
|
+
{ id: 2, name: 'John', email: 'john@example.com', createdAt: new Date() },
|
|
41
|
+
{ id: 3, name: 'Jane', email: 'jane@example.com', createdAt: new Date() }
|
|
42
|
+
];
|
|
38
43
|
const fetch = async (args, context) => {
|
|
39
|
-
types.fetch.args.parse(args);
|
|
40
|
-
console.log(
|
|
41
|
-
|
|
44
|
+
args = types.fetch.args.parse(args);
|
|
45
|
+
console.log(context);
|
|
46
|
+
const user = users.find(user => user.id === args.id);
|
|
47
|
+
if (!user) {
|
|
48
|
+
throw new Error(`User with ID ${args.id} not found`);
|
|
49
|
+
}
|
|
50
|
+
return user;
|
|
42
51
|
};
|
|
43
52
|
exports.fetch = fetch;
|
|
53
|
+
const update = async (args) => {
|
|
54
|
+
const user = users.find(user => user.id === args.id);
|
|
55
|
+
if (!user) {
|
|
56
|
+
throw new Error(`User with ID ${args.id} not found`);
|
|
57
|
+
}
|
|
58
|
+
if (args.name)
|
|
59
|
+
user.name = args.name;
|
|
60
|
+
if (args.email)
|
|
61
|
+
user.email = args.email;
|
|
62
|
+
return user;
|
|
63
|
+
};
|
|
64
|
+
exports.update = update;
|
|
65
|
+
const fetchAll = async () => {
|
|
66
|
+
return users;
|
|
67
|
+
};
|
|
68
|
+
exports.fetchAll = fetchAll;
|
|
@@ -1,16 +1,16 @@
|
|
|
1
|
-
import { z } from '
|
|
1
|
+
import { $z } from '@/index';
|
|
2
2
|
export declare const fetch: {
|
|
3
|
-
args: z.ZodObject<{
|
|
4
|
-
id: z.ZodNumber;
|
|
5
|
-
}, "strip", z.ZodTypeAny, {
|
|
3
|
+
args: $z.ZodObject<{
|
|
4
|
+
id: $z.ZodNumber;
|
|
5
|
+
}, "strip", $z.ZodTypeAny, {
|
|
6
6
|
id: number;
|
|
7
7
|
}, {
|
|
8
8
|
id: number;
|
|
9
9
|
}>;
|
|
10
|
-
res: z.ZodObject<{
|
|
11
|
-
id: z.ZodNumber;
|
|
12
|
-
name: z.ZodString;
|
|
13
|
-
}, "strip", z.ZodTypeAny, {
|
|
10
|
+
res: $z.ZodObject<{
|
|
11
|
+
id: $z.ZodNumber;
|
|
12
|
+
name: $z.ZodString;
|
|
13
|
+
}, "strip", $z.ZodTypeAny, {
|
|
14
14
|
id: number;
|
|
15
15
|
name: string;
|
|
16
16
|
}, {
|
|
@@ -18,9 +18,9 @@ export declare const fetch: {
|
|
|
18
18
|
name: string;
|
|
19
19
|
}>;
|
|
20
20
|
};
|
|
21
|
-
export declare const userContext: z.ZodObject<{
|
|
22
|
-
id: z.ZodNumber;
|
|
23
|
-
}, "strip", z.ZodTypeAny, {
|
|
21
|
+
export declare const userContext: $z.ZodObject<{
|
|
22
|
+
id: $z.ZodNumber;
|
|
23
|
+
}, "strip", $z.ZodTypeAny, {
|
|
24
24
|
id: number;
|
|
25
25
|
}, {
|
|
26
26
|
id: number;
|
|
@@ -1,16 +1,16 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.userContext = exports.fetch = void 0;
|
|
4
|
-
const
|
|
4
|
+
const index_1 = require("@/index");
|
|
5
5
|
exports.fetch = {
|
|
6
|
-
args:
|
|
7
|
-
id:
|
|
6
|
+
args: index_1.$z.object({
|
|
7
|
+
id: index_1.$z.number().min(1)
|
|
8
8
|
}),
|
|
9
|
-
res:
|
|
10
|
-
id:
|
|
11
|
-
name:
|
|
9
|
+
res: index_1.$z.object({
|
|
10
|
+
id: index_1.$z.number(),
|
|
11
|
+
name: index_1.$z.string()
|
|
12
12
|
})
|
|
13
13
|
};
|
|
14
|
-
exports.userContext =
|
|
15
|
-
id:
|
|
14
|
+
exports.userContext = index_1.$z.object({
|
|
15
|
+
id: index_1.$z.number()
|
|
16
16
|
});
|
package/dist/demo/index.js
CHANGED
|
@@ -3,25 +3,10 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
3
3
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
const
|
|
6
|
+
const __1 = require("..");
|
|
7
7
|
const bridge_1 = __importDefault(require("./bridge"));
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
(0,
|
|
13
|
-
const contextParser = async (req, res) => {
|
|
14
|
-
const headers = req.headers;
|
|
15
|
-
const bridge = req.originalUrl.split('/').pop();
|
|
16
|
-
if (bridge === 'test.error') {
|
|
17
|
-
res.sendStatus(400);
|
|
18
|
-
return { next: false };
|
|
19
|
-
}
|
|
20
|
-
return {
|
|
21
|
-
context: {
|
|
22
|
-
name: 'Typed Bridge',
|
|
23
|
-
authorization: headers.authorization || 'NO_AUTH'
|
|
24
|
-
}
|
|
25
|
-
};
|
|
26
|
-
};
|
|
27
|
-
app.use('/bridge', (0, index_1.createBridge)(bridge_1.default, contextParser));
|
|
8
|
+
__1.tbConfig.logs.error = true;
|
|
9
|
+
__1.tbConfig.logs.request = true;
|
|
10
|
+
__1.tbConfig.logs.response = true;
|
|
11
|
+
// tbConfig.responseDelay = 1000
|
|
12
|
+
(0, __1.createBridge)(bridge_1.default, 8080, '/');
|
package/dist/helpers/index.d.ts
CHANGED
package/dist/helpers/index.js
CHANGED
|
@@ -3,21 +3,30 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
3
3
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
exports.printStopLogs = exports.printStartLogs = void 0;
|
|
6
|
+
exports.matchesPattern = exports.printStopLogs = exports.printStartLogs = void 0;
|
|
7
7
|
const chalk_1 = __importDefault(require("chalk"));
|
|
8
8
|
const os_1 = __importDefault(require("os"));
|
|
9
|
-
const __1 = require("..");
|
|
10
9
|
const getLocalIPList = () => {
|
|
11
|
-
const
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
10
|
+
const interfaces = os_1.default.networkInterfaces();
|
|
11
|
+
const ipList = [];
|
|
12
|
+
Object.values(interfaces).forEach(ifaceArr => {
|
|
13
|
+
ifaceArr?.forEach(iface => {
|
|
14
|
+
if (iface.family === 'IPv4' && !iface.internal) {
|
|
15
|
+
ipList.push(iface.address);
|
|
16
|
+
}
|
|
17
|
+
});
|
|
18
|
+
});
|
|
19
|
+
// Always include localhost for convenience
|
|
20
|
+
if (!ipList.includes('127.0.0.1'))
|
|
21
|
+
ipList.unshift('localhost');
|
|
22
|
+
else
|
|
15
23
|
ipList.unshift('localhost');
|
|
16
24
|
return ipList;
|
|
17
25
|
};
|
|
18
26
|
const seperator = '\n-x-x-x-x-x-\n';
|
|
19
27
|
const printStartLogs = (port) => {
|
|
20
28
|
const ipList = getLocalIPList();
|
|
29
|
+
console.log(ipList);
|
|
21
30
|
console.log(seperator);
|
|
22
31
|
console.log(chalk_1.default.bgWhite.black(' Typed Bridge '));
|
|
23
32
|
console.log(seperator);
|
|
@@ -28,7 +37,15 @@ const printStartLogs = (port) => {
|
|
|
28
37
|
exports.printStartLogs = printStartLogs;
|
|
29
38
|
const printStopLogs = () => {
|
|
30
39
|
console.log(seperator);
|
|
31
|
-
console.log(chalk_1.default.red(`Server
|
|
40
|
+
console.log(chalk_1.default.red(`Server stopped at: ` + new Date().toISOString()));
|
|
32
41
|
console.log(seperator);
|
|
33
42
|
};
|
|
34
43
|
exports.printStopLogs = printStopLogs;
|
|
44
|
+
const matchesPattern = (str, pattern) => {
|
|
45
|
+
// Escape regex special chars except "*"
|
|
46
|
+
const escaped = pattern.replace(/[-\\^$+?.()|[\]{}]/g, '\\$&');
|
|
47
|
+
// Replace "*" with ".*" for wildcard matching
|
|
48
|
+
const regexStr = '^' + escaped.replace(/\*/g, '.*') + '$';
|
|
49
|
+
return new RegExp(regexStr).test(str);
|
|
50
|
+
};
|
|
51
|
+
exports.matchesPattern = matchesPattern;
|
package/dist/index.d.ts
CHANGED
|
@@ -1,7 +1,5 @@
|
|
|
1
1
|
import 'dotenv/config';
|
|
2
|
-
export {
|
|
3
|
-
export { default as createBridge } from './bridge';
|
|
2
|
+
export { createBridge, createMiddleware, onShutdown } from './bridge';
|
|
4
3
|
export { config as tbConfig } from './config';
|
|
5
|
-
export { default as startServer } from './server';
|
|
6
4
|
export { default as $z } from 'zod';
|
|
7
5
|
export { Application, default as express, NextFunction, Request, Response, Router } from 'express';
|
package/dist/index.js
CHANGED
|
@@ -3,16 +3,14 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
3
3
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
exports.Router = exports.express = exports.$z = exports.
|
|
6
|
+
exports.Router = exports.express = exports.$z = exports.tbConfig = exports.onShutdown = exports.createMiddleware = exports.createBridge = void 0;
|
|
7
7
|
require("dotenv/config");
|
|
8
|
-
var app_1 = require("./app");
|
|
9
|
-
Object.defineProperty(exports, "createApp", { enumerable: true, get: function () { return __importDefault(app_1).default; } });
|
|
10
8
|
var bridge_1 = require("./bridge");
|
|
11
|
-
Object.defineProperty(exports, "createBridge", { enumerable: true, get: function () { return
|
|
9
|
+
Object.defineProperty(exports, "createBridge", { enumerable: true, get: function () { return bridge_1.createBridge; } });
|
|
10
|
+
Object.defineProperty(exports, "createMiddleware", { enumerable: true, get: function () { return bridge_1.createMiddleware; } });
|
|
11
|
+
Object.defineProperty(exports, "onShutdown", { enumerable: true, get: function () { return bridge_1.onShutdown; } });
|
|
12
12
|
var config_1 = require("./config");
|
|
13
13
|
Object.defineProperty(exports, "tbConfig", { enumerable: true, get: function () { return config_1.config; } });
|
|
14
|
-
var server_1 = require("./server");
|
|
15
|
-
Object.defineProperty(exports, "startServer", { enumerable: true, get: function () { return __importDefault(server_1).default; } });
|
|
16
14
|
var zod_1 = require("zod");
|
|
17
15
|
Object.defineProperty(exports, "$z", { enumerable: true, get: function () { return __importDefault(zod_1).default; } });
|
|
18
16
|
var express_1 = require("express");
|
package/dist/scripts/cli.js
CHANGED
|
@@ -8,7 +8,7 @@ const commander_1 = require("commander");
|
|
|
8
8
|
const buildTypeBridge_1 = __importDefault(require("./buildTypeBridge"));
|
|
9
9
|
const typedBridgeCleaner_1 = __importDefault(require("./typedBridgeCleaner"));
|
|
10
10
|
commander_1.program
|
|
11
|
-
.command('gen-typed-bridge')
|
|
11
|
+
.command('gen-typed-bridge-client')
|
|
12
12
|
.description('Generate a typed bridge')
|
|
13
13
|
.option('--src <string>', 'Set typed bridge source file path')
|
|
14
14
|
.option('--dest <string>', 'Set typed bridge destination file path', 'typedBridge.ts')
|
|
@@ -20,5 +20,6 @@ commander_1.program
|
|
|
20
20
|
throw new Error('--dest required');
|
|
21
21
|
await (0, buildTypeBridge_1.default)(src, dest);
|
|
22
22
|
(0, typedBridgeCleaner_1.default)(dest);
|
|
23
|
+
console.log('\n✅ Bridge generated successfully!\n');
|
|
23
24
|
});
|
|
24
25
|
commander_1.program.parse();
|