@abraca/dabra 2.0.10 → 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 +538 -8
- package/dist/abracadabra-provider.cjs.map +1 -1
- package/dist/abracadabra-provider.esm.js +532 -9
- package/dist/abracadabra-provider.esm.js.map +1 -1
- package/dist/index.d.ts +432 -1
- 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
|
@@ -548,6 +548,25 @@ declare class AbracadabraClient {
|
|
|
548
548
|
searchDocs(query: string, opts?: {
|
|
549
549
|
limit?: number;
|
|
550
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[]>;
|
|
551
570
|
/**
|
|
552
571
|
* List the direct children of a document, returning full metadata. Pass
|
|
553
572
|
* no argument to list the children of the server root — what the
|
|
@@ -2254,6 +2273,113 @@ declare class RpcClient extends EventEmitter {
|
|
|
2254
2273
|
private onCancel;
|
|
2255
2274
|
}
|
|
2256
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
|
|
2257
2383
|
//#region packages/provider/src/AbracadabraBaseProvider.d.ts
|
|
2258
2384
|
type AbracadabraBaseProviderConfiguration = Required<Pick<CompleteAbracadabraBaseProviderConfiguration, "name">> & Partial<CompleteAbracadabraBaseProviderConfiguration> & ((Required<Pick<CompleteAbracadabraWSConfiguration, "url">> & Partial<Pick<CompleteAbracadabraWSConfiguration, "preserveTrailingSlash">>) | Required<Pick<CompleteAbracadabraBaseProviderConfiguration, "websocketProvider">>);
|
|
2259
2385
|
/** @deprecated Use AbracadabraBaseProviderConfiguration */
|
|
@@ -2340,6 +2466,20 @@ declare class AbracadabraBaseProvider extends EventEmitter {
|
|
|
2340
2466
|
*/
|
|
2341
2467
|
private _rpc;
|
|
2342
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;
|
|
2343
2483
|
constructor(configuration: AbracadabraBaseProviderConfiguration);
|
|
2344
2484
|
boundDocumentUpdateHandler: (update: Uint8Array, origin: any) => void;
|
|
2345
2485
|
boundAwarenessUpdateHandler: ({
|
|
@@ -2404,6 +2544,127 @@ declare const HocuspocusProvider: typeof AbracadabraBaseProvider;
|
|
|
2404
2544
|
/** @deprecated Use AbracadabraBaseProvider */
|
|
2405
2545
|
type HocuspocusProvider = AbracadabraBaseProvider;
|
|
2406
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
|
|
2407
2668
|
//#region packages/provider/src/auth.d.ts
|
|
2408
2669
|
declare enum AuthMessageType {
|
|
2409
2670
|
Token = 0,
|
|
@@ -2758,6 +3019,8 @@ declare class BackgroundSyncManager extends EventEmitter {
|
|
|
2758
3019
|
private readonly persistence;
|
|
2759
3020
|
private readonly semaphore;
|
|
2760
3021
|
private readonly syncStates;
|
|
3022
|
+
/** Doc ids we've already warned about; keeps the log to one line per id. */
|
|
3023
|
+
private readonly _warnedInvalidIds;
|
|
2761
3024
|
private _destroyed;
|
|
2762
3025
|
private _initPromise;
|
|
2763
3026
|
constructor(rootProvider: AbracadabraProvider, client: AbracadabraClient, fileBlobStore?: FileBlobStore | null, opts?: BackgroundSyncManagerOptions);
|
|
@@ -4295,6 +4558,98 @@ declare const PAGE_TYPES: Record<string, PageTypeInfo>;
|
|
|
4295
4558
|
declare const TYPE_ALIASES: Record<string, string>;
|
|
4296
4559
|
declare function resolvePageType(key: string | undefined): PageTypeInfo | undefined;
|
|
4297
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
|
|
4298
4653
|
//#region packages/provider/src/TreeManager.d.ts
|
|
4299
4654
|
declare class TreeManager {
|
|
4300
4655
|
private dm;
|
|
@@ -4308,6 +4663,18 @@ declare class TreeManager {
|
|
|
4308
4663
|
/** Build nested tree JSON. */
|
|
4309
4664
|
buildTree(rootId?: string | null, maxDepth?: number): TreeNode[];
|
|
4310
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;
|
|
4311
4678
|
/** Find a single entry by ID. */
|
|
4312
4679
|
get(docId: string): TreeEntry | null;
|
|
4313
4680
|
/** Search by label (case-insensitive substring match). */
|
|
@@ -4459,25 +4826,75 @@ interface DocumentMetaInfo {
|
|
|
4459
4826
|
type?: string;
|
|
4460
4827
|
meta: PageMeta;
|
|
4461
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
|
+
}
|
|
4462
4864
|
declare class MetaManager {
|
|
4463
4865
|
private dm;
|
|
4866
|
+
private schema;
|
|
4464
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;
|
|
4465
4875
|
/** Read metadata for a document. Returns null if not found. */
|
|
4466
4876
|
get(docId: string): DocumentMetaInfo | null;
|
|
4467
4877
|
/**
|
|
4468
4878
|
* Merge fields into a document's metadata.
|
|
4469
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.
|
|
4470
4883
|
*/
|
|
4471
4884
|
update(docId: string, meta: Partial<PageMeta>): void;
|
|
4472
4885
|
/**
|
|
4473
4886
|
* Replace all metadata on a document.
|
|
4474
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.
|
|
4475
4891
|
*/
|
|
4476
4892
|
set(docId: string, meta: PageMeta): void;
|
|
4477
4893
|
/**
|
|
4478
4894
|
* Clear specific metadata keys (set them to null/undefined).
|
|
4479
4895
|
*/
|
|
4480
4896
|
clear(docId: string, keys: string[]): void;
|
|
4897
|
+
private validateOrThrow;
|
|
4481
4898
|
}
|
|
4482
4899
|
//#endregion
|
|
4483
4900
|
//#region packages/provider/src/DocumentManager.d.ts
|
|
@@ -4533,6 +4950,20 @@ declare class DocumentManager {
|
|
|
4533
4950
|
private _rootProvider;
|
|
4534
4951
|
private childCache;
|
|
4535
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>;
|
|
4536
4967
|
get displayName(): string;
|
|
4537
4968
|
get displayColor(): string;
|
|
4538
4969
|
get serverInfo(): ServerInfo | null;
|
|
@@ -4600,4 +5031,4 @@ declare function normalizeRootId(id: string | null | undefined, rootDocId: strin
|
|
|
4600
5031
|
*/
|
|
4601
5032
|
declare function toPlain(val: unknown): unknown;
|
|
4602
5033
|
//#endregion
|
|
4603
|
-
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
|