@interopio/bridge 0.0.1-alpha
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/bin/bridge.js +9 -0
- package/gen/instance/GeneratedBuildInfo.ts +4 -0
- package/license.md +5 -0
- package/package.json +40 -0
- package/readme.md +10 -0
- package/src/cluster/Address.ts +57 -0
- package/src/cluster/Cluster.ts +13 -0
- package/src/cluster/Endpoint.ts +5 -0
- package/src/cluster/Member.ts +9 -0
- package/src/cluster/MembershipListener.ts +6 -0
- package/src/config/Config.ts +100 -0
- package/src/config/DiscoveryConfig.ts +21 -0
- package/src/config/Duration.ts +168 -0
- package/src/config/KubernetesConfig.ts +7 -0
- package/src/config/NamedDiscoveryConfig.ts +17 -0
- package/src/config/Properties.ts +49 -0
- package/src/config/index.ts +1 -0
- package/src/discovery/SimpleDiscoveryNode.ts +14 -0
- package/src/discovery/index.ts +207 -0
- package/src/discovery/multicast/MulticastDiscoveryStrategy.ts +141 -0
- package/src/discovery/multicast/MulticastDiscoveryStrategyFactory.ts +30 -0
- package/src/discovery/multicast/MulticastProperties.ts +4 -0
- package/src/discovery/settings.ts +37 -0
- package/src/error/RequestFailure.ts +48 -0
- package/src/gossip/ApplicationState.ts +48 -0
- package/src/gossip/EndpointState.ts +141 -0
- package/src/gossip/FailureDetector.ts +235 -0
- package/src/gossip/Gossiper.ts +1133 -0
- package/src/gossip/HeartbeatState.ts +66 -0
- package/src/gossip/Messenger.ts +130 -0
- package/src/gossip/VersionedValue.ts +59 -0
- package/src/index.ts +3 -0
- package/src/instance/AddressPicker.ts +245 -0
- package/src/instance/BridgeNode.ts +141 -0
- package/src/instance/ClusterTopologyIntentTracker.ts +4 -0
- package/src/io/VersionedSerializer.ts +230 -0
- package/src/io/util.ts +117 -0
- package/src/kubernetes/DnsEndpointResolver.ts +70 -0
- package/src/kubernetes/KubernetesApiEndpointResolver.ts +111 -0
- package/src/kubernetes/KubernetesApiProvider.ts +75 -0
- package/src/kubernetes/KubernetesClient.ts +264 -0
- package/src/kubernetes/KubernetesConfig.ts +130 -0
- package/src/kubernetes/KubernetesDiscoveryStrategy.ts +30 -0
- package/src/kubernetes/KubernetesDiscoveryStrategyFactory.ts +71 -0
- package/src/kubernetes/KubernetesEndpointResolver.ts +43 -0
- package/src/kubernetes/KubernetesProperties.ts +22 -0
- package/src/license/BridgeLicenseValidator.ts +19 -0
- package/src/license/LicenseValidator.ts +114 -0
- package/src/license/types.ts +40 -0
- package/src/logging.ts +22 -0
- package/src/main.mts +53 -0
- package/src/net/Action.ts +143 -0
- package/src/net/AddressSerializer.ts +44 -0
- package/src/net/ByteBufferAllocator.ts +27 -0
- package/src/net/FrameDecoder.ts +314 -0
- package/src/net/FrameEncoder.ts +138 -0
- package/src/net/HandshakeProtocol.ts +143 -0
- package/src/net/InboundConnection.ts +108 -0
- package/src/net/InboundConnectionInitiator.ts +150 -0
- package/src/net/InboundMessageHandler.ts +377 -0
- package/src/net/InboundSink.ts +38 -0
- package/src/net/Message.ts +428 -0
- package/src/net/OutboundConnection.ts +1141 -0
- package/src/net/OutboundConnectionInitiator.ts +76 -0
- package/src/net/RequestCallbacks.ts +148 -0
- package/src/net/ResponseHandler.ts +30 -0
- package/src/net/ShareableBytes.ts +125 -0
- package/src/net/internal/AsyncResourceExecutor.ts +464 -0
- package/src/net/internal/AsyncSocketPromise.ts +37 -0
- package/src/net/internal/channel/ChannelHandlerAdapter.ts +99 -0
- package/src/net/internal/channel/types.ts +188 -0
- package/src/utils/bigint.ts +23 -0
- package/src/utils/buffer.ts +434 -0
- package/src/utils/clock.ts +148 -0
- package/src/utils/collections.ts +283 -0
- package/src/utils/crc.ts +39 -0
- package/src/utils/internal/IpAddressUtil.ts +161 -0
- package/src/utils/memory/BufferPools.ts +40 -0
- package/src/utils/network.ts +130 -0
- package/src/utils/promise.ts +38 -0
- package/src/utils/uuid.ts +5 -0
- package/src/utils/vint.ts +238 -0
- package/src/version/MemberVersion.ts +42 -0
- package/src/version/Version.ts +12 -0
package/bin/bridge.js
ADDED
package/license.md
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
# License Information
|
|
2
|
+
|
|
3
|
+
This package is subject to the interop.io Developer License Agreement. For full details, please visit the link below. If you have questions regarding licensing or usage, reach out to our support team at [support@interop.io](mailto:support@interop.io).
|
|
4
|
+
|
|
5
|
+
- [Interop.io Developer License Agreement](https://interop.io/developer-license-agreement/)
|
package/package.json
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@interopio/bridge",
|
|
3
|
+
"version": "0.0.1-alpha",
|
|
4
|
+
"license": "see license in license.md",
|
|
5
|
+
"author": "interop.io",
|
|
6
|
+
"homepage": "https://docs.interop.io/bridge",
|
|
7
|
+
"keywords": [
|
|
8
|
+
"io.bridge",
|
|
9
|
+
"bridge",
|
|
10
|
+
"io.connect",
|
|
11
|
+
"glue42",
|
|
12
|
+
"interop.io"
|
|
13
|
+
],
|
|
14
|
+
"type": "module",
|
|
15
|
+
"exports": {
|
|
16
|
+
"./package.json": "./package.json",
|
|
17
|
+
".": {
|
|
18
|
+
"import": "./src/index.ts"
|
|
19
|
+
}
|
|
20
|
+
},
|
|
21
|
+
"bin": {
|
|
22
|
+
"bridge": "./bin/bridge.js"
|
|
23
|
+
},
|
|
24
|
+
"scripts": {
|
|
25
|
+
"test": "mocha test --recursive"
|
|
26
|
+
},
|
|
27
|
+
"dependencies": {
|
|
28
|
+
"@interopio/gateway-server": "^0.5.0-beta",
|
|
29
|
+
"dotenv": "^17.2.0",
|
|
30
|
+
"jsrsasign": "^11.1.0",
|
|
31
|
+
"nanoid": "^5.0.9"
|
|
32
|
+
},
|
|
33
|
+
"devDependencies": {
|
|
34
|
+
"@types/jsrsasign": "^10.5.15",
|
|
35
|
+
"rand-seed": "^3.0.0"
|
|
36
|
+
},
|
|
37
|
+
"engines": {
|
|
38
|
+
"node": ">=22.2.0"
|
|
39
|
+
}
|
|
40
|
+
}
|
package/readme.md
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import {getByName, type IpAddress} from '../utils/network.ts';
|
|
2
|
+
import {type Comparator, compareArrays} from '../utils/collections.ts';
|
|
3
|
+
|
|
4
|
+
export type Address = Readonly<{type: 4 | 6, host: string, port: number, address: IpAddress}>;
|
|
5
|
+
export function addressAsString(address: Pick<Address, 'host' | 'port' | 'type'>): string {
|
|
6
|
+
// todo: do better for ipv6
|
|
7
|
+
return `${address.host}:${address.port}`;
|
|
8
|
+
}
|
|
9
|
+
export async function toIpAddress(address: Address): Promise<IpAddress> {
|
|
10
|
+
return await getByName(scopedHost(address));
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export async function fromHostname(hostname: string, port: number): Promise<Address> {
|
|
14
|
+
return new AddressImpl(hostname, await getByName(hostname), port);
|
|
15
|
+
}
|
|
16
|
+
export function fromIpAddress(address: IpAddress, port: number): Address {
|
|
17
|
+
|
|
18
|
+
return new AddressImpl(undefined, address, port);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
class AddressImpl {
|
|
22
|
+
readonly type: 4 | 6;
|
|
23
|
+
readonly host: string;
|
|
24
|
+
readonly port: number;
|
|
25
|
+
readonly address: IpAddress;
|
|
26
|
+
|
|
27
|
+
constructor(hostname: string | undefined, ipAddress: IpAddress, port : number) {
|
|
28
|
+
const type = ipAddress.family;
|
|
29
|
+
if (type !== 4 && type !== 6) {
|
|
30
|
+
throw new Error('unexpected address type');
|
|
31
|
+
}
|
|
32
|
+
this.type= type;
|
|
33
|
+
this.host = hostname ?? ipAddress.address;
|
|
34
|
+
this.port = port;
|
|
35
|
+
this.address = ipAddress;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function scopedHost(address: Address): string {
|
|
40
|
+
if (address.type === 6) {
|
|
41
|
+
return `[${address.host}]`;
|
|
42
|
+
}
|
|
43
|
+
return address.host;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export const compareAddress: Comparator<Address> = (a1: Address, a2: Address) => {
|
|
47
|
+
const addressBytes1 = a1.address.addressBytes;
|
|
48
|
+
const addressBytes2 = a2.address.addressBytes;
|
|
49
|
+
const result = compareArrays(addressBytes1, 0, addressBytes1.length, addressBytes2, 0, addressBytes2.length, (b1, b2) => {
|
|
50
|
+
return b1 === b2 ? 0 : (b1 > b2 ? 1 : -1);
|
|
51
|
+
});
|
|
52
|
+
if (result !== 0) {
|
|
53
|
+
return result;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
return a1.port === a2.port ? 0 : (a1.port > a2.port ? 1 : -1);
|
|
57
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { Version } from '../version/Version.ts';
|
|
2
|
+
import { type Member } from './Member.ts';
|
|
3
|
+
import { type MembershipListener } from './MembershipListener.ts';
|
|
4
|
+
|
|
5
|
+
type RegistrationId = number
|
|
6
|
+
|
|
7
|
+
interface Cluster {
|
|
8
|
+
addMembershipListener(listener: MembershipListener) : RegistrationId
|
|
9
|
+
readonly version: Version
|
|
10
|
+
readonly members: ReadonlySet<Member>
|
|
11
|
+
readonly localMember: Member
|
|
12
|
+
readonly clusterTime: number
|
|
13
|
+
}
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
import {type Properties, type PropertyType} from './Properties.ts';
|
|
2
|
+
import {KubernetesConfig} from './KubernetesConfig.ts';
|
|
3
|
+
import {
|
|
4
|
+
DefaultDiscoveryConfig,
|
|
5
|
+
type DiscoveryConfig
|
|
6
|
+
} from './DiscoveryConfig.ts';
|
|
7
|
+
|
|
8
|
+
export class AutoDetectionConfig {
|
|
9
|
+
enabled: boolean = true;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export interface MulticastConfig {
|
|
13
|
+
readonly group: string
|
|
14
|
+
readonly port: number
|
|
15
|
+
}
|
|
16
|
+
class DefaultMulticastConfig implements MulticastConfig {
|
|
17
|
+
enabled: boolean = false;
|
|
18
|
+
group: string = '239.1.2.3';
|
|
19
|
+
port: number = 34567;
|
|
20
|
+
}
|
|
21
|
+
export interface TcpIpConfig {
|
|
22
|
+
members: string[]
|
|
23
|
+
}
|
|
24
|
+
class DefaultTcpIpConfig implements TcpIpConfig {
|
|
25
|
+
enabled: boolean = false;
|
|
26
|
+
members: string[] = [];
|
|
27
|
+
}
|
|
28
|
+
export class JoinConfig {
|
|
29
|
+
private _multicast= new DefaultMulticastConfig();
|
|
30
|
+
private _tcpIp = new DefaultTcpIpConfig();
|
|
31
|
+
private _kubernetes = new KubernetesConfig();
|
|
32
|
+
private _discovery = new DefaultDiscoveryConfig();
|
|
33
|
+
private _autoDetection = new AutoDetectionConfig();
|
|
34
|
+
|
|
35
|
+
get discovery(): DiscoveryConfig {
|
|
36
|
+
return this._discovery;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
get multicast(): MulticastConfig {
|
|
40
|
+
return this._multicast;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
get autoDetectionEnabled(): boolean {
|
|
44
|
+
return this._autoDetection.enabled
|
|
45
|
+
&& !this._multicast.enabled
|
|
46
|
+
&& !this._tcpIp.enabled
|
|
47
|
+
&& !this._kubernetes.enabled
|
|
48
|
+
&& !this._discovery.enabled;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export class NetworkConfig {
|
|
53
|
+
public static readonly DEFAULT_PORT: number = 8383;
|
|
54
|
+
port: number = NetworkConfig.DEFAULT_PORT;
|
|
55
|
+
reuseAddress: boolean;
|
|
56
|
+
publicAddress?: string;
|
|
57
|
+
join = new JoinConfig();
|
|
58
|
+
|
|
59
|
+
constructor() {
|
|
60
|
+
this.reuseAddress = process.platform !== "win32";
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
export class ServerConfig {
|
|
65
|
+
port?: number = undefined;
|
|
66
|
+
host?: string = undefined;
|
|
67
|
+
mesh: {
|
|
68
|
+
ping?: number
|
|
69
|
+
} = {};
|
|
70
|
+
gateway: {
|
|
71
|
+
enabled?: boolean,
|
|
72
|
+
contexts: {
|
|
73
|
+
lifetime: 'retained' | 'ref-counted' | 'ownership'
|
|
74
|
+
}
|
|
75
|
+
} = {
|
|
76
|
+
contexts: {
|
|
77
|
+
lifetime: 'retained'
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
export class Config {
|
|
83
|
+
properties: Properties = {};
|
|
84
|
+
network = new NetworkConfig();
|
|
85
|
+
license: string
|
|
86
|
+
readonly server: ServerConfig;
|
|
87
|
+
constructor() {
|
|
88
|
+
this.server = new ServerConfig();
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
get<T extends PropertyType>(name: string): T {
|
|
92
|
+
const value = this.properties[name] as T;
|
|
93
|
+
return value;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
set<T extends PropertyType>(name: string, value: T): Config {
|
|
97
|
+
this.properties[name] = value;
|
|
98
|
+
return this;
|
|
99
|
+
}
|
|
100
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type {DiscoveryServiceFactory, DiscoveryStrategyFactory} from '../discovery/index.ts';
|
|
2
|
+
import type {Properties} from './Properties.ts';
|
|
3
|
+
|
|
4
|
+
export interface DiscoveryStrategyConfig {
|
|
5
|
+
readonly discoveryStrategyFactory?: DiscoveryStrategyFactory
|
|
6
|
+
readonly properties: Properties
|
|
7
|
+
}
|
|
8
|
+
export interface DiscoveryConfig {
|
|
9
|
+
readonly discoveryStrategyConfigs: ReadonlyArray<DiscoveryStrategyConfig>
|
|
10
|
+
readonly discoveryServiceFactory?: DiscoveryServiceFactory
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export class DefaultDiscoveryConfig implements DiscoveryConfig
|
|
14
|
+
{
|
|
15
|
+
discoveryStrategyConfigs: DiscoveryStrategyConfig[] = [];
|
|
16
|
+
discoveryServiceFactory?: DiscoveryServiceFactory;
|
|
17
|
+
|
|
18
|
+
get enabled() {
|
|
19
|
+
return this.discoveryStrategyConfigs.length > 0;
|
|
20
|
+
}
|
|
21
|
+
}
|
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
const UNITS_PATTERN = /^(\d+)(d|h|m|s|ms|us|µs|ns)$/;
|
|
2
|
+
|
|
3
|
+
export type Unit = keyof typeof SCALES;
|
|
4
|
+
|
|
5
|
+
export function unitForSymbol(symbol: string): Unit {
|
|
6
|
+
switch (symbol.toLowerCase()) {
|
|
7
|
+
case 'ns':
|
|
8
|
+
return 'nanoseconds';
|
|
9
|
+
case 'us':
|
|
10
|
+
case 'µs':
|
|
11
|
+
return 'microseconds';
|
|
12
|
+
case 'ms':
|
|
13
|
+
return 'milliseconds';
|
|
14
|
+
case 's':
|
|
15
|
+
return 'seconds';
|
|
16
|
+
case 'm':
|
|
17
|
+
return 'minutes';
|
|
18
|
+
case 'h':
|
|
19
|
+
return 'hours';
|
|
20
|
+
case 'd':
|
|
21
|
+
return 'days';
|
|
22
|
+
}
|
|
23
|
+
throw new Error(`Unsupported unit symbol: ${symbol}`);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export function symbolForUnit(unit: Unit): string {
|
|
27
|
+
switch (unit) {
|
|
28
|
+
case 'days': return 'd';
|
|
29
|
+
case 'hours': return 'h';
|
|
30
|
+
case 'minutes': return 'm';
|
|
31
|
+
case 'seconds': return 's';
|
|
32
|
+
case 'milliseconds': return 'ms';
|
|
33
|
+
case 'microseconds': return 'us';
|
|
34
|
+
case 'nanoseconds': return 'ns';
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
const SCALES: { [key :string ]: bigint } = {
|
|
39
|
+
nanoseconds: 1n,
|
|
40
|
+
microseconds: 1000n,
|
|
41
|
+
milliseconds: 1000000n,
|
|
42
|
+
seconds: 1000000000n,
|
|
43
|
+
minutes: 60n * 1000000000n,
|
|
44
|
+
hours: 60n * 60n * 1000000000n,
|
|
45
|
+
days: 24n * 60n * 60n * 1000000000n,
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function toNanos(unit: Unit, duration: number | bigint) {
|
|
49
|
+
const scale = SCALES[unit];
|
|
50
|
+
const ns = BigInt(duration) * scale;
|
|
51
|
+
return ns;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
function toMicros(unit: Unit, duration: number | bigint): bigint {
|
|
55
|
+
const scale = SCALES[unit];
|
|
56
|
+
let microScale = SCALES.microseconds;
|
|
57
|
+
if (scale <= microScale) {
|
|
58
|
+
return scale === microScale ? BigInt(duration) : BigInt(duration) / (microScale / scale);
|
|
59
|
+
}
|
|
60
|
+
else {
|
|
61
|
+
return BigInt(duration) * (scale / microScale);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
function toMillis(unit: Unit, duration: number | bigint) : bigint {
|
|
66
|
+
const scale = SCALES[unit];
|
|
67
|
+
let milliScale = SCALES.milliseconds;
|
|
68
|
+
if (scale <= milliScale) {
|
|
69
|
+
return scale === milliScale ? BigInt(duration) : BigInt(duration) / (milliScale / scale);
|
|
70
|
+
}
|
|
71
|
+
else {
|
|
72
|
+
return BigInt(duration) * (scale / milliScale);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
function toSeconds(unit: Unit, duration: number | bigint): bigint {
|
|
77
|
+
const scale = SCALES[unit];
|
|
78
|
+
const secondScale = SCALES.seconds;
|
|
79
|
+
if (scale <= secondScale) {
|
|
80
|
+
return scale === secondScale ? BigInt(duration) : BigInt(duration) / (secondScale / scale);
|
|
81
|
+
}
|
|
82
|
+
else {
|
|
83
|
+
return BigInt(duration) * (scale / secondScale);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
export function convert(targetUnit: Unit, quantity: number | bigint, sourceUnit: Unit): bigint {
|
|
88
|
+
switch (targetUnit) {
|
|
89
|
+
case 'nanoseconds': return toNanos(sourceUnit, quantity);
|
|
90
|
+
case 'microseconds': return toMicros(sourceUnit, quantity);
|
|
91
|
+
case 'milliseconds': return toMillis(sourceUnit, quantity);
|
|
92
|
+
case 'seconds': return toSeconds(sourceUnit, quantity);
|
|
93
|
+
default: throw new Error('not implemented');
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
abstract class Duration {
|
|
98
|
+
readonly unit: Unit;
|
|
99
|
+
readonly quantity: number;
|
|
100
|
+
|
|
101
|
+
/*protected*/ constructor(value: string, minUnit: Unit, max?: number) {
|
|
102
|
+
const match = UNITS_PATTERN.exec(value);
|
|
103
|
+
if (!match) {
|
|
104
|
+
throw new Error(`Invalid duration: ${value}`);
|
|
105
|
+
}
|
|
106
|
+
this.quantity = parseInt(match[1]);
|
|
107
|
+
this.unit = unitForSymbol(match[2]);
|
|
108
|
+
if (max) {
|
|
109
|
+
if (SCALES[this.unit] < SCALES[minUnit]) {
|
|
110
|
+
throw new Error(`Duration must be at least ${minUnit}`);
|
|
111
|
+
}
|
|
112
|
+
if (convert(minUnit, this.quantity, this.unit) > max) {
|
|
113
|
+
throw new Error(`Invalid Duration: ${value}. Must be at most ${max - 1} in ${minUnit}`);
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
export class NanosecondsDuration extends Duration {
|
|
120
|
+
constructor(value: string) {
|
|
121
|
+
super(value, 'nanoseconds', Number.MAX_SAFE_INTEGER);
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
toNanoseconds() {
|
|
125
|
+
return toNanos(this.unit, this.quantity);
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
export class MicrosecondsDuration extends Duration {
|
|
129
|
+
constructor(value: string) {
|
|
130
|
+
super(value, 'microseconds', Number.MAX_SAFE_INTEGER);
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
toMicroseconds() {
|
|
134
|
+
return convert('microseconds', this.quantity, this.unit);
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
toSeconds() {
|
|
138
|
+
return convert('seconds', this.quantity, this.unit);
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
export class MillisecondsDuration extends Duration {
|
|
143
|
+
constructor(value: string) {
|
|
144
|
+
super(value, 'milliseconds', Number.MAX_SAFE_INTEGER);
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
toMilliseconds() {
|
|
148
|
+
return convert('milliseconds', this.quantity, this.unit);
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
export class SecondsDuration extends Duration {
|
|
153
|
+
constructor(value: string) {
|
|
154
|
+
super(value, 'seconds', Number.MAX_SAFE_INTEGER);
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
toMilliseconds() {
|
|
158
|
+
return toMillis(this.unit, this.quantity);
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
toNanoSeconds() {
|
|
162
|
+
return toNanos(this.unit, this.quantity);
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
toSeconds() {
|
|
166
|
+
return toSeconds(this.unit, this.quantity);
|
|
167
|
+
}
|
|
168
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
export abstract class NamedDiscoveryConfig<T extends NamedDiscoveryConfig<T>> {
|
|
2
|
+
readonly tag;
|
|
3
|
+
private _enabled: boolean = false;
|
|
4
|
+
|
|
5
|
+
constructor(tag: string) {
|
|
6
|
+
this.tag = tag;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
setEnabled(enabled: boolean): T {
|
|
10
|
+
this._enabled = enabled;
|
|
11
|
+
return this as unknown as T;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
get enabled(): boolean {
|
|
15
|
+
return this._enabled;
|
|
16
|
+
}
|
|
17
|
+
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
export interface PropertyDefinition {
|
|
2
|
+
readonly key: string
|
|
3
|
+
readonly optional: boolean
|
|
4
|
+
readonly converter: PropertyTypeConverter<PropertyType>
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
export type PropertyType = string | number | boolean;
|
|
8
|
+
export type Properties = { [key: string]: PropertyType };
|
|
9
|
+
|
|
10
|
+
export interface PropertyTypeConverter<T extends PropertyType> {
|
|
11
|
+
convert(value: unknown): T
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export const BOOLEAN = new class implements PropertyTypeConverter<boolean> {
|
|
15
|
+
convert(value: unknown): boolean {
|
|
16
|
+
if (typeof value === 'boolean') {
|
|
17
|
+
return value;
|
|
18
|
+
}
|
|
19
|
+
else if (typeof value === "string") {
|
|
20
|
+
return value.toLowerCase() === "true";
|
|
21
|
+
}
|
|
22
|
+
throw new Error("Cannot convert value to boolean: " + value);
|
|
23
|
+
}
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
export const STRING = new class implements PropertyTypeConverter<string> {
|
|
27
|
+
convert(value: unknown): string {
|
|
28
|
+
if (value === undefined) {
|
|
29
|
+
throw new Error("Cannot convert undefined ");
|
|
30
|
+
}
|
|
31
|
+
return String(value);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export const NUMBER = new class implements PropertyTypeConverter<number> {
|
|
36
|
+
convert(value: unknown): number {
|
|
37
|
+
if (value === undefined) {
|
|
38
|
+
throw new Error("Cannot convert undefined ");
|
|
39
|
+
}
|
|
40
|
+
if (typeof value === "object") {
|
|
41
|
+
throw new Error("Cannot convert object/array to number: " + value);
|
|
42
|
+
}
|
|
43
|
+
return Number(value);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export function property<T extends PropertyType>(name: string, converter: PropertyTypeConverter<T>): PropertyDefinition {
|
|
48
|
+
return {key: name, optional: true, converter: converter};
|
|
49
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import {type DiscoveryNode} from './index.ts';
|
|
2
|
+
import {type Address} from '../cluster/Address.ts';
|
|
3
|
+
|
|
4
|
+
export class SimpleDiscoveryNode implements DiscoveryNode {
|
|
5
|
+
readonly privateAddress: Address;
|
|
6
|
+
readonly publicAddress: Address;
|
|
7
|
+
readonly properties: ReadonlyMap<string, unknown>;
|
|
8
|
+
constructor(privateAddress: Address, publicAddress?: Address, properties?: ReadonlyMap<string, unknown>)
|
|
9
|
+
{
|
|
10
|
+
this.privateAddress = privateAddress;
|
|
11
|
+
this.publicAddress = publicAddress ?? privateAddress;
|
|
12
|
+
this.properties = properties ?? new Map();
|
|
13
|
+
}
|
|
14
|
+
}
|