@interopio/gateway-server 0.21.0 → 0.22.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/{gateway-server → bin/gateway-server.cjs} +203 -49
- package/changelog.md +12 -0
- package/dist/config.cjs +2 -0
- package/dist/config.cjs.map +7 -0
- package/dist/config.js +2 -0
- package/dist/config.js.map +7 -0
- package/dist/index.cjs +17 -3
- package/dist/index.cjs.map +4 -4
- package/dist/index.js +17 -3
- package/dist/index.js.map +4 -4
- package/dist/metrics/publisher/rest.js +1 -1
- package/dist/metrics/publisher/rest.js.map +3 -3
- package/dist/tools.cjs +2 -0
- package/dist/tools.cjs.map +7 -0
- package/dist/tools.js +2 -0
- package/dist/tools.js.map +7 -0
- package/dist/web/test.js +13 -2
- package/dist/web/test.js.map +4 -4
- package/gateway-server.d.ts +3 -126
- package/package.json +11 -5
- package/readme.md +53 -0
- package/types/config.d.ts +147 -0
- package/types/manage.d.ts +46 -0
- package/types/path.d.ts +20 -0
- package/types/tools.d.ts +1 -2
- package/types/web/server.d.ts +11 -3
- package/types/web/test.d.ts +3 -3
- package/dist/metrics/publisher/rest.cjs +0 -2
- package/dist/metrics/publisher/rest.cjs.map +0 -7
- package/dist/tools/index.js +0 -2
- package/dist/tools/index.js.map +0 -7
package/gateway-server.d.ts
CHANGED
|
@@ -1,138 +1,15 @@
|
|
|
1
1
|
import { IOGateway } from '@interopio/gateway';
|
|
2
2
|
import type { AddressInfo } from 'node:net';
|
|
3
|
-
import type {
|
|
3
|
+
import type { ServerConfig as GatewayServerConfig } from './types/config';
|
|
4
4
|
|
|
5
5
|
export default GatewayServer.Factory;
|
|
6
6
|
|
|
7
7
|
export namespace GatewayServer {
|
|
8
|
+
export const VERSION: string;
|
|
8
9
|
export const Factory: (options: ServerConfig) => Promise<Server>
|
|
9
|
-
/**
|
|
10
|
-
* SSL/TLS configuration.
|
|
11
|
-
* All key and certificate files must be in PEM format.
|
|
12
|
-
*/
|
|
13
|
-
export type SslConfig = Readonly<{
|
|
14
|
-
/** Path to server private key file (PEM format) */
|
|
15
|
-
key?: string,
|
|
16
|
-
/** Path to server certificate file (PEM format) */
|
|
17
|
-
cert?: string,
|
|
18
|
-
/** Path to CA certificate for client certificate verification (PEM format) */
|
|
19
|
-
ca?: string,
|
|
20
|
-
/**
|
|
21
|
-
* Passphrase for encrypted private key
|
|
22
|
-
* @since 0.20.0
|
|
23
|
-
*/
|
|
24
|
-
passphrase?: string,
|
|
25
|
-
/**
|
|
26
|
-
* Request client certificate during TLS handshake.
|
|
27
|
-
* When false (default), clients will not send certificates.
|
|
28
|
-
* When true, clients are asked to send a certificate.
|
|
29
|
-
* @since 0.20.0
|
|
30
|
-
*/
|
|
31
|
-
requestCert?: boolean,
|
|
32
|
-
/**
|
|
33
|
-
* Reject clients with invalid or missing certificates.
|
|
34
|
-
* Only effective when requestCert is true.
|
|
35
|
-
* When false, clients without valid certs are still allowed.
|
|
36
|
-
* When true, enforces mutual TLS authentication.
|
|
37
|
-
* @since 0.20.0
|
|
38
|
-
*/
|
|
39
|
-
rejectUnauthorized?: boolean
|
|
40
|
-
}>
|
|
41
10
|
|
|
42
11
|
export import LoggerConfig = IOGateway.Logging.LogConfig;
|
|
43
|
-
|
|
44
|
-
export type AuthConfig = Readonly<{
|
|
45
|
-
type: 'none'
|
|
46
|
-
| 'basic'
|
|
47
|
-
| 'x509' // since 0.20.0
|
|
48
|
-
| 'oauth2'
|
|
49
|
-
,
|
|
50
|
-
/** X.509 client certificate authentication configuration
|
|
51
|
-
* @since 0.20.0
|
|
52
|
-
*/
|
|
53
|
-
x509?: {
|
|
54
|
-
/** Path to CA private key file (PEM format), for generating client certificates and server certificates */
|
|
55
|
-
key?: string,
|
|
56
|
-
/** Passphrase for encrypted CA private key */
|
|
57
|
-
passphrase?: string,
|
|
58
|
-
/** Extract principal from Subject Alternative Name. Use 'email' to extract from email SAN. If undefined, uses subject (default). */
|
|
59
|
-
principalAltName?: 'email'
|
|
60
|
-
},
|
|
61
|
-
/** Basic authentication configuration */
|
|
62
|
-
basic?: {
|
|
63
|
-
realm?: string
|
|
64
|
-
},
|
|
65
|
-
/** OAuth2 authentication configuration */
|
|
66
|
-
oauth2?: {
|
|
67
|
-
jwt: { issuerUri: string, issuer?: string, audience?: string | string[] }
|
|
68
|
-
},
|
|
69
|
-
// in-memory user for basic auth
|
|
70
|
-
user?: { name: string, password?: string, readonly roles?: string[] }
|
|
71
|
-
}>;
|
|
72
|
-
|
|
73
|
-
export type ServerCustomizer = (configurer: ServerConfigurer, config: ServerConfig) => Promise<void>;
|
|
74
|
-
|
|
75
|
-
export type ServerConfig = {
|
|
76
|
-
/**
|
|
77
|
-
* The port to bind for network communication.
|
|
78
|
-
* Accepts a single value or a range.
|
|
79
|
-
* If a range is specified, will bind to the first available port in the range.
|
|
80
|
-
*
|
|
81
|
-
* @defaultValue 0 (random port)
|
|
82
|
-
*/
|
|
83
|
-
port?: number | string
|
|
84
|
-
/**
|
|
85
|
-
* The network address.
|
|
86
|
-
*/
|
|
87
|
-
host?: string
|
|
88
|
-
ssl?: SslConfig,
|
|
89
|
-
// http2?: {
|
|
90
|
-
// enabled: boolean
|
|
91
|
-
// }
|
|
92
|
-
// node:http server options
|
|
93
|
-
http?: {
|
|
94
|
-
maxHeaderSize?: number,
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
auth?: AuthConfig,
|
|
98
|
-
/**
|
|
99
|
-
* CORS configuration.
|
|
100
|
-
*/
|
|
101
|
-
cors?: false | ServerCorsConfig,
|
|
102
|
-
|
|
103
|
-
memory?: {
|
|
104
|
-
memory_limit?: number
|
|
105
|
-
dump_location?: string,
|
|
106
|
-
dump_prefix?: string,
|
|
107
|
-
report_interval?: number,
|
|
108
|
-
max_backups?: number,
|
|
109
|
-
},
|
|
110
|
-
|
|
111
|
-
app?: ServerCustomizer,
|
|
112
|
-
resources?: {
|
|
113
|
-
enabled?: boolean,
|
|
114
|
-
locations: string[]
|
|
115
|
-
},
|
|
116
|
-
gateway?: IOGateway.GatewayConfig & ServerWebSocketOptions & {
|
|
117
|
-
route?: string,
|
|
118
|
-
/**
|
|
119
|
-
* Gateway scope - determines how gateway instances are managed:
|
|
120
|
-
* - 'principal': Each authenticated principal gets their own gateway instance.
|
|
121
|
-
* A default (shared) gateway instance handles all unauthenticated connections.
|
|
122
|
-
* - 'singleton': All connections share the same gateway instance
|
|
123
|
-
*
|
|
124
|
-
* @defaultValue 'principal'
|
|
125
|
-
* @since 0.20.0
|
|
126
|
-
*/
|
|
127
|
-
scope?: 'singleton' | 'principal',
|
|
128
|
-
mesh?: IOGateway.MeshConfig & {
|
|
129
|
-
enabled?: boolean
|
|
130
|
-
}
|
|
131
|
-
metrics?: IOGateway.MetricsConfig & {
|
|
132
|
-
enabled?: boolean
|
|
133
|
-
}
|
|
134
|
-
}
|
|
135
|
-
}
|
|
12
|
+
export type ServerConfig = GatewayServerConfig;
|
|
136
13
|
|
|
137
14
|
export interface Server {
|
|
138
15
|
readonly gateway: ScopedGateway
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@interopio/gateway-server",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.22.0",
|
|
4
4
|
"keywords": [
|
|
5
5
|
"gateway",
|
|
6
6
|
"server",
|
|
@@ -48,9 +48,15 @@
|
|
|
48
48
|
"types": "./types/web/test.d.ts",
|
|
49
49
|
"import": "./dist/web/test.js"
|
|
50
50
|
},
|
|
51
|
+
"./config": {
|
|
52
|
+
"types:": "./types/config.d.ts",
|
|
53
|
+
"import": "./dist/config.js",
|
|
54
|
+
"require": "./dist/config.cjs"
|
|
55
|
+
},
|
|
51
56
|
"./tools": {
|
|
52
57
|
"types": "./types/tools.d.ts",
|
|
53
|
-
"import": "./dist/tools
|
|
58
|
+
"import": "./dist/tools.js",
|
|
59
|
+
"require": "./dist/tools.cjs"
|
|
54
60
|
},
|
|
55
61
|
"./metrics/publisher/rest": {
|
|
56
62
|
"import": "./dist/metrics/publisher/rest.js",
|
|
@@ -73,7 +79,7 @@
|
|
|
73
79
|
}
|
|
74
80
|
},
|
|
75
81
|
"bin": {
|
|
76
|
-
"gateway-server": "
|
|
82
|
+
"gateway-server": "bin/gateway-server.cjs"
|
|
77
83
|
},
|
|
78
84
|
"main": "dist/index.js",
|
|
79
85
|
"types": "gateway-server.d.ts",
|
|
@@ -82,13 +88,13 @@
|
|
|
82
88
|
"node": ">=20.18 || >=22.12 || >=24"
|
|
83
89
|
},
|
|
84
90
|
"dependencies": {
|
|
85
|
-
"@interopio/gateway": "^0.
|
|
91
|
+
"@interopio/gateway": "^0.24.0",
|
|
86
92
|
"tough-cookie": "^6.0.0",
|
|
87
93
|
"http-cookie-agent": "^7.0.3",
|
|
88
94
|
"ws": "^8.19.0"
|
|
89
95
|
},
|
|
90
96
|
"peerDependencies": {
|
|
91
|
-
"undici": "^7.
|
|
97
|
+
"undici": "^7.22.0"
|
|
92
98
|
},
|
|
93
99
|
"peerDependenciesMeta": {
|
|
94
100
|
"undici": {
|
package/readme.md
CHANGED
|
@@ -21,6 +21,7 @@ npm install @interopio/gateway-server
|
|
|
21
21
|
|
|
22
22
|
- [Getting Started](#getting-started)
|
|
23
23
|
- [Configure HTTPS](#configure-https)
|
|
24
|
+
- [Management](#management)
|
|
24
25
|
- [@glue42/gateway-ent Compatibility](#glue42gateway-ent-compatibility)
|
|
25
26
|
|
|
26
27
|
## Getting Started
|
|
@@ -280,6 +281,58 @@ await server.close();
|
|
|
280
281
|
|
|
281
282
|
```
|
|
282
283
|
|
|
284
|
+
## Management
|
|
285
|
+
|
|
286
|
+
The gateway server supports a management interface for remote administration via named pipes (Windows) or Unix sockets. This allows external tools to send commands like `info` (get server status) or `shutdown` (gracefully stop the server).
|
|
287
|
+
|
|
288
|
+
### CLI
|
|
289
|
+
|
|
290
|
+
Use the `manage` command to send management commands to a running gateway server:
|
|
291
|
+
|
|
292
|
+
```shell
|
|
293
|
+
# Get server info
|
|
294
|
+
npx @interopio/gateway-server manage --path \\.\pipe\glue42-gateway-xxx info
|
|
295
|
+
|
|
296
|
+
# Shutdown the server
|
|
297
|
+
npx @interopio/gateway-server manage --path \\.\pipe\glue42-gateway-xxx shutdown
|
|
298
|
+
```
|
|
299
|
+
|
|
300
|
+
### API
|
|
301
|
+
|
|
302
|
+
```typescript
|
|
303
|
+
import { manage } from '@interopio/gateway-server/tools';
|
|
304
|
+
|
|
305
|
+
// Send a command to a running gateway server
|
|
306
|
+
const result = await manage.sendCommand(
|
|
307
|
+
{ path: '\\\\.\\pipe\\glue42-gateway-xxx' },
|
|
308
|
+
{ command: 'info' }
|
|
309
|
+
);
|
|
310
|
+
```
|
|
311
|
+
|
|
312
|
+
### Server Configuration
|
|
313
|
+
|
|
314
|
+
```typescript
|
|
315
|
+
function getPath() {
|
|
316
|
+
const prefix = `glue42-gateway`;
|
|
317
|
+
const env = process.env['GLUE-ENV'] || 'DEMO';
|
|
318
|
+
const region = process.env['GLUE-REGION'] || 'INTEROP.IO';
|
|
319
|
+
return process.platform === 'win32'
|
|
320
|
+
? `\\\\.\\pipe\\glue42-gateway-${env}-${region}-${process.env.USERNAME}`
|
|
321
|
+
: `${tmpdir()}/${prefix}-${env}-${region}-${process.env.USERNAME}.sock`;
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
const server: GatewayServer.Server = await GatewayServerFactory({
|
|
325
|
+
management: {
|
|
326
|
+
server: {
|
|
327
|
+
path: getPath()
|
|
328
|
+
},
|
|
329
|
+
commands: {
|
|
330
|
+
shutdown: { enabled: false },
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
});
|
|
334
|
+
```
|
|
335
|
+
|
|
283
336
|
## `@glue42/gateway-ent` Compatibility
|
|
284
337
|
|
|
285
338
|
This package aims to provide compatibility with `@glue42/gateway-ent` proprietary package used in Glue42 Desktop (now io.Connect Desktop)
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
import { IOGateway } from '@interopio/gateway';
|
|
2
|
+
import type { ManagementEndpoint } from './manage.d.ts';
|
|
3
|
+
import type { ServerConfigurer, ServerCorsConfig, ServerWebSocketOptions } from './web/server.d.ts'
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* SSL/TLS configuration.
|
|
7
|
+
* All key and certificate files must be in PEM format.
|
|
8
|
+
*/
|
|
9
|
+
export type SslConfig = {
|
|
10
|
+
/** Path to server private key file (PEM format) */
|
|
11
|
+
key?: string,
|
|
12
|
+
/** Path to server certificate file (PEM format) */
|
|
13
|
+
cert?: string,
|
|
14
|
+
/** Path to CA certificate for client certificate verification (PEM format) */
|
|
15
|
+
ca?: string,
|
|
16
|
+
/**
|
|
17
|
+
* Passphrase for encrypted private key
|
|
18
|
+
* @since 0.20.0
|
|
19
|
+
*/
|
|
20
|
+
passphrase?: string,
|
|
21
|
+
/**
|
|
22
|
+
* Request client certificate during TLS handshake.
|
|
23
|
+
* When false (default), clients will not send certificates.
|
|
24
|
+
* When true, clients are asked to send a certificate.
|
|
25
|
+
* @since 0.20.0
|
|
26
|
+
*/
|
|
27
|
+
requestCert?: boolean,
|
|
28
|
+
/**
|
|
29
|
+
* Reject clients with invalid or missing certificates.
|
|
30
|
+
* Only effective when requestCert is true.
|
|
31
|
+
* When false, clients without valid certs are still allowed.
|
|
32
|
+
* When true, enforces mutual TLS authentication.
|
|
33
|
+
* @since 0.20.0
|
|
34
|
+
*/
|
|
35
|
+
rejectUnauthorized?: boolean
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export type AuthConfig = {
|
|
39
|
+
type: 'none'
|
|
40
|
+
| 'basic'
|
|
41
|
+
| 'x509' // since 0.20.0
|
|
42
|
+
| 'oauth2'
|
|
43
|
+
,
|
|
44
|
+
/** X.509 client certificate authentication configuration
|
|
45
|
+
* @since 0.20.0
|
|
46
|
+
*/
|
|
47
|
+
x509?: {
|
|
48
|
+
/** Path to CA private key file (PEM format), for generating client certificates and server certificates */
|
|
49
|
+
key?: string,
|
|
50
|
+
/** Passphrase for encrypted CA private key */
|
|
51
|
+
passphrase?: string,
|
|
52
|
+
/** Extract principal from Subject Alternative Name. Use 'email' to extract from email SAN. If undefined, uses subject (default). */
|
|
53
|
+
principalAltName?: 'email'
|
|
54
|
+
},
|
|
55
|
+
/** Basic authentication configuration */
|
|
56
|
+
basic?: {
|
|
57
|
+
realm?: string
|
|
58
|
+
},
|
|
59
|
+
/** OAuth2 authentication configuration */
|
|
60
|
+
oauth2?: {
|
|
61
|
+
jwt: { issuerUri: string, issuer?: string, audience?: string | string[] }
|
|
62
|
+
},
|
|
63
|
+
// in-memory user for basic auth
|
|
64
|
+
user?: { name: string, password?: string, readonly roles?: string[] }
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
export type ServerCustomizer = (configurer: ServerConfigurer, config: ServerConfig) => Promise<void>;
|
|
68
|
+
|
|
69
|
+
export type ServerConfig = {
|
|
70
|
+
/**
|
|
71
|
+
* The port to bind for network communication.
|
|
72
|
+
* Accepts a single value or a range.
|
|
73
|
+
* If a range is specified, will bind to the first available port in the range.
|
|
74
|
+
*
|
|
75
|
+
* @defaultValue 0 (random port)
|
|
76
|
+
*/
|
|
77
|
+
port?: number | string
|
|
78
|
+
/**
|
|
79
|
+
* The network address.
|
|
80
|
+
*/
|
|
81
|
+
host?: string
|
|
82
|
+
ssl?: SslConfig,
|
|
83
|
+
// http2?: {
|
|
84
|
+
// enabled: boolean
|
|
85
|
+
// }
|
|
86
|
+
// node:http server options
|
|
87
|
+
http?: {
|
|
88
|
+
maxHeaderSize?: number,
|
|
89
|
+
},
|
|
90
|
+
/**
|
|
91
|
+
* Management configuratoni.
|
|
92
|
+
*/
|
|
93
|
+
management?: {
|
|
94
|
+
server: ManagementEndpoint & {
|
|
95
|
+
// node:net listen options
|
|
96
|
+
},
|
|
97
|
+
commands?: {
|
|
98
|
+
// shutdown, info, etc
|
|
99
|
+
[command: string]: {
|
|
100
|
+
enabled?: boolean,
|
|
101
|
+
// command specific options
|
|
102
|
+
[key: string]: unknown
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
auth?: AuthConfig,
|
|
108
|
+
/**
|
|
109
|
+
* CORS configuration.
|
|
110
|
+
*/
|
|
111
|
+
cors?: false | ServerCorsConfig,
|
|
112
|
+
|
|
113
|
+
memory?: {
|
|
114
|
+
memory_limit?: number
|
|
115
|
+
dump_location?: string,
|
|
116
|
+
dump_prefix?: string,
|
|
117
|
+
report_interval?: number,
|
|
118
|
+
max_backups?: number,
|
|
119
|
+
},
|
|
120
|
+
|
|
121
|
+
app?: ServerCustomizer,
|
|
122
|
+
resources?: {
|
|
123
|
+
enabled?: boolean,
|
|
124
|
+
locations: string[]
|
|
125
|
+
},
|
|
126
|
+
gateway?: IOGateway.GatewayConfig & ServerWebSocketOptions & {
|
|
127
|
+
route?: string,
|
|
128
|
+
/**
|
|
129
|
+
* Gateway scope - determines how gateway instances are managed:
|
|
130
|
+
* - 'principal': Each authenticated principal gets their own gateway instance.
|
|
131
|
+
* A default (shared) gateway instance handles all unauthenticated connections.
|
|
132
|
+
* - 'singleton': All connections share the same gateway instance
|
|
133
|
+
*
|
|
134
|
+
* @defaultValue 'principal'
|
|
135
|
+
* @since 0.20.0
|
|
136
|
+
*/
|
|
137
|
+
scope?: 'singleton' | 'principal',
|
|
138
|
+
mesh?: IOGateway.MeshConfig & {
|
|
139
|
+
enabled?: boolean
|
|
140
|
+
}
|
|
141
|
+
metrics?: IOGateway.MetricsConfig & {
|
|
142
|
+
enabled?: boolean
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
export function defineConfig(config: ServerConfig): ServerConfig;
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
export type ManagementCommand = { command: string, [key: string]: unknown };
|
|
2
|
+
|
|
3
|
+
export type ManagementEndpoint = {
|
|
4
|
+
// windows named pipe or unix domain socket
|
|
5
|
+
path?: string,
|
|
6
|
+
port?: number,
|
|
7
|
+
};
|
|
8
|
+
|
|
9
|
+
export interface SendCommandOptions {
|
|
10
|
+
/** Timeout in milliseconds (default: 5000) */
|
|
11
|
+
timeout?: number;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Send a management command to a running gateway server via its control socket.
|
|
16
|
+
*
|
|
17
|
+
* @param server - Management server configuration (path or port)
|
|
18
|
+
* @param command - Command object with command name and optional parameters
|
|
19
|
+
* @param options - Additional options like timeout
|
|
20
|
+
*
|
|
21
|
+
* @example
|
|
22
|
+
* ```ts
|
|
23
|
+
* // On Windows (named pipe)
|
|
24
|
+
* const result = await sendCommand(
|
|
25
|
+
* { path: '\\\\.\\pipe\\glue42-gateway-xxx' },
|
|
26
|
+
* { command: 'info' }
|
|
27
|
+
* );
|
|
28
|
+
*
|
|
29
|
+
* // On Unix/Linux/macOS (Unix socket)
|
|
30
|
+
* const result = await sendCommand(
|
|
31
|
+
* { path: '/tmp/gateway.sock' },
|
|
32
|
+
* { command: 'shutdown' }
|
|
33
|
+
* );
|
|
34
|
+
*
|
|
35
|
+
* // Using TCP port
|
|
36
|
+
* const result = await sendCommand(
|
|
37
|
+
* { port: 9999 },
|
|
38
|
+
* { command: 'info' }
|
|
39
|
+
* );
|
|
40
|
+
* ```
|
|
41
|
+
*/
|
|
42
|
+
export function sendCommand(
|
|
43
|
+
server: ManagementEndpoint,
|
|
44
|
+
command: ManagementCommand,
|
|
45
|
+
options?: SendCommandOptions
|
|
46
|
+
): Promise<unknown>;
|
package/types/path.d.ts
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Structured representation of a path parsed via {@link parsePath} into a sequence of
|
|
3
|
+
* {@link Separator} and {@link PathSegment} elements.
|
|
4
|
+
*/
|
|
5
|
+
export type PathContainer = {
|
|
6
|
+
readonly value: string;
|
|
7
|
+
readonly elements: readonly PathContainerElement[];
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export type PathContainerElement = Separator | PathSegment
|
|
11
|
+
|
|
12
|
+
export type PathSegment = {
|
|
13
|
+
readonly value: string,
|
|
14
|
+
readonly valueToMatch: string,
|
|
15
|
+
readonly parameters: ReadonlyMap<string, readonly string[]>
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export type Separator = {
|
|
19
|
+
readonly value: string
|
|
20
|
+
}
|
package/types/tools.d.ts
CHANGED
package/types/web/server.d.ts
CHANGED
|
@@ -1,16 +1,17 @@
|
|
|
1
1
|
import type {
|
|
2
|
-
HttpMethod,
|
|
3
2
|
HttpInputMessage,
|
|
3
|
+
HttpMethod,
|
|
4
|
+
HttpOutputMessage,
|
|
4
5
|
HttpRequest,
|
|
5
6
|
HttpResponse,
|
|
6
|
-
HttpOutputMessage,
|
|
7
7
|
HttpStatusCode,
|
|
8
8
|
MutableHttpHeaders,
|
|
9
9
|
ReadonlyHttpHeaders,
|
|
10
10
|
ResponseCookie
|
|
11
11
|
} from './http';
|
|
12
12
|
import type { WebSocketHandler } from './socket';
|
|
13
|
-
import type {
|
|
13
|
+
import type { AuthorizationRule, Principal } from '../auth';
|
|
14
|
+
import { PathContainer } from '../path';
|
|
14
15
|
import type { AsyncLocalStorage } from 'node:async_hooks';
|
|
15
16
|
import type { X509Certificate } from 'node:crypto';
|
|
16
17
|
import type { AddressInfo } from 'node:net';
|
|
@@ -81,6 +82,7 @@ export type ServerWebExchange<Request extends ServerHttpRequest = ServerHttpRequ
|
|
|
81
82
|
readonly request: Request
|
|
82
83
|
readonly response: Response;
|
|
83
84
|
attribute<T>(key: string): T | undefined;
|
|
85
|
+
readonly attributes: Map<string, unknown>;
|
|
84
86
|
principal<P extends Principal>(): Promise<P | undefined>;
|
|
85
87
|
readonly logPrefix: string;
|
|
86
88
|
}
|
|
@@ -100,6 +102,7 @@ export type SslInfo = { peerCertificate?: X509Certificate };
|
|
|
100
102
|
export type ServerHttpRequest = HttpRequest<ReadonlyHttpHeaders> & HttpInputMessage<ReadonlyHttpHeaders> & {
|
|
101
103
|
readonly id: string
|
|
102
104
|
readonly path: string,
|
|
105
|
+
readonly requestPath: RequestPath,
|
|
103
106
|
// URL scheme without ':'
|
|
104
107
|
readonly protocol: string
|
|
105
108
|
/**
|
|
@@ -125,3 +128,8 @@ export interface ServerHttpResponse extends HttpResponse<MutableHttpHeaders>, Ht
|
|
|
125
128
|
addCookie(cookie: ResponseCookie): this
|
|
126
129
|
|
|
127
130
|
}
|
|
131
|
+
|
|
132
|
+
export interface RequestPath extends PathContainer {
|
|
133
|
+
readonly contextPath: PathContainer;
|
|
134
|
+
readonly pathWithinApplication: PathContainer;
|
|
135
|
+
}
|
package/types/web/test.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import type { Middleware, SslInfo } from './server';
|
|
2
|
-
import {
|
|
1
|
+
import type { Middleware, SslInfo } from './server.d.ts';
|
|
2
|
+
import type { ServerCustomizer } from '../config.d.ts';
|
|
3
3
|
|
|
4
4
|
|
|
5
5
|
export interface TestClient {
|
|
@@ -19,6 +19,6 @@ export interface MockServerSpec {
|
|
|
19
19
|
}
|
|
20
20
|
|
|
21
21
|
declare const TestClient: {
|
|
22
|
-
bindToApp(app:
|
|
22
|
+
bindToApp(app: ServerCustomizer): MockServerSpec;
|
|
23
23
|
bindToServer(): TestClientBuilder;
|
|
24
24
|
}
|
|
@@ -1,2 +0,0 @@
|
|
|
1
|
-
"use strict";var k=Object.create;var n=Object.defineProperty;var C=Object.getOwnPropertyDescriptor;var d=Object.getOwnPropertyNames;var y=Object.getPrototypeOf,l=Object.prototype.hasOwnProperty;var w=(e,o)=>{for(var t in o)n(e,t,{get:o[t],enumerable:!0})},c=(e,o,t,i)=>{if(o&&typeof o=="object"||typeof o=="function")for(let r of d(o))!l.call(e,r)&&r!==t&&n(e,r,{get:()=>o[r],enumerable:!(i=C(o,r))||i.enumerable});return e};var A=(e,o,t)=>(t=e!=null?k(y(e)):{},c(o||!e||!e.__esModule?n(t,"default",{value:e,enumerable:!0}):t,e)),U=e=>c(n({},"__esModule",{value:!0}),e);var q={};w(q,{create:()=>R,fetchWithCookies:()=>g,name:()=>x});module.exports=U(q);var s=A(require("@interopio/gateway/metrics/publisher/rest"),1),a=require("http-cookie-agent/undici/v6"),f=require("http-cookie-agent/undici"),p=require("undici"),m=require("tough-cookie");function b(e){let o=process.version,t=Number(o.substring(1,o.indexOf(".")));return t>=18&&t<24?new a.CookieAgent({cookies:{jar:e}}):new f.CookieAgent({cookies:{jar:e}}).compose(p.interceptors.redirect())}var g=e=>{let o=e??globalThis.fetch,t=new m.CookieJar,i=b(t);return async(r,u)=>{let h={...u,dispatcher:i};return await o(r,h)}},x=s.name;async function R(e,o){return s.create({...e,fetch:g(e.fetch)},o)}0&&(module.exports={create,fetchWithCookies,name});
|
|
2
|
-
//# sourceMappingURL=rest.cjs.map
|
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"version": 3,
|
|
3
|
-
"sources": ["../../../src/gateway/metrics/rest.ts"],
|
|
4
|
-
"sourcesContent": ["import * as rest from '@interopio/gateway/metrics/publisher/rest';\nimport type { Logger } from '@interopio/gateway/logging/api';\nimport { CookieAgent as CookieAgentUndiciV6 } from 'http-cookie-agent/undici/v6';\nimport { CookieAgent as CookieAgentUndici } from 'http-cookie-agent/undici';\nimport { interceptors } from 'undici';\nimport { CookieJar } from 'tough-cookie';\n\nfunction cookieAgent(jar: CookieJar) {\n const version = process.version;\n const major = Number(version.substring(1 /*skip v prefix*/, version.indexOf('.')));\n const useUndiciV6 = major >= 18 && major < 24; // undici v6 is used for Node.js 18.x to 23.x\n\n if (useUndiciV6) {\n return new CookieAgentUndiciV6({cookies: {jar}});\n }\n else {\n return new CookieAgentUndici({cookies: {jar}}).compose(interceptors.redirect());\n }\n\n}\nexport const fetchWithCookies = (existing?: typeof fetch): typeof fetch => {\n const fetchFn = existing ?? globalThis.fetch;\n const jar = new CookieJar();\n const dispatcher = cookieAgent(jar);\n return async (input: RequestInfo | URL, request?: RequestInit) => {\n const requestWithDispatcher = {...request, dispatcher};\n return await fetchFn(input, requestWithDispatcher);\n }\n}\nexport const name = rest.name;\n\nexport async function create(cfg: rest.RestPublisherConfig, logger: Logger) {\n return rest.create({...cfg, fetch: fetchWithCookies(cfg.fetch)}, logger);\n}\n"],
|
|
5
|
-
"mappings": "0jBAAA,IAAAA,EAAA,GAAAC,EAAAD,EAAA,YAAAE,EAAA,qBAAAC,EAAA,SAAAC,IAAA,eAAAC,EAAAL,GAAA,IAAAM,EAAsB,0DAEtBC,EAAmD,uCACnDC,EAAiD,oCACjDA,EAA6B,kBAC7BC,EAA0B,wBAE1B,SAASC,EAAYC,EAAgB,CACjC,IAAMC,EAAU,QAAQ,QAClBC,EAAQ,OAAOD,EAAQ,UAAU,EAAqBA,EAAQ,QAAQ,GAAG,CAAC,CAAC,EAGjF,OAFoBC,GAAS,IAAMA,EAAQ,GAGhC,IAAI,EAAAC,YAAoB,CAAC,QAAS,CAAC,IAAAH,CAAG,CAAC,CAAC,EAGxC,IAAI,EAAAI,YAAkB,CAAC,QAAS,CAAC,IAAAJ,CAAG,CAAC,CAAC,EAAE,QAAQ,eAAa,SAAS,CAAC,CAGtF,CACO,IAAMR,EAAoBa,GAA0C,CACvE,IAAMC,EAAUD,GAAY,WAAW,MACjCL,EAAM,IAAI,YACVO,EAAaR,EAAYC,CAAG,EAClC,MAAO,OAAOQ,EAA0BC,IAA0B,CAC9D,IAAMC,EAAwB,CAAC,GAAGD,EAAS,WAAAF,CAAU,EACrD,OAAO,MAAMD,EAAQE,EAAOE,CAAqB,CACrD,CACJ,EACajB,EAAY,OAEzB,eAAsBF,EAAOoB,EAA+BC,EAAgB,CACxE,OAAY,SAAO,CAAC,GAAGD,EAAK,MAAOnB,EAAiBmB,EAAI,KAAK,CAAC,EAAGC,CAAM,CAC3E",
|
|
6
|
-
"names": ["rest_exports", "__export", "create", "fetchWithCookies", "name", "__toCommonJS", "rest", "import_v6", "import_undici", "import_tough_cookie", "cookieAgent", "jar", "version", "major", "CookieAgentUndiciV6", "CookieAgentUndici", "existing", "fetchFn", "dispatcher", "input", "request", "requestWithDispatcher", "cfg", "logger"]
|
|
7
|
-
}
|
package/dist/tools/index.js
DELETED
|
@@ -1,2 +0,0 @@
|
|
|
1
|
-
var B=Object.defineProperty;var x=(s,e)=>{for(var r in e)B(s,r,{get:e[r],enumerable:!0})};var K={};x(K,{ARGON2_VERSION:()=>O,ARGON2_VERSION_10:()=>N,ARGON2_VERSION_13:()=>k,DEFAULT_ALGORITHM:()=>G,DEFAULT_HASH_LENGTH:()=>R,DEFAULT_MEMORY:()=>S,DEFAULT_PARALLELISM:()=>P,DEFAULT_PASSES:()=>L,DEFAULT_SALT_LENGTH:()=>w,createHash:()=>$,decode:()=>U,encode:()=>H,hash:()=>V,verify:()=>W});var D={};x(D,{createSalt:()=>h});import{getRandomValues as M}from"node:crypto";function h(s){return M(Buffer.alloc(s))}var E;async function F(){if(E===void 0)try{let s=await import("node:crypto");if(!s.argon2)throw new Error("argon2 not available");E=s.argon2}catch{throw new Error("argon2 is not available in this Node.js version. Node.js 24.7.0 or later is required for Argon2 password encoding. Please upgrade Node.js or use a different authentication method.")}return E}var N=16,k=19,O=k,G="argon2id",w=16,R=32,P=4,S=65536,L=3;function U(s){let e=s.split("$");if(e.length<4)throw new Error("Invalid encoded Argon2 hash");let r=1,t=e[r++];if(t!=="argon2d"&&t!=="argon2i"&&t!=="argon2id")throw new Error("Invalid Argon2 type");let n=N;e[r].startsWith("v=")&&(n=parseInt(e[r].substring(2),10),r++);let a={},c=e[r++].split(",");for(let g of c){let[o,i]=g.split("=");switch(o){case"m":a.memory=parseInt(i,10);break;case"t":a.passes=parseInt(i,10);break;case"p":a.parallelism=parseInt(i,10);break}}a.nonce=Buffer.from(e[r++],"base64url");let l=Buffer.from(e[r++],"base64url");return{algorithm:t,version:n,parameters:a,hash:l}}function H(s){let{algorithm:e,version:r,parameters:t,hash:n}=s;return`$${e}$v=${r}$m=${t.memory},t=${t.passes},p=${t.parallelism}$${t.nonce.toString("base64url")}$${n.toString("base64url")}`}async function $(s,e,r,t){let n=t?.nonce??h(w),a=t?.memory??S,c=t?.passes??L,l=t?.parallelism??P,g={message:e,tagLength:r,nonce:n,memory:a,passes:c,parallelism:l},o=await F();return new Promise((i,p)=>{o(s,g,(d,f)=>{d?p(d):f?i(Buffer.from(f)):p(new Error("argon2 returned no result"))})})}async function V(s,e){let r=e?.algorithm??"argon2id",t=e?.saltLength??w,n=e?.hashLength??R,a=e?.parallelism??P,c=e?.memory??S,l=e?.passes??L,g=h(t),o={memory:c,passes:l,parallelism:a,nonce:g},i=await $(r,s,n,o);return H({algorithm:r,version:O,parameters:o,hash:i})}async function W(s,e){try{let r=U(s),t=await $(r.algorithm,e,r.hash.length,r.parameters);return r.hash.equals(t)}catch{return!1}}var j={};x(j,{DEFAULT_CA_NAME:()=>T,generateCert:()=>Q,generateRootCA:()=>z});import{KEYUTIL as u,KJUR as _,datetozulu as A}from"jsrsasign";import{userInfo as J,hostname as q}from"node:os";import{randomBytes as Y}from"node:crypto";var y=`${J().username}@${q()}`;function I(){return Y(16).toString("hex")}var b="io.Gateway Dev CA",T=`${b} ${y}`;function z(s){let e=s?.name||T,r=s?.passphrase,t=u.generateKeypair("EC","secp384r1"),n=t.prvKeyObj,a=t.pubKeyObj,c=r?u.getPEM(n,"PKCS8PRV",r,"AES-256-CBC"):u.getPEM(n,"PKCS8PRV"),g=new _.asn1.x509.Certificate({version:3,serial:{hex:I()},issuer:{str:`/CN=${e}/O=${b}/OU=${y}`},subject:{str:`/CN=${e}/O=${b}/OU=${y}`},notbefore:A(new Date(Date.now()-60*1e3),!1,!1),notafter:A(new Date(Date.now()+10*365*24*60*60*1e3),!1,!1),sbjpubkey:a,ext:[{extname:"basicConstraints",critical:!0,cA:!0},{extname:"keyUsage",critical:!0,names:["keyCertSign"]}],sigalg:"SHA384withECDSA",cakey:n}).getPEM();return{key:c,cert:g}}function Q(s,e,r,t=!1,n=7){let a=u.generateKeypair("EC","secp256r1"),c=a.prvKeyObj,l=a.pubKeyObj,g=u.getPEM(c,"PKCS8PRV"),o=[],i=t?"dev-user":"localhost";for(let m of r)if(m.toLowerCase().startsWith("ip:"))o.push({ip:m.substring(3)});else if(m.toLowerCase().startsWith("email:"))o.push({rfc822:m.substring(6)});else{let v=m.toLowerCase().startsWith("dns:");if(t)i=v?m.substring(4):m;else{let C=v?m.substring(4):m;o.push({dns:C}),o.length===1&&(i=C)}}let p=[{extname:"basicConstraints",cA:!1},{extname:"keyUsage",critical:!0,names:["digitalSignature","keyEncipherment"]},{extname:"extKeyUsage",array:[t?"clientAuth":"serverAuth"]}];o.length>0&&p.push({extname:"subjectAltName",array:o});let f=new _.asn1.x509.Certificate({version:3,serial:{hex:I()},issuer:{str:e},subject:{str:`/CN=${i}/O=${b}/OU=${y}`},notbefore:A(new Date(Date.now()-60*1e3),!1,!1),notafter:A(new Date(Date.now()+n*24*60*60*1e3),!1,!1),sbjpubkey:l,ext:p,sigalg:"SHA256withECDSA",cakey:s}).getPEM();return{key:g,cert:f}}export{K as argon2,D as keygen,j as mkcert};
|
|
2
|
-
//# sourceMappingURL=index.js.map
|
package/dist/tools/index.js.map
DELETED
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"version": 3,
|
|
3
|
-
"sources": ["../../src/tools/crypto/argon2.ts", "../../src/tools/crypto/keygen.ts", "../../src/tools/mkcert.ts"],
|
|
4
|
-
"sourcesContent": ["import type {\n Argon2Algorithm,\n Argon2Parameters\n} from 'node:crypto';\n\nimport { createSalt } from './keygen.ts';\nimport type { Argon2Hash, Argon2HashParameters } from '../../../types/crypto/argon2';\n\n// Lazy import for argon2 (added in Node.js 24.7.0)\n// This allows the module to load on older Node.js versions\nlet argon2: typeof import('node:crypto').argon2 | undefined;\n\nasync function getArgon2() {\n if (argon2 === undefined) {\n try {\n const crypto = await import('node:crypto');\n if (!crypto.argon2) {\n throw new Error('argon2 not available');\n }\n argon2 = crypto.argon2;\n } catch (err) {\n throw new Error(\n 'argon2 is not available in this Node.js version. ' +\n 'Node.js 24.7.0 or later is required for Argon2 password encoding. ' +\n 'Please upgrade Node.js or use a different authentication method.'\n );\n }\n }\n return argon2;\n}\n\n// Argon2 version constants\nexport const ARGON2_VERSION_10 = 0x10; // 16\nexport const ARGON2_VERSION_13 = 0x13; // 19\nexport const ARGON2_VERSION = ARGON2_VERSION_13;\n\n// Default parameters for Argon2 hashing\nexport const DEFAULT_ALGORITHM: Argon2Algorithm = 'argon2id';\nexport const DEFAULT_SALT_LENGTH = 16;\nexport const DEFAULT_HASH_LENGTH = 32;\nexport const DEFAULT_PARALLELISM = 4;\nexport const DEFAULT_MEMORY = 65536; // 64 MB in KiB\nexport const DEFAULT_PASSES = 3;\n\n/**\n * Decode an encoded Argon2 hash string into its components.\n * Format: $argon2id$v=19$m=65536,t=3,p=4$base64salt$base64hash\n *\n * @param encodedHash - The encoded Argon2 hash string\n * @returns Decoded hash components\n * @see https://github.com/P-H-C/phc-winner-argon2/blob/master/src/encoding.c#L244\n */\nexport function decode(encodedHash: string): Argon2Hash {\n const parts = encodedHash.split('$'); // ['', 'argon2id', 'v=19', 'm=65536,t=3,p=4', 'base64salt', 'base64hash']\n if (parts.length < 4) {\n throw new Error('Invalid encoded Argon2 hash');\n }\n let current = 1;\n const algorithm = parts[current++];\n if (algorithm !== 'argon2d' && algorithm !== 'argon2i' && algorithm !== 'argon2id') {\n throw new Error('Invalid Argon2 type');\n }\n let version = ARGON2_VERSION_10;\n if (parts[current].startsWith('v=')) {\n version = parseInt(parts[current].substring(2), 10);\n current++;\n }\n const parameters: Partial<Argon2HashParameters> = {};\n const paramsParts = parts[current++].split(',');\n\n for (const param of paramsParts) {\n const [key, value] = param.split('=');\n switch (key) {\n case 'm':\n parameters.memory = parseInt(value, 10);\n break;\n case 't':\n parameters.passes = parseInt(value, 10);\n break;\n case 'p':\n parameters.parallelism = parseInt(value, 10);\n break;\n }\n }\n parameters.nonce = Buffer.from(parts[current++], 'base64url');\n const hash = Buffer.from(parts[current++], 'base64url');\n\n return { algorithm, version, parameters: parameters as Argon2Hash['parameters'], hash };\n}\n\n/**\n * Encode Argon2 hash components into a standard string format.\n * Format: $argon2id$v=19$m=65536,t=3,p=4$base64salt$base64hash\n *\n * @param hashData - The hash components to encode\n * @returns Encoded hash string\n */\nexport function encode(hashData: Argon2Hash): string {\n const { algorithm, version, parameters, hash } = hashData;\n return `$${algorithm}$v=${version}$m=${parameters.memory},t=${parameters.passes},p=${parameters.parallelism}$${parameters.nonce.toString('base64url')}$${hash.toString('base64url')}`;\n}\n\n\n/**\n * Create an Argon2 hash from parameters.\n * Low-level wrapper around node:crypto's argon2.\n *\n * @param algorithm - Argon2 algorithm variant ('argon2d' | 'argon2i' | 'argon2id')\n * @param password - The password to hash\n * @param hashLength - Length of the output hash in bytes\n * @param parameters - Argon2 parameters (nonce, memory, passes, parallelism). If not provided, defaults will be used.\n * @returns The hash buffer\n */\nexport async function createHash(\n algorithm: Argon2Algorithm,\n password: string,\n hashLength: number,\n parameters?: Argon2Hash['parameters']\n): Promise<Buffer> {\n const nonce = parameters?.nonce ?? createSalt(DEFAULT_SALT_LENGTH);\n const memory = parameters?.memory ?? DEFAULT_MEMORY;\n const passes = parameters?.passes ?? DEFAULT_PASSES;\n const parallelism = parameters?.parallelism ?? DEFAULT_PARALLELISM;\n\n const argon2Params: Argon2Parameters = {\n message: password,\n tagLength: hashLength,\n nonce,\n memory,\n passes,\n parallelism\n };\n const argon2Fn = await getArgon2();\n\n // Wrap callback-based argon2 in a Promise\n return new Promise((resolve, reject) => {\n argon2Fn(algorithm, argon2Params, (err: Error | null, result?: Uint8Array) => {\n if (err) {\n reject(err);\n }\n else if (result) {\n resolve(Buffer.from(result));\n }\n else {\n reject(new Error('argon2 returned no result'));\n }\n });\n });\n}\n\n\n/**\n * Hash a password using Argon2.\n *\n * @param password - The plain text password to hash\n * @param options - Optional parameters object. If not provided, defaults will be used.\n * @returns The encoded Argon2 hash string\n */\nexport async function hash(\n password: string,\n options?: {\n algorithm?: Argon2Algorithm;\n saltLength?: number;\n hashLength?: number;\n parallelism?: number;\n memory?: number;\n passes?: number;\n }\n): Promise<string> {\n const algorithm = options?.algorithm ?? 'argon2id';\n const saltLength = options?.saltLength ?? DEFAULT_SALT_LENGTH;\n const hashLength = options?.hashLength ?? DEFAULT_HASH_LENGTH;\n const parallelism = options?.parallelism ?? DEFAULT_PARALLELISM;\n const memory = options?.memory ?? DEFAULT_MEMORY;\n const passes = options?.passes ?? DEFAULT_PASSES;\n\n const nonce = createSalt(saltLength);\n const parameters = { memory, passes, parallelism, nonce };\n const hashBuffer = await createHash(algorithm, password, hashLength, parameters);\n return encode({\n algorithm,\n version: ARGON2_VERSION,\n parameters,\n hash: hashBuffer\n });\n}\n\n/**\n * Verify a password against an Argon2 hash.\n *\n * @param encodedHash - The encoded Argon2 hash to verify against\n * @param password - The plain text password to verify\n * @returns true if password matches the hash, false otherwise\n */\nexport async function verify(encodedHash: string, password: string): Promise<boolean> {\n try {\n const decoded = decode(encodedHash);\n const hashBuffer = await createHash(\n decoded.algorithm,\n password,\n decoded.hash.length,\n decoded.parameters\n );\n return decoded.hash.equals(hashBuffer);\n } catch {\n return false;\n }\n}\n\n", "import { getRandomValues } from 'node:crypto';\n\n/**\n * Create a random salt (nonce) for cryptographic operations.\n *\n * @param length - Length of the salt in bytes\n * @returns Random salt buffer\n */\nexport function createSalt(length: number): Buffer {\n return getRandomValues(Buffer.alloc(length));\n}\n\n", "import { KEYUTIL, KJUR, RSAKey, datetozulu } from \"jsrsasign\";\nimport { userInfo, hostname } from 'node:os';\nimport { randomBytes } from 'node:crypto';\n\nconst userAndHost = `${userInfo().username}@${hostname()}`;\n\nfunction randomSerialNumber(): string {\n const bytes = randomBytes(16);\n return bytes.toString('hex');\n}\n\nconst DEFAULT_ORGANISATION = `io.Gateway Dev CA`;\nexport const DEFAULT_CA_NAME = `${DEFAULT_ORGANISATION} ${userAndHost}`;\n\nexport function generateRootCA(options?: { name?: string, passphrase?: string } ): { key: string, cert: string } {\n\n const commonName = options?.name || DEFAULT_CA_NAME;\n const passphrase = options?.passphrase;\n\n // STEP 1. generate a key pair for Root CA (secp384r1 for higher security)\n const kp = KEYUTIL.generateKeypair(\"EC\", \"secp384r1\");\n const prv = kp.prvKeyObj;\n const pub = kp.pubKeyObj;\n\n const prvPem = passphrase\n ? KEYUTIL.getPEM(prv, \"PKCS8PRV\", passphrase, \"AES-256-CBC\")\n : KEYUTIL.getPEM(prv, \"PKCS8PRV\");\n\n // STEP 2: create self-signed CA certificate\n const cert = new KJUR.asn1.x509.Certificate({\n version: 3,\n serial: { hex: randomSerialNumber() },\n issuer: { str: `/CN=${commonName}/O=${DEFAULT_ORGANISATION}/OU=${userAndHost}` },\n subject: { str: `/CN=${commonName}/O=${DEFAULT_ORGANISATION}/OU=${userAndHost}` },\n\n notbefore: datetozulu(new Date(Date.now() - 60 * 1000), false, false),\n notafter: datetozulu(new Date(Date.now() + 10 * 365 * 24 * 60 * 60 * 1000), false, false), // 10 years\n\n sbjpubkey: pub,\n\n ext: [\n {extname: \"basicConstraints\", critical: true, cA: true},\n {extname: \"keyUsage\", critical: true, names: [\"keyCertSign\"]}\n ],\n\n sigalg: \"SHA384withECDSA\",\n cakey: prv\n });\n\n const certPem = cert.getPEM();\n\n return {\n key: prvPem,\n cert: certPem\n };\n}\n\n\n/**\n * Generate a certificate with custom SAN entries\n * @param cakey - CA private key\n * @param issuer - Issuer DN string\n * @param sanEntries - Array of SAN entry strings (e.g., \"localhost\", \"IP:192.168.1.1\", \"EMAIL:user@example.com\")\n * @param isClient - Generate client cert (clientAuth) vs server cert (serverAuth)\n * @param validityDays - Certificate validity period\n */\nexport function generateCert(\n cakey: string | RSAKey | KJUR.crypto.DSA | KJUR.crypto.ECDSA,\n issuer: string,\n sanEntries: string[],\n isClient: boolean = false,\n validityDays: number = 7\n): {\n key: string,\n cert: string\n} {\n // STEP 1. generate key pair (secp256r1 for faster generation)\n const kp = KEYUTIL.generateKeypair(\"EC\", \"secp256r1\");\n const prv = kp.prvKeyObj;\n const pub = kp.pubKeyObj;\n\n const prvPem = KEYUTIL.getPEM(prv, \"PKCS8PRV\");\n\n // Parse SAN entries\n const sanArray: Array<{dns?: string, ip?: string, rfc822?: string}> = [];\n let commonName = isClient ? 'dev-user' : 'localhost';\n\n for (const entry of sanEntries) {\n if (entry.toLowerCase().startsWith('ip:')) {\n sanArray.push({ip: entry.substring(3)});\n }\n else if (entry.toLowerCase().startsWith('email:')) {\n sanArray.push({rfc822: entry.substring(6)});\n }\n else {\n const dnsPrefixed = entry.toLowerCase().startsWith('dns:');\n if (isClient) {\n commonName = dnsPrefixed ? entry.substring(4) : entry;\n }\n else {\n const dns = dnsPrefixed ? entry.substring(4) : entry;\n sanArray.push({dns});\n if (sanArray.length === 1) {\n commonName = dns; // Use first DNS as CN\n }\n }\n }\n }\n\n // STEP 2: create certificate signed by CA\n const extensions: Array<{\n extname: string;\n cA?: boolean;\n critical?: boolean;\n names?: string[];\n array?: (string[] | Array<{dns?: string, ip?: string, rfc822?: string}>);\n }> = [\n {extname: \"basicConstraints\", cA: false},\n {\n extname: \"keyUsage\",\n critical: true,\n names: [\"digitalSignature\", \"keyEncipherment\"]\n },\n {\n extname: \"extKeyUsage\",\n array: [isClient ? \"clientAuth\" : \"serverAuth\"]\n }\n ];\n\n // Add SAN only if we have entries\n if (sanArray.length > 0) {\n extensions.push({\n extname: \"subjectAltName\",\n array: sanArray\n });\n }\n\n const cert = new KJUR.asn1.x509.Certificate({\n version: 3,\n serial: { hex: randomSerialNumber() },\n issuer: { str: issuer },\n subject: { str: `/CN=${commonName}/O=${DEFAULT_ORGANISATION}/OU=${userAndHost}`},\n\n notbefore: datetozulu(new Date(Date.now() - 60 * 1000), false, false),\n notafter: datetozulu(new Date(Date.now() + validityDays * 24 * 60 * 60 * 1000), false, false),\n\n sbjpubkey: pub,\n ext: extensions,\n\n sigalg: \"SHA256withECDSA\",\n cakey: cakey\n });\n\n const certPem = cert.getPEM();\n\n return {\n key: prvPem,\n cert: certPem\n };\n}\n"],
|
|
5
|
-
"mappings": "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",
|
|
6
|
-
"names": ["argon2_exports", "__export", "ARGON2_VERSION", "ARGON2_VERSION_10", "ARGON2_VERSION_13", "DEFAULT_ALGORITHM", "DEFAULT_HASH_LENGTH", "DEFAULT_MEMORY", "DEFAULT_PARALLELISM", "DEFAULT_PASSES", "DEFAULT_SALT_LENGTH", "createHash", "decode", "encode", "hash", "verify", "keygen_exports", "__export", "createSalt", "getRandomValues", "length", "argon2", "getArgon2", "crypto", "ARGON2_VERSION_10", "ARGON2_VERSION_13", "ARGON2_VERSION", "DEFAULT_ALGORITHM", "DEFAULT_SALT_LENGTH", "DEFAULT_HASH_LENGTH", "DEFAULT_PARALLELISM", "DEFAULT_MEMORY", "DEFAULT_PASSES", "decode", "encodedHash", "parts", "current", "algorithm", "version", "parameters", "paramsParts", "param", "key", "value", "hash", "encode", "hashData", "createHash", "password", "hashLength", "nonce", "createSalt", "memory", "passes", "parallelism", "argon2Params", "argon2Fn", "resolve", "reject", "err", "result", "options", "saltLength", "hashBuffer", "verify", "decoded", "mkcert_exports", "__export", "DEFAULT_CA_NAME", "generateCert", "generateRootCA", "KEYUTIL", "KJUR", "datetozulu", "userInfo", "hostname", "randomBytes", "userAndHost", "randomSerialNumber", "DEFAULT_ORGANISATION", "options", "commonName", "passphrase", "kp", "prv", "pub", "prvPem", "certPem", "cakey", "issuer", "sanEntries", "isClient", "validityDays", "sanArray", "entry", "dnsPrefixed", "dns", "extensions"]
|
|
7
|
-
}
|