@schemashift/core 0.12.0 → 0.13.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +252 -31
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +78 -1
- package/dist/index.d.ts +78 -1
- package/dist/index.js +241 -31
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -30,11 +30,16 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
30
30
|
// src/index.ts
|
|
31
31
|
var index_exports = {};
|
|
32
32
|
__export(index_exports, {
|
|
33
|
+
AUDIT_LOG_FILE: () => AUDIT_LOG_FILE,
|
|
33
34
|
ApprovalManager: () => ApprovalManager,
|
|
35
|
+
BACKUP_DIR: () => BACKUP_DIR,
|
|
34
36
|
BehavioralWarningAnalyzer: () => BehavioralWarningAnalyzer,
|
|
35
37
|
BundleEstimator: () => BundleEstimator,
|
|
38
|
+
CONFIG_FILE_NAMES: () => CONFIG_FILE_NAMES,
|
|
36
39
|
CompatibilityAnalyzer: () => CompatibilityAnalyzer,
|
|
37
40
|
ComplexityEstimator: () => ComplexityEstimator,
|
|
41
|
+
DEFAULT_CONFIG_FILE: () => DEFAULT_CONFIG_FILE,
|
|
42
|
+
DeadSchemaDetector: () => DeadSchemaDetector,
|
|
38
43
|
DetailedAnalyzer: () => DetailedAnalyzer,
|
|
39
44
|
DriftDetector: () => DriftDetector,
|
|
40
45
|
EcosystemAnalyzer: () => EcosystemAnalyzer,
|
|
@@ -43,16 +48,22 @@ __export(index_exports, {
|
|
|
43
48
|
GovernanceEngine: () => GovernanceEngine,
|
|
44
49
|
GovernanceFixer: () => GovernanceFixer,
|
|
45
50
|
GraphExporter: () => GraphExporter,
|
|
51
|
+
INCREMENTAL_STATE_FILE: () => INCREMENTAL_STATE_FILE,
|
|
52
|
+
ImportDeduplicator: () => ImportDeduplicator,
|
|
46
53
|
IncrementalTracker: () => IncrementalTracker,
|
|
47
54
|
MigrationAuditLog: () => MigrationAuditLog,
|
|
48
55
|
MigrationChain: () => MigrationChain,
|
|
49
56
|
MonorepoResolver: () => MonorepoResolver,
|
|
57
|
+
PENDING_DIR: () => PENDING_DIR,
|
|
50
58
|
PackageUpdater: () => PackageUpdater,
|
|
51
59
|
PerformanceAnalyzer: () => PerformanceAnalyzer,
|
|
52
60
|
PluginLoader: () => PluginLoader,
|
|
61
|
+
SCHEMASHIFT_DIR: () => SCHEMASHIFT_DIR,
|
|
62
|
+
SCHEMA_SNAPSHOT_FILE: () => SCHEMA_SNAPSHOT_FILE,
|
|
53
63
|
SchemaAnalyzer: () => SchemaAnalyzer,
|
|
54
64
|
SchemaDependencyResolver: () => SchemaDependencyResolver,
|
|
55
65
|
StandardSchemaAdvisor: () => StandardSchemaAdvisor,
|
|
66
|
+
TESTS_DIR: () => TESTS_DIR,
|
|
56
67
|
TestScaffolder: () => TestScaffolder,
|
|
57
68
|
TransformEngine: () => TransformEngine,
|
|
58
69
|
TypeDedupDetector: () => TypeDedupDetector,
|
|
@@ -229,10 +240,30 @@ var SchemaAnalyzer = class {
|
|
|
229
240
|
// src/approval.ts
|
|
230
241
|
var import_node_fs = require("fs");
|
|
231
242
|
var import_node_path = require("path");
|
|
243
|
+
|
|
244
|
+
// src/constants.ts
|
|
245
|
+
var SCHEMASHIFT_DIR = ".schemashift";
|
|
246
|
+
var BACKUP_DIR = ".schemashift-backup";
|
|
247
|
+
var CONFIG_FILE_NAMES = [
|
|
248
|
+
".schemashiftrc",
|
|
249
|
+
".schemashiftrc.json",
|
|
250
|
+
".schemashiftrc.yaml",
|
|
251
|
+
".schemashiftrc.yml",
|
|
252
|
+
".schemashiftrc.js",
|
|
253
|
+
".schemashiftrc.cjs"
|
|
254
|
+
];
|
|
255
|
+
var DEFAULT_CONFIG_FILE = ".schemashiftrc.json";
|
|
256
|
+
var INCREMENTAL_STATE_FILE = "incremental.json";
|
|
257
|
+
var AUDIT_LOG_FILE = "audit-log.json";
|
|
258
|
+
var SCHEMA_SNAPSHOT_FILE = "schema-snapshot.json";
|
|
259
|
+
var PENDING_DIR = "pending";
|
|
260
|
+
var TESTS_DIR = "tests";
|
|
261
|
+
|
|
262
|
+
// src/approval.ts
|
|
232
263
|
var ApprovalManager = class {
|
|
233
264
|
pendingDir;
|
|
234
265
|
constructor(projectPath) {
|
|
235
|
-
this.pendingDir = (0, import_node_path.join)(projectPath,
|
|
266
|
+
this.pendingDir = (0, import_node_path.join)(projectPath, SCHEMASHIFT_DIR, PENDING_DIR);
|
|
236
267
|
}
|
|
237
268
|
/**
|
|
238
269
|
* Create a new migration request for review.
|
|
@@ -281,8 +312,16 @@ var ApprovalManager = class {
|
|
|
281
312
|
if (!(0, import_node_fs.existsSync)(filePath)) {
|
|
282
313
|
return null;
|
|
283
314
|
}
|
|
284
|
-
|
|
285
|
-
|
|
315
|
+
try {
|
|
316
|
+
const content = (0, import_node_fs.readFileSync)(filePath, "utf-8");
|
|
317
|
+
const parsed = JSON.parse(content);
|
|
318
|
+
if (!this.isValidRequest(parsed)) {
|
|
319
|
+
return null;
|
|
320
|
+
}
|
|
321
|
+
return parsed;
|
|
322
|
+
} catch {
|
|
323
|
+
return null;
|
|
324
|
+
}
|
|
286
325
|
}
|
|
287
326
|
/**
|
|
288
327
|
* List all migration requests, optionally filtered by status.
|
|
@@ -294,10 +333,14 @@ var ApprovalManager = class {
|
|
|
294
333
|
const files = (0, import_node_fs.readdirSync)(this.pendingDir).filter((f) => f.endsWith(".json"));
|
|
295
334
|
const requests = [];
|
|
296
335
|
for (const file of files) {
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
336
|
+
try {
|
|
337
|
+
const content = (0, import_node_fs.readFileSync)((0, import_node_path.join)(this.pendingDir, file), "utf-8");
|
|
338
|
+
const parsed = JSON.parse(content);
|
|
339
|
+
if (!this.isValidRequest(parsed)) continue;
|
|
340
|
+
if (!status || parsed.status === status) {
|
|
341
|
+
requests.push(parsed);
|
|
342
|
+
}
|
|
343
|
+
} catch {
|
|
301
344
|
}
|
|
302
345
|
}
|
|
303
346
|
return requests.sort(
|
|
@@ -323,6 +366,11 @@ var ApprovalManager = class {
|
|
|
323
366
|
const request = this.getRequest(requestId);
|
|
324
367
|
return request?.status === "approved";
|
|
325
368
|
}
|
|
369
|
+
isValidRequest(data) {
|
|
370
|
+
if (typeof data !== "object" || data === null) return false;
|
|
371
|
+
const obj = data;
|
|
372
|
+
return typeof obj.id === "string" && typeof obj.from === "string" && typeof obj.to === "string" && Array.isArray(obj.files) && typeof obj.requestedBy === "string" && typeof obj.status === "string" && ["pending", "approved", "rejected"].includes(obj.status);
|
|
373
|
+
}
|
|
326
374
|
ensureDir() {
|
|
327
375
|
if (!(0, import_node_fs.existsSync)(this.pendingDir)) {
|
|
328
376
|
(0, import_node_fs.mkdirSync)(this.pendingDir, { recursive: true });
|
|
@@ -468,15 +516,13 @@ function transformMethodChain(chain, newBase, factoryMapper, methodMapper) {
|
|
|
468
516
|
var import_node_crypto = require("crypto");
|
|
469
517
|
var import_node_fs2 = require("fs");
|
|
470
518
|
var import_node_path2 = require("path");
|
|
471
|
-
var AUDIT_DIR = ".schemashift";
|
|
472
|
-
var AUDIT_FILE = "audit-log.json";
|
|
473
519
|
var AUDIT_VERSION = 1;
|
|
474
520
|
var MigrationAuditLog = class {
|
|
475
521
|
logDir;
|
|
476
522
|
logPath;
|
|
477
523
|
constructor(projectPath) {
|
|
478
|
-
this.logDir = (0, import_node_path2.join)(projectPath,
|
|
479
|
-
this.logPath = (0, import_node_path2.join)(this.logDir,
|
|
524
|
+
this.logDir = (0, import_node_path2.join)(projectPath, SCHEMASHIFT_DIR);
|
|
525
|
+
this.logPath = (0, import_node_path2.join)(this.logDir, AUDIT_LOG_FILE);
|
|
480
526
|
}
|
|
481
527
|
/**
|
|
482
528
|
* Append a new entry to the audit log.
|
|
@@ -520,7 +566,11 @@ var MigrationAuditLog = class {
|
|
|
520
566
|
if (!content.trim()) {
|
|
521
567
|
return { version: AUDIT_VERSION, entries: [] };
|
|
522
568
|
}
|
|
523
|
-
|
|
569
|
+
const parsed = JSON.parse(content);
|
|
570
|
+
if (!this.isValidAuditLog(parsed)) {
|
|
571
|
+
return { version: AUDIT_VERSION, entries: [] };
|
|
572
|
+
}
|
|
573
|
+
return parsed;
|
|
524
574
|
} catch {
|
|
525
575
|
return { version: AUDIT_VERSION, entries: [] };
|
|
526
576
|
}
|
|
@@ -690,6 +740,13 @@ var MigrationAuditLog = class {
|
|
|
690
740
|
gitCommit: process.env.GITHUB_SHA || process.env.CI_COMMIT_SHA || void 0
|
|
691
741
|
};
|
|
692
742
|
}
|
|
743
|
+
isValidAuditLog(data) {
|
|
744
|
+
if (typeof data !== "object" || data === null) return false;
|
|
745
|
+
const obj = data;
|
|
746
|
+
if (typeof obj.version !== "number") return false;
|
|
747
|
+
if (!Array.isArray(obj.entries)) return false;
|
|
748
|
+
return true;
|
|
749
|
+
}
|
|
693
750
|
write(log) {
|
|
694
751
|
if (!(0, import_node_fs2.existsSync)(this.logDir)) {
|
|
695
752
|
(0, import_node_fs2.mkdirSync)(this.logDir, { recursive: true });
|
|
@@ -1133,6 +1190,12 @@ var import_node_path4 = require("path");
|
|
|
1133
1190
|
// src/ecosystem.ts
|
|
1134
1191
|
var import_node_fs3 = require("fs");
|
|
1135
1192
|
var import_node_path3 = require("path");
|
|
1193
|
+
function parseMajorVersion(version) {
|
|
1194
|
+
const match = version.match(/(\d+)/);
|
|
1195
|
+
const num = match?.[1] ? Number.parseInt(match[1], 10) : 0;
|
|
1196
|
+
if (!Number.isFinite(num) || num < 0 || num > 999) return 0;
|
|
1197
|
+
return num;
|
|
1198
|
+
}
|
|
1136
1199
|
var ECOSYSTEM_RULES = [
|
|
1137
1200
|
// ORM integrations
|
|
1138
1201
|
{
|
|
@@ -1174,8 +1237,7 @@ var ECOSYSTEM_RULES = [
|
|
|
1174
1237
|
category: "api",
|
|
1175
1238
|
migrations: ["zod-v3->v4"],
|
|
1176
1239
|
check: (version) => {
|
|
1177
|
-
const
|
|
1178
|
-
const major = majorMatch?.[1] ? Number.parseInt(majorMatch[1], 10) : 0;
|
|
1240
|
+
const major = parseMajorVersion(version);
|
|
1179
1241
|
if (major < 11) {
|
|
1180
1242
|
return {
|
|
1181
1243
|
issue: `tRPC v${major} expects Zod v3 types. A v3 ZodType is not assignable to a v4 ZodType.`,
|
|
@@ -1208,8 +1270,7 @@ var ECOSYSTEM_RULES = [
|
|
|
1208
1270
|
category: "validation-util",
|
|
1209
1271
|
migrations: ["zod-v3->v4"],
|
|
1210
1272
|
check: (version) => {
|
|
1211
|
-
const
|
|
1212
|
-
const major = majorMatch?.[1] ? Number.parseInt(majorMatch[1], 10) : 0;
|
|
1273
|
+
const major = parseMajorVersion(version);
|
|
1213
1274
|
if (major < 4) {
|
|
1214
1275
|
return {
|
|
1215
1276
|
issue: `zod-validation-error v${major} is not compatible with Zod v4.`,
|
|
@@ -1499,8 +1560,7 @@ var ECOSYSTEM_RULES = [
|
|
|
1499
1560
|
category: "validation-util",
|
|
1500
1561
|
migrations: ["zod-v3->v4"],
|
|
1501
1562
|
check: (version) => {
|
|
1502
|
-
const
|
|
1503
|
-
const major = majorMatch?.[1] ? Number.parseInt(majorMatch[1], 10) : 0;
|
|
1563
|
+
const major = parseMajorVersion(version);
|
|
1504
1564
|
if (major < 4) {
|
|
1505
1565
|
return {
|
|
1506
1566
|
issue: "zod-to-json-schema v3 may not fully support Zod v4 schemas.",
|
|
@@ -1895,7 +1955,7 @@ async function loadConfig(configPath) {
|
|
|
1895
1955
|
include: ["**/*.ts", "**/*.tsx"],
|
|
1896
1956
|
exclude: ["**/node_modules/**", "**/dist/**", "**/*.d.ts"],
|
|
1897
1957
|
git: { enabled: false },
|
|
1898
|
-
backup: { enabled: true, dir:
|
|
1958
|
+
backup: { enabled: true, dir: BACKUP_DIR },
|
|
1899
1959
|
...result?.config
|
|
1900
1960
|
};
|
|
1901
1961
|
}
|
|
@@ -2006,6 +2066,75 @@ function suggestCrossFieldPattern(whenCode) {
|
|
|
2006
2066
|
return null;
|
|
2007
2067
|
}
|
|
2008
2068
|
|
|
2069
|
+
// src/dead-schema-detector.ts
|
|
2070
|
+
var DeadSchemaDetector = class {
|
|
2071
|
+
detect(sourceFiles) {
|
|
2072
|
+
const schemas = this.collectSchemaDefinitions(sourceFiles);
|
|
2073
|
+
const unusedSchemas = this.findUnusedSchemas(schemas, sourceFiles);
|
|
2074
|
+
return {
|
|
2075
|
+
unusedSchemas,
|
|
2076
|
+
totalSchemas: schemas.length,
|
|
2077
|
+
summary: unusedSchemas.length > 0 ? `Found ${unusedSchemas.length} unused schema(s) out of ${schemas.length} total that may be safely removed.` : `All ${schemas.length} schema(s) are referenced.`
|
|
2078
|
+
};
|
|
2079
|
+
}
|
|
2080
|
+
collectSchemaDefinitions(sourceFiles) {
|
|
2081
|
+
const schemas = [];
|
|
2082
|
+
const schemaPattern = /(?:const|let|var|export\s+(?:const|let|var))\s+(\w+)\s*=\s*(?:z\.|yup\.|Yup\.|Joi\.|t\.|v\.|type\(|object\(|string\(|S\.)/;
|
|
2083
|
+
for (const file of sourceFiles) {
|
|
2084
|
+
const text = file.getFullText();
|
|
2085
|
+
const lines = text.split("\n");
|
|
2086
|
+
const filePath = file.getFilePath();
|
|
2087
|
+
for (let i = 0; i < lines.length; i++) {
|
|
2088
|
+
const line = lines[i];
|
|
2089
|
+
if (!line) continue;
|
|
2090
|
+
const match = schemaPattern.exec(line);
|
|
2091
|
+
if (match?.[1]) {
|
|
2092
|
+
schemas.push({
|
|
2093
|
+
schemaName: match[1],
|
|
2094
|
+
filePath,
|
|
2095
|
+
lineNumber: i + 1
|
|
2096
|
+
});
|
|
2097
|
+
}
|
|
2098
|
+
}
|
|
2099
|
+
}
|
|
2100
|
+
return schemas;
|
|
2101
|
+
}
|
|
2102
|
+
findUnusedSchemas(schemas, sourceFiles) {
|
|
2103
|
+
const fileContents = /* @__PURE__ */ new Map();
|
|
2104
|
+
for (const file of sourceFiles) {
|
|
2105
|
+
fileContents.set(file.getFilePath(), file.getFullText());
|
|
2106
|
+
}
|
|
2107
|
+
const unused = [];
|
|
2108
|
+
for (const schema of schemas) {
|
|
2109
|
+
const { schemaName, filePath } = schema;
|
|
2110
|
+
let referenceCount = 0;
|
|
2111
|
+
for (const [path, content] of fileContents) {
|
|
2112
|
+
const pattern = new RegExp(`\\b${schemaName}\\b`, "g");
|
|
2113
|
+
const matches = content.match(pattern);
|
|
2114
|
+
const matchCount = matches?.length ?? 0;
|
|
2115
|
+
if (path === filePath) {
|
|
2116
|
+
if (matchCount > 1) {
|
|
2117
|
+
referenceCount += matchCount - 1;
|
|
2118
|
+
}
|
|
2119
|
+
} else {
|
|
2120
|
+
referenceCount += matchCount;
|
|
2121
|
+
}
|
|
2122
|
+
}
|
|
2123
|
+
const fileContent = fileContents.get(filePath) ?? "";
|
|
2124
|
+
const exportPattern = new RegExp(
|
|
2125
|
+
`export\\s+(?:const|let|var)\\s+${schemaName}\\b|export\\s*\\{[^}]*\\b${schemaName}\\b`
|
|
2126
|
+
);
|
|
2127
|
+
if (exportPattern.test(fileContent)) {
|
|
2128
|
+
referenceCount++;
|
|
2129
|
+
}
|
|
2130
|
+
if (referenceCount === 0) {
|
|
2131
|
+
unused.push(schema);
|
|
2132
|
+
}
|
|
2133
|
+
}
|
|
2134
|
+
return unused;
|
|
2135
|
+
}
|
|
2136
|
+
};
|
|
2137
|
+
|
|
2009
2138
|
// src/dependency-graph.ts
|
|
2010
2139
|
var import_node_fs5 = require("fs");
|
|
2011
2140
|
var import_node_path5 = require("path");
|
|
@@ -2546,15 +2675,13 @@ var DetailedAnalyzer = class {
|
|
|
2546
2675
|
var import_node_crypto2 = require("crypto");
|
|
2547
2676
|
var import_node_fs7 = require("fs");
|
|
2548
2677
|
var import_node_path7 = require("path");
|
|
2549
|
-
var SNAPSHOT_DIR = ".schemashift";
|
|
2550
|
-
var SNAPSHOT_FILE = "schema-snapshot.json";
|
|
2551
2678
|
var SNAPSHOT_VERSION = 1;
|
|
2552
2679
|
var DriftDetector = class {
|
|
2553
2680
|
snapshotDir;
|
|
2554
2681
|
snapshotPath;
|
|
2555
2682
|
constructor(projectPath) {
|
|
2556
|
-
this.snapshotDir = (0, import_node_path7.join)(projectPath,
|
|
2557
|
-
this.snapshotPath = (0, import_node_path7.join)(this.snapshotDir,
|
|
2683
|
+
this.snapshotDir = (0, import_node_path7.join)(projectPath, SCHEMASHIFT_DIR);
|
|
2684
|
+
this.snapshotPath = (0, import_node_path7.join)(this.snapshotDir, SCHEMA_SNAPSHOT_FILE);
|
|
2558
2685
|
}
|
|
2559
2686
|
/**
|
|
2560
2687
|
* Take a snapshot of the current schema state
|
|
@@ -2824,7 +2951,8 @@ var GovernanceEngine = class {
|
|
|
2824
2951
|
if (this.rules.has("naming-convention")) {
|
|
2825
2952
|
const config = this.rules.get("naming-convention") ?? {};
|
|
2826
2953
|
const pattern = config.pattern || ".*Schema$";
|
|
2827
|
-
|
|
2954
|
+
const regex = this.safeRegExp(pattern);
|
|
2955
|
+
if (regex && !regex.test(schemaName)) {
|
|
2828
2956
|
violations.push({
|
|
2829
2957
|
rule: "naming-convention",
|
|
2830
2958
|
message: `Schema "${schemaName}" does not match naming pattern: ${pattern}`,
|
|
@@ -2986,6 +3114,14 @@ var GovernanceEngine = class {
|
|
|
2986
3114
|
passed: violations.filter((v) => v.severity === "error").length === 0
|
|
2987
3115
|
};
|
|
2988
3116
|
}
|
|
3117
|
+
safeRegExp(pattern) {
|
|
3118
|
+
if (pattern.length > 500) return null;
|
|
3119
|
+
try {
|
|
3120
|
+
return new RegExp(pattern);
|
|
3121
|
+
} catch {
|
|
3122
|
+
return null;
|
|
3123
|
+
}
|
|
3124
|
+
}
|
|
2989
3125
|
detectFileLibrary(sourceFile) {
|
|
2990
3126
|
for (const imp of sourceFile.getImportDeclarations()) {
|
|
2991
3127
|
const lib = detectSchemaLibrary(imp.getModuleSpecifierValue());
|
|
@@ -3721,17 +3857,77 @@ var GraphExporter = class {
|
|
|
3721
3857
|
}
|
|
3722
3858
|
};
|
|
3723
3859
|
|
|
3860
|
+
// src/import-deduplicator.ts
|
|
3861
|
+
var ImportDeduplicator = class {
|
|
3862
|
+
detect(sourceFiles) {
|
|
3863
|
+
const allGroups = [];
|
|
3864
|
+
for (const file of sourceFiles) {
|
|
3865
|
+
const groups = this.findDuplicatesInFile(file);
|
|
3866
|
+
allGroups.push(...groups);
|
|
3867
|
+
}
|
|
3868
|
+
const totalDuplicates = allGroups.reduce((sum, g) => sum + g.occurrences.length, 0);
|
|
3869
|
+
return {
|
|
3870
|
+
duplicateGroups: allGroups,
|
|
3871
|
+
totalDuplicates,
|
|
3872
|
+
summary: allGroups.length > 0 ? `Found ${allGroups.length} duplicate import group(s) across ${new Set(allGroups.map((g) => g.occurrences[0]?.filePath)).size} file(s). Merge them for cleaner imports.` : "No duplicate imports found."
|
|
3873
|
+
};
|
|
3874
|
+
}
|
|
3875
|
+
findDuplicatesInFile(sourceFile) {
|
|
3876
|
+
const imports = sourceFile.getImportDeclarations();
|
|
3877
|
+
const filePath = sourceFile.getFilePath();
|
|
3878
|
+
const bySource = /* @__PURE__ */ new Map();
|
|
3879
|
+
for (const imp of imports) {
|
|
3880
|
+
const source = imp.getModuleSpecifierValue();
|
|
3881
|
+
const namedImports = imp.getNamedImports().map((n) => n.getName());
|
|
3882
|
+
const namespaceImport = imp.getNamespaceImport()?.getText();
|
|
3883
|
+
const defaultImport = imp.getDefaultImport()?.getText();
|
|
3884
|
+
const importedNames = [];
|
|
3885
|
+
if (defaultImport) importedNames.push(defaultImport);
|
|
3886
|
+
if (namespaceImport) importedNames.push(`* as ${namespaceImport}`);
|
|
3887
|
+
importedNames.push(...namedImports);
|
|
3888
|
+
if (importedNames.length === 0) continue;
|
|
3889
|
+
const entry = {
|
|
3890
|
+
source,
|
|
3891
|
+
filePath,
|
|
3892
|
+
lineNumber: imp.getStartLineNumber(),
|
|
3893
|
+
importedNames
|
|
3894
|
+
};
|
|
3895
|
+
const existing = bySource.get(source);
|
|
3896
|
+
if (existing) {
|
|
3897
|
+
existing.push(entry);
|
|
3898
|
+
} else {
|
|
3899
|
+
bySource.set(source, [entry]);
|
|
3900
|
+
}
|
|
3901
|
+
}
|
|
3902
|
+
const groups = [];
|
|
3903
|
+
for (const [source, occurrences] of bySource) {
|
|
3904
|
+
if (occurrences.length <= 1) continue;
|
|
3905
|
+
const allNames = /* @__PURE__ */ new Set();
|
|
3906
|
+
for (const occ of occurrences) {
|
|
3907
|
+
for (const name of occ.importedNames) {
|
|
3908
|
+
allNames.add(name);
|
|
3909
|
+
}
|
|
3910
|
+
}
|
|
3911
|
+
const mergedNames = [...allNames].sort().join(", ");
|
|
3912
|
+
groups.push({
|
|
3913
|
+
source,
|
|
3914
|
+
occurrences,
|
|
3915
|
+
suggestion: `Merge into single import: import { ${mergedNames} } from '${source}';`
|
|
3916
|
+
});
|
|
3917
|
+
}
|
|
3918
|
+
return groups;
|
|
3919
|
+
}
|
|
3920
|
+
};
|
|
3921
|
+
|
|
3724
3922
|
// src/incremental.ts
|
|
3725
3923
|
var import_node_fs8 = require("fs");
|
|
3726
3924
|
var import_node_path8 = require("path");
|
|
3727
|
-
var STATE_DIR = ".schemashift";
|
|
3728
|
-
var STATE_FILE = "incremental.json";
|
|
3729
3925
|
var IncrementalTracker = class {
|
|
3730
3926
|
stateDir;
|
|
3731
3927
|
statePath;
|
|
3732
3928
|
constructor(projectPath) {
|
|
3733
|
-
this.stateDir = (0, import_node_path8.join)(projectPath,
|
|
3734
|
-
this.statePath = (0, import_node_path8.join)(this.stateDir,
|
|
3929
|
+
this.stateDir = (0, import_node_path8.join)(projectPath, SCHEMASHIFT_DIR);
|
|
3930
|
+
this.statePath = (0, import_node_path8.join)(this.stateDir, INCREMENTAL_STATE_FILE);
|
|
3735
3931
|
}
|
|
3736
3932
|
start(files, from, to) {
|
|
3737
3933
|
const state = {
|
|
@@ -3768,7 +3964,9 @@ var IncrementalTracker = class {
|
|
|
3768
3964
|
getState() {
|
|
3769
3965
|
if (!(0, import_node_fs8.existsSync)(this.statePath)) return null;
|
|
3770
3966
|
try {
|
|
3771
|
-
|
|
3967
|
+
const parsed = JSON.parse((0, import_node_fs8.readFileSync)(this.statePath, "utf-8"));
|
|
3968
|
+
if (!this.isValidState(parsed)) return null;
|
|
3969
|
+
return parsed;
|
|
3772
3970
|
} catch {
|
|
3773
3971
|
return null;
|
|
3774
3972
|
}
|
|
@@ -3815,6 +4013,11 @@ var IncrementalTracker = class {
|
|
|
3815
4013
|
(0, import_node_fs8.unlinkSync)(this.statePath);
|
|
3816
4014
|
}
|
|
3817
4015
|
}
|
|
4016
|
+
isValidState(data) {
|
|
4017
|
+
if (typeof data !== "object" || data === null) return false;
|
|
4018
|
+
const obj = data;
|
|
4019
|
+
return typeof obj.migrationId === "string" && typeof obj.from === "string" && typeof obj.to === "string" && typeof obj.startedAt === "string" && Array.isArray(obj.completedFiles) && Array.isArray(obj.remainingFiles) && Array.isArray(obj.failedFiles);
|
|
4020
|
+
}
|
|
3818
4021
|
saveState(state) {
|
|
3819
4022
|
if (!(0, import_node_fs8.existsSync)(this.stateDir)) {
|
|
3820
4023
|
(0, import_node_fs8.mkdirSync)(this.stateDir, { recursive: true });
|
|
@@ -4138,11 +4341,15 @@ var WebhookNotifier = class {
|
|
|
4138
4341
|
const signature = await computeSignature(payload, webhook.secret);
|
|
4139
4342
|
headers["X-SchemaShift-Signature"] = `sha256=${signature}`;
|
|
4140
4343
|
}
|
|
4344
|
+
const timeoutMs = webhook.timeoutMs ?? 1e4;
|
|
4345
|
+
const controller = new AbortController();
|
|
4346
|
+
const timeoutId = setTimeout(() => controller.abort(), timeoutMs);
|
|
4141
4347
|
try {
|
|
4142
4348
|
const response = await fetch(webhook.url, {
|
|
4143
4349
|
method: "POST",
|
|
4144
4350
|
headers,
|
|
4145
|
-
body: payload
|
|
4351
|
+
body: payload,
|
|
4352
|
+
signal: controller.signal
|
|
4146
4353
|
});
|
|
4147
4354
|
return {
|
|
4148
4355
|
success: response.ok,
|
|
@@ -4150,10 +4357,13 @@ var WebhookNotifier = class {
|
|
|
4150
4357
|
error: response.ok ? void 0 : `HTTP ${response.status}: ${response.statusText}`
|
|
4151
4358
|
};
|
|
4152
4359
|
} catch (err) {
|
|
4360
|
+
const message = err instanceof Error && err.name === "AbortError" ? `Webhook request timed out after ${timeoutMs}ms` : err instanceof Error ? err.message : String(err);
|
|
4153
4361
|
return {
|
|
4154
4362
|
success: false,
|
|
4155
|
-
error:
|
|
4363
|
+
error: message
|
|
4156
4364
|
};
|
|
4365
|
+
} finally {
|
|
4366
|
+
clearTimeout(timeoutId);
|
|
4157
4367
|
}
|
|
4158
4368
|
}
|
|
4159
4369
|
/**
|
|
@@ -5068,11 +5278,16 @@ var TypeDedupDetector = class {
|
|
|
5068
5278
|
};
|
|
5069
5279
|
// Annotate the CommonJS export names for ESM import in node:
|
|
5070
5280
|
0 && (module.exports = {
|
|
5281
|
+
AUDIT_LOG_FILE,
|
|
5071
5282
|
ApprovalManager,
|
|
5283
|
+
BACKUP_DIR,
|
|
5072
5284
|
BehavioralWarningAnalyzer,
|
|
5073
5285
|
BundleEstimator,
|
|
5286
|
+
CONFIG_FILE_NAMES,
|
|
5074
5287
|
CompatibilityAnalyzer,
|
|
5075
5288
|
ComplexityEstimator,
|
|
5289
|
+
DEFAULT_CONFIG_FILE,
|
|
5290
|
+
DeadSchemaDetector,
|
|
5076
5291
|
DetailedAnalyzer,
|
|
5077
5292
|
DriftDetector,
|
|
5078
5293
|
EcosystemAnalyzer,
|
|
@@ -5081,16 +5296,22 @@ var TypeDedupDetector = class {
|
|
|
5081
5296
|
GovernanceEngine,
|
|
5082
5297
|
GovernanceFixer,
|
|
5083
5298
|
GraphExporter,
|
|
5299
|
+
INCREMENTAL_STATE_FILE,
|
|
5300
|
+
ImportDeduplicator,
|
|
5084
5301
|
IncrementalTracker,
|
|
5085
5302
|
MigrationAuditLog,
|
|
5086
5303
|
MigrationChain,
|
|
5087
5304
|
MonorepoResolver,
|
|
5305
|
+
PENDING_DIR,
|
|
5088
5306
|
PackageUpdater,
|
|
5089
5307
|
PerformanceAnalyzer,
|
|
5090
5308
|
PluginLoader,
|
|
5309
|
+
SCHEMASHIFT_DIR,
|
|
5310
|
+
SCHEMA_SNAPSHOT_FILE,
|
|
5091
5311
|
SchemaAnalyzer,
|
|
5092
5312
|
SchemaDependencyResolver,
|
|
5093
5313
|
StandardSchemaAdvisor,
|
|
5314
|
+
TESTS_DIR,
|
|
5094
5315
|
TestScaffolder,
|
|
5095
5316
|
TransformEngine,
|
|
5096
5317
|
TypeDedupDetector,
|