@kortexya/reasoninglayer 1.27.0 → 2.0.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/README.md +38 -18
- package/dist/index.cjs +988 -125
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +3107 -761
- package/dist/index.d.ts +3107 -761
- package/dist/index.js +986 -126
- package/dist/index.js.map +1 -1
- package/package.json +5 -3
package/dist/index.js
CHANGED
|
@@ -5,7 +5,7 @@ var __export = (target, all) => {
|
|
|
5
5
|
};
|
|
6
6
|
|
|
7
7
|
// src/config.ts
|
|
8
|
-
var SDK_VERSION = "
|
|
8
|
+
var SDK_VERSION = "2.0.0";
|
|
9
9
|
function resolveConfig(config) {
|
|
10
10
|
if (!config.baseUrl) {
|
|
11
11
|
throw new Error("ClientConfig.baseUrl is required");
|
|
@@ -100,6 +100,20 @@ var ConstraintViolationError = class extends ApiError {
|
|
|
100
100
|
this.constraint = constraint;
|
|
101
101
|
}
|
|
102
102
|
};
|
|
103
|
+
var BulkRefusedError = class extends ApiError {
|
|
104
|
+
name = "BulkRefusedError";
|
|
105
|
+
/**
|
|
106
|
+
* The refused rows, each naming its index in the request's `terms` array.
|
|
107
|
+
*
|
|
108
|
+
* @remarks
|
|
109
|
+
* Never empty — the engine answers this shape only when it refused something.
|
|
110
|
+
*/
|
|
111
|
+
rows;
|
|
112
|
+
constructor(message, body, headers, rows, errorCode) {
|
|
113
|
+
super(message, 422, body, headers, errorCode);
|
|
114
|
+
this.rows = rows;
|
|
115
|
+
}
|
|
116
|
+
};
|
|
103
117
|
var RateLimitError = class extends ApiError {
|
|
104
118
|
name = "RateLimitError";
|
|
105
119
|
/** Seconds to wait before retrying, or null if not specified. */
|
|
@@ -189,6 +203,13 @@ function createApiError(status, body, headers) {
|
|
|
189
203
|
constraint
|
|
190
204
|
);
|
|
191
205
|
}
|
|
206
|
+
case 422: {
|
|
207
|
+
const rows = bulkRefusalRows(body);
|
|
208
|
+
if (rows !== null) {
|
|
209
|
+
return new BulkRefusedError(message, body, headers, rows, errorCode);
|
|
210
|
+
}
|
|
211
|
+
return new ApiError(message, status, body, headers, errorCode);
|
|
212
|
+
}
|
|
192
213
|
case 429: {
|
|
193
214
|
const rl = parseRateLimitHeaders(headers);
|
|
194
215
|
return new RateLimitError(message, body, headers, errorCode, rl.retryAfter, rl.limit, rl.remaining);
|
|
@@ -205,6 +226,23 @@ function isErrorBody(body) {
|
|
|
205
226
|
const obj = body;
|
|
206
227
|
return typeof obj.error === "string" && typeof obj.message === "string";
|
|
207
228
|
}
|
|
229
|
+
function bulkRefusalRows(body) {
|
|
230
|
+
if (typeof body !== "object" || body === null) return null;
|
|
231
|
+
const obj = body;
|
|
232
|
+
if (!Array.isArray(obj.errors)) return null;
|
|
233
|
+
const rows = [];
|
|
234
|
+
for (const entry of obj.errors) {
|
|
235
|
+
if (typeof entry !== "object" || entry === null) return null;
|
|
236
|
+
const row = entry;
|
|
237
|
+
if (typeof row.index !== "number" || typeof row.message !== "string") return null;
|
|
238
|
+
rows.push({
|
|
239
|
+
index: row.index,
|
|
240
|
+
message: row.message,
|
|
241
|
+
...typeof row.feature === "string" ? { feature: row.feature } : {}
|
|
242
|
+
});
|
|
243
|
+
}
|
|
244
|
+
return rows.length > 0 ? rows : null;
|
|
245
|
+
}
|
|
208
246
|
function isConstraintViolationBody(body) {
|
|
209
247
|
if (!isErrorBody(body)) return false;
|
|
210
248
|
if (!("details" in body)) return false;
|
|
@@ -1124,11 +1162,11 @@ var Sorts = class {
|
|
|
1124
1162
|
...params
|
|
1125
1163
|
});
|
|
1126
1164
|
/**
|
|
1127
|
-
* @description Per Definition IV.5 (Milanese & Pasi, IEEE TFS 2024 — CC-BY manuscript in reasoninglayer-sources/pdf_sources/), verbatim: ≺∼· ≝ ((≺∼ .− ∼) ⊍ ⪯)⊕ The combined chain preorder ≺∼ of Definition IV.1 with every DIRECTLY-similar pair deleted (`.−` zeroes the pair — it is not an arithmetic difference), the crisp order unioned back, and the result re-closed. A chain survives when its ENDPOINTS are not directly similar: slasher ⪯ horror ∼₀.₅ thriller keeps 0.5 while (horror, thriller) itself answers 0 — two similar sorts meet through their GLB instead (Fig. 4c: horror ⩏ thriller = slasher). The combined ≺∼ — where a direct ∼ edge IS a step; the coarse retrieval mode of Example V.4 — backs equivalence classes and term substitutability internally. (History: the differencing form here is ORIGINAL and faithful; #203 swapped in the combined semantics, and a 2026-08-23 pass re-documented that as correct from secondary sources. The accepted manuscript settled it the other way.)
|
|
1165
|
+
* @description Omitted, the answer is Def. IV.5 `≾̇` — the default because that is the relation the graded GLB and term substitutability are computed from. `granularity: "combined"` answers Def. IV.1 `≺∼`, where a similarity edge IS a step, so a directly-similar pair reads its similarity degree instead of the `0` the pair deletion gives it. A caller asking "how close are these two sorts" wants the second; a caller asking "does this sort substitute for that one" wants the first (#282). Per Definition IV.5 (Milanese & Pasi, IEEE TFS 2024 — CC-BY manuscript in reasoninglayer-sources/pdf_sources/), verbatim: ≺∼· ≝ ((≺∼ .− ∼) ⊍ ⪯)⊕ The combined chain preorder ≺∼ of Definition IV.1 with every DIRECTLY-similar pair deleted (`.−` zeroes the pair — it is not an arithmetic difference), the crisp order unioned back, and the result re-closed. A chain survives when its ENDPOINTS are not directly similar: slasher ⪯ horror ∼₀.₅ thriller keeps 0.5 while (horror, thriller) itself answers 0 — two similar sorts meet through their GLB instead (Fig. 4c: horror ⩏ thriller = slasher). The combined ≺∼ — where a direct ∼ edge IS a step; the coarse retrieval mode of Example V.4 — backs equivalence classes and term substitutability internally. (History: the differencing form here is ORIGINAL and faithful; #203 swapped in the combined semantics, and a 2026-08-23 pass re-documented that as correct from secondary sources. The accepted manuscript settled it the other way.)
|
|
1128
1166
|
*
|
|
1129
1167
|
* @tags sorts
|
|
1130
1168
|
* @name GetPreorderDegree
|
|
1131
|
-
* @summary Get preorder degree
|
|
1169
|
+
* @summary Get the preorder degree between two sorts, in either of the paper's two readings — `granularity` selects which, exactly as `GET /api/v1/sorts/quotient-order` does, and the answer echoes it back.
|
|
1132
1170
|
* @request POST:/api/v1/sorts/preorder-degree
|
|
1133
1171
|
*/
|
|
1134
1172
|
getPreorderDegree = (data, params = {}) => this.http.request({
|
|
@@ -1639,7 +1677,7 @@ var Terms = class {
|
|
|
1639
1677
|
...params
|
|
1640
1678
|
});
|
|
1641
1679
|
/**
|
|
1642
|
-
* @description Creates multiple terms in a single operation for efficiency. This is optimized for high-volume data loading scenarios and skips individual witness validation (constraint propagation runs once at the end). # Performance - Terms are added to the domain store in a single lock acquisition - Constraint propagation runs once after all terms are added - Much faster than calling add_term N times # Declarations and all-or-nothing (#238, #239) Every entry is held to the same declarations as `POST /terms`, with the same `422`. One refusal — a declaration violation or a bound-constraint violation — refuses the WHOLE batch and puts the store and the facade back: fresh ids removed, coreferenced entities restored to their pre-batch description. A batch may reference the client-minted ids it is itself creating: the resolver is the resident store unioned with the batch's own ids. When such an id then coreferences through a `@key`, every designator of it in the same batch is rewritten to the entity it merged into — the reference names what the write actually produced, never an id the coreference removed. `term_ids` carries the EFFECTIVE id per request position — the created id, or the existing entity's id when the entry coreferenced through a `@key`. # Authorization Requires X-Tenant-Id header. The tenant_id is taken from the header.
|
|
1680
|
+
* @description Creates multiple terms in a single operation for efficiency. This is optimized for high-volume data loading scenarios and skips individual witness validation (constraint propagation runs once at the end). # Performance - Terms are added to the domain store in a single lock acquisition - Constraint propagation runs once after all terms are added - Much faster than calling add_term N times # Declarations and all-or-nothing (#238, #239, #262) Every entry is held to the same declarations as `POST /terms`, with the same `422`. One refusal — a declaration violation or a bound-constraint violation — refuses the WHOLE batch and puts the store and the facade back: fresh ids removed, coreferenced entities restored to their pre-batch description. The refusal NAMES THE ROWS (#262): the body carries `errors[]`, one entry per refused row, each with its `index` in the request's `terms` array and the `feature` the refusal is about when it names one, so a client repairs the exact rows it sent instead of guessing which one a single-message refusal meant. `partial: true` (#271) writes the rows the checks did NOT refuse instead of refusing the batch for one of them. A real import holding one bad record had to be resent minus that record — a second full call, a second full round of constraint evaluation, and a window in which a survivor's reference target can be deleted between the two — although the engine already names every bad entry by index in one pass and therefore already knows which ones were fine. The answer is `201` when anything was written, with `term_ids` carrying one id per ACCEPTED row and the same `errors[]` beside it; `422` when every row was refused, because nothing was written and that is a refusal. ⛔ Two failures stay batch-wide under `partial`, because neither can be attributed to a row, and the flag does not pretend otherwise: the end-of-batch constraint propagation answers ONE `409` for the whole batch (`facade.process_events`), and a persistence failure reverts what the batch inserted (#239). `dry_run: true` (#262) runs every check the real write runs — conversion, declarations, coreference, events — and writes nothing: the store and the facade are put back inside the write lock, nothing persists, nothing is notified, no derivation is queued. A clean dry run answers `200` with the count the batch would have produced and NO `term_ids` (#268): the rollback has already discarded every id it minted for a fresh entry, and a second, real POST of the same body mints different ones — so the vector named nothing, in the very positions the field documents as storable. The ids that DO outlive a dry run are the entities a `@key` coreference would have merged into, and those are answered separately as `coreferenced_term_ids`. A refused dry run answers the same `errors[]` body the real write answers with. A batch may reference the client-minted ids it is itself creating: the resolver is the resident store unioned with the batch's own ids. When such an id then coreferences through a `@key`, every designator of it in the same batch is rewritten to the entity it merged into — the reference names what the write actually produced, never an id the coreference removed. On a real write `term_ids` carries the EFFECTIVE id per request position — the created id, or the existing entity's id when the entry coreferenced through a `@key`. It is ABSENT on a dry run. # Authorization Requires X-Tenant-Id header. The tenant_id is taken from the header.
|
|
1643
1681
|
*
|
|
1644
1682
|
* @tags terms
|
|
1645
1683
|
* @name BulkAddTerms
|
|
@@ -1829,7 +1867,7 @@ var Inference = class {
|
|
|
1829
1867
|
...params
|
|
1830
1868
|
});
|
|
1831
1869
|
/**
|
|
1832
|
-
* @description # TRUE HOMOICONIC API Request contains a goal term and optional constraints. Response returns solutions with term-based substitutions. ## Temporal Reasoning Use constraints to filter by temporal relations: ```json { "goal": {"sort_name": "Employment", "features": {"valid_to": {"name": "?EndTime"}}}, "constraints": [{"type": "Guard", "left": "?EndTime", "op": "lt", "right": "1583020800000"}] } ``` ## Reading a solution `solutions[].proof.kind` says how the goal was established, and is always present: `proved` (a rule was applied — either it fired in this search, or the goal was a conclusion a forward chain had materialised and the engine recovered the rule from its recorded derivation, so `rule_term_id` and `rule_label` are filled), `fact` (a stored fact with no recorded derivation), `residuated` (an open-world leaf: unknown, never false), `unattributed` (a sound answer whose derivation the engine could not report). A client never has to infer this from the node's shape. A goal that carries `constraints` is a CONJUNCTION: its root node holds one subproof per clause and reports the strongest kind the clauses carry — `proved` when any clause was proved by a rule, else `fact`. `solutions[].substitution.bindings` carries one entry per query variable the engine resolved. A variable left unresolved is ABSENT; it is never bound to itself. # Authorization Requires X-Tenant-Id header. Traced: this is the path the zanzibar gateway hits for every permission check, so it is where an end-to-end trace either explains a slow request or does not. `skip_all` because the request body can be large and has no business in a span attribute.
|
|
1870
|
+
* @description # TRUE HOMOICONIC API Request contains a goal term and optional constraints. Response returns solutions with term-based substitutions. ## Temporal Reasoning Use constraints to filter by temporal relations: ```json { "goal": {"sort_name": "Employment", "features": {"valid_to": {"name": "?EndTime"}}}, "constraints": [{"type": "Guard", "left": "?EndTime", "op": "lt", "right": "1583020800000"}] } ``` ## Reading a solution `solutions[].proof.kind` says how the goal was established, and is always present: `proved` (a rule was applied — either it fired in this search, or the goal was a conclusion a forward chain had materialised and the engine recovered the rule from its recorded derivation, so `rule_term_id` and `rule_label` are filled), `fact` (a stored fact with no recorded derivation), `residuated` (an open-world leaf: unknown, never false), `unattributed` (a sound answer whose derivation the engine could not report). A client never has to infer this from the node's shape. A goal that carries `constraints` is a CONJUNCTION: its root node holds one subproof per clause and reports the strongest kind the clauses carry — `proved` when any clause was proved by a rule, else `fact`. `solutions[].substitution.bindings` carries one entry per query variable the engine resolved. A variable left unresolved is ABSENT; it is never bound to itself. ## Joining a proof to the thing it proves `proof.goal_term_id` is the TermId of the CONCLUSION the node proved. It joins to `POST /api/v1/query/by-sort` and `GET /api/v1/terms/{id}`, and two solutions of one goal share it only when they prove the same conclusion. It is ABSENT when there is no such id — a goal proved without a preceding `CHAIN` materialised no conclusion, and a derivation replayed from a persistent store cannot be attributed to one. A client falls back deliberately there rather than by accident. `proof.rule_head_term_id` names the rule's instantiated HEAD on a rule application, when it differs from the conclusion. ⛔ It is rule scaffolding: `GET /api/v1/terms/{id}` refuses it deliberately (#192). It is a grouping key, not a fetchable id. # Authorization Requires X-Tenant-Id header. Traced: this is the path the zanzibar gateway hits for every permission check, so it is where an end-to-end trace either explains a slow request or does not. `skip_all` because the request body can be large and has no business in a span attribute.
|
|
1833
1871
|
*
|
|
1834
1872
|
* @tags inference
|
|
1835
1873
|
* @name BackwardChain
|
|
@@ -1937,7 +1975,7 @@ var Inference = class {
|
|
|
1937
1975
|
...params
|
|
1938
1976
|
});
|
|
1939
1977
|
/**
|
|
1940
|
-
* @description This drops the hydrated base facts, the forward-chain `
|
|
1978
|
+
* @description This drops the hydrated base facts, the forward-chain `keep_derived` facts and the residuation store, then forgets the hydration flag so the next request reloads the base facts from PostgreSQL. **It does not delete durable data.** The terms are the authority; this is their cache. That makes it the retraction primitive forward chaining otherwise lacks. Chaining is monotonic — delete a `blocks` edge and the derived "A blocks B" survives every later pass, so the KB keeps asserting a relationship the user removed. Clearing and re-chaining rebuilds the closure from the terms that actually exist. # History Until 2026-07-28 this handler enumerated every term in the tenant and deleted it from PostgreSQL, while calling itself "clear facts" and reporting `"Cleared N facts/rules"`. It is reachable with an ordinary tenant credential — unlike `/api/v1/admin/clear-tenant/{tenant_id}`, which gateways block — so a caller reading the name, the path or the response body had no way to know it was a tenant wipe. It destroyed a live tenant that way. Use `DELETE /api/v1/terms/{term_id}` to delete a term, and the admin route to wipe a tenant; deleting durable data must not be something an endpoint does as a side effect of its name. # Authorization Requires X-Tenant-Id header, and the path tenant must match it.
|
|
1941
1979
|
*
|
|
1942
1980
|
* @tags inference
|
|
1943
1981
|
* @name ClearFacts
|
|
@@ -6679,7 +6717,7 @@ var Query = class {
|
|
|
6679
6717
|
this.http = http;
|
|
6680
6718
|
}
|
|
6681
6719
|
/**
|
|
6682
|
-
* @description Returns all terms with the specified sort OR any of its subtypes. This implements proper OSF polymorphic query semantics where querying a parent sort returns all instances of that sort and its descendants. ## Resolving `sort_name` A name can denote more than one id (see `sort_name_candidates`), and no cheap probe tells which of them the query can actually answer from: on the production adapter `get_sort` and `get_sort_ids_by_names` are bare reads of an in-memory cache with no persistence fallback, while the query's own `get_compatible_sorts` does fall back to Postgres. Confirming a candidate with `get_sort` would therefore 404 every tenant sort created before the last restart — a guard strictly stricter than the thing it guards. So the candidates are **tried** against the real query; only `SortNotFound` moves on to the next, every other failure is returned as-is. A phantom id (minted into the tenant lattice by ingestion and never persisted, #138) cannot be returned: the query authority refuses it and the loop skips past it. When no candidate answers, the sort is not queryable and the honest reply is 404 naming the sort the CALLER asked for — never a 400 leaking an internal `SortId` the caller never supplied. Every candidate that answers **contributes**; the answer is their union, deduplicated by term id. Registration now keeps a tenant to one sort per name (#139), so two answering candidates mean rows a pre-fix engine left behind: one name, two sorts, and the tenant's terms of that type divided between them. Stopping at the first — what this route did — returned one half and reported nothing about the other, because an id the caller never supplied going unqueried raises no error. In the ordinary case exactly one candidate answers and the route runs exactly one term query, as it always did.
|
|
6720
|
+
* @description Returns all terms with the specified sort OR any of its subtypes. This implements proper OSF polymorphic query semantics where querying a parent sort returns all instances of that sort and its descendants. ## Resolving `sort_name` A name can denote more than one id (see `sort_name_candidates`), and no cheap probe tells which of them the query can actually answer from: on the production adapter `get_sort` and `get_sort_ids_by_names` are bare reads of an in-memory cache with no persistence fallback, while the query's own `get_compatible_sorts` does fall back to Postgres. Confirming a candidate with `get_sort` would therefore 404 every tenant sort created before the last restart — a guard strictly stricter than the thing it guards. So the candidates are **tried** against the real query; only `SortNotFound` moves on to the next, every other failure is returned as-is. A phantom id (minted into the tenant lattice by ingestion and never persisted, #138) cannot be returned: the query authority refuses it and the loop skips past it. When no candidate answers, the sort is not queryable and the honest reply is 404 naming the sort the CALLER asked for — never a 400 leaking an internal `SortId` the caller never supplied. Every candidate that answers **contributes**; the answer is their union, deduplicated by term id. Registration now keeps a tenant to one sort per name (#139), so two answering candidates mean rows a pre-fix engine left behind: one name, two sorts, and the tenant's terms of that type divided between them. Stopping at the first — what this route did — returned one half and reported nothing about the other, because an id the caller never supplied going unqueried raises no error. In the ordinary case exactly one candidate answers and the route runs exactly one term query, as it always did. ## Conclusions, and what this route promises The answer is the tenant's durable extension of the browse closure UNIONED with the conclusions this process currently HOLDS — the same set OSFQL `MATCH` reads. That is what makes a rule sort readable here at all: no OSFQL path persists a conclusion, so the durable half is empty for a sort whose members exist only by derivation (#261). This route does NOT chain. It reports what the last chain left, so: * after a restart the derived half is empty until something chains again — `CHAIN`, `PROVE`, a retraction's re-chain, or `POST /api/v1/inference/forward-chain`; * a conclusion whose premise was withdrawn through a door that runs no truth maintenance is still answered until the next chain (`docs/OPEN_DEFECTS.md` #82). `total` is the size of the answer before `offset`/`limit`, counted — never a search bound. Rows are ordered by term id, so a page is stable. When the answer is EMPTY and the browsed sort is a rule conclusion sort, `note` says so and names the routes that materialise the conclusions. A bare `total: 0` reads as an authoritative count, and for a rule sort in a freshly-started process it is the one answer a caller must not take at face value — the same confusion #261 was filed about, one state later.
|
|
6683
6721
|
*
|
|
6684
6722
|
* @tags query
|
|
6685
6723
|
* @name FindBySort
|
|
@@ -9592,7 +9630,7 @@ var Admin = class {
|
|
|
9592
9630
|
...params
|
|
9593
9631
|
});
|
|
9594
9632
|
/**
|
|
9595
|
-
* @description
|
|
9633
|
+
* @description Re-materialises the tenant's conclusions SYNCHRONOUSLY: chains the resident store once, so every conclusion the rules prove and the store lacks comes back. `rematerialised` reports how many, and every read surface answers from that store — this is the one call that repairs a drifted tenant (#270). ONE DIRECTION: it does not remove a conclusion the rules no longer support, because the write doors' truth maintenance already does that on the write that changed the premise. It ALSO truncates the durable derived_facts table and queues a BootstrapAll event over every analyzed rule; that half is asynchronous, and `removed` / `rules_queued` / `materialization_lsn` describe it. Concurrent rebuilds for the same tenant are rejected with 409 (per-tenant mutex). Full operational runbook (prerequisites, timings, monitoring, failure recovery, known limitations) lives in .claude/SUB_MS_FORWARD_CHAINING_STATUS.md, section 'Operator runbook — derived-facts rebuild'.
|
|
9596
9634
|
*
|
|
9597
9635
|
* @tags admin
|
|
9598
9636
|
* @name RebuildDerivedFacts
|
|
@@ -10038,6 +10076,24 @@ var Osfql = class {
|
|
|
10038
10076
|
format: "json",
|
|
10039
10077
|
...params
|
|
10040
10078
|
});
|
|
10079
|
+
/**
|
|
10080
|
+
* @description Takes the same body as `POST /api/v1/osfql` (the `query` matters; `atomic` decides whether the answer reports the atomic refusal) and answers: - `mutates` — whether ANY statement writes, folding every nested statement, so an `IF` whose THEN or ELSE branch writes reports `true`, and including an inline-write `MATCH` a first-statement classification would read as safe; - `atomic_refusal` — why the program cannot run as one atomic unit, with the same code the run would refuse with (`drop_sort_in_atomic_program`); - `statements` — one entry per statement with the ENGINE's classification (catalog id, risk tier, and a per-statement `mutates` — not a client-side tokenizer), the exact source text, the sorts named, nested entries for IF branches and `WITH` continuations, and — for each destructive statement — the affected-row count and up to 10 sample rows. The per-statement `mutates` is what a write gate reads: `CHAIN` and `RELEASE RESIDUATIONS` report `true` although they classify as `process_control`, while `MARK`, `CUT` and `SPACE` report `false`. No client has to keep its own table of which process-control statements write; `GET /api/v1/osfql/catalog` carries the same verdict per entry as `never` / `always` / `depends` (#266). The classification comes from the engine's own parse; the counts come from a read-only `MATCH` derived from the destructive statement's pattern and run against a CLONE of the tenant. Nothing runs against the live store, so nothing observable happens in the tenant afterwards. # Examples ```json { "query": "MATCH person(name: ?N); RETRACT person(name: \"Bob\");" } ``` ```json { "query": "DROP SORT person; INSERT person(name: \"Bob\");" } ```
|
|
10081
|
+
*
|
|
10082
|
+
* @tags osfql
|
|
10083
|
+
* @name PreviewOsfql
|
|
10084
|
+
* @summary Preview an OSFQL program: what each statement would do, decided without running anything (#257).
|
|
10085
|
+
* @request POST:/api/v1/osfql/preview
|
|
10086
|
+
* @secure
|
|
10087
|
+
*/
|
|
10088
|
+
previewOsfql = (data, params = {}) => this.http.request({
|
|
10089
|
+
path: `/api/v1/osfql/preview`,
|
|
10090
|
+
method: "POST",
|
|
10091
|
+
body: data,
|
|
10092
|
+
secure: true,
|
|
10093
|
+
type: "application/json",
|
|
10094
|
+
format: "json",
|
|
10095
|
+
...params
|
|
10096
|
+
});
|
|
10041
10097
|
};
|
|
10042
10098
|
|
|
10043
10099
|
// src/api-spec/generated/Context.ts
|
|
@@ -13227,6 +13283,45 @@ var Research = class {
|
|
|
13227
13283
|
});
|
|
13228
13284
|
};
|
|
13229
13285
|
|
|
13286
|
+
// src/normalizers/ontology-alignment.ts
|
|
13287
|
+
function AlignOntologyRequestFromFrontToApi(model) {
|
|
13288
|
+
return {
|
|
13289
|
+
domain_owl: model.domainOwl,
|
|
13290
|
+
targets: model.targets
|
|
13291
|
+
};
|
|
13292
|
+
}
|
|
13293
|
+
function AlignmentMatchDtoFromApiToFront(dto) {
|
|
13294
|
+
return {
|
|
13295
|
+
domainSort: dto.domain_sort,
|
|
13296
|
+
matchType: dto.match_type,
|
|
13297
|
+
targetCurie: dto.target_curie,
|
|
13298
|
+
targetLabel: dto.target_label
|
|
13299
|
+
};
|
|
13300
|
+
}
|
|
13301
|
+
function AlignmentConflictDtoFromApiToFront(dto) {
|
|
13302
|
+
return {
|
|
13303
|
+
domainSort: dto.domain_sort,
|
|
13304
|
+
targetA: dto.target_a,
|
|
13305
|
+
targetB: dto.target_b
|
|
13306
|
+
};
|
|
13307
|
+
}
|
|
13308
|
+
function ExternalMatchDtoFromApiToFront(dto) {
|
|
13309
|
+
return {
|
|
13310
|
+
matchType: dto.match_type,
|
|
13311
|
+
ontologyId: dto.ontology_id,
|
|
13312
|
+
source: dto.source
|
|
13313
|
+
};
|
|
13314
|
+
}
|
|
13315
|
+
function AlignOntologyResponseFromApiToFront(dto) {
|
|
13316
|
+
return {
|
|
13317
|
+
conflicts: dto.conflicts.map(AlignmentConflictDtoFromApiToFront),
|
|
13318
|
+
domainSorts: dto.domain_sorts,
|
|
13319
|
+
mappingTtl: dto.mapping_ttl,
|
|
13320
|
+
matches: dto.matches.map(AlignmentMatchDtoFromApiToFront),
|
|
13321
|
+
targetSorts: dto.target_sorts
|
|
13322
|
+
};
|
|
13323
|
+
}
|
|
13324
|
+
|
|
13230
13325
|
// src/utils/records.ts
|
|
13231
13326
|
function definedEntries(map) {
|
|
13232
13327
|
return Object.entries(map).filter((entry) => entry[1] !== void 0);
|
|
@@ -13278,7 +13373,12 @@ function FeatureDescriptorDtoFromApiToFront(dto) {
|
|
|
13278
13373
|
// `undefined` for a question the wire already answers.
|
|
13279
13374
|
required: dto.required ?? false,
|
|
13280
13375
|
constraint: dto.constraint ? ConstraintDtoFromApiToFront(dto.constraint) : void 0,
|
|
13281
|
-
key: dto.key
|
|
13376
|
+
key: dto.key,
|
|
13377
|
+
expectedSortName: dto.expected_sort_name ?? void 0,
|
|
13378
|
+
minCount: dto.min_count ?? void 0,
|
|
13379
|
+
maxCount: dto.max_count ?? void 0,
|
|
13380
|
+
cardinalityOrigin: dto.cardinality_origin ?? void 0,
|
|
13381
|
+
annotations: definedRecord2(dto.annotations)
|
|
13282
13382
|
};
|
|
13283
13383
|
}
|
|
13284
13384
|
function FeatureDescriptorDtoFromFrontToApi(model) {
|
|
@@ -13288,7 +13388,18 @@ function FeatureDescriptorDtoFromFrontToApi(model) {
|
|
|
13288
13388
|
expected_type_hint: model.expectedTypeHint ?? void 0,
|
|
13289
13389
|
required: model.required,
|
|
13290
13390
|
constraint: model.constraint ? ConstraintDtoFromFrontToApi(model.constraint) : void 0,
|
|
13291
|
-
key: model.key
|
|
13391
|
+
key: model.key,
|
|
13392
|
+
expected_sort_name: model.expectedSortName ?? void 0,
|
|
13393
|
+
min_count: model.minCount ?? void 0,
|
|
13394
|
+
max_count: model.maxCount ?? void 0,
|
|
13395
|
+
cardinality_origin: model.cardinalityOrigin ?? void 0,
|
|
13396
|
+
annotations: model.annotations
|
|
13397
|
+
};
|
|
13398
|
+
}
|
|
13399
|
+
function CoextensiveDefinitionDtoFromApiToFront(dto) {
|
|
13400
|
+
return {
|
|
13401
|
+
definition: dto.definition,
|
|
13402
|
+
exampleCount: dto.example_count
|
|
13292
13403
|
};
|
|
13293
13404
|
}
|
|
13294
13405
|
function BoundConstraintDtoFromApiToFront(dto) {
|
|
@@ -13370,7 +13481,17 @@ function SortDtoFromApiToFront(dto) {
|
|
|
13370
13481
|
worldMode: dto.world_mode,
|
|
13371
13482
|
annotations: definedRecord2(dto.annotations),
|
|
13372
13483
|
pluginId: dto.plugin_id ?? void 0,
|
|
13373
|
-
pluginLocalName: dto.plugin_local_name ?? void 0
|
|
13484
|
+
pluginLocalName: dto.plugin_local_name ?? void 0,
|
|
13485
|
+
altLabels: dto.alt_labels,
|
|
13486
|
+
hiddenLabels: dto.hidden_labels,
|
|
13487
|
+
scopeNote: dto.scope_note ?? void 0,
|
|
13488
|
+
related: dto.related,
|
|
13489
|
+
definition: dto.definition ?? void 0,
|
|
13490
|
+
coextensive: dto.coextensive?.map(CoextensiveDefinitionDtoFromApiToFront),
|
|
13491
|
+
externalMatches: dto.external_matches?.map(
|
|
13492
|
+
(match) => ExternalMatchDtoFromApiToFront(match)
|
|
13493
|
+
),
|
|
13494
|
+
featureEquations: dto.feature_equations
|
|
13374
13495
|
};
|
|
13375
13496
|
}
|
|
13376
13497
|
function SortInfoDtoFromApiToFront(dto) {
|
|
@@ -13382,7 +13503,20 @@ function SortInfoDtoFromApiToFront(dto) {
|
|
|
13382
13503
|
function SortListResponseFromApiToFront(dto) {
|
|
13383
13504
|
return {
|
|
13384
13505
|
sorts: dto.sorts.map(SortDtoFromApiToFront),
|
|
13385
|
-
count: dto.count
|
|
13506
|
+
count: dto.count,
|
|
13507
|
+
total: dto.total,
|
|
13508
|
+
offset: dto.offset
|
|
13509
|
+
};
|
|
13510
|
+
}
|
|
13511
|
+
function ListSortsQueryFromFrontToApi(query) {
|
|
13512
|
+
if (query === void 0) return void 0;
|
|
13513
|
+
return {
|
|
13514
|
+
include_system: query.includeSystem,
|
|
13515
|
+
limit: query.limit,
|
|
13516
|
+
llm_extracted: query.llmExtracted,
|
|
13517
|
+
name_prefix: query.namePrefix?.join(","),
|
|
13518
|
+
needs_review: query.needsReview,
|
|
13519
|
+
offset: query.offset
|
|
13386
13520
|
};
|
|
13387
13521
|
}
|
|
13388
13522
|
function CreateSortRequestFromFrontToApi(model) {
|
|
@@ -13404,6 +13538,8 @@ function BulkSortDefinitionFromFrontToApi(model) {
|
|
|
13404
13538
|
parents: model.parents,
|
|
13405
13539
|
features: model.features?.map(FeatureDescriptorDtoFromFrontToApi),
|
|
13406
13540
|
alt_labels: model.altLabels,
|
|
13541
|
+
hidden_labels: model.hiddenLabels,
|
|
13542
|
+
scope_note: model.scopeNote ?? void 0,
|
|
13407
13543
|
description: model.description ?? void 0,
|
|
13408
13544
|
world_mode: model.worldMode
|
|
13409
13545
|
};
|
|
@@ -13532,19 +13668,56 @@ function BulkSetSimilaritiesResponseFromApiToFront(dto) {
|
|
|
13532
13668
|
errors: dto.errors
|
|
13533
13669
|
};
|
|
13534
13670
|
}
|
|
13671
|
+
var SORT_PREORDER_GRANULARITIES = [
|
|
13672
|
+
"similarity_deleted",
|
|
13673
|
+
"combined"
|
|
13674
|
+
];
|
|
13675
|
+
function toSortPreorderGranularity(value) {
|
|
13676
|
+
const known = SORT_PREORDER_GRANULARITIES.find((candidate) => candidate === value);
|
|
13677
|
+
if (known === void 0) {
|
|
13678
|
+
throw new ValidationError(
|
|
13679
|
+
`Unknown preorder granularity "${value}" \u2014 expected ${SORT_PREORDER_GRANULARITIES.join(" or ")}.`
|
|
13680
|
+
);
|
|
13681
|
+
}
|
|
13682
|
+
return known;
|
|
13683
|
+
}
|
|
13535
13684
|
function GetPreorderDegreeRequestFromFrontToApi(model) {
|
|
13536
13685
|
return {
|
|
13537
13686
|
sort1_id: model.sort1Id,
|
|
13538
|
-
sort2_id: model.sort2Id
|
|
13687
|
+
sort2_id: model.sort2Id,
|
|
13688
|
+
granularity: model.granularity
|
|
13539
13689
|
};
|
|
13540
13690
|
}
|
|
13541
13691
|
function GetPreorderDegreeResponseFromApiToFront(dto) {
|
|
13542
13692
|
return {
|
|
13543
13693
|
sort1Id: dto.sort1_id,
|
|
13544
13694
|
sort2Id: dto.sort2_id,
|
|
13695
|
+
degree: dto.degree,
|
|
13696
|
+
granularity: toSortPreorderGranularity(dto.granularity)
|
|
13697
|
+
};
|
|
13698
|
+
}
|
|
13699
|
+
function QuotientClassFromApiToFront(dto) {
|
|
13700
|
+
return {
|
|
13701
|
+
sortIds: dto.sort_ids,
|
|
13702
|
+
size: dto.size,
|
|
13703
|
+
alpha: dto.alpha
|
|
13704
|
+
};
|
|
13705
|
+
}
|
|
13706
|
+
function QuotientOrderEdgeFromApiToFront(dto) {
|
|
13707
|
+
return {
|
|
13708
|
+
from: dto.from,
|
|
13709
|
+
to: dto.to,
|
|
13545
13710
|
degree: dto.degree
|
|
13546
13711
|
};
|
|
13547
13712
|
}
|
|
13713
|
+
function GetQuotientOrderResponseFromApiToFront(dto) {
|
|
13714
|
+
return {
|
|
13715
|
+
granularity: toSortPreorderGranularity(dto.granularity),
|
|
13716
|
+
classes: dto.classes.map(QuotientClassFromApiToFront),
|
|
13717
|
+
count: dto.count,
|
|
13718
|
+
orderEdges: dto.order_edges.map(QuotientOrderEdgeFromApiToFront)
|
|
13719
|
+
};
|
|
13720
|
+
}
|
|
13548
13721
|
function EquivalenceClassFromApiToFront(dto) {
|
|
13549
13722
|
return {
|
|
13550
13723
|
sortIds: dto.sort_ids,
|
|
@@ -14013,13 +14186,60 @@ var SortsClient = class {
|
|
|
14013
14186
|
return response.data;
|
|
14014
14187
|
}
|
|
14015
14188
|
/**
|
|
14016
|
-
* List
|
|
14189
|
+
* List every sort the tenant owns.
|
|
14017
14190
|
*
|
|
14018
|
-
* @
|
|
14191
|
+
* @param requestOptions - Per-call transport overrides.
|
|
14192
|
+
* @returns The sorts, each with its feature declarations.
|
|
14193
|
+
* @throws {ApiError} When the engine refuses the request.
|
|
14194
|
+
*
|
|
14195
|
+
* @remarks
|
|
14196
|
+
* This asks for the whole listing and keeps only the array. A production
|
|
14197
|
+
* tenant can own 1 M+ sorts (~500 MB uncompressed), which one response
|
|
14198
|
+
* cannot deliver — use {@link SortsClient.listSortsPage} to window it, to
|
|
14199
|
+
* filter it, or to read the `total` that says when to stop.
|
|
14200
|
+
*
|
|
14201
|
+
* @example
|
|
14202
|
+
* ```typescript
|
|
14203
|
+
* const sorts = await client.sorts.listSorts();
|
|
14204
|
+
* ```
|
|
14019
14205
|
*/
|
|
14020
14206
|
async listSorts(requestOptions) {
|
|
14021
|
-
|
|
14022
|
-
|
|
14207
|
+
return (await this.listSortsPage(void 0, requestOptions)).sorts;
|
|
14208
|
+
}
|
|
14209
|
+
/**
|
|
14210
|
+
* List the tenant's sorts, keeping the envelope — `count`, `total` and
|
|
14211
|
+
* `offset` beside the page.
|
|
14212
|
+
*
|
|
14213
|
+
* @param query - The window and filters over the listing.
|
|
14214
|
+
* @param requestOptions - Per-call transport overrides.
|
|
14215
|
+
* @returns The page, its length, the tenant's filtered total and the
|
|
14216
|
+
* offset the page starts at.
|
|
14217
|
+
* @throws {ApiError} When the engine refuses the request.
|
|
14218
|
+
*
|
|
14219
|
+
* @remarks
|
|
14220
|
+
* `count` is the length of THIS page; `total` is what the tenant owns after
|
|
14221
|
+
* filters, across all pages. Measured against the engine on a tenant
|
|
14222
|
+
* holding three sorts, `GET /api/v1/sorts/tenant/{id}` answers
|
|
14223
|
+
* `{"count":3,"total":3,"offset":0}`.
|
|
14224
|
+
*
|
|
14225
|
+
* @example
|
|
14226
|
+
* ```typescript
|
|
14227
|
+
* let offset = 0;
|
|
14228
|
+
* for (;;) {
|
|
14229
|
+
* const page = await client.sorts.listSortsPage({ limit: 500, offset });
|
|
14230
|
+
* consume(page.sorts);
|
|
14231
|
+
* offset += page.count;
|
|
14232
|
+
* if (page.total === undefined || offset >= page.total) break;
|
|
14233
|
+
* }
|
|
14234
|
+
* ```
|
|
14235
|
+
*/
|
|
14236
|
+
async listSortsPage(query, requestOptions) {
|
|
14237
|
+
const response = await this.sorts.listSorts(
|
|
14238
|
+
this.tenantId,
|
|
14239
|
+
ListSortsQueryFromFrontToApi(query),
|
|
14240
|
+
toRequestParams(requestOptions)
|
|
14241
|
+
);
|
|
14242
|
+
return SortListResponseFromApiToFront(response.data);
|
|
14023
14243
|
}
|
|
14024
14244
|
/**
|
|
14025
14245
|
* Bulk-create sorts with name-based parent references.
|
|
@@ -14344,13 +14564,32 @@ var SortsClient = class {
|
|
|
14344
14564
|
/**
|
|
14345
14565
|
* Compute the preorder degree between two sorts.
|
|
14346
14566
|
*
|
|
14347
|
-
* @param request - Sort pair to compute preorder degree for
|
|
14348
|
-
*
|
|
14349
|
-
* @
|
|
14350
|
-
*
|
|
14351
|
-
* @
|
|
14352
|
-
*
|
|
14353
|
-
*
|
|
14567
|
+
* @param request - Sort pair to compute preorder degree for, and optionally
|
|
14568
|
+
* which of the two preorders to read it from.
|
|
14569
|
+
* @returns The preorder degree response including sort IDs, degree, and the
|
|
14570
|
+
* granularity the degree was read from.
|
|
14571
|
+
* @throws {@link ApiError} If the sorts do not exist, or if `granularity`
|
|
14572
|
+
* carries a spelling the engine does not accept.
|
|
14573
|
+
* @throws {@link ValidationError} If the engine answers a granularity this
|
|
14574
|
+
* SDK version does not know.
|
|
14575
|
+
*
|
|
14576
|
+
* @remarks
|
|
14577
|
+
* Per Definition IV.5 (Milanese and Pasi, IEEE TFS 2024), the dotted preorder
|
|
14578
|
+
* is `preorder_dot = ((combined_preorder .- similarity) union subsumption)^+`,
|
|
14579
|
+
* where `.-` DELETES each directly-similar pair — it is NOT an arithmetic
|
|
14580
|
+
* difference. So a directly-similar pair answers `0` under the default
|
|
14581
|
+
* granularity: two similar sorts meet through their GLB, not through each
|
|
14582
|
+
* other.
|
|
14583
|
+
*
|
|
14584
|
+
* `granularity` selects the reading, with the same two spellings
|
|
14585
|
+
* `GET /api/v1/sorts/quotient-order` uses:
|
|
14586
|
+
* - omitted or `similarity_deleted` — Definition IV.5, the default, and the
|
|
14587
|
+
* relation the graded GLB and term substitutability are computed from.
|
|
14588
|
+
* - `combined` — Definition IV.1, where a similarity edge IS a step, so a
|
|
14589
|
+
* directly-similar pair answers its similarity degree.
|
|
14590
|
+
*
|
|
14591
|
+
* The response always echoes the granularity back, so a `0.0` is never
|
|
14592
|
+
* ambiguous between "no path" and "the pair deletion zeroed it".
|
|
14354
14593
|
*
|
|
14355
14594
|
* Degree interpretation:
|
|
14356
14595
|
* - 1.0 = subsumption (sort1 <= sort2)
|
|
@@ -14361,17 +14600,78 @@ var SortsClient = class {
|
|
|
14361
14600
|
*
|
|
14362
14601
|
* @example
|
|
14363
14602
|
* ```typescript
|
|
14364
|
-
* const
|
|
14603
|
+
* const strict = await client.sorts.getPreorderDegree({
|
|
14365
14604
|
* sort1Id: 'uuid-1',
|
|
14366
14605
|
* sort2Id: 'uuid-2',
|
|
14367
14606
|
* });
|
|
14368
|
-
* console.log(
|
|
14607
|
+
* console.log(strict.degree, strict.granularity); // 0 'similarity_deleted'
|
|
14608
|
+
*
|
|
14609
|
+
* const coarse = await client.sorts.getPreorderDegree({
|
|
14610
|
+
* sort1Id: 'uuid-1',
|
|
14611
|
+
* sort2Id: 'uuid-2',
|
|
14612
|
+
* granularity: 'combined',
|
|
14613
|
+
* });
|
|
14614
|
+
* console.log(coarse.degree, coarse.granularity); // 0.5 'combined'
|
|
14369
14615
|
* ```
|
|
14370
14616
|
*/
|
|
14371
14617
|
async getPreorderDegree(request, requestOptions) {
|
|
14372
14618
|
const response = await this.sorts.getPreorderDegree(GetPreorderDegreeRequestFromFrontToApi(request), toRequestParams(requestOptions));
|
|
14373
14619
|
return GetPreorderDegreeResponseFromApiToFront(response.data);
|
|
14374
14620
|
}
|
|
14621
|
+
/**
|
|
14622
|
+
* Get the Definition IV.9 quotient order over the caller's own lattice.
|
|
14623
|
+
*
|
|
14624
|
+
* @param options - Which of the two fuzzy preorders to quotient. Omitted
|
|
14625
|
+
* means `combined`, the engine's default HERE.
|
|
14626
|
+
* @returns The equivalence classes with their degrees, and the fuzzy partial
|
|
14627
|
+
* order between them.
|
|
14628
|
+
* @throws {@link ApiError} 400 when `granularity` carries a spelling the
|
|
14629
|
+
* engine does not accept.
|
|
14630
|
+
* @throws {@link ValidationError} If the engine answers a granularity this
|
|
14631
|
+
* SDK version does not know.
|
|
14632
|
+
*
|
|
14633
|
+
* @remarks
|
|
14634
|
+
* This is the tenant-scoped companion of
|
|
14635
|
+
* {@link SortsClient.getEquivalenceClasses}: the classes, degrees and order
|
|
14636
|
+
* describe exactly the sorts the caller can see, where the older
|
|
14637
|
+
* equivalence-classes route computes process-wide and then filters. Prefer
|
|
14638
|
+
* this one.
|
|
14639
|
+
*
|
|
14640
|
+
* ⚠️ The default granularity here is `combined`, NOT the
|
|
14641
|
+
* `similarity_deleted` default of {@link SortsClient.getPreorderDegree}. The
|
|
14642
|
+
* two routes take the same two spellings and disagree on which is the
|
|
14643
|
+
* default, so state it when it matters. The response echoes it back either
|
|
14644
|
+
* way.
|
|
14645
|
+
*
|
|
14646
|
+
* `orderEdges` is SPARSE and indexes into `classes`: a pair with no edge has
|
|
14647
|
+
* degree `0`. The order is a partial order — antisymmetric, unlike either
|
|
14648
|
+
* preorder it is built from.
|
|
14649
|
+
*
|
|
14650
|
+
* Uses tagged serialization format.
|
|
14651
|
+
*
|
|
14652
|
+
* @example
|
|
14653
|
+
* ```typescript
|
|
14654
|
+
* const quotient = await client.sorts.getQuotientOrder({
|
|
14655
|
+
* granularity: 'similarity_deleted',
|
|
14656
|
+
* });
|
|
14657
|
+
*
|
|
14658
|
+
* for (const cls of quotient.classes) {
|
|
14659
|
+
* console.log(`class of ${cls.size} sorts, degree ${cls.alpha}`);
|
|
14660
|
+
* }
|
|
14661
|
+
* for (const edge of quotient.orderEdges) {
|
|
14662
|
+
* const lower = quotient.classes[edge.from];
|
|
14663
|
+
* const upper = quotient.classes[edge.to];
|
|
14664
|
+
* console.log(`${lower.sortIds} <= ${upper.sortIds} at ${edge.degree}`);
|
|
14665
|
+
* }
|
|
14666
|
+
* ```
|
|
14667
|
+
*/
|
|
14668
|
+
async getQuotientOrder(options, requestOptions) {
|
|
14669
|
+
const response = await this.sorts.getQuotientOrder(
|
|
14670
|
+
{ granularity: options?.granularity },
|
|
14671
|
+
toRequestParams(requestOptions)
|
|
14672
|
+
);
|
|
14673
|
+
return GetQuotientOrderResponseFromApiToFront(response.data);
|
|
14674
|
+
}
|
|
14375
14675
|
/**
|
|
14376
14676
|
* Get equivalence classes based on the combined preorder.
|
|
14377
14677
|
*
|
|
@@ -14379,8 +14679,16 @@ var SortsClient = class {
|
|
|
14379
14679
|
* @throws {@link ApiError} If the lattice cannot be computed.
|
|
14380
14680
|
*
|
|
14381
14681
|
* @remarks
|
|
14382
|
-
* Per Definition IV.9 (Milanese and Pasi 2024)
|
|
14383
|
-
* s1 ~ s2 iff
|
|
14682
|
+
* Per Definition IV.9 (Milanese and Pasi 2024), two sorts are equivalent when
|
|
14683
|
+
* each reaches the other: `s1 ~ s2` iff `preorder(s1, s2) > 0` AND
|
|
14684
|
+
* `preorder(s2, s1) > 0`. The preorder here is the COMBINED one, where a
|
|
14685
|
+
* similarity edge is itself a step — not the `similarity_deleted` default of
|
|
14686
|
+
* {@link SortsClient.getPreorderDegree}.
|
|
14687
|
+
*
|
|
14688
|
+
* ⚠️ This route computes PROCESS-WIDE and then filters, so its classes can
|
|
14689
|
+
* be shaped by sorts the caller cannot see. {@link SortsClient.getQuotientOrder}
|
|
14690
|
+
* computes on the tenant-visible hierarchy instead, returns the same classes
|
|
14691
|
+
* with their degrees, and adds the partial order between them. Prefer it.
|
|
14384
14692
|
*
|
|
14385
14693
|
* Uses tagged serialization format.
|
|
14386
14694
|
*
|
|
@@ -14626,6 +14934,11 @@ function toUntaggedValue(value) {
|
|
|
14626
14934
|
return value.value.map(toUntaggedValue);
|
|
14627
14935
|
}
|
|
14628
14936
|
if (value.type === "Reference") {
|
|
14937
|
+
if (typeof value.value !== "string") {
|
|
14938
|
+
throw new ValidationError(
|
|
14939
|
+
`A @key reference designator (sort "${value.value.sortName}") cannot be a FEATURE value on an untagged inference endpoint: the untagged feature-value family has no designator variant. Name a whole term with { designator: \u2026 } where a TermInputDto is expected, or resolve this one to a term id.`
|
|
14940
|
+
);
|
|
14941
|
+
}
|
|
14629
14942
|
return { termId: value.value };
|
|
14630
14943
|
}
|
|
14631
14944
|
if (value.type === "SortId") {
|
|
@@ -14689,6 +15002,18 @@ function ValueDtoFromApiToFront(dto) {
|
|
|
14689
15002
|
value: dto.value.map(ValueDtoFromApiToFront)
|
|
14690
15003
|
};
|
|
14691
15004
|
}
|
|
15005
|
+
if (dto.type === "Reference") {
|
|
15006
|
+
if (typeof dto.value === "string") {
|
|
15007
|
+
return { type: "Reference", value: dto.value };
|
|
15008
|
+
}
|
|
15009
|
+
return {
|
|
15010
|
+
type: "Reference",
|
|
15011
|
+
value: {
|
|
15012
|
+
sortName: dto.value.sort_name,
|
|
15013
|
+
features: featuresFromApiToFront(dto.value.features)
|
|
15014
|
+
}
|
|
15015
|
+
};
|
|
15016
|
+
}
|
|
14692
15017
|
if (dto.type === "PsiTerm") {
|
|
14693
15018
|
const features = dto.value.features;
|
|
14694
15019
|
return {
|
|
@@ -14779,6 +15104,18 @@ function ValueDtoFromFrontToApi(value) {
|
|
|
14779
15104
|
value: value.value.map(ValueDtoFromFrontToApi)
|
|
14780
15105
|
};
|
|
14781
15106
|
}
|
|
15107
|
+
if (value.type === "Reference") {
|
|
15108
|
+
if (typeof value.value === "string") {
|
|
15109
|
+
return { type: "Reference", value: value.value };
|
|
15110
|
+
}
|
|
15111
|
+
return {
|
|
15112
|
+
type: "Reference",
|
|
15113
|
+
value: {
|
|
15114
|
+
sort_name: value.value.sortName,
|
|
15115
|
+
features: featuresFromFrontToApi(value.value.features)
|
|
15116
|
+
}
|
|
15117
|
+
};
|
|
15118
|
+
}
|
|
14782
15119
|
if (value.type === "PsiTerm") {
|
|
14783
15120
|
const features = value.value.features;
|
|
14784
15121
|
return {
|
|
@@ -14864,7 +15201,10 @@ function TermDtoFromApiToFront(dto) {
|
|
|
14864
15201
|
sortName: dto.sort_name ?? void 0,
|
|
14865
15202
|
displayName: dto.display_name ?? void 0,
|
|
14866
15203
|
referencedTerms,
|
|
14867
|
-
origin: dto.origin
|
|
15204
|
+
origin: dto.origin,
|
|
15205
|
+
// `derived_by` is a uuid or `null`; the shipped field is `string |
|
|
15206
|
+
// undefined`, so a null collapses rather than travelling as a third state.
|
|
15207
|
+
...dto.derived_by == null ? {} : { derivedBy: dto.derived_by }
|
|
14868
15208
|
};
|
|
14869
15209
|
}
|
|
14870
15210
|
function WitnessProofDtoFromApiToFront(dto) {
|
|
@@ -14893,9 +15233,21 @@ function CreateTermRequestFromFrontToApi(model) {
|
|
|
14893
15233
|
return {
|
|
14894
15234
|
sort_id: model.sortId,
|
|
14895
15235
|
owner_id: model.ownerId,
|
|
14896
|
-
features: featuresFromFrontToApi(model.features)
|
|
15236
|
+
features: featuresFromFrontToApi(model.features),
|
|
15237
|
+
...model.id === void 0 ? {} : { id: model.id }
|
|
14897
15238
|
};
|
|
14898
15239
|
}
|
|
15240
|
+
function CreateTermInputFromFrontToApi(model) {
|
|
15241
|
+
if ("sortName" in model) {
|
|
15242
|
+
return {
|
|
15243
|
+
sort_name: model.sortName,
|
|
15244
|
+
owner_id: model.ownerId,
|
|
15245
|
+
features: featuresFromFrontToApi(model.features),
|
|
15246
|
+
...model.id === void 0 ? {} : { id: model.id }
|
|
15247
|
+
};
|
|
15248
|
+
}
|
|
15249
|
+
return CreateTermRequestFromFrontToApi(model);
|
|
15250
|
+
}
|
|
14899
15251
|
function UpdateTermRequestFromFrontToApi(model) {
|
|
14900
15252
|
return {
|
|
14901
15253
|
features: featuresFromFrontToApi(model.features)
|
|
@@ -14903,15 +15255,34 @@ function UpdateTermRequestFromFrontToApi(model) {
|
|
|
14903
15255
|
}
|
|
14904
15256
|
function BulkAddTermsRequestFromFrontToApi(model) {
|
|
14905
15257
|
return {
|
|
14906
|
-
terms: model.terms.map(
|
|
15258
|
+
terms: model.terms.map(CreateTermInputFromFrontToApi),
|
|
15259
|
+
dry_run: model.dryRun,
|
|
15260
|
+
partial: model.partial
|
|
14907
15261
|
};
|
|
14908
15262
|
}
|
|
14909
15263
|
function BulkAddTermsResponseFromApiToFront(dto) {
|
|
14910
15264
|
return {
|
|
14911
|
-
|
|
15265
|
+
// `term_ids` is absent on a dry run and shorter than the request under
|
|
15266
|
+
// `partial`, so it is optional on the shipped type rather than defaulted to
|
|
15267
|
+
// an empty array — an empty array would read as "nothing was created", and
|
|
15268
|
+
// a dry run over a clean batch creates nothing while reporting a non-zero
|
|
15269
|
+
// `termsAdded`. The two facts are different and both matter.
|
|
15270
|
+
...dto.term_ids ? { termIds: dto.term_ids } : {},
|
|
15271
|
+
termsAdded: dto.terms_added,
|
|
15272
|
+
dryRun: dto.dry_run,
|
|
15273
|
+
...dto.refused === void 0 || dto.refused === null ? {} : { refused: dto.refused },
|
|
15274
|
+
...dto.errors ? { errors: dto.errors.map(BulkRowRefusalFromApiToFront) } : {},
|
|
15275
|
+
...dto.coreferenced_term_ids ? { coreferencedTermIds: dto.coreferenced_term_ids } : {},
|
|
14912
15276
|
processingTimeMs: dto.processing_time_ms
|
|
14913
15277
|
};
|
|
14914
15278
|
}
|
|
15279
|
+
function BulkRowRefusalFromApiToFront(dto) {
|
|
15280
|
+
return {
|
|
15281
|
+
index: dto.index,
|
|
15282
|
+
message: dto.message,
|
|
15283
|
+
...dto.feature ? { feature: dto.feature } : {}
|
|
15284
|
+
};
|
|
15285
|
+
}
|
|
14915
15286
|
function ClearTermsResponseFromApiToFront(dto) {
|
|
14916
15287
|
return {
|
|
14917
15288
|
message: dto.message,
|
|
@@ -14956,7 +15327,12 @@ function ValidatedUnifyResponseFromApiToFront(dto) {
|
|
|
14956
15327
|
function TermListResponseFromApiToFront(dto) {
|
|
14957
15328
|
return {
|
|
14958
15329
|
terms: dto.terms.map(TermDtoFromApiToFront),
|
|
14959
|
-
count: dto.count
|
|
15330
|
+
count: dto.count,
|
|
15331
|
+
// `total` is `null` on a route that does not page, and `note` is `null`
|
|
15332
|
+
// whenever the engine has no remark. Both collapse to absent so a consumer
|
|
15333
|
+
// tests one thing — presence — rather than two.
|
|
15334
|
+
...dto.total == null ? {} : { total: dto.total },
|
|
15335
|
+
...dto.note == null ? {} : { note: dto.note }
|
|
14960
15336
|
};
|
|
14961
15337
|
}
|
|
14962
15338
|
function TermReferrerDtoFromApiToFront(dto) {
|
|
@@ -15073,10 +15449,9 @@ var TermsClient = class {
|
|
|
15073
15449
|
* ```
|
|
15074
15450
|
*/
|
|
15075
15451
|
async createTerm(request, requestOptions) {
|
|
15076
|
-
const wireRequest =
|
|
15077
|
-
...request,
|
|
15078
|
-
|
|
15079
|
-
});
|
|
15452
|
+
const wireRequest = CreateTermInputFromFrontToApi(
|
|
15453
|
+
"sortName" in request ? { ...request, features: convertFeatures(request.features) } : { ...request, features: convertFeatures(request.features) }
|
|
15454
|
+
);
|
|
15080
15455
|
const response = await this.api.addTerm(wireRequest, toRequestParams(requestOptions));
|
|
15081
15456
|
return TermResponseFromApiToFront(response.data);
|
|
15082
15457
|
}
|
|
@@ -15251,13 +15626,91 @@ var TermsClient = class {
|
|
|
15251
15626
|
}
|
|
15252
15627
|
}
|
|
15253
15628
|
/**
|
|
15254
|
-
*
|
|
15629
|
+
* Create many terms in one request, all-or-nothing by default.
|
|
15630
|
+
*
|
|
15631
|
+
* @param request - The rows, plus `dryRun` to check without writing and
|
|
15632
|
+
* `partial` to keep the rows that passed. Features may be plain JS values
|
|
15633
|
+
* or `Value.*` output.
|
|
15634
|
+
* @param requestOptions - Per-call request options.
|
|
15635
|
+
* @returns What was written (or, on a dry run, what WOULD be written):
|
|
15636
|
+
* `termsAdded`, `termIds` on a real write, `errors` beside them under
|
|
15637
|
+
* `partial`, `coreferencedTermIds` on a dry run.
|
|
15638
|
+
* @throws {@link BulkRefusedError} 422 when the batch was refused and
|
|
15639
|
+
* NOTHING was written. Read `error.rows` — one entry per refused row, each
|
|
15640
|
+
* naming its `index` in `request.terms`. This is the answer for a refused
|
|
15641
|
+
* default batch, for a refused dry run, and for a `partial` batch in which
|
|
15642
|
+
* every row was refused.
|
|
15643
|
+
* @throws {@link ApiError} 409 when the end-of-batch constraint propagation
|
|
15644
|
+
* refuses the batch as a whole. That verdict cannot be attributed to a row,
|
|
15645
|
+
* so `partial` does not split it.
|
|
15646
|
+
*
|
|
15647
|
+
* @remarks
|
|
15648
|
+
* **Serialization format: Tagged (`ValueDto`).** Plain feature values are
|
|
15649
|
+
* converted exactly as {@link TermsClient.createTerm} converts them.
|
|
15650
|
+
*
|
|
15651
|
+
* There are three outcomes, and they are distinguishable without reading a
|
|
15652
|
+
* status code:
|
|
15653
|
+
*
|
|
15654
|
+
* 1. **A clean write** — `201`. `termIds` holds one id per request row, in
|
|
15655
|
+
* request order; `termsAdded` equals its length; `errors` is absent.
|
|
15656
|
+
* Measured 2026-09-18, a 2-row clean batch:
|
|
15657
|
+
* `{"terms_added":2,"term_ids":["3d936f37-…","f4a77a0b-…"],
|
|
15658
|
+
* "processing_time_ms":38,"dry_run":false}`.
|
|
15659
|
+
* 2. **A partial write** — `201`, and only with `partial: true`. Some rows
|
|
15660
|
+
* landed. `termIds` holds one id per ACCEPTED row, so it is SHORTER than
|
|
15661
|
+
* `request.terms`, and `errors` sits beside it naming the refused ones.
|
|
15662
|
+
* Map a refusal back with `errors[].index`, never with a position in
|
|
15663
|
+
* `termIds`. Measured, a 2-row batch whose second row violates a declared
|
|
15664
|
+
* range:
|
|
15665
|
+
* `{"terms_added":1,"term_ids":["d6ba85e6-…"],"errors":[{"index":1,
|
|
15666
|
+
* "feature":"price","message":"Constraint violation: Feature 'price'
|
|
15667
|
+
* value violates its declared range/constraint"}],"refused":1,
|
|
15668
|
+
* "processing_time_ms":37,"dry_run":false}`.
|
|
15669
|
+
* 3. **A refusal** — `422`, thrown as {@link BulkRefusedError}. Nothing was
|
|
15670
|
+
* written. Measured, the same bad batch WITHOUT `partial`:
|
|
15671
|
+
* `{"code":"bulk_refused","message":"1 of 2 entries were refused; the
|
|
15672
|
+
* whole batch was refused and nothing was written","errors":[{"index":1,
|
|
15673
|
+
* "feature":"price","message":"Constraint violation: …"}]}`. With
|
|
15674
|
+
* `partial: true` and BOTH rows bad, the same `422`:
|
|
15675
|
+
* `"2 of 2 entries were refused; the whole batch was refused and nothing
|
|
15676
|
+
* was written"`.
|
|
15677
|
+
*
|
|
15678
|
+
* **`dryRun` runs every check and writes nothing**, and it answers in the
|
|
15679
|
+
* same two shapes. A clean dry run is `200` and reports NO `termIds` —
|
|
15680
|
+
* measured:
|
|
15681
|
+
* `{"terms_added":2,"coreferenced_term_ids":[],"processing_time_ms":0,
|
|
15682
|
+
* "dry_run":true}`. The absence is deliberate: the rollback discarded every
|
|
15683
|
+
* id it minted, a later real write mints different ones, so the vector would
|
|
15684
|
+
* name nothing. The ids that DO outlive a dry run are the existing entities
|
|
15685
|
+
* a `@key` coreference would have merged into, and they come back as
|
|
15686
|
+
* `coreferencedTermIds`. A dry run over a BAD batch throws
|
|
15687
|
+
* {@link BulkRefusedError} with the same per-row refusals a real write throws
|
|
15688
|
+
* — measured, identical body to outcome 3 — so a caller can validate an
|
|
15689
|
+
* import with one call and never touch the store.
|
|
15690
|
+
*
|
|
15691
|
+
* @example
|
|
15692
|
+
* ```typescript
|
|
15693
|
+
* // Validate an import without writing.
|
|
15694
|
+
* try {
|
|
15695
|
+
* const check = await client.terms.bulkCreateTerms({ terms: rows, dryRun: true });
|
|
15696
|
+
* console.log(`${check.termsAdded} rows would be written`); // no check.termIds
|
|
15697
|
+
* } catch (e) {
|
|
15698
|
+
* if (e instanceof BulkRefusedError) {
|
|
15699
|
+
* for (const row of e.rows) console.error(`row ${row.index}: ${row.message}`);
|
|
15700
|
+
* }
|
|
15701
|
+
* }
|
|
15255
15702
|
*
|
|
15256
|
-
*
|
|
15257
|
-
*
|
|
15703
|
+
* // Write what passes, and report what did not.
|
|
15704
|
+
* const result = await client.terms.bulkCreateTerms({ terms: rows, partial: true });
|
|
15705
|
+
* console.log(`${result.termsAdded} written`, result.termIds);
|
|
15706
|
+
* for (const bad of result.errors ?? []) {
|
|
15707
|
+
* console.warn(`row ${bad.index} (${bad.feature}): ${bad.message}`);
|
|
15708
|
+
* }
|
|
15709
|
+
* ```
|
|
15258
15710
|
*/
|
|
15259
15711
|
async bulkCreateTerms(request, requestOptions) {
|
|
15260
15712
|
const wireRequest = BulkAddTermsRequestFromFrontToApi({
|
|
15713
|
+
...request,
|
|
15261
15714
|
terms: request.terms.map((t) => ({
|
|
15262
15715
|
...t,
|
|
15263
15716
|
features: convertFeatures(t.features)
|
|
@@ -15267,34 +15720,66 @@ var TermsClient = class {
|
|
|
15267
15720
|
return BulkAddTermsResponseFromApiToFront(response.data);
|
|
15268
15721
|
}
|
|
15269
15722
|
/**
|
|
15270
|
-
* List
|
|
15271
|
-
*
|
|
15272
|
-
* @param query - Optional paging and sort filter. Omit for every term.
|
|
15273
|
-
* @returns The list of terms with total count.
|
|
15274
|
-
* @throws {ApiError} If the request fails.
|
|
15723
|
+
* List one page of the tenant's terms, and say how many there are.
|
|
15275
15724
|
*
|
|
15276
|
-
* @
|
|
15277
|
-
*
|
|
15278
|
-
*
|
|
15279
|
-
*
|
|
15280
|
-
*
|
|
15281
|
-
*
|
|
15282
|
-
*
|
|
15283
|
-
*
|
|
15284
|
-
*
|
|
15285
|
-
*
|
|
15286
|
-
*
|
|
15287
|
-
*
|
|
15288
|
-
*
|
|
15289
|
-
*
|
|
15725
|
+
* @param query - Paging, the sort filter, and `includeDerived`. Omit for
|
|
15726
|
+
* every term.
|
|
15727
|
+
* @param requestOptions - Per-call request options.
|
|
15728
|
+
* @returns The page in `terms`, its length in `count`, the size of the whole
|
|
15729
|
+
* answer in `total`, and an engine remark in `note`.
|
|
15730
|
+
* @throws {@link ApiError} If the request fails.
|
|
15731
|
+
*
|
|
15732
|
+
* @remarks
|
|
15733
|
+
* **Serialization format: Tagged (`ValueDto`).** Terms are enriched with sort
|
|
15734
|
+
* names, display names, and referenced-term summaries. Requires the
|
|
15735
|
+
* `X-Tenant-Id` header, which the client configuration sets.
|
|
15736
|
+
*
|
|
15737
|
+
* **Page off `total`, not `count`.** `count` is this page's length and
|
|
15738
|
+
* nothing else. Measured 2026-09-18 on a tenant holding 3 terms:
|
|
15739
|
+
* `GET /api/v1/terms?limit=1&offset=2` answered
|
|
15740
|
+
* `{"terms":[…one…],"count":1,"total":3}`. `total` is counted before the
|
|
15741
|
+
* window, so it is the number to compare an offset against.
|
|
15742
|
+
*
|
|
15743
|
+
* **When the page is empty, read `note` before you report "no results".**
|
|
15744
|
+
* The route neither chains nor persists conclusions, so for a sort whose
|
|
15745
|
+
* members exist only by derivation the honest answer in a freshly started
|
|
15746
|
+
* process is zero rows — and `total: 0` reads like an authoritative "no
|
|
15747
|
+
* members", which is false. Measured 2026-09-18, a tenant with `widget`,
|
|
15748
|
+
* subsort `premium_widget`, one `widget` fact and the rule
|
|
15749
|
+
* `widget(name: ?N) → premium_widget(name: ?N)`:
|
|
15750
|
+
* `GET /api/v1/terms?sort_name=premium_widget` answered
|
|
15751
|
+
* `{"terms":[],"count":0,"total":0,"note":"`premium_widget` is a rule
|
|
15752
|
+
* conclusion sort: its members are derived … An empty answer here does not
|
|
15753
|
+
* mean the sort has no members."}`. Without the rule, the same empty answer
|
|
15754
|
+
* carried no note.
|
|
15755
|
+
*
|
|
15756
|
+
* `sortName` filters on the sort's committed name, and on that sort EXACTLY
|
|
15757
|
+
* — a member of a subsort is not answered. For a plugin-contributed sort the
|
|
15758
|
+
* committed name is the namespaced form (`plugin:<plugin-name>:<local>`),
|
|
15759
|
+
* which {@link SortDto.name} carries and {@link SortDto.pluginLocalName}
|
|
15760
|
+
* maps back to the name its author wrote.
|
|
15761
|
+
*
|
|
15762
|
+
* @example
|
|
15763
|
+
* ```typescript
|
|
15764
|
+
* const first = await client.terms.listTerms({ sortName: 'person', limit: 50 });
|
|
15765
|
+
* if (first.terms.length === 0 && first.note) console.info(first.note);
|
|
15766
|
+
* for (let offset = 50; offset < (first.total ?? 0); offset += 50) {
|
|
15767
|
+
* const page = await client.terms.listTerms({ sortName: 'person', limit: 50, offset });
|
|
15768
|
+
* // …
|
|
15769
|
+
* }
|
|
15290
15770
|
*
|
|
15291
|
-
* //
|
|
15292
|
-
* const
|
|
15771
|
+
* // The asserted rows alone — the engine includes conclusions by default.
|
|
15772
|
+
* const asserted = await client.terms.listTerms({ includeDerived: false });
|
|
15293
15773
|
* ```
|
|
15294
15774
|
*/
|
|
15295
15775
|
async listTerms(query, requestOptions) {
|
|
15296
15776
|
const response = await this.api.listTerms(
|
|
15297
|
-
query ? {
|
|
15777
|
+
query ? {
|
|
15778
|
+
limit: query.limit,
|
|
15779
|
+
offset: query.offset,
|
|
15780
|
+
sort_name: query.sortName,
|
|
15781
|
+
include_derived: query.includeDerived
|
|
15782
|
+
} : void 0,
|
|
15298
15783
|
toRequestParams(requestOptions)
|
|
15299
15784
|
);
|
|
15300
15785
|
return TermListResponseFromApiToFront(response.data);
|
|
@@ -15323,12 +15808,25 @@ var TermsClient = class {
|
|
|
15323
15808
|
// ─── Friendly Aliases ─────────────────────────────────────────────
|
|
15324
15809
|
/**
|
|
15325
15810
|
* Create multiple records in a single request.
|
|
15326
|
-
* Alias for {@link bulkCreateTerms}.
|
|
15811
|
+
* Alias for {@link TermsClient.bulkCreateTerms}.
|
|
15327
15812
|
*
|
|
15328
|
-
* @param request -
|
|
15329
|
-
* @
|
|
15813
|
+
* @param request - The rows, plus `dryRun` and `partial`.
|
|
15814
|
+
* @param requestOptions - Per-call request options.
|
|
15815
|
+
* @returns What was written, exactly as {@link TermsClient.bulkCreateTerms}
|
|
15816
|
+
* returns it.
|
|
15817
|
+
* @throws {@link BulkRefusedError} 422 when nothing was written.
|
|
15818
|
+
*
|
|
15819
|
+
* @remarks
|
|
15820
|
+
* **Serialization format: Tagged (`ValueDto`).** Same call, friendlier name
|
|
15821
|
+
* — read {@link TermsClient.bulkCreateTerms} for the three outcomes and for
|
|
15822
|
+
* what a dry run does and does not report.
|
|
15330
15823
|
*
|
|
15331
|
-
* @
|
|
15824
|
+
* @example
|
|
15825
|
+
* ```typescript
|
|
15826
|
+
* const result = await client.terms.createMany({ terms: rows, partial: true });
|
|
15827
|
+
* ```
|
|
15828
|
+
*
|
|
15829
|
+
* @see {@link TermsClient.bulkCreateTerms}
|
|
15332
15830
|
*/
|
|
15333
15831
|
async createMany(request, requestOptions) {
|
|
15334
15832
|
return this.bulkCreateTerms(request, requestOptions);
|
|
@@ -15359,7 +15857,7 @@ var TermsClient = class {
|
|
|
15359
15857
|
return paginateByOffset(
|
|
15360
15858
|
async (window, perCall) => {
|
|
15361
15859
|
const page = await this.listTerms({ ...query, ...window }, perCall);
|
|
15362
|
-
return { items: page.terms };
|
|
15860
|
+
return page.total === void 0 ? { items: page.terms } : { items: page.terms, total: page.total };
|
|
15363
15861
|
},
|
|
15364
15862
|
options,
|
|
15365
15863
|
requestOptions
|
|
@@ -15367,10 +15865,6 @@ var TermsClient = class {
|
|
|
15367
15865
|
}
|
|
15368
15866
|
};
|
|
15369
15867
|
function convertFeatures(features) {
|
|
15370
|
-
const values = Object.values(features);
|
|
15371
|
-
if (values.length > 0 && values.every(isTaggedValueDto)) {
|
|
15372
|
-
return features;
|
|
15373
|
-
}
|
|
15374
15868
|
return toTaggedFeatures(features);
|
|
15375
15869
|
}
|
|
15376
15870
|
|
|
@@ -15421,6 +15915,14 @@ function TermInputDtoFromFrontToApi(model) {
|
|
|
15421
15915
|
if ("termId" in model) {
|
|
15422
15916
|
return { term_id: model.termId };
|
|
15423
15917
|
}
|
|
15918
|
+
if ("designator" in model) {
|
|
15919
|
+
return {
|
|
15920
|
+
designator: {
|
|
15921
|
+
sort_name: model.designator.sortName,
|
|
15922
|
+
features: model.designator.features
|
|
15923
|
+
}
|
|
15924
|
+
};
|
|
15925
|
+
}
|
|
15424
15926
|
if ("sortId" in model) {
|
|
15425
15927
|
const inline = {
|
|
15426
15928
|
sort_id: model.sortId,
|
|
@@ -15493,6 +15995,7 @@ function ProofDtoFromApiToFront(dto) {
|
|
|
15493
15995
|
kind: dto.kind,
|
|
15494
15996
|
goalTermId: dto.goal_term_id,
|
|
15495
15997
|
ruleTermId: dto.rule_term_id,
|
|
15998
|
+
ruleHeadTermId: dto.rule_head_term_id,
|
|
15496
15999
|
goalDisplay: dto.goal_display,
|
|
15497
16000
|
ruleLabel: dto.rule_label,
|
|
15498
16001
|
substitution: HomoiconicSubstitutionDtoFromApiToFront(dto.substitution),
|
|
@@ -15591,7 +16094,7 @@ function ForwardChainResponseFromApiToFront(dto) {
|
|
|
15591
16094
|
iterations: dto.iterations,
|
|
15592
16095
|
totalFacts: dto.total_facts,
|
|
15593
16096
|
materializationTimeMs: dto.materialization_time_ms,
|
|
15594
|
-
|
|
16097
|
+
keptCount: dto.kept_count,
|
|
15595
16098
|
provenanceTags: dto.provenance_tags?.map(ProvenanceTagDtoFromApiToFront)
|
|
15596
16099
|
};
|
|
15597
16100
|
}
|
|
@@ -16028,7 +16531,7 @@ function BackwardChainRequestFromFrontToApi(model) {
|
|
|
16028
16531
|
function ForwardChainRequestFromFrontToApi(model) {
|
|
16029
16532
|
return {
|
|
16030
16533
|
initial_facts: model.initialFacts?.map(TermInputDtoFromFrontToApi),
|
|
16031
|
-
|
|
16534
|
+
keep_derived: model.keepDerived,
|
|
16032
16535
|
enable_provenance_tags: model.enableProvenanceTags,
|
|
16033
16536
|
max_iterations: model.maxIterations,
|
|
16034
16537
|
max_facts: model.maxFacts,
|
|
@@ -16372,6 +16875,14 @@ var InferenceClient = class {
|
|
|
16372
16875
|
*
|
|
16373
16876
|
* The `timeout_ms` field on the request is a wall-clock timeout for the search.
|
|
16374
16877
|
* When it fires, the backend returns whatever solutions have been found so far.
|
|
16878
|
+
*
|
|
16879
|
+
* To join a proof node to the thing it proves, read `proof.goalTermId` — the
|
|
16880
|
+
* conclusion's term ID, which `client.terms.getTerm()` and
|
|
16881
|
+
* `client.query.findBySort()` both answer for. It is OPTIONAL: a goal proved
|
|
16882
|
+
* without a preceding forward chain materialises no conclusion, so the node
|
|
16883
|
+
* carries no id and `proof.goalDisplay` renders it instead. Do not read
|
|
16884
|
+
* `proof.ruleHeadTermId` as a fetchable id — it names the rule's instantiated
|
|
16885
|
+
* head, which the term routes refuse.
|
|
16375
16886
|
*/
|
|
16376
16887
|
async backwardChain(request, requestOptions) {
|
|
16377
16888
|
const wireRequest = {
|
|
@@ -16391,7 +16902,19 @@ var InferenceClient = class {
|
|
|
16391
16902
|
* Forward chaining starts from existing facts and applies rules to derive new facts,
|
|
16392
16903
|
* repeating until no more new facts can be derived (fixpoint) or limits are reached.
|
|
16393
16904
|
*
|
|
16394
|
-
*
|
|
16905
|
+
* `keepDerived: true` keeps the run's derivations RESIDENT so a later `MATCH`
|
|
16906
|
+
* reads them. A default run is rolled back, not merely unwritten — so this
|
|
16907
|
+
* route reports what it derived and leaves the store as it found it. Neither
|
|
16908
|
+
* setting is durable: to repair a tenant whose materialised set has drifted,
|
|
16909
|
+
* use OSFQL `CHAIN;` or {@link AdminClient.rebuildDerivedFacts}.
|
|
16910
|
+
*
|
|
16911
|
+
* `timeoutMs` is a server-side deadline in milliseconds, checked at every
|
|
16912
|
+
* fixpoint boundary and every rule application. Omitted means the engine's own
|
|
16913
|
+
* backstop (`OSFKB_FC_TIMEOUT_SECS`, 300 s by default); `0` opts out entirely.
|
|
16914
|
+
* The route answers `504` when the deadline passes, and the body says whether
|
|
16915
|
+
* a `keepDerived` run kept the partial derivation it had reached.
|
|
16916
|
+
*
|
|
16917
|
+
* @throws {ApiError} With status 504 when the derivation passed its deadline.
|
|
16395
16918
|
*/
|
|
16396
16919
|
async forwardChain(request, requestOptions) {
|
|
16397
16920
|
const wireRequest = {
|
|
@@ -16463,13 +16986,37 @@ var InferenceClient = class {
|
|
|
16463
16986
|
/**
|
|
16464
16987
|
* Run negation-as-failure (NAF) proof search.
|
|
16465
16988
|
*
|
|
16466
|
-
* @param request - NAF prove request.
|
|
16989
|
+
* @param request - NAF prove request. Each literal's `term` takes a
|
|
16990
|
+
* {@link psi} term or a wire {@link TermInputDto}, like every other
|
|
16991
|
+
* term-carrying method.
|
|
16467
16992
|
* @returns NAF proof result.
|
|
16468
16993
|
*
|
|
16994
|
+
* @remarks
|
|
16995
|
+
* Uses the untagged (homoiconic) serialization format: a literal's features
|
|
16996
|
+
* are plain scalars and `"?Var"` strings.
|
|
16997
|
+
*
|
|
16998
|
+
* @example
|
|
16999
|
+
* ```typescript
|
|
17000
|
+
* const result = await client.inference.nafProve({
|
|
17001
|
+
* literals: [
|
|
17002
|
+
* { term: psi('employee', { name: '?Name' }) },
|
|
17003
|
+
* { term: psi('senior_engineer', { name: '?Name' }), negated: true },
|
|
17004
|
+
* ],
|
|
17005
|
+
* maxSolutions: 10,
|
|
17006
|
+
* });
|
|
17007
|
+
* ```
|
|
17008
|
+
*
|
|
16469
17009
|
* @see proveWithNegation — friendlier alias for this method.
|
|
16470
17010
|
*/
|
|
16471
17011
|
async nafProve(request, requestOptions) {
|
|
16472
|
-
const
|
|
17012
|
+
const wireRequest = {
|
|
17013
|
+
...request,
|
|
17014
|
+
literals: request.literals?.map((literal) => ({
|
|
17015
|
+
...literal,
|
|
17016
|
+
term: convertTermArg(literal.term)
|
|
17017
|
+
}))
|
|
17018
|
+
};
|
|
17019
|
+
const response = await this.api.nafProve(NafProveRequestFromFrontToApi(wireRequest), toRequestParams(requestOptions));
|
|
16473
17020
|
return NafProveResponseFromApiToFront(response.data);
|
|
16474
17021
|
}
|
|
16475
17022
|
/**
|
|
@@ -16691,6 +17238,7 @@ function FindBySortRequestFromFrontToApi(model) {
|
|
|
16691
17238
|
sort_name: model.sortName ?? void 0,
|
|
16692
17239
|
filter: model.filter ?? void 0,
|
|
16693
17240
|
limit: model.limit ?? void 0,
|
|
17241
|
+
offset: model.offset ?? void 0,
|
|
16694
17242
|
include_derived: model.includeDerived
|
|
16695
17243
|
};
|
|
16696
17244
|
}
|
|
@@ -16831,11 +17379,97 @@ var QueryClient = class {
|
|
|
16831
17379
|
return response.data.results.map(TermDtoFromApiToFront);
|
|
16832
17380
|
}
|
|
16833
17381
|
/**
|
|
16834
|
-
*
|
|
17382
|
+
* Browse a sort and everything below it, one page at a time.
|
|
16835
17383
|
*
|
|
16836
|
-
* @param request -
|
|
16837
|
-
*
|
|
16838
|
-
* @
|
|
17384
|
+
* @param request - `sortId` (UUID) or `sortName`, an optional feature
|
|
17385
|
+
* `filter`, the `limit`/`offset` window, and `includeDerived`.
|
|
17386
|
+
* @param requestOptions - Per-call request options.
|
|
17387
|
+
* @returns The page in `terms`, its length in `count`, the size of the whole
|
|
17388
|
+
* answer in `total`, and an engine remark in `note`.
|
|
17389
|
+
* @throws {@link ApiError} 404 when no sort of that name is queryable for
|
|
17390
|
+
* the tenant.
|
|
17391
|
+
*
|
|
17392
|
+
* @remarks
|
|
17393
|
+
* **Serialization format: Tagged (`ValueDto`).** This is the polymorphic
|
|
17394
|
+
* browse: a query on a sort answers that sort AND every subsort of it, which
|
|
17395
|
+
* is what distinguishes it from {@link TermsClient.listTerms}'s exact
|
|
17396
|
+
* `sortName` filter.
|
|
17397
|
+
*
|
|
17398
|
+
* **Page off `total`.** `count` is this page's length. `total` is the number
|
|
17399
|
+
* of rows matched BEFORE `offset` and `limit`, counted rather than estimated.
|
|
17400
|
+
*
|
|
17401
|
+
* **A page is stable.** The answer is ordered by term id before the window
|
|
17402
|
+
* is applied, so page 2 neither repeats nor skips a row of page 1. Measured
|
|
17403
|
+
* 2026-09-18 against a 2-member sort:
|
|
17404
|
+
* `POST /api/v1/query/by-sort {"sort_name":"widget","include_derived":true}`
|
|
17405
|
+
* answered ids `829ef5dc-…` then `d4f7a4f8-…` with
|
|
17406
|
+
* `{"count":2,"total":2}`; the same request plus `{"limit":1,"offset":1}`
|
|
17407
|
+
* answered `d4f7a4f8-…` alone with `{"count":1,"total":2}` — the second row,
|
|
17408
|
+
* and the same total.
|
|
17409
|
+
*
|
|
17410
|
+
* **The route does not chain.** It answers the tenant's durable extension
|
|
17411
|
+
* UNIONED with the conclusions this process currently HOLDS. After a restart
|
|
17412
|
+
* the derived half is empty until something chains again. So when the page
|
|
17413
|
+
* is empty, read `note` rather than trusting `total: 0`: measured
|
|
17414
|
+
* 2026-09-18, a tenant with `widget`, subsort `premium_widget`, one `widget`
|
|
17415
|
+
* fact and the rule `widget(name: ?N) → premium_widget(name: ?N)`,
|
|
17416
|
+
* `{"sort_name":"premium_widget"}` answered
|
|
17417
|
+
* `{"terms":[],"count":0,"total":0,"note":"`premium_widget` is a rule
|
|
17418
|
+
* conclusion sort: its members are derived … Materialise them with OSFQL
|
|
17419
|
+
* CHAIN, POST /api/v1/inference/forward-chain, or POST
|
|
17420
|
+
* /api/v1/admin/derived-facts/rebuild/{tenant_id} … An empty answer here
|
|
17421
|
+
* does not mean the sort has no members."}`. Before the rule existed, the
|
|
17422
|
+
* same empty query carried no note.
|
|
17423
|
+
*
|
|
17424
|
+
* @example
|
|
17425
|
+
* ```typescript
|
|
17426
|
+
* const page = await client.query.findBySortPage({
|
|
17427
|
+
* sortName: 'sales_order',
|
|
17428
|
+
* includeDerived: true,
|
|
17429
|
+
* limit: 25,
|
|
17430
|
+
* offset: 0,
|
|
17431
|
+
* });
|
|
17432
|
+
* if (page.terms.length === 0 && page.note) console.info(page.note);
|
|
17433
|
+
* console.log(`${page.count} of ${page.total}`);
|
|
17434
|
+
* ```
|
|
17435
|
+
*
|
|
17436
|
+
* @see {@link QueryClient.findBySort} — the deprecated array-returning form.
|
|
17437
|
+
*/
|
|
17438
|
+
async findBySortPage(request, requestOptions) {
|
|
17439
|
+
const response = await this.api.findBySort(
|
|
17440
|
+
FindBySortRequestFromFrontToApi(request),
|
|
17441
|
+
toRequestParams(requestOptions)
|
|
17442
|
+
);
|
|
17443
|
+
return TermListResponseFromApiToFront(response.data);
|
|
17444
|
+
}
|
|
17445
|
+
/**
|
|
17446
|
+
* Browse a sort and everything below it, discarding the envelope.
|
|
17447
|
+
*
|
|
17448
|
+
* @deprecated Use {@link QueryClient.findBySortPage}, which returns the
|
|
17449
|
+
* engine's envelope. This method drops `count`, `total` and `note`, so a
|
|
17450
|
+
* caller cannot tell a full answer from a truncated one, cannot page, and
|
|
17451
|
+
* reads an empty array for a rule-conclusion sort with no way to see the
|
|
17452
|
+
* engine's explanation. It is kept so 1.27 callers keep compiling.
|
|
17453
|
+
*
|
|
17454
|
+
* @param request - `sortId` or `sortName`, an optional feature `filter`, the
|
|
17455
|
+
* `limit`/`offset` window, and `includeDerived`.
|
|
17456
|
+
* @param requestOptions - Per-call request options.
|
|
17457
|
+
* @returns The page's terms alone.
|
|
17458
|
+
* @throws {@link ApiError} 404 when no sort of that name is queryable for
|
|
17459
|
+
* the tenant.
|
|
17460
|
+
*
|
|
17461
|
+
* @remarks
|
|
17462
|
+
* **Serialization format: Tagged (`ValueDto`).** Identical request,
|
|
17463
|
+
* identical rows, identical ordering — see
|
|
17464
|
+
* {@link QueryClient.findBySortPage} for the measured ordering and paging
|
|
17465
|
+
* contract. The only difference is what is thrown away.
|
|
17466
|
+
*
|
|
17467
|
+
* @example
|
|
17468
|
+
* ```typescript
|
|
17469
|
+
* const terms = await client.query.findBySort({ sortName: 'sales_order' });
|
|
17470
|
+
* ```
|
|
17471
|
+
*
|
|
17472
|
+
* @see {@link QueryClient.findBySortPage}
|
|
16839
17473
|
*/
|
|
16840
17474
|
async findBySort(request, requestOptions) {
|
|
16841
17475
|
const response = await this.api.findBySort(FindBySortRequestFromFrontToApi(request), toRequestParams(requestOptions));
|
|
@@ -16943,10 +17577,6 @@ var QueryClient = class {
|
|
|
16943
17577
|
}
|
|
16944
17578
|
};
|
|
16945
17579
|
function convertPattern(pattern) {
|
|
16946
|
-
const values = Object.values(pattern.features);
|
|
16947
|
-
if (values.length > 0 && values.every(isTaggedValueDto)) {
|
|
16948
|
-
return pattern;
|
|
16949
|
-
}
|
|
16950
17580
|
return {
|
|
16951
17581
|
sortId: pattern.sortId,
|
|
16952
17582
|
features: toTaggedFeatures(pattern.features)
|
|
@@ -31397,6 +32027,11 @@ function OsfqlValueFromApiToFront(value) {
|
|
|
31397
32027
|
return typeof payload === "string" ? { type: "string", value: payload } : void 0;
|
|
31398
32028
|
case "boolean":
|
|
31399
32029
|
return typeof payload === "boolean" ? { type: "boolean", value: payload } : void 0;
|
|
32030
|
+
// The engine's own RFC 3339 rendering, passed through as the string it is.
|
|
32031
|
+
// Parsing it to a `Date` here would reformat a response, which this layer
|
|
32032
|
+
// never does — and would lose the offset the engine chose to send.
|
|
32033
|
+
case "datetime":
|
|
32034
|
+
return typeof payload === "string" ? { type: "datetime", value: payload } : void 0;
|
|
31400
32035
|
case "term_ref":
|
|
31401
32036
|
return typeof payload === "string" ? { type: "term_ref", value: payload } : void 0;
|
|
31402
32037
|
case "list": {
|
|
@@ -31530,6 +32165,7 @@ function OsfqlCatalogEntryFromApiToFront(dto) {
|
|
|
31530
32165
|
syntax: dto.syntax,
|
|
31531
32166
|
examples: dto.examples,
|
|
31532
32167
|
risk: dto.risk,
|
|
32168
|
+
mutates: dto.mutates,
|
|
31533
32169
|
execution: OsfqlCatalogExecutionFromApiToFront(dto.execution),
|
|
31534
32170
|
uiAffinity: {
|
|
31535
32171
|
display: dto.ui_affinity.display ?? void 0,
|
|
@@ -31548,6 +32184,74 @@ function OsfqlCatalogExecutionFromApiToFront(dto) {
|
|
|
31548
32184
|
if (dto === "PlanOnly") return { status: "planOnly" };
|
|
31549
32185
|
return { status: "partial", note: dto.Partial };
|
|
31550
32186
|
}
|
|
32187
|
+
function OsfqlPreviewSortCountFromApiToFront(dto) {
|
|
32188
|
+
return { sort: dto.sort, count: dto.count };
|
|
32189
|
+
}
|
|
32190
|
+
function OsfqlPreviewAffectedFromApiToFront(dto) {
|
|
32191
|
+
const affected = { count: dto.count };
|
|
32192
|
+
if (dto.exact !== void 0) {
|
|
32193
|
+
affected.exact = dto.exact;
|
|
32194
|
+
}
|
|
32195
|
+
if (dto.sample_rows !== void 0) {
|
|
32196
|
+
const rows = OsfqlBindingsFromApiToFront(dto.sample_rows);
|
|
32197
|
+
if (rows === void 0) {
|
|
32198
|
+
return void 0;
|
|
32199
|
+
}
|
|
32200
|
+
affected.sampleRows = rows;
|
|
32201
|
+
}
|
|
32202
|
+
if (dto.by_sort !== void 0) {
|
|
32203
|
+
affected.bySort = dto.by_sort.map(OsfqlPreviewSortCountFromApiToFront);
|
|
32204
|
+
}
|
|
32205
|
+
return affected;
|
|
32206
|
+
}
|
|
32207
|
+
function OsfqlAtomicRefusalFromApiToFront(dto) {
|
|
32208
|
+
return { code: dto.code, message: dto.message };
|
|
32209
|
+
}
|
|
32210
|
+
function OsfqlPreviewStatementFromApiToFront(dto) {
|
|
32211
|
+
const statement = {
|
|
32212
|
+
index: dto.index,
|
|
32213
|
+
id: dto.id,
|
|
32214
|
+
statement: dto.statement,
|
|
32215
|
+
risk: dto.risk,
|
|
32216
|
+
mutates: dto.mutates,
|
|
32217
|
+
sorts: dto.sorts,
|
|
32218
|
+
source: dto.source
|
|
32219
|
+
};
|
|
32220
|
+
if (dto.affected !== void 0) {
|
|
32221
|
+
const affected = OsfqlPreviewAffectedFromApiToFront(dto.affected);
|
|
32222
|
+
if (affected === void 0) {
|
|
32223
|
+
return void 0;
|
|
32224
|
+
}
|
|
32225
|
+
statement.affected = affected;
|
|
32226
|
+
}
|
|
32227
|
+
if (dto.nested !== void 0) {
|
|
32228
|
+
const nested = [];
|
|
32229
|
+
for (const child of dto.nested) {
|
|
32230
|
+
const parsed = OsfqlPreviewStatementFromApiToFront(child);
|
|
32231
|
+
if (parsed === void 0) {
|
|
32232
|
+
return void 0;
|
|
32233
|
+
}
|
|
32234
|
+
nested.push(parsed);
|
|
32235
|
+
}
|
|
32236
|
+
statement.nested = nested;
|
|
32237
|
+
}
|
|
32238
|
+
return statement;
|
|
32239
|
+
}
|
|
32240
|
+
function OsfqlPreviewResponseFromApiToFront(dto) {
|
|
32241
|
+
const statements = [];
|
|
32242
|
+
for (const entry of dto.statements) {
|
|
32243
|
+
const parsed = OsfqlPreviewStatementFromApiToFront(entry);
|
|
32244
|
+
if (parsed === void 0) {
|
|
32245
|
+
return void 0;
|
|
32246
|
+
}
|
|
32247
|
+
statements.push(parsed);
|
|
32248
|
+
}
|
|
32249
|
+
const response = { mutates: dto.mutates, statements };
|
|
32250
|
+
if (dto.atomic_refusal !== void 0) {
|
|
32251
|
+
response.atomicRefusal = dto.atomic_refusal ? OsfqlAtomicRefusalFromApiToFront(dto.atomic_refusal) : dto.atomic_refusal;
|
|
32252
|
+
}
|
|
32253
|
+
return response;
|
|
32254
|
+
}
|
|
31551
32255
|
|
|
31552
32256
|
// src/resources/osfql.ts
|
|
31553
32257
|
var OsfqlClient = class {
|
|
@@ -31623,6 +32327,107 @@ var OsfqlClient = class {
|
|
|
31623
32327
|
}
|
|
31624
32328
|
return parsed;
|
|
31625
32329
|
}
|
|
32330
|
+
/**
|
|
32331
|
+
* Preview an OSFQL program: what each statement would do, decided without
|
|
32332
|
+
* running anything.
|
|
32333
|
+
*
|
|
32334
|
+
* @param query - The OSFQL program text (one or more statements separated by `;`).
|
|
32335
|
+
* @param options - Optional request options. Only `atomic` changes the answer:
|
|
32336
|
+
* it decides whether `atomicRefusal` is reported. `reactive`, `maxRows` and
|
|
32337
|
+
* `timeoutMs` are accepted for wire parity with {@link execute} and are
|
|
32338
|
+
* ignored by the route.
|
|
32339
|
+
* @param requestOptions - Per-call transport options (timeout, signal, headers).
|
|
32340
|
+
* @returns The per-statement classification, the affected-row reports, and the
|
|
32341
|
+
* atomic refusal when there is one.
|
|
32342
|
+
* @throws {ApiError} If the program does not parse (HTTP 400, `OsfqlErrorResponse`),
|
|
32343
|
+
* or the request otherwise fails.
|
|
32344
|
+
* @throws {ReasoningLayerError} If a sample row carries a value that does not satisfy
|
|
32345
|
+
* the published {@link OsfqlValue} contract.
|
|
32346
|
+
*
|
|
32347
|
+
* @remarks
|
|
32348
|
+
* **Why call this instead of classifying the program client-side.** Three
|
|
32349
|
+
* answers only the engine's own parse can give:
|
|
32350
|
+
*
|
|
32351
|
+
* 1. **Per-statement `mutates`.** A client keeps no table of which
|
|
32352
|
+
* process-control statements write. Measured on the dev engine: `CHAIN` and
|
|
32353
|
+
* `RELEASE RESIDUATIONS` report `mutates: true`, while `MARK ?m1`, `CUT` and
|
|
32354
|
+
* `SPACE CREATE ?S` report `false` — all five are `risk: "process_control"`.
|
|
32355
|
+
* In the other direction, `MATCH customer(name: ?N) INSERT vip(name: ?N)`
|
|
32356
|
+
* is `risk: "read"` with `mutates: true`. Read `mutates`, never `risk`.
|
|
32357
|
+
* 2. **`affected.count` and `affected.sampleRows`.** Up to 10 rows, each
|
|
32358
|
+
* identifying itself with a `term_id` column and the declaring sort's
|
|
32359
|
+
* `@key` features, so a confirm dialog can say WHICH rows go, not just how
|
|
32360
|
+
* many. Counted by running a derived read-only `MATCH` against a clone of
|
|
32361
|
+
* the tenant. Verified: four destructive previews (`RETRACT`, `CLEAR FACTS`,
|
|
32362
|
+
* `DROP SORT`, `DEFINE`) left the tenant's fact count and its lattice
|
|
32363
|
+
* unchanged.
|
|
32364
|
+
* 3. **`atomicRefusal`.** The same `drop_sort_in_atomic_program` verdict
|
|
32365
|
+
* `POST /api/v1/osfql` would refuse the program with, answered before the
|
|
32366
|
+
* program runs.
|
|
32367
|
+
*
|
|
32368
|
+
* ⚠️ **The one thing a caller must NOT do: treat `affected.count` as the
|
|
32369
|
+
* number of rows the run will remove.** It is the reach of the statement's
|
|
32370
|
+
* PATTERN. For `MATCH customer(name: ?N, spend: ?S); IF ?S > 60 THEN RETRACT
|
|
32371
|
+
* customer(name: ?N);` over three customers of which one has `spend > 60`, the
|
|
32372
|
+
* nested `RETRACT` answers `count: 3` — the derived `MATCH customer(name: ?N)`
|
|
32373
|
+
* applies neither the `IF` condition nor the binding the earlier statement
|
|
32374
|
+
* gives `?N`. Present it as "up to N rows" for a nested statement or one that
|
|
32375
|
+
* reads a variable from an earlier statement.
|
|
32376
|
+
*
|
|
32377
|
+
* Two further measured facts. The top-level `mutates` DOES fold nested
|
|
32378
|
+
* statements now: the `IF` program above answers `true`, and so does an `IF`
|
|
32379
|
+
* whose only writing branch is the `ELSE` — the earlier defect where an `IF`
|
|
32380
|
+
* with a writing branch answered `false` is fixed. And `affected` being absent
|
|
32381
|
+
* is ambiguous: `RETRACT customer(tier: "bronze")` matching zero rows omits
|
|
32382
|
+
* the field entirely rather than answering `count: 0`, exactly as a read does.
|
|
32383
|
+
*
|
|
32384
|
+
* `statement.index` is a single counter over the whole program: an `IF` at
|
|
32385
|
+
* index 1 carries nested entries at index 2 and 3.
|
|
32386
|
+
*
|
|
32387
|
+
* Request serialization matches {@link execute} — `query` plus the optional
|
|
32388
|
+
* `atomic` / `reactive` / `max_rows` / `timeout_ms` keys. The program TEXT is
|
|
32389
|
+
* a string value, so the request bridge's snake_case pass does not touch it;
|
|
32390
|
+
* a `camelCase` feature name inside the query survives verbatim. Sample-row
|
|
32391
|
+
* KEYS are data and are not camelCased on the way back.
|
|
32392
|
+
*
|
|
32393
|
+
* @example
|
|
32394
|
+
* ```typescript
|
|
32395
|
+
* const preview = await client.osfql.preview(
|
|
32396
|
+
* 'MATCH customer(name: ?N); RETRACT customer(tier: "gold");'
|
|
32397
|
+
* );
|
|
32398
|
+
*
|
|
32399
|
+
* console.log(preview.mutates); // true
|
|
32400
|
+
* const writes = preview.statements.filter((s) => s.mutates);
|
|
32401
|
+
* console.log(writes[0].id); // "retract"
|
|
32402
|
+
* console.log(writes[0].risk); // "targeted_destructive"
|
|
32403
|
+
* console.log(writes[0].affected?.count); // 2
|
|
32404
|
+
* console.log(writes[0].affected?.sampleRows); // [{ term_id: …, tier: … }, …]
|
|
32405
|
+
*
|
|
32406
|
+
* // A DROP SORT cannot share an atomic program
|
|
32407
|
+
* const refused = await client.osfql.preview('DROP SORT widget; INSERT widget(label: "x");');
|
|
32408
|
+
* console.log(refused.atomicRefusal?.code); // "drop_sort_in_atomic_program"
|
|
32409
|
+
*
|
|
32410
|
+
* // Ask the same question without the atomic constraint
|
|
32411
|
+
* const loose = await client.osfql.preview(
|
|
32412
|
+
* 'DROP SORT widget; INSERT widget(label: "x");',
|
|
32413
|
+
* { atomic: false },
|
|
32414
|
+
* );
|
|
32415
|
+
* console.log(loose.atomicRefusal); // undefined
|
|
32416
|
+
* ```
|
|
32417
|
+
*/
|
|
32418
|
+
async preview(query, options, requestOptions) {
|
|
32419
|
+
const response = await this.api.previewOsfql(
|
|
32420
|
+
OsfqlRequestFromFrontToApi({ ...options, query }),
|
|
32421
|
+
toRequestParams(requestOptions)
|
|
32422
|
+
);
|
|
32423
|
+
const parsed = OsfqlPreviewResponseFromApiToFront(response.data);
|
|
32424
|
+
if (parsed === void 0) {
|
|
32425
|
+
throw new ReasoningLayerError(
|
|
32426
|
+
"osfql/preview returned an affected sample row that does not match the published OsfqlValue contract (expected tagged values with a lowercase `type` discriminator)"
|
|
32427
|
+
);
|
|
32428
|
+
}
|
|
32429
|
+
return parsed;
|
|
32430
|
+
}
|
|
31626
32431
|
/**
|
|
31627
32432
|
* Diagnose an OSFQL program for contradictions and inconsistencies.
|
|
31628
32433
|
*
|
|
@@ -35440,38 +36245,6 @@ var SolverClient = class {
|
|
|
35440
36245
|
}
|
|
35441
36246
|
};
|
|
35442
36247
|
|
|
35443
|
-
// src/normalizers/ontology-alignment.ts
|
|
35444
|
-
function AlignOntologyRequestFromFrontToApi(model) {
|
|
35445
|
-
return {
|
|
35446
|
-
domain_owl: model.domainOwl,
|
|
35447
|
-
targets: model.targets
|
|
35448
|
-
};
|
|
35449
|
-
}
|
|
35450
|
-
function AlignmentMatchDtoFromApiToFront(dto) {
|
|
35451
|
-
return {
|
|
35452
|
-
domainSort: dto.domain_sort,
|
|
35453
|
-
matchType: dto.match_type,
|
|
35454
|
-
targetCurie: dto.target_curie,
|
|
35455
|
-
targetLabel: dto.target_label
|
|
35456
|
-
};
|
|
35457
|
-
}
|
|
35458
|
-
function AlignmentConflictDtoFromApiToFront(dto) {
|
|
35459
|
-
return {
|
|
35460
|
-
domainSort: dto.domain_sort,
|
|
35461
|
-
targetA: dto.target_a,
|
|
35462
|
-
targetB: dto.target_b
|
|
35463
|
-
};
|
|
35464
|
-
}
|
|
35465
|
-
function AlignOntologyResponseFromApiToFront(dto) {
|
|
35466
|
-
return {
|
|
35467
|
-
conflicts: dto.conflicts.map(AlignmentConflictDtoFromApiToFront),
|
|
35468
|
-
domainSorts: dto.domain_sorts,
|
|
35469
|
-
mappingTtl: dto.mapping_ttl,
|
|
35470
|
-
matches: dto.matches.map(AlignmentMatchDtoFromApiToFront),
|
|
35471
|
-
targetSorts: dto.target_sorts
|
|
35472
|
-
};
|
|
35473
|
-
}
|
|
35474
|
-
|
|
35475
36248
|
// src/resources/ontology-alignment.ts
|
|
35476
36249
|
var OntologyAlignmentClient = class {
|
|
35477
36250
|
/** @internal */
|
|
@@ -38527,15 +39300,15 @@ function TemporalSeriesPointFromApiToFront(value) {
|
|
|
38527
39300
|
}
|
|
38528
39301
|
const bucketStart = asNumber(value["bucket_start"]);
|
|
38529
39302
|
const count = asNumber(value["count"]);
|
|
38530
|
-
const
|
|
38531
|
-
if (bucketStart === void 0 || count === void 0 ||
|
|
39303
|
+
const aggregate2 = asNumber(value["aggregate"]);
|
|
39304
|
+
if (bucketStart === void 0 || count === void 0 || aggregate2 === void 0) {
|
|
38532
39305
|
return void 0;
|
|
38533
39306
|
}
|
|
38534
39307
|
return {
|
|
38535
39308
|
bucketStart,
|
|
38536
39309
|
bucketEnd: asNumber(value["bucket_end"]),
|
|
38537
39310
|
count,
|
|
38538
|
-
aggregate,
|
|
39311
|
+
aggregate: aggregate2,
|
|
38539
39312
|
aggregateSecondary: asNumber(value["aggregate_secondary"])
|
|
38540
39313
|
};
|
|
38541
39314
|
}
|
|
@@ -42016,21 +42789,75 @@ var embeddings_exports = {};
|
|
|
42016
42789
|
// src/builders/value.ts
|
|
42017
42790
|
var Value = {
|
|
42018
42791
|
/**
|
|
42019
|
-
* Create a reference to another term by UUID
|
|
42792
|
+
* Create a reference to another stored term — by its UUID, or by the `@key`
|
|
42793
|
+
* values that name it.
|
|
42020
42794
|
*
|
|
42021
|
-
* @param
|
|
42022
|
-
*
|
|
42795
|
+
* @param target - The referenced term's UUID, or a {@link ReferenceDesignator}
|
|
42796
|
+
* naming it through the `@key` features of its sort.
|
|
42797
|
+
* @returns A tagged `ReferenceValue`: `{"type": "Reference", "value": "uuid"}`
|
|
42798
|
+
* for the id form, `{"type":"Reference","value":{"sort_name":…,"features":…}}`
|
|
42799
|
+
* for the designator form.
|
|
42800
|
+
* @throws {@link ValidationError} when the designator carries an empty
|
|
42801
|
+
* `sortName`, or names no feature — the engine refuses both, so the builder
|
|
42802
|
+
* refuses them before the round trip.
|
|
42023
42803
|
*
|
|
42024
42804
|
* @remarks
|
|
42025
|
-
* Serialization format: Tagged (ValueDto)
|
|
42805
|
+
* **Serialization format: Tagged (`ValueDto`).** Use with term CRUD, queries
|
|
42806
|
+
* and fuzzy operations. The homoiconic inference endpoints take the untagged
|
|
42807
|
+
* format and have no designator form.
|
|
42808
|
+
*
|
|
42809
|
+
* The two forms differ in what they can be used for:
|
|
42810
|
+
*
|
|
42811
|
+
* - The **UUID form** is what a read answers, and the only form that can
|
|
42812
|
+
* point at a term whose sort declares no `@key`.
|
|
42813
|
+
* - The **designator form** is WRITE-side only. It RESOLVES to a term that
|
|
42814
|
+
* already exists and never mints one: measured on 2026-09-18, writing
|
|
42815
|
+
* a `payment` whose `invoice` feature was
|
|
42816
|
+
* `Value.reference({ sortName: 'invoice', features: { number: { type: 'String', value: 'INV-1' } } })`
|
|
42817
|
+
* stored `{"type":"Reference","value":"4641cbcb-…"}` — the id of the single
|
|
42818
|
+
* existing invoice — and the invoice extent still held exactly one term. A
|
|
42819
|
+
* designator matching nothing is refused `422 no term of sort 'invoice'
|
|
42820
|
+
* carries @key 'number' = "INV-NOPE"; a reference designator names an
|
|
42821
|
+
* existing term, it never creates one`. The same refusal arrives from
|
|
42822
|
+
* `POST /api/v1/terms/bulk` as a `BulkRefusedError` naming the entry index.
|
|
42823
|
+
*
|
|
42824
|
+
* The designator's `features` keys are OSF feature names — user data, not
|
|
42825
|
+
* schema — and survive the request bridge verbatim, so a feature declared
|
|
42826
|
+
* `invoiceNumber` must be spelled `invoiceNumber` here. Sending the
|
|
42827
|
+
* snake_cased spelling is refused: `422 feature 'invoice_number' of sort
|
|
42828
|
+
* 'invoice' is not a @key feature; a designator addresses a term only through
|
|
42829
|
+
* its @key`.
|
|
42026
42830
|
*
|
|
42027
42831
|
* @example
|
|
42028
42832
|
* ```typescript
|
|
42833
|
+
* // By id:
|
|
42029
42834
|
* Value.reference("550e8400-e29b-41d4-a716-446655440000")
|
|
42835
|
+
*
|
|
42836
|
+
* // By @key, when the caller has the business key and not the UUID:
|
|
42837
|
+
* Value.reference({
|
|
42838
|
+
* sortName: 'invoice',
|
|
42839
|
+
* features: { number: { type: 'String', value: 'INV-1' } },
|
|
42840
|
+
* })
|
|
42030
42841
|
* ```
|
|
42031
42842
|
*/
|
|
42032
|
-
reference(
|
|
42033
|
-
|
|
42843
|
+
reference(target) {
|
|
42844
|
+
if (typeof target === "string") {
|
|
42845
|
+
return { type: "Reference", value: target };
|
|
42846
|
+
}
|
|
42847
|
+
if (target.sortName.length === 0) {
|
|
42848
|
+
throw new ValidationError(
|
|
42849
|
+
"Value.reference() expects a designator with a non-empty sortName"
|
|
42850
|
+
);
|
|
42851
|
+
}
|
|
42852
|
+
if (Object.keys(target.features).length === 0) {
|
|
42853
|
+
throw new ValidationError(
|
|
42854
|
+
`Value.reference() expects a designator naming at least one @key feature of sort "${target.sortName}"; the engine refuses an empty designator`
|
|
42855
|
+
);
|
|
42856
|
+
}
|
|
42857
|
+
return {
|
|
42858
|
+
type: "Reference",
|
|
42859
|
+
value: { sortName: target.sortName, features: target.features }
|
|
42860
|
+
};
|
|
42034
42861
|
},
|
|
42035
42862
|
/**
|
|
42036
42863
|
* Create a reference to a sort by UUID.
|
|
@@ -42769,6 +43596,7 @@ var FuzzyShape = {
|
|
|
42769
43596
|
};
|
|
42770
43597
|
|
|
42771
43598
|
// src/builders/psi.ts
|
|
43599
|
+
var NEGATION_SORT = "negation";
|
|
42772
43600
|
function psi(sortOrName, features) {
|
|
42773
43601
|
if (typeof sortOrName === "string") {
|
|
42774
43602
|
if (!features) {
|
|
@@ -42797,6 +43625,38 @@ function bind(name, term) {
|
|
|
42797
43625
|
}
|
|
42798
43626
|
return { ...term, binding: name };
|
|
42799
43627
|
}
|
|
43628
|
+
function not(clause) {
|
|
43629
|
+
if ("sortName" in clause && clause.sortName === NEGATION_SORT) {
|
|
43630
|
+
throw new ValidationError(
|
|
43631
|
+
`not() cannot negate a negation: the chainers read the "${NEGATION_SORT}" carrier one clause deep, so a nested negation is accepted and derives nothing`
|
|
43632
|
+
);
|
|
43633
|
+
}
|
|
43634
|
+
return {
|
|
43635
|
+
__psiTerm: true,
|
|
43636
|
+
sortName: NEGATION_SORT,
|
|
43637
|
+
features: { clause }
|
|
43638
|
+
};
|
|
43639
|
+
}
|
|
43640
|
+
function aggregate(spec) {
|
|
43641
|
+
if (spec.groupBy.length === 0) {
|
|
43642
|
+
throw new ValidationError(
|
|
43643
|
+
"aggregate() expects at least one groupBy feature; an aggregator with no group key has no group to fold into"
|
|
43644
|
+
);
|
|
43645
|
+
}
|
|
43646
|
+
const blank = spec.groupBy.find((name) => name.trim().length === 0);
|
|
43647
|
+
if (blank !== void 0) {
|
|
43648
|
+
throw new ValidationError("aggregate() expects every groupBy entry to be a feature name");
|
|
43649
|
+
}
|
|
43650
|
+
if (spec.target.trim().length === 0) {
|
|
43651
|
+
throw new ValidationError("aggregate() expects a target feature name");
|
|
43652
|
+
}
|
|
43653
|
+
if (spec.groupBy.includes(spec.target)) {
|
|
43654
|
+
throw new ValidationError(
|
|
43655
|
+
`aggregate() cannot aggregate "${spec.target}" and also group by it`
|
|
43656
|
+
);
|
|
43657
|
+
}
|
|
43658
|
+
return { groupBy: [...spec.groupBy], op: spec.op, target: spec.target };
|
|
43659
|
+
}
|
|
42800
43660
|
function constrained(name, constraint) {
|
|
42801
43661
|
return { __constrainedVar: true, name, constraint };
|
|
42802
43662
|
}
|
|
@@ -43468,6 +44328,6 @@ var Flow = {
|
|
|
43468
44328
|
}
|
|
43469
44329
|
};
|
|
43470
44330
|
|
|
43471
|
-
export { ANY_ROLE, action_reviews_exports as ActionReviews, actions_exports as Actions, admin_exports as Admin, agui_exports as Agui, analysis_exports as Analysis, anonymization_exports as Anonymization, ApiError, audit_exports as Audit, AuthenticationError, authz_exports as Authz, BadRequestError, batch_exports as Batch, cdl_exports as CDL, causal_exports as Causal, chase_exports as Chase, cognitive_exports as Cognitive, coherence_exports as Coherence, collections_exports as Collections, communities_exports as Communities, compliance_exports as Compliance, compliance_markings_exports as ComplianceMarkings, conformal_exports as Conformal, conformance_exports as Conformance, connectors_exports as Connectors, Constraint, ConstraintViolationError, constraints_exports as Constraints, control_exports as Control, conversation_exports as Conversation, corpus_exports as Corpus, dl_exports as DL, demo_exports as Demo, discovery_exports as Discovery, document_check_exports as DocumentCheck, documents_exports as Documents, embeddings_exports as Embeddings, execution_exports as Execution, extract_exports as Extract, feasibility_exports as Feasibility, Flow, flow_networks_exports as FlowNetworks, ForbiddenError, forecast_exports as Forecast, functions_exports as Functions, fuzzy_exports as Fuzzy, FuzzyShape, generation_exports as Generation, Geometry, guardrail_exports as Guardrail, health_exports as Health, homoiconic_exports as Homoiconic, ilp_exports as ILP, image_extraction_exports as ImageExtraction, inference_exports as Inference, ingestion_exports as Ingestion, IngestionFailedError, IngestionSession, InternalServerError, LP, ltn_exports as LTN, marketplace_exports as Marketplace, namespaces_exports as Namespaces, NetworkError, neuro_symbolic_exports as NeuroSymbolic, NotFoundError, ontology_exports as Ontology, ontology_alignment_exports as OntologyAlignment, ontology_bridge_exports as OntologyBridge, ontology_export_exports as OntologyExport, ontology_facade_exports as OntologyFacade, operations_exports as Operations, optimize_exports as Optimize, osf_diff_exports as OsfDiff, osfql_exports as Osfql, oversight_exports as Oversight, plain_values_exports as PlainValues, preferences_exports as Preferences, proof_engine_exports as ProofEngine, property_graph_exports as PropertyGraph, query_exports as Query, rag_exports as RAG, RateLimitError, reasoning_exports as Reasoning, ReasoningLayerClient, ReasoningLayerError, research_exports as Research, reviews_exports as Reviews, reward_exports as Reward, row_exports as Row, SDK_VERSION, sat_exports as Sat, scenarios_exports as Scenarios, scheduling_exports as Scheduling, smt_exports as Smt, snapshots_exports as Snapshots, solver_exports as Solver, SortBuilder, sorts_exports as Sorts, sources_exports as Sources, spaces_exports as Spaces, sparql_exports as Sparql, speakers_exports as Speakers, speech_exports as Speech, statistical_exports as Statistical, streaming_exports as Streaming, subscriptions_exports as Subscriptions, synthetic_exports as Synthetic, temporal_exports as Temporal, terms_exports as Terms, thomas_exports as Thomas, TimeoutError, translation_exports as Translation, ui_exports as UI, utilities_exports as Utilities, ValidationError, Value, values_exports as Values, verification_exports as Verification, vision_exports as Vision, visualization_exports as Visualization, WebSocketClient, WebSocketConnection, webhook_actions_exports as WebhookActions, allen, bind, collect, constrained, discriminateFeatureValue, guard, isConstrainedPlainVar, isPsiTermInput, isTaggedValueDto, isUuid, paginateByOffset, paginateByPage, psi, toTaggedFeatures, toTaggedValue, toTermInputDto, toUntaggedFeatures, toUntaggedValue };
|
|
44331
|
+
export { ANY_ROLE, action_reviews_exports as ActionReviews, actions_exports as Actions, admin_exports as Admin, agui_exports as Agui, analysis_exports as Analysis, anonymization_exports as Anonymization, ApiError, audit_exports as Audit, AuthenticationError, authz_exports as Authz, BadRequestError, batch_exports as Batch, BulkRefusedError, cdl_exports as CDL, causal_exports as Causal, chase_exports as Chase, cognitive_exports as Cognitive, coherence_exports as Coherence, collections_exports as Collections, communities_exports as Communities, compliance_exports as Compliance, compliance_markings_exports as ComplianceMarkings, conformal_exports as Conformal, conformance_exports as Conformance, connectors_exports as Connectors, Constraint, ConstraintViolationError, constraints_exports as Constraints, control_exports as Control, conversation_exports as Conversation, corpus_exports as Corpus, dl_exports as DL, demo_exports as Demo, discovery_exports as Discovery, document_check_exports as DocumentCheck, documents_exports as Documents, embeddings_exports as Embeddings, execution_exports as Execution, extract_exports as Extract, feasibility_exports as Feasibility, Flow, flow_networks_exports as FlowNetworks, ForbiddenError, forecast_exports as Forecast, functions_exports as Functions, fuzzy_exports as Fuzzy, FuzzyShape, generation_exports as Generation, Geometry, guardrail_exports as Guardrail, health_exports as Health, homoiconic_exports as Homoiconic, ilp_exports as ILP, image_extraction_exports as ImageExtraction, inference_exports as Inference, ingestion_exports as Ingestion, IngestionFailedError, IngestionSession, InternalServerError, LP, ltn_exports as LTN, marketplace_exports as Marketplace, namespaces_exports as Namespaces, NetworkError, neuro_symbolic_exports as NeuroSymbolic, NotFoundError, ontology_exports as Ontology, ontology_alignment_exports as OntologyAlignment, ontology_bridge_exports as OntologyBridge, ontology_export_exports as OntologyExport, ontology_facade_exports as OntologyFacade, operations_exports as Operations, optimize_exports as Optimize, osf_diff_exports as OsfDiff, osfql_exports as Osfql, oversight_exports as Oversight, plain_values_exports as PlainValues, preferences_exports as Preferences, proof_engine_exports as ProofEngine, property_graph_exports as PropertyGraph, query_exports as Query, rag_exports as RAG, RateLimitError, reasoning_exports as Reasoning, ReasoningLayerClient, ReasoningLayerError, research_exports as Research, reviews_exports as Reviews, reward_exports as Reward, row_exports as Row, SDK_VERSION, sat_exports as Sat, scenarios_exports as Scenarios, scheduling_exports as Scheduling, smt_exports as Smt, snapshots_exports as Snapshots, solver_exports as Solver, SortBuilder, sorts_exports as Sorts, sources_exports as Sources, spaces_exports as Spaces, sparql_exports as Sparql, speakers_exports as Speakers, speech_exports as Speech, statistical_exports as Statistical, streaming_exports as Streaming, subscriptions_exports as Subscriptions, synthetic_exports as Synthetic, temporal_exports as Temporal, terms_exports as Terms, thomas_exports as Thomas, TimeoutError, translation_exports as Translation, ui_exports as UI, utilities_exports as Utilities, ValidationError, Value, values_exports as Values, verification_exports as Verification, vision_exports as Vision, visualization_exports as Visualization, WebSocketClient, WebSocketConnection, webhook_actions_exports as WebhookActions, aggregate, allen, bind, collect, constrained, discriminateFeatureValue, guard, isConstrainedPlainVar, isPsiTermInput, isTaggedValueDto, isUuid, not, paginateByOffset, paginateByPage, psi, toTaggedFeatures, toTaggedValue, toTermInputDto, toUntaggedFeatures, toUntaggedValue };
|
|
43472
44332
|
//# sourceMappingURL=index.js.map
|
|
43473
44333
|
//# sourceMappingURL=index.js.map
|