@streamorchard/grapevine 0.0.0-stage → 0.1.0
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/LICENSE +674 -0
- package/PROTOCOL.md +266 -0
- package/README.md +161 -2
- package/assets/apps/pear-drops.png +0 -0
- package/assets/apps/stream-multiview.png +0 -0
- package/fixtures/drops.farming/1/invalid/bad-time.json +1 -0
- package/fixtures/drops.farming/1/invalid/channel-without-slug.json +4 -0
- package/fixtures/drops.farming/1/invalid/no-state.json +1 -0
- package/fixtures/drops.farming/1/valid/farming.json +11 -0
- package/fixtures/drops.farming/1/valid/future-state-and-field.json +13 -0
- package/fixtures/drops.farming/1/valid/get-result.json +1 -0
- package/fixtures/drops.farming/1/valid/waiting.json +4 -0
- package/fixtures/index.js +36 -0
- package/fixtures/players/1/invalid/no-open-list.json +4 -0
- package/fixtures/players/1/invalid/signed-in-not-boolean.json +4 -0
- package/fixtures/players/1/valid/no-sites.json +1 -0
- package/fixtures/players/1/valid/signed-out.json +18 -0
- package/package.json +66 -4
- package/schemas/common.json +40 -0
- package/schemas/drops.farming/1.json +45 -0
- package/schemas/players/1.json +43 -0
- package/src/connection.js +316 -0
- package/src/errors.js +39 -0
- package/src/framing.js +65 -0
- package/src/handshake.js +134 -0
- package/src/index.js +421 -0
- package/src/keys.js +71 -0
- package/src/known-apps.js +63 -0
- package/src/paths.js +85 -0
- package/src/report.js +49 -0
- package/src/rpc.js +92 -0
- package/src/schema.js +125 -0
- package/stand-in-peer.js +109 -0
- package/types/capabilities.d.ts +77 -0
- package/types/index.d.ts +198 -0
package/types/index.d.ts
ADDED
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
// The types of what an app calls. The message types come from the schemas (capabilities.d.ts).
|
|
2
|
+
import { EventEmitter } from 'events';
|
|
3
|
+
import type { Events, Methods } from './capabilities';
|
|
4
|
+
|
|
5
|
+
export * from './capabilities';
|
|
6
|
+
|
|
7
|
+
/** Which events and methods there are, and what they hold, comes from the schemas. */
|
|
8
|
+
export type EventName = keyof Events;
|
|
9
|
+
export type MethodName = keyof Methods;
|
|
10
|
+
/** An event's own fields: `seq` and `at` are added by publish(). */
|
|
11
|
+
export type EventData<E extends EventName> = Omit<Events[E], 'seq' | 'at'>;
|
|
12
|
+
|
|
13
|
+
export interface AppInfo {
|
|
14
|
+
/** The app's install id, like com.example.app */
|
|
15
|
+
id: string;
|
|
16
|
+
/** Its socket name: a-z, 0-9 and dashes */
|
|
17
|
+
slug: string;
|
|
18
|
+
name: string;
|
|
19
|
+
version: string;
|
|
20
|
+
summary?: string;
|
|
21
|
+
/** A PNG data URL, or the path of a PNG file */
|
|
22
|
+
icon?: string;
|
|
23
|
+
homepage?: string;
|
|
24
|
+
download?: string;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export interface KeyStore {
|
|
28
|
+
/** Whether keys are encrypted at rest */
|
|
29
|
+
encrypted?: boolean;
|
|
30
|
+
get(appId: string): { key: string; pairedAt?: string } | null;
|
|
31
|
+
set(appId: string, key: string): void;
|
|
32
|
+
delete(appId: string): void;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export interface Place {
|
|
36
|
+
platform?: NodeJS.Platform;
|
|
37
|
+
env?: NodeJS.ProcessEnv;
|
|
38
|
+
home?: string;
|
|
39
|
+
uid?: number;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export interface Timing {
|
|
43
|
+
handshakeMs: number;
|
|
44
|
+
pingMs: number;
|
|
45
|
+
pongMs: number;
|
|
46
|
+
rateMax: number;
|
|
47
|
+
strikes: number;
|
|
48
|
+
pollMs: number;
|
|
49
|
+
backoffMs: number[];
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export interface KnownApp {
|
|
53
|
+
id: string;
|
|
54
|
+
slug: string;
|
|
55
|
+
name: string;
|
|
56
|
+
summary: string;
|
|
57
|
+
/** A bundled 256×256 PNG */
|
|
58
|
+
icon: string;
|
|
59
|
+
homepage: string;
|
|
60
|
+
download: string;
|
|
61
|
+
/** Its folder in the OS's app data folder */
|
|
62
|
+
dataFolder: string;
|
|
63
|
+
/** "capability@major.minor" (or "protocol@N") -> the app version that first had it */
|
|
64
|
+
firstRelease: Record<string, string>;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
export interface LinkOptions {
|
|
68
|
+
app: AppInfo;
|
|
69
|
+
/** Capabilities this app provides, e.g. { 'drops.farming': '1.0' } */
|
|
70
|
+
provides?: Record<string, string | string[]>;
|
|
71
|
+
/** Capabilities this app uses, e.g. { players: '1.0' } */
|
|
72
|
+
uses?: Record<string, string | string[]>;
|
|
73
|
+
/** Whether its Connected apps switch for that app is on */
|
|
74
|
+
allowed: (appId: string) => boolean;
|
|
75
|
+
/** Where pairing keys are kept: fileKeyStore() (in memory if left out) */
|
|
76
|
+
keys?: KeyStore;
|
|
77
|
+
/** The OS's app data folder, to tell whether known apps are installed */
|
|
78
|
+
appDataDir?: string;
|
|
79
|
+
place?: Place;
|
|
80
|
+
schemaDirs?: string[];
|
|
81
|
+
knownApps?: KnownApp[];
|
|
82
|
+
timing?: Partial<Timing>;
|
|
83
|
+
/** Throw on a message that doesn't fit its schema (default: when NODE_ENV is "test") */
|
|
84
|
+
strict?: boolean;
|
|
85
|
+
log?: (level: 'info' | 'warn' | 'error', text: string) => void;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
export interface Report {
|
|
89
|
+
state: 'works' | 'limited' | 'incompatible';
|
|
90
|
+
missing: { capability: string; update: { app: string; appId: string; version: string | null } }[];
|
|
91
|
+
update?: { app: string; appId: string; version: string | null };
|
|
92
|
+
/** One line for the card, e.g. "Works, with a feature missing: update Pear Drops to 0.1.7 or later" */
|
|
93
|
+
text: string;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
export interface Peer {
|
|
97
|
+
id: string;
|
|
98
|
+
name: string;
|
|
99
|
+
summary: string;
|
|
100
|
+
/** Path of its bundled logo */
|
|
101
|
+
icon: string;
|
|
102
|
+
homepage: string;
|
|
103
|
+
download: string;
|
|
104
|
+
/** Whether it looks installed; null without appDataDir */
|
|
105
|
+
installed: boolean | null;
|
|
106
|
+
/** Whether this app's switch for it is on */
|
|
107
|
+
enabled: boolean;
|
|
108
|
+
status: 'off' | 'waiting' | 'connected' | 'pair-again' | 'incompatible';
|
|
109
|
+
/** e.g. 'switched-off' (its own switch is off), 'disconnected-there' */
|
|
110
|
+
reason: string | null;
|
|
111
|
+
/** For 'pair-again': which app holds the old key, so where to press Pair again */
|
|
112
|
+
keyHeldBy: 'us' | 'them' | null;
|
|
113
|
+
/** Its version, while connected */
|
|
114
|
+
version: string | null;
|
|
115
|
+
report: Report | null;
|
|
116
|
+
pairedAt: string | null;
|
|
117
|
+
keysEncrypted: boolean;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
export interface CallContext {
|
|
121
|
+
appId: string;
|
|
122
|
+
app: AppInfo;
|
|
123
|
+
major: number;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
export declare class Link extends EventEmitter {
|
|
127
|
+
constructor(opts: LinkOptions);
|
|
128
|
+
/** Listens for other apps and starts looking for them. */
|
|
129
|
+
start(): Promise<void>;
|
|
130
|
+
/** Says goodbye to every app and stops listening. */
|
|
131
|
+
stop(): Promise<void>;
|
|
132
|
+
/** Call after a Connected apps switch changes. */
|
|
133
|
+
refresh(): void;
|
|
134
|
+
/** Tells every subscribed app something changed; returns the params as sent. */
|
|
135
|
+
publish<E extends EventName>(event: E, data: EventData<E>): Events[E];
|
|
136
|
+
publish(event: string, data: Record<string, unknown>): Record<string, unknown>;
|
|
137
|
+
/** Answers a method of a capability this app provides. */
|
|
138
|
+
handle<M extends MethodName>(method: M, fn: (params: Methods[M]['params'], ctx: CallContext) => Methods[M]['result'] | Promise<Methods[M]['result']>): void;
|
|
139
|
+
handle(method: string, fn: (params: any, ctx: CallContext) => unknown): void;
|
|
140
|
+
/** Asks a connected app something. */
|
|
141
|
+
request<M extends MethodName>(appId: string, method: M, params?: Methods[M]['params'], opts?: { timeout?: number }): Promise<Methods[M]['result']>;
|
|
142
|
+
request(appId: string, method: string, params?: Record<string, unknown>, opts?: { timeout?: number }): Promise<unknown>;
|
|
143
|
+
/** Disconnect on a card: both apps forget the pairing. Switch this app's own setting off too. */
|
|
144
|
+
disconnect(appId: string): Promise<void>;
|
|
145
|
+
/** Pair again on a card: forgets this app's old key. */
|
|
146
|
+
pairAgain(appId: string): void;
|
|
147
|
+
isConnected(appId: string): boolean;
|
|
148
|
+
/** Every known app, for the Connected apps page. */
|
|
149
|
+
peers(): Peer[];
|
|
150
|
+
|
|
151
|
+
on(event: 'event', listener: <E extends EventName>(from: string, name: E, params: Events[E]) => void): this;
|
|
152
|
+
on(event: 'connected', listener: (appId: string) => void): this;
|
|
153
|
+
on(event: 'disconnected', listener: (appId: string, reason: string) => void): this;
|
|
154
|
+
on(event: 'unpaired', listener: (appId: string) => void): this;
|
|
155
|
+
on(event: 'change', listener: () => void): this;
|
|
156
|
+
on(event: 'log', listener: (level: 'info' | 'warn' | 'error', text: string) => void): this;
|
|
157
|
+
on(event: string, listener: (...args: any[]) => void): this;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
export declare function createLink(opts: LinkOptions): Link;
|
|
161
|
+
|
|
162
|
+
export declare class GrapevineError extends Error {
|
|
163
|
+
constructor(code: number, message: string, data?: unknown);
|
|
164
|
+
code: number;
|
|
165
|
+
data: unknown;
|
|
166
|
+
toJSON(): { code: number; message: string; data?: unknown };
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
export declare const CODES: Readonly<{
|
|
170
|
+
PARSE_ERROR: -32700;
|
|
171
|
+
INVALID_REQUEST: -32600;
|
|
172
|
+
METHOD_NOT_FOUND: -32601;
|
|
173
|
+
INVALID_PARAMS: -32602;
|
|
174
|
+
INTERNAL_ERROR: -32603;
|
|
175
|
+
NOT_PAIRED: -32001;
|
|
176
|
+
NOT_AGREED: -32002;
|
|
177
|
+
UNSUPPORTED: -32003;
|
|
178
|
+
TOO_MANY: -32004;
|
|
179
|
+
SWITCHED_OFF: -32005;
|
|
180
|
+
CLOSED: -32099;
|
|
181
|
+
TIMEOUT: -32098;
|
|
182
|
+
}>;
|
|
183
|
+
|
|
184
|
+
export declare const PROTOCOL: { min: number; max: number };
|
|
185
|
+
export declare const KNOWN_APPS: KnownApp[];
|
|
186
|
+
|
|
187
|
+
/** Keys in a small file in the app's data folder, encrypted with encrypt/decrypt (e.g. Electron's safeStorage) when given. */
|
|
188
|
+
export declare function fileKeyStore(file: string, crypt?: { encrypt?: (text: string) => string; decrypt?: (stored: string) => string }): KeyStore;
|
|
189
|
+
export declare function memoryKeyStore(): KeyStore;
|
|
190
|
+
export declare function isInstalled(app: KnownApp, appDataDir: string): boolean;
|
|
191
|
+
export declare function iconDataUrl(file: string): string;
|
|
192
|
+
|
|
193
|
+
export declare const paths: {
|
|
194
|
+
linkDir(place?: Place): string;
|
|
195
|
+
socketDir(place?: Place): string;
|
|
196
|
+
socketPath(slug: string, place?: Place): string;
|
|
197
|
+
listSlugs(place?: Place): string[];
|
|
198
|
+
};
|