@abraca/dabra 1.0.10 → 1.0.12
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/abracadabra-provider.cjs +33 -7
- package/dist/abracadabra-provider.cjs.map +1 -1
- package/dist/abracadabra-provider.esm.js +33 -7
- package/dist/abracadabra-provider.esm.js.map +1 -1
- package/dist/index.d.ts +21 -2
- package/package.json +1 -1
- package/src/AbracadabraClient.ts +20 -0
- package/src/AbracadabraWS.ts +14 -5
- package/src/FileBlobStore.ts +23 -6
- package/src/types.ts +16 -0
package/dist/index.d.ts
CHANGED
|
@@ -362,6 +362,8 @@ declare class AbracadabraClient {
|
|
|
362
362
|
}): Promise<DocumentMeta>;
|
|
363
363
|
/** List all permissions for a document (requires read access). */
|
|
364
364
|
listPermissions(docId: string): Promise<PermissionEntry[]>;
|
|
365
|
+
/** List effective permissions including inherited ones from ancestor documents. */
|
|
366
|
+
listEffectivePermissions(docId: string): Promise<EffectivePermissionsResponse>;
|
|
365
367
|
/** Grant or change a user's role on a document (requires Owner). */
|
|
366
368
|
setPermission(docId: string, opts: {
|
|
367
369
|
user_id: string;
|
|
@@ -971,6 +973,18 @@ interface PermissionEntry {
|
|
|
971
973
|
username: string;
|
|
972
974
|
display_name: string | null;
|
|
973
975
|
}
|
|
976
|
+
interface EffectivePermissionEntry {
|
|
977
|
+
user_id: string;
|
|
978
|
+
role: "owner" | "editor" | "viewer" | "observer";
|
|
979
|
+
username: string;
|
|
980
|
+
display_name: string | null;
|
|
981
|
+
source: "direct" | "inherited";
|
|
982
|
+
inherited_from_doc_id?: string;
|
|
983
|
+
}
|
|
984
|
+
interface EffectivePermissionsResponse {
|
|
985
|
+
permissions: EffectivePermissionEntry[];
|
|
986
|
+
default_role: string;
|
|
987
|
+
}
|
|
974
988
|
interface HealthStatus {
|
|
975
989
|
status: string;
|
|
976
990
|
version: string;
|
|
@@ -983,6 +997,8 @@ interface ServerInfo {
|
|
|
983
997
|
version?: string;
|
|
984
998
|
/** Entry-point document ID advertised by the server, if configured. */
|
|
985
999
|
index_doc_id?: string;
|
|
1000
|
+
/** Default role assigned to users without explicit permissions. */
|
|
1001
|
+
default_role?: string;
|
|
986
1002
|
}
|
|
987
1003
|
interface SearchResult {
|
|
988
1004
|
docId: string;
|
|
@@ -1352,10 +1368,13 @@ declare class FileBlobStore extends EventEmitter {
|
|
|
1352
1368
|
private db;
|
|
1353
1369
|
/** Tracks active object URLs so we can revoke them on destroy. */
|
|
1354
1370
|
private readonly objectUrls;
|
|
1371
|
+
/** Keys that returned 404 from the server — avoids repeated requests. TTL: 5 min. */
|
|
1372
|
+
private readonly _notFound;
|
|
1373
|
+
private static readonly NOT_FOUND_TTL;
|
|
1355
1374
|
/** Prevents concurrent flush runs. */
|
|
1356
1375
|
private _flushing;
|
|
1357
1376
|
private readonly _onlineHandler;
|
|
1358
|
-
constructor(serverOrigin: string, client
|
|
1377
|
+
constructor(serverOrigin: string, client?: AbracadabraClient | null);
|
|
1359
1378
|
private getDb;
|
|
1360
1379
|
private blobKey;
|
|
1361
1380
|
/**
|
|
@@ -2114,4 +2133,4 @@ declare class BroadcastChannelSync extends EventEmitter {
|
|
|
2114
2133
|
private handleAwarenessMessage;
|
|
2115
2134
|
}
|
|
2116
2135
|
//#endregion
|
|
2117
|
-
export { AbracadabraBaseProvider, AbracadabraBaseProviderConfiguration, AbracadabraClient, AbracadabraClientConfig, AbracadabraOutgoingMessageArguments, AbracadabraProvider, AbracadabraProviderConfiguration, AbracadabraWS, AbracadabraWSConfiguration, AbracadabraWebRTC, type AbracadabraWebRTCConfiguration, AbracadabraWebSocketConn, AuthMessageType, AuthorizedScope, AwarenessError, BackgroundSyncManager, type BackgroundSyncManagerOptions, BackgroundSyncPersistence, BroadcastChannelSync, CHANNEL_NAMES, CloseEvent, CompleteAbracadabraBaseProviderConfiguration, CompleteAbracadabraWSConfiguration, CompleteHocuspocusProviderConfiguration, CompleteHocuspocusProviderWebsocketConfiguration, ConnectionTimeout, Constructable, ConstructableOutgoingMessage, CryptoIdentity, CryptoIdentityKeystore, DEFAULT_FILE_CHUNK_SIZE, DEFAULT_ICE_SERVERS, DataChannelRouter, type DocEncryptionInfo, DocKeyManager, type DocSyncState, DocumentCache, type DocumentCacheOptions, DocumentMeta, E2EAbracadabraProvider, E2EEChannel, type E2EEIdentity, E2EOfflineStore, EffectiveRole, EncryptedYMap, EncryptedYText, FileBlobStore, FileTransferChannel, FileTransferHandle, type FileTransferMeta, type FileTransferStatus, Forbidden, HealthStatus, HocusPocusWebSocket, HocuspocusProvider, HocuspocusProviderConfiguration, HocuspocusProviderWebsocket, HocuspocusProviderWebsocketConfiguration, HocuspocusWebSocket, InviteRow, KEY_EXCHANGE_CHANNEL, ManualSignaling, type ManualSignalingBlob, MessageTooBig, MessageType, OfflineStore, OutgoingMessageArguments, OutgoingMessageInterface, PeerConnection, type PeerInfo, type PeerState, PendingSubdoc, PermissionEntry, PublicKeyInfo, ResetConnection, SearchIndex, SearchResult, ServerInfo, type SignalingIncoming, type SignalingOutgoing, SignalingSocket, SpaceMeta, StatesArray, SubdocMessage, SubdocRegisteredEvent, Unauthorized, UploadInfo, UploadMeta, UploadQueueEntry, UploadQueueStatus, UserProfile, WebSocketStatus, WsReadyStates, YjsDataChannel, attachUpdatedAtObserver, awarenessStatesToArray, decryptField, encryptField, makeEncryptedYMap, makeEncryptedYText, onAuthenticatedParameters, onAuthenticationFailedParameters, onAwarenessChangeParameters, onAwarenessUpdateParameters, onCloseParameters, onDisconnectParameters, onMessageParameters, onOpenParameters, onOutgoingMessageParameters, onStatelessParameters, onStatusParameters, onSubdocLoadedParameters, onSubdocRegisteredParameters, onSyncedParameters, onUnsyncedChangesParameters, readAuthMessage, writeAuthenticated, writeAuthentication, writePermissionDenied, writeTokenSyncRequest };
|
|
2136
|
+
export { AbracadabraBaseProvider, AbracadabraBaseProviderConfiguration, AbracadabraClient, AbracadabraClientConfig, AbracadabraOutgoingMessageArguments, AbracadabraProvider, AbracadabraProviderConfiguration, AbracadabraWS, AbracadabraWSConfiguration, AbracadabraWebRTC, type AbracadabraWebRTCConfiguration, AbracadabraWebSocketConn, AuthMessageType, AuthorizedScope, AwarenessError, BackgroundSyncManager, type BackgroundSyncManagerOptions, BackgroundSyncPersistence, BroadcastChannelSync, CHANNEL_NAMES, CloseEvent, CompleteAbracadabraBaseProviderConfiguration, CompleteAbracadabraWSConfiguration, CompleteHocuspocusProviderConfiguration, CompleteHocuspocusProviderWebsocketConfiguration, ConnectionTimeout, Constructable, ConstructableOutgoingMessage, CryptoIdentity, CryptoIdentityKeystore, DEFAULT_FILE_CHUNK_SIZE, DEFAULT_ICE_SERVERS, DataChannelRouter, type DocEncryptionInfo, DocKeyManager, type DocSyncState, DocumentCache, type DocumentCacheOptions, DocumentMeta, E2EAbracadabraProvider, E2EEChannel, type E2EEIdentity, E2EOfflineStore, EffectivePermissionEntry, EffectivePermissionsResponse, EffectiveRole, EncryptedYMap, EncryptedYText, FileBlobStore, FileTransferChannel, FileTransferHandle, type FileTransferMeta, type FileTransferStatus, Forbidden, HealthStatus, HocusPocusWebSocket, HocuspocusProvider, HocuspocusProviderConfiguration, HocuspocusProviderWebsocket, HocuspocusProviderWebsocketConfiguration, HocuspocusWebSocket, InviteRow, KEY_EXCHANGE_CHANNEL, ManualSignaling, type ManualSignalingBlob, MessageTooBig, MessageType, OfflineStore, OutgoingMessageArguments, OutgoingMessageInterface, PeerConnection, type PeerInfo, type PeerState, PendingSubdoc, PermissionEntry, PublicKeyInfo, ResetConnection, SearchIndex, SearchResult, ServerInfo, type SignalingIncoming, type SignalingOutgoing, SignalingSocket, SpaceMeta, StatesArray, SubdocMessage, SubdocRegisteredEvent, Unauthorized, UploadInfo, UploadMeta, UploadQueueEntry, UploadQueueStatus, UserProfile, WebSocketStatus, WsReadyStates, YjsDataChannel, attachUpdatedAtObserver, awarenessStatesToArray, decryptField, encryptField, makeEncryptedYMap, makeEncryptedYText, onAuthenticatedParameters, onAuthenticationFailedParameters, onAwarenessChangeParameters, onAwarenessUpdateParameters, onCloseParameters, onDisconnectParameters, onMessageParameters, onOpenParameters, onOutgoingMessageParameters, onStatelessParameters, onStatusParameters, onSubdocLoadedParameters, onSubdocRegisteredParameters, onSyncedParameters, onUnsyncedChangesParameters, readAuthMessage, writeAuthenticated, writeAuthentication, writePermissionDenied, writeTokenSyncRequest };
|
package/package.json
CHANGED
package/src/AbracadabraClient.ts
CHANGED
|
@@ -5,6 +5,7 @@ import type {
|
|
|
5
5
|
UploadInfo,
|
|
6
6
|
PublicKeyInfo,
|
|
7
7
|
PermissionEntry,
|
|
8
|
+
EffectivePermissionsResponse,
|
|
8
9
|
HealthStatus,
|
|
9
10
|
ServerInfo,
|
|
10
11
|
InviteRow,
|
|
@@ -355,6 +356,25 @@ export class AbracadabraClient {
|
|
|
355
356
|
return res.permissions;
|
|
356
357
|
}
|
|
357
358
|
|
|
359
|
+
/** List effective permissions including inherited ones from ancestor documents. */
|
|
360
|
+
async listEffectivePermissions(
|
|
361
|
+
docId: string,
|
|
362
|
+
): Promise<EffectivePermissionsResponse> {
|
|
363
|
+
try {
|
|
364
|
+
return await this.request<EffectivePermissionsResponse>(
|
|
365
|
+
"GET",
|
|
366
|
+
`/docs/${encodeURIComponent(docId)}/effective-permissions`,
|
|
367
|
+
);
|
|
368
|
+
} catch {
|
|
369
|
+
// Fallback for older servers that don't support this endpoint
|
|
370
|
+
const perms = await this.listPermissions(docId);
|
|
371
|
+
return {
|
|
372
|
+
permissions: perms.map((p) => ({ ...p, source: "direct" as const })),
|
|
373
|
+
default_role: "viewer",
|
|
374
|
+
};
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
|
|
358
378
|
/** Grant or change a user's role on a document (requires Owner). */
|
|
359
379
|
async setPermission(
|
|
360
380
|
docId: string,
|
package/src/AbracadabraWS.ts
CHANGED
|
@@ -218,6 +218,13 @@ export class AbracadabraWS extends EventEmitter {
|
|
|
218
218
|
}
|
|
219
219
|
|
|
220
220
|
attach(provider: AbracadabraBaseProvider) {
|
|
221
|
+
const existing = this.configuration.providerMap.get(provider.configuration.name);
|
|
222
|
+
if (existing && existing !== provider) {
|
|
223
|
+
console.warn(
|
|
224
|
+
`[AbracadabraWS] attach: overwriting provider for "${provider.configuration.name}". ` +
|
|
225
|
+
`This may indicate a duplicate loadChild for the same document.`
|
|
226
|
+
);
|
|
227
|
+
}
|
|
221
228
|
this.configuration.providerMap.set(provider.configuration.name, provider);
|
|
222
229
|
|
|
223
230
|
if (this.status === WebSocketStatus.Disconnected && this.shouldConnect) {
|
|
@@ -230,12 +237,14 @@ export class AbracadabraWS extends EventEmitter {
|
|
|
230
237
|
}
|
|
231
238
|
|
|
232
239
|
detach(provider: AbracadabraBaseProvider) {
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
});
|
|
237
|
-
this.configuration.providerMap.delete(
|
|
240
|
+
const name = provider.configuration.name;
|
|
241
|
+
const current = this.configuration.providerMap.get(name);
|
|
242
|
+
if (current === provider) {
|
|
243
|
+
provider.send(CloseMessage, { documentName: name });
|
|
244
|
+
this.configuration.providerMap.delete(name);
|
|
238
245
|
}
|
|
246
|
+
// If current !== provider, another provider owns this slot —
|
|
247
|
+
// don't send CloseMessage or remove it from the map.
|
|
239
248
|
}
|
|
240
249
|
|
|
241
250
|
public setConfiguration(
|
package/src/FileBlobStore.ts
CHANGED
|
@@ -63,22 +63,26 @@ interface BlobCacheEntry {
|
|
|
63
63
|
|
|
64
64
|
export class FileBlobStore extends EventEmitter {
|
|
65
65
|
private readonly origin: string;
|
|
66
|
-
private readonly client: AbracadabraClient;
|
|
66
|
+
private readonly client: AbracadabraClient | null;
|
|
67
67
|
private dbPromise: Promise<IDBDatabase | null> | null = null;
|
|
68
68
|
private db: IDBDatabase | null = null;
|
|
69
69
|
|
|
70
70
|
/** Tracks active object URLs so we can revoke them on destroy. */
|
|
71
71
|
private readonly objectUrls = new Map<string, string>();
|
|
72
72
|
|
|
73
|
+
/** Keys that returned 404 from the server — avoids repeated requests. TTL: 5 min. */
|
|
74
|
+
private readonly _notFound = new Map<string, number>();
|
|
75
|
+
private static readonly NOT_FOUND_TTL = 5 * 60 * 1000;
|
|
76
|
+
|
|
73
77
|
/** Prevents concurrent flush runs. */
|
|
74
78
|
private _flushing = false;
|
|
75
79
|
|
|
76
80
|
private readonly _onlineHandler: () => void;
|
|
77
81
|
|
|
78
|
-
constructor(serverOrigin: string, client
|
|
82
|
+
constructor(serverOrigin: string, client?: AbracadabraClient | null) {
|
|
79
83
|
super();
|
|
80
84
|
this.origin = serverOrigin;
|
|
81
|
-
this.client = client;
|
|
85
|
+
this.client = client ?? null;
|
|
82
86
|
|
|
83
87
|
this._onlineHandler = () => { this.flushQueue().catch(() => null); };
|
|
84
88
|
if (typeof window !== "undefined") {
|
|
@@ -135,11 +139,23 @@ export class FileBlobStore extends EventEmitter {
|
|
|
135
139
|
}
|
|
136
140
|
}
|
|
137
141
|
|
|
138
|
-
// Not cached — try downloading
|
|
142
|
+
// Not cached — try downloading from server (requires a client)
|
|
143
|
+
if (!this.client) return null;
|
|
144
|
+
|
|
145
|
+
// Skip if we recently got a 404 for this key
|
|
146
|
+
const nfTime = this._notFound.get(key);
|
|
147
|
+
if (nfTime && Date.now() - nfTime < FileBlobStore.NOT_FOUND_TTL) {
|
|
148
|
+
return null;
|
|
149
|
+
}
|
|
150
|
+
|
|
139
151
|
let blob: Blob;
|
|
140
152
|
try {
|
|
141
153
|
blob = await this.client.getUpload(docId, uploadId);
|
|
142
|
-
} catch {
|
|
154
|
+
} catch (err: unknown) {
|
|
155
|
+
const status = (err as any)?.status;
|
|
156
|
+
if (status === 404) {
|
|
157
|
+
this._notFound.set(key, Date.now());
|
|
158
|
+
}
|
|
143
159
|
return null;
|
|
144
160
|
}
|
|
145
161
|
|
|
@@ -174,6 +190,7 @@ export class FileBlobStore extends EventEmitter {
|
|
|
174
190
|
if (typeof window === "undefined") return URL.createObjectURL(blob);
|
|
175
191
|
|
|
176
192
|
const key = this.blobKey(docId, uploadId);
|
|
193
|
+
this._notFound.delete(key);
|
|
177
194
|
|
|
178
195
|
// Return existing URL if already cached in-memory
|
|
179
196
|
const existing = this.objectUrls.get(key);
|
|
@@ -268,7 +285,7 @@ export class FileBlobStore extends EventEmitter {
|
|
|
268
285
|
* Entries that fail are marked with status "error" and left in the queue.
|
|
269
286
|
*/
|
|
270
287
|
async flushQueue(): Promise<void> {
|
|
271
|
-
if (this._flushing) return;
|
|
288
|
+
if (this._flushing || !this.client) return;
|
|
272
289
|
this._flushing = true;
|
|
273
290
|
|
|
274
291
|
try {
|
package/src/types.ts
CHANGED
|
@@ -202,6 +202,20 @@ export interface PermissionEntry {
|
|
|
202
202
|
display_name: string | null;
|
|
203
203
|
}
|
|
204
204
|
|
|
205
|
+
export interface EffectivePermissionEntry {
|
|
206
|
+
user_id: string;
|
|
207
|
+
role: "owner" | "editor" | "viewer" | "observer";
|
|
208
|
+
username: string;
|
|
209
|
+
display_name: string | null;
|
|
210
|
+
source: "direct" | "inherited";
|
|
211
|
+
inherited_from_doc_id?: string;
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
export interface EffectivePermissionsResponse {
|
|
215
|
+
permissions: EffectivePermissionEntry[];
|
|
216
|
+
default_role: string;
|
|
217
|
+
}
|
|
218
|
+
|
|
205
219
|
export interface HealthStatus {
|
|
206
220
|
status: string;
|
|
207
221
|
version: string;
|
|
@@ -215,6 +229,8 @@ export interface ServerInfo {
|
|
|
215
229
|
version?: string;
|
|
216
230
|
/** Entry-point document ID advertised by the server, if configured. */
|
|
217
231
|
index_doc_id?: string;
|
|
232
|
+
/** Default role assigned to users without explicit permissions. */
|
|
233
|
+
default_role?: string;
|
|
218
234
|
}
|
|
219
235
|
|
|
220
236
|
// ── Search ───────────────────────────────────────────────────────────────────
|