@abraca/dabra 2.0.9 → 2.3.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/dist/abracadabra-provider.cjs +540 -9
- package/dist/abracadabra-provider.cjs.map +1 -1
- package/dist/abracadabra-provider.esm.js +534 -10
- package/dist/abracadabra-provider.esm.js.map +1 -1
- package/dist/index.d.ts +434 -2
- package/package.json +8 -2
- package/src/AbracadabraBaseProvider.ts +28 -0
- package/src/AbracadabraClient.ts +32 -0
- package/src/BackgroundSyncManager.ts +34 -1
- package/src/DocumentManager.ts +70 -0
- package/src/MetaManager.ts +98 -1
- package/src/QueryClient.ts +209 -0
- package/src/SchemaTypes.ts +179 -0
- package/src/TokenManager.ts +329 -0
- package/src/TreeManager.ts +27 -0
- package/src/index.ts +32 -2
package/dist/index.d.ts
CHANGED
|
@@ -315,7 +315,8 @@ declare class AbracadabraClient {
|
|
|
315
315
|
deviceName?: string;
|
|
316
316
|
displayName?: string;
|
|
317
317
|
email?: string;
|
|
318
|
-
inviteCode?: string;
|
|
318
|
+
inviteCode?: string; /** X25519 encryption public key paired with the Ed25519 identity key. Required for E2E channels. */
|
|
319
|
+
x25519Key?: string;
|
|
319
320
|
}): Promise<UserProfile>;
|
|
320
321
|
/** Login with username + password. Auto-persists returned token. */
|
|
321
322
|
login(opts: {
|
|
@@ -547,6 +548,25 @@ declare class AbracadabraClient {
|
|
|
547
548
|
searchDocs(query: string, opts?: {
|
|
548
549
|
limit?: number;
|
|
549
550
|
}): Promise<DocSearchHit[]>;
|
|
551
|
+
/**
|
|
552
|
+
* Query for documents matching a structural predicate. v1 of the
|
|
553
|
+
* indexed-query layer described in
|
|
554
|
+
* `ARCHITECTURE/20-kickoff-phase3-queries.md`.
|
|
555
|
+
*
|
|
556
|
+
* The wire shape accepts a `where` field so v2 (meta-predicate
|
|
557
|
+
* filtering on top of the `doc_meta_index` projection) can light up
|
|
558
|
+
* without a compatibility break. Sending a non-empty `where` today
|
|
559
|
+
* returns a 400 — callers should leave it absent.
|
|
560
|
+
*
|
|
561
|
+
* Returns docs the requester can read at viewer-or-above, filtered
|
|
562
|
+
* through the cascading permission resolver server-side.
|
|
563
|
+
*/
|
|
564
|
+
queryDocs(opts: {
|
|
565
|
+
type?: string;
|
|
566
|
+
parentId?: string;
|
|
567
|
+
labelContains?: string;
|
|
568
|
+
limit?: number;
|
|
569
|
+
}): Promise<DocumentMeta[]>;
|
|
550
570
|
/**
|
|
551
571
|
* List the direct children of a document, returning full metadata. Pass
|
|
552
572
|
* no argument to list the children of the server root — what the
|
|
@@ -2253,6 +2273,113 @@ declare class RpcClient extends EventEmitter {
|
|
|
2253
2273
|
private onCancel;
|
|
2254
2274
|
}
|
|
2255
2275
|
//#endregion
|
|
2276
|
+
//#region packages/provider/src/QueryClient.d.ts
|
|
2277
|
+
/**
|
|
2278
|
+
* V2 query layer — live subscription client.
|
|
2279
|
+
*
|
|
2280
|
+
* Mirrors the server in `crates/abracadabra/src/query_stream.rs`. Frames
|
|
2281
|
+
* travel as `MSG_STATELESS` payloads with the literal `query:v1:` prefix
|
|
2282
|
+
* followed by a JSON envelope.
|
|
2283
|
+
*
|
|
2284
|
+
* Each call to `subscribeQuery(...)` opens a long-lived subscription:
|
|
2285
|
+
* 1. The client sends `req` with a fresh correlation id.
|
|
2286
|
+
* 2. The server replies `ack` with an initial snapshot
|
|
2287
|
+
* (`DocumentMeta[]`).
|
|
2288
|
+
* 3. The server emits `delta` frames as the doc-tree projection
|
|
2289
|
+
* drifts under the predicate.
|
|
2290
|
+
* 4. The client sends `cancel` to tear it down (or relies on the
|
|
2291
|
+
* WebSocket close path to free server state).
|
|
2292
|
+
*
|
|
2293
|
+
* The shape of the `query` field mirrors the REST `POST /docs/query`
|
|
2294
|
+
* body — `type`, `parentId`, `labelContains`, `where`, `limit`.
|
|
2295
|
+
*/
|
|
2296
|
+
declare const QUERY_PREFIX = "query:v1:";
|
|
2297
|
+
type QueryKind = "req" | "ack" | "delta" | "cancel" | "err";
|
|
2298
|
+
/** Mirrors `crate::query_stream::QuerySpec`. */
|
|
2299
|
+
interface QuerySpec {
|
|
2300
|
+
/** Match `documents.kind` exactly. */
|
|
2301
|
+
type?: string;
|
|
2302
|
+
/** Narrow to children of this doc. */
|
|
2303
|
+
parentId?: string;
|
|
2304
|
+
/** Case-insensitive substring on `documents.label`. */
|
|
2305
|
+
labelContains?: string;
|
|
2306
|
+
/** Maximum rows returned (default 50, hard cap 500 server-side). */
|
|
2307
|
+
limit?: number;
|
|
2308
|
+
/** Predicate AST. See `@abraca/schema/src/query.ts` for the shape. */
|
|
2309
|
+
where?: unknown;
|
|
2310
|
+
}
|
|
2311
|
+
interface DocumentMetaWire {
|
|
2312
|
+
id: string;
|
|
2313
|
+
parent_id?: string | null;
|
|
2314
|
+
label?: string | null;
|
|
2315
|
+
kind?: string | null;
|
|
2316
|
+
doc_type?: string | null;
|
|
2317
|
+
public_access?: string | null;
|
|
2318
|
+
description?: string | null;
|
|
2319
|
+
owner_id?: string | null;
|
|
2320
|
+
}
|
|
2321
|
+
interface QueryFrame {
|
|
2322
|
+
kind: QueryKind;
|
|
2323
|
+
id: string;
|
|
2324
|
+
query?: QuerySpec;
|
|
2325
|
+
initial?: DocumentMetaWire[];
|
|
2326
|
+
added?: DocumentMetaWire[];
|
|
2327
|
+
updated?: DocumentMetaWire[];
|
|
2328
|
+
removed?: string[];
|
|
2329
|
+
error?: {
|
|
2330
|
+
code: string;
|
|
2331
|
+
message: string;
|
|
2332
|
+
};
|
|
2333
|
+
}
|
|
2334
|
+
declare class QueryError extends Error {
|
|
2335
|
+
code: string;
|
|
2336
|
+
constructor(code: string, message: string);
|
|
2337
|
+
}
|
|
2338
|
+
interface QuerySubscriptionHandlers {
|
|
2339
|
+
/** Fired once with the initial snapshot from the server's `ack`. */
|
|
2340
|
+
onSnapshot?: (rows: DocumentMetaWire[]) => void;
|
|
2341
|
+
/**
|
|
2342
|
+
* Fired on every `delta` after the initial ack. `added` are rows
|
|
2343
|
+
* newly matching the predicate; `removed` are doc_ids that no
|
|
2344
|
+
* longer match (or are no longer readable). v2 does not emit
|
|
2345
|
+
* `updated` — clients see updates as remove + add today; that
|
|
2346
|
+
* gap closes in v3 when row-level updates are tracked.
|
|
2347
|
+
*/
|
|
2348
|
+
onDelta?: (delta: {
|
|
2349
|
+
added: DocumentMetaWire[];
|
|
2350
|
+
removed: string[];
|
|
2351
|
+
}) => void;
|
|
2352
|
+
/** Fired on `err` frames from the server. */
|
|
2353
|
+
onError?: (err: QueryError) => void;
|
|
2354
|
+
}
|
|
2355
|
+
interface QuerySubscriptionHandle {
|
|
2356
|
+
/** Correlation id assigned when the request was sent. */
|
|
2357
|
+
readonly id: string;
|
|
2358
|
+
/** Send a `cancel` to the server and stop dispatching to handlers. */
|
|
2359
|
+
cancel(): void;
|
|
2360
|
+
}
|
|
2361
|
+
/**
|
|
2362
|
+
* Minimal transport surface — matches `RpcTransport` so the provider
|
|
2363
|
+
* can satisfy both without extra glue.
|
|
2364
|
+
*/
|
|
2365
|
+
interface QueryTransport {
|
|
2366
|
+
sendStateless(payload: string): void;
|
|
2367
|
+
on(event: string, fn: Function): unknown;
|
|
2368
|
+
off(event: string, fn?: Function): unknown;
|
|
2369
|
+
}
|
|
2370
|
+
declare class QueryClient extends EventEmitter {
|
|
2371
|
+
private readonly transport;
|
|
2372
|
+
private readonly subs;
|
|
2373
|
+
private readonly onStatelessBound;
|
|
2374
|
+
private idCounter;
|
|
2375
|
+
constructor(transport: QueryTransport);
|
|
2376
|
+
destroy(): void;
|
|
2377
|
+
subscribeQuery(spec: QuerySpec, handlers?: QuerySubscriptionHandlers): QuerySubscriptionHandle;
|
|
2378
|
+
private nextId;
|
|
2379
|
+
private send;
|
|
2380
|
+
private receive;
|
|
2381
|
+
}
|
|
2382
|
+
//#endregion
|
|
2256
2383
|
//#region packages/provider/src/AbracadabraBaseProvider.d.ts
|
|
2257
2384
|
type AbracadabraBaseProviderConfiguration = Required<Pick<CompleteAbracadabraBaseProviderConfiguration, "name">> & Partial<CompleteAbracadabraBaseProviderConfiguration> & ((Required<Pick<CompleteAbracadabraWSConfiguration, "url">> & Partial<Pick<CompleteAbracadabraWSConfiguration, "preserveTrailingSlash">>) | Required<Pick<CompleteAbracadabraBaseProviderConfiguration, "websocketProvider">>);
|
|
2258
2385
|
/** @deprecated Use AbracadabraBaseProviderConfiguration */
|
|
@@ -2339,6 +2466,20 @@ declare class AbracadabraBaseProvider extends EventEmitter {
|
|
|
2339
2466
|
*/
|
|
2340
2467
|
private _rpc;
|
|
2341
2468
|
get rpc(): RpcClient;
|
|
2469
|
+
/**
|
|
2470
|
+
* Lazily-constructed V2 query subscription client. See
|
|
2471
|
+
* `QueryClient`. Each call to `subscribeQuery(...)` opens a
|
|
2472
|
+
* long-lived subscription that fires `onSnapshot` with the initial
|
|
2473
|
+
* result set and `onDelta` whenever the projection moves.
|
|
2474
|
+
*/
|
|
2475
|
+
private _queryClient;
|
|
2476
|
+
private getQueryClient;
|
|
2477
|
+
/**
|
|
2478
|
+
* Open a live query subscription. Mirrors the v1 REST shape
|
|
2479
|
+
* (`POST /docs/query`) but pushes deltas as the server-side
|
|
2480
|
+
* projection drifts under the predicate.
|
|
2481
|
+
*/
|
|
2482
|
+
subscribeQuery(spec: QuerySpec, handlers?: QuerySubscriptionHandlers): QuerySubscriptionHandle;
|
|
2342
2483
|
constructor(configuration: AbracadabraBaseProviderConfiguration);
|
|
2343
2484
|
boundDocumentUpdateHandler: (update: Uint8Array, origin: any) => void;
|
|
2344
2485
|
boundAwarenessUpdateHandler: ({
|
|
@@ -2403,6 +2544,127 @@ declare const HocuspocusProvider: typeof AbracadabraBaseProvider;
|
|
|
2403
2544
|
/** @deprecated Use AbracadabraBaseProvider */
|
|
2404
2545
|
type HocuspocusProvider = AbracadabraBaseProvider;
|
|
2405
2546
|
//#endregion
|
|
2547
|
+
//#region packages/provider/src/TokenManager.d.ts
|
|
2548
|
+
/**
|
|
2549
|
+
* On-disk shape of a device session. The server returns these values from
|
|
2550
|
+
* `POST /auth/device-session`; persistence keeps them across reloads so a
|
|
2551
|
+
* warm boot can mint a fresh JWT without re-prompting for the passkey.
|
|
2552
|
+
*/
|
|
2553
|
+
interface DeviceSessionRecord {
|
|
2554
|
+
sessionId: string;
|
|
2555
|
+
sessionToken: string;
|
|
2556
|
+
/** Unix seconds — matches what the server returns. */
|
|
2557
|
+
expiresAt: number;
|
|
2558
|
+
}
|
|
2559
|
+
interface DeviceSessionStorage {
|
|
2560
|
+
load(): DeviceSessionRecord | null;
|
|
2561
|
+
save(record: DeviceSessionRecord): void;
|
|
2562
|
+
clear(): void;
|
|
2563
|
+
}
|
|
2564
|
+
/** localStorage-backed storage; safe in SSR/Node (no-ops without localStorage). */
|
|
2565
|
+
declare class LocalStorageDeviceSessionStorage implements DeviceSessionStorage {
|
|
2566
|
+
private readonly key;
|
|
2567
|
+
constructor(key?: string);
|
|
2568
|
+
load(): DeviceSessionRecord | null;
|
|
2569
|
+
save(record: DeviceSessionRecord): void;
|
|
2570
|
+
clear(): void;
|
|
2571
|
+
}
|
|
2572
|
+
interface TokenManagerOptions {
|
|
2573
|
+
client: AbracadabraClient;
|
|
2574
|
+
/**
|
|
2575
|
+
* Where the device session record lives. Defaults to a localStorage
|
|
2576
|
+
* adapter under "abracadabra:device-session".
|
|
2577
|
+
*/
|
|
2578
|
+
storage?: DeviceSessionStorage;
|
|
2579
|
+
/**
|
|
2580
|
+
* Refresh the JWT this many milliseconds before its `exp`. Defaults to
|
|
2581
|
+
* 5 minutes — well clear of clock skew without burning a refresh per
|
|
2582
|
+
* minute. Refreshes are also dedup'd via in-flight promise.
|
|
2583
|
+
*/
|
|
2584
|
+
refreshLeadMs?: number;
|
|
2585
|
+
/**
|
|
2586
|
+
* Hook `document.visibilitychange` and `window.online` to opportunistically
|
|
2587
|
+
* refresh when the tab/network resumes. Default true. The proactive
|
|
2588
|
+
* `setTimeout` doesn't fire reliably while the tab is hidden or the
|
|
2589
|
+
* laptop is asleep, which is the actual breakage path being addressed —
|
|
2590
|
+
* the resume hook is what makes the system self-heal after a long sleep.
|
|
2591
|
+
*/
|
|
2592
|
+
installResumeHandlers?: boolean;
|
|
2593
|
+
}
|
|
2594
|
+
/**
|
|
2595
|
+
* Manages the lifetime of the short-lived JWT against a long-lived device
|
|
2596
|
+
* session token.
|
|
2597
|
+
*
|
|
2598
|
+
* Flow:
|
|
2599
|
+
* - Bootstrap: if a stored device session exists, exchange it for a fresh
|
|
2600
|
+
* JWT immediately (`bootstrap()`). Otherwise the consumer must run a
|
|
2601
|
+
* full crypto-auth handshake and then call `attachSession(record)` once
|
|
2602
|
+
* the server returns a device-session token.
|
|
2603
|
+
* - Steady state: `getValidToken()` returns the JWT, refreshing if the
|
|
2604
|
+
* JWT is within `refreshLeadMs` of expiry. Concurrent callers share one
|
|
2605
|
+
* in-flight refresh.
|
|
2606
|
+
* - Background: a proactive timer fires `refreshLeadMs` before `exp`.
|
|
2607
|
+
* `visibilitychange` and `online` events trigger an opportunistic
|
|
2608
|
+
* refresh in case the timer was suppressed (hidden tab, sleeping
|
|
2609
|
+
* laptop). The combination is what restores the WS without a reload.
|
|
2610
|
+
* - Failure: refresh errors with status 401/403 mean the device session
|
|
2611
|
+
* itself is dead (revoked or 30 d expired). The record is cleared and
|
|
2612
|
+
* `session-expired` fires so the consumer can prompt re-auth. Other
|
|
2613
|
+
* errors (network) are transparent — the existing JWT is returned and
|
|
2614
|
+
* the caller's normal retry loop covers it.
|
|
2615
|
+
*/
|
|
2616
|
+
declare class TokenManager extends EventEmitter {
|
|
2617
|
+
private readonly client;
|
|
2618
|
+
private readonly storage;
|
|
2619
|
+
private readonly refreshLeadMs;
|
|
2620
|
+
private session;
|
|
2621
|
+
private refreshing;
|
|
2622
|
+
private proactiveTimer;
|
|
2623
|
+
private resumeHandlersInstalled;
|
|
2624
|
+
private boundOnResume;
|
|
2625
|
+
private disposed;
|
|
2626
|
+
constructor(options: TokenManagerOptions);
|
|
2627
|
+
get hasSession(): boolean;
|
|
2628
|
+
get currentSession(): DeviceSessionRecord | null;
|
|
2629
|
+
/**
|
|
2630
|
+
* Persist a freshly-issued device session and exchange it immediately
|
|
2631
|
+
* for a JWT. Call this once after a successful crypto-auth login when
|
|
2632
|
+
* the server returns a device-session token.
|
|
2633
|
+
*/
|
|
2634
|
+
attachSession(record: DeviceSessionRecord): Promise<string>;
|
|
2635
|
+
/**
|
|
2636
|
+
* Drop the local device session (no server call). Use after a logout
|
|
2637
|
+
* or when `session-expired` fires. To revoke server-side as well, call
|
|
2638
|
+
* `client.revokeDeviceSession(sessionId)` first.
|
|
2639
|
+
*/
|
|
2640
|
+
clearSession(): void;
|
|
2641
|
+
/**
|
|
2642
|
+
* Return a valid JWT, refreshing if it is missing or within
|
|
2643
|
+
* `refreshLeadMs` of expiry. Safe to call concurrently — one
|
|
2644
|
+
* refresh is in flight at a time.
|
|
2645
|
+
*
|
|
2646
|
+
* Transient refresh errors (network) fall back to the cached JWT so
|
|
2647
|
+
* the WS auth attempt can still proceed; the server will reject and
|
|
2648
|
+
* the next reconnect tries again. Only 401/403 from the refresh
|
|
2649
|
+
* endpoint surfaces as a thrown error here, since those mean the
|
|
2650
|
+
* device session itself is dead.
|
|
2651
|
+
*/
|
|
2652
|
+
getValidToken(): Promise<string>;
|
|
2653
|
+
/**
|
|
2654
|
+
* If a stored device session exists, exchange it for a fresh JWT now.
|
|
2655
|
+
* Returns the JWT (or null if there is no session to bootstrap from).
|
|
2656
|
+
* Throws on 401/403 — the stored session is invalid and the caller
|
|
2657
|
+
* should fall through to a full crypto-auth flow.
|
|
2658
|
+
*/
|
|
2659
|
+
bootstrap(): Promise<string | null>;
|
|
2660
|
+
dispose(): void;
|
|
2661
|
+
private runRefresh;
|
|
2662
|
+
private tokenIsFresh;
|
|
2663
|
+
private parseExp;
|
|
2664
|
+
private scheduleProactiveRefresh;
|
|
2665
|
+
private installResumeHandlers;
|
|
2666
|
+
}
|
|
2667
|
+
//#endregion
|
|
2406
2668
|
//#region packages/provider/src/auth.d.ts
|
|
2407
2669
|
declare enum AuthMessageType {
|
|
2408
2670
|
Token = 0,
|
|
@@ -2757,6 +3019,8 @@ declare class BackgroundSyncManager extends EventEmitter {
|
|
|
2757
3019
|
private readonly persistence;
|
|
2758
3020
|
private readonly semaphore;
|
|
2759
3021
|
private readonly syncStates;
|
|
3022
|
+
/** Doc ids we've already warned about; keeps the log to one line per id. */
|
|
3023
|
+
private readonly _warnedInvalidIds;
|
|
2760
3024
|
private _destroyed;
|
|
2761
3025
|
private _initPromise;
|
|
2762
3026
|
constructor(rootProvider: AbracadabraProvider, client: AbracadabraClient, fileBlobStore?: FileBlobStore | null, opts?: BackgroundSyncManagerOptions);
|
|
@@ -4294,6 +4558,98 @@ declare const PAGE_TYPES: Record<string, PageTypeInfo>;
|
|
|
4294
4558
|
declare const TYPE_ALIASES: Record<string, string>;
|
|
4295
4559
|
declare function resolvePageType(key: string | undefined): PageTypeInfo | undefined;
|
|
4296
4560
|
//#endregion
|
|
4561
|
+
//#region packages/provider/src/SchemaTypes.d.ts
|
|
4562
|
+
/**
|
|
4563
|
+
* Structural shape of a typed schema registry. The `TMap` parameter is the
|
|
4564
|
+
* meta-type map keyed by doc-type name (e.g. `{ "kanban": KanbanMeta, ... }`).
|
|
4565
|
+
* `@abraca/schema`'s `SchemaRegistry<TMap>` satisfies this shape.
|
|
4566
|
+
*/
|
|
4567
|
+
interface SchemaRegistryLike<TMap extends Record<string, unknown> = Record<string, unknown>> {
|
|
4568
|
+
readonly types: ReadonlyMap<string, unknown>;
|
|
4569
|
+
readonly __metaMap?: TMap;
|
|
4570
|
+
}
|
|
4571
|
+
/** Extract the doc-type names from a registry. */
|
|
4572
|
+
type SchemaDocTypeName<S> = S extends SchemaRegistryLike<infer M> ? keyof M & string : never;
|
|
4573
|
+
/** Extract the meta type for a given doc-type name from a registry. */
|
|
4574
|
+
type SchemaMetaOf<S, N extends string> = S extends SchemaRegistryLike<infer M> ? N extends keyof M ? M[N] : never : never;
|
|
4575
|
+
/**
|
|
4576
|
+
* A tree entry projected against a typed registry. The `meta` field is
|
|
4577
|
+
* narrowed to `TMap[N] | undefined` so consumers get full type inference
|
|
4578
|
+
* without an explicit cast.
|
|
4579
|
+
*
|
|
4580
|
+
* Note: `meta` is optional even when the registry mandates fields — the
|
|
4581
|
+
* tree may contain entries written before the schema was authored, and
|
|
4582
|
+
* Rule 4 means we don't reject them at read time.
|
|
4583
|
+
*/
|
|
4584
|
+
interface TypedTreeEntry<TMap extends Record<string, unknown>, N extends keyof TMap & string> {
|
|
4585
|
+
readonly id: string;
|
|
4586
|
+
readonly type: N;
|
|
4587
|
+
readonly label: string;
|
|
4588
|
+
readonly parentId: string | null;
|
|
4589
|
+
readonly order: number;
|
|
4590
|
+
readonly meta: TMap[N] | undefined;
|
|
4591
|
+
readonly createdAt: number | undefined;
|
|
4592
|
+
readonly updatedAt: number | undefined;
|
|
4593
|
+
}
|
|
4594
|
+
/**
|
|
4595
|
+
* A typed surface bound to a schema. Returned by
|
|
4596
|
+
* `DocumentManager.docs(schema)` so the schema doesn't have to be repeated
|
|
4597
|
+
* at every call site.
|
|
4598
|
+
*
|
|
4599
|
+
* Read methods (`get`, `getEntry`, `narrow`) project the existing
|
|
4600
|
+
* untyped tree without performing schema validation. Write methods
|
|
4601
|
+
* (`update`, `set`, `clear`) delegate to `MetaManager` and are subject
|
|
4602
|
+
* to whatever validator was attached via `MetaManager.setSchema` —
|
|
4603
|
+
* they do NOT auto-attach the registry passed to `dm.docs()`.
|
|
4604
|
+
*/
|
|
4605
|
+
interface TypedDocsClient<TMap extends Record<string, unknown>> {
|
|
4606
|
+
/**
|
|
4607
|
+
* Fetch a tree entry projected as the requested doc-type. Returns
|
|
4608
|
+
* `null` when the entry doesn't exist OR when its `type` field doesn't
|
|
4609
|
+
* match `expectedType`.
|
|
4610
|
+
*/
|
|
4611
|
+
get<N extends keyof TMap & string>(expectedType: N, id: string): TypedTreeEntry<TMap, N> | null;
|
|
4612
|
+
/**
|
|
4613
|
+
* Fetch the raw, untyped entry (no type-narrowing applied). Useful when
|
|
4614
|
+
* the caller wants the underlying record without the projection.
|
|
4615
|
+
*/
|
|
4616
|
+
getEntry(id: string): TreeEntry | null;
|
|
4617
|
+
/**
|
|
4618
|
+
* Discriminator helper: narrows an arbitrary `TreeEntry` to the
|
|
4619
|
+
* requested doc-type, returning a typed projection or `null` on mismatch.
|
|
4620
|
+
* No runtime read — pure type-guard helper.
|
|
4621
|
+
*/
|
|
4622
|
+
narrow<N extends keyof TMap & string>(expectedType: N, entry: TreeEntry | null | undefined): TypedTreeEntry<TMap, N> | null;
|
|
4623
|
+
/**
|
|
4624
|
+
* Merge typed meta into a document's metadata. Throws
|
|
4625
|
+
* `TypedDocTypeMismatchError` when the stored entry's `type` doesn't
|
|
4626
|
+
* match `expectedType`; throws `MetaValidationError` when a validator
|
|
4627
|
+
* is attached and the merged meta fails validation.
|
|
4628
|
+
*/
|
|
4629
|
+
update<N extends keyof TMap & string>(expectedType: N, id: string, meta: Partial<TMap[N]>): void;
|
|
4630
|
+
/**
|
|
4631
|
+
* Replace all metadata on a document with a fully-typed payload.
|
|
4632
|
+
* Same throw semantics as `update`.
|
|
4633
|
+
*/
|
|
4634
|
+
set<N extends keyof TMap & string>(expectedType: N, id: string, meta: TMap[N]): void;
|
|
4635
|
+
/**
|
|
4636
|
+
* Delete specific metadata keys. Constrained to the typed key-set of
|
|
4637
|
+
* `TMap[N]` so typos surface at compile time.
|
|
4638
|
+
*/
|
|
4639
|
+
clear<N extends keyof TMap & string>(expectedType: N, id: string, keys: ReadonlyArray<keyof TMap[N] & string>): void;
|
|
4640
|
+
}
|
|
4641
|
+
/**
|
|
4642
|
+
* Thrown by typed write methods on `TypedDocsClient` when the stored
|
|
4643
|
+
* entry's `type` doesn't match the caller's `expectedType`. Distinct from
|
|
4644
|
+
* `MetaValidationError` (which signals schema-content mismatches).
|
|
4645
|
+
*/
|
|
4646
|
+
declare class TypedDocTypeMismatchError extends Error {
|
|
4647
|
+
readonly docId: string;
|
|
4648
|
+
readonly expectedType: string;
|
|
4649
|
+
readonly actualType: string | undefined;
|
|
4650
|
+
constructor(docId: string, expectedType: string, actualType: string | undefined);
|
|
4651
|
+
}
|
|
4652
|
+
//#endregion
|
|
4297
4653
|
//#region packages/provider/src/TreeManager.d.ts
|
|
4298
4654
|
declare class TreeManager {
|
|
4299
4655
|
private dm;
|
|
@@ -4307,6 +4663,18 @@ declare class TreeManager {
|
|
|
4307
4663
|
/** Build nested tree JSON. */
|
|
4308
4664
|
buildTree(rootId?: string | null, maxDepth?: number): TreeNode[];
|
|
4309
4665
|
private _buildTree;
|
|
4666
|
+
/**
|
|
4667
|
+
* Schema-typed lookup. Returns a `TypedTreeEntry<TMap, N>` when the
|
|
4668
|
+
* entry's `type` matches `expectedType`, else `null`. Pure projection
|
|
4669
|
+
* over the existing untyped tree — no schema validation is performed
|
|
4670
|
+
* here (the entry's data is whatever was last synced; meta correctness
|
|
4671
|
+
* is the writer's responsibility, optionally enforced via
|
|
4672
|
+
* `MetaManager.setSchema`).
|
|
4673
|
+
*
|
|
4674
|
+
* Rule 4 alignment: when the entry's type doesn't match, returns null
|
|
4675
|
+
* rather than throwing — callers branch on the result.
|
|
4676
|
+
*/
|
|
4677
|
+
getTyped<TMap extends Record<string, unknown>, N extends keyof TMap & string>(_schema: SchemaRegistryLike<TMap>, expectedType: N, docId: string): TypedTreeEntry<TMap, N> | null;
|
|
4310
4678
|
/** Find a single entry by ID. */
|
|
4311
4679
|
get(docId: string): TreeEntry | null;
|
|
4312
4680
|
/** Search by label (case-insensitive substring match). */
|
|
@@ -4458,25 +4826,75 @@ interface DocumentMetaInfo {
|
|
|
4458
4826
|
type?: string;
|
|
4459
4827
|
meta: PageMeta;
|
|
4460
4828
|
}
|
|
4829
|
+
/**
|
|
4830
|
+
* Structural shape of a schema validator MetaManager can consult before
|
|
4831
|
+
* writes. Compatible with `@abraca/schema`'s `SchemaRegistry`.
|
|
4832
|
+
*
|
|
4833
|
+
* Implementations MUST treat unknown doc-types as unconstrained
|
|
4834
|
+
* (return `{ ok: true }`) — Rule 4: existing-app traffic for doc-types
|
|
4835
|
+
* not in the validator's bundle is never rejected.
|
|
4836
|
+
*/
|
|
4837
|
+
interface SchemaValidatorLike {
|
|
4838
|
+
validateMeta(docType: string, meta: unknown): {
|
|
4839
|
+
ok: true;
|
|
4840
|
+
value?: unknown;
|
|
4841
|
+
} | {
|
|
4842
|
+
ok: false;
|
|
4843
|
+
errors: ReadonlyArray<{
|
|
4844
|
+
path: ReadonlyArray<PropertyKey>;
|
|
4845
|
+
message: string;
|
|
4846
|
+
code?: string;
|
|
4847
|
+
}>;
|
|
4848
|
+
};
|
|
4849
|
+
}
|
|
4850
|
+
declare class MetaValidationError extends Error {
|
|
4851
|
+
readonly docId: string;
|
|
4852
|
+
readonly docType: string;
|
|
4853
|
+
readonly errors: ReadonlyArray<{
|
|
4854
|
+
path: ReadonlyArray<PropertyKey>;
|
|
4855
|
+
message: string;
|
|
4856
|
+
code?: string;
|
|
4857
|
+
}>;
|
|
4858
|
+
constructor(docId: string, docType: string, errors: ReadonlyArray<{
|
|
4859
|
+
path: ReadonlyArray<PropertyKey>;
|
|
4860
|
+
message: string;
|
|
4861
|
+
code?: string;
|
|
4862
|
+
}>);
|
|
4863
|
+
}
|
|
4461
4864
|
declare class MetaManager {
|
|
4462
4865
|
private dm;
|
|
4866
|
+
private schema;
|
|
4463
4867
|
constructor(dm: DocumentManager);
|
|
4868
|
+
/**
|
|
4869
|
+
* Attach (or detach with `null`) a schema validator. When set, every
|
|
4870
|
+
* `update` / `set` validates the post-write meta against the entry's
|
|
4871
|
+
* declared `type` before writing to the doc-tree. Entries without a
|
|
4872
|
+
* `type` field pass through unconditionally.
|
|
4873
|
+
*/
|
|
4874
|
+
setSchema(schema: SchemaValidatorLike | null): void;
|
|
4464
4875
|
/** Read metadata for a document. Returns null if not found. */
|
|
4465
4876
|
get(docId: string): DocumentMetaInfo | null;
|
|
4466
4877
|
/**
|
|
4467
4878
|
* Merge fields into a document's metadata.
|
|
4468
4879
|
* Existing keys not in the update are preserved.
|
|
4880
|
+
*
|
|
4881
|
+
* @throws {MetaValidationError} when a schema is attached and the
|
|
4882
|
+
* merged meta fails validation for the entry's declared type.
|
|
4469
4883
|
*/
|
|
4470
4884
|
update(docId: string, meta: Partial<PageMeta>): void;
|
|
4471
4885
|
/**
|
|
4472
4886
|
* Replace all metadata on a document.
|
|
4473
4887
|
* This overwrites the entire meta object.
|
|
4888
|
+
*
|
|
4889
|
+
* @throws {MetaValidationError} when a schema is attached and the
|
|
4890
|
+
* replacement meta fails validation for the entry's declared type.
|
|
4474
4891
|
*/
|
|
4475
4892
|
set(docId: string, meta: PageMeta): void;
|
|
4476
4893
|
/**
|
|
4477
4894
|
* Clear specific metadata keys (set them to null/undefined).
|
|
4478
4895
|
*/
|
|
4479
4896
|
clear(docId: string, keys: string[]): void;
|
|
4897
|
+
private validateOrThrow;
|
|
4480
4898
|
}
|
|
4481
4899
|
//#endregion
|
|
4482
4900
|
//#region packages/provider/src/DocumentManager.d.ts
|
|
@@ -4532,6 +4950,20 @@ declare class DocumentManager {
|
|
|
4532
4950
|
private _rootProvider;
|
|
4533
4951
|
private childCache;
|
|
4534
4952
|
constructor(config: DocumentManagerConfig);
|
|
4953
|
+
/**
|
|
4954
|
+
* Bind a schema registry to a typed accessor surface. The returned
|
|
4955
|
+
* client offers `.get(type, id)` with the registry's meta types
|
|
4956
|
+
* inferred at the call site, removing the need to pass `schema`
|
|
4957
|
+
* to every lookup. The provider remains schema-free at runtime —
|
|
4958
|
+
* `schema` is only used to drive type inference (see
|
|
4959
|
+
* `SchemaRegistryLike` for the structural witness).
|
|
4960
|
+
*
|
|
4961
|
+
* @example
|
|
4962
|
+
* const docs = dm.docs(kanbanSchema);
|
|
4963
|
+
* const board = docs.get("kanban", id);
|
|
4964
|
+
* if (board) console.log(board.meta.kanbanColumnWidth); // typed
|
|
4965
|
+
*/
|
|
4966
|
+
docs<TMap extends Record<string, unknown>>(_schema: SchemaRegistryLike<TMap>): TypedDocsClient<TMap>;
|
|
4535
4967
|
get displayName(): string;
|
|
4536
4968
|
get displayColor(): string;
|
|
4537
4969
|
get serverInfo(): ServerInfo | null;
|
|
@@ -4599,4 +5031,4 @@ declare function normalizeRootId(id: string | null | undefined, rootDocId: strin
|
|
|
4599
5031
|
*/
|
|
4600
5032
|
declare function toPlain(val: unknown): unknown;
|
|
4601
5033
|
//#endregion
|
|
4602
|
-
export { AbracadabraBaseProvider, AbracadabraBaseProviderConfiguration, AbracadabraClient, AbracadabraClientConfig, AbracadabraOutgoingMessageArguments, AbracadabraProvider, AbracadabraProviderConfiguration, AbracadabraWS, AbracadabraWSConfiguration, AbracadabraWebRTC, type AbracadabraWebRTCConfiguration, AbracadabraWebSocketConn, AdminConfigField, AdminConfigOriginKind, AuditLogEntry, AuditQueryOpts, AuditVerifyResult, AuthFailureContext, AuthFailureReason, AuthMessageType, AuthorizedScope, AwarenessError, BackgroundSyncManager, type BackgroundSyncManagerOptions, BackgroundSyncPersistence, BroadcastChannelSync, CHANNEL_NAMES, ChannelKeyResolver, type ChatChannel, ChatClient, type ChatClientTransport, type MarkReadInput as ChatMarkReadInput, type ChatMessage, type ChatReadCursor, type ChatReadReceipt, type ChatTypingEvent, CloseEvent, CompleteAbracadabraBaseProviderConfiguration, CompleteAbracadabraWSConfiguration, CompleteHocuspocusProviderConfiguration, CompleteHocuspocusProviderWebsocketConfiguration, ConnectionTimeout, Constructable, ConstructableOutgoingMessage, ContentManager, type CreateNotificationInput, CryptoIdentity, CryptoIdentityKeystore, DEFAULT_FILE_CHUNK_SIZE, DEFAULT_ICE_SERVERS, DataChannelRouter, type DeleteMessageInput, DevicePairingChannel, type DevicePairingConfig, DeviceRegistrationService, type DeviceServerStatus, type DeviceTier, type DocEncryptionInfo, DocKeyManager, DocSearchHit, type DocSyncState, type DocumentBlock, DocumentCache, type DocumentCacheOptions, type DocumentContent, DocumentManager, type DocumentManagerConfig, DocumentMeta, type DocumentMetaInfo, E2EAbracadabraProvider, E2EEChannel, type E2EEIdentity, E2EOfflineStore, type EditMessageInput, EffectivePermissionEntry, EffectivePermissionsResponse, EffectiveRole, EncryptedChatClient, EncryptedYMap, EncryptedYText, EnvSnapshotExtension, EnvSnapshotItem, EnvSnapshotResponse, type FetchInboxInput, type FetchNotificationsInput, FileBlobStore, FileTransferChannel, FileTransferHandle, type FileTransferMeta, type FileTransferStatus, type FoldedMessage, Forbidden, GEO_TYPE_META_SCHEMAS, type GetChatHistoryInput, HealthStatus, HocusPocusWebSocket, HocuspocusProvider, HocuspocusProviderConfiguration, HocuspocusProviderWebsocket, HocuspocusProviderWebsocketConfiguration, HocuspocusWebSocket, type IdentityDeviceEntry, type IdentityDocConfiguration, IdentityDocProvider, type IdentityProfile, type IdentityServerEntry, type IdentitySpaceEntry, type InboxEntry, type MarkReadInput$1 as InboxMarkReadInput, InviteRow, KEY_EXCHANGE_CHANNEL, Kind, ManualSignaling, type ManualSignalingBlob, type MessageRecord, MessageTooBig, MessageType, MetaFieldType, MetaManager, type NotificationReadUpdate, type NotificationRecord, NotificationsClient, OfflineStore, OutgoingMessageArguments, OutgoingMessageInterface, PAGE_TYPES, PageMeta, PageTypeInfo, PageTypeMetaField, type PairingRequest, type PairingResult, PeerConnection, type PeerInfo, type PeerState, PendingSubdoc, PermissionEntry, PublicKeyInfo, RPC_PREFIX, ReadyzStatus, ResetConnection, type RpcCallHandle, type RpcCallOptions, RpcClient, RpcError, type RpcErrorCode, type RpcErrorPayload, type RpcFrame, type RpcHandler, type RpcHandlerContext, type RpcKind, type RpcTarget, type RpcTransport, SERVER_ROOT_ID, SearchIndex, SearchResult, type SendChatMessageInput, type SendMessageInput, type SendMessageResult, ServerInfo, type SignalingIncoming, type SignalingOutgoing, SignalingSocket, SnapshotCreateResult, SnapshotData, SnapshotFileEntry, SnapshotForkResult, SnapshotMeta, SnapshotRestoreResult, StatesArray, SubdocMessage, SubdocRegisteredEvent, TYPE_ALIASES, TreeEntry, TreeManager, TreeNode, TreeSearchResult, Unauthorized, UploadInfo, UploadMeta, UploadQueueEntry, UploadQueueStatus, UserMetaField, UserProfile, WebSocketStatus, WsReadyStates, YjsDataChannel, attachUpdatedAtObserver, awarenessStatesToArray, wordlist as bip39Wordlist, buildBlockquoteElement, buildBlocksFromMarkdown, buildBulletListElement, buildCodeBlockElement, buildHeadingElement, buildHorizontalRuleElement, buildOrderedListElement, buildParagraphElement, buildTaskListElement, decryptChatContent, decryptField, deriveIdentityDocId, deriveSeedWrappingKey, encryptChatContent, encryptField, filenameToLabel, foldRecords, generateMnemonic, isEncryptedContent, makeEncryptedYMap, makeEncryptedYText, mnemonicToEd25519Seed, mnemonicToKeyPair, normalizeRootId, onAuthenticatedParameters, onAuthenticationFailedParameters, onAwarenessChangeParameters, onAwarenessUpdateParameters, onCloseParameters, onCompactedParameters, onDisconnectParameters, onMessageParameters, onOpenParameters, onOutgoingMessageParameters, onServerErrorParameters, onStatelessParameters, onStatusParameters, onSubdocLoadedParameters, onSubdocRegisteredParameters, onSyncedParameters, onUnsyncedChangesParameters, parseFrontmatter, populateYDocFromMarkdown, readAuthMessage, readBlocksFromFragment, recordFromYAny, resolvePageType, toPlain, unwrapSeed, validateMnemonic, waitForSync, withTimeout, wrapSeed, writeAuthenticated, writeAuthentication, writePermissionDenied, writeTokenSyncRequest, yjsToMarkdown };
|
|
5034
|
+
export { AbracadabraBaseProvider, AbracadabraBaseProviderConfiguration, AbracadabraClient, AbracadabraClientConfig, AbracadabraOutgoingMessageArguments, AbracadabraProvider, AbracadabraProviderConfiguration, AbracadabraWS, AbracadabraWSConfiguration, AbracadabraWebRTC, type AbracadabraWebRTCConfiguration, AbracadabraWebSocketConn, AdminConfigField, AdminConfigOriginKind, AuditLogEntry, AuditQueryOpts, AuditVerifyResult, AuthFailureContext, AuthFailureReason, AuthMessageType, AuthorizedScope, AwarenessError, BackgroundSyncManager, type BackgroundSyncManagerOptions, BackgroundSyncPersistence, BroadcastChannelSync, CHANNEL_NAMES, ChannelKeyResolver, type ChatChannel, ChatClient, type ChatClientTransport, type MarkReadInput as ChatMarkReadInput, type ChatMessage, type ChatReadCursor, type ChatReadReceipt, type ChatTypingEvent, CloseEvent, CompleteAbracadabraBaseProviderConfiguration, CompleteAbracadabraWSConfiguration, CompleteHocuspocusProviderConfiguration, CompleteHocuspocusProviderWebsocketConfiguration, ConnectionTimeout, Constructable, ConstructableOutgoingMessage, ContentManager, type CreateNotificationInput, CryptoIdentity, CryptoIdentityKeystore, DEFAULT_FILE_CHUNK_SIZE, DEFAULT_ICE_SERVERS, DataChannelRouter, type DeleteMessageInput, DevicePairingChannel, type DevicePairingConfig, DeviceRegistrationService, type DeviceServerStatus, type DeviceSessionRecord, type DeviceSessionStorage, type DeviceTier, type DocEncryptionInfo, DocKeyManager, DocSearchHit, type DocSyncState, type DocumentBlock, DocumentCache, type DocumentCacheOptions, type DocumentContent, DocumentManager, type DocumentManagerConfig, DocumentMeta, type DocumentMetaInfo, type DocumentMetaWire, E2EAbracadabraProvider, E2EEChannel, type E2EEIdentity, E2EOfflineStore, type EditMessageInput, EffectivePermissionEntry, EffectivePermissionsResponse, EffectiveRole, EncryptedChatClient, EncryptedYMap, EncryptedYText, EnvSnapshotExtension, EnvSnapshotItem, EnvSnapshotResponse, type FetchInboxInput, type FetchNotificationsInput, FileBlobStore, FileTransferChannel, FileTransferHandle, type FileTransferMeta, type FileTransferStatus, type FoldedMessage, Forbidden, GEO_TYPE_META_SCHEMAS, type GetChatHistoryInput, HealthStatus, HocusPocusWebSocket, HocuspocusProvider, HocuspocusProviderConfiguration, HocuspocusProviderWebsocket, HocuspocusProviderWebsocketConfiguration, HocuspocusWebSocket, type IdentityDeviceEntry, type IdentityDocConfiguration, IdentityDocProvider, type IdentityProfile, type IdentityServerEntry, type IdentitySpaceEntry, type InboxEntry, type MarkReadInput$1 as InboxMarkReadInput, InviteRow, KEY_EXCHANGE_CHANNEL, Kind, LocalStorageDeviceSessionStorage, ManualSignaling, type ManualSignalingBlob, type MessageRecord, MessageTooBig, MessageType, MetaFieldType, MetaManager, MetaValidationError, type NotificationReadUpdate, type NotificationRecord, NotificationsClient, OfflineStore, OutgoingMessageArguments, OutgoingMessageInterface, PAGE_TYPES, PageMeta, PageTypeInfo, PageTypeMetaField, type PairingRequest, type PairingResult, PeerConnection, type PeerInfo, type PeerState, PendingSubdoc, PermissionEntry, PublicKeyInfo, QUERY_PREFIX, QueryClient, QueryError, type QueryFrame, type QueryKind, type QuerySpec, type QuerySubscriptionHandle, type QuerySubscriptionHandlers, type QueryTransport, RPC_PREFIX, ReadyzStatus, ResetConnection, type RpcCallHandle, type RpcCallOptions, RpcClient, RpcError, type RpcErrorCode, type RpcErrorPayload, type RpcFrame, type RpcHandler, type RpcHandlerContext, type RpcKind, type RpcTarget, type RpcTransport, SERVER_ROOT_ID, type SchemaDocTypeName, type SchemaMetaOf, type SchemaRegistryLike, type SchemaValidatorLike, SearchIndex, SearchResult, type SendChatMessageInput, type SendMessageInput, type SendMessageResult, ServerInfo, type SignalingIncoming, type SignalingOutgoing, SignalingSocket, SnapshotCreateResult, SnapshotData, SnapshotFileEntry, SnapshotForkResult, SnapshotMeta, SnapshotRestoreResult, StatesArray, SubdocMessage, SubdocRegisteredEvent, TYPE_ALIASES, TokenManager, type TokenManagerOptions, TreeEntry, TreeManager, TreeNode, TreeSearchResult, TypedDocTypeMismatchError, type TypedDocsClient, type TypedTreeEntry, Unauthorized, UploadInfo, UploadMeta, UploadQueueEntry, UploadQueueStatus, UserMetaField, UserProfile, WebSocketStatus, WsReadyStates, YjsDataChannel, attachUpdatedAtObserver, awarenessStatesToArray, wordlist as bip39Wordlist, buildBlockquoteElement, buildBlocksFromMarkdown, buildBulletListElement, buildCodeBlockElement, buildHeadingElement, buildHorizontalRuleElement, buildOrderedListElement, buildParagraphElement, buildTaskListElement, decryptChatContent, decryptField, deriveIdentityDocId, deriveSeedWrappingKey, encryptChatContent, encryptField, filenameToLabel, foldRecords, generateMnemonic, isEncryptedContent, makeEncryptedYMap, makeEncryptedYText, mnemonicToEd25519Seed, mnemonicToKeyPair, normalizeRootId, onAuthenticatedParameters, onAuthenticationFailedParameters, onAwarenessChangeParameters, onAwarenessUpdateParameters, onCloseParameters, onCompactedParameters, onDisconnectParameters, onMessageParameters, onOpenParameters, onOutgoingMessageParameters, onServerErrorParameters, onStatelessParameters, onStatusParameters, onSubdocLoadedParameters, onSubdocRegisteredParameters, onSyncedParameters, onUnsyncedChangesParameters, parseFrontmatter, populateYDocFromMarkdown, readAuthMessage, readBlocksFromFragment, recordFromYAny, resolvePageType, toPlain, unwrapSeed, validateMnemonic, waitForSync, withTimeout, wrapSeed, writeAuthenticated, writeAuthentication, writePermissionDenied, writeTokenSyncRequest, yjsToMarkdown };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@abraca/dabra",
|
|
3
|
-
"version": "2.0
|
|
3
|
+
"version": "2.3.0",
|
|
4
4
|
"description": "abracadabra provider",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"abracadabra",
|
|
@@ -39,5 +39,11 @@
|
|
|
39
39
|
"peerDependencies": {
|
|
40
40
|
"y-protocols": "^1.0.6",
|
|
41
41
|
"yjs": "^13.6.8"
|
|
42
|
+
},
|
|
43
|
+
"devDependencies": {
|
|
44
|
+
"@abraca/schema": "2.3.0"
|
|
45
|
+
},
|
|
46
|
+
"scripts": {
|
|
47
|
+
"test": "node --no-warnings --conditions=source --experimental-transform-types --test 'tests/*.test.ts'"
|
|
42
48
|
}
|
|
43
|
-
}
|
|
49
|
+
}
|
|
@@ -12,6 +12,8 @@ import { AuthenticationMessage } from "./OutgoingMessages/AuthenticationMessage.
|
|
|
12
12
|
import { AwarenessMessage } from "./OutgoingMessages/AwarenessMessage.ts";
|
|
13
13
|
import { StatelessMessage } from "./OutgoingMessages/StatelessMessage.ts";
|
|
14
14
|
import { RpcClient } from "./RpcClient.ts";
|
|
15
|
+
import { QueryClient } from "./QueryClient.ts";
|
|
16
|
+
import type { QuerySpec, QuerySubscriptionHandlers, QuerySubscriptionHandle } from "./QueryClient.ts";
|
|
15
17
|
import { SyncStepOneMessage } from "./OutgoingMessages/SyncStepOneMessage.ts";
|
|
16
18
|
import { UpdateMessage } from "./OutgoingMessages/UpdateMessage.ts";
|
|
17
19
|
import type {
|
|
@@ -181,6 +183,32 @@ export class AbracadabraBaseProvider extends EventEmitter {
|
|
|
181
183
|
return this._rpc;
|
|
182
184
|
}
|
|
183
185
|
|
|
186
|
+
/**
|
|
187
|
+
* Lazily-constructed V2 query subscription client. See
|
|
188
|
+
* `QueryClient`. Each call to `subscribeQuery(...)` opens a
|
|
189
|
+
* long-lived subscription that fires `onSnapshot` with the initial
|
|
190
|
+
* result set and `onDelta` whenever the projection moves.
|
|
191
|
+
*/
|
|
192
|
+
private _queryClient: QueryClient | undefined;
|
|
193
|
+
private getQueryClient(): QueryClient {
|
|
194
|
+
if (!this._queryClient) {
|
|
195
|
+
this._queryClient = new QueryClient(this);
|
|
196
|
+
}
|
|
197
|
+
return this._queryClient;
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
/**
|
|
201
|
+
* Open a live query subscription. Mirrors the v1 REST shape
|
|
202
|
+
* (`POST /docs/query`) but pushes deltas as the server-side
|
|
203
|
+
* projection drifts under the predicate.
|
|
204
|
+
*/
|
|
205
|
+
subscribeQuery(
|
|
206
|
+
spec: QuerySpec,
|
|
207
|
+
handlers: QuerySubscriptionHandlers = {},
|
|
208
|
+
): QuerySubscriptionHandle {
|
|
209
|
+
return this.getQueryClient().subscribeQuery(spec, handlers);
|
|
210
|
+
}
|
|
211
|
+
|
|
184
212
|
constructor(configuration: AbracadabraBaseProviderConfiguration) {
|
|
185
213
|
super();
|
|
186
214
|
this.setConfiguration(configuration);
|
package/src/AbracadabraClient.ts
CHANGED
|
@@ -603,6 +603,38 @@ export class AbracadabraClient {
|
|
|
603
603
|
return res.results;
|
|
604
604
|
}
|
|
605
605
|
|
|
606
|
+
/**
|
|
607
|
+
* Query for documents matching a structural predicate. v1 of the
|
|
608
|
+
* indexed-query layer described in
|
|
609
|
+
* `ARCHITECTURE/20-kickoff-phase3-queries.md`.
|
|
610
|
+
*
|
|
611
|
+
* The wire shape accepts a `where` field so v2 (meta-predicate
|
|
612
|
+
* filtering on top of the `doc_meta_index` projection) can light up
|
|
613
|
+
* without a compatibility break. Sending a non-empty `where` today
|
|
614
|
+
* returns a 400 — callers should leave it absent.
|
|
615
|
+
*
|
|
616
|
+
* Returns docs the requester can read at viewer-or-above, filtered
|
|
617
|
+
* through the cascading permission resolver server-side.
|
|
618
|
+
*/
|
|
619
|
+
async queryDocs(opts: {
|
|
620
|
+
type?: string;
|
|
621
|
+
parentId?: string;
|
|
622
|
+
labelContains?: string;
|
|
623
|
+
limit?: number;
|
|
624
|
+
}): Promise<DocumentMeta[]> {
|
|
625
|
+
const body: Record<string, unknown> = {};
|
|
626
|
+
if (opts.type != null) body.type = opts.type;
|
|
627
|
+
if (opts.parentId != null) body.parent_id = opts.parentId;
|
|
628
|
+
if (opts.labelContains != null) body.label_contains = opts.labelContains;
|
|
629
|
+
if (opts.limit != null) body.limit = opts.limit;
|
|
630
|
+
const res = await this.request<{ documents: DocumentMeta[]; v: number }>(
|
|
631
|
+
"POST",
|
|
632
|
+
"/docs/query",
|
|
633
|
+
{ body },
|
|
634
|
+
);
|
|
635
|
+
return res.documents;
|
|
636
|
+
}
|
|
637
|
+
|
|
606
638
|
/**
|
|
607
639
|
* List the direct children of a document, returning full metadata. Pass
|
|
608
640
|
* no argument to list the children of the server root — what the
|