@rsconcept/rstool 1.3.0 → 1.4.1
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 +4 -4
- package/dist/{agent-workflow-Gk0Vfnv1.d.ts → agent-workflow-D22FeODL.d.ts} +6 -6
- package/dist/{analysis-LLnPhmGa.d.ts → analysis-DA44pfgA.d.ts} +4 -3
- package/dist/common-D6TFl6l_.d.ts +11 -0
- package/dist/{constituenta-DnGR6bnM.d.ts → constituenta-QY5w1x91.d.ts} +7 -7
- package/dist/diagnostic-C6TRoBgt.d.ts +38 -0
- package/dist/diagnostic-assembly-DRunIcKQ.js +54 -0
- package/dist/diagnostic-assembly-DRunIcKQ.js.map +1 -0
- package/dist/{evaluation-Cns8BFm4.d.ts → evaluation-Cd4S7-3t.d.ts} +4 -3
- package/dist/index.d.ts +12 -12
- package/dist/index.js +4 -3
- package/dist/mappers/model-adapter.d.ts +3 -3
- package/dist/mappers/model-adapter.js +1 -199
- package/dist/mappers/schema-adapter.d.ts +5 -5
- package/dist/mappers/schema-adapter.js +13 -13
- package/dist/mappers/schema-adapter.js.map +1 -1
- package/dist/mappers/types.d.ts +6 -6
- package/dist/mappers/types.js +6 -10
- package/dist/mappers/types.js.map +1 -1
- package/dist/model-adapter-D-YmIvQW.js +225 -0
- package/dist/model-adapter-D-YmIvQW.js.map +1 -0
- package/dist/{model-value-BbonPzMz.d.ts → model-value-BUhr2IjA.d.ts} +2 -2
- package/dist/models/agent-workflow.d.ts +1 -1
- package/dist/models/analysis.d.ts +1 -1
- package/dist/models/common.d.ts +2 -2
- package/dist/models/common.js +1 -1
- package/dist/models/constituenta.d.ts +1 -1
- package/dist/models/diagnostic.d.ts +2 -2
- package/dist/models/diagnostic.js +3 -1
- package/dist/models/evaluation.d.ts +1 -1
- package/dist/models/index.d.ts +11 -11
- package/dist/models/index.js +3 -2
- package/dist/models/model-value.d.ts +1 -1
- package/dist/models/rstool-agent.d.ts +1 -1
- package/dist/models/rstool-agent.js +1 -1
- package/dist/models/session.d.ts +1 -1
- package/dist/models/tool-contract.d.ts +1 -1
- package/dist/models/tool-contract.js +1 -1
- package/dist/models/tool-contract.js.map +1 -1
- package/dist/{models-9G6ur_pg.js → models-B255GaEC.js} +76 -14
- package/dist/models-B255GaEC.js.map +1 -0
- package/dist/{rstool-agent-D2cQze_b.d.ts → rstool-agent-BGQAzFly.d.ts} +12 -10
- package/dist/session/session-store.d.ts +6 -6
- package/dist/session/session-store.js +1 -1
- package/dist/{session-ChexW8i7.d.ts → session-CI4rLYby.d.ts} +3 -3
- package/dist/{tool-contract-0uRGhEfW.d.ts → tool-contract-sQfr-DYD.d.ts} +12 -12
- package/dist/wrapper/stdio-wrapper.js +1 -1
- package/docs/DIAGNOSTICS.md +63 -20
- package/examples/README.md +2 -0
- package/examples/agent-client.ts +15 -3
- package/examples/audit-diagnostics.ts +40 -0
- package/examples/chocolate-nim/build-rsform.ts +15 -0
- package/examples/chocolate-nim/build-rsmodel.ts +15 -22
- package/examples/chocolate-nim/model-demo.ts +11 -0
- package/examples/chocolate-nim/rsform-session.json +329 -310
- package/examples/chocolate-nim/rsmodel-session.json +311 -311
- package/examples/diagnostics-utils.ts +27 -0
- package/examples/expression-bank/bank-constituents.ts +10 -10
- package/examples/expression-bank/build-rsform.ts +8 -0
- package/examples/expression-bank/rsform-session.json +1501 -1501
- package/examples/kinship/build-rsform.ts +16 -1
- package/examples/kinship/build-rsmodel.ts +15 -56
- package/examples/kinship/model-demo.ts +35 -0
- package/examples/kinship/rsform-session.json +300 -215
- package/examples/kinship/rsmodel-session.json +216 -216
- package/examples/movd/build-rsform.ts +15 -0
- package/examples/movd/build-rsmodel.ts +15 -46
- package/examples/movd/model-demo.ts +31 -0
- package/examples/movd/rsform-session.json +359 -270
- package/examples/movd/rsmodel-session.json +263 -263
- package/examples/sample/build-rsform.ts +43 -3
- package/examples/sample/build-rsmodel.ts +26 -2
- package/examples/sample/rsform-session.json +41 -12
- package/examples/sample/rsmodel-session.json +37 -37
- package/examples/template-apply/build-rsform.ts +31 -18
- package/examples/template-apply/rsform-session.json +100 -45
- package/package.json +3 -2
- package/skills/rstool-helper/EXAMPLES.md +3 -1
- package/skills/rstool-helper/GUIDE.md +7 -6
- package/skills/rstool-helper/REFERENCE.md +19 -19
- package/src/index.ts +7 -4
- package/src/mappers/diagnostic-assembly.ts +96 -0
- package/src/mappers/diagnostics-collector.test.ts +92 -0
- package/src/mappers/diagnostics-collector.ts +77 -0
- package/src/mappers/model-adapter.ts +29 -100
- package/src/mappers/rsform-builder.ts +127 -0
- package/src/mappers/schema-adapter.ts +15 -15
- package/src/mappers/types.ts +12 -12
- package/src/models/agent-workflow.ts +2 -2
- package/src/models/analysis.ts +3 -2
- package/src/models/common.ts +0 -12
- package/src/models/constituenta.ts +4 -4
- package/src/models/diagnostic.ts +15 -10
- package/src/models/evaluation.ts +3 -8
- package/src/models/index.ts +8 -2
- package/src/models/rstool-agent.test.ts +92 -5
- package/src/models/rstool-agent.ts +29 -11
- package/src/models/tool-contract.ts +4 -4
- package/src/session/persistence.ts +2 -2
- package/src/session/session-store.ts +14 -7
- package/dist/common-DHJalS-Q.d.ts +0 -22
- package/dist/diagnostic-D9yl_mEL.d.ts +0 -19
- package/dist/mappers/model-adapter.js.map +0 -1
- package/dist/models-9G6ur_pg.js.map +0 -1
|
@@ -84,12 +84,13 @@ kind `auto` (default) определит `portal-details` или `portal-schema`
|
|
|
84
84
|
|
|
85
85
|
Когда `analysis.success === false` или `listDiagnostics` не пуст. Коды и типичные ошибки — [DIAGNOSTICS.md](../../docs/DIAGNOSTICS.md).
|
|
86
86
|
|
|
87
|
-
1.
|
|
88
|
-
2.
|
|
89
|
-
3.
|
|
90
|
-
4.
|
|
91
|
-
5.
|
|
92
|
-
6.
|
|
87
|
+
1. Вызови `listDiagnostics()` — смотри `kind`: `expression` (формула), `schema` (омонимы, дубликаты, термин/конвенция), `model` (интерпретация).
|
|
88
|
+
2. Для черновика формулы — `analyzeExpression` (без сохранения).
|
|
89
|
+
3. Прочитай `name`, `severity`, `alias`, `from`, `to`, `params` в `analysis.diagnostics` или через `listDiagnostics({ kind })`.
|
|
90
|
+
4. Сопоставь `name` (или `code`) с исправлением в [DIAGNOSTICS.md](../../docs/DIAGNOSTICS.md).
|
|
91
|
+
5. Исправь именно диапазон в `definitionFormal` (expression) или метаданные/данные модели (schema/model).
|
|
92
|
+
6. Не повторяй вызов без изменения ввода.
|
|
93
|
+
7. После успеха — ещё один `applySchemaPatch` / `setModelValues` с исправлением.
|
|
93
94
|
|
|
94
95
|
### Проверка на маленькой КМ
|
|
95
96
|
|
|
@@ -3,30 +3,30 @@
|
|
|
3
3
|
## Контракт rstool
|
|
4
4
|
|
|
5
5
|
- Пакет: `@rsconcept/rstool`
|
|
6
|
-
- Версия контракта: `
|
|
6
|
+
- Версия контракта: `3.0.0` (`CONTRACT_VERSION`)
|
|
7
7
|
- Основной класс: `RSToolAgent`
|
|
8
8
|
- Публичные импорты: `@rsconcept/rstool` и `@rsconcept/rstool/wrapper`
|
|
9
9
|
|
|
10
10
|
### Методы library API
|
|
11
11
|
|
|
12
|
-
| Метод | Назначение
|
|
13
|
-
| ---------------------------------------------- |
|
|
14
|
-
| `ensureSession(initial?)` | Вернуть текущую сессию или создать новую
|
|
15
|
-
| `createSession(initial?)` | Новая in-memory сессия
|
|
16
|
-
| `getCurrentSession()` | Текущая активная сессия или `null`
|
|
17
|
-
| `setCurrentSession(sessionId)` | Сделать сессию текущей
|
|
18
|
-
| `applySchemaPatch(input, sessionId?)` | Единственный путь правки схемы
|
|
19
|
-
| `getSessionState(detail?, sessionId?)` | `summary` (default) или `full`
|
|
20
|
-
| `listDiagnostics(filters?, sessionId?)` | Активные диагностики
|
|
21
|
-
| `analyzeExpression(input, sessionId?)` | Разбор без сохранения (`recordDiagnostics?`)
|
|
22
|
-
| `commitStep(message?, sessionId?)` | Ревизия
|
|
23
|
-
| `exportSession(sessionId?)` | JSON сессии
|
|
24
|
-
| `exportPortal({ kind, format? }, sessionId?)` | Portal Load from JSON
|
|
25
|
-
| `importData(payload, kind?)` | Импорт сессии / Portal (`kind` см. ниже)
|
|
26
|
-
| `setModelValues({ set?, clear? }, sessionId?)` | Значения модели (async — нужен `await`)
|
|
27
|
-
| `getModelState(sessionId?)` | Состояние интерпретации
|
|
28
|
-
| `evaluate(input, sessionId?)` | Scratch или конституента
|
|
29
|
-
| `recalculateModel(sessionId?)` | Пересчёт
|
|
12
|
+
| Метод | Назначение |
|
|
13
|
+
| ---------------------------------------------- | ------------------------------------------------------------------ |
|
|
14
|
+
| `ensureSession(initial?)` | Вернуть текущую сессию или создать новую |
|
|
15
|
+
| `createSession(initial?)` | Новая in-memory сессия |
|
|
16
|
+
| `getCurrentSession()` | Текущая активная сессия или `null` |
|
|
17
|
+
| `setCurrentSession(sessionId)` | Сделать сессию текущей |
|
|
18
|
+
| `applySchemaPatch(input, sessionId?)` | Единственный путь правки схемы |
|
|
19
|
+
| `getSessionState(detail?, sessionId?)` | `summary` (default) или `full` |
|
|
20
|
+
| `listDiagnostics(filters?, sessionId?)` | Активные диагностики (`kind`: `expression` \| `schema` \| `model`) |
|
|
21
|
+
| `analyzeExpression(input, sessionId?)` | Разбор без сохранения (`recordDiagnostics?`) |
|
|
22
|
+
| `commitStep(message?, sessionId?)` | Ревизия |
|
|
23
|
+
| `exportSession(sessionId?)` | JSON сессии |
|
|
24
|
+
| `exportPortal({ kind, format? }, sessionId?)` | Portal Load from JSON |
|
|
25
|
+
| `importData(payload, kind?)` | Импорт сессии / Portal (`kind` см. ниже) |
|
|
26
|
+
| `setModelValues({ set?, clear? }, sessionId?)` | Значения модели (async — нужен `await`) |
|
|
27
|
+
| `getModelState(sessionId?)` | Состояние интерпретации |
|
|
28
|
+
| `evaluate(input, sessionId?)` | Scratch или конституента |
|
|
29
|
+
| `recalculateModel(sessionId?)` | Пересчёт |
|
|
30
30
|
|
|
31
31
|
### `applySchemaPatch`
|
|
32
32
|
|
package/src/index.ts
CHANGED
|
@@ -2,14 +2,16 @@
|
|
|
2
2
|
* Public API for `@rsconcept/rstool`.
|
|
3
3
|
*
|
|
4
4
|
* Primary entry points: {@link RSToolAgent} (in-process) and {@link RSToolWrapperClient}
|
|
5
|
-
* (stdio child process). Types describe the
|
|
5
|
+
* (stdio child process). Types describe the v3 agent contract.
|
|
6
6
|
*/
|
|
7
|
-
export {
|
|
7
|
+
export { toDiagnostic, toPublicAnalysis, type DomainAnalysisLike, type DomainErrorLike } from './mappers/types';
|
|
8
8
|
export {
|
|
9
9
|
CONTRACT_VERSION,
|
|
10
10
|
CstType,
|
|
11
|
+
DiagnosticKind,
|
|
11
12
|
EvalStatus,
|
|
12
13
|
RSErrorCode,
|
|
14
|
+
RSDiagnosticCode,
|
|
13
15
|
RSToolAgent,
|
|
14
16
|
ValueClass,
|
|
15
17
|
type AgentConstituentaPatch,
|
|
@@ -20,7 +22,9 @@ export {
|
|
|
20
22
|
type BasicBinding,
|
|
21
23
|
type ConstituentaDraft,
|
|
22
24
|
type ConstituentaState,
|
|
23
|
-
type
|
|
25
|
+
type CstDiagnostic,
|
|
26
|
+
type Diagnostic,
|
|
27
|
+
type DiagnosticSeverity,
|
|
24
28
|
type EvaluateInput,
|
|
25
29
|
type EvaluationResult,
|
|
26
30
|
type ExportFormat,
|
|
@@ -40,7 +44,6 @@ export {
|
|
|
40
44
|
type RecalculateModelResult,
|
|
41
45
|
type RSToolAgentContract,
|
|
42
46
|
type RSToolAgentOptions,
|
|
43
|
-
type RSToolErrorDescription,
|
|
44
47
|
type RSToolValue,
|
|
45
48
|
type SessionHandle,
|
|
46
49
|
type SessionModelState,
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import {
|
|
2
|
+
type Constituenta,
|
|
3
|
+
type CstDiagnostic,
|
|
4
|
+
DiagnosticKind,
|
|
5
|
+
modelStatusCstDiagnostic,
|
|
6
|
+
RSDiagnosticCode
|
|
7
|
+
} from '@rsconcept/domain/library';
|
|
8
|
+
import { EvalStatus } from '@rsconcept/domain/library/rsmodel';
|
|
9
|
+
import { isCritical, RSErrorCode, type RSErrorDescription } from '@rsconcept/domain/rslang/error';
|
|
10
|
+
|
|
11
|
+
export { DiagnosticKind, RSDiagnosticCode };
|
|
12
|
+
|
|
13
|
+
/** Severity of a {@link Diagnostic}. */
|
|
14
|
+
export type DiagnosticSeverity = 'error' | 'warning';
|
|
15
|
+
|
|
16
|
+
/** Agent-facing diagnostic with redundant fields for autonomous repair. */
|
|
17
|
+
export interface Diagnostic extends CstDiagnostic {
|
|
18
|
+
name: string;
|
|
19
|
+
severity: DiagnosticSeverity;
|
|
20
|
+
constituentId?: number;
|
|
21
|
+
alias?: string;
|
|
22
|
+
expression: string;
|
|
23
|
+
from: number;
|
|
24
|
+
to: number;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/** Context for attributing a {@link Diagnostic} to a constituent. */
|
|
28
|
+
export interface DiagnosticTarget {
|
|
29
|
+
constituentId?: number;
|
|
30
|
+
alias?: string;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
const DIAGNOSTIC_NAMES: ReadonlyMap<number, string> = new Map([
|
|
34
|
+
...Object.entries(RSErrorCode).map(([name, code]) => [code, name] as const),
|
|
35
|
+
...Object.entries(RSDiagnosticCode).map(([name, code]) => [code, name] as const)
|
|
36
|
+
]);
|
|
37
|
+
|
|
38
|
+
export function getDiagnosticName(code: number): string {
|
|
39
|
+
return DIAGNOSTIC_NAMES.get(code) ?? `0x${code.toString(16).toUpperCase()}`;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export function getDiagnosticSeverity(code: number): DiagnosticSeverity {
|
|
43
|
+
return isCritical(code as RSErrorCode) ? 'error' : 'warning';
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export function expandCstDiagnostic(cst: Constituenta, diagnostic: CstDiagnostic): Diagnostic {
|
|
47
|
+
const expression = expressionForCstDiagnostic(cst, diagnostic);
|
|
48
|
+
return {
|
|
49
|
+
...diagnostic,
|
|
50
|
+
name: getDiagnosticName(diagnostic.code),
|
|
51
|
+
severity: getDiagnosticSeverity(diagnostic.code),
|
|
52
|
+
constituentId: cst.id,
|
|
53
|
+
alias: cst.alias,
|
|
54
|
+
expression,
|
|
55
|
+
from: 0,
|
|
56
|
+
to: diagnostic.kind === DiagnosticKind.MODEL ? 0 : Math.max(expression.length, 1)
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
export function expressionDiagnostic(
|
|
61
|
+
error: RSErrorDescription,
|
|
62
|
+
expression: string,
|
|
63
|
+
target?: DiagnosticTarget
|
|
64
|
+
): Diagnostic {
|
|
65
|
+
return {
|
|
66
|
+
kind: DiagnosticKind.EXPRESSION,
|
|
67
|
+
code: error.code,
|
|
68
|
+
name: getDiagnosticName(error.code),
|
|
69
|
+
severity: getDiagnosticSeverity(error.code),
|
|
70
|
+
constituentId: target?.constituentId,
|
|
71
|
+
alias: target?.alias,
|
|
72
|
+
expression,
|
|
73
|
+
from: error.from,
|
|
74
|
+
to: error.to,
|
|
75
|
+
...(error.params ? { params: error.params } : {})
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
export function modelStatusDiagnostic(status: EvalStatus, cst: Constituenta): Diagnostic | null {
|
|
80
|
+
const base = modelStatusCstDiagnostic(status, cst.cst_type);
|
|
81
|
+
return base ? expandCstDiagnostic(cst, base) : null;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
function expressionForCstDiagnostic(cst: Constituenta, diagnostic: CstDiagnostic): string {
|
|
85
|
+
switch (diagnostic.code) {
|
|
86
|
+
case RSDiagnosticCode.schemaHomonym:
|
|
87
|
+
case RSDiagnosticCode.schemaMissingTerm:
|
|
88
|
+
return cst.term_resolved;
|
|
89
|
+
case RSDiagnosticCode.schemaMissingConvention:
|
|
90
|
+
return cst.convention;
|
|
91
|
+
case RSDiagnosticCode.schemaTypeMismatch:
|
|
92
|
+
return cst.typification_manual;
|
|
93
|
+
default:
|
|
94
|
+
return cst.definition_formal;
|
|
95
|
+
}
|
|
96
|
+
}
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest';
|
|
2
|
+
|
|
3
|
+
import { CstType, RSErrorCode, RSDiagnosticCode, type SessionState, ValueClass } from '../models';
|
|
4
|
+
import { expressionDiagnostic } from './diagnostic-assembly';
|
|
5
|
+
import { collectModelDiagnostics, collectSchemaDiagnostics, collectSessionDiagnostics } from './diagnostics-collector';
|
|
6
|
+
|
|
7
|
+
function buildFixtureSession(): SessionState {
|
|
8
|
+
return {
|
|
9
|
+
sessionId: 'test',
|
|
10
|
+
alias: '',
|
|
11
|
+
title: '',
|
|
12
|
+
comment: '',
|
|
13
|
+
createdAt: '',
|
|
14
|
+
updatedAt: '',
|
|
15
|
+
revisions: [],
|
|
16
|
+
model: { items: [] },
|
|
17
|
+
items: [
|
|
18
|
+
{
|
|
19
|
+
id: 1,
|
|
20
|
+
alias: 'X1',
|
|
21
|
+
cstType: CstType.BASE,
|
|
22
|
+
definitionFormal: 'Z',
|
|
23
|
+
term: '',
|
|
24
|
+
definitionText: '',
|
|
25
|
+
convention: '',
|
|
26
|
+
analysis: {
|
|
27
|
+
success: false,
|
|
28
|
+
type: null,
|
|
29
|
+
valueClass: null,
|
|
30
|
+
diagnostics: [
|
|
31
|
+
expressionDiagnostic(
|
|
32
|
+
{ code: RSErrorCode.definitionNotAllowed, from: 0, to: 1, params: [CstType.BASE, 'X1'] },
|
|
33
|
+
'Z',
|
|
34
|
+
{ constituentId: 1, alias: 'X1' }
|
|
35
|
+
)
|
|
36
|
+
]
|
|
37
|
+
}
|
|
38
|
+
},
|
|
39
|
+
{
|
|
40
|
+
id: 2,
|
|
41
|
+
alias: 'X2',
|
|
42
|
+
cstType: CstType.BASE,
|
|
43
|
+
definitionFormal: '',
|
|
44
|
+
term: 'person',
|
|
45
|
+
definitionText: '',
|
|
46
|
+
convention: 'people',
|
|
47
|
+
analysis: { success: true, type: {}, valueClass: ValueClass.VALUE, diagnostics: [] }
|
|
48
|
+
},
|
|
49
|
+
{
|
|
50
|
+
id: 3,
|
|
51
|
+
alias: 'X3',
|
|
52
|
+
cstType: CstType.BASE,
|
|
53
|
+
definitionFormal: '',
|
|
54
|
+
term: 'Person',
|
|
55
|
+
definitionText: '',
|
|
56
|
+
convention: 'other',
|
|
57
|
+
analysis: { success: true, type: {}, valueClass: ValueClass.VALUE, diagnostics: [] }
|
|
58
|
+
}
|
|
59
|
+
]
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
describe('collectSchemaDiagnostics', () => {
|
|
64
|
+
it('collects expression and schema diagnostics without model issues', () => {
|
|
65
|
+
const records = collectSchemaDiagnostics(buildFixtureSession());
|
|
66
|
+
|
|
67
|
+
expect(records.some(record => record.kind === 'expression')).toBe(true);
|
|
68
|
+
expect(records.some(record => record.code === RSDiagnosticCode.schemaHomonym)).toBe(true);
|
|
69
|
+
expect(records.some(record => record.code === RSDiagnosticCode.schemaMissingTerm)).toBe(true);
|
|
70
|
+
expect(records.some(record => record.code === RSDiagnosticCode.modelEmpty)).toBe(false);
|
|
71
|
+
expect(records.every(record => record.name.length > 0)).toBe(true);
|
|
72
|
+
});
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
describe('collectModelDiagnostics', () => {
|
|
76
|
+
it('collects model diagnostics for empty basic sets', () => {
|
|
77
|
+
const records = collectModelDiagnostics(buildFixtureSession());
|
|
78
|
+
|
|
79
|
+
expect(records.some(record => record.code === RSDiagnosticCode.modelEmpty)).toBe(true);
|
|
80
|
+
expect(records.every(record => record.kind === 'model')).toBe(true);
|
|
81
|
+
});
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
describe('collectSessionDiagnostics', () => {
|
|
85
|
+
it('merges schema and model diagnostics', () => {
|
|
86
|
+
const records = collectSessionDiagnostics(buildFixtureSession());
|
|
87
|
+
|
|
88
|
+
expect(records.some(record => record.kind === 'expression')).toBe(true);
|
|
89
|
+
expect(records.some(record => record.code === RSDiagnosticCode.schemaHomonym)).toBe(true);
|
|
90
|
+
expect(records.some(record => record.code === RSDiagnosticCode.modelEmpty)).toBe(true);
|
|
91
|
+
});
|
|
92
|
+
});
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import { isModelIssue } from '@rsconcept/domain/library/rsmodel-api';
|
|
2
|
+
|
|
3
|
+
import { type SessionState } from '../models';
|
|
4
|
+
import {
|
|
5
|
+
DiagnosticKind,
|
|
6
|
+
expandCstDiagnostic,
|
|
7
|
+
modelStatusDiagnostic,
|
|
8
|
+
type Diagnostic
|
|
9
|
+
} from './diagnostic-assembly';
|
|
10
|
+
import { ModelAdapter } from './model-adapter';
|
|
11
|
+
import { enrichSessionConstituents } from './rsform-builder';
|
|
12
|
+
import { toDiagnostic } from './types';
|
|
13
|
+
|
|
14
|
+
const modelAdapter = new ModelAdapter();
|
|
15
|
+
|
|
16
|
+
/** Collect expression and schema diagnostics for a session. */
|
|
17
|
+
export function collectSchemaDiagnostics(session: SessionState): Diagnostic[] {
|
|
18
|
+
const records: Diagnostic[] = [];
|
|
19
|
+
|
|
20
|
+
for (const item of session.items) {
|
|
21
|
+
for (const diagnostic of item.analysis.diagnostics) {
|
|
22
|
+
const base = diagnostic.name
|
|
23
|
+
? diagnostic
|
|
24
|
+
: toDiagnostic(diagnostic, item.definitionFormal, {
|
|
25
|
+
constituentId: item.id,
|
|
26
|
+
alias: item.alias
|
|
27
|
+
});
|
|
28
|
+
records.push({
|
|
29
|
+
...base,
|
|
30
|
+
kind: DiagnosticKind.EXPRESSION,
|
|
31
|
+
constituentId: base.constituentId ?? item.id,
|
|
32
|
+
alias: base.alias ?? item.alias
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const enriched = enrichSessionConstituents(session);
|
|
38
|
+
for (const { cst } of enriched) {
|
|
39
|
+
for (const diagnostic of cst.diagnostics) {
|
|
40
|
+
if (diagnostic.kind !== DiagnosticKind.SCHEMA) {
|
|
41
|
+
continue;
|
|
42
|
+
}
|
|
43
|
+
records.push(expandCstDiagnostic(cst, diagnostic));
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
return records;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/** Collect model diagnostics for a session (requires schema items). */
|
|
51
|
+
export function collectModelDiagnostics(session: SessionState): Diagnostic[] {
|
|
52
|
+
if (session.items.length === 0) {
|
|
53
|
+
return [];
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
const records: Diagnostic[] = [];
|
|
57
|
+
const enriched = enrichSessionConstituents(session);
|
|
58
|
+
const schema = enriched.map(entry => entry.cst);
|
|
59
|
+
const engine = modelAdapter.createEngine(session);
|
|
60
|
+
engine.recalculateAll();
|
|
61
|
+
for (const cst of schema) {
|
|
62
|
+
if (!isModelIssue(engine, cst)) {
|
|
63
|
+
continue;
|
|
64
|
+
}
|
|
65
|
+
const diagnostic = modelStatusDiagnostic(engine.getCstStatus(cst.id), cst);
|
|
66
|
+
if (diagnostic) {
|
|
67
|
+
records.push(diagnostic);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
return records;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/** Collect expression, schema, and model diagnostics for a session. */
|
|
75
|
+
export function collectSessionDiagnostics(session: SessionState): Diagnostic[] {
|
|
76
|
+
return [...collectSchemaDiagnostics(session), ...collectModelDiagnostics(session)];
|
|
77
|
+
}
|
|
@@ -1,8 +1,7 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import { type ExpressionType, RSLangAnalyzer, type Value, type ValueClass } from '@rsconcept/domain/rslang';
|
|
1
|
+
import { DiagnosticKind, expressionDiagnostic } from './diagnostic-assembly';
|
|
2
|
+
import { type RSErrorDescription, type Value } from '@rsconcept/domain/rslang';
|
|
4
3
|
import { isBaseSet } from '@rsconcept/domain/library/rsform-api';
|
|
5
|
-
import {
|
|
4
|
+
import { CstType, type RSForm } from '@rsconcept/domain/library/rsform';
|
|
6
5
|
import { RSEngine, type RSEngineServices } from '@rsconcept/domain/library/rsengine';
|
|
7
6
|
import { type BasicBinding, EvalStatus, type RSModel } from '@rsconcept/domain/library/rsmodel';
|
|
8
7
|
import {
|
|
@@ -14,14 +13,13 @@ import {
|
|
|
14
13
|
} from '@rsconcept/domain/library/rsmodel-api';
|
|
15
14
|
|
|
16
15
|
import {
|
|
17
|
-
type ConstituentaState,
|
|
18
16
|
type EvaluationResult,
|
|
19
17
|
type RecalculateModelResult,
|
|
20
18
|
type SessionModelState,
|
|
21
19
|
type SessionState,
|
|
22
20
|
type SetConstituentaValueInput
|
|
23
21
|
} from '../models';
|
|
24
|
-
import {
|
|
22
|
+
import { buildRSFormFromSession } from './rsform-builder';
|
|
25
23
|
|
|
26
24
|
const SESSION_MODEL_ID = 0;
|
|
27
25
|
|
|
@@ -73,7 +71,14 @@ export class ModelAdapter {
|
|
|
73
71
|
? EvalStatus.EVAL_FAIL
|
|
74
72
|
: EvalStatus.EMPTY
|
|
75
73
|
: EvalStatus.HAS_DATA;
|
|
76
|
-
return toPublicEvaluationResult(
|
|
74
|
+
return toPublicEvaluationResult(
|
|
75
|
+
result.value,
|
|
76
|
+
result.errors,
|
|
77
|
+
result.iterations,
|
|
78
|
+
result.cacheHits,
|
|
79
|
+
status,
|
|
80
|
+
expression
|
|
81
|
+
);
|
|
77
82
|
}
|
|
78
83
|
|
|
79
84
|
public evaluateConstituenta(session: SessionState, constituentId: number): EvaluationResult {
|
|
@@ -84,7 +89,15 @@ export class ModelAdapter {
|
|
|
84
89
|
const engine = this.createEngine(session);
|
|
85
90
|
const result = engine.calculateCst(constituentId);
|
|
86
91
|
const status = engine.getCstStatus(constituentId);
|
|
87
|
-
return toPublicEvaluationResult(
|
|
92
|
+
return toPublicEvaluationResult(
|
|
93
|
+
result.value,
|
|
94
|
+
result.errors,
|
|
95
|
+
result.iterations,
|
|
96
|
+
result.cacheHits,
|
|
97
|
+
status,
|
|
98
|
+
cst.definitionFormal,
|
|
99
|
+
{ constituentId: cst.id, alias: cst.alias }
|
|
100
|
+
);
|
|
88
101
|
}
|
|
89
102
|
|
|
90
103
|
public recalculateModel(session: SessionState): RecalculateModelResult {
|
|
@@ -155,55 +168,6 @@ function createInMemoryServices(session: SessionState): RSEngineServices {
|
|
|
155
168
|
};
|
|
156
169
|
}
|
|
157
170
|
|
|
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
171
|
function buildRSModelFromSession(session: SessionState): RSModel {
|
|
208
172
|
return {
|
|
209
173
|
id: SESSION_MODEL_ID,
|
|
@@ -217,54 +181,19 @@ function buildRSModelFromSession(session: SessionState): RSModel {
|
|
|
217
181
|
} as unknown as RSModel;
|
|
218
182
|
}
|
|
219
183
|
|
|
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
184
|
function toPublicEvaluationResult(
|
|
261
185
|
value: Value | null,
|
|
262
|
-
errors:
|
|
186
|
+
errors: RSErrorDescription[],
|
|
263
187
|
iterations: number,
|
|
264
188
|
cacheHits: number,
|
|
265
|
-
status: EvalStatus
|
|
189
|
+
status: EvalStatus,
|
|
190
|
+
expression: string,
|
|
191
|
+
target?: { constituentId?: number; alias?: string }
|
|
266
192
|
): EvaluationResult {
|
|
267
|
-
const diagnostics = errors.map(
|
|
193
|
+
const diagnostics = errors.map(error => ({
|
|
194
|
+
...expressionDiagnostic(error, expression, target),
|
|
195
|
+
kind: DiagnosticKind.MODEL
|
|
196
|
+
}));
|
|
268
197
|
return {
|
|
269
198
|
success: diagnostics.length === 0 && value !== null,
|
|
270
199
|
value: value as EvaluationResult['value'],
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
import { Graph } from '@rsconcept/domain/graph/graph';
|
|
2
|
+
import { extractGlobals } from '@rsconcept/domain/rslang/api';
|
|
3
|
+
import { type ExpressionType, RSLangAnalyzer, type ValueClass } from '@rsconcept/domain/rslang';
|
|
4
|
+
import { assignSchemaDiagnostics } from '@rsconcept/domain/library';
|
|
5
|
+
import { isBasicConcept, isLogical, normalizeExpression } from '@rsconcept/domain/library/rsform-api';
|
|
6
|
+
import { type Constituenta, CstType, type RSForm } from '@rsconcept/domain/library/rsform';
|
|
7
|
+
|
|
8
|
+
import { type ConstituentaState, type SessionState } from '../models';
|
|
9
|
+
|
|
10
|
+
export interface EnrichedConstituenta {
|
|
11
|
+
item: ConstituentaState;
|
|
12
|
+
cst: Constituenta;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
/** Build a domain {@link RSForm} view from session state (same shape as Portal loader output). */
|
|
16
|
+
export function buildRSFormFromSession(session: SessionState): RSForm {
|
|
17
|
+
const graph = new Graph<number>();
|
|
18
|
+
const cstByAlias = new Map<string, Constituenta>();
|
|
19
|
+
const cstByID = new Map<number, Constituenta>();
|
|
20
|
+
const analyzer = new RSLangAnalyzer();
|
|
21
|
+
|
|
22
|
+
const items = session.items.map(item => {
|
|
23
|
+
const cst = toFrontendConstituenta(item);
|
|
24
|
+
cstByAlias.set(cst.alias, cst);
|
|
25
|
+
cstByID.set(cst.id, cst);
|
|
26
|
+
graph.addNode(cst.id);
|
|
27
|
+
if (item.cstType === CstType.BASE) {
|
|
28
|
+
analyzer.addBase(cst.alias);
|
|
29
|
+
}
|
|
30
|
+
if (cst.effectiveType) {
|
|
31
|
+
analyzer.setGlobal(cst.alias, cst.effectiveType, cst.analysis.valueClass as ValueClass | null);
|
|
32
|
+
}
|
|
33
|
+
return cst;
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
for (const cst of items) {
|
|
37
|
+
for (const alias of extractGlobals(cst.definition_formal)) {
|
|
38
|
+
const source = cstByAlias.get(alias);
|
|
39
|
+
if (source) {
|
|
40
|
+
graph.addEdge(source.id, cst.id);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
return {
|
|
46
|
+
id: 0,
|
|
47
|
+
items,
|
|
48
|
+
cstByAlias,
|
|
49
|
+
cstByID,
|
|
50
|
+
graph,
|
|
51
|
+
analyzer,
|
|
52
|
+
inheritance: [],
|
|
53
|
+
attribution: [],
|
|
54
|
+
attribution_graph: graph.clone(),
|
|
55
|
+
oss: [],
|
|
56
|
+
models: [],
|
|
57
|
+
editors: [],
|
|
58
|
+
versions: [],
|
|
59
|
+
is_produced: false,
|
|
60
|
+
is_attributive: false,
|
|
61
|
+
version: 'latest'
|
|
62
|
+
} as unknown as RSForm;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/** Enrich session constituents with Portal-style schema metadata used for diagnostics. */
|
|
66
|
+
export function enrichSessionConstituents(session: SessionState): EnrichedConstituenta[] {
|
|
67
|
+
const schema = buildRSFormFromSession(session);
|
|
68
|
+
const normalizedDefinitions = new Map<number, string>();
|
|
69
|
+
const resolveAlias = (id: number) => schema.cstByID.get(id)?.alias ?? String(id);
|
|
70
|
+
|
|
71
|
+
for (const item of session.items) {
|
|
72
|
+
const normalized = normalizeExpression(item.definitionFormal, item.cstType);
|
|
73
|
+
if (normalized) {
|
|
74
|
+
normalizedDefinitions.set(item.id, normalized);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
assignSchemaDiagnostics(schema.items, resolveAlias, normalizedDefinitions);
|
|
79
|
+
|
|
80
|
+
return session.items.map(item => ({
|
|
81
|
+
item,
|
|
82
|
+
cst: schema.cstByID.get(item.id)!
|
|
83
|
+
}));
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
function toFrontendConstituenta(item: ConstituentaState): Constituenta {
|
|
87
|
+
const effectiveType = (item.analysis.type ?? null) as ExpressionType | null;
|
|
88
|
+
return {
|
|
89
|
+
id: item.id,
|
|
90
|
+
alias: item.alias,
|
|
91
|
+
cst_type: item.cstType,
|
|
92
|
+
definition_formal: item.definitionFormal,
|
|
93
|
+
definition_raw: item.definitionFormal,
|
|
94
|
+
definition_resolved: item.definitionFormal,
|
|
95
|
+
term_raw: item.term,
|
|
96
|
+
term_resolved: item.term,
|
|
97
|
+
term_forms: [],
|
|
98
|
+
convention: item.convention,
|
|
99
|
+
typification_manual: '',
|
|
100
|
+
value_is_property: false,
|
|
101
|
+
crucial: false,
|
|
102
|
+
attributes: [],
|
|
103
|
+
diagnostics: [],
|
|
104
|
+
analysis: {
|
|
105
|
+
success: item.analysis.success,
|
|
106
|
+
type: effectiveType,
|
|
107
|
+
valueClass: item.analysis.valueClass
|
|
108
|
+
},
|
|
109
|
+
effectiveType,
|
|
110
|
+
is_type_mismatch: false,
|
|
111
|
+
schema: 0,
|
|
112
|
+
cst_class: 'derived',
|
|
113
|
+
status: item.analysis.success ? 'verified' : 'incorrect',
|
|
114
|
+
is_template: false,
|
|
115
|
+
is_simple_expression: true,
|
|
116
|
+
parent_schema_index: 0,
|
|
117
|
+
parent_schema: null,
|
|
118
|
+
is_inherited: false,
|
|
119
|
+
has_inherited_children: false,
|
|
120
|
+
spawn: [],
|
|
121
|
+
spawn_alias: []
|
|
122
|
+
} as unknown as Constituenta;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
export function needsBasicMetadata(item: ConstituentaState): boolean {
|
|
126
|
+
return isBasicConcept(item.cstType) && !isLogical(item.cstType);
|
|
127
|
+
}
|