@abraca/dabra 0.1.0 → 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/dist/{hocuspocus-provider.cjs → abracadabra-provider.cjs} +7 -58
- package/dist/abracadabra-provider.cjs.map +1 -0
- package/dist/{hocuspocus-provider.esm.js → abracadabra-provider.esm.js} +2 -53
- package/dist/abracadabra-provider.esm.js.map +1 -0
- package/dist/index.d.ts +1 -6
- package/package.json +1 -1
- package/src/AbracadabraClient.ts +381 -0
- package/src/AbracadabraProvider.ts +46 -6
- package/src/CryptoIdentityKeystore.ts +14 -2
- package/src/HocuspocusProvider.ts +518 -575
- package/src/HocuspocusProviderWebsocket.ts +508 -512
- package/src/MessageReceiver.ts +125 -128
- package/src/OutgoingMessages/AuthenticationMessage.ts +12 -14
- package/src/index.ts +1 -0
- package/src/types.ts +99 -55
- package/dist/hocuspocus-provider.cjs.map +0 -1
- package/dist/hocuspocus-provider.esm.js.map +0 -1
package/src/MessageReceiver.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { readAuthMessage } from "@
|
|
1
|
+
import { readAuthMessage } from "@abraca/dabra-common";
|
|
2
2
|
import { readVarInt, readVarString } from "lib0/decoding";
|
|
3
3
|
import type { CloseEvent } from "ws";
|
|
4
4
|
import * as awarenessProtocol from "y-protocols/awareness";
|
|
@@ -9,131 +9,128 @@ import { OutgoingMessage } from "./OutgoingMessage.ts";
|
|
|
9
9
|
import { MessageType } from "./types.ts";
|
|
10
10
|
|
|
11
11
|
export class MessageReceiver {
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
),
|
|
137
|
-
);
|
|
138
|
-
}
|
|
12
|
+
message: IncomingMessage;
|
|
13
|
+
|
|
14
|
+
constructor(message: IncomingMessage) {
|
|
15
|
+
this.message = message;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
public apply(provider: HocuspocusProvider, emitSynced: boolean) {
|
|
19
|
+
const { message } = this;
|
|
20
|
+
const type = message.readVarUint();
|
|
21
|
+
|
|
22
|
+
const emptyMessageLength = message.length();
|
|
23
|
+
|
|
24
|
+
switch (type) {
|
|
25
|
+
case MessageType.Sync:
|
|
26
|
+
this.applySyncMessage(provider, emitSynced);
|
|
27
|
+
break;
|
|
28
|
+
|
|
29
|
+
case MessageType.Awareness:
|
|
30
|
+
this.applyAwarenessMessage(provider);
|
|
31
|
+
break;
|
|
32
|
+
|
|
33
|
+
case MessageType.Auth:
|
|
34
|
+
this.applyAuthMessage(provider);
|
|
35
|
+
break;
|
|
36
|
+
|
|
37
|
+
case MessageType.QueryAwareness:
|
|
38
|
+
this.applyQueryAwarenessMessage(provider);
|
|
39
|
+
break;
|
|
40
|
+
|
|
41
|
+
case MessageType.Stateless:
|
|
42
|
+
provider.receiveStateless(readVarString(message.decoder));
|
|
43
|
+
break;
|
|
44
|
+
|
|
45
|
+
case MessageType.SyncStatus:
|
|
46
|
+
this.applySyncStatusMessage(provider, readVarInt(message.decoder) === 1);
|
|
47
|
+
break;
|
|
48
|
+
|
|
49
|
+
case MessageType.CLOSE:
|
|
50
|
+
// eslint-disable-next-line no-case-declarations
|
|
51
|
+
const event: CloseEvent = {
|
|
52
|
+
code: 1000,
|
|
53
|
+
reason: readVarString(message.decoder),
|
|
54
|
+
// @ts-ignore
|
|
55
|
+
target: provider.configuration.websocketProvider.webSocket!,
|
|
56
|
+
type: "close",
|
|
57
|
+
};
|
|
58
|
+
provider.onClose();
|
|
59
|
+
provider.configuration.onClose({ event });
|
|
60
|
+
provider.forwardClose({ event });
|
|
61
|
+
break;
|
|
62
|
+
|
|
63
|
+
default:
|
|
64
|
+
throw new Error(`Can’t apply message of unknown type: ${type}`);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// Reply
|
|
68
|
+
if (message.length() > emptyMessageLength + 1) {
|
|
69
|
+
// length of documentName (considered in emptyMessageLength plus length of yjs sync type, set in applySyncMessage)
|
|
70
|
+
// @ts-ignore
|
|
71
|
+
provider.send(OutgoingMessage, { encoder: message.encoder });
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
private applySyncMessage(provider: HocuspocusProvider, emitSynced: boolean) {
|
|
76
|
+
const { message } = this;
|
|
77
|
+
|
|
78
|
+
message.writeVarUint(MessageType.Sync);
|
|
79
|
+
|
|
80
|
+
// Apply update
|
|
81
|
+
const syncMessageType = readSyncMessage(
|
|
82
|
+
message.decoder,
|
|
83
|
+
message.encoder,
|
|
84
|
+
provider.document,
|
|
85
|
+
provider,
|
|
86
|
+
);
|
|
87
|
+
|
|
88
|
+
// Synced once we receive Step2
|
|
89
|
+
if (emitSynced && syncMessageType === messageYjsSyncStep2) {
|
|
90
|
+
provider.synced = true;
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
applySyncStatusMessage(provider: HocuspocusProvider, applied: boolean) {
|
|
95
|
+
if (applied) {
|
|
96
|
+
provider.decrementUnsyncedChanges();
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
private applyAwarenessMessage(provider: HocuspocusProvider) {
|
|
101
|
+
if (!provider.awareness) return;
|
|
102
|
+
|
|
103
|
+
const { message } = this;
|
|
104
|
+
|
|
105
|
+
awarenessProtocol.applyAwarenessUpdate(
|
|
106
|
+
provider.awareness,
|
|
107
|
+
message.readVarUint8Array(),
|
|
108
|
+
provider,
|
|
109
|
+
);
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
private applyAuthMessage(provider: HocuspocusProvider) {
|
|
113
|
+
const { message } = this;
|
|
114
|
+
|
|
115
|
+
readAuthMessage(
|
|
116
|
+
message.decoder,
|
|
117
|
+
provider.sendToken.bind(provider),
|
|
118
|
+
provider.permissionDeniedHandler.bind(provider),
|
|
119
|
+
provider.authenticatedHandler.bind(provider),
|
|
120
|
+
);
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
private applyQueryAwarenessMessage(provider: HocuspocusProvider) {
|
|
124
|
+
if (!provider.awareness) return;
|
|
125
|
+
|
|
126
|
+
const { message } = this;
|
|
127
|
+
|
|
128
|
+
message.writeVarUint(MessageType.Awareness);
|
|
129
|
+
message.writeVarUint8Array(
|
|
130
|
+
awarenessProtocol.encodeAwarenessUpdate(
|
|
131
|
+
provider.awareness,
|
|
132
|
+
Array.from(provider.awareness.getStates().keys()),
|
|
133
|
+
),
|
|
134
|
+
);
|
|
135
|
+
}
|
|
139
136
|
}
|
|
@@ -1,25 +1,23 @@
|
|
|
1
1
|
import { writeVarString, writeVarUint } from "lib0/encoding";
|
|
2
|
-
import { writeAuthentication } from "@
|
|
2
|
+
import { writeAuthentication } from "@abraca/dabra-common";
|
|
3
3
|
import type { OutgoingMessageArguments } from "../types.ts";
|
|
4
4
|
import { MessageType } from "../types.ts";
|
|
5
5
|
import { OutgoingMessage } from "../OutgoingMessage.ts";
|
|
6
6
|
|
|
7
7
|
export class AuthenticationMessage extends OutgoingMessage {
|
|
8
|
-
|
|
8
|
+
type = MessageType.Auth;
|
|
9
9
|
|
|
10
|
-
|
|
10
|
+
description = "Authentication";
|
|
11
11
|
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
);
|
|
17
|
-
}
|
|
12
|
+
get(args: Partial<OutgoingMessageArguments>) {
|
|
13
|
+
if (typeof args.token === "undefined") {
|
|
14
|
+
throw new Error("The authentication message requires `token` as an argument.");
|
|
15
|
+
}
|
|
18
16
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
17
|
+
writeVarString(this.encoder, args.documentName!);
|
|
18
|
+
writeVarUint(this.encoder, this.type);
|
|
19
|
+
writeAuthentication(this.encoder, args.token);
|
|
22
20
|
|
|
23
|
-
|
|
24
|
-
|
|
21
|
+
return this.encoder;
|
|
22
|
+
}
|
|
25
23
|
}
|
package/src/index.ts
CHANGED
|
@@ -2,6 +2,7 @@ export * from "./HocuspocusProvider.ts";
|
|
|
2
2
|
export * from "./HocuspocusProviderWebsocket.ts";
|
|
3
3
|
export * from "./types.ts";
|
|
4
4
|
export * from "./AbracadabraProvider.ts";
|
|
5
|
+
export * from "./AbracadabraClient.ts";
|
|
5
6
|
export * from "./OfflineStore.ts";
|
|
6
7
|
export { SubdocMessage } from "./OutgoingMessages/SubdocMessage.ts";
|
|
7
8
|
export { CryptoIdentityKeystore } from "./CryptoIdentityKeystore.ts";
|
package/src/types.ts
CHANGED
|
@@ -2,7 +2,7 @@ import type { Encoder } from "lib0/encoding";
|
|
|
2
2
|
import type { Event, MessageEvent } from "ws";
|
|
3
3
|
import type { Awareness } from "y-protocols/awareness";
|
|
4
4
|
import type * as Y from "yjs";
|
|
5
|
-
import type { CloseEvent } from "@
|
|
5
|
+
import type { CloseEvent } from "@abraca/dabra-common";
|
|
6
6
|
import type { IncomingMessage } from "./IncomingMessage.ts";
|
|
7
7
|
import type { OutgoingMessage } from "./OutgoingMessage.ts";
|
|
8
8
|
import type { AuthenticationMessage } from "./OutgoingMessages/AuthenticationMessage.ts";
|
|
@@ -13,104 +13,104 @@ import type { SyncStepTwoMessage } from "./OutgoingMessages/SyncStepTwoMessage.t
|
|
|
13
13
|
import type { UpdateMessage } from "./OutgoingMessages/UpdateMessage.ts";
|
|
14
14
|
|
|
15
15
|
export enum MessageType {
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
16
|
+
Sync = 0,
|
|
17
|
+
Awareness = 1,
|
|
18
|
+
Auth = 2,
|
|
19
|
+
QueryAwareness = 3,
|
|
20
|
+
Subdoc = 4,
|
|
21
|
+
Stateless = 5,
|
|
22
|
+
CLOSE = 7,
|
|
23
|
+
SyncStatus = 8,
|
|
24
24
|
}
|
|
25
25
|
|
|
26
26
|
export enum WebSocketStatus {
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
27
|
+
Connecting = "connecting",
|
|
28
|
+
Connected = "connected",
|
|
29
|
+
Disconnected = "disconnected",
|
|
30
30
|
}
|
|
31
31
|
|
|
32
32
|
export type AuthorizedScope = "read-write" | "readonly";
|
|
33
33
|
|
|
34
34
|
export interface OutgoingMessageInterface {
|
|
35
|
-
|
|
36
|
-
|
|
35
|
+
encoder: Encoder;
|
|
36
|
+
type?: MessageType;
|
|
37
37
|
}
|
|
38
38
|
|
|
39
39
|
export interface OutgoingMessageArguments {
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
40
|
+
documentName: string;
|
|
41
|
+
token: string;
|
|
42
|
+
document: Y.Doc;
|
|
43
|
+
awareness: Awareness;
|
|
44
|
+
clients: number[];
|
|
45
|
+
states: Map<number, { [key: string]: any }>;
|
|
46
|
+
update: any;
|
|
47
|
+
payload: string;
|
|
48
|
+
encoder: Encoder;
|
|
49
49
|
}
|
|
50
50
|
|
|
51
51
|
export interface Constructable<T> {
|
|
52
|
-
|
|
52
|
+
new (...args: any): T;
|
|
53
53
|
}
|
|
54
54
|
|
|
55
55
|
export type ConstructableOutgoingMessage =
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
56
|
+
| Constructable<AuthenticationMessage>
|
|
57
|
+
| Constructable<AwarenessMessage>
|
|
58
|
+
| Constructable<QueryAwarenessMessage>
|
|
59
|
+
| Constructable<SyncStepOneMessage>
|
|
60
|
+
| Constructable<SyncStepTwoMessage>
|
|
61
|
+
| Constructable<UpdateMessage>;
|
|
62
62
|
|
|
63
63
|
export type onAuthenticationFailedParameters = {
|
|
64
|
-
|
|
64
|
+
reason: string;
|
|
65
65
|
};
|
|
66
66
|
|
|
67
67
|
export type onAuthenticatedParameters = {
|
|
68
|
-
|
|
68
|
+
scope: AuthorizedScope;
|
|
69
69
|
};
|
|
70
70
|
|
|
71
71
|
export type onOpenParameters = {
|
|
72
|
-
|
|
72
|
+
event: Event;
|
|
73
73
|
};
|
|
74
74
|
|
|
75
75
|
export type onMessageParameters = {
|
|
76
|
-
|
|
77
|
-
|
|
76
|
+
event: MessageEvent;
|
|
77
|
+
message: IncomingMessage;
|
|
78
78
|
};
|
|
79
79
|
|
|
80
80
|
export type onOutgoingMessageParameters = {
|
|
81
|
-
|
|
81
|
+
message: OutgoingMessage;
|
|
82
82
|
};
|
|
83
83
|
|
|
84
84
|
export type onStatusParameters = {
|
|
85
|
-
|
|
85
|
+
status: WebSocketStatus;
|
|
86
86
|
};
|
|
87
87
|
|
|
88
88
|
export type onSyncedParameters = {
|
|
89
|
-
|
|
89
|
+
state: boolean;
|
|
90
90
|
};
|
|
91
91
|
|
|
92
92
|
export type onUnsyncedChangesParameters = {
|
|
93
|
-
|
|
93
|
+
number: number;
|
|
94
94
|
};
|
|
95
95
|
|
|
96
96
|
export type onDisconnectParameters = {
|
|
97
|
-
|
|
97
|
+
event: CloseEvent;
|
|
98
98
|
};
|
|
99
99
|
|
|
100
100
|
export type onCloseParameters = {
|
|
101
|
-
|
|
101
|
+
event: CloseEvent;
|
|
102
102
|
};
|
|
103
103
|
|
|
104
104
|
export type onAwarenessUpdateParameters = {
|
|
105
|
-
|
|
105
|
+
states: StatesArray;
|
|
106
106
|
};
|
|
107
107
|
|
|
108
108
|
export type onAwarenessChangeParameters = {
|
|
109
|
-
|
|
109
|
+
states: StatesArray;
|
|
110
110
|
};
|
|
111
111
|
|
|
112
112
|
export type onStatelessParameters = {
|
|
113
|
-
|
|
113
|
+
payload: string;
|
|
114
114
|
};
|
|
115
115
|
|
|
116
116
|
export type StatesArray = { clientId: number; [key: string | number]: any }[];
|
|
@@ -119,26 +119,70 @@ export type StatesArray = { clientId: number; [key: string | number]: any }[];
|
|
|
119
119
|
|
|
120
120
|
export type EffectiveRole = "owner" | "editor" | "viewer" | null;
|
|
121
121
|
|
|
122
|
-
/**
|
|
122
|
+
/**
|
|
123
|
+
* Ed25519 identity for passwordless crypto auth.
|
|
124
|
+
*
|
|
125
|
+
* The public key is the sole identifier sent to the server during the
|
|
126
|
+
* challenge-response handshake. Username is decoupled from auth and is
|
|
127
|
+
* managed separately as a mutable display name (see PATCH /users/me).
|
|
128
|
+
*/
|
|
123
129
|
export interface CryptoIdentity {
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
publicKey: string;
|
|
130
|
+
/** base64url-encoded Ed25519 public key (32 bytes). Primary auth identifier. */
|
|
131
|
+
publicKey: string;
|
|
127
132
|
}
|
|
128
133
|
|
|
129
134
|
export interface SubdocRegisteredEvent {
|
|
130
|
-
|
|
131
|
-
|
|
135
|
+
childId: string;
|
|
136
|
+
parentId: string;
|
|
132
137
|
}
|
|
133
138
|
|
|
134
139
|
export type onSubdocRegisteredParameters = SubdocRegisteredEvent;
|
|
135
140
|
|
|
136
141
|
export type onSubdocLoadedParameters = {
|
|
137
|
-
|
|
138
|
-
|
|
142
|
+
childId: string;
|
|
143
|
+
provider: import("./AbracadabraProvider.ts").AbracadabraProvider;
|
|
139
144
|
};
|
|
140
145
|
|
|
141
|
-
export interface AbracadabraOutgoingMessageArguments
|
|
142
|
-
|
|
143
|
-
|
|
146
|
+
export interface AbracadabraOutgoingMessageArguments extends OutgoingMessageArguments {
|
|
147
|
+
childDocumentName: string;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
// ── REST API response types ──────────────────────────────────────────────────
|
|
151
|
+
|
|
152
|
+
export interface UserProfile {
|
|
153
|
+
id: string;
|
|
154
|
+
username: string;
|
|
155
|
+
email: string | null;
|
|
156
|
+
displayName: string | null;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
export interface DocumentMeta {
|
|
160
|
+
id: string;
|
|
161
|
+
parent_id: string | null;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
export interface UploadMeta {
|
|
165
|
+
id: string;
|
|
166
|
+
doc_id: string;
|
|
167
|
+
filename: string;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
export interface UploadInfo {
|
|
171
|
+
id: string;
|
|
172
|
+
filename: string;
|
|
173
|
+
mime_type: string;
|
|
174
|
+
size: number;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
export interface PublicKeyInfo {
|
|
178
|
+
id: string;
|
|
179
|
+
publicKey: string;
|
|
180
|
+
deviceName: string | null;
|
|
181
|
+
revoked: boolean;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
export interface HealthStatus {
|
|
185
|
+
status: string;
|
|
186
|
+
version: string;
|
|
187
|
+
active_documents: number;
|
|
144
188
|
}
|