@wirecat/cli-base 0.48.0 → 0.49.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/CHANGELOG.md CHANGED
@@ -8,6 +8,16 @@ Versions before 0.8.0 are in the [git tags](https://github.com/WireCatLabs/cli-b
8
8
  Every entry says what changed as a caller sees it, why, and what to watch for — the rules are
9
9
  [`docs/dev/CONVENTIONS.md`](docs/dev/CONVENTIONS.md#the-changelog).
10
10
 
11
+ ## 0.49.0 — 21.10.2026
12
+
13
+ ### Added
14
+
15
+ - **`parseLucene` can read a phrase's distance.** With `{ proximity: true }`, `"a b"~3` parses as a phrase predicate
16
+ with `slop: 3`, and `queryNodeSchema` accepts `slop`. Meeting search needs it for words spread over neighbouring
17
+ transcript lines, and a caller that turns it on says what the number counts. Without the option `~` is still
18
+ `query.unsupported-operator`, so messages and lists see no change. A distance that is not a whole number is
19
+ `query.invalid-syntax`; `word~2` (fuzzy) stays unsupported either way.
20
+
11
21
  ## 0.48.0 — 21.10.2026
12
22
 
13
23
  ### Changed — may break callers
@@ -2,7 +2,9 @@ import { type QueryAst, type QueryContext } from "./types.js";
2
2
  export interface ParseOptions extends QueryContext {
3
3
  /** The field a bare word searches; `none` for a list, where a bare word is `query.bare-word`. */
4
4
  defaultField?: string;
5
+ /** Read `"a b"~N` as a phrase with `slop: N`; a caller that turns it on says what N counts. */
6
+ proximity?: boolean;
5
7
  }
6
- export declare const parseLucene: (text: string, { defaultField, ...context }?: ParseOptions) => QueryAst;
8
+ export declare const parseLucene: (text: string, { defaultField, proximity, ...context }?: ParseOptions) => QueryAst;
7
9
  /** Bare terms eligible for forgiving retrieval; explicit syntax keeps strict semantics. */
8
10
  export declare const implicitTextTerms: (text: string) => Set<number>;
@@ -141,7 +141,7 @@ const regexHint = (text, at) => {
141
141
  const word = `${/[^\s()"]*$/u.exec(text.slice(0, at))?.[0] ?? ""}${/^[^\s()"]*/u.exec(text.slice(at))?.[0] ?? ""}`;
142
142
  return quoteHint(text, at) ?? `/ starts a regular expression; put the value in quotes: "${word}"`;
143
143
  };
144
- export const parseLucene = (text, { defaultField = "text", ...context } = {}) => {
144
+ export const parseLucene = (text, { defaultField = "text", proximity = false, ...context } = {}) => {
145
145
  const fail = (reason, span, alternative) => queryError(reason, span, alternative ?? quoteHint(text, span.start), context);
146
146
  if (text.length > QUERY_LIMITS.bytes || new TextEncoder().encode(text).length > QUERY_LIMITS.bytes)
147
147
  fail("query.too-large", { start: 0, end: text.length });
@@ -290,6 +290,13 @@ export const parseLucene = (text, { defaultField = "text", ...context } = {}) =>
290
290
  ...(implicit ? { implicit: true } : {}),
291
291
  });
292
292
  }
293
+ if (proximity && peek().kind === "~" && node.kind === "predicate" && node.operator === "phrase") {
294
+ take();
295
+ const distance = take();
296
+ if (distance.kind !== "number" || !/^\d+$/u.test(distance.raw))
297
+ fail("query.invalid-syntax", distance.span, 'write the distance as a whole number: "a b"~3');
298
+ node = { ...node, slop: Number(distance.raw), span: { start: node.span.start, end: distance.span.end } };
299
+ }
293
300
  if (peek().kind === "~")
294
301
  fail("query.unsupported-operator", peek().span, node.kind === "predicate" && node.operator === "phrase"
295
302
  ? "proximity (~) is not supported — drop the ~ for the exact phrase, or join the words with AND"
@@ -21,6 +21,8 @@ export interface Predicate {
21
21
  span: Span;
22
22
  /** A bare word, under no field. */
23
23
  implicit?: true;
24
+ /** `"a b"~N`, read only when the parser is asked to: how far apart the phrase's words may be. */
25
+ slop?: number;
24
26
  }
25
27
  export type QueryNode = Predicate | {
26
28
  kind: "boolean";
@@ -16,6 +16,7 @@ const predicateSchema = v.object({
16
16
  upper_inclusive: v.optional(v.boolean()),
17
17
  span: spanSchema,
18
18
  implicit: v.optional(v.literal(true)),
19
+ slop: v.optional(v.pipe(v.number(), v.integer(), v.minValue(0))),
19
20
  });
20
21
  export const queryNodeSchema = v.variant("kind", [
21
22
  predicateSchema,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wirecat/cli-base",
3
- "version": "0.48.0",
3
+ "version": "0.49.0",
4
4
  "description": "The parts every command line tool needs: output modes, terminal rendering, error model, exit codes, credentials, clocks",
5
5
  "license": "Apache-2.0",
6
6
  "author": "Viacheslav Ptsarev",