@saptools/service-flow 0.1.66 → 0.1.68
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +14 -0
- package/README.md +20 -8
- package/TECHNICAL-NOTE.md +27 -0
- package/dist/{chunk-TOVX4WYH.js → chunk-AEM4JY22.js} +12676 -6714
- package/dist/chunk-AEM4JY22.js.map +1 -0
- package/dist/cli.js +2325 -413
- package/dist/cli.js.map +1 -1
- package/dist/index.d.ts +61 -2
- package/dist/index.js +1 -1
- package/package.json +1 -1
- package/src/cli/003-doctor-package-resolution.ts +68 -0
- package/src/cli/doctor.ts +25 -14
- package/src/db/000-call-fact-repository.ts +475 -342
- package/src/db/001-fact-lifecycle.ts +60 -30
- package/src/db/002-fact-json-inventory.ts +169 -0
- package/src/db/003-current-fact-semantics.ts +698 -0
- package/src/db/004-package-target-invalidation.ts +173 -0
- package/src/db/005-schema-structure.ts +201 -0
- package/src/db/006-relative-symbol-resolution.ts +443 -0
- package/src/db/007-package-fact-semantics.ts +573 -0
- package/src/db/008-relative-fact-semantics.ts +207 -0
- package/src/db/009-binding-fact-semantics.ts +347 -0
- package/src/db/010-package-symbol-surface-semantics.ts +320 -0
- package/src/db/011-symbol-call-semantics.ts +144 -0
- package/src/db/012-binding-reference-proof.ts +264 -0
- package/src/db/migrations.ts +16 -3
- package/src/db/repositories.ts +113 -6
- package/src/db/schema.ts +4 -2
- package/src/index.ts +12 -0
- package/src/indexer/repository-indexer.ts +72 -11
- package/src/indexer/workspace-indexer.ts +123 -32
- package/src/linker/003-package-import-symbol-resolver.ts +363 -131
- package/src/linker/004-event-subscription-handler-linker.ts +2 -0
- package/src/linker/005-odata-path-structure.ts +371 -0
- package/src/linker/cross-repo-linker.ts +2 -1
- package/src/linker/odata-path-normalizer.ts +273 -180
- package/src/linker/service-resolver.ts +197 -77
- package/src/parsers/002-symbol-import-bindings.ts +516 -0
- package/src/parsers/003-package-public-surface.ts +661 -0
- package/src/parsers/004-fact-identity.ts +108 -0
- package/src/parsers/005-event-subscription-facts.ts +281 -0
- package/src/parsers/006-binding-identity.ts +343 -0
- package/src/parsers/007-source-fact-reconciliation.ts +105 -0
- package/src/parsers/008-package-surface-publication.ts +82 -0
- package/src/parsers/009-symbol-call-facts.ts +528 -0
- package/src/parsers/010-package-public-surface-analysis.ts +352 -0
- package/src/parsers/011-binding-lexical-scope.ts +583 -0
- package/src/parsers/012-package-fact-contract.ts +306 -0
- package/src/parsers/013-executable-body-eligibility.ts +35 -0
- package/src/parsers/014-service-binding-helper-flow.ts +240 -0
- package/src/parsers/015-service-binding-collector.ts +693 -0
- package/src/parsers/016-local-symbol-reference.ts +261 -0
- package/src/parsers/017-symbol-derived-contexts.ts +268 -0
- package/src/parsers/018-package-commonjs-syntax.ts +142 -0
- package/src/parsers/019-binding-assignment-targets.ts +76 -0
- package/src/parsers/020-stable-local-value.ts +217 -0
- package/src/parsers/021-binding-visibility.ts +152 -0
- package/src/parsers/operation-path-analysis.ts +6 -1
- package/src/parsers/outbound-call-parser.ts +19 -6
- package/src/parsers/package-json-parser.ts +45 -3
- package/src/parsers/service-binding-parser-helpers.ts +86 -15
- package/src/parsers/service-binding-parser.ts +147 -597
- package/src/parsers/symbol-parser.ts +482 -353
- package/src/trace/002-trace-diagnostics.ts +36 -1
- package/src/trace/016-compact-projector.ts +16 -17
- package/src/trace/020-compact-field-projection.ts +48 -37
- package/src/trace/021-compact-decision-normalization.ts +105 -0
- package/src/trace/022-trace-fact-preflight.ts +21 -0
- package/src/trace/023-nested-event-scopes.ts +23 -0
- package/src/trace/024-compact-observation-decision.ts +76 -0
- package/src/trace/025-trace-implementation-scope.ts +123 -0
- package/src/trace/026-trace-start-scope.ts +335 -0
- package/src/trace/027-trace-scope-execution.ts +566 -0
- package/src/trace/028-trace-operation-execution.ts +336 -0
- package/src/trace/029-trace-start-implementation.ts +172 -0
- package/src/trace/trace-engine.ts +122 -624
- package/src/types.ts +56 -0
- package/src/utils/001-placeholders.ts +188 -10
- package/src/version.ts +1 -1
- package/dist/chunk-TOVX4WYH.js.map +0 -1
|
@@ -1,5 +1,18 @@
|
|
|
1
1
|
import type { Db } from './connection.js';
|
|
2
2
|
import { CURRENT_SCHEMA_VERSION, schemaVersion } from './migrations.js';
|
|
3
|
+
import {
|
|
4
|
+
invalidFactJsonCategories,
|
|
5
|
+
type FactJsonCategoryCount,
|
|
6
|
+
} from './002-fact-json-inventory.js';
|
|
7
|
+
import {
|
|
8
|
+
invalidFactSemanticCategories,
|
|
9
|
+
type FactSemanticCategoryCount,
|
|
10
|
+
type PackageFactPhase,
|
|
11
|
+
} from './003-current-fact-semantics.js';
|
|
12
|
+
import {
|
|
13
|
+
invalidSchemaStructureCategories,
|
|
14
|
+
type SchemaStructureCategoryCount,
|
|
15
|
+
} from './005-schema-structure.js';
|
|
3
16
|
import { ANALYZER_VERSION } from '../version.js';
|
|
4
17
|
|
|
5
18
|
export type FactLifecycleCode =
|
|
@@ -18,6 +31,7 @@ const remediation = [
|
|
|
18
31
|
'service-flow index --workspace /workspace --force',
|
|
19
32
|
'service-flow link --workspace /workspace --force',
|
|
20
33
|
].join('\n');
|
|
34
|
+
const CATEGORY_LIMIT = 24;
|
|
21
35
|
|
|
22
36
|
function count(db: Db, sql: string, ...params: unknown[]): number {
|
|
23
37
|
const row = db.prepare(sql).get(...params);
|
|
@@ -32,35 +46,13 @@ function oldAnalyzerCount(db: Db, workspaceId?: number): number {
|
|
|
32
46
|
workspaceId, workspaceId, ANALYZER_VERSION);
|
|
33
47
|
}
|
|
34
48
|
|
|
35
|
-
function invalidCurrentFactCount(db: Db, workspaceId?: number): number {
|
|
36
|
-
const outbound = count(db, `SELECT COUNT(*) count
|
|
37
|
-
FROM outbound_calls c JOIN repositories r ON r.id=c.repo_id
|
|
38
|
-
WHERE (? IS NULL OR r.workspace_id=?) AND r.fact_analyzer_version=?
|
|
39
|
-
AND (typeof(c.call_site_start_offset)<>'integer'
|
|
40
|
-
OR typeof(c.call_site_end_offset)<>'integer'
|
|
41
|
-
OR c.call_site_start_offset<0
|
|
42
|
-
OR c.call_site_end_offset<=c.call_site_start_offset)`,
|
|
43
|
-
workspaceId, workspaceId, ANALYZER_VERSION);
|
|
44
|
-
const symbols = count(db, `SELECT COUNT(*) count
|
|
45
|
-
FROM symbol_calls c JOIN repositories r ON r.id=c.repo_id
|
|
46
|
-
WHERE (? IS NULL OR r.workspace_id=?)
|
|
47
|
-
AND (c.call_role='legacy_unknown'
|
|
48
|
-
OR (r.fact_analyzer_version=? AND (
|
|
49
|
-
typeof(c.call_site_start_offset)<>'integer'
|
|
50
|
-
OR typeof(c.call_site_end_offset)<>'integer'
|
|
51
|
-
OR c.call_site_start_offset<0
|
|
52
|
-
OR c.call_site_end_offset<=c.call_site_start_offset
|
|
53
|
-
OR c.call_role NOT IN ('ordinary_call','event_subscribe_handler'))))`,
|
|
54
|
-
workspaceId, workspaceId, ANALYZER_VERSION);
|
|
55
|
-
return outbound + symbols;
|
|
56
|
-
}
|
|
57
|
-
|
|
58
49
|
export function factLifecycleDiagnostic(
|
|
59
50
|
db: Db,
|
|
60
51
|
workspaceId?: number,
|
|
52
|
+
phase: PackageFactPhase = 'pre_package',
|
|
61
53
|
): FactLifecycleDiagnostic | undefined {
|
|
62
54
|
return schemaLifecycleDiagnostic(db)
|
|
63
|
-
?? currentFactLifecycleDiagnostic(db, workspaceId);
|
|
55
|
+
?? currentFactLifecycleDiagnostic(db, workspaceId, phase);
|
|
64
56
|
}
|
|
65
57
|
|
|
66
58
|
export function schemaLifecycleDiagnostic(
|
|
@@ -83,29 +75,67 @@ export function schemaLifecycleDiagnostic(
|
|
|
83
75
|
currentSchemaVersion: currentSchema,
|
|
84
76
|
requiredSchemaVersion: CURRENT_SCHEMA_VERSION,
|
|
85
77
|
};
|
|
78
|
+
const structureCategories = invalidSchemaStructureCategories(db);
|
|
79
|
+
if (structureCategories.length > 0)
|
|
80
|
+
return reindexDiagnostic(0, structureCategories);
|
|
86
81
|
return undefined;
|
|
87
82
|
}
|
|
88
83
|
|
|
89
84
|
export function currentFactLifecycleDiagnostic(
|
|
90
85
|
db: Db,
|
|
91
86
|
workspaceId?: number,
|
|
87
|
+
phase: PackageFactPhase = 'pre_package',
|
|
92
88
|
): FactLifecycleDiagnostic | undefined {
|
|
93
89
|
const staleRepositories = oldAnalyzerCount(db, workspaceId);
|
|
94
|
-
|
|
95
|
-
|
|
90
|
+
if (staleRepositories > 0)
|
|
91
|
+
return reindexDiagnostic(staleRepositories, []);
|
|
92
|
+
const jsonCategories = invalidFactJsonCategories(db, workspaceId);
|
|
93
|
+
if (jsonCategories.length > 0)
|
|
94
|
+
return reindexDiagnostic(0, jsonCategories);
|
|
95
|
+
const semanticCategories = invalidFactSemanticCategories(
|
|
96
|
+
db, workspaceId, phase,
|
|
97
|
+
);
|
|
98
|
+
if (semanticCategories.length === 0) return undefined;
|
|
99
|
+
return reindexDiagnostic(0, semanticCategories);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
type InvalidFactCategory =
|
|
103
|
+
| FactJsonCategoryCount
|
|
104
|
+
| FactSemanticCategoryCount
|
|
105
|
+
| SchemaStructureCategoryCount;
|
|
106
|
+
|
|
107
|
+
function reindexDiagnostic(
|
|
108
|
+
staleRepositories: number,
|
|
109
|
+
categories: InvalidFactCategory[],
|
|
110
|
+
): FactLifecycleDiagnostic {
|
|
111
|
+
const invalidFacts = categories.reduce((sum, item) => sum + item.count, 0);
|
|
112
|
+
const shown = categories.slice(0, CATEGORY_LIMIT);
|
|
96
113
|
return {
|
|
97
114
|
severity: 'error',
|
|
98
115
|
code: 'reindex_required',
|
|
99
|
-
message: '
|
|
116
|
+
message: 'Current facts are stale or fail bounded semantic integrity checks; force index and link before tracing or rebuilding graph edges.',
|
|
100
117
|
remediation,
|
|
101
118
|
staleRepositoryCount: staleRepositories,
|
|
102
119
|
invalidCallFactCount: invalidFacts,
|
|
120
|
+
invalidFactCategories: shown,
|
|
121
|
+
invalidFactCategoryCount: categories.length,
|
|
122
|
+
shownInvalidFactCategoryCount: shown.length,
|
|
123
|
+
omittedInvalidFactCategoryCount: categories.length - shown.length,
|
|
103
124
|
requiredAnalyzerVersion: ANALYZER_VERSION,
|
|
104
125
|
};
|
|
105
126
|
}
|
|
106
127
|
|
|
107
|
-
export function assertWorkspaceLinkable(
|
|
108
|
-
|
|
128
|
+
export function assertWorkspaceLinkable(
|
|
129
|
+
db: Db,
|
|
130
|
+
workspaceId: number,
|
|
131
|
+
phase: PackageFactPhase = 'pre_package',
|
|
132
|
+
): void {
|
|
133
|
+
const diagnostic = factLifecycleDiagnostic(db, workspaceId, phase);
|
|
109
134
|
if (!diagnostic) return;
|
|
110
|
-
|
|
135
|
+
const categories = Array.isArray(diagnostic.invalidFactCategories)
|
|
136
|
+
? ` categories=${JSON.stringify(diagnostic.invalidFactCategories)}`
|
|
137
|
+
: '';
|
|
138
|
+
throw new Error(
|
|
139
|
+
`${diagnostic.code}: ${diagnostic.message}${categories}\n${diagnostic.remediation}`,
|
|
140
|
+
);
|
|
111
141
|
}
|
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
import type { Db } from './connection.js';
|
|
2
|
+
import { ANALYZER_VERSION } from '../version.js';
|
|
3
|
+
|
|
4
|
+
export type FactJsonShape = 'object' | 'array';
|
|
5
|
+
|
|
6
|
+
export interface FactJsonInventoryItem {
|
|
7
|
+
table: string;
|
|
8
|
+
column: string;
|
|
9
|
+
nullable: boolean;
|
|
10
|
+
shape: FactJsonShape;
|
|
11
|
+
consumers: readonly string[];
|
|
12
|
+
repositoryJoin: string;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export interface FactJsonCategoryCount {
|
|
16
|
+
category: string;
|
|
17
|
+
count: number;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
const directRepositoryJoin = 'JOIN repositories lifecycle_repo ON lifecycle_repo.id=fact.repo_id';
|
|
21
|
+
|
|
22
|
+
export const LINK_FACT_JSON_INVENTORY: readonly FactJsonInventoryItem[] = [
|
|
23
|
+
{
|
|
24
|
+
table: 'repositories',
|
|
25
|
+
column: 'dependencies_json',
|
|
26
|
+
nullable: false,
|
|
27
|
+
shape: 'object',
|
|
28
|
+
consumers: ['helper-package-linker'],
|
|
29
|
+
repositoryJoin: '',
|
|
30
|
+
},
|
|
31
|
+
{
|
|
32
|
+
table: 'repositories',
|
|
33
|
+
column: 'package_public_surface_json',
|
|
34
|
+
nullable: false,
|
|
35
|
+
shape: 'object',
|
|
36
|
+
consumers: ['package-import-symbol-resolver'],
|
|
37
|
+
repositoryJoin: '',
|
|
38
|
+
},
|
|
39
|
+
{
|
|
40
|
+
table: 'handler_methods',
|
|
41
|
+
column: 'decorator_resolution_json',
|
|
42
|
+
nullable: false,
|
|
43
|
+
shape: 'object',
|
|
44
|
+
consumers: ['implementation-linker', 'trace-selector'],
|
|
45
|
+
repositoryJoin:
|
|
46
|
+
'JOIN handler_classes owner ON owner.id=fact.handler_class_id JOIN repositories lifecycle_repo ON lifecycle_repo.id=owner.repo_id',
|
|
47
|
+
},
|
|
48
|
+
{
|
|
49
|
+
table: 'symbols',
|
|
50
|
+
column: 'evidence_json',
|
|
51
|
+
nullable: true,
|
|
52
|
+
shape: 'object',
|
|
53
|
+
consumers: ['symbol-call-resolver', 'implementation-linker'],
|
|
54
|
+
repositoryJoin: directRepositoryJoin,
|
|
55
|
+
},
|
|
56
|
+
{
|
|
57
|
+
table: 'symbol_calls',
|
|
58
|
+
column: 'evidence_json',
|
|
59
|
+
nullable: false,
|
|
60
|
+
shape: 'object',
|
|
61
|
+
consumers: ['package-import-symbol-resolver', 'event-subscription-linker'],
|
|
62
|
+
repositoryJoin: directRepositoryJoin,
|
|
63
|
+
},
|
|
64
|
+
{
|
|
65
|
+
table: 'outbound_calls',
|
|
66
|
+
column: 'evidence_json',
|
|
67
|
+
nullable: false,
|
|
68
|
+
shape: 'object',
|
|
69
|
+
consumers: ['cross-repository-linker', 'trace-engine'],
|
|
70
|
+
repositoryJoin: directRepositoryJoin,
|
|
71
|
+
},
|
|
72
|
+
{
|
|
73
|
+
table: 'outbound_calls',
|
|
74
|
+
column: 'alias_chain_json',
|
|
75
|
+
nullable: true,
|
|
76
|
+
shape: 'array',
|
|
77
|
+
consumers: ['cross-repository-linker', 'trace-engine'],
|
|
78
|
+
repositoryJoin: directRepositoryJoin,
|
|
79
|
+
},
|
|
80
|
+
{
|
|
81
|
+
table: 'service_bindings',
|
|
82
|
+
column: 'placeholders_json',
|
|
83
|
+
nullable: false,
|
|
84
|
+
shape: 'array',
|
|
85
|
+
consumers: ['cross-repository-linker', 'trace-engine'],
|
|
86
|
+
repositoryJoin: directRepositoryJoin,
|
|
87
|
+
},
|
|
88
|
+
{
|
|
89
|
+
table: 'service_bindings',
|
|
90
|
+
column: 'helper_chain_json',
|
|
91
|
+
nullable: true,
|
|
92
|
+
shape: 'array',
|
|
93
|
+
consumers: ['cross-repository-linker', 'trace-engine'],
|
|
94
|
+
repositoryJoin: directRepositoryJoin,
|
|
95
|
+
},
|
|
96
|
+
{
|
|
97
|
+
table: 'cds_requires',
|
|
98
|
+
column: 'raw_json',
|
|
99
|
+
nullable: false,
|
|
100
|
+
shape: 'object',
|
|
101
|
+
consumers: ['service-resolution'],
|
|
102
|
+
repositoryJoin: directRepositoryJoin,
|
|
103
|
+
},
|
|
104
|
+
] as const;
|
|
105
|
+
|
|
106
|
+
function repositoryPredicate(item: FactJsonInventoryItem): string {
|
|
107
|
+
if (item.table === 'repositories') {
|
|
108
|
+
return `fact.fact_analyzer_version=?
|
|
109
|
+
AND (? IS NULL OR fact.workspace_id=?)`;
|
|
110
|
+
}
|
|
111
|
+
return `lifecycle_repo.fact_analyzer_version=?
|
|
112
|
+
AND (? IS NULL OR lifecycle_repo.workspace_id=?)`;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
function invalidJsonCount(
|
|
116
|
+
db: Db,
|
|
117
|
+
item: FactJsonInventoryItem,
|
|
118
|
+
workspaceId: number | undefined,
|
|
119
|
+
): number {
|
|
120
|
+
const nullClause = item.nullable
|
|
121
|
+
? `fact.${item.column} IS NOT NULL AND`
|
|
122
|
+
: `fact.${item.column} IS NULL OR`;
|
|
123
|
+
const row = db.prepare(`SELECT COUNT(*) count FROM ${item.table} fact
|
|
124
|
+
${item.repositoryJoin}
|
|
125
|
+
WHERE ${repositoryPredicate(item)}
|
|
126
|
+
AND (${nullClause} json_valid(fact.${item.column})=0)`).get(
|
|
127
|
+
ANALYZER_VERSION, workspaceId, workspaceId,
|
|
128
|
+
);
|
|
129
|
+
return Number(row?.count ?? 0);
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
function wrongShapeCount(
|
|
133
|
+
db: Db,
|
|
134
|
+
item: FactJsonInventoryItem,
|
|
135
|
+
workspaceId: number | undefined,
|
|
136
|
+
): number {
|
|
137
|
+
const row = db.prepare(`SELECT COUNT(*) count FROM ${item.table} fact
|
|
138
|
+
${item.repositoryJoin}
|
|
139
|
+
WHERE ${repositoryPredicate(item)}
|
|
140
|
+
AND fact.${item.column} IS NOT NULL
|
|
141
|
+
AND json_type(fact.${item.column})<>?`).get(
|
|
142
|
+
ANALYZER_VERSION, workspaceId, workspaceId, item.shape,
|
|
143
|
+
);
|
|
144
|
+
return Number(row?.count ?? 0);
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
function category(
|
|
148
|
+
item: FactJsonInventoryItem,
|
|
149
|
+
suffix: string,
|
|
150
|
+
count: number,
|
|
151
|
+
): FactJsonCategoryCount {
|
|
152
|
+
return {
|
|
153
|
+
category: `json_${item.table}_${item.column}_${suffix}`,
|
|
154
|
+
count,
|
|
155
|
+
};
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
export function invalidFactJsonCategories(
|
|
159
|
+
db: Db,
|
|
160
|
+
workspaceId?: number,
|
|
161
|
+
): FactJsonCategoryCount[] {
|
|
162
|
+
const invalid = LINK_FACT_JSON_INVENTORY.map((item) =>
|
|
163
|
+
category(item, 'invalid', invalidJsonCount(db, item, workspaceId)))
|
|
164
|
+
.filter((item) => item.count > 0);
|
|
165
|
+
if (invalid.length > 0) return invalid;
|
|
166
|
+
return LINK_FACT_JSON_INVENTORY.map((item) =>
|
|
167
|
+
category(item, 'wrong_shape', wrongShapeCount(db, item, workspaceId)))
|
|
168
|
+
.filter((item) => item.count > 0);
|
|
169
|
+
}
|