Skip to content

Commit bba8baf

Browse files
absorb sqon-builder into modules/sqon
1 parent 8fd4de4 commit bba8baf

24 files changed

Lines changed: 2758 additions & 902 deletions

File tree

.dev/sessions.md

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,28 @@ Newest first.
88

99
## 2026-06-30
1010

11+
Absorbed `@overture-stack/sqon-builder` functionality into `modules/sqon`, making it the single package for SQON construction and validation; `sqon-builder` is now redundant and will be deprecated separately.
12+
13+
- `modules/sqon/src/schema/constants.ts`: added `zod.boolean()` to `SqonScalarValueSchema`; exported `SqonScalar` and `SqonScalarOrArray` types
14+
- `modules/sqon/src/schema/index.ts`: re-exported the two new types
15+
- `modules/sqon/src/builder/utils.ts`: new file - `SqonFieldFilter` type, `isGroupNode`, `isFieldFilter`, `asArray`, `emptySqon`, `checkMatchingArrays`, `checkMatchingFilter`; `SqonFieldFilter` defined as `SqonNode & { content: { fieldName: string; value: SqonScalarOrArray } }` so it satisfies the type predicate constraint
16+
- `modules/sqon/src/builder/reduce.ts`: new file - `reduceSqon`; merges in/not-in/some-not-in/all value arrays, applies boundary semantics to gt/gte/lt/lte, removes empty combinations, unwraps single-item and/or (except pivoted ones), flattens same-op same-pivot combinations; `between` intentionally left non-reducible (v2)
17+
- `modules/sqon/src/builder/index.ts`: new file - `SQON` factory and `SqonBuilder` type; covers `in`, `not-in`, `some-not-in`, `all`, `gt`, `gte`, `lt`, `lte`, `between`, `fuzzy` (op stays `'filter'` for serialized SQON compatibility), `and`, `or`, `not`, `setFilter`, `removeFilter`, `removeExactFilter`; builder is a pure wrapper over `SqonNode`, not the `& SQON` anti-pattern
18+
- `modules/sqon/src/index.ts`: exported `SQON`, `SqonBuilder`, `SqonFieldFilter`, `SqonFieldFilterKey`, `checkMatchingArrays`, `checkMatchingFilter`, `emptySqon`
19+
- `modules/sqon/src/builder/index.test.ts`, `modules/sqon/src/__tests__/schema.test.ts`: new/extended BDD tests (91 + 152 total passing)
20+
- `modules/graphql-router/src/network/utils/sqon.ts`: swapped `@overture-stack/sqon-builder` import for `@overture-stack/sqon`; removed `@ts-expect-error`; `convertToSqon` now returns `Result<SqonNode, ...>` via `SQON.from(filter).toValue()`
21+
- `modules/components/src/DataContext/types.ts`: `SQONType` now aliases `SqonNode | null` (from `@overture-stack/sqon`)
22+
- `modules/components/src/Table/DownloadButton/DownloadButton.tsx`: swapped `SQONBuilder.in(...)` for `SQON.in(...)`
23+
- `modules/graphql-router/package.json`, `modules/components/package.json`: replaced `@overture-stack/sqon-builder` dependency with `@overture-stack/sqon` (local workspace ref)
24+
- `modules/sqon/src/index.ts`: updated exports to use combined value+type re-export syntax
25+
- `modules/sqon/src/builder/index.test.ts`: updated all references to match renamed identifiers
26+
- `modules/graphql-router/src/network/utils/sqon.ts`, `modules/components/src/Table/DownloadButton/DownloadButton.tsx`: updated to `SqonBuilder` (was `SQON`)
27+
- `modules/sqon/README.md`: rewrote to document builder API; added usage section covering the recommended `SqonNode`-at-boundaries pattern, `SqonBuilder.from()` for validation, filter and combination examples, and type reference table
28+
- `modules/sqon/src/__tests__/buildQueryFilter.test.js`, `modules/sqon/src/builder/index.test.ts`, `modules/sqon/src/__tests__/schema.test.ts`, `modules/graphql-router/src/middleware/__tests__/buildQuery/normalizeFilter.test.js`, `modules/components/src/SQONViewer/utils.test.js`: added failing tests specifying the `'filter'``'fuzzy'` canonical op rename; all intentionally red pending implementation
29+
- `modules/sqon/package.json`, `modules/sqon/tsup.config.ts` removed in favour of CLI flags, `modules/sqon/src/version/constants.ts`: switched sqon build from `tsc` to `tsup` with dual ESM+CJS output; added conditional `exports` field so CJS consumers can `require()` the package; fixed `import.meta.url` usage to fall back gracefully in CJS context; resolves `integration-tests-import` failure where components' Babel CJS build could not load the ESM-only sqon package
30+
- `integration-tests/import/package.json`, `integration-tests/import/test.ts`: added `@overture-stack/sqon` as a direct dep; added test asserting `SqonBuilder`, `SqonSchema`, and `SQON_SCHEMA_VERSION` are importable from the CJS build; removed sqon from the "pure ESM" exclusion comment (graphql-router remains excluded)
31+
- `package.json`: added `"esbuild": "0.17.19"` to both `overrides` and `devDependencies`; the override alone is insufficient because npm does not apply overrides to peer dep auto-installation - making it an explicit devDep forces the correct version at the root where bundle-require loads it; tsup upgrade to 8.5.1 attempted and reverted (esbuild hoisting conflicts under npm); logged pnpm migration and tsup upgrade as standalone tech-debt
32+
1133
Fact-checked the search engine permissions reference against the authoritative OpenSearch source (`static_action_groups.yml`); corrected two mistakes introduced in the 2026-06-28 session; corrected a further mistake about alias resolution permission level, verified against live cluster behaviour and OpenSearch static plugin config.
1234

1335
- `modules/graphql-router/src/searchClient/index.ts`: fixed wrong permission name in the dual-403 error message: `cluster:monitor/nodes_info` is not a real permission; the correct transport action is `cluster:monitor/nodes/info`
@@ -31,6 +53,8 @@ Fact-checked the search engine permissions reference against the authoritative O
3153
- `.dev/roadmap.md`: added "Decouple startup health check from application credential" to the Deployment section; describes why the current coupling is wrong and what the correct fix is (`GET /` via `cluster:monitor/main` for liveness; remove `cluster:monitor/health` dependency)
3254
- `scripts/ping-elasticsearch.sh`: removed mutational engine-label logic (placeholder assignment in a `case`, conditionally overwritten later based on a string-equality check); engine detection is now a single-assignment `engine_label()` function; `if [ $? -ne 0 ]` replaced with `if ! command; then`
3355
- `.dev/roadmap.md`: rewrote "Decouple startup health check from application credential" with an init-container design: elevated credential (`cluster:monitor/health`) confined to an init container that owns the `wait_for_status=yellow` readiness gate and the cluster-status log output; main container runs with `cluster:monitor/main` only; scope note updated to flag the new Vault role/policy, VSO secret, and Helm `initContainers` work this requires
56+
- `modules/graphql-router/src/middleware/__tests__/buildQuery/buildQueryFilter.test.js`: added a failing test specifying that `op: 'fuzzy'` must produce the same ES output as `op: 'filter'`; this is the behavioral specification for the upcoming canonical-op flip; the test is intentionally red now and will go green when the flip is complete
57+
- `~/.claude/CLAUDE.md`, `agentics/CLAUDE.md`, `agentics/AGENTS.md`, `agentics/template/CLAUDE.md`: added "verify purpose alignment before implementing" to Interaction parameters; added to agentics CHANGELOG.md
3458

3559
---
3660

.dev/tech-debt.md

Lines changed: 57 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,16 @@ Issues logged here when found scope-adjacent to other work. Not a priority backl
55

66
---
77

8+
## build tooling
9+
10+
### Migrate from npm to pnpm
11+
standalone: yes
12+
context: npm's flat hoisting causes esbuild binary version conflicts across workspaces when multiple packages use tsup. Adding sqon as a second tsup consumer caused bundle-require's peer esbuild to be hoisted to root at a mismatched version. pnpm's strict per-package isolation would prevent this class of issue; each package sees only what it declares. Migration requires updating the Jenkins pipeline and any Dockerfiles that invoke npm.
13+
14+
### Upgrade tsup from 6.7.0 to 8.5.1
15+
standalone: no
16+
context: tsup@6.7.0 is ~2 years old. Upgrading to 8.5.1 is blocked by the npm hoisting problem above: tsup@8.5.1 brings esbuild@^0.27.0, which conflicts with tsx's esbuild@~0.28.0 and bundle-require's peer dep resolution under npm. Revisit after pnpm migration.
17+
818
## apps/mcp-server
919

1020
### `InMemoryEventStore` is not suitable for production
@@ -313,6 +323,33 @@ When Arranger Server (`apps/search-server`) is updated to use `catalogue`, the M
313323
**Issue:** `resolveSetsInSqon` has two paths - SQON contains no `set_id:` values (no-op, returns SQON unchanged) and SQON contains `set_id:` values (expands to stored IDs via an ES search). Neither path has a unit test.
314324
**Standalone:** yes; but note the file also carries the `hackyTemporaryEsSetResolution` tech-debt entry; evaluate for removal during Sets full-feature implementation rather than investing deeply in tests for code that may be replaced
315325

326+
### No unit tests for `convertToSqon` or other `network/utils/` functions
327+
328+
**Files:** `modules/graphql-router/src/network/utils/sqon.ts`, `modules/graphql-router/src/network/utils/gql.ts`, `modules/graphql-router/src/network/utils/promise.ts`
329+
**Severity:** medium
330+
**Kind:** missing test coverage
331+
**Issue:** `convertToSqon` is a pure function at a user-input boundary: it parses an unknown value and returns `Result<SqonNode, { INVALID_SQON: string }>`. Every incoming SQON passes through it, making it security-relevant, yet it has zero test coverage. The other two utils files (`gql.ts`, `promise.ts`) are also untested.
332+
**Fix:** Unit tests for `convertToSqon` covering: valid SQON returns `success(SqonNode)`; invalid SQON (wrong shape, missing `op`) returns failure with `INVALID_SQON`; null/undefined input returns failure; JSON string input is accepted. Add tests for `gql.ts` and `promise.ts` once their exported surface is confirmed non-trivial.
333+
**Standalone:** yes; pure functions, no mocking required
334+
335+
### No unit tests for network resolvers
336+
337+
**Files:** `modules/graphql-router/src/network/resolvers/` (aggregations.ts, fetch.ts, networkNode.ts, query.ts, response.ts)
338+
**Severity:** medium
339+
**Kind:** missing test coverage
340+
**Issue:** The entire network resolver layer has no tests. This is the core async multi-node query execution path: aggregation response resolving, remote node data fetching, network node response building, query construction, and response transformation. Bugs here affect all multi-catalogue network searches silently.
341+
**Fix:** Unit tests with mocked network node responses. The pure transformation files (`response.ts`, `networkNode.ts`, `query.ts`) can be tested directly. `fetch.ts` requires HTTP-level mocking (e.g. `undici MockAgent` or similar). Cover: single-node success; partial node failure (one down, others succeed); empty response; aggregation accumulation across nodes.
342+
**Standalone:** partial; transformation functions are standalone; `fetch.ts` depends on establishing the HTTP mock pattern first
343+
344+
### No unit tests for `dataToExportFormat`
345+
346+
**File:** `modules/graphql-router/src/utils/dataToExportFormat.js`
347+
**Severity:** medium
348+
**Kind:** missing test coverage
349+
**Issue:** `dataToExportFormat` transforms ES hit data into the export column format, handling `extendedDisplayValues` label substitution, `jsonPath` extraction, column visibility, and hit flattening. No unit tests exist.
350+
**Fix:** Unit tests covering: basic field mapping; `jsonPath` extraction; `extendedDisplayValues` label substitution; columns with `show: false` excluded; empty hit set returns empty array.
351+
**Standalone:** yes; pure transformation function
352+
316353
### `hackyTemporaryEsSetResolution.js`: stale ES 6.2 workaround + convention violation
317354

318355
**File:** `modules/graphql-router/src/mapping/hackyTemporaryEsSetResolution.js`
@@ -326,6 +363,15 @@ When Arranger Server (`apps/search-server`) is updated to use `catalogue`, the M
326363

327364
## modules/types
328365

366+
### No unit tests for `tools/` utilities or `networkAggregationConfigUtils`
367+
368+
**Files:** `modules/types/src/tools/stringFns.ts`, `modules/types/src/tools/typeFns.ts`, `modules/types/src/configs/networkAggregationConfigUtils.ts`
369+
**Severity:** low
370+
**Kind:** missing test coverage
371+
**Issue:** All three files are exported from the package and used across the monorepo but have zero test coverage. `stringFns.ts` and `typeFns.ts` are utility and type guard functions where a regression would propagate silently to every consumer. `networkAggregationConfigUtils.ts` contains non-trivial domain logic for network aggregation config setup.
372+
**Fix:** Co-located unit tests (e.g. `stringFns.test.ts` alongside `stringFns.ts`) covering each exported function. Prioritize `networkAggregationConfigUtils` as the highest-complexity target.
373+
**Standalone:** yes
374+
329375
### Config constants need reorganization (blocked on architecture work)
330376

331377
**File:** `modules/types/src/configs/constants.ts`
@@ -486,11 +532,11 @@ When Arranger Server (`apps/search-server`) is updated to use `catalogue`, the M
486532
**Files:** `integration-tests/import/test.ts`, `integration-tests/import/package.json`
487533
**Severity:** low (gap in regression coverage)
488534
**Kind:** missing test coverage
489-
**Issue:** `integration-tests/import` runs under Jest + ts-jest, which handles CJS and TypeScript source but cannot import pure-ESM dist packages (`.js` files with `"type": "module"` and no `"require"` export) without additional configuration. `@overture-stack/arranger-graphql-router` and `@overture-stack/sqon` are pure ESM; both are missing from the import smoke test. `@overture-stack/arranger-types` (CJS + ESM hybrid) and `@overture-stack/arranger-components` (CJS via Babel) are covered. An import regression in `graphql-router` or `sqon` would not be caught by this test.
535+
**Issue:** `integration-tests/import` runs under Jest + ts-jest, which handles CJS and TypeScript source but cannot import pure-ESM dist packages (`.js` files with `"type": "module"` and no `"require"` export) without additional configuration. `@overture-stack/arranger-graphql-router` is pure ESM and is missing from the import smoke test. `@overture-stack/arranger-types` (CJS + ESM hybrid), `@overture-stack/arranger-components` (CJS via Babel), and `@overture-stack/sqon` (dual ESM+CJS since 2026-06-30) are covered. An import regression in `graphql-router` would not be caught by this test.
490536

491537
Additionally: `integration-tests/import` resolves all deps via npm workspaces symlinks (`file:` paths), so it tests local build output, not the published tarball. Publishing regressions (e.g. stale `file:` refs in `package.json`) are caught by `npm run release:check` (`scripts/verify-pack.mjs`), not by this test.
492538

493-
**Fix:** Either configure Jest to handle pure-ESM packages (update `transformIgnorePatterns`, enable `--experimental-vm-modules`), or add a separate lightweight smoke test using `node --input-type=module` or `tsx` that imports from `arranger-graphql-router` and `arranger-sqon` and checks their key exports.
539+
**Fix:** Either configure Jest to handle pure-ESM packages (update `transformIgnorePatterns`, enable `--experimental-vm-modules`), or add a separate lightweight smoke test using `node --input-type=module` or `tsx` that imports from `arranger-graphql-router` and checks its key exports.
494540

495541
**Additional TODOs on top of the ESM gap:**
496542
1. **Verify `exports` subpaths, not just package root.** The smoke test should assert each named subpath in the `exports` field (`./utils`, `./download`, etc.) resolves and exposes the expected named exports. A missing barrel re-export (e.g. `getAllData` was absent from `utils/index.ts`) causes `ERR_PACKAGE_PATH_NOT_EXPORTED` for consumers importing via a subpath, which the current test would not catch.
@@ -525,6 +571,15 @@ Additionally: `integration-tests/import` resolves all deps via npm workspaces sy
525571

526572
## apps/search-server
527573

574+
### No unit tests for catalog config loading or `catalogId`
575+
576+
**Files:** `apps/search-server/src/configs/index.ts`, `apps/search-server/src/configs/catalogId.ts`, `apps/search-server/src/configs/fromFiles/` (4 files), `apps/search-server/src/configs/fromEnv/` (3 files)
577+
**Severity:** high
578+
**Kind:** missing test coverage
579+
**Issue:** The catalog loading logic - recursing subdirectories, aggregating config files, generating unique IDs - has no tests. This is startup-critical: bugs cause startup failures or silent misconfiguration in multicatalog deployments. `catalogId.ts` tracks ID uniqueness across loads but is also untested. `fromFiles/` and `fromEnv/` parsing has no coverage either.
580+
**Fix:** Unit tests using a temporary directory fixture for flat (single-catalog) and nested (multicatalog) layouts; error handling on malformed config files; unique ID generation and collision detection in `catalogId.ts`; env var aggregation in `fromEnv/`.
581+
**Standalone:** yes; no running server required; mock the filesystem with a temp directory
582+
528583
### No README
529584

530585
**File:** `apps/search-server/`; no `README.md` present

integration-tests/import/package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,8 @@
22
"name": "integration-tests-import",
33
"dependencies": {
44
"@overture-stack/arranger-components": "file:../../modules/components",
5-
"@overture-stack/arranger-types": "file:../../modules/types"
5+
"@overture-stack/arranger-types": "file:../../modules/types",
6+
"@overture-stack/sqon": "file:../../modules/sqon"
67
},
78
"devDependencies": {
89
"jest": "^29.7.0",

integration-tests/import/test.ts

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
11
import { describe, expect, test } from '@jest/globals';
22
import { Query, Aggregations, SQONViewer, Table } from '@overture-stack/arranger-components';
33
import { configs, elastic } from '@overture-stack/arranger-types';
4+
import { SqonBuilder, SqonSchema, SQON_SCHEMA_VERSION } from '@overture-stack/sqon';
45

5-
// NOTE: @overture-stack/arranger-graphql-router and @overture-stack/sqon are pure ESM
6-
// and require a separate test runner (see tech-debt: ESM import test gap).
6+
// NOTE: @overture-stack/arranger-graphql-router is pure ESM and requires a separate
7+
// test runner (see tech-debt: ESM import test gap).
78
//
89
// NOTE: These tests use file: workspace deps and therefore exercise local build output,
910
// not the published npm tarball. Publishing regressions (e.g. file: paths in package.json)
@@ -23,4 +24,10 @@ describe('integration-tests/import', () => {
2324
expect(configs.configOptionalProperties).toBeDefined();
2425
expect(elastic.esToAggTypesMap).toBeDefined();
2526
});
27+
28+
test('imports @overture-stack/sqon CJS exports', () => {
29+
expect(SqonBuilder).toBeDefined();
30+
expect(SqonSchema).toBeDefined();
31+
expect(typeof SQON_SCHEMA_VERSION).toBe('string');
32+
});
2633
});

modules/components/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@
3232
"@overture-stack/arranger-types": "file:../types",
3333
"@emotion/react": "^11.0.0",
3434
"@emotion/styled": "^11.0.0",
35-
"@overture-stack/sqon-builder": "^1.1.0",
35+
"@overture-stack/sqon": "file:../sqon",
3636
"@reach/component-component": "^0.17.0",
3737
"@tanstack/react-table": "^8.7.9",
3838
"axios": "^1.16.0",

modules/components/src/DataContext/types.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import type { SQON } from '@overture-stack/sqon-builder';
1+
import type { SqonNode } from '@overture-stack/sqon';
22
import type { AxiosResponse, Method } from 'axios';
33
import type { Dispatch, SetStateAction } from 'react';
44

@@ -80,7 +80,7 @@ export type APIFetcherFn = (options: {
8080
url?: string;
8181
}) => Promise<AxiosResponse<unknown>>;
8282

83-
export type SQONType = SQON | null;
83+
export type SQONType = SqonNode | null;
8484

8585
export type FetchDataFn = (options?: {
8686
config?: Record<string, any>;

modules/components/src/Table/DownloadButton/DownloadButton.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { css } from '@emotion/react';
2-
import SQONBuilder from '@overture-stack/sqon-builder';
2+
import { SqonBuilder } from '@overture-stack/sqon';
33
import cx from 'classnames';
44
import { merge } from 'lodash-es';
55
import urlJoin from 'url-join';
@@ -103,7 +103,7 @@ const DownloadButton = ({
103103

104104
const downloadSqon =
105105
!disableRowSelection && hasSelectedRows
106-
? SQONBuilder.in(exportSelectedRowsField, selectedRows).toValue()
106+
? SqonBuilder.in(exportSelectedRowsField, selectedRows).toValue()
107107
: sqon;
108108

109109
const handleExporterClick = (

modules/graphql-router/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@
3838
"@graphql-tools/utils": "^10.2.2",
3939
"@opensearch-project/opensearch": "^3.6.0",
4040
"@overture-stack/arranger-types": "file:../types",
41-
"@overture-stack/sqon-builder": "^1.1.0",
41+
"@overture-stack/sqon": "file:../sqon",
4242
"apollo-server": "^3.10.3",
4343
"apollo-server-core": "^3.10.3",
4444
"apollo-server-express": "^3.10.3",

0 commit comments

Comments
 (0)