@streamorchard/grapevine 0.4.0 → 0.5.1
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/PROTOCOL.md +5 -1
- package/README.md +24 -12
- package/package.json +3 -2
- package/schemas/released.json +5 -0
- package/src/index.js +23 -1
- package/src/known-apps.js +10 -2
- package/types/index.d.ts +5 -0
package/PROTOCOL.md
CHANGED
|
@@ -197,6 +197,8 @@ A capability is a named feature one app provides and others use, defined by a JS
|
|
|
197
197
|
|
|
198
198
|
What a drops app is farming, with enough for another app to show it, in a top-bar widget say. Provided by Pear Drops.
|
|
199
199
|
|
|
200
|
+
Event `drops.farming.changed`, and `drops.farming.get` answers with the same: the whole state, below. `drops.farming.show` is further down.
|
|
201
|
+
|
|
200
202
|
| Field | |
|
|
201
203
|
| -------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
202
204
|
| `state` | `farming` (its player is watching a stream for drop credit), `waiting`, `paused`, `done`, `signed-out`, or one added later |
|
|
@@ -216,6 +218,8 @@ What a drops app is farming, with enough for another app to show it, in a top-ba
|
|
|
216
218
|
|
|
217
219
|
An app's stream players, by site. Provided by Stream Multiview.
|
|
218
220
|
|
|
221
|
+
Event `players.changed`, and `players.get` answers with the same: the whole state, below.
|
|
222
|
+
|
|
219
223
|
| Field | |
|
|
220
224
|
| ----------- | --------------------------------------------------------------------------------------------------------------- |
|
|
221
225
|
| `platforms` | An object keyed by `Platform`. Each site has `{ signedIn, mode, account, open }` |
|
|
@@ -259,7 +263,7 @@ After the hello, each app tells its user one of:
|
|
|
259
263
|
| **Works, with a feature missing** | A capability one app wants has no major in common. It names the app with the older versions and, if known, the release to update to: "update Pear Drops to 0.1.7 or later" |
|
|
260
264
|
| **Can't connect** | No protocol version in common. Again, it names the app to update |
|
|
261
265
|
|
|
262
|
-
The releases come from
|
|
266
|
+
The releases come from `schemas/released.json`, recorded as each app ships (`npm run released`), through the known-apps list's `firstRelease`: `"<capability>@<major>.<minor>"` and `"protocol@<n>"` mapped to the first release of that app that had them.
|
|
263
267
|
|
|
264
268
|
## Renaming an app
|
|
265
269
|
|
package/README.md
CHANGED
|
@@ -111,17 +111,18 @@ Editors suggest every event's fields and underline mistakes, in plain JavaScript
|
|
|
111
111
|
|
|
112
112
|
`link.peers()` lists every known app with what its card needs, and `link.on('change', …)` says when to draw it again.
|
|
113
113
|
|
|
114
|
-
| Field |
|
|
115
|
-
| ------------------------------------- |
|
|
116
|
-
| `name`, `summary`, `icon`, `download` | The app's logo (bundled, so it shows even when it isn't installed), one line about it, and where to get it
|
|
117
|
-
| `installed` | Whether its data folder exists (with `appDataDir`)
|
|
118
|
-
| `enabled` | Your switch for it
|
|
119
|
-
| `status` | `off`, `waiting`, `connected`, `pair-again` or `incompatible`
|
|
120
|
-
| `reason` | Why it's waiting: `switched-off` (its own switch is off), `disconnected-there`
|
|
121
|
-
| `keyHeldBy` | For `pair-again`: `us` means show **Pair again** here, `them` means it's pressed in the other app
|
|
122
|
-
| `version`, `report.text` | While connected: its version, and "Works" or what's missing ("update Pear Drops to 0.1.7 or later")
|
|
123
|
-
| `pairedAt`, `keysEncrypted` | When they paired, and whether keys are stored encrypted
|
|
124
|
-
| `formerIds` | Ids the app had before a rename
|
|
114
|
+
| Field | |
|
|
115
|
+
| ------------------------------------- | ------------------------------------------------------------------------------------------------------------ |
|
|
116
|
+
| `name`, `summary`, `icon`, `download` | The app's logo (bundled, so it shows even when it isn't installed), one line about it, and where to get it |
|
|
117
|
+
| `installed` | Whether its data folder exists (with `appDataDir`) |
|
|
118
|
+
| `enabled` | Your switch for it |
|
|
119
|
+
| `status` | `off`, `waiting`, `connected`, `pair-again` or `incompatible` |
|
|
120
|
+
| `reason` | Why it's waiting: `switched-off` (its own switch is off), `disconnected-there` |
|
|
121
|
+
| `keyHeldBy` | For `pair-again`: `us` means show **Pair again** here, `them` means it's pressed in the other app |
|
|
122
|
+
| `version`, `report.text` | While connected: its version, and "Works" or what's missing ("update Pear Drops to 0.1.7 or later") |
|
|
123
|
+
| `pairedAt`, `keysEncrypted` | When they paired, and whether keys are stored encrypted |
|
|
124
|
+
| `formerIds` | Ids the app had before a rename |
|
|
125
|
+
| `shares` | What the two apps share: `{ out, in }`, capabilities this app provides that it uses, and the other way round |
|
|
125
126
|
|
|
126
127
|
Two calls go with buttons on the card:
|
|
127
128
|
|
|
@@ -166,7 +167,17 @@ To change one, follow [the version rules](PROTOCOL.md#versions): additions are a
|
|
|
166
167
|
|
|
167
168
|
## Adding an app
|
|
168
169
|
|
|
169
|
-
Add it to `src/known-apps.js
|
|
170
|
+
Add it to `src/known-apps.js` with:
|
|
171
|
+
|
|
172
|
+
- its id and slug
|
|
173
|
+
- a one-line summary
|
|
174
|
+
- a 256×256 logo in `assets/apps/`
|
|
175
|
+
- its download page and its data folder's name
|
|
176
|
+
- the capabilities it `provides` and `uses`
|
|
177
|
+
|
|
178
|
+
Every app that updates Grapevine then shows it on its Connected apps page, with what the two would share. `link.peers()` gives that as `shares`, from this list until they meet and from the hello after.
|
|
179
|
+
|
|
180
|
+
When it ships a release that uses Grapevine, record it: `npm run released -- <its id> <its version>`. That fixes the capability versions it uses (they can't change in place after that) and tells other apps which release to ask for.
|
|
170
181
|
|
|
171
182
|
## Renaming an app
|
|
172
183
|
|
|
@@ -183,6 +194,7 @@ npm run lint # ESLint and Prettier
|
|
|
183
194
|
npm run format # fix what lint can
|
|
184
195
|
npm run types # types/capabilities.d.ts from the schemas
|
|
185
196
|
npm run check # the types are up to date, and tsc finds no type errors
|
|
197
|
+
npm run released -- <app id> <version> # an app shipped a release using Grapevine (see Adding an app)
|
|
186
198
|
```
|
|
187
199
|
|
|
188
200
|
CI runs the tests on Windows, macOS and Linux, and lint, types and coverage on Linux.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@streamorchard/grapevine",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.5.1",
|
|
4
4
|
"description": "Lets desktop apps on the same computer find each other and talk, privately: local sockets, JSON-RPC, pairing and versioned capabilities.",
|
|
5
5
|
"license": "GPL-3.0-only",
|
|
6
6
|
"author": "StreamOrchard",
|
|
@@ -52,7 +52,8 @@
|
|
|
52
52
|
"lint": "eslint . && prettier --check .",
|
|
53
53
|
"format": "prettier --write . && eslint --fix .",
|
|
54
54
|
"types": "node scripts/types.js",
|
|
55
|
-
"check": "node scripts/types.js --check && tsc -p jsconfig.json"
|
|
55
|
+
"check": "node scripts/types.js --check && tsc -p jsconfig.json",
|
|
56
|
+
"released": "node scripts/released.js"
|
|
56
57
|
},
|
|
57
58
|
"devDependencies": {
|
|
58
59
|
"@eslint/js": "^10.0.1",
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
{
|
|
2
|
+
"description": "Capability versions that have shipped in an app's release, and which release of each app first had them. Written by `npm run released -- <app id> <version>`; never by hand. A version recorded here can't change in place: test/released.test.js fails until the change goes in a new minor.",
|
|
3
|
+
"schemas": {},
|
|
4
|
+
"firstRelease": {}
|
|
5
|
+
}
|
package/src/index.js
CHANGED
|
@@ -99,7 +99,14 @@ class Link extends EventEmitter {
|
|
|
99
99
|
const server = net.createServer((socket) => this.accept(socket));
|
|
100
100
|
const go = () =>
|
|
101
101
|
server.listen(this.socketPath, () => {
|
|
102
|
-
|
|
102
|
+
// The socket can vanish before this runs (its folder removed): start() fails, rather than
|
|
103
|
+
// an exception the app can't catch.
|
|
104
|
+
try {
|
|
105
|
+
if (platform !== 'win32') fs.chmodSync(this.socketPath, 0o600);
|
|
106
|
+
} catch (err) {
|
|
107
|
+
server.close();
|
|
108
|
+
return reject(err);
|
|
109
|
+
}
|
|
103
110
|
this.server = server;
|
|
104
111
|
resolve();
|
|
105
112
|
});
|
|
@@ -415,6 +422,20 @@ class Link extends EventEmitter {
|
|
|
415
422
|
return this.conns.has(this.currentId(appId));
|
|
416
423
|
}
|
|
417
424
|
|
|
425
|
+
/**
|
|
426
|
+
* What this app and another share: `out`, capabilities this app provides that it uses; `in`,
|
|
427
|
+
* ones it provides that this app uses. Agreed in the hello while connected; from the known-apps
|
|
428
|
+
* list otherwise, so a card can say what linking would do before the two have met.
|
|
429
|
+
*/
|
|
430
|
+
sharesWith(app, conn) {
|
|
431
|
+
if (conn) return { out: [...conn.agreed.outgoing.keys()].sort(), in: [...conn.agreed.incoming.keys()].sort() };
|
|
432
|
+
const mine = (list) => (cap) => cap in list;
|
|
433
|
+
return {
|
|
434
|
+
out: (app.uses ?? []).filter(mine(this.mine.provides)).sort(),
|
|
435
|
+
in: (app.provides ?? []).filter(mine(this.mine.uses)).sort(),
|
|
436
|
+
};
|
|
437
|
+
}
|
|
438
|
+
|
|
418
439
|
/**
|
|
419
440
|
* Every known app, for the Connected apps page:
|
|
420
441
|
* status 'off' (its switch is off) | 'waiting' | 'connected' | 'pair-again' | 'incompatible'.
|
|
@@ -445,6 +466,7 @@ class Link extends EventEmitter {
|
|
|
445
466
|
keyHeldBy: status === 'pair-again' ? (why?.keyHeldBy ?? null) : null,
|
|
446
467
|
version: conn?.peer.version ?? null,
|
|
447
468
|
report,
|
|
469
|
+
shares: this.sharesWith(app, conn),
|
|
448
470
|
pairedAt: paired?.pairedAt ?? null,
|
|
449
471
|
keysEncrypted: !!this.keys.encrypted,
|
|
450
472
|
};
|
package/src/known-apps.js
CHANGED
|
@@ -7,6 +7,8 @@ const fs = require('fs');
|
|
|
7
7
|
const path = require('path');
|
|
8
8
|
|
|
9
9
|
const ICONS = path.join(__dirname, '..', 'assets', 'apps');
|
|
10
|
+
// Which release of each app first had each capability version: written by `npm run released`.
|
|
11
|
+
const RELEASED = JSON.parse(fs.readFileSync(path.join(__dirname, '..', 'schemas', 'released.json'), 'utf8')).firstRelease;
|
|
10
12
|
|
|
11
13
|
/**
|
|
12
14
|
* @typedef {object} KnownApp
|
|
@@ -19,6 +21,8 @@ const ICONS = path.join(__dirname, '..', 'assets', 'apps');
|
|
|
19
21
|
* @property {string} homepage
|
|
20
22
|
* @property {string} download its latest release
|
|
21
23
|
* @property {string} dataFolder its folder in the OS's app data folder: no folder means not installed
|
|
24
|
+
* @property {string[]} provides the capabilities it provides (their names), so another app can say what the two share before they've met
|
|
25
|
+
* @property {string[]} uses the capabilities it uses
|
|
22
26
|
* @property {Record<string, string>} firstRelease "capability@major.minor" (or "protocol@N") -> the app version that first had it
|
|
23
27
|
*/
|
|
24
28
|
|
|
@@ -34,7 +38,9 @@ const KNOWN_APPS = [
|
|
|
34
38
|
homepage: 'https://github.com/Stream-Orchard/pear-drops',
|
|
35
39
|
download: 'https://github.com/Stream-Orchard/pear-drops/releases/latest',
|
|
36
40
|
dataFolder: 'Pear Drops',
|
|
37
|
-
|
|
41
|
+
provides: ['drops.farming'],
|
|
42
|
+
uses: ['players'],
|
|
43
|
+
firstRelease: { ...RELEASED['com.streamorchard.peardrops'] },
|
|
38
44
|
},
|
|
39
45
|
{
|
|
40
46
|
id: 'com.glowingpear.streammultiview',
|
|
@@ -46,7 +52,9 @@ const KNOWN_APPS = [
|
|
|
46
52
|
homepage: 'https://github.com/GlowingPear/stream-window-multiview',
|
|
47
53
|
download: 'https://github.com/GlowingPear/stream-window-multiview/releases/latest',
|
|
48
54
|
dataFolder: 'Stream Multiview',
|
|
49
|
-
|
|
55
|
+
provides: ['players'],
|
|
56
|
+
uses: ['drops.farming'],
|
|
57
|
+
firstRelease: { ...RELEASED['com.glowingpear.streammultiview'] },
|
|
50
58
|
},
|
|
51
59
|
];
|
|
52
60
|
|
package/types/index.d.ts
CHANGED
|
@@ -64,6 +64,9 @@ export interface KnownApp {
|
|
|
64
64
|
download: string;
|
|
65
65
|
/** Its folder in the OS's app data folder */
|
|
66
66
|
dataFolder: string;
|
|
67
|
+
/** The capabilities it provides and uses (names), so a card can say what linking would do */
|
|
68
|
+
provides: string[];
|
|
69
|
+
uses: string[];
|
|
67
70
|
/** "capability@major.minor" (or "protocol@N") -> the app version that first had it */
|
|
68
71
|
firstRelease: Record<string, string>;
|
|
69
72
|
}
|
|
@@ -119,6 +122,8 @@ export interface Peer {
|
|
|
119
122
|
/** Its version, while connected */
|
|
120
123
|
version: string | null;
|
|
121
124
|
report: Report | null;
|
|
125
|
+
/** What the two apps share: `out`, capabilities this app provides that it uses; `in`, ones it provides that this app uses */
|
|
126
|
+
shares: { out: string[]; in: string[] };
|
|
122
127
|
pairedAt: string | null;
|
|
123
128
|
keysEncrypted: boolean;
|
|
124
129
|
}
|