@drakkar.software/starfish-client 3.0.0-alpha.2 → 3.0.0-alpha.21
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/README.md +44 -0
- package/dist/append-log.d.ts +228 -0
- package/dist/bindings/legend.d.ts +23 -0
- package/dist/bindings/legend.js +32 -0
- package/dist/bindings/legend.js.map +2 -2
- package/dist/bindings/zustand.d.ts +72 -1
- package/dist/bindings/zustand.js +427 -63
- package/dist/bindings/zustand.js.map +3 -3
- package/dist/client.d.ts +128 -5
- package/dist/config.d.ts +9 -0
- package/dist/index.d.ts +9 -5
- package/dist/index.js +578 -60
- package/dist/index.js.map +4 -4
- package/dist/logger.d.ts +3 -0
- package/dist/mobile-lifecycle.d.ts +28 -1
- package/dist/mutate.d.ts +39 -0
- package/dist/sync.d.ts +28 -0
- package/dist/types.d.ts +62 -0
- package/package.json +2 -2
package/dist/client.d.ts
CHANGED
|
@@ -1,5 +1,17 @@
|
|
|
1
1
|
import type { PullResult, PushSuccess } from "@drakkar.software/starfish-protocol";
|
|
2
|
+
import { type AppendAuthor } from "@drakkar.software/starfish-protocol";
|
|
2
3
|
import type { StarfishClientOptions } from "./types.js";
|
|
4
|
+
/**
|
|
5
|
+
* Whether a {@link PullResult} was served from the offline read-through cache
|
|
6
|
+
* (the transport was unreachable) rather than a live server response. Used by
|
|
7
|
+
* {@link SyncManager} to surface a `stale` flag to the UI without treating a
|
|
8
|
+
* cache hit as proof the server is reachable.
|
|
9
|
+
*/
|
|
10
|
+
export declare function pullWasFromCache(result: PullResult): boolean;
|
|
11
|
+
/** The storage `documentKey` for a push `path`: the path with the `/push/`
|
|
12
|
+
* action prefix stripped (the namespace lives only in the URL). The author
|
|
13
|
+
* signature binds to this key. */
|
|
14
|
+
export declare function stripPushPrefix(path: string): string;
|
|
3
15
|
/** Result of pulling a binary blob from the server. */
|
|
4
16
|
export interface BlobPullResult {
|
|
5
17
|
data: ArrayBuffer;
|
|
@@ -19,6 +31,13 @@ export interface AppendPullOptions {
|
|
|
19
31
|
since?: number;
|
|
20
32
|
/** Return only the last K items (applied after `since` filter). Sent as `?last=`. */
|
|
21
33
|
last?: number;
|
|
34
|
+
/** Return only the last K items. Alias of `last`; sent as `?limit=`. When both
|
|
35
|
+
* are given, `limit` wins. */
|
|
36
|
+
limit?: number;
|
|
37
|
+
/** Explicitly fetch the whole collection (sent as `?full=true`). Mutually
|
|
38
|
+
* exclusive with `since`/`limit`/`last` — the server requires a pull to declare
|
|
39
|
+
* exactly one of {checkpoint, limit/last, full}. */
|
|
40
|
+
full?: boolean;
|
|
22
41
|
}
|
|
23
42
|
/**
|
|
24
43
|
* Options for a structured (non-append) pull.
|
|
@@ -36,20 +55,55 @@ export interface PullOptions {
|
|
|
36
55
|
/** Include the sibling `_keyring` document in the response. Defaults to false. */
|
|
37
56
|
withKeyring?: boolean;
|
|
38
57
|
}
|
|
58
|
+
/** Per-collection result in a {@link BatchPullResult}: either the pulled
|
|
59
|
+
* document (`data`/`hash`/`timestamp`) or a per-collection `error` string. */
|
|
60
|
+
export interface BatchPullEntry {
|
|
61
|
+
data?: unknown;
|
|
62
|
+
hash?: string;
|
|
63
|
+
timestamp?: number;
|
|
64
|
+
error?: string;
|
|
65
|
+
}
|
|
66
|
+
/** Response of {@link StarfishClient.batchPull}: a map of requested collection
|
|
67
|
+
* name → an ARRAY of {@link BatchPullEntry}, one per requested param-set, in
|
|
68
|
+
* request order. A collection read with no params yields a one-element array. */
|
|
69
|
+
export interface BatchPullResult {
|
|
70
|
+
collections: Record<string, BatchPullEntry[]>;
|
|
71
|
+
}
|
|
72
|
+
/** Options for {@link StarfishClient.batchPull}. */
|
|
73
|
+
export interface BatchPullOptions {
|
|
74
|
+
/** Per-collection path params: collection name → an ARRAY of param-sets, one
|
|
75
|
+
* per document to read from that collection, e.g.
|
|
76
|
+
* `{ profile: [{ identity: "a" }, { identity: "b" }] }` reads two profiles in
|
|
77
|
+
* one round-trip. Serialized to a URL-encoded JSON `params` query. The
|
|
78
|
+
* `{identity}` param is auto-filled by the server from the authenticated
|
|
79
|
+
* caller when a set omits it, so a single self-doc read can pass `[{}]` — or
|
|
80
|
+
* omit the collection from `params` entirely (an unlisted collection reads one
|
|
81
|
+
* auto-filled doc). Results come back under the same name in request order. */
|
|
82
|
+
params?: Record<string, Record<string, string>[]>;
|
|
83
|
+
}
|
|
39
84
|
/**
|
|
40
85
|
* Low-level HTTP client for the Starfish sync protocol.
|
|
41
86
|
* Handles auth headers and response parsing.
|
|
42
87
|
*/
|
|
43
88
|
export declare class StarfishClient {
|
|
44
89
|
private readonly baseUrl;
|
|
90
|
+
private readonly namespace?;
|
|
45
91
|
private readonly capProvider?;
|
|
46
92
|
private readonly fetch;
|
|
93
|
+
private readonly cache?;
|
|
94
|
+
private readonly cacheMaxAgeMs?;
|
|
47
95
|
/**
|
|
48
96
|
* Installed client-side plugins. Currently stored as inert data; no
|
|
49
97
|
* hooks fire yet. Extensions can inspect this list if needed.
|
|
50
98
|
*/
|
|
51
99
|
readonly plugins: ReadonlyArray<import("./types.js").ClientPlugin>;
|
|
52
100
|
constructor(options: StarfishClientOptions);
|
|
101
|
+
/**
|
|
102
|
+
* Mark a `PullResult` as having been served from the offline read-through
|
|
103
|
+
* cache (transport was unreachable). Non-enumerable so it doesn't leak into
|
|
104
|
+
* JSON / equality / re-caching; read via {@link pullWasFromCache}.
|
|
105
|
+
*/
|
|
106
|
+
private tagFromCache;
|
|
53
107
|
/**
|
|
54
108
|
* Resolve the host portion of the URL the client will send to. The host
|
|
55
109
|
* is folded into the signed canonical input as the `h` field so the
|
|
@@ -63,6 +117,18 @@ export declare class StarfishClient {
|
|
|
63
117
|
* empty-host case still verifies symmetrically when both sides agree.
|
|
64
118
|
*/
|
|
65
119
|
private signingHost;
|
|
120
|
+
/**
|
|
121
|
+
* Rewrite a request path for the configured namespace. A no-op when no
|
|
122
|
+
* namespace is set; otherwise `/{action}/…` becomes `/v1/{namespace}/{action}/…`
|
|
123
|
+
* (the `/v1` protocol-version segment is part of the namespaced route, matching
|
|
124
|
+
* the Python client and the server's namespace mount).
|
|
125
|
+
*
|
|
126
|
+
* Applied to the path used for BOTH the signature and the URL so the canonical
|
|
127
|
+
* path the client signs equals the path the server reconstructs from the URL.
|
|
128
|
+
* Covers SDK-helper-built paths too — that's the point: a namespace-unaware
|
|
129
|
+
* helper passing `/push/spaces/x/_keyring` reaches `/v1/{ns}/push/spaces/x/_keyring`.
|
|
130
|
+
*/
|
|
131
|
+
private applyNamespace;
|
|
66
132
|
/**
|
|
67
133
|
* Build auth headers for a request. When a `capProvider` is set, signs the
|
|
68
134
|
* request with the device's Ed25519 private key and returns the v3 header
|
|
@@ -74,23 +140,78 @@ export declare class StarfishClient {
|
|
|
74
140
|
* The host bound into the signature is derived from `baseUrl` once per call.
|
|
75
141
|
*/
|
|
76
142
|
private buildAuthHeaders;
|
|
143
|
+
/**
|
|
144
|
+
* Build the request-signing headers from an ALREADY-fetched cap context. Split
|
|
145
|
+
* out of {@link buildAuthHeaders} so {@link append} can fetch the cap once and
|
|
146
|
+
* reuse it for BOTH the author signature (over the element data) and the
|
|
147
|
+
* request signature (over the body), without redeeming the cap twice — a
|
|
148
|
+
* second `getCap()` could rotate keys and break the `authorPubkey ===
|
|
149
|
+
* presenter` bind the server checks.
|
|
150
|
+
*/
|
|
151
|
+
private capRequestHeaders;
|
|
152
|
+
/**
|
|
153
|
+
* Resolve the author public key to attach to a signed append: the redeemer's
|
|
154
|
+
* `pubHex` for an audience cap, else the cert subject `cap.sub` for a
|
|
155
|
+
* device/member cap. This is the SAME key that signs the request, so a server
|
|
156
|
+
* enforcing author proof can bind the stored element to its writer. Returns
|
|
157
|
+
* undefined only for a (malformed) cap with neither — the append then goes
|
|
158
|
+
* unsigned and a server requiring signatures rejects it.
|
|
159
|
+
*/
|
|
160
|
+
private appendAuthorKey;
|
|
77
161
|
/** Pull synced data from the server. Returns the raw `PullResult`. */
|
|
78
162
|
pull(path: string, checkpoint?: number): Promise<PullResult>;
|
|
79
163
|
/** Pull synced data with structured options (e.g. `{withKeyring: true}`). */
|
|
80
164
|
pull(path: string, options: PullOptions): Promise<PullResult>;
|
|
81
165
|
/** Pull an append-only collection. Extracts and returns `data[appendField]` as `T[]`. */
|
|
82
166
|
pull<T = unknown>(path: string, options: AppendPullOptions): Promise<T[]>;
|
|
167
|
+
/**
|
|
168
|
+
* Read the cached snapshot for a document `path` WITHOUT hitting the network —
|
|
169
|
+
* the basis for cache-first paint (seed the UI from the last-synced snapshot,
|
|
170
|
+
* then revalidate with a live {@link pull}). Returns the tagged `PullResult`,
|
|
171
|
+
* or null when no cache is configured / there's no entry. Namespacing matches
|
|
172
|
+
* {@link pull}, so the key lines up with whatever `pull` wrote.
|
|
173
|
+
*/
|
|
174
|
+
peekCache(path: string): Promise<PullResult | null>;
|
|
175
|
+
/** Read + parse a cached pull snapshot, tagged {@link tagFromCache}. Returns
|
|
176
|
+
* null on a miss or an unparseable blob (never throws — a corrupt cache entry
|
|
177
|
+
* must not break a pull, just miss). */
|
|
178
|
+
private readCache;
|
|
179
|
+
/**
|
|
180
|
+
* Pull several documents in one round-trip via `/batch/pull`. `collections` is
|
|
181
|
+
* the list of distinct collection names; `opts.params` supplies, per collection,
|
|
182
|
+
* an ARRAY of path-param sets — one per document to read — so the SAME collection
|
|
183
|
+
* can fan in many documents (e.g. many users' `profile`) in a single request.
|
|
184
|
+
* The server auto-fills the `{identity}` param from the authenticated caller for
|
|
185
|
+
* any set that omits it, so a self-doc collection needs no params. Returns a map
|
|
186
|
+
* of collection name → an ARRAY of pulled documents (or per-document `{ error }`),
|
|
187
|
+
* in request order. Honors the configured namespace.
|
|
188
|
+
*
|
|
189
|
+
* For the common "many docs of one collection" case prefer {@link batchPullMany}.
|
|
190
|
+
*
|
|
191
|
+
* Note: not append/checkpoint-aware — for incremental append-only reads use
|
|
192
|
+
* `pull(path, { since })` (or `AppendLogCursor`) per collection.
|
|
193
|
+
*/
|
|
194
|
+
batchPull(collections: string[], opts?: BatchPullOptions): Promise<BatchPullResult>;
|
|
195
|
+
/**
|
|
196
|
+
* Convenience over {@link batchPull} for reading MANY documents of ONE
|
|
197
|
+
* collection in a single round-trip: pass the per-document param-sets and get
|
|
198
|
+
* back the {@link BatchPullEntry} array aligned to `paramsList` by index (each
|
|
199
|
+
* entry is `{ data, hash, timestamp }` or `{ error }`). An empty `paramsList`
|
|
200
|
+
* issues no request and returns `[]`.
|
|
201
|
+
*/
|
|
202
|
+
batchPullMany(collection: string, paramsList: Record<string, string>[]): Promise<BatchPullEntry[]>;
|
|
83
203
|
/**
|
|
84
204
|
* Push synced data to the server.
|
|
85
205
|
* @param path - The push endpoint path (e.g. "/push/users/abc/settings")
|
|
86
206
|
* @param data - The full document data to push
|
|
87
207
|
* @param baseHash - Hash of the document this push is based on (null for first push)
|
|
88
208
|
*
|
|
89
|
-
* v3 author
|
|
90
|
-
*
|
|
209
|
+
* v3 author proof (`authorPubkey` + `authorSignature`) is passed via `author`
|
|
210
|
+
* (produced by `SyncManager` when a `signer` is configured) and sent as
|
|
211
|
+
* top-level body siblings of `data`, where the server verifies it.
|
|
91
212
|
* @throws {ConflictError} if the server detects a hash mismatch (409)
|
|
92
213
|
*/
|
|
93
|
-
push(path: string, data: Record<string, unknown>, baseHash: string | null): Promise<PushSuccess>;
|
|
214
|
+
push(path: string, data: Record<string, unknown>, baseHash: string | null, author?: AppendAuthor): Promise<PushSuccess>;
|
|
94
215
|
/**
|
|
95
216
|
* Append an element to an appendOnly (`by_timestamp`) collection.
|
|
96
217
|
*
|
|
@@ -105,8 +226,10 @@ export declare class StarfishClient {
|
|
|
105
226
|
* @param opts.ts - optional client-supplied element timestamp (ms). Must be a
|
|
106
227
|
* non-negative integer strictly greater than the latest stored element's ts
|
|
107
228
|
* (else the server responds 409). Omit to let the server assign one.
|
|
108
|
-
* @throws {StarfishHttpError} on a non-2xx response
|
|
109
|
-
* non-monotonic timestamp
|
|
229
|
+
* @throws {StarfishHttpError} on a non-2xx response — e.g. 409
|
|
230
|
+
* `{ error: "non_monotonic_timestamp" }` for a non-monotonic timestamp, or
|
|
231
|
+
* `{ error: "append_limit_exceeded", limit }` if the collection's `maxItems`
|
|
232
|
+
* cap is reached (partition by a path parameter for higher volume).
|
|
110
233
|
*/
|
|
111
234
|
append(path: string, data: Record<string, unknown>, opts?: {
|
|
112
235
|
ts?: number;
|
package/dist/config.d.ts
CHANGED
|
@@ -9,6 +9,15 @@ export interface AppendOnlyClientInfo {
|
|
|
9
9
|
/** false = no storage write (replaces queueOnly). true/absent = append to array. */
|
|
10
10
|
persist?: boolean;
|
|
11
11
|
}
|
|
12
|
+
/** Append-only configuration exposed via GET /config. */
|
|
13
|
+
export interface AppendOnlyClientInfo {
|
|
14
|
+
/** Array field name in the stored document. Defaults to "items". */
|
|
15
|
+
field?: string;
|
|
16
|
+
/** false = no storage write (replaces queueOnly). true/absent = append to array. */
|
|
17
|
+
persist?: boolean;
|
|
18
|
+
/** When true, server validates client's baseHash against hash(lastItem). */
|
|
19
|
+
checkLastItem?: boolean;
|
|
20
|
+
}
|
|
12
21
|
/** Per-collection metadata returned by GET /config. */
|
|
13
22
|
export interface CollectionClientInfo {
|
|
14
23
|
name: string;
|
package/dist/index.d.ts
CHANGED
|
@@ -4,14 +4,16 @@ export { stableStringify, computeHash } from "@drakkar.software/starfish-protoco
|
|
|
4
4
|
export { buildRevocationList, revocationListCanonicalSigningInput } from "@drakkar.software/starfish-protocol";
|
|
5
5
|
export type { RevocationList, RevocationEntry, RevokedSubject, BuildRevocationListOpts, } from "@drakkar.software/starfish-protocol";
|
|
6
6
|
export type { PullResult, PushSuccess, PullKeyringProjection } from "@drakkar.software/starfish-protocol";
|
|
7
|
-
export { StarfishClient } from "./client.js";
|
|
8
|
-
export type { BlobPullResult, BlobPushResult, AppendPullOptions, PullOptions } from "./client.js";
|
|
7
|
+
export { StarfishClient, pullWasFromCache } from "./client.js";
|
|
8
|
+
export type { BlobPullResult, BlobPushResult, AppendPullOptions, PullOptions, BatchPullOptions, BatchPullResult, BatchPullEntry, } from "./client.js";
|
|
9
9
|
export { SyncManager, AbortError } from "./sync.js";
|
|
10
10
|
export type { SyncManagerOptions, SyncSigner } from "./sync.js";
|
|
11
|
+
export { AppendLogCursor, AppendAuthorError, checkpointOf } from "./append-log.js";
|
|
12
|
+
export type { AppendLogCursorOptions, AppendElement, AuthorVerifier, ElementErrorPolicy } from "./append-log.js";
|
|
11
13
|
export { ENCRYPTED_KEY } from "@drakkar.software/starfish-protocol";
|
|
12
14
|
export type { Encryptor } from "@drakkar.software/starfish-protocol";
|
|
13
15
|
export { ConflictError, StarfishHttpError, } from "./types.js";
|
|
14
|
-
export type { StarfishClientOptions, StarfishCapProvider, ConflictResolver, ClientPlugin, } from "./types.js";
|
|
16
|
+
export type { StarfishClientOptions, StarfishCapProvider, PullCache, ConflictResolver, ClientPlugin, } from "./types.js";
|
|
15
17
|
export { consoleSyncLogger, noopSyncLogger, createMetricsCollector } from "./logger.js";
|
|
16
18
|
export type { SyncLogger, SyncMetrics, MetricsCollector } from "./logger.js";
|
|
17
19
|
export { createMigrator } from "./migrate.js";
|
|
@@ -27,6 +29,8 @@ export type { Snapshot, SnapshotHistoryOptions } from "./history.js";
|
|
|
27
29
|
export { startPolling, startAdaptivePolling } from "./polling.js";
|
|
28
30
|
export type { PollableState, AdaptivePollingOptions, AdaptivePollingControls } from "./polling.js";
|
|
29
31
|
export { createDedupFetch } from "./dedup.js";
|
|
32
|
+
export { mutateDoc } from "./mutate.js";
|
|
33
|
+
export type { DocState, DocMutator, MutateDocOptions } from "./mutate.js";
|
|
30
34
|
export { fetchServerConfig } from "./config.js";
|
|
31
35
|
export type { EncryptionMode, CollectionClientInfo, ConfigResponse } from "./config.js";
|
|
32
36
|
export { createIndexedDBStorage } from "./storage/indexeddb.js";
|
|
@@ -40,8 +44,8 @@ export type { ServiceWorkerOptions } from "./service-worker.js";
|
|
|
40
44
|
export { createSuspenseResource } from "./bindings/suspense.js";
|
|
41
45
|
export { createDebouncedSync, createDebouncedPush } from "./debounced-sync.js";
|
|
42
46
|
export type { DebouncedSyncOptions, DebouncedSync, DebouncedPushOptions, DebouncedPush } from "./debounced-sync.js";
|
|
43
|
-
export { createMobileLifecycle } from "./mobile-lifecycle.js";
|
|
44
|
-
export type { AppStateModule, NetInfoModule, MobileLifecycleDeps, MobileLifecycleOptions } from "./mobile-lifecycle.js";
|
|
47
|
+
export { createMobileLifecycle, createAppendLogMobileLifecycle } from "./mobile-lifecycle.js";
|
|
48
|
+
export type { AppStateModule, NetInfoModule, MobileLifecycleDeps, MobileLifecycleOptions, AppendLogLifecycleOptions } from "./mobile-lifecycle.js";
|
|
45
49
|
export { createMultiStoreSync } from "./multi-store.js";
|
|
46
50
|
export type { StoreSlice, BackupDocument, MultiStoreMigrationFn, MultiStoreSyncOptions, MultiStoreSync, } from "./multi-store.js";
|
|
47
51
|
export type { AppendOnlyClientInfo } from "./config.js";
|