@hydranium/protocol 1.0.0-next.72 → 1.0.0-next.75
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/lib/client/data-connection.d.ts +11 -0
- package/lib/client/data-connection.d.ts.map +1 -1
- package/lib/client/data-connection.js +14 -0
- package/lib/client/data-connection.js.map +1 -1
- package/lib/client/data-session.d.ts +12 -0
- package/lib/client/data-session.d.ts.map +1 -1
- package/lib/client/data-session.js +35 -1
- package/lib/client/data-session.js.map +1 -1
- package/package.json +1 -1
- package/src/client/data-connection.ts +14 -0
- package/src/client/data-session.ts +35 -1
|
@@ -69,6 +69,17 @@ export declare class DataConnection<TTransfer extends TransferElement, TServer e
|
|
|
69
69
|
* the framework's own broadcasts as its own echoes and drop them. Nothing
|
|
70
70
|
* about that fails on its own: the document simply stops following, which
|
|
71
71
|
* looks like a dead connection.
|
|
72
|
+
*
|
|
73
|
+
* Throws, too, for an id a LIVE session on this connection already holds.
|
|
74
|
+
* Per-document membership is a set of client ids, so two participants
|
|
75
|
+
* sharing one collapse to a single hold and the first close releases it
|
|
76
|
+
* under the survivor, which then stops receiving updates for a document it
|
|
77
|
+
* is still showing. A constant bound once per participant KIND — one per
|
|
78
|
+
* widget class rather than per instance — satisfies the type and violates
|
|
79
|
+
* this.
|
|
80
|
+
*
|
|
81
|
+
* The check spans this connection only, so a head several connections reach
|
|
82
|
+
* can still be addressed twice under one id.
|
|
72
83
|
*/
|
|
73
84
|
createSession(clientId: string): DataSession<TTransfer, TServer>;
|
|
74
85
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"data-connection.d.ts","sourceRoot":"","sources":["../../src/client/data-connection.ts"],"names":[],"mappings":"AAAA;;;;;;;kFAOkF;AAGlF,OAAO,EAGJ,KAAK,kBAAkB,EACvB,KAAK,kBAAkB,EACvB,KAAK,YAAY,EACjB,KAAK,SAAS,EAChB,MAAM,SAAS,CAAC;AACjB,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAC;AAC3D,OAAO,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAC3C,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AAC5C,OAAO,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAC7C,OAAO,EAAE,aAAa,EAAE,KAAK,sBAAsB,EAAE,MAAM,kBAAkB,CAAC;AAE9E,0CAA0C;AAC1C,MAAM,WAAW,qBAAsB,SAAQ,sBAAsB;IAClE;;;;;;OAMG;IACH,QAAQ,CAAC,eAAe,CAAC,EAAE,MAAM,CAAC;CACpC;AAED,iGAAiG;AACjG,MAAM,WAAW,gCAAgC,CAAC,OAAO,SAAS,MAAM,CAAE,SAAQ,qBAAqB;IACpG;;;;OAIG;IACH,QAAQ,CAAC,aAAa,EAAE,SAAS,CAAC,MAAM,OAAO,GAAG,MAAM,CAAC,EAAE,CAAC;CAC9D;AAED;;;;;;;GAOG;AACH,MAAM,MAAM,kBAAkB,CAAC,SAAS,SAAS,eAAe,EAAE,OAAO,SAAS,MAAM,IACrF,OAAO,SAAS,kBAAkB,CAAC,SAAS,CAAC,GACxC,CAAC,OAAO,CAAC,EAAE,qBAAqB,GAAG,OAAO,CAAC,gCAAgC,CAAC,OAAO,CAAC,CAAC,CAAC,GACtF,CAAC,OAAO,EAAE,gCAAgC,CAAC,OAAO,CAAC,CAAC,CAAC;AAE7D;;;;;;;;;;;;GAYG;AACH,qBAAa,cAAc,CACxB,SAAS,SAAS,eAAe,EACjC,OAAO,SAAS,kBAAkB,CAAC,SAAS,EAAE,YAAY,CAAC,OAAO,CAAC,CAAC,GAAG,kBAAkB,CAAC,SAAS,CAAC,EACpG,OAAO,SAAS,MAAM,GAAG,kBAAkB,CAAC,SAAS,CAAC,CACvD,SAAQ,aAAa,CAAC,OAAO,EAAE,OAAO,CAAC;IACtC,SAAS,CAAC,QAAQ,CAAC,QAAQ,uCAA8C;gBAE7D,IAAI,EAAE,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,GAAG,IAAI,EAAE,kBAAkB,CAAC,SAAS,EAAE,OAAO,CAAC;IAY5F
|
|
1
|
+
{"version":3,"file":"data-connection.d.ts","sourceRoot":"","sources":["../../src/client/data-connection.ts"],"names":[],"mappings":"AAAA;;;;;;;kFAOkF;AAGlF,OAAO,EAGJ,KAAK,kBAAkB,EACvB,KAAK,kBAAkB,EACvB,KAAK,YAAY,EACjB,KAAK,SAAS,EAChB,MAAM,SAAS,CAAC;AACjB,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAC;AAC3D,OAAO,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAC3C,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AAC5C,OAAO,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAC7C,OAAO,EAAE,aAAa,EAAE,KAAK,sBAAsB,EAAE,MAAM,kBAAkB,CAAC;AAE9E,0CAA0C;AAC1C,MAAM,WAAW,qBAAsB,SAAQ,sBAAsB;IAClE;;;;;;OAMG;IACH,QAAQ,CAAC,eAAe,CAAC,EAAE,MAAM,CAAC;CACpC;AAED,iGAAiG;AACjG,MAAM,WAAW,gCAAgC,CAAC,OAAO,SAAS,MAAM,CAAE,SAAQ,qBAAqB;IACpG;;;;OAIG;IACH,QAAQ,CAAC,aAAa,EAAE,SAAS,CAAC,MAAM,OAAO,GAAG,MAAM,CAAC,EAAE,CAAC;CAC9D;AAED;;;;;;;GAOG;AACH,MAAM,MAAM,kBAAkB,CAAC,SAAS,SAAS,eAAe,EAAE,OAAO,SAAS,MAAM,IACrF,OAAO,SAAS,kBAAkB,CAAC,SAAS,CAAC,GACxC,CAAC,OAAO,CAAC,EAAE,qBAAqB,GAAG,OAAO,CAAC,gCAAgC,CAAC,OAAO,CAAC,CAAC,CAAC,GACtF,CAAC,OAAO,EAAE,gCAAgC,CAAC,OAAO,CAAC,CAAC,CAAC;AAE7D;;;;;;;;;;;;GAYG;AACH,qBAAa,cAAc,CACxB,SAAS,SAAS,eAAe,EACjC,OAAO,SAAS,kBAAkB,CAAC,SAAS,EAAE,YAAY,CAAC,OAAO,CAAC,CAAC,GAAG,kBAAkB,CAAC,SAAS,CAAC,EACpG,OAAO,SAAS,MAAM,GAAG,kBAAkB,CAAC,SAAS,CAAC,CACvD,SAAQ,aAAa,CAAC,OAAO,EAAE,OAAO,CAAC;IACtC,SAAS,CAAC,QAAQ,CAAC,QAAQ,uCAA8C;gBAE7D,IAAI,EAAE,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,GAAG,IAAI,EAAE,kBAAkB,CAAC,SAAS,EAAE,OAAO,CAAC;IAY5F;;;;;;;;;;;;;;;;;;;;;;;OAuBG;IACH,aAAa,CAAC,QAAQ,EAAE,MAAM,GAAG,WAAW,CAAC,SAAS,EAAE,OAAO,CAAC;IAgBhE;;;;OAIG;IACM,OAAO,IAAI,IAAI;CAO1B;AAED;;;;;;;;;;;;;;GAcG;AACH,qBAAa,wBAAwB,CAClC,SAAS,SAAS,eAAe,EACjC,OAAO,SAAS,kBAAkB,CAAC,SAAS,EAAE,YAAY,CAAC,OAAO,CAAC,CAAC,GAAG,kBAAkB,CAAC,SAAS,CAAC,CACrG,SAAQ,cAAc,CAAC,SAAS,EAAE,OAAO,EAAE,UAAU,CAAC,SAAS,EAAE,YAAY,CAAC,OAAO,CAAC,EAAE,SAAS,CAAC,OAAO,CAAC,CAAC,CAAC;IAC3G,4EAA4E;IAC5E,QAAQ,CAAC,MAAM,EAAE,UAAU,CAAC,SAAS,EAAE,YAAY,CAAC,OAAO,CAAC,EAAE,SAAS,CAAC,OAAO,CAAC,CAAC,CAAC;gBAEtE,IAAI,EAAE,QAAQ,EAAE,OAAO,CAAC,EAAE,qBAAqB;IAQ3D,mEAAmE;IAC1D,OAAO,IAAI,IAAI;CAI1B"}
|
|
@@ -52,12 +52,26 @@ class DataConnection extends rpc_connection_1.RpcConnection {
|
|
|
52
52
|
* the framework's own broadcasts as its own echoes and drop them. Nothing
|
|
53
53
|
* about that fails on its own: the document simply stops following, which
|
|
54
54
|
* looks like a dead connection.
|
|
55
|
+
*
|
|
56
|
+
* Throws, too, for an id a LIVE session on this connection already holds.
|
|
57
|
+
* Per-document membership is a set of client ids, so two participants
|
|
58
|
+
* sharing one collapse to a single hold and the first close releases it
|
|
59
|
+
* under the survivor, which then stops receiving updates for a document it
|
|
60
|
+
* is still showing. A constant bound once per participant KIND — one per
|
|
61
|
+
* widget class rather than per instance — satisfies the type and violates
|
|
62
|
+
* this.
|
|
63
|
+
*
|
|
64
|
+
* The check spans this connection only, so a head several connections reach
|
|
65
|
+
* can still be addressed twice under one id.
|
|
55
66
|
*/
|
|
56
67
|
createSession(clientId) {
|
|
57
68
|
this.assertLive();
|
|
58
69
|
if (client_ids_1.FRAMEWORK_CLIENT_IDS.includes(clientId)) {
|
|
59
70
|
throw new Error(`clientId '${clientId}' is reserved by the framework and cannot identify a participant`);
|
|
60
71
|
}
|
|
72
|
+
if ([...this.sessions].some(session => session.clientId === clientId)) {
|
|
73
|
+
throw new Error(`clientId '${clientId}' already identifies a live participant on this connection`);
|
|
74
|
+
}
|
|
61
75
|
const session = new data_session_1.DataSession(clientId, {
|
|
62
76
|
connected: () => this.connected(),
|
|
63
77
|
releaseSession: released => this.sessions.delete(released)
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"data-connection.js","sourceRoot":"","sources":["../../src/client/data-connection.ts"],"names":[],"mappings":";AAAA;;;;;;;kFAOkF;;;AAElF,8CAAqD;AACrD,kCAOiB;AAEjB,+CAA2C;AAE3C,iDAA6C;AAC7C,qDAA8E;AAqC9E;;;;;;;;;;;;GAYG;AACH,MAAa,cAIX,SAAQ,8BAA+B;IACnB,QAAQ,GAAG,IAAI,GAAG,EAAmC,CAAC;IAEzE,YAAY,IAAc,EAAE,MAAe,EAAE,GAAG,IAA4C;QACzF,MAAM,CAAC,OAAO,GAAG,EAAE,CAAC,GAAG,IAAuF,CAAC;QAC/G,KAAK,CAAC,IAAI,EAAE,MAAM,EAAE;YACjB,eAAe,EAAE,OAAO,CAAC,eAAe,IAAI,8BAAuB;YACnE,0DAA0D;YAC1D,sEAAsE;YACtE,mEAAmE;YACnE,aAAa,EAAE,OAAO,CAAC,aAAa,IAAK,mCAA+E;YACxH,SAAS,EAAE,OAAO;SACpB,CAAC,CAAC;IACN,CAAC;IAED
|
|
1
|
+
{"version":3,"file":"data-connection.js","sourceRoot":"","sources":["../../src/client/data-connection.ts"],"names":[],"mappings":";AAAA;;;;;;;kFAOkF;;;AAElF,8CAAqD;AACrD,kCAOiB;AAEjB,+CAA2C;AAE3C,iDAA6C;AAC7C,qDAA8E;AAqC9E;;;;;;;;;;;;GAYG;AACH,MAAa,cAIX,SAAQ,8BAA+B;IACnB,QAAQ,GAAG,IAAI,GAAG,EAAmC,CAAC;IAEzE,YAAY,IAAc,EAAE,MAAe,EAAE,GAAG,IAA4C;QACzF,MAAM,CAAC,OAAO,GAAG,EAAE,CAAC,GAAG,IAAuF,CAAC;QAC/G,KAAK,CAAC,IAAI,EAAE,MAAM,EAAE;YACjB,eAAe,EAAE,OAAO,CAAC,eAAe,IAAI,8BAAuB;YACnE,0DAA0D;YAC1D,sEAAsE;YACtE,mEAAmE;YACnE,aAAa,EAAE,OAAO,CAAC,aAAa,IAAK,mCAA+E;YACxH,SAAS,EAAE,OAAO;SACpB,CAAC,CAAC;IACN,CAAC;IAED;;;;;;;;;;;;;;;;;;;;;;;OAuBG;IACH,aAAa,CAAC,QAAgB;QAC3B,IAAI,CAAC,UAAU,EAAE,CAAC;QAClB,IAAI,iCAAoB,CAAC,QAAQ,CAAC,QAAQ,CAAC,EAAE,CAAC;YAC3C,MAAM,IAAI,KAAK,CAAC,aAAa,QAAQ,kEAAkE,CAAC,CAAC;QAC5G,CAAC;QACD,IAAI,CAAC,GAAG,IAAI,CAAC,QAAQ,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC,OAAO,CAAC,QAAQ,KAAK,QAAQ,CAAC,EAAE,CAAC;YACrE,MAAM,IAAI,KAAK,CAAC,aAAa,QAAQ,4DAA4D,CAAC,CAAC;QACtG,CAAC;QACD,MAAM,OAAO,GAAG,IAAI,0BAAW,CAAqB,QAAQ,EAAE;YAC3D,SAAS,EAAE,GAAG,EAAE,CAAC,IAAI,CAAC,SAAS,EAAE;YACjC,cAAc,EAAE,QAAQ,CAAC,EAAE,CAAC,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,QAAQ,CAAC;SAC5D,CAAC,CAAC;QACH,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;QAC3B,OAAO,OAAO,CAAC;IAClB,CAAC;IAED;;;;OAIG;IACM,OAAO;QACb,KAAK,MAAM,OAAO,IAAI,CAAC,GAAG,IAAI,CAAC,QAAQ,CAAC,EAAE,CAAC;YACxC,OAAO,CAAC,MAAM,EAAE,CAAC;QACpB,CAAC;QACD,IAAI,CAAC,QAAQ,CAAC,KAAK,EAAE,CAAC;QACtB,KAAK,CAAC,OAAO,EAAE,CAAC;IACnB,CAAC;CACH;AAvED,wCAuEC;AAED;;;;;;;;;;;;;;GAcG;AACH,MAAa,wBAGX,SAAQ,cAAoG;IAC3G,4EAA4E;IACnE,MAAM,CAAmE;IAElF,YAAY,IAAc,EAAE,OAA+B;QACxD,sEAAsE;QACtE,4BAA4B;QAC5B,MAAM,MAAM,GAAG,IAAI,wBAAU,EAAwD,CAAC;QACtF,KAAK,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,CAAC,CAAC;QAC7B,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;IACxB,CAAC;IAED,mEAAmE;IAC1D,OAAO;QACb,KAAK,CAAC,OAAO,EAAE,CAAC;QAChB,IAAI,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC;IACzB,CAAC;CACH;AApBD,4DAoBC"}
|
|
@@ -68,6 +68,12 @@ export declare class DataSession<TTransfer extends TransferElement, TServer exte
|
|
|
68
68
|
/** URIs this session holds open, so {@link dispose} can release exactly those. */
|
|
69
69
|
protected readonly openUris: Set<string>;
|
|
70
70
|
protected disposed: boolean;
|
|
71
|
+
/**
|
|
72
|
+
* Disposed through {@link detach} rather than {@link dispose}, so nothing may
|
|
73
|
+
* be sent. Folding it into {@link disposed} costs {@link openDocument} the
|
|
74
|
+
* distinction, and its close would then go out on a detached session.
|
|
75
|
+
*/
|
|
76
|
+
protected detached: boolean;
|
|
71
77
|
constructor(clientId: string, host: DataSessionHost<TTransfer, TServer>);
|
|
72
78
|
/**
|
|
73
79
|
* The connected, READY server proxy, for protocol methods this session does
|
|
@@ -92,6 +98,12 @@ export declare class DataSession<TTransfer extends TransferElement, TServer exte
|
|
|
92
98
|
* valid: `open` settles at the integrity landmark, not at validation.
|
|
93
99
|
* Validity arrives asynchronously on `onDocumentUpdated`, or synchronously
|
|
94
100
|
* from `getModelDocument({ includeDiagnostics: true })`.
|
|
101
|
+
*
|
|
102
|
+
* A dispose landing between the two awaits is handled here rather than in
|
|
103
|
+
* {@link dispose}, which cannot release a hold that does not exist yet: it
|
|
104
|
+
* reads {@link openUris}, and this method fills it only once both calls have
|
|
105
|
+
* returned. Left to `dispose`, the hold and its watch outlive the participant
|
|
106
|
+
* and go only when the connection closes.
|
|
95
107
|
*/
|
|
96
108
|
openDocument(args: DataSessionOpenArgs<TTransfer, TServer>): Promise<DataSessionDocument<TTransfer, TServer>>;
|
|
97
109
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"data-session.d.ts","sourceRoot":"","sources":["../../src/client/data-session.ts"],"names":[],"mappings":"AAAA;;;;;;;kFAOkF;AAElF,OAAO,KAAK,EAAE,kBAAkB,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAChE,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,QAAQ,CAAC;AACvC,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,sBAAsB,CAAC;AAC7D,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAC;AAE3D;;;;;;;;GAQG;AACH,MAAM,MAAM,eAAe,CAAC,OAAO,SAAS,CAAC,IAAI,EAAE,KAAK,KAAK,OAAO,IAAI,IAAI,CAAC,UAAU,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,EAAE,UAAU,CAAC,CAAC;AAEjH,0EAA0E;AAC1E,MAAM,MAAM,mBAAmB,CAC5B,SAAS,SAAS,eAAe,EACjC,OAAO,SAAS,kBAAkB,CAAC,SAAS,EAAE,YAAY,CAAC,OAAO,CAAC,CAAC,GAAG,kBAAkB,CAAC,SAAS,CAAC,IACnG,eAAe,CAAC,OAAO,CAAC,mBAAmB,CAAC,CAAC,CAAC;AAElD,2EAA2E;AAC3E,MAAM,MAAM,oBAAoB,CAC7B,SAAS,SAAS,eAAe,EACjC,OAAO,SAAS,kBAAkB,CAAC,SAAS,EAAE,YAAY,CAAC,OAAO,CAAC,CAAC,GAAG,kBAAkB,CAAC,SAAS,CAAC,IACnG,eAAe,CAAC,OAAO,CAAC,oBAAoB,CAAC,CAAC,CAAC;AAEnD,4EAA4E;AAC5E,MAAM,MAAM,qBAAqB,CAC9B,SAAS,SAAS,eAAe,EACjC,OAAO,SAAS,kBAAkB,CAAC,SAAS,EAAE,YAAY,CAAC,OAAO,CAAC,CAAC,GAAG,kBAAkB,CAAC,SAAS,CAAC,IACnG,eAAe,CAAC,OAAO,CAAC,qBAAqB,CAAC,CAAC,CAAC;AAEpD,6EAA6E;AAC7E,MAAM,MAAM,mBAAmB,CAC5B,SAAS,SAAS,eAAe,EACjC,OAAO,SAAS,kBAAkB,CAAC,SAAS,EAAE,YAAY,CAAC,OAAO,CAAC,CAAC,GAAG,kBAAkB,CAAC,SAAS,CAAC,IACnG,eAAe,CAAC,OAAO,CAAC,mBAAmB,CAAC,CAAC,CAAC;AAElD,iFAAiF;AACjF,MAAM,MAAM,mBAAmB,CAC5B,SAAS,SAAS,eAAe,EACjC,OAAO,SAAS,kBAAkB,CAAC,SAAS,EAAE,YAAY,CAAC,OAAO,CAAC,CAAC,GAAG,kBAAkB,CAAC,SAAS,CAAC,IACnG,gBAAgB,CAAC,SAAS,EAAE,YAAY,CAAC,OAAO,CAAC,CAAC,CAAC;AAEvD;;;;;GAKG;AACH,MAAM,WAAW,eAAe,CAAC,SAAS,SAAS,eAAe,EAAE,OAAO,SAAS,kBAAkB,CAAC,SAAS,EAAE,YAAY,CAAC,OAAO,CAAC,CAAC;IACrI,SAAS,IAAI,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC,CAAC;IACxC,cAAc,CAAC,OAAO,EAAE,WAAW,CAAC,SAAS,EAAE,OAAO,CAAC,GAAG,IAAI,CAAC;CACjE;AAED;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,qBAAa,WAAW,CACrB,SAAS,SAAS,eAAe,EACjC,OAAO,SAAS,kBAAkB,CAAC,SAAS,EAAE,YAAY,CAAC,OAAO,CAAC,CAAC,GAAG,kBAAkB,CAAC,SAAS,CAAC;
|
|
1
|
+
{"version":3,"file":"data-session.d.ts","sourceRoot":"","sources":["../../src/client/data-session.ts"],"names":[],"mappings":"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"}
|
|
@@ -37,6 +37,12 @@ class DataSession {
|
|
|
37
37
|
/** URIs this session holds open, so {@link dispose} can release exactly those. */
|
|
38
38
|
openUris = new Set();
|
|
39
39
|
disposed = false;
|
|
40
|
+
/**
|
|
41
|
+
* Disposed through {@link detach} rather than {@link dispose}, so nothing may
|
|
42
|
+
* be sent. Folding it into {@link disposed} costs {@link openDocument} the
|
|
43
|
+
* distinction, and its close would then go out on a detached session.
|
|
44
|
+
*/
|
|
45
|
+
detached = false;
|
|
40
46
|
constructor(clientId, host) {
|
|
41
47
|
this.clientId = clientId;
|
|
42
48
|
this.host = host;
|
|
@@ -70,11 +76,33 @@ class DataSession {
|
|
|
70
76
|
* valid: `open` settles at the integrity landmark, not at validation.
|
|
71
77
|
* Validity arrives asynchronously on `onDocumentUpdated`, or synchronously
|
|
72
78
|
* from `getModelDocument({ includeDiagnostics: true })`.
|
|
79
|
+
*
|
|
80
|
+
* A dispose landing between the two awaits is handled here rather than in
|
|
81
|
+
* {@link dispose}, which cannot release a hold that does not exist yet: it
|
|
82
|
+
* reads {@link openUris}, and this method fills it only once both calls have
|
|
83
|
+
* returned. Left to `dispose`, the hold and its watch outlive the participant
|
|
84
|
+
* and go only when the connection closes.
|
|
73
85
|
*/
|
|
74
86
|
async openDocument(args) {
|
|
75
87
|
const server = await this.connected();
|
|
76
88
|
const document = await server.openModelDocument({ ...args, clientId: this.clientId });
|
|
77
89
|
await server.watchModelDocument({ uri: args.uri, clientId: this.clientId });
|
|
90
|
+
if (this.disposed) {
|
|
91
|
+
// Registering the URI BEFORE the open, so `dispose` could close on
|
|
92
|
+
// intent, is NOT the same fix and can invert into the leak it is meant
|
|
93
|
+
// to prevent: the close would then be issued while the open is still in
|
|
94
|
+
// flight, and a peer that does not serialise the two can complete the
|
|
95
|
+
// close first, after which the open re-registers the hold. Closing
|
|
96
|
+
// strictly after the open has resolved is the only ordering with no
|
|
97
|
+
// losing interleaving.
|
|
98
|
+
if (!this.detached) {
|
|
99
|
+
void server.closeModelDocument({ uri: args.uri, clientId: this.clientId }).catch(() => undefined);
|
|
100
|
+
}
|
|
101
|
+
// Returned rather than thrown: the caller that reaches this window is
|
|
102
|
+
// one that never awaited the open, so a rejection here surfaces as an
|
|
103
|
+
// unhandled one against a participant already gone.
|
|
104
|
+
return document;
|
|
105
|
+
}
|
|
78
106
|
this.openUris.add(args.uri);
|
|
79
107
|
return document;
|
|
80
108
|
}
|
|
@@ -84,8 +112,13 @@ class DataSession {
|
|
|
84
112
|
*/
|
|
85
113
|
async closeDocument(args) {
|
|
86
114
|
const server = await this.connected();
|
|
87
|
-
this.openUris.delete(args.uri);
|
|
88
115
|
await server.closeModelDocument({ ...args, clientId: this.clientId });
|
|
116
|
+
// Untracked only once the close has actually landed. Dropping it first
|
|
117
|
+
// gives a FAILED close the same effect as a successful one: the hold
|
|
118
|
+
// survives on the server and `dispose` no longer knows to retry it.
|
|
119
|
+
// A `dispose` racing this therefore closes the same URI twice, which the
|
|
120
|
+
// server answers as a no-op for a client that no longer holds it.
|
|
121
|
+
this.openUris.delete(args.uri);
|
|
89
122
|
}
|
|
90
123
|
/** Write `args.model` back as this session. */
|
|
91
124
|
async updateDocument(args) {
|
|
@@ -140,6 +173,7 @@ class DataSession {
|
|
|
140
173
|
*/
|
|
141
174
|
detach() {
|
|
142
175
|
this.disposed = true;
|
|
176
|
+
this.detached = true;
|
|
143
177
|
this.openUris.clear();
|
|
144
178
|
}
|
|
145
179
|
assertLive() {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"data-session.js","sourceRoot":"","sources":["../../src/client/data-session.ts"],"names":[],"mappings":";AAAA;;;;;;;kFAOkF;;;AA2DlF;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,MAAa,WAAW;
|
|
1
|
+
{"version":3,"file":"data-session.js","sourceRoot":"","sources":["../../src/client/data-session.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -101,12 +101,26 @@ export class DataConnection<
|
|
|
101
101
|
* the framework's own broadcasts as its own echoes and drop them. Nothing
|
|
102
102
|
* about that fails on its own: the document simply stops following, which
|
|
103
103
|
* looks like a dead connection.
|
|
104
|
+
*
|
|
105
|
+
* Throws, too, for an id a LIVE session on this connection already holds.
|
|
106
|
+
* Per-document membership is a set of client ids, so two participants
|
|
107
|
+
* sharing one collapse to a single hold and the first close releases it
|
|
108
|
+
* under the survivor, which then stops receiving updates for a document it
|
|
109
|
+
* is still showing. A constant bound once per participant KIND — one per
|
|
110
|
+
* widget class rather than per instance — satisfies the type and violates
|
|
111
|
+
* this.
|
|
112
|
+
*
|
|
113
|
+
* The check spans this connection only, so a head several connections reach
|
|
114
|
+
* can still be addressed twice under one id.
|
|
104
115
|
*/
|
|
105
116
|
createSession(clientId: string): DataSession<TTransfer, TServer> {
|
|
106
117
|
this.assertLive();
|
|
107
118
|
if (FRAMEWORK_CLIENT_IDS.includes(clientId)) {
|
|
108
119
|
throw new Error(`clientId '${clientId}' is reserved by the framework and cannot identify a participant`);
|
|
109
120
|
}
|
|
121
|
+
if ([...this.sessions].some(session => session.clientId === clientId)) {
|
|
122
|
+
throw new Error(`clientId '${clientId}' already identifies a live participant on this connection`);
|
|
123
|
+
}
|
|
110
124
|
const session = new DataSession<TTransfer, TServer>(clientId, {
|
|
111
125
|
connected: () => this.connected(),
|
|
112
126
|
releaseSession: released => this.sessions.delete(released)
|
|
@@ -93,6 +93,12 @@ export class DataSession<
|
|
|
93
93
|
/** URIs this session holds open, so {@link dispose} can release exactly those. */
|
|
94
94
|
protected readonly openUris = new Set<string>();
|
|
95
95
|
protected disposed = false;
|
|
96
|
+
/**
|
|
97
|
+
* Disposed through {@link detach} rather than {@link dispose}, so nothing may
|
|
98
|
+
* be sent. Folding it into {@link disposed} costs {@link openDocument} the
|
|
99
|
+
* distinction, and its close would then go out on a detached session.
|
|
100
|
+
*/
|
|
101
|
+
protected detached = false;
|
|
96
102
|
|
|
97
103
|
constructor(
|
|
98
104
|
readonly clientId: string,
|
|
@@ -129,11 +135,33 @@ export class DataSession<
|
|
|
129
135
|
* valid: `open` settles at the integrity landmark, not at validation.
|
|
130
136
|
* Validity arrives asynchronously on `onDocumentUpdated`, or synchronously
|
|
131
137
|
* from `getModelDocument({ includeDiagnostics: true })`.
|
|
138
|
+
*
|
|
139
|
+
* A dispose landing between the two awaits is handled here rather than in
|
|
140
|
+
* {@link dispose}, which cannot release a hold that does not exist yet: it
|
|
141
|
+
* reads {@link openUris}, and this method fills it only once both calls have
|
|
142
|
+
* returned. Left to `dispose`, the hold and its watch outlive the participant
|
|
143
|
+
* and go only when the connection closes.
|
|
132
144
|
*/
|
|
133
145
|
async openDocument(args: DataSessionOpenArgs<TTransfer, TServer>): Promise<DataSessionDocument<TTransfer, TServer>> {
|
|
134
146
|
const server = await this.connected();
|
|
135
147
|
const document = await server.openModelDocument({ ...args, clientId: this.clientId });
|
|
136
148
|
await server.watchModelDocument({ uri: args.uri, clientId: this.clientId });
|
|
149
|
+
if (this.disposed) {
|
|
150
|
+
// Registering the URI BEFORE the open, so `dispose` could close on
|
|
151
|
+
// intent, is NOT the same fix and can invert into the leak it is meant
|
|
152
|
+
// to prevent: the close would then be issued while the open is still in
|
|
153
|
+
// flight, and a peer that does not serialise the two can complete the
|
|
154
|
+
// close first, after which the open re-registers the hold. Closing
|
|
155
|
+
// strictly after the open has resolved is the only ordering with no
|
|
156
|
+
// losing interleaving.
|
|
157
|
+
if (!this.detached) {
|
|
158
|
+
void server.closeModelDocument({ uri: args.uri, clientId: this.clientId }).catch(() => undefined);
|
|
159
|
+
}
|
|
160
|
+
// Returned rather than thrown: the caller that reaches this window is
|
|
161
|
+
// one that never awaited the open, so a rejection here surfaces as an
|
|
162
|
+
// unhandled one against a participant already gone.
|
|
163
|
+
return document;
|
|
164
|
+
}
|
|
137
165
|
this.openUris.add(args.uri);
|
|
138
166
|
return document;
|
|
139
167
|
}
|
|
@@ -144,8 +172,13 @@ export class DataSession<
|
|
|
144
172
|
*/
|
|
145
173
|
async closeDocument(args: DataSessionCloseArgs<TTransfer, TServer>): Promise<void> {
|
|
146
174
|
const server = await this.connected();
|
|
147
|
-
this.openUris.delete(args.uri);
|
|
148
175
|
await server.closeModelDocument({ ...args, clientId: this.clientId });
|
|
176
|
+
// Untracked only once the close has actually landed. Dropping it first
|
|
177
|
+
// gives a FAILED close the same effect as a successful one: the hold
|
|
178
|
+
// survives on the server and `dispose` no longer knows to retry it.
|
|
179
|
+
// A `dispose` racing this therefore closes the same URI twice, which the
|
|
180
|
+
// server answers as a no-op for a client that no longer holds it.
|
|
181
|
+
this.openUris.delete(args.uri);
|
|
149
182
|
}
|
|
150
183
|
|
|
151
184
|
/** Write `args.model` back as this session. */
|
|
@@ -205,6 +238,7 @@ export class DataSession<
|
|
|
205
238
|
*/
|
|
206
239
|
detach(): void {
|
|
207
240
|
this.disposed = true;
|
|
241
|
+
this.detached = true;
|
|
208
242
|
this.openUris.clear();
|
|
209
243
|
}
|
|
210
244
|
|