Skip to content

Commit 8ec0039

Browse files
committed
[core] Synclets can have multiple transport
1 parent b9ddabf commit 8ec0039

8 files changed

Lines changed: 144 additions & 88 deletions

File tree

src/@types/index.d.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ export type SyncletOptions = {
5757

5858
export function createSynclet(
5959
connector: Connector,
60-
transport: Transport,
60+
transport: Transport | Transport[],
6161
implementations?: SyncletImplementations,
6262
options?: SyncletOptions,
6363
): Promise<Synclet>;

src/common/queue.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import {arrayPush, arrayShift} from './array.ts';
22

3-
type Task = () => Promise<any>;
3+
export type Task = () => Promise<any>;
44

55
export const getQueueFunctions = (): [
66
queue: (...tasks: Task[]) => Promise<void>,

src/core/connector.ts

Lines changed: 20 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@ import type {
99
Hash,
1010
LogLevel,
1111
Meta,
12-
Synclet,
1312
Timestamp,
1413
} from '@synclets/@types';
1514
import {Tomb} from '@synclets/@types/utils';
@@ -27,8 +26,11 @@ import {
2726
size,
2827
} from '../common/other.ts';
2928
import {getQueueFunctions} from '../common/queue.ts';
30-
import {setEvery, setNew} from '../common/set.ts';
31-
import {ProtectedConnector} from './types.js';
29+
import {
30+
ProtectedConnector,
31+
ProtectedSynclet,
32+
ProtectedTransport,
33+
} from './types.js';
3234

3335
export const createConnector: typeof createConnectorDecl = async (
3436
depth,
@@ -51,10 +53,9 @@ export const createConnector: typeof createConnectorDecl = async (
5153
}
5254

5355
let connected = false;
56+
let boundSynclet: ProtectedSynclet | undefined;
5457
let id = options.id ?? getUniqueId();
5558

56-
const boundSynclets: Set<Synclet> = setNew();
57-
5859
const logger = options.logger ?? {};
5960

6061
const [getNextTimestamp, seenTimestamp] = getHlcFunctions(id);
@@ -105,18 +106,19 @@ export const createConnector: typeof createConnectorDecl = async (
105106
const log = (string: string, level: LogLevel = 'info') =>
106107
logger?.[level]?.(`[${id}/C] ${string}`);
107108

108-
const bind = (synclet: Synclet, syncletId: string) => {
109-
boundSynclets.add(synclet);
110-
if (boundSynclets.size == 1) {
111-
id = syncletId;
109+
const bind = (synclet: ProtectedSynclet, syncletId: string) => {
110+
if (boundSynclet) {
111+
errorNew('Connector is already attached to Synclet');
112112
}
113+
boundSynclet = synclet;
114+
id = syncletId;
113115
};
114116

115117
const setOrDelAtom = async (
116118
address: Address,
117119
atomOrUndefined: Atom | undefined,
118120
context: Context = {},
119-
syncOrFromSynclet: boolean | Synclet = true,
121+
syncOrFromTransport: boolean | ProtectedTransport = true,
120122
newTimestamp?: Timestamp,
121123
oldTimestamp?: Timestamp,
122124
) => {
@@ -154,13 +156,12 @@ export const createConnector: typeof createConnectorDecl = async (
154156
});
155157
}
156158
}
157-
if (syncOrFromSynclet) {
158-
boundSynclets.forEach((boundSynclet) => {
159-
if (boundSynclet !== syncOrFromSynclet) {
160-
arrayPush(tasks, () => boundSynclet.sync(address));
161-
}
162-
});
159+
if (syncOrFromTransport === true) {
160+
arrayPush(tasks, () => boundSynclet?.sync(address));
161+
} else if (syncOrFromTransport) {
162+
arrayPush(tasks, () => boundSynclet?._[0](address, syncOrFromTransport));
163163
}
164+
164165
await queue(...tasks);
165166
};
166167

@@ -177,13 +178,9 @@ export const createConnector: typeof createConnectorDecl = async (
177178

178179
disconnect: async () => {
179180
if (connected) {
180-
if (
181-
setEvery(boundSynclets, (boundSynclet) => !boundSynclet.isStarted())
182-
) {
183-
log('disconnect');
184-
await disconnect?.();
185-
connected = false;
186-
}
181+
log('disconnect');
182+
await disconnect?.();
183+
connected = false;
187184
}
188185
},
189186

src/core/synclet.ts

Lines changed: 67 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -6,22 +6,27 @@ import {
66
LogLevel,
77
ProtocolNode,
88
ProtocolSubNodes,
9-
Synclet,
109
SyncletImplementations,
1110
SyncletOptions,
1211
Timestamp,
1312
TimestampAndAtom,
1413
} from '@synclets/@types';
1514
import {getUniqueId} from '@synclets/utils';
16-
import {arrayDifference, arrayMap} from '../common/array.ts';
15+
import {
16+
arrayDifference,
17+
arrayForEach,
18+
arrayMap,
19+
arrayPush,
20+
isArray,
21+
} from '../common/array.ts';
1722
import {objKeys, objNotEmpty, objToArray} from '../common/object.ts';
1823
import {
1924
ifNotUndefined,
2025
isUndefined,
2126
promiseAll,
2227
size,
2328
} from '../common/other.ts';
24-
import {getQueueFunctions} from '../common/queue.ts';
29+
import {getQueueFunctions, Task} from '../common/queue.ts';
2530
import {
2631
ASTERISK,
2732
EMPTY_STRING,
@@ -35,7 +40,12 @@ import {
3540
isTimestamp,
3641
isTimestampAndAtom,
3742
} from '../common/types.ts';
38-
import {Message, ProtectedConnector, ProtectedTransport} from './types.js';
43+
import {
44+
Message,
45+
ProtectedConnector,
46+
ProtectedSynclet,
47+
ProtectedTransport,
48+
} from './types.js';
3949

4050
const VERSION = 1;
4151

@@ -53,17 +63,10 @@ export const createSynclet: typeof createSyncletDecl = (async (
5363
connectorSetOrDelAtom,
5464
],
5565
}: ProtectedConnector,
56-
{
57-
_: [
58-
transportBind,
59-
transportConnect,
60-
transportDisconnect,
61-
transportSendMessage,
62-
],
63-
}: ProtectedTransport,
66+
transport: ProtectedTransport | ProtectedTransport[],
6467
{canReceiveMessage, getSendContext}: SyncletImplementations = {},
6568
options: SyncletOptions = {},
66-
): Promise<Synclet> => {
69+
): Promise<ProtectedSynclet> => {
6770
let started = false;
6871
const id = options.id ?? getUniqueId();
6972
const logger = options.logger ?? {};
@@ -75,6 +78,8 @@ export const createSynclet: typeof createSyncletDecl = (async (
7578
}
7679
};
7780

81+
const transports = isArray(transport) ? transport : [transport];
82+
7883
const readHashOrTimestamp = async (address: Address, context: Context) =>
7984
size(address) < connectorDepth
8085
? ((await connectorReadHash(address, context)) ?? 0)
@@ -117,19 +122,35 @@ export const createSynclet: typeof createSyncletDecl = (async (
117122
return [subNodeObj];
118123
};
119124

120-
const sync = (address: Address) =>
121-
queueIfStarted(async () => {
125+
const sync = (address: Address) => syncExcept(address);
126+
127+
const syncExcept = async (
128+
address: Address,
129+
exceptTransport?: ProtectedTransport,
130+
) => {
131+
if (started) {
122132
log(`sync ` + address);
123-
await sendNodeMessage(address, await readHashOrTimestamp(address, {}));
124-
});
133+
const hashOrTimestamp = await readHashOrTimestamp(address, {});
134+
const tasks: Task[] = [];
135+
arrayForEach(transports, (transport) => {
136+
if (transport !== exceptTransport) {
137+
arrayPush(tasks, () =>
138+
sendNodeMessage(transport, address, hashOrTimestamp),
139+
);
140+
}
141+
});
142+
await queue(...tasks);
143+
}
144+
};
125145

126146
const sendNodeMessage = async (
147+
transport: ProtectedTransport,
127148
address: Address,
128149
node: ProtocolNode,
129150
receivedContext: Context = {},
130151
to?: string,
131152
) =>
132-
await transportSendMessage(
153+
await transport._[3](
133154
[
134155
VERSION,
135156
0,
@@ -141,7 +162,11 @@ export const createSynclet: typeof createSyncletDecl = (async (
141162
to,
142163
);
143164

144-
const receiveMessage = (message: Message, from: string) =>
165+
const receiveMessage = (
166+
transport: ProtectedTransport,
167+
message: Message,
168+
from: string,
169+
) =>
145170
queueIfStarted(async () => {
146171
const [version, type, depth, address, node, context] = message;
147172

@@ -164,12 +189,13 @@ export const createSynclet: typeof createSyncletDecl = (async (
164189
return log(`can't receive message: ${from}`, WARN);
165190
}
166191

167-
await transformNode(address, node, context, (newNode) =>
168-
sendNodeMessage(address, newNode, context, from),
192+
await transformNode(transport, address, node, context, (newNode) =>
193+
sendNodeMessage(transport, address, newNode, context, from),
169194
);
170195
});
171196

172197
const transformNode = async (
198+
transport: ProtectedTransport,
173199
address: Address,
174200
node: ProtocolNode,
175201
context: Context,
@@ -187,12 +213,13 @@ export const createSynclet: typeof createSyncletDecl = (async (
187213
: isProtocolSubNodes(node)
188214
? transformSubNodes
189215
: () => log(INVALID_NODE + ' ' + address, WARN)) as any
190-
)?.(address, node, context),
216+
)?.(transport, address, node, context),
191217
ifTransformedToDefined,
192218
ifTransformedToUndefined,
193219
);
194220

195221
const transformTimestamp = async (
222+
_transport: ProtectedTransport,
196223
address: Address,
197224
otherTimestamp: Timestamp,
198225
context: Context,
@@ -211,6 +238,7 @@ export const createSynclet: typeof createSyncletDecl = (async (
211238
};
212239

213240
const transformTimestampAndAtom = async (
241+
transport: ProtectedTransport,
214242
address: Address,
215243
otherTimestampAndAtom: TimestampAndAtom,
216244
context: Context,
@@ -224,7 +252,7 @@ export const createSynclet: typeof createSyncletDecl = (async (
224252
address,
225253
otherAtom,
226254
context,
227-
synclet,
255+
transport,
228256
otherTimestamp,
229257
myTimestamp,
230258
);
@@ -237,6 +265,7 @@ export const createSynclet: typeof createSyncletDecl = (async (
237265
};
238266

239267
const transformHash = async (
268+
_transport: ProtectedTransport,
240269
address: Address,
241270
otherHash: Hash,
242271
context: Context,
@@ -263,6 +292,7 @@ export const createSynclet: typeof createSyncletDecl = (async (
263292
};
264293

265294
const transformSubNodes = async (
295+
transport: ProtectedTransport,
266296
address: Address,
267297
[otherSubNodeObj, partial]: ProtocolSubNodes,
268298
context: Context,
@@ -274,6 +304,7 @@ export const createSynclet: typeof createSyncletDecl = (async (
274304
await promiseAll(
275305
objToArray(otherSubNodeObj, (id, otherSubNode) =>
276306
transformNode(
307+
transport,
277308
[...address, id],
278309
otherSubNode,
279310
context,
@@ -297,14 +328,20 @@ export const createSynclet: typeof createSyncletDecl = (async (
297328
const log = (string: string, level: LogLevel = 'info') =>
298329
logger?.[level]?.(`[${id}] ${string}`);
299330

300-
const synclet: Synclet = {
331+
const synclet: ProtectedSynclet = {
301332
log,
302333

303334
start: async () => {
304335
if (!started) {
305336
log('start');
306337
await connectorConnect();
307-
await transportConnect(receiveMessage);
338+
await promiseAll(
339+
arrayMap(transports, (transport) =>
340+
transport._[1]((message: Message, from: string) =>
341+
receiveMessage(transport, message, from),
342+
),
343+
),
344+
);
308345
started = true;
309346
await sync([]);
310347
}
@@ -315,16 +352,19 @@ export const createSynclet: typeof createSyncletDecl = (async (
315352
log('stop');
316353
started = false;
317354
await connectorDisconnect();
318-
await transportDisconnect();
355+
await promiseAll(arrayMap(transports, (transport) => transport._[2]()));
319356
}
320357
},
321358

322359
isStarted: () => started,
323360

324361
sync,
362+
363+
_: [syncExcept],
325364
};
326365

327366
connectorBind(synclet, id);
328-
transportBind(synclet, id);
367+
arrayForEach(transports, (transport) => transport._[0](synclet, id));
368+
329369
return synclet;
330370
}) as any;

src/core/transport.ts

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
import type {
22
createTransport as createTransportDecl,
33
LogLevel,
4-
Synclet,
54
TransportImplementations,
65
TransportOptions,
76
} from '@synclets/@types';
@@ -10,7 +9,12 @@ import {arrayJoin, arrayMap} from '../common/array.ts';
109
import {mapEnsure, mapNew} from '../common/map.ts';
1110
import {errorNew, promiseAll, size} from '../common/other.ts';
1211
import {ASTERISK, SPACE} from '../common/string.ts';
13-
import {Message, ProtectedTransport, ReceiveMessage} from './types.js';
12+
import {
13+
Message,
14+
ProtectedSynclet,
15+
ProtectedTransport,
16+
ReceiveMessage,
17+
} from './types.js';
1418

1519
type Pending = [fragments: string[], due: number];
1620

@@ -99,7 +103,7 @@ export const createTransport: typeof createTransportDecl = async (
99103
options: TransportOptions = {},
100104
): Promise<ProtectedTransport> => {
101105
let connected = false;
102-
let boundSynclet: Synclet | undefined;
106+
let boundSynclet: ProtectedSynclet | undefined;
103107
let id = options.id ?? getUniqueId();
104108

105109
const logger = options.logger ?? {};
@@ -110,7 +114,7 @@ export const createTransport: typeof createTransportDecl = async (
110114
const [startBuffer, stopBuffer, receivePacket, sendPackets] =
111115
getPacketFunctions(log, sendPacket, options.fragmentSize ?? 4096);
112116

113-
const bind = (synclet: Synclet, syncletId: string) => {
117+
const bind = (synclet: ProtectedSynclet, syncletId: string) => {
114118
if (boundSynclet) {
115119
errorNew('Transport is already attached to Synclet');
116120
}

0 commit comments

Comments
 (0)