@byline/client 3.21.0 → 4.1.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.
Files changed (34) hide show
  1. package/dist/client.d.ts +6 -3
  2. package/dist/client.js +3 -1
  3. package/dist/collection-handle.d.ts +33 -38
  4. package/dist/collection-handle.js +175 -137
  5. package/dist/index.d.ts +2 -1
  6. package/dist/read-context.d.ts +11 -0
  7. package/dist/read-context.js +36 -0
  8. package/dist/register.d.ts +44 -0
  9. package/dist/register.js +8 -0
  10. package/dist/search.js +23 -6
  11. package/dist/server/admin-context.d.ts +50 -0
  12. package/dist/server/admin-context.js +125 -0
  13. package/dist/server/admin-context.test.node.d.ts +8 -0
  14. package/dist/server/admin-context.test.node.js +256 -0
  15. package/dist/server/browser.d.ts +8 -0
  16. package/dist/server/browser.js +18 -0
  17. package/dist/server/clients.d.ts +92 -0
  18. package/dist/server/clients.js +170 -0
  19. package/dist/server/index.d.ts +26 -0
  20. package/dist/server/index.js +26 -0
  21. package/dist/server/preview-cookies.d.ts +19 -0
  22. package/dist/server/preview-cookies.js +69 -0
  23. package/dist/server/request-context-contract.test.node.d.ts +8 -0
  24. package/dist/server/request-context-contract.test.node.js +114 -0
  25. package/dist/server/request-context-factories.test.node.d.ts +8 -0
  26. package/dist/server/request-context-factories.test.node.js +116 -0
  27. package/dist/server/request-scope.d.ts +17 -0
  28. package/dist/server/request-scope.js +50 -0
  29. package/dist/server/request-scope.test.node.d.ts +8 -0
  30. package/dist/server/request-scope.test.node.js +89 -0
  31. package/dist/server/session-cookies.d.ts +34 -0
  32. package/dist/server/session-cookies.js +95 -0
  33. package/dist/types.d.ts +60 -23
  34. package/package.json +12 -5
package/dist/client.d.ts CHANGED
@@ -8,13 +8,14 @@
8
8
  import { type RequestContext } from '@byline/auth';
9
9
  import type { BylineLogger, CollectionDefinition, CollectionRegistry, IDbAdapter, IStorageProvider, RichTextPopulateFn, RichTextToTextFn, SearchProvider, SlugifierFn } from '@byline/core';
10
10
  import { CollectionHandle } from './collection-handle.js';
11
+ import type { RegisteredCollections } from './register.js';
11
12
  import type { BylineClientConfig, ClientSearchResults, ZoneSearchOptions } from './types.js';
12
13
  /**
13
14
  * The main Byline client instance. Holds the database adapter, collection
14
15
  * definitions, and optional storage provider. Use `collection(path)` to get
15
16
  * a scoped handle for querying and mutating documents.
16
17
  */
17
- export declare class BylineClient<TRegistry extends CollectionRegistry = CollectionRegistry> {
18
+ export declare class BylineClient<TRegistry extends CollectionRegistry = RegisteredCollections> {
18
19
  readonly db: IDbAdapter;
19
20
  readonly collections: readonly CollectionDefinition[];
20
21
  readonly storage: IStorageProvider | undefined;
@@ -71,6 +72,8 @@ export declare class BylineClient<TRegistry extends CollectionRegistry = Collect
71
72
  resolveCollectionId(path: string): Promise<string>;
72
73
  }
73
74
  /**
74
- * Create a new Byline client instance.
75
+ * Create a new Byline client instance. The registry generic defaults to
76
+ * `RegisteredCollections`, so in an app whose generated types augment
77
+ * `Register` the returned client is fully typed with no explicit generic.
75
78
  */
76
- export declare function createBylineClient<TRegistry extends CollectionRegistry = CollectionRegistry>(config: BylineClientConfig): BylineClient<TRegistry>;
79
+ export declare function createBylineClient<TRegistry extends CollectionRegistry = RegisteredCollections>(config: BylineClientConfig): BylineClient<TRegistry>;
package/dist/client.js CHANGED
@@ -174,7 +174,9 @@ export class BylineClient {
174
174
  }
175
175
  }
176
176
  /**
177
- * Create a new Byline client instance.
177
+ * Create a new Byline client instance. The registry generic defaults to
178
+ * `RegisteredCollections`, so in an app whose generated types augment
179
+ * `Register` the returned client is fully typed with no explicit generic.
178
180
  */
179
181
  export function createBylineClient(config) {
180
182
  return new BylineClient(config);
@@ -7,7 +7,7 @@
7
7
  */
8
8
  import type { AuditLogPage, ChangeStatusResult, CollectionDefinition, CreateDocumentResult, DeleteDocumentResult, RestoreVersionResult, UnpublishResult, UpdateDocumentResult } from '@byline/core';
9
9
  import type { BylineClient } from './client.js';
10
- import type { AuditLogOptions, ClientDocument, ClientSearchResults, CollectionSearchOptions, CreateOptions, FindByIdOptions, FindByPathOptions, FindByVersionOptions, FindOneOptions, FindOptions, FindResult, GetAncestorsOptions, GetSubtreeOptions, HistoryOptions, PlaceTreeNodeOptions, ReindexResult, TreeNode, UpdateOptions } from './types.js';
10
+ import type { AuditLogOptions, ClientDocument, ClientSearchResults, CollectionSearchOptions, CreateOptions, FindByIdOptions, FindByPathOptions, FindByVersionOptions, FindOneOptions, FindOptions, FindResult, GetAncestorsOptions, GetSubtreeOptions, GetTreeParentOptions, HistoryOptions, PlaceTreeNodeOptions, ReindexResult, RemoveFromTreeOptions, TreeNode, TreeParentResult, UpdateOptions } from './types.js';
11
11
  /**
12
12
  * A handle scoped to a single collection. Provides read (and eventually write)
13
13
  * operations against that collection's documents.
@@ -55,8 +55,8 @@ export declare class CollectionHandle<TFields extends Record<string, any> = Reco
55
55
  * comes free) and attaches them as `hit.document`. Two consequences to be
56
56
  * aware of:
57
57
  *
58
- * - `total` (and facet counts) remain the provider's pre-authorization
59
- * numbers — approximate under row scoping, exact without it.
58
+ * - under row scoping, `total` is the authorized hit count for this page
59
+ * and facets are omitted rather than leaking provider-wide aggregates.
60
60
  * - a page of hits can come back shorter than `limit` when candidates
61
61
  * are dropped; paginate on `offset`, not on received length.
62
62
  *
@@ -150,8 +150,10 @@ export declare class CollectionHandle<TFields extends Record<string, any> = Reco
150
150
  */
151
151
  delete(documentId: string): Promise<DeleteDocumentResult>;
152
152
  /**
153
- * Count documents visible to the current actor, optionally filtered by
154
- * status.
153
+ * Sum the current-version workflow status buckets visible to the actor,
154
+ * optionally selecting one exact status. This is an editorial status-count
155
+ * API, not an ordinary published-view document count; it therefore
156
+ * authorizes as `readMode: 'any'` and rejects anonymous callers.
155
157
  *
156
158
  * Applies the collection's `beforeRead` predicate so the count reflects
157
159
  * only the rows the actor can see (multi-tenant scoping, owner-only
@@ -163,7 +165,8 @@ export declare class CollectionHandle<TFields extends Record<string, any> = Reco
163
165
  }): Promise<number>;
164
166
  /**
165
167
  * Per-status document counts for this collection. Used by admin status
166
- * bars / dashboards. Applies `beforeRead` so per-status counts reflect
168
+ * bars / dashboards. This always reads current versions across statuses,
169
+ * authorizes as `readMode: 'any'`, and applies `beforeRead` so counts reflect
167
170
  * only the actor's visible rows.
168
171
  */
169
172
  countByStatus(options?: {
@@ -174,9 +177,8 @@ export declare class CollectionHandle<TFields extends Record<string, any> = Reco
174
177
  }>>;
175
178
  /**
176
179
  * Fetch the version history for a single document. Applies `beforeRead`
177
- * as an access gate via `findById` — if the actor can't see the
178
- * document at all, history returns an empty result rather than
179
- * leaking version metadata.
180
+ * independently to every immutable version before pagination and counting,
181
+ * so ownership changes cannot expose hidden version content or metadata.
180
182
  *
181
183
  * Each version in the response is a shaped `ClientDocument`. Pagination
182
184
  * mirrors the storage adapter's `{ documents, meta }` shape, then is
@@ -189,9 +191,9 @@ export declare class CollectionHandle<TFields extends Record<string, any> = Reco
189
191
  * available-locales), in-place status transitions, and the deletion event
190
192
  * the immutable version stream deliberately does not record an actor for.
191
193
  *
192
- * Applies `beforeRead` as an access gate via `findById` — exactly as
193
- * `history()` does — so an actor who cannot see the document at all gets an
194
- * empty log rather than leaking change metadata. Entries are newest-first.
194
+ * Applies `beforeRead` as a current-document access gate via `findById`, so
195
+ * an actor who cannot see the document at all gets an empty log rather than
196
+ * leaking change metadata. Entries are newest-first.
195
197
  * Actor *ids* are returned raw; resolving them to display labels is an
196
198
  * admin-realm concern handled above the SDK.
197
199
  *
@@ -203,12 +205,10 @@ export declare class CollectionHandle<TFields extends Record<string, any> = Reco
203
205
  * Fetch a specific version of a document by its `documentVersionId`.
204
206
  * Used by admin diff views.
205
207
  *
206
- * Pass-through to `getDocumentByVersion`; access enforcement falls back
207
- * to the collection-level `read` ability (asserted at the top of every
208
- * read entry point). Row-level `beforeRead` does **not** apply here —
209
- * version-by-id is a history-viewing primitive and the caller is
210
- * expected to have already passed an access check on the parent
211
- * document. Use `history()` instead if you want the access gate.
208
+ * The adapter query is constrained to this handle's collection and applies
209
+ * the collection's strict `beforeRead` predicate directly to the historical
210
+ * version. Unknown, cross-collection, and row-scoped version ids all return
211
+ * `null` without revealing ownership.
212
212
  */
213
213
  findByVersion<F = TFields>(versionId: string, options?: FindByVersionOptions<F>): Promise<ClientDocument<F> | null>;
214
214
  /**
@@ -227,14 +227,15 @@ export declare class CollectionHandle<TFields extends Record<string, any> = Reco
227
227
  * in the collection, but not in any table of contents). Distinct from
228
228
  * deleting the document. No-op when already unplaced.
229
229
  */
230
- removeFromTree(documentId: string): Promise<void>;
230
+ removeFromTree(documentId: string, options?: RemoveFromTreeOptions): Promise<void>;
231
231
  /**
232
232
  * Read a node's subtree as a nested {@link TreeNode} forest, hydrated to
233
233
  * `ClientDocument`s. `rootDocumentId: null` (default) returns the whole tree
234
234
  * from the collection roots; a value returns the subtree rooted at (and
235
235
  * including) that node. Children are ordered per-parent. `status` defaults to
236
236
  * `'published'` (the client default) — an unpublished node hides its whole
237
- * subtree in that mode.
237
+ * subtree in that mode. `beforeRead` visibility follows the same edge rule:
238
+ * a hidden node and its descendants are omitted, never promoted.
238
239
  */
239
240
  getSubtree<F = TFields>(options?: GetSubtreeOptions<F>): Promise<TreeNode<F>[]>;
240
241
  /**
@@ -243,7 +244,8 @@ export declare class CollectionHandle<TFields extends Record<string, any> = Reco
243
244
  * to `ClientDocument`s. In `'published'` mode (the client default) the walk
244
245
  * applies status-at-edge: it stops at the first unpublished ancestor (a
245
246
  * truncated chain), so a broken spine surfaces as a short chain the caller can
246
- * detect rather than a silently-compacted one.
247
+ * detect rather than a silently-compacted one. A `beforeRead`-hidden ancestor
248
+ * breaks the edge identically.
247
249
  */
248
250
  getAncestors<F = TFields>(documentId: string, options?: GetAncestorsOptions<F>): Promise<ClientDocument<F>[]>;
249
251
  /**
@@ -251,12 +253,11 @@ export declare class CollectionHandle<TFields extends Record<string, any> = Reco
251
253
  * `getAncestors` cannot express (it returns `[]` for both a root and an
252
254
  * unplaced node). Returns `{ placed, parentDocumentId }`: `placed: false` is
253
255
  * *unplaced* (no edge row), `placed: true` with a null parent is a *root*, and
254
- * a non-null parent is a *child*. Structure-only — no document hydration.
256
+ * a non-null parent is a *child*. A hidden queried node reports unplaced; a
257
+ * visible child of a hidden parent remains placed but the parent id is
258
+ * redacted to null. Structure-only — no document hydration.
255
259
  */
256
- getTreeParent(documentId: string): Promise<{
257
- placed: boolean;
258
- parentDocumentId: string | null;
259
- }>;
260
+ getTreeParent(documentId: string, options?: GetTreeParentOptions): Promise<TreeParentResult>;
260
261
  /**
261
262
  * Build a fresh `DocumentLifecycleContext` for a write call. Pulls the
262
263
  * resolved collection id, inherits the client-wide logger and storage
@@ -283,8 +284,8 @@ export declare class CollectionHandle<TFields extends Record<string, any> = Reco
283
284
  * Batch-hydrate a set of tree node document ids into shaped
284
285
  * `ClientDocument`s, keyed by document id. Reuses the populate batch read
285
286
  * (`getDocumentsByDocumentIds`) under the requested `readMode` — the tree's
286
- * own status-at-edge filtering already happened structurally, so a node
287
- * absent here in `'published'` mode simply has no published version. Fires
287
+ * own status/beforeRead edge filtering already happened structurally, and the
288
+ * same filters are applied again to close the structure/hydration race. Fires
288
289
  * `afterRead` per node (deduped within one shared `ReadContext`) to stay
289
290
  * consistent with the other client read paths.
290
291
  */
@@ -309,17 +310,11 @@ export declare class CollectionHandle<TFields extends Record<string, any> = Reco
309
310
  * is registered.
310
311
  */
311
312
  private richTextPopulateSources;
313
+ /** Complete the common post-reconstruction pipeline in security order. */
314
+ private finishReadDocuments;
312
315
  /**
313
- * Resolve and cache the `beforeRead` hook predicate for this collection
314
- * inside the current `ReadContext`. Honours `_bypassBeforeRead` (admin
315
- * tooling, seeds, migrations).
316
- */
317
- private resolveBeforeReadPredicate;
318
- /**
319
- * Like `resolveBeforeReadPredicate`, but parses the predicate to the
320
- * adapter-facing `DocumentFilter[]` shape used by `getDocumentById` /
321
- * `getDocumentByPath`. Returns `undefined` (not `[]`) when there is no
322
- * scoping, so the adapter can skip the loop entirely.
316
+ * Resolve the cached strict security filters used by every read shape.
317
+ * Returns `undefined` when no scoping applies.
323
318
  */
324
319
  private resolveBeforeReadFilters;
325
320
  /**