@volter/teams 0.1.1 → 0.1.2

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/admission.mjs CHANGED
@@ -4,7 +4,8 @@
4
4
  // { id, caps, targets } rows from the caller's machine grants (design §11).
5
5
  //
6
6
  // Targets: `<kind>:<name>` for the kinds a product serves on this machine
7
- // (the harness: `pane:<contextKey>`, `runtime:<id>`, `root:<path>`, `port:<pane>/<n>`),
7
+ // (the harness: `pane:<contextKey>`, `runtime:<id>`, `root:<path>`, `port:<pane>/<n>`,
8
+ // `mail:<address>` for one session's mailbox),
8
9
  // `<kind>:*` for every target of one kind, or `*` (operate and view only).
9
10
  // `root:<path>` covers the path and everything under it.
10
11
 
package/channel.mjs CHANGED
@@ -182,10 +182,17 @@ export class Channel extends EventEmitter {
182
182
  this._streams = new Map();
183
183
  this._nextStream = initiator ? 1 : 2;
184
184
  this.closed = false;
185
+ // A broken channel ends every open stream with the reason, so each caller's
186
+ // request fails with it; 'error' is emitted only to someone listening for it,
187
+ // never as an uncaught exception that takes the process down.
188
+ const failed = (error) => {
189
+ this.error ??= error;
190
+ if (this.listenerCount('error') > 0) this.emit('error', error);
191
+ };
185
192
  frameReader(
186
193
  socket,
187
194
  (frame) => this._onEnvelope(JSON.parse(frame.toString('utf8'))),
188
- (error) => this.emit('error', error),
195
+ failed,
189
196
  );
190
197
  // The hello left the socket paused with any early frames unshifted; our reader is on now.
191
198
  socket.resume();
@@ -196,12 +203,12 @@ export class Channel extends EventEmitter {
196
203
  this.closed = true;
197
204
  for (const stream of this._streams.values()) {
198
205
  stream.closed = true;
199
- stream._deliver('close', 'connection closed');
206
+ stream._deliver('close', this.error ? `connection closed: ${this.error.message}` : 'connection closed');
200
207
  }
201
208
  this._streams.clear();
202
209
  this.emit('close');
203
210
  });
204
- socket.on('error', (error) => this.emit('error', error));
211
+ socket.on('error', failed);
205
212
  }
206
213
 
207
214
  _onEnvelope(envelope) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@volter/teams",
3
- "version": "0.1.1",
3
+ "version": "0.1.2",
4
4
  "type": "module",
5
5
  "description": "Volter Teams, the neutral team plane: machine identity and daemon core, the local door and server link, admission from machine grants, product doors, and the Teams server's neutral routes (company RFC 0008).",
6
6
  "license": "MIT",
@@ -19,8 +19,8 @@ import { delegatedAccessOf, delegatedPrincipal, looksDelegated } from './delegat
19
19
 
20
20
  const LINK_PROTOCOL = 'supercode-machine-link.v1';
21
21
  const CHANNEL_PROTOCOL = 'supercode-machine-channel.v1';
22
- /** The machine daemon's door vocabulary: operate (operator: harness.v1 and launch) and the participant caps its doors check. */
23
- const CAPS = new Set(['operate', 'view', 'observe', 'interact', 'control', 'files', 'ports']);
22
+ /** The machine daemon's door vocabulary: operate (operator: harness.v1 and launch) and the participant caps its doors check. `mail` writes one message into a session there (`mail:*`, or `mail:<address>` for one session). */
23
+ const CAPS = new Set(['operate', 'view', 'observe', 'interact', 'control', 'files', 'ports', 'mail']);
24
24
  // A target is `*` or `<kind>:<name>`. The server checks shape only: which kinds a machine
25
25
  // serves is the daemon's business, and a grant on a kind it does not serve covers nothing.
26
26
  const TARGET = /^(\*|root:(\*|\/[^\x00-\x1f]{0,4095})|(?!root:)[a-z][a-z0-9-]{0,31}:[^\x00-\x1f]{1,4096})$/;