@byline/client 4.7.0 → 4.9.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/collection-handle.d.ts +1 -1
- package/dist/collection-handle.js +5 -3
- package/dist/response.js +2 -2
- package/dist/search.d.ts +1 -0
- package/dist/search.js +14 -1
- package/dist/search.test.node.d.ts +8 -0
- package/dist/search.test.node.js +22 -0
- package/dist/types.d.ts +17 -10
- package/package.json +5 -5
|
@@ -186,7 +186,7 @@ export declare class CollectionHandle<TFields extends Record<string, any> = Reco
|
|
|
186
186
|
*/
|
|
187
187
|
history<F = TFields>(documentId: string, options?: HistoryOptions): Promise<FindResult<F>>;
|
|
188
188
|
/**
|
|
189
|
-
* Fetch the document-grain audit log for a single document (docs/
|
|
189
|
+
* Fetch the document-grain audit log for a single document (docs/07-auth-and-security/02-auditability.md —
|
|
190
190
|
* Workstream 3): the non-versioned system-field writes (path,
|
|
191
191
|
* available-locales), in-place status transitions, and the deletion event
|
|
192
192
|
* the immutable version stream deliberately does not record an actor for.
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
import { applyAfterRead, assertActorCanPerform, buildSearchDocument, changeDocumentStatus, createDocument, createReadContext, createRichTextDocumentReader, deleteDocument, ERR_VALIDATION, parseSort, parseWhere, placeTreeNode as placeTreeNodeLifecycle, populateDocuments, populateRichTextFields, removeFromTree as removeFromTreeLifecycle, resolveIdentityField, restoreDocumentVersion, unpublishDocument, updateDocument, } from '@byline/core';
|
|
9
9
|
import { getReadSecurityDomain, resolveReadRequestContext, resolveReadSecurityFilters, } from './read-context.js';
|
|
10
10
|
import { shapeDocument, shapePopulatedInPlace } from './response.js';
|
|
11
|
-
import { finalizeSearchHits } from './search.js';
|
|
11
|
+
import { assertSearchQueryLength, finalizeSearchHits } from './search.js';
|
|
12
12
|
/**
|
|
13
13
|
* A handle scoped to a single collection. Provides read (and eventually write)
|
|
14
14
|
* operations against that collection's documents.
|
|
@@ -130,6 +130,7 @@ export class CollectionHandle {
|
|
|
130
130
|
* Throws `ERR_VALIDATION` when no provider is registered.
|
|
131
131
|
*/
|
|
132
132
|
async search(options) {
|
|
133
|
+
assertSearchQueryLength(options.query);
|
|
133
134
|
const readMode = resolveReadMode(options.status);
|
|
134
135
|
const readCtx = createReadContext();
|
|
135
136
|
const requestContext = await this.resolveAndAssertRead(readMode, readCtx);
|
|
@@ -144,6 +145,7 @@ export class CollectionHandle {
|
|
|
144
145
|
}
|
|
145
146
|
const results = await provider.search({
|
|
146
147
|
query: options.query,
|
|
148
|
+
matching: options.matching,
|
|
147
149
|
collectionPath: this.definition.path,
|
|
148
150
|
locale: options.locale ?? this.client.defaultLocale,
|
|
149
151
|
status: readMode,
|
|
@@ -240,7 +242,7 @@ export class CollectionHandle {
|
|
|
240
242
|
if (provider == null || this.definition.search == null)
|
|
241
243
|
return result;
|
|
242
244
|
// Clear the slice so deleted documents don't leave orphan rows.
|
|
243
|
-
await provider.reindex
|
|
245
|
+
await provider.reindex({ collectionPath: this.definition.path });
|
|
244
246
|
const pageSize = 100;
|
|
245
247
|
let page = 1;
|
|
246
248
|
for (;;) {
|
|
@@ -524,7 +526,7 @@ export class CollectionHandle {
|
|
|
524
526
|
};
|
|
525
527
|
}
|
|
526
528
|
/**
|
|
527
|
-
* Fetch the document-grain audit log for a single document (docs/
|
|
529
|
+
* Fetch the document-grain audit log for a single document (docs/07-auth-and-security/02-auditability.md —
|
|
528
530
|
* Workstream 3): the non-versioned system-field writes (path,
|
|
529
531
|
* available-locales), in-place status transitions, and the deletion event
|
|
530
532
|
* the immutable version stream deliberately does not record an actor for.
|
package/dist/response.js
CHANGED
|
@@ -43,7 +43,7 @@ export function shapeDocument(raw) {
|
|
|
43
43
|
// `created_by` / `event_type`).
|
|
44
44
|
// `created_by` is NULL on rows written before audit wiring or by
|
|
45
45
|
// internal tooling without a request context — omitted here, rendered as
|
|
46
|
-
// "unknown" by consumers. See docs/
|
|
46
|
+
// "unknown" by consumers. See docs/07-auth-and-security/02-auditability.md — Workstream 1.
|
|
47
47
|
if (typeof raw.created_by === 'string') {
|
|
48
48
|
shaped.createdBy = raw.created_by;
|
|
49
49
|
}
|
|
@@ -55,7 +55,7 @@ export function shapeDocument(raw) {
|
|
|
55
55
|
// Storage raw keys already match the surface names (passthrough) —
|
|
56
56
|
// `availableLocales` is the editorial advertised set (document-grain,
|
|
57
57
|
// stored), `_availableVersionLocales` is the structural ledger fact
|
|
58
|
-
// (version-grain, computed). See docs/
|
|
58
|
+
// (version-grain, computed). See docs/08-internationalization/index.md.
|
|
59
59
|
if (Array.isArray(raw.availableLocales)) {
|
|
60
60
|
shaped.availableLocales = raw.availableLocales;
|
|
61
61
|
}
|
package/dist/search.d.ts
CHANGED
|
@@ -58,3 +58,4 @@ export declare function finalizeSearchHits(params: FinalizeSearchHitsParams): Pr
|
|
|
58
58
|
* ability error only when the actor can read none of the members.
|
|
59
59
|
*/
|
|
60
60
|
export declare function zoneSearch(client: BylineClient<any>, options: ZoneSearchOptions): Promise<ClientSearchResults>;
|
|
61
|
+
export declare function assertSearchQueryLength(query: string): void;
|
package/dist/search.js
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
*
|
|
6
6
|
* Copyright (c) Infonomic Company Limited
|
|
7
7
|
*/
|
|
8
|
-
import { assertActorCanPerform, createReadContext, ERR_VALIDATION, getCollectionAdminConfig, resolveItemViewColumns, resolveSearchZones, } from '@byline/core';
|
|
8
|
+
import { assertActorCanPerform, createReadContext, ERR_VALIDATION, getCollectionAdminConfig, MAX_SEARCH_QUERY_LENGTH, resolveItemViewColumns, resolveSearchZones, } from '@byline/core';
|
|
9
9
|
import { resolveReadRequestContext, resolveReadSecurityFilters } from './read-context.js';
|
|
10
10
|
/**
|
|
11
11
|
* Authorise (and optionally hydrate) provider hits, per collection:
|
|
@@ -127,6 +127,7 @@ export async function finalizeSearchHits(params) {
|
|
|
127
127
|
* ability error only when the actor can read none of the members.
|
|
128
128
|
*/
|
|
129
129
|
export async function zoneSearch(client, options) {
|
|
130
|
+
assertSearchQueryLength(options.query);
|
|
130
131
|
const provider = client.searchProvider;
|
|
131
132
|
if (provider == null) {
|
|
132
133
|
throw ERR_VALIDATION({
|
|
@@ -175,6 +176,7 @@ export async function zoneSearch(client, options) {
|
|
|
175
176
|
}
|
|
176
177
|
const results = await provider.search({
|
|
177
178
|
query: options.query,
|
|
179
|
+
matching: options.matching,
|
|
178
180
|
zone: options.zone,
|
|
179
181
|
locale: options.locale ?? client.defaultLocale,
|
|
180
182
|
status: readMode,
|
|
@@ -199,3 +201,14 @@ export async function zoneSearch(client, options) {
|
|
|
199
201
|
? { hits, total: hits.length }
|
|
200
202
|
: { hits, total: results.total, facets: results.facets };
|
|
201
203
|
}
|
|
204
|
+
export function assertSearchQueryLength(query) {
|
|
205
|
+
if (query.length <= MAX_SEARCH_QUERY_LENGTH)
|
|
206
|
+
return;
|
|
207
|
+
throw ERR_VALIDATION({
|
|
208
|
+
message: `Search query exceeds the maximum length of ${MAX_SEARCH_QUERY_LENGTH} UTF-16 code units.`,
|
|
209
|
+
details: {
|
|
210
|
+
maximumLength: MAX_SEARCH_QUERY_LENGTH,
|
|
211
|
+
actualLength: query.length,
|
|
212
|
+
},
|
|
213
|
+
});
|
|
214
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* This Source Code is subject to the terms of the Mozilla Public
|
|
3
|
+
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
4
|
+
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
|
5
|
+
*
|
|
6
|
+
* Copyright (c) Infonomic Company Limited
|
|
7
|
+
*/
|
|
8
|
+
export {};
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* This Source Code is subject to the terms of the Mozilla Public
|
|
3
|
+
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
4
|
+
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
|
5
|
+
*
|
|
6
|
+
* Copyright (c) Infonomic Company Limited
|
|
7
|
+
*/
|
|
8
|
+
import { MAX_SEARCH_QUERY_LENGTH } from '@byline/core';
|
|
9
|
+
import { describe, expect, it } from 'vitest';
|
|
10
|
+
import { assertSearchQueryLength } from './search.js';
|
|
11
|
+
describe('search query validation', () => {
|
|
12
|
+
it('accepts the portable query limit and rejects longer input', () => {
|
|
13
|
+
expect(() => assertSearchQueryLength('a'.repeat(MAX_SEARCH_QUERY_LENGTH))).not.toThrow();
|
|
14
|
+
expect(() => assertSearchQueryLength('a'.repeat(MAX_SEARCH_QUERY_LENGTH + 1))).toThrowError(expect.objectContaining({
|
|
15
|
+
code: 'ERR_VALIDATION',
|
|
16
|
+
details: {
|
|
17
|
+
maximumLength: MAX_SEARCH_QUERY_LENGTH,
|
|
18
|
+
actualLength: MAX_SEARCH_QUERY_LENGTH + 1,
|
|
19
|
+
},
|
|
20
|
+
}));
|
|
21
|
+
});
|
|
22
|
+
});
|
package/dist/types.d.ts
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
* Copyright (c) Infonomic Company Limited
|
|
7
7
|
*/
|
|
8
8
|
import type { RequestContext } from '@byline/auth';
|
|
9
|
-
import type { BylineLogger, CollectionDefinition, IDbAdapter, IStorageProvider, MissingLocalePolicy, PopulateSpec, PredicateValue, QueryPredicate, ReadContext, ReadMode, RichTextPopulateFn, RichTextToTextFn, SearchFacetBucket, SearchHit, SearchProvider, ServerConfig, SlugifierFn, SortSpec } from '@byline/core';
|
|
9
|
+
import type { BylineLogger, CollectionDefinition, IDbAdapter, IStorageProvider, MissingLocalePolicy, PopulateSpec, PredicateValue, QueryPredicate, ReadContext, ReadMode, RichTextPopulateFn, RichTextToTextFn, SearchFacetBucket, SearchHit, SearchMatching, SearchProvider, ServerConfig, SlugifierFn, SortSpec } from '@byline/core';
|
|
10
10
|
export type { FilterOperators, PredicateValue, QueryPredicate, SearchFacetBucket, SearchHit, SearchProvider, SearchResults, SortDirection, SortSpec, } from '@byline/core';
|
|
11
11
|
export interface BylineClientConfig {
|
|
12
12
|
/**
|
|
@@ -183,6 +183,8 @@ interface StatusControls {
|
|
|
183
183
|
export interface CollectionSearchOptions extends BeforeReadControls {
|
|
184
184
|
/** Free-text query string. */
|
|
185
185
|
query: string;
|
|
186
|
+
/** Provider-neutral term and phrase matching semantics. */
|
|
187
|
+
matching?: SearchMatching;
|
|
186
188
|
/** Restrict to a single content locale (defaults to the client default). */
|
|
187
189
|
locale?: string;
|
|
188
190
|
/** Read mode — `'published'` (default) or `'any'`. */
|
|
@@ -215,6 +217,8 @@ export interface CollectionSearchOptions extends BeforeReadControls {
|
|
|
215
217
|
export interface ZoneSearchOptions extends BeforeReadControls {
|
|
216
218
|
/** Free-text query string. */
|
|
217
219
|
query: string;
|
|
220
|
+
/** Provider-neutral term and phrase matching semantics. */
|
|
221
|
+
matching?: SearchMatching;
|
|
218
222
|
/** The zone (named cross-collection scope) to search. */
|
|
219
223
|
zone: string;
|
|
220
224
|
/** Restrict to a single content locale (defaults to the client default). */
|
|
@@ -253,7 +257,10 @@ export interface ReindexResult {
|
|
|
253
257
|
collectionPath: string;
|
|
254
258
|
/** Number of published documents walked. */
|
|
255
259
|
documents: number;
|
|
256
|
-
/**
|
|
260
|
+
/**
|
|
261
|
+
* Currently mirrors `documents` — the provider upserts are the source of
|
|
262
|
+
* truth for how many `(document, locale)` rows were written.
|
|
263
|
+
*/
|
|
257
264
|
indexed: number;
|
|
258
265
|
}
|
|
259
266
|
/**
|
|
@@ -275,7 +282,7 @@ export interface ReindexResult {
|
|
|
275
282
|
* in every locale.
|
|
276
283
|
*
|
|
277
284
|
* Availability is the version-grain ledger described in
|
|
278
|
-
* `docs/
|
|
285
|
+
* `docs/08-internationalization/index.md`. Relationship population always behaves
|
|
279
286
|
* as `'fallback'` so a populated tree never has holes.
|
|
280
287
|
*/
|
|
281
288
|
interface MissingLocaleControls {
|
|
@@ -349,7 +356,7 @@ export interface HistoryOptions extends BeforeReadControls {
|
|
|
349
356
|
}
|
|
350
357
|
/**
|
|
351
358
|
* Options for `CollectionHandle.auditLog(documentId, options)`. The
|
|
352
|
-
* document-grain audit log (docs/
|
|
359
|
+
* document-grain audit log (docs/07-auth-and-security/02-auditability.md — Workstream 3) records the
|
|
353
360
|
* non-versioned changes the immutable version stream does not capture an
|
|
354
361
|
* actor for: system-field writes (path, available-locales), in-place status
|
|
355
362
|
* transitions, and the deletion event. Entries are newest-first and paged;
|
|
@@ -390,7 +397,7 @@ export interface CreateOptions {
|
|
|
390
397
|
* The editorial advertised-locale set, stored document-grain in
|
|
391
398
|
* `byline_document_available_locales`. When omitted, a new document starts
|
|
392
399
|
* with an empty set; an explicit array (empty included) is stored verbatim.
|
|
393
|
-
* Surfaced on reads as `availableLocales`. See `docs/
|
|
400
|
+
* Surfaced on reads as `availableLocales`. See `docs/08-internationalization/index.md`.
|
|
394
401
|
*/
|
|
395
402
|
availableLocales?: string[];
|
|
396
403
|
}
|
|
@@ -406,7 +413,7 @@ export interface UpdateOptions {
|
|
|
406
413
|
* The editorial advertised-locale set. When omitted, the existing set
|
|
407
414
|
* carries forward unchanged (sticky — document-grain, like `path`); an
|
|
408
415
|
* explicit array (empty included) replaces it wholesale. Surfaced on reads
|
|
409
|
-
* as `availableLocales`. See `docs/
|
|
416
|
+
* as `availableLocales`. See `docs/08-internationalization/index.md`.
|
|
410
417
|
*/
|
|
411
418
|
availableLocales?: string[];
|
|
412
419
|
}
|
|
@@ -552,7 +559,7 @@ export interface ClientDocument<F = Record<string, any>> {
|
|
|
552
559
|
* yardstick). Stable, document-grain. Surfaced so consumers / the admin can
|
|
553
560
|
* indicate a document whose primary language differs from the system default.
|
|
554
561
|
* Present on `find` / `findById` / `findByPath`. See
|
|
555
|
-
* `docs/
|
|
562
|
+
* `docs/08-internationalization/index.md`.
|
|
556
563
|
*/
|
|
557
564
|
sourceLocale?: string;
|
|
558
565
|
/** When this version was created. */
|
|
@@ -565,7 +572,7 @@ export interface ClientDocument<F = Record<string, any>> {
|
|
|
565
572
|
* the creator of the current version is also "who last updated the
|
|
566
573
|
* document". Absent on rows written before audit wiring or by
|
|
567
574
|
* internal tooling without a request context. Raw id only — display-name
|
|
568
|
-
* resolution is an admin-realm concern (see docs/
|
|
575
|
+
* resolution is an admin-realm concern (see docs/07-auth-and-security/02-auditability.md — Workstream 1).
|
|
569
576
|
*/
|
|
570
577
|
createdBy?: string;
|
|
571
578
|
/**
|
|
@@ -593,7 +600,7 @@ export interface ClientDocument<F = Record<string, any>> {
|
|
|
593
600
|
* `_availableVersionLocales` fact below; the public advertised set is their
|
|
594
601
|
* intersection (`availableLocales ∩ _availableVersionLocales`), which the
|
|
595
602
|
* host composes for hreflang / sitemap / "Also available in…" menus.
|
|
596
|
-
* Present on `find` / `findById` / `findByPath`. See `docs/
|
|
603
|
+
* Present on `find` / `findById` / `findByPath`. See `docs/08-internationalization/index.md`.
|
|
597
604
|
*/
|
|
598
605
|
availableLocales?: string[];
|
|
599
606
|
/**
|
|
@@ -603,7 +610,7 @@ export interface ClientDocument<F = Record<string, any>> {
|
|
|
603
610
|
* Present on `find` / `findById` / `findByPath` (absent on version/history
|
|
604
611
|
* reads); the published-available set on a normal (published) read. The
|
|
605
612
|
* structural fact reconciled against the editorial `availableLocales` above.
|
|
606
|
-
* See `docs/
|
|
613
|
+
* See `docs/08-internationalization/index.md`.
|
|
607
614
|
*/
|
|
608
615
|
_availableVersionLocales?: string[];
|
|
609
616
|
/**
|
package/package.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"name": "@byline/client",
|
|
3
3
|
"private": false,
|
|
4
4
|
"license": "MPL-2.0",
|
|
5
|
-
"version": "4.
|
|
5
|
+
"version": "4.9.0",
|
|
6
6
|
"engines": {
|
|
7
7
|
"node": ">=20.9.0"
|
|
8
8
|
},
|
|
@@ -44,8 +44,8 @@
|
|
|
44
44
|
],
|
|
45
45
|
"dependencies": {
|
|
46
46
|
"uuid": "^14.0.1",
|
|
47
|
-
"@byline/auth": "4.
|
|
48
|
-
"@byline/core": "4.
|
|
47
|
+
"@byline/auth": "4.9.0",
|
|
48
|
+
"@byline/core": "4.9.0"
|
|
49
49
|
},
|
|
50
50
|
"devDependencies": {
|
|
51
51
|
"@biomejs/biome": "2.5.4",
|
|
@@ -58,8 +58,8 @@
|
|
|
58
58
|
"tsx": "^4.23.1",
|
|
59
59
|
"typescript": "^7.0.2",
|
|
60
60
|
"vitest": "^4.1.10",
|
|
61
|
-
"@byline/db-postgres": "4.
|
|
62
|
-
"@byline/search-postgres": "4.
|
|
61
|
+
"@byline/db-postgres": "4.9.0",
|
|
62
|
+
"@byline/search-postgres": "4.9.0"
|
|
63
63
|
},
|
|
64
64
|
"publishConfig": {
|
|
65
65
|
"access": "public",
|