@hydranium/protocol 1.0.0-next.117 → 1.0.0-next.123

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.
Files changed (60) hide show
  1. package/lib/client/data-connection.d.ts +28 -66
  2. package/lib/client/data-connection.d.ts.map +1 -1
  3. package/lib/client/data-connection.js +41 -112
  4. package/lib/client/data-connection.js.map +1 -1
  5. package/lib/client/data-session.d.ts +168 -93
  6. package/lib/client/data-session.d.ts.map +1 -1
  7. package/lib/client/data-session.js +291 -147
  8. package/lib/client/data-session.js.map +1 -1
  9. package/lib/data/data-protocol-methods.d.ts +2 -2
  10. package/lib/data/data-protocol-methods.d.ts.map +1 -1
  11. package/lib/data/data-protocol-methods.js +4 -0
  12. package/lib/data/data-protocol-methods.js.map +1 -1
  13. package/lib/data/data-server-protocol.d.ts +66 -30
  14. package/lib/data/data-server-protocol.d.ts.map +1 -1
  15. package/lib/data/requests.d.ts +67 -11
  16. package/lib/data/requests.d.ts.map +1 -1
  17. package/lib/errors.d.ts +66 -4
  18. package/lib/errors.d.ts.map +1 -1
  19. package/lib/errors.js +96 -5
  20. package/lib/errors.js.map +1 -1
  21. package/lib/messages/index.d.ts +1 -0
  22. package/lib/messages/index.d.ts.map +1 -1
  23. package/lib/messages/index.js +4 -1
  24. package/lib/messages/index.js.map +1 -1
  25. package/lib/model-server.d.ts +13 -1
  26. package/lib/model-server.d.ts.map +1 -1
  27. package/lib/model-server.js.map +1 -1
  28. package/lib/model-service/based-on.d.ts +2 -0
  29. package/lib/model-service/based-on.d.ts.map +1 -1
  30. package/lib/model-service/based-on.js +3 -0
  31. package/lib/model-service/based-on.js.map +1 -1
  32. package/lib/model-service/index.d.ts +0 -1
  33. package/lib/model-service/index.d.ts.map +1 -1
  34. package/lib/model-service/index.js +3 -5
  35. package/lib/model-service/index.js.map +1 -1
  36. package/lib/patch-merge.d.ts +28 -27
  37. package/lib/patch-merge.d.ts.map +1 -1
  38. package/lib/patch-merge.js +66 -22
  39. package/lib/patch-merge.js.map +1 -1
  40. package/lib/transfer-document.d.ts +2 -3
  41. package/lib/transfer-document.d.ts.map +1 -1
  42. package/lib/transfer-document.js.map +1 -1
  43. package/package.json +1 -1
  44. package/src/client/data-connection.ts +69 -119
  45. package/src/client/data-session.ts +348 -177
  46. package/src/data/data-protocol-methods.ts +4 -0
  47. package/src/data/data-server-protocol.ts +74 -30
  48. package/src/data/requests.ts +71 -11
  49. package/src/errors.ts +101 -4
  50. package/src/messages/index.ts +1 -0
  51. package/src/model-server.ts +13 -5
  52. package/src/model-service/based-on.ts +3 -0
  53. package/src/model-service/index.ts +3 -5
  54. package/src/patch-merge.ts +90 -37
  55. package/src/transfer-document.ts +2 -3
  56. package/lib/model-service/args.d.ts +0 -65
  57. package/lib/model-service/args.d.ts.map +0 -1
  58. package/lib/model-service/args.js +0 -11
  59. package/lib/model-service/args.js.map +0 -1
  60. package/src/model-service/args.ts +0 -69
@@ -10,10 +10,10 @@ import { type DataClientProtocol, type DataServerProtocol, type DiagnosticOf, ty
10
10
  import type { TransferElement } from '../transfer-element';
11
11
  import { DataEvents } from './data-events';
12
12
  import type { DataPort } from './data-port';
13
- import { DataSession } from './data-session';
13
+ import { DataSession, type DataSessionFactory } from './data-session';
14
14
  import { RpcConnection, type RpcConnectionLifecycle } from './rpc-connection';
15
15
  /** Options for {@link DataConnection}. */
16
- export interface DataConnectionOptions extends RpcConnectionLifecycle {
16
+ export interface DataConnectionOptions<TTransfer extends TransferElement = TransferElement, TServer extends DataServerProtocol<TTransfer, DiagnosticOf<TServer>> = DataServerProtocol<TTransfer>> extends RpcConnectionLifecycle {
17
17
  /**
18
18
  * Wire namespace the server is addressed under. Defaults to the
19
19
  * framework's {@link DATA_SERVER_WIRE_PREFIX}, which is what an unmodified
@@ -22,9 +22,11 @@ export interface DataConnectionOptions extends RpcConnectionLifecycle {
22
22
  * "Unhandled method" rather than failing at wire-up.
23
23
  */
24
24
  readonly methodNamespace?: string;
25
+ /** Builds the sessions `createSession` hands out. Defaults to a plain {@link DataSession}. */
26
+ readonly sessionFactory?: DataSessionFactory<TTransfer, TServer>;
25
27
  }
26
28
  /** {@link DataConnectionOptions} for a client that does not speak {@link DataClientProtocol}. */
27
- export interface DataConnectionOptionsWithMethods<TClient extends object> extends DataConnectionOptions {
29
+ export interface DataConnectionOptionsWithMethods<TClient extends object, TTransfer extends TransferElement = TransferElement, TServer extends DataServerProtocol<TTransfer, DiagnosticOf<TServer>> = DataServerProtocol<TTransfer>> extends DataConnectionOptions<TTransfer, TServer> {
28
30
  /**
29
31
  * Method names of the client to bind as inbound handlers. Declare it
30
32
  * `as const satisfies ReadonlyArray<keyof YourClient & string>` so the list
@@ -40,14 +42,14 @@ export interface DataConnectionOptionsWithMethods<TClient extends object> extend
40
42
  * request/response-only client binding the default list fails at wire-up. The
41
43
  * conditional turns that into a compile error.
42
44
  */
43
- export type DataConnectionArgs<TTransfer extends TransferElement, TClient extends object> = TClient extends DataClientProtocol<TTransfer> ? [options?: DataConnectionOptions & Partial<DataConnectionOptionsWithMethods<TClient>>] : [options: DataConnectionOptionsWithMethods<TClient>];
45
+ export type DataConnectionArgs<TTransfer extends TransferElement, TClient extends object, TServer extends DataServerProtocol<TTransfer, DiagnosticOf<TServer>> = DataServerProtocol<TTransfer>> = TClient extends DataClientProtocol<TTransfer> ? [options?: DataConnectionOptions<TTransfer, TServer> & Partial<DataConnectionOptionsWithMethods<TClient, TTransfer, TServer>>] : [options: DataConnectionOptionsWithMethods<TClient, TTransfer, TServer>];
44
46
  /**
45
47
  * A {@link RpcConnection} to the data head, carrying as many participants as
46
48
  * the host has interested parties.
47
49
  *
48
50
  * Document operations live on the participants rather than here: they carry a
49
51
  * `clientId`, which identifies a participant rather than a wire, and the server
50
- * keys its holds and watches per `(uri, clientId)`. Two parties sharing one
52
+ * keys its opens and watches per `(uri, clientId)`. Two parties sharing one
51
53
  * identity cannot tell each other's writes from their own echoes.
52
54
  *
53
55
  * Generic over the transfer root so this file names no grammar. An adopter
@@ -56,74 +58,34 @@ export type DataConnectionArgs<TTransfer extends TransferElement, TClient extend
56
58
  */
57
59
  export declare class DataConnection<TTransfer extends TransferElement, TServer extends DataServerProtocol<TTransfer, DiagnosticOf<TServer>> = DataServerProtocol<TTransfer>, TClient extends object = DataClientProtocol<TTransfer>> extends RpcConnection<TServer, TClient> {
58
60
  protected readonly sessions: Set<DataSession<TTransfer, TServer>>;
61
+ protected readonly sessionFactory: DataSessionFactory<TTransfer, TServer>;
62
+ constructor(port: DataPort, client: TClient, ...rest: DataConnectionArgs<TTransfer, TClient, TServer>);
59
63
  /**
60
- * Holds a disposed session could not release, keyed `clientId` + `uri` so a
61
- * repeated handover does not queue the same close twice.
62
- *
63
- * Retried on this connection's own activity rather than on a timer: every
64
- * session operation asks for the proxy, so a shared connection with anything
65
- * else going on supplies the occasions, and one with nothing going on has
66
- * nobody whose documents the stale hold could affect.
67
- */
68
- protected readonly orphanedHolds: Map<string, {
69
- readonly uri: string;
70
- readonly clientId: string;
71
- }>;
72
- /** Guards {@link releaseOrphanedHolds} against re-entering through its own proxy lookup. */
73
- protected releasingOrphans: boolean;
74
- constructor(port: DataPort, client: TClient, ...rest: DataConnectionArgs<TTransfer, TClient>);
75
- /**
76
- * Mint a participant on this connection under `clientId`.
77
- *
78
- * `clientId` must be distinct per participant and stable for its lifetime:
79
- * it keys the server's per-`(uri, clientId)` hold and watch, and it is the
80
- * echo key an inbound `onDocumentUpdated` is matched against.
64
+ * Start a participant on this connection, registered with the server under a
65
+ * fresh id, `label` plus `#` plus a random UUID, or under `clientId` when
66
+ * given. Synchronous: the registration is sent at once, and the session's
67
+ * calls wait for it. The server refuses an id live anywhere in its process,
68
+ * and every call of that session then rejects with a
69
+ * `DuplicateClientIdError` code.
81
70
  *
82
71
  * Throws for an id in {@link FRAMEWORK_CLIENT_IDS} — those are authors the
83
72
  * SERVER emits rather than participants, so a session holding one would read
84
- * the framework's own broadcasts as its own echoes and drop them. Nothing
85
- * about that fails on its own: the document simply stops following, which
86
- * looks like a dead connection.
87
- *
88
- * Throws, too, for an id a LIVE session on this connection already holds.
89
- * Per-document membership is a set of client ids, so two participants
90
- * sharing one collapse to a single hold and the first close releases it
91
- * under the survivor, which then stops receiving updates for a document it
92
- * is still showing. A constant bound once per participant KIND — one per
93
- * widget class rather than per instance — satisfies the type and violates
94
- * this.
95
- *
96
- * And throws for an id whose previous session left a hold this connection is
97
- * still trying to release. That release closes `(uri, clientId)`, so an id
98
- * reissued while it is outstanding can have the new participant's hold closed
99
- * out from under it. The id frees itself as soon as the release lands.
100
- *
101
- * The checks span this connection only, so a head several connections reach
102
- * can still be addressed twice under one id.
73
+ * the framework's own broadcasts as its own echoes and drop them — and for
74
+ * an id a live session on this connection already holds.
103
75
  */
104
- createSession(clientId: string): DataSession<TTransfer, TServer>;
76
+ createSession(label?: string, clientId?: string): DataSession<TTransfer, TServer>;
105
77
  /**
106
- * Take over a `(uri, clientId)` hold a disposed session could not release.
107
- *
108
- * One attempt is made straight away, since whatever refused the session's
109
- * close may already be over; a refusal leaves the entry for the next
110
- * operation on this connection to retry.
111
- */
112
- protected orphanHold(uri: string, clientId: string): void;
113
- /**
114
- * Try to close every hold handed over by a disposed session, keeping the ones
115
- * the server still refuses.
116
- *
117
- * Errors are swallowed per entry: this runs off another participant's
118
- * operation, which has no interest in an unrelated release and no caller to
119
- * surface it to. A hold that survives every attempt is released by the
120
- * server's connection-close cleanup.
78
+ * After the transport dropped, reconnect on the next macrotask for the
79
+ * sessions with documents open, so they re-watch and follow their documents
80
+ * again without waiting for a call of their own; a session with nothing
81
+ * open restores on its next call. Nothing is scheduled once this connection
82
+ * is disposed, which drops its generation too.
121
83
  */
122
- protected releaseOrphanedHolds(): Promise<void>;
84
+ protected dropGeneration(): void;
123
85
  /**
124
- * Sessions are detached rather than disposed: the server releases every hold
125
- * on a connection it sees close, so closing each document first sends
126
- * requests over a connection this call is about to dispose.
86
+ * Sessions are detached rather than disposed: the server ends every session
87
+ * on a connection it sees close, so ending each one first sends requests
88
+ * over a connection this call is about to dispose.
127
89
  */
128
90
  dispose(): void;
129
91
  }
@@ -145,7 +107,7 @@ export declare class DataConnection<TTransfer extends TransferElement, TServer e
145
107
  export declare class DataConnectionWithEvents<TTransfer extends TransferElement, TServer extends DataServerProtocol<TTransfer, DiagnosticOf<TServer>> = DataServerProtocol<TTransfer>> extends DataConnection<TTransfer, TServer, DataEvents<TTransfer, DiagnosticOf<TServer>, ProjectOf<TServer>>> {
146
108
  /** Server pushes, fanned out to as many local listeners as the host has. */
147
109
  readonly events: DataEvents<TTransfer, DiagnosticOf<TServer>, ProjectOf<TServer>>;
148
- constructor(port: DataPort, options?: DataConnectionOptions);
110
+ constructor(port: DataPort, options?: DataConnectionOptions<TTransfer, TServer>);
149
111
  /** Disposes the fan-out it created, which no caller else holds. */
150
112
  dispose(): void;
151
113
  }
@@ -1 +1 @@
1
- {"version":3,"file":"data-connection.d.ts","sourceRoot":"","sources":["../../src/client/data-connection.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"data-connection.d.ts","sourceRoot":"","sources":["../../src/client/data-connection.ts"],"names":[],"mappings":"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"}
@@ -20,7 +20,7 @@ const rpc_connection_1 = require("./rpc-connection");
20
20
  *
21
21
  * Document operations live on the participants rather than here: they carry a
22
22
  * `clientId`, which identifies a participant rather than a wire, and the server
23
- * keys its holds and watches per `(uri, clientId)`. Two parties sharing one
23
+ * keys its opens and watches per `(uri, clientId)`. Two parties sharing one
24
24
  * identity cannot tell each other's writes from their own echoes.
25
25
  *
26
26
  * Generic over the transfer root so this file names no grammar. An adopter
@@ -29,18 +29,7 @@ const rpc_connection_1 = require("./rpc-connection");
29
29
  */
30
30
  class DataConnection extends rpc_connection_1.RpcConnection {
31
31
  sessions = new Set();
32
- /**
33
- * Holds a disposed session could not release, keyed `clientId` + `uri` so a
34
- * repeated handover does not queue the same close twice.
35
- *
36
- * Retried on this connection's own activity rather than on a timer: every
37
- * session operation asks for the proxy, so a shared connection with anything
38
- * else going on supplies the occasions, and one with nothing going on has
39
- * nobody whose documents the stale hold could affect.
40
- */
41
- orphanedHolds = new Map();
42
- /** Guards {@link releaseOrphanedHolds} against re-entering through its own proxy lookup. */
43
- releasingOrphans = false;
32
+ sessionFactory;
44
33
  constructor(port, client, ...rest) {
45
34
  const [options = {}] = rest;
46
35
  super(port, client, {
@@ -51,130 +40,70 @@ class DataConnection extends rpc_connection_1.RpcConnection {
51
40
  clientMethods: options.clientMethods ?? data_1.DATA_CLIENT_PROTOCOL_METHODS,
52
41
  lifecycle: options
53
42
  });
43
+ this.sessionFactory =
44
+ options.sessionFactory ?? ((clientId, host, label) => new data_session_1.DataSession(clientId, host, label));
54
45
  }
55
46
  /**
56
- * Mint a participant on this connection under `clientId`.
57
- *
58
- * `clientId` must be distinct per participant and stable for its lifetime:
59
- * it keys the server's per-`(uri, clientId)` hold and watch, and it is the
60
- * echo key an inbound `onDocumentUpdated` is matched against.
47
+ * Start a participant on this connection, registered with the server under a
48
+ * fresh id, `label` plus `#` plus a random UUID, or under `clientId` when
49
+ * given. Synchronous: the registration is sent at once, and the session's
50
+ * calls wait for it. The server refuses an id live anywhere in its process,
51
+ * and every call of that session then rejects with a
52
+ * `DuplicateClientIdError` code.
61
53
  *
62
54
  * Throws for an id in {@link FRAMEWORK_CLIENT_IDS} — those are authors the
63
55
  * SERVER emits rather than participants, so a session holding one would read
64
- * the framework's own broadcasts as its own echoes and drop them. Nothing
65
- * about that fails on its own: the document simply stops following, which
66
- * looks like a dead connection.
67
- *
68
- * Throws, too, for an id a LIVE session on this connection already holds.
69
- * Per-document membership is a set of client ids, so two participants
70
- * sharing one collapse to a single hold and the first close releases it
71
- * under the survivor, which then stops receiving updates for a document it
72
- * is still showing. A constant bound once per participant KIND — one per
73
- * widget class rather than per instance — satisfies the type and violates
74
- * this.
75
- *
76
- * And throws for an id whose previous session left a hold this connection is
77
- * still trying to release. That release closes `(uri, clientId)`, so an id
78
- * reissued while it is outstanding can have the new participant's hold closed
79
- * out from under it. The id frees itself as soon as the release lands.
80
- *
81
- * The checks span this connection only, so a head several connections reach
82
- * can still be addressed twice under one id.
56
+ * the framework's own broadcasts as its own echoes and drop them — and for
57
+ * an id a live session on this connection already holds.
83
58
  */
84
- createSession(clientId) {
59
+ createSession(label = 'session', clientId) {
85
60
  this.assertLive();
86
- if (client_ids_1.FRAMEWORK_CLIENT_IDS.includes(clientId)) {
87
- throw new Error(`clientId '${clientId}' is reserved by the framework and cannot identify a participant`);
61
+ const id = clientId ?? `${label}#${globalThis.crypto.randomUUID()}`;
62
+ if (client_ids_1.FRAMEWORK_CLIENT_IDS.includes(id)) {
63
+ throw new Error(`clientId '${id}' is reserved by the framework and cannot identify a participant`);
88
64
  }
89
- if ([...this.sessions].some(session => session.clientId === clientId)) {
90
- throw new Error(`clientId '${clientId}' already identifies a live participant on this connection`);
65
+ if ([...this.sessions].some(session => session.clientId === id)) {
66
+ throw new Error(`clientId '${id}' already identifies a live participant on this connection`);
91
67
  }
92
- if ([...this.orphanedHolds.values()].some(hold => hold.clientId === clientId)) {
93
- // A retry is still outstanding for this id, and it closes
94
- // `(uri, clientId)` — which the server keys one hold per. Handing the
95
- // id over now lets that close land on the NEW participant's hold
96
- // instead, taking a document away from a session that opened it
97
- // successfully. Nothing about that fails on its own: the document
98
- // simply stops following, and the close that did it was issued for a
99
- // participant already gone.
100
- //
101
- // Attempted first, so an id whose release has become possible is free
102
- // by the next call rather than waiting for other traffic.
103
- void this.releaseOrphanedHolds();
104
- throw new Error(`clientId '${clientId}' has an unreleased hold on this connection and cannot identify a new participant yet`);
105
- }
106
- const session = new data_session_1.DataSession(clientId, {
107
- connected: async () => {
108
- const server = await this.connected();
109
- // Unawaited: a session's own operation must not wait on, or fail
110
- // for, the release of a hold another participant abandoned.
111
- void this.releaseOrphanedHolds();
112
- return server;
113
- },
68
+ const session = this.sessionFactory(id, {
69
+ connected: () => this.connected(),
114
70
  releaseSession: released => this.sessions.delete(released),
115
- orphanHold: (uri, holder) => this.orphanHold(uri, holder)
116
- });
71
+ reportError: (error, reported) => this.reportError(error, reported)
72
+ }, label);
117
73
  this.sessions.add(session);
74
+ // A failure reaches the session's own calls, which wait for the same
75
+ // registration; caught here only so it is not also reported unhandled.
76
+ session.connected().catch(() => undefined);
118
77
  return session;
119
78
  }
120
79
  /**
121
- * Take over a `(uri, clientId)` hold a disposed session could not release.
122
- *
123
- * One attempt is made straight away, since whatever refused the session's
124
- * close may already be over; a refusal leaves the entry for the next
125
- * operation on this connection to retry.
126
- */
127
- orphanHold(uri, clientId) {
128
- this.orphanedHolds.set(`${clientId}\u0000${uri}`, { uri, clientId });
129
- void this.releaseOrphanedHolds();
130
- }
131
- /**
132
- * Try to close every hold handed over by a disposed session, keeping the ones
133
- * the server still refuses.
134
- *
135
- * Errors are swallowed per entry: this runs off another participant's
136
- * operation, which has no interest in an unrelated release and no caller to
137
- * surface it to. A hold that survives every attempt is released by the
138
- * server's connection-close cleanup.
80
+ * After the transport dropped, reconnect on the next macrotask for the
81
+ * sessions with documents open, so they re-watch and follow their documents
82
+ * again without waiting for a call of their own; a session with nothing
83
+ * open restores on its next call. Nothing is scheduled once this connection
84
+ * is disposed, which drops its generation too.
139
85
  */
140
- async releaseOrphanedHolds() {
141
- if (this.releasingOrphans || this.orphanedHolds.size === 0 || this.disposed) {
142
- return;
143
- }
144
- this.releasingOrphans = true;
145
- try {
146
- const server = await this.connected();
147
- for (const [key, hold] of [...this.orphanedHolds]) {
148
- try {
149
- await server.closeModelDocument({ uri: hold.uri, clientId: hold.clientId });
150
- this.orphanedHolds.delete(key);
151
- }
152
- catch {
153
- // Kept for the next occasion.
86
+ dropGeneration() {
87
+ const dropped = this.generation !== undefined;
88
+ super.dropGeneration();
89
+ if (dropped && !this.disposed) {
90
+ setTimeout(() => {
91
+ if (!this.disposed) {
92
+ this.sessions.forEach(session => session.reconnect());
154
93
  }
155
- }
156
- }
157
- catch {
158
- // No proxy to release through; the entries wait for one.
159
- }
160
- finally {
161
- this.releasingOrphans = false;
94
+ }, 0);
162
95
  }
163
96
  }
164
97
  /**
165
- * Sessions are detached rather than disposed: the server releases every hold
166
- * on a connection it sees close, so closing each document first sends
167
- * requests over a connection this call is about to dispose.
98
+ * Sessions are detached rather than disposed: the server ends every session
99
+ * on a connection it sees close, so ending each one first sends requests
100
+ * over a connection this call is about to dispose.
168
101
  */
169
102
  dispose() {
170
103
  for (const session of [...this.sessions]) {
171
104
  session.detach();
172
105
  }
173
106
  this.sessions.clear();
174
- // Dropped rather than closed, for the same reason the sessions detach: the
175
- // server releases every hold on a connection it sees close, and the close
176
- // would travel over the connection being disposed.
177
- this.orphanedHolds.clear();
178
107
  super.dispose();
179
108
  }
180
109
  }
@@ -1 +1 @@
1
- {"version":3,"file":"data-connection.js","sourceRoot":"","sources":["../../src/client/data-connection.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"data-connection.js","sourceRoot":"","sources":["../../src/client/data-connection.ts"],"names":[],"mappings":"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"}