@rsconcept/rstool 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (84) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +150 -0
  3. package/dist/index.d.ts +15 -0
  4. package/dist/index.js +585 -0
  5. package/dist/index.js.map +1 -0
  6. package/dist/mappers/model-adapter.d.ts +27 -0
  7. package/dist/mappers/model-adapter.js +248 -0
  8. package/dist/mappers/model-adapter.js.map +1 -0
  9. package/dist/mappers/schema-adapter.d.ts +22 -0
  10. package/dist/mappers/schema-adapter.js +89 -0
  11. package/dist/mappers/schema-adapter.js.map +1 -0
  12. package/dist/mappers/types.d.ts +23 -0
  13. package/dist/mappers/types.js +22 -0
  14. package/dist/mappers/types.js.map +1 -0
  15. package/dist/models/analysis.d.ts +18 -0
  16. package/dist/models/analysis.js +1 -0
  17. package/dist/models/analysis.js.map +1 -0
  18. package/dist/models/common.d.ts +15 -0
  19. package/dist/models/common.js +12 -0
  20. package/dist/models/common.js.map +1 -0
  21. package/dist/models/constituenta.d.ts +38 -0
  22. package/dist/models/constituenta.js +1 -0
  23. package/dist/models/constituenta.js.map +1 -0
  24. package/dist/models/diagnostic.d.ts +17 -0
  25. package/dist/models/diagnostic.js +1 -0
  26. package/dist/models/diagnostic.js.map +1 -0
  27. package/dist/models/evaluation.d.ts +23 -0
  28. package/dist/models/evaluation.js +1 -0
  29. package/dist/models/evaluation.js.map +1 -0
  30. package/dist/models/index.d.ts +13 -0
  31. package/dist/models/index.js +491 -0
  32. package/dist/models/index.js.map +1 -0
  33. package/dist/models/model-value.d.ts +37 -0
  34. package/dist/models/model-value.js +1 -0
  35. package/dist/models/model-value.js.map +1 -0
  36. package/dist/models/rstool-agent.d.ts +36 -0
  37. package/dist/models/rstool-agent.js +480 -0
  38. package/dist/models/rstool-agent.js.map +1 -0
  39. package/dist/models/session.d.ts +29 -0
  40. package/dist/models/session.js +1 -0
  41. package/dist/models/session.js.map +1 -0
  42. package/dist/models/tool-contract.d.ts +33 -0
  43. package/dist/models/tool-contract.js +6 -0
  44. package/dist/models/tool-contract.js.map +1 -0
  45. package/dist/session/session-store.d.ts +26 -0
  46. package/dist/session/session-store.js +66 -0
  47. package/dist/session/session-store.js.map +1 -0
  48. package/dist/wrapper/client.d.ts +30 -0
  49. package/dist/wrapper/client.js +96 -0
  50. package/dist/wrapper/client.js.map +1 -0
  51. package/dist/wrapper/stdio-wrapper.d.ts +1 -0
  52. package/dist/wrapper/stdio-wrapper.js +679 -0
  53. package/dist/wrapper/stdio-wrapper.js.map +1 -0
  54. package/docs/CONSTITUENTA.md +55 -0
  55. package/docs/DIAGNOSTICS.md +125 -0
  56. package/docs/DOMAIN.md +89 -0
  57. package/docs/GRAMMAR-REF.md +98 -0
  58. package/docs/PORTAL-API.md +48 -0
  59. package/docs/README.md +15 -0
  60. package/docs/SYNTAX.md +139 -0
  61. package/docs/TYPIFICATION.md +79 -0
  62. package/package.json +76 -0
  63. package/skills/README.md +15 -0
  64. package/skills/rstool-helper/EXAMPLES.md +154 -0
  65. package/skills/rstool-helper/REFERENCE.md +169 -0
  66. package/skills/rstool-helper/SKILL.md +148 -0
  67. package/src/index.ts +43 -0
  68. package/src/mappers/model-adapter.ts +276 -0
  69. package/src/mappers/schema-adapter.ts +87 -0
  70. package/src/mappers/types.ts +35 -0
  71. package/src/models/analysis.ts +13 -0
  72. package/src/models/common.ts +17 -0
  73. package/src/models/constituenta.ts +35 -0
  74. package/src/models/diagnostic.ts +12 -0
  75. package/src/models/evaluation.ts +25 -0
  76. package/src/models/index.ts +33 -0
  77. package/src/models/model-value.ts +31 -0
  78. package/src/models/rstool-agent.test.ts +300 -0
  79. package/src/models/rstool-agent.ts +143 -0
  80. package/src/models/session.ts +22 -0
  81. package/src/models/tool-contract.ts +47 -0
  82. package/src/session/session-store.ts +81 -0
  83. package/src/wrapper/client.ts +116 -0
  84. package/src/wrapper/stdio-wrapper.ts +225 -0
@@ -0,0 +1,148 @@
1
+ ---
2
+ name: rstool-helper
3
+ description: RS language and @rsconcept/rstool for AI agents: incremental RSForm construction, formal definitions, typification, diagnostics, modeling, evaluation.
4
+ ---
5
+
6
+ # RS Language & rstool — Compact Guide for Agents
7
+
8
+ **RS language** is a formal scheme notation for concepts, relations, operations—extends FOL, core: membership `x∈y`; typification via set-theoretic/logical expressions.
9
+
10
+ **rstool** is the agent API for sessions, upserts, analysis, diagnostics, modeling/evaluation, (de)serialization.
11
+
12
+ - Library: `@rsconcept/rstool` (npm)
13
+ - Analyzer: `@rsconcept/domain` (installed automatically as a dependency)
14
+ - Language and API reference: bundled `docs/*.md` copied into this skill during installation
15
+
16
+ ## Installing This Skill
17
+
18
+ After installing the package in an agent project:
19
+
20
+ ```bash
21
+ npm install @rsconcept/rstool
22
+ ```
23
+
24
+ copy the skill into your agent host's skill directory.
25
+
26
+ Cursor per-project skills:
27
+
28
+ ```bash
29
+ mkdir -p .agents/skills
30
+ cp -R node_modules/@rsconcept/rstool/skills/rstool-helper .agents/skills/rstool-helper
31
+ cp -R node_modules/@rsconcept/rstool/docs .agents/skills/rstool-helper/docs
32
+ ```
33
+
34
+ PowerShell:
35
+
36
+ ```powershell
37
+ New-Item -ItemType Directory -Force .agents/skills
38
+ Copy-Item -Recurse -Force node_modules/@rsconcept/rstool/skills/rstool-helper .agents/skills/rstool-helper
39
+ Copy-Item -Recurse -Force node_modules/@rsconcept/rstool/docs .agents/skills/rstool-helper/docs
40
+ ```
41
+
42
+ This makes the skill self-contained for hosts that only read files under the skill directory.
43
+
44
+ ## Docs/Hints Reference
45
+
46
+ | Info | Location |
47
+ | :---------------------------------------- | :---------------------------------------------------------------- |
48
+ | rstool API, methods, error codes | [REFERENCE.md](REFERENCE.md) |
49
+ | Worked examples, common mistakes | [EXAMPLES.md](EXAMPLES.md) |
50
+ | Domain vocabulary (English) | `docs/DOMAIN.md` (bundled with the npm package) |
51
+ | Constituenta fields, validation, ordering | `docs/CONSTITUENTA.md` |
52
+ | RSLang syntax (operators, quantifiers) | `docs/SYNTAX.md` |
53
+ | Typification grades, radicals | `docs/TYPIFICATION.md` |
54
+ | Diagnostic code → fix table | `docs/DIAGNOSTICS.md` |
55
+ | Portal REST API (live data) | `docs/PORTAL-API.md` |
56
+ | Lezer grammar pointers | `docs/GRAMMAR-REF.md` (full grammar lives in `@rsconcept/domain`) |
57
+ | Code samples | [EXAMPLES.md](EXAMPLES.md), package README |
58
+
59
+ If you installed `@rsconcept/rstool` from npm, the source docs ship inside `node_modules/@rsconcept/rstool/docs/`; the install commands above copy them into this skill folder.
60
+
61
+ ## Protocol Summary
62
+
63
+ 1. **Start session**: `createSession`
64
+ 2. **Add bases/constants**: type `basic` (`X*`), `constant` (`C*`) — `definitionFormal: ''`
65
+ 3. **Add derived**: terms, axioms, etc.—only after dependencies present
66
+ 4. **Analyze scratch**: `analyzeExpression`
67
+ 5. **Process diagnostics**: check `analysis.diagnostics`/`listDiagnostics`; fix by range
68
+ 6. **Checkpoint**: `commitStep` (message optional)
69
+ 7. **Model values**: `setConstituentaValue` / `setConstituentaValues` for base bindings (`{0:"a",1:"b"}`) or structured values; `getModelState`
70
+ 8. **Evaluate**: `evaluateExpression` (scratch) or `evaluateConstituenta` / `recalculateModel` (stored definitions)
71
+ 9. **Export/import**: persist with `exportSession`/`importSession` (includes `state.model`)
72
+
73
+ **Clients**:
74
+
75
+ - Node: use `RSToolWrapperClient`
76
+ - Stdio process: `npx rstool-wrapper` — JSON per line
77
+
78
+ ## API/REST
79
+
80
+ For full reference see `docs/PORTAL-API.md`. Short form:
81
+
82
+ - **Portal UI**: `https://portal.acconcept.ru`
83
+ - **API**: `https://api.portal.acconcept.ru`
84
+ - Endpoints: `GET /api/rsforms/{id}`, `GET /api/rsforms/{id}/details`, `GET /api/library/{id}/versions/{v}`, `GET /api/oss/{id}`, `GET /api/models/{id}`, OpenAPI at `GET /schema`.
85
+ - Don't scrape SPA or use UI query params (`tab=`, etc.).
86
+ - rstool itself never calls the REST API; bring data in via `addOrUpdateConstituenta` after fetching.
87
+
88
+ ## Constituent Types (`cstType`)
89
+
90
+ | cstType | Example | Formal | Notes |
91
+ | :-------- | :------ | :-------- | :------------------- |
92
+ | basic | X1 | **empty** | Required |
93
+ | constant | C1 | **empty** | Required |
94
+ | nominal | S1 | allowed | Naming |
95
+ | structure | custom | allowed | Structured genus |
96
+ | term | D1 | allowed | Set/term |
97
+ | axiom | A1 | allowed | Logical (type=Logic) |
98
+ | statement | T1 | allowed | Logical (type=Logic) |
99
+ | function | F1 | allowed | Parameterized |
100
+ | predicate | P1 | allowed | Parameterized |
101
+
102
+ - Non-empty formal for `basic`/`constant` ⇒ error `0x8862` (`definitionNotAllowed`)
103
+ - Empty `basic`/`constant` always typified
104
+ - **Interpretable** (can set value): `basic`, `constant`, `structure`, `axiom`, `term`, `statement`
105
+ - **Inferrable** (computed, do not set directly): `term`, `axiom`, `statement`
106
+
107
+ ## Syntax
108
+
109
+ - **Globals**: `X1`, `C1`, `D1`, `F1`, `P1`, `A1`, `R1`
110
+ - **Locals**: `x`, `ξ`, `μ2`
111
+ - **Literals**: `42`, `Z`, `∅`
112
+ - Full operator + precedence table: `docs/SYNTAX.md`
113
+ - Grammar pointers: `docs/GRAMMAR-REF.md`
114
+
115
+ ## Expression Types
116
+
117
+ - **Set-theoretic**: `∪`, `∩`, `\`, `∆`, `×`, `∈`, `⊆`, `ℬ(...)`, tuples
118
+ - **Logical**: `¬`, `&`, `∨`, `⇒`, `⇔`, `∀`, `∃`, comparisons `=`, `≠`, `<`
119
+ - **Parameterized**: `[arg1∈H1, arg2∈H2] body`
120
+ - Detailed semantics in `docs/SYNTAX.md`; typification grades and radicals in `docs/TYPIFICATION.md`.
121
+
122
+ Always set `cstType` in upserts/analysis to true role.
123
+
124
+ ## Diagnostics Loop
125
+
126
+ 1. Check `analysis.success`
127
+ 2. If not, see `analysis.diagnostics` / `listDiagnostics`
128
+ 3. Map `code` → cause → fix via `docs/DIAGNOSTICS.md`
129
+ 4. Use `from`, `to` to patch `definitionFormal`; re-send
130
+
131
+ Don’t infer types—always read tool output.
132
+
133
+ ## Declaration Order
134
+
135
+ 1. All `basic`, `constant`
136
+ 2. Core/critical first
137
+ 3. Topological: dependencies before dependents (e.g. `D1` before `D2` if `D2` refers to `D1`)
138
+ 4. Derived right after their sources
139
+
140
+ ## Checklist
141
+
142
+ - [ ] `sessionId` obtained & tracked
143
+ - [ ] Bases/constants are empty formal
144
+ - [ ] All dependencies exist before upsert
145
+ - [ ] Matching `cstType`
146
+ - [ ] Diagnostics handled before commit/export
147
+ - [ ] Base bindings set before evaluating expressions that reference base elements
148
+ - [ ] For other details, check help topics or [REFERENCE.md](REFERENCE.md)
package/src/index.ts ADDED
@@ -0,0 +1,43 @@
1
+ export {
2
+ toPublicAnalysis,
3
+ toPublicError,
4
+ type DomainAnalysisLike,
5
+ type DomainErrorLike
6
+ } from './mappers/types';
7
+ export {
8
+ CONTRACT_VERSION,
9
+ CstType,
10
+ EvalStatus,
11
+ RSErrorCode,
12
+ RSToolAgent,
13
+ ValueClass,
14
+ type AddOrUpdateConstituentaInput,
15
+ type AddOrUpdateConstituentaResult,
16
+ type AnalysisResult,
17
+ type AnalyzeExpressionInput,
18
+ type BasicBinding,
19
+ type ClearConstituentaValuesInput,
20
+ type ConstituentaDraft,
21
+ type ConstituentaState,
22
+ type DiagnosticRecord,
23
+ type EvaluateConstituentaInput,
24
+ type EvaluateExpressionInput,
25
+ type EvaluationResult,
26
+ type ListDiagnosticsFilters,
27
+ type ModelValueState,
28
+ type RecalculateModelResult,
29
+ type RSToolAgentContract,
30
+ type RSToolErrorDescription,
31
+ type RSToolValue,
32
+ type SessionHandle,
33
+ type SessionModelState,
34
+ type SessionRevision,
35
+ type SessionState,
36
+ type SetConstituentaValueInput,
37
+ type SetConstituentaValuesInput
38
+ } from './models';
39
+ export {
40
+ RSToolWrapperClient,
41
+ type RSToolWrapperClientOptions,
42
+ type WrapperResponse
43
+ } from './wrapper/client';
@@ -0,0 +1,276 @@
1
+ import { Graph } from '@rsconcept/domain/graph/graph';
2
+ import { extractGlobals } from '@rsconcept/domain/rslang/api';
3
+ import { type ExpressionType, RSLangAnalyzer, type Value, type ValueClass } from '@rsconcept/domain/rslang';
4
+ import { isBaseSet } from '@rsconcept/domain/library/rsform-api';
5
+ import { type Constituenta, CstType, type RSForm } from '@rsconcept/domain/library/rsform';
6
+ import { RSEngine, type RSEngineServices } from '@rsconcept/domain/library/rsengine';
7
+ import { type BasicBinding, EvalStatus, type RSModel } from '@rsconcept/domain/library/rsmodel';
8
+ import {
9
+ isInferrable,
10
+ isInterpretable,
11
+ toBasicBinding,
12
+ validateBasicBindingData,
13
+ validateValueData
14
+ } from '@rsconcept/domain/library/rsmodel-api';
15
+
16
+ import {
17
+ type ConstituentaState,
18
+ type EvaluationResult,
19
+ type RecalculateModelResult,
20
+ type SessionModelState,
21
+ type SessionState,
22
+ type SetConstituentaValueInput
23
+ } from '../models';
24
+ import { toPublicError } from './types';
25
+
26
+ const SESSION_MODEL_ID = 0;
27
+
28
+ export class ModelAdapter {
29
+ public async setConstituentaValue(
30
+ session: SessionState,
31
+ input: SetConstituentaValueInput
32
+ ): Promise<SessionModelState> {
33
+ this.validateSetInput(session, input);
34
+ const engine = this.createEngine(session);
35
+ const cst = session.items.find(item => item.id === input.target)!;
36
+ const frontendType = cst.cstType;
37
+
38
+ if (isBaseSet(frontendType)) {
39
+ const binding = toBasicBinding(input.value as Record<string | number, string>);
40
+ await engine.setBasicValue(input.target, binding);
41
+ } else {
42
+ await engine.setStructureValue(input.target, input.value as Value);
43
+ }
44
+ session.updatedAt = new Date().toISOString();
45
+ return structuredClone(session.model);
46
+ }
47
+
48
+ public async setConstituentaValues(
49
+ session: SessionState,
50
+ input: { items: SetConstituentaValueInput[] }
51
+ ): Promise<SessionModelState> {
52
+ for (const item of input.items) {
53
+ await this.setConstituentaValue(session, item);
54
+ }
55
+ return structuredClone(session.model);
56
+ }
57
+
58
+ public async clearConstituentaValues(session: SessionState, ids: number[]): Promise<SessionModelState> {
59
+ const engine = this.createEngine(session);
60
+ for (const id of ids) {
61
+ await engine.resetValue(id);
62
+ session.updatedAt = new Date().toISOString();
63
+ }
64
+ return structuredClone(session.model);
65
+ }
66
+
67
+ public evaluateExpression(session: SessionState, expression: string, cstType: CstType): EvaluationResult {
68
+ const engine = this.createEngine(session);
69
+ const result = engine.evaluateExpression(expression, cstType);
70
+ const status =
71
+ result.value === null
72
+ ? result.errors.length > 0
73
+ ? EvalStatus.EVAL_FAIL
74
+ : EvalStatus.EMPTY
75
+ : EvalStatus.HAS_DATA;
76
+ return toPublicEvaluationResult(result.value, result.errors, result.iterations, result.cacheHits, status);
77
+ }
78
+
79
+ public evaluateConstituenta(session: SessionState, constituentId: number): EvaluationResult {
80
+ const cst = session.items.find(item => item.id === constituentId);
81
+ if (!cst) {
82
+ throw new Error(`Unknown constituent: ${constituentId}`);
83
+ }
84
+ const engine = this.createEngine(session);
85
+ const result = engine.calculateCst(constituentId);
86
+ const status = engine.getCstStatus(constituentId);
87
+ return toPublicEvaluationResult(result.value, result.errors, result.iterations, result.cacheHits, status);
88
+ }
89
+
90
+ public recalculateModel(session: SessionState): RecalculateModelResult {
91
+ const engine = this.createEngine(session);
92
+ engine.recalculateAll();
93
+ const items = session.items.map(item => ({
94
+ id: item.id,
95
+ alias: item.alias,
96
+ value: engine.getCstValue(item.id) as number | number[] | null,
97
+ status: engine.getCstStatus(item.id)
98
+ }));
99
+ return { items };
100
+ }
101
+
102
+ public createEngine(session: SessionState): RSEngine {
103
+ const schema = buildRSFormFromSession(session);
104
+ const model = buildRSModelFromSession(session);
105
+ const engine = new RSEngine(SESSION_MODEL_ID, createInMemoryServices(session));
106
+ engine.loadData(schema, model);
107
+ return engine;
108
+ }
109
+
110
+ private validateSetInput(session: SessionState, input: SetConstituentaValueInput): void {
111
+ const cst = session.items.find(item => item.id === input.target);
112
+ if (!cst) {
113
+ throw new Error(`Unknown constituent: ${input.target}`);
114
+ }
115
+ const frontendType = cst.cstType;
116
+ if (!isInterpretable(frontendType)) {
117
+ throw new Error(`Constituent ${cst.alias} is not interpretable`);
118
+ }
119
+ if (isInferrable(frontendType)) {
120
+ throw new Error(`Constituent ${cst.alias} is inferrable and cannot be set directly`);
121
+ }
122
+ if (isBaseSet(frontendType)) {
123
+ if (!validateBasicBindingData(input.value)) {
124
+ throw new Error(`Invalid basic binding for ${cst.alias}`);
125
+ }
126
+ return;
127
+ }
128
+ if (!validateValueData(input.value)) {
129
+ throw new Error(`Invalid structured value for ${cst.alias}`);
130
+ }
131
+ }
132
+ }
133
+
134
+ function createInMemoryServices(session: SessionState): RSEngineServices {
135
+ return {
136
+ setCstValue: async ({ data }) => {
137
+ for (const item of data) {
138
+ const entry = {
139
+ id: item.target,
140
+ type: item.type,
141
+ value: item.data as Value | BasicBinding
142
+ };
143
+ const index = session.model.items.findIndex(existing => existing.id === item.target);
144
+ if (index === -1) {
145
+ session.model.items.push(entry);
146
+ } else {
147
+ session.model.items[index] = entry;
148
+ }
149
+ }
150
+ },
151
+ clearValues: async ({ data }) => {
152
+ const ids = new Set(data.items);
153
+ session.model.items = session.model.items.filter(item => !ids.has(item.id));
154
+ }
155
+ };
156
+ }
157
+
158
+ function buildRSFormFromSession(session: SessionState): RSForm {
159
+ const graph = new Graph<number>();
160
+ const cstByAlias = new Map<string, Constituenta>();
161
+ const cstByID = new Map<number, Constituenta>();
162
+ const analyzer = new RSLangAnalyzer();
163
+
164
+ const items = session.items.map(item => {
165
+ const cst = toFrontendConstituenta(item);
166
+ cstByAlias.set(cst.alias, cst);
167
+ cstByID.set(cst.id, cst);
168
+ graph.addNode(cst.id);
169
+ if (item.cstType === CstType.BASE) {
170
+ analyzer.addBase(cst.alias);
171
+ }
172
+ if (cst.effectiveType) {
173
+ analyzer.setGlobal(cst.alias, cst.effectiveType, cst.analysis.valueClass as ValueClass | null);
174
+ }
175
+ return cst;
176
+ });
177
+
178
+ for (const cst of items) {
179
+ for (const alias of extractGlobals(cst.definition_formal)) {
180
+ const source = cstByAlias.get(alias);
181
+ if (source) {
182
+ graph.addEdge(source.id, cst.id);
183
+ }
184
+ }
185
+ }
186
+
187
+ return {
188
+ id: 0,
189
+ items,
190
+ cstByAlias,
191
+ cstByID,
192
+ graph,
193
+ analyzer,
194
+ inheritance: [],
195
+ attribution: [],
196
+ attribution_graph: graph.clone(),
197
+ oss: [],
198
+ models: [],
199
+ editors: [],
200
+ versions: [],
201
+ is_produced: false,
202
+ is_attributive: false,
203
+ version: 'latest'
204
+ } as unknown as RSForm;
205
+ }
206
+
207
+ function buildRSModelFromSession(session: SessionState): RSModel {
208
+ return {
209
+ id: SESSION_MODEL_ID,
210
+ schema: 0,
211
+ editors: [],
212
+ items: session.model.items.map(item => ({
213
+ id: item.id,
214
+ type: item.type,
215
+ value: item.value as Value | BasicBinding
216
+ }))
217
+ } as unknown as RSModel;
218
+ }
219
+
220
+ function toFrontendConstituenta(item: ConstituentaState): Constituenta {
221
+ const effectiveType = (item.analysis.type ?? null) as ExpressionType | null;
222
+ return {
223
+ id: item.id,
224
+ alias: item.alias,
225
+ cst_type: item.cstType,
226
+ definition_formal: item.definitionFormal,
227
+ definition_raw: item.definitionFormal,
228
+ definition_resolved: item.definitionFormal,
229
+ term_raw: item.term,
230
+ term_resolved: item.term,
231
+ term_forms: [],
232
+ convention: item.convention,
233
+ typification_manual: '',
234
+ value_is_property: false,
235
+ crucial: false,
236
+ attributes: [],
237
+ homonyms: [],
238
+ formalDuplicates: [],
239
+ analysis: {
240
+ success: item.analysis.success,
241
+ type: effectiveType,
242
+ valueClass: item.analysis.valueClass
243
+ },
244
+ effectiveType,
245
+ is_type_mismatch: false,
246
+ schema: 0,
247
+ cst_class: 'derived',
248
+ status: item.analysis.success ? 'verified' : 'incorrect',
249
+ is_template: false,
250
+ is_simple_expression: true,
251
+ parent_schema_index: 0,
252
+ parent_schema: null,
253
+ is_inherited: false,
254
+ has_inherited_children: false,
255
+ spawn: [],
256
+ spawn_alias: []
257
+ } as unknown as Constituenta;
258
+ }
259
+
260
+ function toPublicEvaluationResult(
261
+ value: Value | null,
262
+ errors: { code: number; from: number; to: number; params?: readonly string[] }[],
263
+ iterations: number,
264
+ cacheHits: number,
265
+ status: EvalStatus
266
+ ): EvaluationResult {
267
+ const diagnostics = errors.map(toPublicError);
268
+ return {
269
+ success: diagnostics.length === 0 && value !== null,
270
+ value: value as EvaluationResult['value'],
271
+ status,
272
+ iterations,
273
+ cacheHits,
274
+ diagnostics
275
+ };
276
+ }
@@ -0,0 +1,87 @@
1
+ import { RSLangAnalyzer, type AnalysisFull, type ValueClass } from '@rsconcept/domain/rslang';
2
+ import { getAnalysisFor } from '@rsconcept/domain/library/rsform-api';
3
+ import { CstType, type RSForm } from '@rsconcept/domain/library/rsform';
4
+
5
+ import {
6
+ type AnalysisResult,
7
+ type ConstituentaDraft,
8
+ type ConstituentaState,
9
+ type DiagnosticRecord,
10
+ type SessionState
11
+ } from '../models';
12
+ import { toPublicAnalysis, toPublicError } from './types';
13
+
14
+ export class SchemaAdapter {
15
+ public analyzeAgainstSession(
16
+ session: SessionState,
17
+ draft: ConstituentaDraft
18
+ ): { result: AnalysisResult; diagnostics: DiagnosticRecord[] } {
19
+ const analyzer = this.buildAnalyzer(session);
20
+ const schema = this.toPseudoRSFormState(session, analyzer);
21
+ const analysis = getAnalysisFor(draft.definitionFormal, draft.cstType, schema as unknown as RSForm, draft.alias);
22
+ const result = toPublicAnalysis({
23
+ success: analysis.success,
24
+ type: analysis.type as Record<string, unknown> | null,
25
+ valueClass: analysis.valueClass,
26
+ errors: analysis.errors
27
+ });
28
+ return {
29
+ result,
30
+ diagnostics: analysis.errors.map(error => ({
31
+ sessionId: session.sessionId,
32
+ constituentId: draft.id,
33
+ expression: draft.definitionFormal,
34
+ error: toPublicError(error)
35
+ }))
36
+ };
37
+ }
38
+
39
+ public mergeStateWithDraft(
40
+ session: SessionState,
41
+ draft: ConstituentaDraft,
42
+ analysis: AnalysisResult
43
+ ): ConstituentaState {
44
+ const state: ConstituentaState = {
45
+ ...draft,
46
+ term: draft.term ?? '',
47
+ definitionText: draft.definitionText ?? '',
48
+ convention: draft.convention ?? '',
49
+ analysis
50
+ };
51
+ const index = session.items.findIndex(item => item.id === draft.id);
52
+ if (index === -1) {
53
+ session.items.push(state);
54
+ } else {
55
+ session.items[index] = state;
56
+ }
57
+ session.updatedAt = new Date().toISOString();
58
+ return state;
59
+ }
60
+
61
+ public toPseudoRSFormState(
62
+ session: SessionState,
63
+ analyzer: RSLangAnalyzer
64
+ ): Pick<RSForm, 'items' | 'cstByAlias' | 'analyzer'> {
65
+ const cstByAlias = new Map(session.items.map(item => [item.alias, item]));
66
+ return {
67
+ items: session.items as unknown as RSForm['items'],
68
+ cstByAlias: cstByAlias as unknown as RSForm['cstByAlias'],
69
+ analyzer
70
+ };
71
+ }
72
+
73
+ private buildAnalyzer(session: SessionState): RSLangAnalyzer {
74
+ const analyzer = new RSLangAnalyzer();
75
+ for (const item of session.items) {
76
+ if (item.cstType === CstType.BASE) {
77
+ analyzer.addBase(item.alias);
78
+ }
79
+ analyzer.setGlobal(
80
+ item.alias,
81
+ item.analysis.type as AnalysisFull['type'],
82
+ item.analysis.valueClass as ValueClass | null
83
+ );
84
+ }
85
+ return analyzer;
86
+ }
87
+ }
@@ -0,0 +1,35 @@
1
+ import { type ValueClass } from '@rsconcept/domain/rslang';
2
+
3
+ import { type AnalysisResult, type RSToolErrorDescription } from '../models';
4
+
5
+ export interface DomainErrorLike {
6
+ code: number;
7
+ from: number;
8
+ to: number;
9
+ params?: readonly string[];
10
+ }
11
+
12
+ export interface DomainAnalysisLike {
13
+ success: boolean;
14
+ type: Record<string, unknown> | null;
15
+ valueClass: ValueClass | null;
16
+ errors: DomainErrorLike[];
17
+ }
18
+
19
+ export function toPublicError(error: DomainErrorLike): RSToolErrorDescription {
20
+ return {
21
+ code: error.code,
22
+ from: error.from,
23
+ to: error.to,
24
+ params: error.params
25
+ };
26
+ }
27
+
28
+ export function toPublicAnalysis(analysis: DomainAnalysisLike): AnalysisResult {
29
+ return {
30
+ success: analysis.success,
31
+ type: analysis.type,
32
+ valueClass: analysis.valueClass,
33
+ diagnostics: analysis.errors.map(toPublicError)
34
+ };
35
+ }
@@ -0,0 +1,13 @@
1
+ import { type CstType, type RSToolErrorDescription, type ValueClass } from './common';
2
+
3
+ export interface AnalyzeExpressionInput {
4
+ expression: string;
5
+ cstType: CstType;
6
+ }
7
+
8
+ export interface AnalysisResult {
9
+ success: boolean;
10
+ type: Record<string, unknown> | null;
11
+ valueClass: ValueClass | null;
12
+ diagnostics: RSToolErrorDescription[];
13
+ }
@@ -0,0 +1,17 @@
1
+ import { CstType } from '@rsconcept/domain/library/rsform';
2
+ import { EvalStatus, type BasicBinding } from '@rsconcept/domain/library/rsmodel';
3
+ import { ValueClass } from '@rsconcept/domain/rslang';
4
+ import { RSErrorCode } from '@rsconcept/domain/rslang/error';
5
+
6
+ export { CstType, EvalStatus, RSErrorCode, ValueClass };
7
+ export type { BasicBinding };
8
+
9
+ /** Runtime evaluation value: number, nested array (set/tuple), or boolean 0/1. */
10
+ export type RSToolValue = number | RSToolValue[];
11
+
12
+ export interface RSToolErrorDescription {
13
+ code: number;
14
+ from: number;
15
+ to: number;
16
+ params?: readonly string[];
17
+ }
@@ -0,0 +1,35 @@
1
+ import { type AnalysisResult } from './analysis';
2
+ import { type CstType } from './common';
3
+ import { type DiagnosticRecord } from './diagnostic';
4
+
5
+ export interface ConstituentaDraft {
6
+ id: number;
7
+ /** Alias */
8
+ alias: string;
9
+ /** CST type */
10
+ cstType: CstType;
11
+ /** Formal definition */
12
+ definitionFormal: string;
13
+ /** Natural-language term */
14
+ term?: string;
15
+ /** Natural-language definition */
16
+ definitionText?: string;
17
+ /** Convention or comment */
18
+ convention?: string;
19
+ }
20
+
21
+ export interface ConstituentaState extends Omit<ConstituentaDraft, 'term' | 'definitionText' | 'convention'> {
22
+ term: string;
23
+ definitionText: string;
24
+ convention: string;
25
+ analysis: AnalysisResult;
26
+ }
27
+
28
+ export interface AddOrUpdateConstituentaInput {
29
+ draft: ConstituentaDraft;
30
+ }
31
+
32
+ export interface AddOrUpdateConstituentaResult {
33
+ state: ConstituentaState;
34
+ diagnostics: DiagnosticRecord[];
35
+ }
@@ -0,0 +1,12 @@
1
+ import { type RSToolErrorDescription } from './common';
2
+
3
+ export interface DiagnosticRecord {
4
+ sessionId: string;
5
+ constituentId?: number;
6
+ expression: string;
7
+ error: RSToolErrorDescription;
8
+ }
9
+
10
+ export interface ListDiagnosticsFilters {
11
+ constituentId?: number;
12
+ }
@@ -0,0 +1,25 @@
1
+ import {
2
+ type BasicBinding,
3
+ type CstType,
4
+ type EvalStatus,
5
+ type RSToolErrorDescription,
6
+ type RSToolValue
7
+ } from './common';
8
+
9
+ export interface EvaluateExpressionInput {
10
+ expression: string;
11
+ cstType: CstType;
12
+ }
13
+
14
+ export interface EvaluateConstituentaInput {
15
+ constituentId: number;
16
+ }
17
+
18
+ export interface EvaluationResult {
19
+ success: boolean;
20
+ value: RSToolValue | BasicBinding | null;
21
+ status: EvalStatus;
22
+ iterations: number;
23
+ cacheHits: number;
24
+ diagnostics: RSToolErrorDescription[];
25
+ }