Skip to content

Commit 572b04a

Browse files
committed
[basics] First end to end
1 parent 1bd404f commit 572b04a

10 files changed

Lines changed: 184 additions & 42 deletions

File tree

src/@types/connector/value/index.d.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
/// connector/value
22

3-
import type {Connector, Timestamp, Value} from '../../index.js';
3+
import type {Connector, Timestamp, Value} from '../../index.d.ts';
44

55
export class ValueConnector extends Connector {
6+
valueChanged(): Promise<void>;
7+
68
getValue(): Promise<Value>;
79

810
setValue(value: Value): Promise<void>;

src/@types/index.d.ts

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,24 @@ export type Timestamp = string;
66

77
export type Value = string | number | boolean | null | DeletedValue;
88

9+
export type Address = string[];
10+
911
export class Connector {
1012
getConnected(): boolean;
1113

1214
connect(): Promise<void>;
1315

1416
disconnect(): Promise<void>;
17+
18+
nodeChanged(address: Address): Promise<void>;
19+
20+
getNode(address: Address): Promise<Value>;
21+
22+
getNodeTimestamp(address: Address): Promise<Timestamp>;
23+
24+
setNode(address: Address, value: Value): Promise<void>;
25+
26+
setNodeTimestamp(address: Address, timestamp: Timestamp): Promise<void>;
1527
}
1628
type BaseConnector = Connector;
1729

@@ -22,9 +34,9 @@ export class Transport {
2234

2335
disconnect(): Promise<void>;
2436

25-
send(data: any): Promise<void>;
37+
send(message: any): Promise<void>;
2638

27-
receive(): Promise<any>;
39+
receive(message: string): Promise<any>;
2840
}
2941
type BaseTransport = Transport;
3042

src/connector/value/index.ts

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,33 @@
11
import {Connector} from '@synclets';
2-
import type {Timestamp, Value} from '@synclets/@types';
2+
import type {Address, Timestamp, Value} from '@synclets/@types';
33
import type {ValueConnector as ValueConnectorDecl} from '@synclets/@types/connector/value';
44

55
export class ValueConnector extends Connector implements ValueConnectorDecl {
6+
async getNode(_address: Address): Promise<Value> {
7+
return this.getValue();
8+
}
9+
10+
async getNodeTimestamp(_address: Address): Promise<Timestamp> {
11+
return this.getValueTimestamp();
12+
}
13+
14+
async setNode(_address: Address, value: Value): Promise<void> {
15+
this.setValue(value);
16+
}
17+
18+
async setNodeTimestamp(
19+
_address: Address,
20+
timestamp: Timestamp,
21+
): Promise<void> {
22+
this.setValueTimestamp(timestamp);
23+
}
24+
25+
// ---
26+
27+
async valueChanged(): Promise<void> {
28+
await this.nodeChanged([]);
29+
}
30+
631
async getValue(): Promise<Value> {
732
return null;
833
}

src/core/connector.ts

Lines changed: 29 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,15 @@
1-
import type {Connector as ConnectorDecl, Synclet} from '@synclets/@types';
1+
import type {
2+
Address,
3+
Connector as ConnectorDecl,
4+
Timestamp,
5+
Value,
6+
} from '@synclets/@types';
27
import {errorNew} from '@synclets/utils';
3-
import type {ProtectedConnector} from './protected.d.ts';
8+
import type {ProtectedConnector, ProtectedSynclet} from './protected.d.ts';
49

510
export class Connector implements ProtectedConnector<ConnectorDecl> {
611
#connected: boolean = false;
7-
#synclet: Synclet | undefined;
12+
#synclet: ProtectedSynclet | undefined;
813

914
getConnected(): boolean {
1015
return this.#connected;
@@ -13,13 +18,33 @@ export class Connector implements ProtectedConnector<ConnectorDecl> {
1318
async connect(): Promise<void> {
1419
this.#connected = true;
1520
}
21+
1622
async disconnect(): Promise<void> {
1723
this.#connected = false;
1824
}
1925

26+
async nodeChanged(address: Address): Promise<void> {
27+
this.#synclet?.sync(address);
28+
}
29+
30+
async getNode(_address: Address): Promise<Value> {
31+
return null;
32+
}
33+
34+
async getNodeTimestamp(_address: Address): Promise<Timestamp> {
35+
return '';
36+
}
37+
38+
async setNode(_address: Address, _value: Value): Promise<void> {}
39+
40+
async setNodeTimestamp(
41+
_address: Address,
42+
_timestamp: Timestamp,
43+
): Promise<void> {}
44+
2045
// ---
2146

22-
attachToSynclet(synclet: Synclet) {
47+
attachToSynclet(synclet: ProtectedSynclet): void {
2348
if (this.#synclet) {
2449
errorNew('Connector is already attached to a Synclet');
2550
}

src/core/protected.d.ts

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,17 @@
1-
import type {Connector, Synclet, Transport} from '@synclets/@types';
1+
import type {Address, Connector, Synclet, Transport} from '@synclets/@types';
22

33
export type ProtectedConnector<C extends Connector = Connector> = C & {
4-
attachToSynclet: (synclet: Synclet) => void;
4+
attachToSynclet: (synclet: ProtectedSynclet) => void;
55
};
66

77
export type ProtectedTransport<T extends Transport = Transport> = T & {
8-
attachToSynclet: (synclet: Synclet) => void;
8+
attachToSynclet: (synclet: ProtectedSynclet) => void;
9+
};
10+
11+
export type ProtectedSynclet<
12+
C extends Connector = Connector,
13+
T extends Transport = Transport,
14+
> = Synclet<C, T> & {
15+
sync: (address: Address) => Promise<void>;
16+
receive(message: string): Promise<void>;
917
};

src/core/synclet.ts

Lines changed: 34 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,18 @@
11
import type {
2+
Address,
23
Connector as BaseConnector,
34
Transport as BaseTransport,
4-
Synclet as SyncletDecl,
55
} from '@synclets/@types';
6-
import type {ProtectedConnector, ProtectedTransport} from './protected.d.ts';
6+
import type {
7+
ProtectedConnector,
8+
ProtectedSynclet,
9+
ProtectedTransport,
10+
} from './protected.d.ts';
711

812
export class Synclet<
913
Connector extends BaseConnector = BaseConnector,
1014
Transport extends BaseTransport = BaseTransport,
11-
> implements SyncletDecl<Connector, Transport>
15+
> implements ProtectedSynclet<Connector, Transport>
1216
{
1317
#connector: ProtectedConnector<Connector>;
1418
#transport: ProtectedTransport<Transport>;
@@ -35,14 +39,40 @@ export class Synclet<
3539
}
3640

3741
async start() {
38-
await this.#connector.connect();
3942
await this.#transport.connect();
43+
await this.#connector.connect();
4044
this.#started = true;
45+
46+
await this.sync([]);
4147
}
4248

4349
async stop() {
4450
await this.#connector.disconnect();
4551
await this.#transport.disconnect();
4652
this.#started = false;
4753
}
54+
55+
// ---
56+
57+
async sync(address: Address): Promise<void> {
58+
if (!this.#started) {
59+
return;
60+
}
61+
await this.#transport.send(
62+
JSON.stringify({
63+
address,
64+
node: await this.#connector.getNode(address),
65+
}),
66+
);
67+
}
68+
69+
async receive(message: string): Promise<void> {
70+
if (!this.#started) {
71+
return;
72+
}
73+
const {address, node} = JSON.parse(message);
74+
if (address && node) {
75+
await this.#connector.setNode(address, node);
76+
}
77+
}
4878
}

src/core/transport.ts

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
1-
import type {Synclet, Transport as TransportDecl} from '@synclets/@types';
1+
import type {Transport as TransportDecl} from '@synclets/@types';
22
import {errorNew} from '@synclets/utils';
3-
import type {ProtectedTransport} from './protected.d.ts';
3+
import type {ProtectedSynclet, ProtectedTransport} from './protected.d.ts';
44

55
export class Transport implements ProtectedTransport<TransportDecl> {
66
#connected: boolean = false;
7-
#synclet: Synclet | undefined;
7+
#synclet: ProtectedSynclet | undefined;
88

99
getConnected(): boolean {
1010
return this.#connected;
@@ -18,13 +18,15 @@ export class Transport implements ProtectedTransport<TransportDecl> {
1818
this.#connected = false;
1919
}
2020

21-
async send(): Promise<void> {}
21+
async send(_message: string): Promise<void> {}
2222

23-
async receive(): Promise<any> {}
23+
async receive(message: string): Promise<any> {
24+
await this.#synclet?.receive(message);
25+
}
2426

2527
// ---
2628

27-
attachToSynclet(synclet: Synclet) {
29+
attachToSynclet(synclet: ProtectedSynclet) {
2830
if (this.#synclet) {
2931
errorNew('Transport is already attached to a Synclet');
3032
}

src/transport/memory/index.ts

Lines changed: 23 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,32 +1,46 @@
11
import {Transport} from '@synclets';
22
import type {MemoryTransport as MemoryTransportDecl} from '@synclets/@types/transport/memory';
3-
import {getUniqueId, mapDel, mapEnsure, mapNew, mapSet} from '@synclets/utils';
3+
import {
4+
getUniqueId,
5+
mapDel,
6+
mapEnsure,
7+
mapForEach,
8+
mapGet,
9+
mapNew,
10+
mapSet,
11+
} from '@synclets/utils';
412

513
const clientPools: Map<string, Map<string, MemoryTransport>> = mapNew();
614

715
export class MemoryTransport extends Transport implements MemoryTransportDecl {
8-
#clientId: string;
16+
#id: string;
917
#poolId: string;
1018

1119
constructor(private poolId: string = 'default') {
1220
super();
13-
this.#clientId = getUniqueId();
21+
this.#id = getUniqueId();
1422
this.#poolId = poolId;
1523
}
1624

1725
async connect(): Promise<void> {
18-
mapSet(mapEnsure(clientPools, this.#poolId, mapNew), this.#clientId, this);
26+
mapSet(mapEnsure(clientPools, this.#poolId, mapNew), this.#id, this);
1927
}
2028

2129
async disconnect(): Promise<void> {
22-
mapDel(mapEnsure(clientPools, this.#poolId, mapNew), this.#clientId);
30+
mapDel(mapEnsure(clientPools, this.#poolId, mapNew), this.#id);
2331
}
2432

25-
async send(): Promise<void> {}
26-
27-
async receive(): Promise<any> {}
33+
async send(message: string): Promise<void> {
34+
// eslint-disable-next-line no-console
35+
console.log(this.#id, 'sent', message);
36+
mapForEach(mapGet(clientPools, this.#poolId), (id, transport) => {
37+
if (id !== this.#id) {
38+
transport.receive(message);
39+
}
40+
});
41+
}
2842

2943
getClientId(): string {
30-
return this.#clientId;
44+
return this.#id;
3145
}
3246
}

src/utils/map.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,3 +29,8 @@ export const mapEnsure = <Key, Value>(
2929
}
3030
return mapGet(map, key) as Value;
3131
};
32+
33+
export const mapForEach = <Key, Value>(
34+
map: Map<Key, Value> | undefined,
35+
cb: (key: Key, value: Value) => void,
36+
): void => map?.forEach((value, key) => cb(key, value));

test/unit/value-connector.test.ts

Lines changed: 31 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,47 @@
1-
import {Synclet} from 'synclets';
1+
import {Synclet, Timestamp, Value} from 'synclets';
22
import {ValueConnector} from 'synclets/connector/value';
33
import {MemoryTransport} from 'synclets/transport/memory';
44

55
test('value sync', async () => {
66
class TestValueConnector extends ValueConnector {
7-
private value: string = '';
7+
#value: Value = 'V1';
8+
#timestamp: Timestamp = '';
9+
810
async getValue() {
9-
return this.value;
11+
return this.#value;
12+
}
13+
async setValue(value: Value) {
14+
this.#value = value;
1015
}
11-
async setValue(value: string) {
12-
this.value = value;
16+
async getValueTimestamp() {
17+
return this.#timestamp;
1318
}
19+
async setValueTimestamp(timestamp: Timestamp) {
20+
this.#timestamp = timestamp;
21+
}
22+
1423
getUnderlyingValue() {
15-
return this.value;
24+
return this.#value;
1625
}
17-
setUnderlyingValue(value: string) {
18-
this.value = value;
26+
async setUnderlyingValue(value: Value) {
27+
this.#value = value;
28+
await this.valueChanged();
1929
}
2030
}
2131

22-
const synclet1 = new Synclet(new TestValueConnector(), new MemoryTransport());
23-
const synclet2 = new Synclet(new TestValueConnector(), new MemoryTransport());
32+
const connector1 = new TestValueConnector();
33+
const connector2 = new TestValueConnector();
2434

25-
expect(synclet1.getConnector().getUnderlyingValue()).toEqual(
26-
synclet2.getConnector().getUnderlyingValue(),
35+
const synclet1 = new Synclet(connector1, new MemoryTransport());
36+
await synclet1.start();
37+
38+
const synclet2 = new Synclet(connector2, new MemoryTransport());
39+
await synclet2.start();
40+
41+
expect(connector1.getUnderlyingValue()).toEqual(
42+
connector2.getUnderlyingValue(),
2743
);
44+
45+
await connector1.setUnderlyingValue('V2');
46+
expect(connector2.getUnderlyingValue()).toEqual('V2');
2847
});

0 commit comments

Comments
 (0)