@topogram/cli 0.3.76 → 0.3.78
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/package.json +1 -1
- package/src/adoption/plan/index.js +12 -1
- package/src/cli/commands/import/workspace.js +1 -0
- package/src/cli.js +3 -3
- package/src/import/core/runner/candidates.js +2 -0
- package/src/import/core/runner/reports.js +12 -5
- package/src/import/core/runner/tracks.js +1 -1
- package/src/import/core/shared/files.js +76 -1
- package/src/import/core/shared/next-app.js +2 -2
- package/src/import/core/shared/ui-routes.js +106 -0
- package/src/import/core/shared.js +8 -1
- package/src/import/enrichers/django-rest.js +4 -4
- package/src/import/enrichers/rails-controllers.js +3 -3
- package/src/import/enrichers/rails-models.js +3 -3
- package/src/import/extractors/api/aspnet-core.js +5 -5
- package/src/import/extractors/api/django-routes.js +5 -5
- package/src/import/extractors/api/express.js +4 -4
- package/src/import/extractors/api/fastify.js +7 -7
- package/src/import/extractors/api/flutter-dio.js +4 -4
- package/src/import/extractors/api/generic-route-fallback.js +3 -2
- package/src/import/extractors/api/graphql-code-first.js +3 -3
- package/src/import/extractors/api/graphql-sdl.js +5 -5
- package/src/import/extractors/api/jaxrs.js +3 -3
- package/src/import/extractors/api/micronaut.js +3 -3
- package/src/import/extractors/api/openapi-code.js +4 -4
- package/src/import/extractors/api/openapi.js +3 -3
- package/src/import/extractors/api/rails-routes.js +3 -3
- package/src/import/extractors/api/react-native-repository.js +3 -3
- package/src/import/extractors/api/retrofit.js +3 -3
- package/src/import/extractors/api/spring-web.js +3 -3
- package/src/import/extractors/api/swift-webapi.js +3 -3
- package/src/import/extractors/api/trpc.js +4 -4
- package/src/import/extractors/cli/generic.js +5 -4
- package/src/import/extractors/db/django-models.js +4 -4
- package/src/import/extractors/db/dotnet-models.js +4 -4
- package/src/import/extractors/db/drizzle.js +21 -9
- package/src/import/extractors/db/ef-core.js +5 -5
- package/src/import/extractors/db/flutter-entities.js +3 -3
- package/src/import/extractors/db/jpa.js +3 -3
- package/src/import/extractors/db/liquibase.js +3 -3
- package/src/import/extractors/db/maintained-seams.js +27 -4
- package/src/import/extractors/db/mybatis-xml.js +4 -4
- package/src/import/extractors/db/prisma.js +10 -3
- package/src/import/extractors/db/rails-schema.js +3 -3
- package/src/import/extractors/db/react-native-entities.js +3 -3
- package/src/import/extractors/db/room.js +5 -5
- package/src/import/extractors/db/snapshot.js +3 -3
- package/src/import/extractors/db/sql.js +3 -3
- package/src/import/extractors/db/swiftdata.js +3 -3
- package/src/import/extractors/ui/android-compose.js +4 -4
- package/src/import/extractors/ui/backend-only.js +3 -3
- package/src/import/extractors/ui/blazor.js +3 -3
- package/src/import/extractors/ui/flutter-screens.js +3 -3
- package/src/import/extractors/ui/maui-xaml.js +4 -4
- package/src/import/extractors/ui/next-app-router.js +26 -5
- package/src/import/extractors/ui/next-pages-router.js +34 -10
- package/src/import/extractors/ui/razor-pages.js +3 -3
- package/src/import/extractors/ui/react-native-screens.js +4 -4
- package/src/import/extractors/ui/react-router.js +34 -6
- package/src/import/extractors/ui/sveltekit.js +34 -6
- package/src/import/extractors/ui/swiftui.js +4 -3
- package/src/import/extractors/ui/uikit.js +3 -3
- package/src/workflows/reconcile/bundle-core/index.js +20 -1
- package/src/workflows/reconcile/candidate-model.js +13 -1
- package/src/workflows/reconcile/impacts/adoption-plan.js +13 -0
- package/src/workflows/reconcile/renderers.js +33 -0
- package/src/workflows/reconcile/workflow.js +2 -1
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import {
|
|
2
2
|
canonicalCandidateTerm,
|
|
3
3
|
dedupeCandidateRecords,
|
|
4
|
-
|
|
4
|
+
findPrimaryImportFiles,
|
|
5
5
|
idHintify,
|
|
6
6
|
makeCandidateRecord,
|
|
7
7
|
relativeTo,
|
|
@@ -82,7 +82,7 @@ export const flutterEntitiesExtractor = {
|
|
|
82
82
|
id: "db.flutter-entities",
|
|
83
83
|
track: "db",
|
|
84
84
|
detect(context) {
|
|
85
|
-
const entityFiles =
|
|
85
|
+
const entityFiles = findPrimaryImportFiles(
|
|
86
86
|
context.paths,
|
|
87
87
|
(filePath) => /\/lib\/features\/.+\/domain\/entities\/.+_entity\.dart$/i.test(filePath)
|
|
88
88
|
);
|
|
@@ -93,7 +93,7 @@ export const flutterEntitiesExtractor = {
|
|
|
93
93
|
};
|
|
94
94
|
},
|
|
95
95
|
extract(context) {
|
|
96
|
-
const entityFiles =
|
|
96
|
+
const entityFiles = findPrimaryImportFiles(
|
|
97
97
|
context.paths,
|
|
98
98
|
(filePath) => /\/lib\/features\/.+\/domain\/entities\/.+_entity\.dart$/i.test(filePath)
|
|
99
99
|
);
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { canonicalCandidateTerm, dedupeCandidateRecords,
|
|
1
|
+
import { canonicalCandidateTerm, dedupeCandidateRecords, findPrimaryImportFiles, makeCandidateRecord, relativeTo, slugify, titleCase } from "../../core/shared.js";
|
|
2
2
|
|
|
3
3
|
function extractAnnotatedFields(text) {
|
|
4
4
|
const fields = [];
|
|
@@ -44,7 +44,7 @@ export const jpaExtractor = {
|
|
|
44
44
|
id: "db.jpa",
|
|
45
45
|
track: "db",
|
|
46
46
|
detect(context) {
|
|
47
|
-
const entityFiles =
|
|
47
|
+
const entityFiles = findPrimaryImportFiles(context.paths, (filePath) => /\.java$/i.test(filePath));
|
|
48
48
|
const count = entityFiles.filter((filePath) => /@Entity\b/.test(context.helpers.readTextIfExists(filePath) || "")).length;
|
|
49
49
|
return {
|
|
50
50
|
score: count > 0 ? 87 : 0,
|
|
@@ -52,7 +52,7 @@ export const jpaExtractor = {
|
|
|
52
52
|
};
|
|
53
53
|
},
|
|
54
54
|
extract(context) {
|
|
55
|
-
const files =
|
|
55
|
+
const files = findPrimaryImportFiles(context.paths, (filePath) => /\.java$/i.test(filePath));
|
|
56
56
|
const findings = [];
|
|
57
57
|
const candidates = { entities: [], enums: [], relations: [], indexes: [] };
|
|
58
58
|
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import {
|
|
2
2
|
canonicalCandidateTerm,
|
|
3
3
|
dedupeCandidateRecords,
|
|
4
|
-
|
|
4
|
+
findPrimaryImportFiles,
|
|
5
5
|
idHintify,
|
|
6
6
|
makeCandidateRecord,
|
|
7
7
|
relativeTo,
|
|
@@ -109,14 +109,14 @@ export const liquibaseExtractor = {
|
|
|
109
109
|
id: "db.liquibase",
|
|
110
110
|
track: "db",
|
|
111
111
|
detect(context) {
|
|
112
|
-
const files =
|
|
112
|
+
const files = findPrimaryImportFiles(context.paths, (filePath) => /db\/changelog\/.+\.(xml|ya?ml)$/i.test(filePath));
|
|
113
113
|
return {
|
|
114
114
|
score: files.length > 0 ? 88 : 0,
|
|
115
115
|
reasons: files.length > 0 ? ["Found Liquibase changelog files"] : []
|
|
116
116
|
};
|
|
117
117
|
},
|
|
118
118
|
extract(context) {
|
|
119
|
-
const changelogFiles =
|
|
119
|
+
const changelogFiles = findPrimaryImportFiles(context.paths, (filePath) => /db\/changelog\/.+\.xml$/i.test(filePath));
|
|
120
120
|
const findings = [];
|
|
121
121
|
const candidates = { entities: [], enums: [], relations: [], indexes: [] };
|
|
122
122
|
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
import path from "node:path";
|
|
4
4
|
|
|
5
|
-
import {
|
|
5
|
+
import { findPrimaryImportFiles, isPrimaryImportSource, makeCandidateRecord, relativeTo } from "../../core/shared.js";
|
|
6
6
|
|
|
7
7
|
/** @param {string} value @returns {string} */
|
|
8
8
|
function toPosix(value) {
|
|
@@ -86,6 +86,13 @@ function maintainedDbSeamCandidate(context, options) {
|
|
|
86
86
|
...(options.migrationsPath ? { migrationsPath: options.migrationsPath } : {})
|
|
87
87
|
};
|
|
88
88
|
const idHint = `seam_${options.tool}_db_migrations`;
|
|
89
|
+
const manualNextSteps = [
|
|
90
|
+
"Review evidence, match_reasons, and missing_decisions before accepting this seam.",
|
|
91
|
+
`Confirm database runtime '${runtimeId}' and projection '${projectionId}' are the right topology targets for the maintained app.`,
|
|
92
|
+
"If accepted, copy proposed_runtime_migration into the matching database runtime's migration block in topogram.project.json.",
|
|
93
|
+
"Keep ownership 'maintained' and apply 'never'; import must not apply migrations or patch maintained app files.",
|
|
94
|
+
"After editing topogram.project.json, run topogram check . --json and the maintained app's migration verification."
|
|
95
|
+
];
|
|
89
96
|
|
|
90
97
|
return makeCandidateRecord({
|
|
91
98
|
kind: "maintained_db_migration_seam",
|
|
@@ -112,6 +119,13 @@ function maintainedDbSeamCandidate(context, options) {
|
|
|
112
119
|
match_reasons: options.matchReasons,
|
|
113
120
|
missing_decisions: options.missingDecisions,
|
|
114
121
|
proposed_runtime_migration: proposedRuntimeMigration,
|
|
122
|
+
manual_next_steps: manualNextSteps,
|
|
123
|
+
project_config_target: {
|
|
124
|
+
file: "topogram.project.json",
|
|
125
|
+
path: `topology.runtimes[id=${runtimeId}].migration`,
|
|
126
|
+
runtime_id: runtimeId,
|
|
127
|
+
projection_id: projectionId
|
|
128
|
+
},
|
|
115
129
|
maintained_modules: [options.schemaPath, options.migrationsPath].filter(Boolean),
|
|
116
130
|
emitted_dependencies: [snapshotPath, projectionId],
|
|
117
131
|
allowed_change_classes: ["proposal_only"],
|
|
@@ -125,7 +139,10 @@ export function inferPrismaMaintainedDbSeams(context, prismaFiles) {
|
|
|
125
139
|
return [];
|
|
126
140
|
}
|
|
127
141
|
const schemaPath = appRelativePath(context, prismaFiles[0]);
|
|
128
|
-
const migrationFiles = /** @type {string[]} */ (
|
|
142
|
+
const migrationFiles = /** @type {string[]} */ (findPrimaryImportFiles(context.paths, /** @param {string} filePath */ (filePath) =>
|
|
143
|
+
toPosix(filePath).includes("/prisma/migrations/") &&
|
|
144
|
+
isPrimaryImportSource(context.paths, filePath)
|
|
145
|
+
));
|
|
129
146
|
const migrationsPath = firstMarkedDirectory(context, migrationFiles, [["prisma", "migrations"]]);
|
|
130
147
|
return [
|
|
131
148
|
maintainedDbSeamCandidate(context, {
|
|
@@ -150,8 +167,14 @@ export function inferDrizzleMaintainedDbSeams(context, schemaFiles) {
|
|
|
150
167
|
if (!schemaFiles.length) {
|
|
151
168
|
return [];
|
|
152
169
|
}
|
|
153
|
-
const configFiles = /** @type {string[]} */ (
|
|
154
|
-
|
|
170
|
+
const configFiles = /** @type {string[]} */ (findPrimaryImportFiles(context.paths, /** @param {string} filePath */ (filePath) =>
|
|
171
|
+
/drizzle\.config\.(ts|js|mjs|cjs)$/i.test(path.basename(filePath)) &&
|
|
172
|
+
isPrimaryImportSource(context.paths, filePath)
|
|
173
|
+
));
|
|
174
|
+
const drizzleFiles = /** @type {string[]} */ (findPrimaryImportFiles(context.paths, /** @param {string} filePath */ (filePath) =>
|
|
175
|
+
appRelativePath(context, filePath).startsWith("drizzle/") &&
|
|
176
|
+
isPrimaryImportSource(context.paths, filePath)
|
|
177
|
+
));
|
|
155
178
|
const configuredOutPath = drizzleOutPathFromConfig(context, configFiles);
|
|
156
179
|
const migrationsPath = configuredOutPath ||
|
|
157
180
|
firstMarkedDirectory(context, drizzleFiles, [["drizzle"]]);
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { dedupeCandidateRecords,
|
|
1
|
+
import { dedupeCandidateRecords, findPrimaryImportFiles, makeCandidateRecord, relativeTo, selectPreferredImportFiles, slugify, titleCase, idHintify, canonicalCandidateTerm } from "../../core/shared.js";
|
|
2
2
|
import { parseSqlSchema } from "./sql.js";
|
|
3
3
|
|
|
4
4
|
function classifyNoiseEntity(tableName, fields) {
|
|
@@ -36,15 +36,15 @@ export const myBatisXmlExtractor = {
|
|
|
36
36
|
id: "db.mybatis-xml",
|
|
37
37
|
track: "db",
|
|
38
38
|
detect(context) {
|
|
39
|
-
const mapperFiles =
|
|
39
|
+
const mapperFiles = findPrimaryImportFiles(context.paths, (filePath) => /\/mapper\/.+\.xml$/i.test(filePath));
|
|
40
40
|
return {
|
|
41
41
|
score: mapperFiles.length > 0 ? 86 : 0,
|
|
42
42
|
reasons: mapperFiles.length > 0 ? ["Found MyBatis mapper XML files"] : []
|
|
43
43
|
};
|
|
44
44
|
},
|
|
45
45
|
extract(context) {
|
|
46
|
-
const mapperFiles =
|
|
47
|
-
const allSqlFiles =
|
|
46
|
+
const mapperFiles = findPrimaryImportFiles(context.paths, (filePath) => /\/mapper\/.+\.xml$/i.test(filePath));
|
|
47
|
+
const allSqlFiles = findPrimaryImportFiles(context.paths, (filePath) => filePath.endsWith(".sql"));
|
|
48
48
|
const schemaSqlFiles = allSqlFiles.filter((filePath) => !/\/src\/test\//i.test(filePath));
|
|
49
49
|
const sqlFiles = selectPreferredImportFiles(context.paths, schemaSqlFiles, "sql");
|
|
50
50
|
const findings = [];
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
|
-
|
|
2
|
+
findPrimaryImportFiles,
|
|
3
|
+
isPrimaryImportSource,
|
|
3
4
|
makeCandidateRecord,
|
|
4
5
|
normalizePrismaType,
|
|
5
6
|
relativeTo,
|
|
@@ -113,7 +114,10 @@ export const prismaExtractor = {
|
|
|
113
114
|
id: "db.prisma",
|
|
114
115
|
track: "db",
|
|
115
116
|
detect(context) {
|
|
116
|
-
const files =
|
|
117
|
+
const files = findPrimaryImportFiles(context.paths, (filePath) =>
|
|
118
|
+
(filePath.endsWith("/prisma/schema.prisma") || filePath.endsWith("prisma/schema.prisma")) &&
|
|
119
|
+
isPrimaryImportSource(context.paths, filePath)
|
|
120
|
+
);
|
|
117
121
|
return {
|
|
118
122
|
score: files.length > 0 ? 100 : 0,
|
|
119
123
|
reasons: files.length > 0 ? ["Found Prisma schema"] : []
|
|
@@ -122,7 +126,10 @@ export const prismaExtractor = {
|
|
|
122
126
|
extract(context) {
|
|
123
127
|
const prismaFiles = selectPreferredImportFiles(
|
|
124
128
|
context.paths,
|
|
125
|
-
|
|
129
|
+
findPrimaryImportFiles(context.paths, (filePath) =>
|
|
130
|
+
(filePath.endsWith("/prisma/schema.prisma") || filePath.endsWith("prisma/schema.prisma")) &&
|
|
131
|
+
isPrimaryImportSource(context.paths, filePath)
|
|
132
|
+
),
|
|
126
133
|
"prisma"
|
|
127
134
|
);
|
|
128
135
|
const findings = [];
|
|
@@ -3,7 +3,7 @@ import path from "node:path";
|
|
|
3
3
|
import {
|
|
4
4
|
canonicalCandidateTerm,
|
|
5
5
|
dedupeCandidateRecords,
|
|
6
|
-
|
|
6
|
+
findPrimaryImportFiles,
|
|
7
7
|
idHintify,
|
|
8
8
|
makeCandidateRecord,
|
|
9
9
|
relativeTo,
|
|
@@ -107,14 +107,14 @@ export const railsSchemaExtractor = {
|
|
|
107
107
|
id: "db.rails-schema",
|
|
108
108
|
track: "db",
|
|
109
109
|
detect(context) {
|
|
110
|
-
const schemaFiles =
|
|
110
|
+
const schemaFiles = findPrimaryImportFiles(context.paths, (filePath) => /db\/schema\.rb$/i.test(filePath));
|
|
111
111
|
return {
|
|
112
112
|
score: schemaFiles.length > 0 ? 92 : 0,
|
|
113
113
|
reasons: schemaFiles.length > 0 ? ["Found Rails schema.rb"] : []
|
|
114
114
|
};
|
|
115
115
|
},
|
|
116
116
|
extract(context) {
|
|
117
|
-
const schemaFiles =
|
|
117
|
+
const schemaFiles = findPrimaryImportFiles(context.paths, (filePath) => /db\/schema\.rb$/i.test(filePath));
|
|
118
118
|
const findings = [];
|
|
119
119
|
const candidates = { entities: [], enums: [], relations: [], indexes: [] };
|
|
120
120
|
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import {
|
|
2
2
|
canonicalCandidateTerm,
|
|
3
3
|
dedupeCandidateRecords,
|
|
4
|
-
|
|
4
|
+
findPrimaryImportFiles,
|
|
5
5
|
idHintify,
|
|
6
6
|
makeCandidateRecord,
|
|
7
7
|
relativeTo,
|
|
@@ -60,7 +60,7 @@ export const reactNativeEntitiesExtractor = {
|
|
|
60
60
|
id: "db.react-native-entities",
|
|
61
61
|
track: "db",
|
|
62
62
|
detect(context) {
|
|
63
|
-
const entityFiles =
|
|
63
|
+
const entityFiles = findPrimaryImportFiles(
|
|
64
64
|
context.paths,
|
|
65
65
|
(filePath) => /\/src\/.+\/domain\/entities\/.+Entity\.ts$/i.test(filePath)
|
|
66
66
|
);
|
|
@@ -71,7 +71,7 @@ export const reactNativeEntitiesExtractor = {
|
|
|
71
71
|
};
|
|
72
72
|
},
|
|
73
73
|
extract(context) {
|
|
74
|
-
const entityFiles =
|
|
74
|
+
const entityFiles = findPrimaryImportFiles(
|
|
75
75
|
context.paths,
|
|
76
76
|
(filePath) => /\/src\/.+\/domain\/entities\/.+Entity\.ts$/i.test(filePath)
|
|
77
77
|
);
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import {
|
|
2
2
|
canonicalCandidateTerm,
|
|
3
3
|
dedupeCandidateRecords,
|
|
4
|
-
|
|
4
|
+
findPrimaryImportFiles,
|
|
5
5
|
idHintify,
|
|
6
6
|
makeCandidateRecord,
|
|
7
7
|
relativeTo,
|
|
@@ -151,8 +151,8 @@ export const roomExtractor = {
|
|
|
151
151
|
id: "db.room",
|
|
152
152
|
track: "db",
|
|
153
153
|
detect(context) {
|
|
154
|
-
const entityFiles =
|
|
155
|
-
const daoFiles =
|
|
154
|
+
const entityFiles = findPrimaryImportFiles(context.paths, (filePath) => /Entity\.kt$/i.test(filePath) || /\/entitiy\/.+\.kt$/i.test(filePath) || /\/entity\/.+\.kt$/i.test(filePath));
|
|
155
|
+
const daoFiles = findPrimaryImportFiles(context.paths, (filePath) => /Dao\.kt$/i.test(filePath));
|
|
156
156
|
const score = entityFiles.length > 0 && daoFiles.length > 0 ? 89 : 0;
|
|
157
157
|
return {
|
|
158
158
|
score,
|
|
@@ -160,8 +160,8 @@ export const roomExtractor = {
|
|
|
160
160
|
};
|
|
161
161
|
},
|
|
162
162
|
extract(context) {
|
|
163
|
-
const entityFiles =
|
|
164
|
-
const daoFiles =
|
|
163
|
+
const entityFiles = findPrimaryImportFiles(context.paths, (filePath) => /Entity\.kt$/i.test(filePath) || /\/entitiy\/.+\.kt$/i.test(filePath) || /\/entity\/.+\.kt$/i.test(filePath));
|
|
164
|
+
const daoFiles = findPrimaryImportFiles(context.paths, (filePath) => /Dao\.kt$/i.test(filePath));
|
|
165
165
|
const findings = [];
|
|
166
166
|
const candidates = { entities: [], enums: [], relations: [], indexes: [] };
|
|
167
167
|
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { findPrimaryImportFiles, makeCandidateRecord, relativeTo, slugify, titleCase, idHintify } from "../../core/shared.js";
|
|
2
2
|
|
|
3
3
|
function parseDbSchemaSnapshot(snapshot) {
|
|
4
4
|
return {
|
|
@@ -42,14 +42,14 @@ export const snapshotExtractor = {
|
|
|
42
42
|
id: "db.snapshot",
|
|
43
43
|
track: "db",
|
|
44
44
|
detect(context) {
|
|
45
|
-
const files =
|
|
45
|
+
const files = findPrimaryImportFiles(context.paths, (filePath) => filePath.endsWith(".db-schema-snapshot.json"));
|
|
46
46
|
return {
|
|
47
47
|
score: files.length > 0 ? 40 : 0,
|
|
48
48
|
reasons: files.length > 0 ? ["Found DB schema snapshot artifacts"] : []
|
|
49
49
|
};
|
|
50
50
|
},
|
|
51
51
|
extract(context) {
|
|
52
|
-
const snapshotFiles =
|
|
52
|
+
const snapshotFiles = findPrimaryImportFiles(context.paths, (filePath) => filePath.endsWith(".db-schema-snapshot.json"));
|
|
53
53
|
const findings = [];
|
|
54
54
|
const candidates = { entities: [], enums: [], relations: [], indexes: [] };
|
|
55
55
|
for (const filePath of snapshotFiles) {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { canonicalCandidateTerm,
|
|
1
|
+
import { canonicalCandidateTerm, findPrimaryImportFiles, isPrimaryImportSource, makeCandidateRecord, relativeTo, selectPreferredImportFiles, slugify, titleCase, idHintify } from "../../core/shared.js";
|
|
2
2
|
import { inferSqlMaintainedDbSeams } from "./maintained-seams.js";
|
|
3
3
|
|
|
4
4
|
function parseTableConstraint(line, tableName) {
|
|
@@ -105,14 +105,14 @@ export const sqlExtractor = {
|
|
|
105
105
|
id: "db.sql",
|
|
106
106
|
track: "db",
|
|
107
107
|
detect(context) {
|
|
108
|
-
const files =
|
|
108
|
+
const files = findPrimaryImportFiles(context.paths, (filePath) => filePath.endsWith(".sql") && isPrimaryImportSource(context.paths, filePath));
|
|
109
109
|
return {
|
|
110
110
|
score: files.length > 0 ? 80 : 0,
|
|
111
111
|
reasons: files.length > 0 ? ["Found SQL schema or migration files"] : []
|
|
112
112
|
};
|
|
113
113
|
},
|
|
114
114
|
extract(context) {
|
|
115
|
-
const allSqlFiles =
|
|
115
|
+
const allSqlFiles = findPrimaryImportFiles(context.paths, (filePath) => filePath.endsWith(".sql") && isPrimaryImportSource(context.paths, filePath));
|
|
116
116
|
const schemaSqlFiles = allSqlFiles.filter((filePath) => !/migration/i.test(filePath) && !/\/src\/test\//i.test(filePath));
|
|
117
117
|
const migrationSqlFiles = allSqlFiles.filter((filePath) => /migration/i.test(filePath));
|
|
118
118
|
const sqlFiles =
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import {
|
|
2
2
|
canonicalCandidateTerm,
|
|
3
3
|
dedupeCandidateRecords,
|
|
4
|
-
|
|
4
|
+
findPrimaryImportFiles,
|
|
5
5
|
idHintify,
|
|
6
6
|
makeCandidateRecord,
|
|
7
7
|
relativeTo,
|
|
@@ -106,7 +106,7 @@ export const swiftDataExtractor = {
|
|
|
106
106
|
id: "db.swiftdata",
|
|
107
107
|
track: "db",
|
|
108
108
|
detect(context) {
|
|
109
|
-
const files =
|
|
109
|
+
const files = findPrimaryImportFiles(context.paths, (filePath) => /\.swift$/i.test(filePath))
|
|
110
110
|
.filter((filePath) => /@Model/.test(context.helpers.readTextIfExists(filePath) || ""));
|
|
111
111
|
return {
|
|
112
112
|
score: files.length > 0 ? 86 : 0,
|
|
@@ -114,7 +114,7 @@ export const swiftDataExtractor = {
|
|
|
114
114
|
};
|
|
115
115
|
},
|
|
116
116
|
extract(context) {
|
|
117
|
-
const files =
|
|
117
|
+
const files = findPrimaryImportFiles(context.paths, (filePath) => /\.swift$/i.test(filePath))
|
|
118
118
|
.filter((filePath) => /@Model/.test(context.helpers.readTextIfExists(filePath) || ""));
|
|
119
119
|
const findings = [];
|
|
120
120
|
const candidates = { entities: [], enums: [], relations: [], indexes: [] };
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import {
|
|
2
2
|
canonicalCandidateTerm,
|
|
3
3
|
dedupeCandidateRecords,
|
|
4
|
-
|
|
4
|
+
findPrimaryImportFiles,
|
|
5
5
|
idHintify,
|
|
6
6
|
makeCandidateRecord,
|
|
7
7
|
relativeTo,
|
|
@@ -85,7 +85,7 @@ export const androidComposeUiExtractor = {
|
|
|
85
85
|
id: "ui.android-compose",
|
|
86
86
|
track: "ui",
|
|
87
87
|
detect(context) {
|
|
88
|
-
const composeFiles =
|
|
88
|
+
const composeFiles = findPrimaryImportFiles(context.paths, (filePath) => /\.kt$/i.test(filePath))
|
|
89
89
|
.filter((filePath) => /@Composable/.test(context.helpers.readTextIfExists(filePath) || ""));
|
|
90
90
|
const score = composeFiles.length > 0 ? 84 : 0;
|
|
91
91
|
return {
|
|
@@ -94,8 +94,8 @@ export const androidComposeUiExtractor = {
|
|
|
94
94
|
};
|
|
95
95
|
},
|
|
96
96
|
extract(context) {
|
|
97
|
-
const navFiles =
|
|
98
|
-
const composeFiles =
|
|
97
|
+
const navFiles = findPrimaryImportFiles(context.paths, (filePath) => /NavHost\.kt$/i.test(filePath) || /navigation\/.+\.kt$/i.test(filePath));
|
|
98
|
+
const composeFiles = findPrimaryImportFiles(context.paths, (filePath) => /\.kt$/i.test(filePath))
|
|
99
99
|
.filter((filePath) => /@Composable/.test(context.helpers.readTextIfExists(filePath) || ""));
|
|
100
100
|
const findings = [];
|
|
101
101
|
const candidates = { screens: [], routes: [], actions: [], stacks: [] };
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import path from "node:path";
|
|
2
2
|
|
|
3
|
-
import {
|
|
3
|
+
import { findPrimaryImportFiles, relativeTo } from "../../core/shared.js";
|
|
4
4
|
|
|
5
5
|
function readPackageJson(context) {
|
|
6
|
-
const packageFile =
|
|
6
|
+
const packageFile = findPrimaryImportFiles(context.paths, (filePath) => /package\.json$/i.test(filePath))[0];
|
|
7
7
|
if (!packageFile) return null;
|
|
8
8
|
try {
|
|
9
9
|
return {
|
|
@@ -24,7 +24,7 @@ function hasFrontendSignals(context) {
|
|
|
24
24
|
if (deps.react || deps.next || deps["@remix-run/react"] || deps["@sveltejs/kit"] || deps.svelte || deps.vue || deps.nuxt) {
|
|
25
25
|
return true;
|
|
26
26
|
}
|
|
27
|
-
const uiFiles =
|
|
27
|
+
const uiFiles = findPrimaryImportFiles(
|
|
28
28
|
context.paths,
|
|
29
29
|
(filePath) =>
|
|
30
30
|
/(app\/.+\/page\.(tsx|ts|jsx|js|mdx)|src\/App\.tsx|src\/routes\/.+\.(svelte|tsx|jsx)|pages\/.+\.(tsx|ts|jsx|js))$/i.test(filePath)
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import {
|
|
2
2
|
canonicalCandidateTerm,
|
|
3
3
|
dedupeCandidateRecords,
|
|
4
|
-
|
|
4
|
+
findPrimaryImportFiles,
|
|
5
5
|
idHintify,
|
|
6
6
|
makeCandidateRecord,
|
|
7
7
|
relativeTo,
|
|
@@ -98,7 +98,7 @@ export const blazorUiExtractor = {
|
|
|
98
98
|
id: "ui.blazor",
|
|
99
99
|
track: "ui",
|
|
100
100
|
detect(context) {
|
|
101
|
-
const razorFiles =
|
|
101
|
+
const razorFiles = findPrimaryImportFiles(context.paths, (filePath) => /\.razor$/i.test(filePath))
|
|
102
102
|
.filter((filePath) => !shouldIgnoreFile(filePath));
|
|
103
103
|
const score = razorFiles.length > 0 ? 86 : 0;
|
|
104
104
|
return {
|
|
@@ -107,7 +107,7 @@ export const blazorUiExtractor = {
|
|
|
107
107
|
};
|
|
108
108
|
},
|
|
109
109
|
extract(context) {
|
|
110
|
-
const razorFiles =
|
|
110
|
+
const razorFiles = findPrimaryImportFiles(context.paths, (filePath) => /\.razor$/i.test(filePath))
|
|
111
111
|
.filter((filePath) => !shouldIgnoreFile(filePath));
|
|
112
112
|
const findings = [];
|
|
113
113
|
const candidates = { screens: [], routes: [], actions: [], stacks: [] };
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import {
|
|
2
2
|
canonicalCandidateTerm,
|
|
3
3
|
dedupeCandidateRecords,
|
|
4
|
-
|
|
4
|
+
findPrimaryImportFiles,
|
|
5
5
|
idHintify,
|
|
6
6
|
makeCandidateRecord,
|
|
7
7
|
relativeTo,
|
|
@@ -110,7 +110,7 @@ export const flutterScreensUiExtractor = {
|
|
|
110
110
|
id: "ui.flutter-screens",
|
|
111
111
|
track: "ui",
|
|
112
112
|
detect(context) {
|
|
113
|
-
const files =
|
|
113
|
+
const files = findPrimaryImportFiles(
|
|
114
114
|
context.paths,
|
|
115
115
|
(filePath) => /\/lib\/features\/.+\/presentation\/screens\/.+_screen\.dart$/i.test(filePath)
|
|
116
116
|
);
|
|
@@ -121,7 +121,7 @@ export const flutterScreensUiExtractor = {
|
|
|
121
121
|
};
|
|
122
122
|
},
|
|
123
123
|
extract(context) {
|
|
124
|
-
const files =
|
|
124
|
+
const files = findPrimaryImportFiles(
|
|
125
125
|
context.paths,
|
|
126
126
|
(filePath) => /\/lib\/features\/.+\/presentation\/screens\/.+_screen\.dart$/i.test(filePath)
|
|
127
127
|
);
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import {
|
|
2
2
|
canonicalCandidateTerm,
|
|
3
3
|
dedupeCandidateRecords,
|
|
4
|
-
|
|
4
|
+
findPrimaryImportFiles,
|
|
5
5
|
idHintify,
|
|
6
6
|
makeCandidateRecord,
|
|
7
7
|
relativeTo,
|
|
@@ -54,7 +54,7 @@ export const mauiXamlUiExtractor = {
|
|
|
54
54
|
id: "ui.maui-xaml",
|
|
55
55
|
track: "ui",
|
|
56
56
|
detect(context) {
|
|
57
|
-
const xamlFiles =
|
|
57
|
+
const xamlFiles = findPrimaryImportFiles(context.paths, (filePath) => /\.xaml$/i.test(filePath));
|
|
58
58
|
const score = xamlFiles.some((filePath) => /ContentPage|Shell/.test(context.helpers.readTextIfExists(filePath) || "")) ? 82 : 0;
|
|
59
59
|
return {
|
|
60
60
|
score,
|
|
@@ -62,8 +62,8 @@ export const mauiXamlUiExtractor = {
|
|
|
62
62
|
};
|
|
63
63
|
},
|
|
64
64
|
extract(context) {
|
|
65
|
-
const shellFiles =
|
|
66
|
-
const viewFiles =
|
|
65
|
+
const shellFiles = findPrimaryImportFiles(context.paths, (filePath) => /AppShell\.xaml$/i.test(filePath));
|
|
66
|
+
const viewFiles = findPrimaryImportFiles(context.paths, (filePath) => /\/Views\/.+\.xaml$/i.test(filePath));
|
|
67
67
|
const findings = [];
|
|
68
68
|
const candidates = { screens: [], routes: [], actions: [], stacks: [] };
|
|
69
69
|
const shellEntries = shellFiles.flatMap((filePath) => parseShellContent(context.helpers.readTextIfExists(filePath) || ""));
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { dedupeCandidateRecords, inferNextAppRoutes, makeCandidateRecord, nextScreenIdForRoute, nextScreenKindForRoute, uiCapabilityHintsForNextRoute, entityIdForNextRoute, conceptIdForNextRoute, relativeTo, titleCase, idHintify } from "../../core/shared.js";
|
|
1
|
+
import { dedupeCandidateRecords, inferNextAppRoutes, inferNonResourceUiFlow, makeCandidateRecord, nextScreenIdForRoute, nextScreenKindForRoute, proposedUiContractAdditionsForFlow, uiCapabilityHintsForNextRoute, entityIdForNextRoute, conceptIdForNextRoute, relativeTo, titleCase, idHintify, uiFlowIdForRoute } from "../../core/shared.js";
|
|
2
2
|
|
|
3
3
|
export const nextAppRouterUiExtractor = {
|
|
4
4
|
id: "ui.next-app-router",
|
|
@@ -13,7 +13,7 @@ export const nextAppRouterUiExtractor = {
|
|
|
13
13
|
extract(context) {
|
|
14
14
|
const routes = inferNextAppRoutes(context.paths.workspaceRoot, context.helpers);
|
|
15
15
|
const findings = [];
|
|
16
|
-
const candidates = { screens: [], routes: [], actions: [], stacks: [] };
|
|
16
|
+
const candidates = { screens: [], routes: [], actions: [], flows: [], stacks: [] };
|
|
17
17
|
if (routes.length > 0) {
|
|
18
18
|
findings.push({
|
|
19
19
|
kind: "next_app_routes",
|
|
@@ -26,9 +26,10 @@ export const nextAppRouterUiExtractor = {
|
|
|
26
26
|
const provenance = `${relativeTo(context.paths.repoRoot, route.file)}#${route.path}`;
|
|
27
27
|
const screenId = nextScreenIdForRoute(route.path);
|
|
28
28
|
const screenKind = nextScreenKindForRoute(route.path);
|
|
29
|
-
const
|
|
30
|
-
const
|
|
31
|
-
const
|
|
29
|
+
const flow = inferNonResourceUiFlow(route.path);
|
|
30
|
+
const capabilityHints = flow ? { load: null, submit: null, primary_action: null } : uiCapabilityHintsForNextRoute(route.path);
|
|
31
|
+
const entityId = flow ? null : entityIdForNextRoute(route.path);
|
|
32
|
+
const conceptId = flow?.concept_id || conceptIdForNextRoute(route.path);
|
|
32
33
|
candidates.screens.push(makeCandidateRecord({
|
|
33
34
|
kind: "screen",
|
|
34
35
|
idHint: screenId,
|
|
@@ -56,6 +57,25 @@ export const nextAppRouterUiExtractor = {
|
|
|
56
57
|
concept_id: conceptId,
|
|
57
58
|
path: route.path
|
|
58
59
|
}));
|
|
60
|
+
if (flow) {
|
|
61
|
+
candidates.flows.push(makeCandidateRecord({
|
|
62
|
+
kind: "ui_flow",
|
|
63
|
+
idHint: uiFlowIdForRoute(route.path, screenId),
|
|
64
|
+
label: `${titleCase(flow.flow_type)} Flow`,
|
|
65
|
+
confidence: flow.confidence,
|
|
66
|
+
sourceKind: "route_code",
|
|
67
|
+
sourceOfTruth: "candidate",
|
|
68
|
+
provenance,
|
|
69
|
+
track: "ui",
|
|
70
|
+
flow_type: flow.flow_type,
|
|
71
|
+
concept_id: flow.concept_id,
|
|
72
|
+
screen_ids: [screenId],
|
|
73
|
+
route_paths: [route.path],
|
|
74
|
+
evidence: [provenance],
|
|
75
|
+
missing_decisions: flow.missing_decisions,
|
|
76
|
+
proposed_ui_contract_additions: proposedUiContractAdditionsForFlow(route.path, screenId, screenKind)
|
|
77
|
+
}));
|
|
78
|
+
}
|
|
59
79
|
if (capabilityHints.primary_action) {
|
|
60
80
|
candidates.actions.push(makeCandidateRecord({
|
|
61
81
|
kind: "ui_action",
|
|
@@ -77,6 +97,7 @@ export const nextAppRouterUiExtractor = {
|
|
|
77
97
|
candidates.screens = dedupeCandidateRecords(candidates.screens, (record) => record.id_hint);
|
|
78
98
|
candidates.routes = dedupeCandidateRecords(candidates.routes, (record) => record.id_hint);
|
|
79
99
|
candidates.actions = dedupeCandidateRecords(candidates.actions, (record) => record.id_hint);
|
|
100
|
+
candidates.flows = dedupeCandidateRecords(candidates.flows, (record) => record.id_hint);
|
|
80
101
|
candidates.stacks = [...new Set(candidates.stacks)].sort();
|
|
81
102
|
return { findings, candidates };
|
|
82
103
|
}
|