@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.js
CHANGED
|
@@ -136,10 +136,30 @@ var SchemaAnalyzer = class {
|
|
|
136
136
|
// src/approval.ts
|
|
137
137
|
import { existsSync, mkdirSync, readdirSync, readFileSync, writeFileSync } from "fs";
|
|
138
138
|
import { join } from "path";
|
|
139
|
+
|
|
140
|
+
// src/constants.ts
|
|
141
|
+
var SCHEMASHIFT_DIR = ".schemashift";
|
|
142
|
+
var BACKUP_DIR = ".schemashift-backup";
|
|
143
|
+
var CONFIG_FILE_NAMES = [
|
|
144
|
+
".schemashiftrc",
|
|
145
|
+
".schemashiftrc.json",
|
|
146
|
+
".schemashiftrc.yaml",
|
|
147
|
+
".schemashiftrc.yml",
|
|
148
|
+
".schemashiftrc.js",
|
|
149
|
+
".schemashiftrc.cjs"
|
|
150
|
+
];
|
|
151
|
+
var DEFAULT_CONFIG_FILE = ".schemashiftrc.json";
|
|
152
|
+
var INCREMENTAL_STATE_FILE = "incremental.json";
|
|
153
|
+
var AUDIT_LOG_FILE = "audit-log.json";
|
|
154
|
+
var SCHEMA_SNAPSHOT_FILE = "schema-snapshot.json";
|
|
155
|
+
var PENDING_DIR = "pending";
|
|
156
|
+
var TESTS_DIR = "tests";
|
|
157
|
+
|
|
158
|
+
// src/approval.ts
|
|
139
159
|
var ApprovalManager = class {
|
|
140
160
|
pendingDir;
|
|
141
161
|
constructor(projectPath) {
|
|
142
|
-
this.pendingDir = join(projectPath,
|
|
162
|
+
this.pendingDir = join(projectPath, SCHEMASHIFT_DIR, PENDING_DIR);
|
|
143
163
|
}
|
|
144
164
|
/**
|
|
145
165
|
* Create a new migration request for review.
|
|
@@ -188,8 +208,16 @@ var ApprovalManager = class {
|
|
|
188
208
|
if (!existsSync(filePath)) {
|
|
189
209
|
return null;
|
|
190
210
|
}
|
|
191
|
-
|
|
192
|
-
|
|
211
|
+
try {
|
|
212
|
+
const content = readFileSync(filePath, "utf-8");
|
|
213
|
+
const parsed = JSON.parse(content);
|
|
214
|
+
if (!this.isValidRequest(parsed)) {
|
|
215
|
+
return null;
|
|
216
|
+
}
|
|
217
|
+
return parsed;
|
|
218
|
+
} catch {
|
|
219
|
+
return null;
|
|
220
|
+
}
|
|
193
221
|
}
|
|
194
222
|
/**
|
|
195
223
|
* List all migration requests, optionally filtered by status.
|
|
@@ -201,10 +229,14 @@ var ApprovalManager = class {
|
|
|
201
229
|
const files = readdirSync(this.pendingDir).filter((f) => f.endsWith(".json"));
|
|
202
230
|
const requests = [];
|
|
203
231
|
for (const file of files) {
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
232
|
+
try {
|
|
233
|
+
const content = readFileSync(join(this.pendingDir, file), "utf-8");
|
|
234
|
+
const parsed = JSON.parse(content);
|
|
235
|
+
if (!this.isValidRequest(parsed)) continue;
|
|
236
|
+
if (!status || parsed.status === status) {
|
|
237
|
+
requests.push(parsed);
|
|
238
|
+
}
|
|
239
|
+
} catch {
|
|
208
240
|
}
|
|
209
241
|
}
|
|
210
242
|
return requests.sort(
|
|
@@ -230,6 +262,11 @@ var ApprovalManager = class {
|
|
|
230
262
|
const request = this.getRequest(requestId);
|
|
231
263
|
return request?.status === "approved";
|
|
232
264
|
}
|
|
265
|
+
isValidRequest(data) {
|
|
266
|
+
if (typeof data !== "object" || data === null) return false;
|
|
267
|
+
const obj = data;
|
|
268
|
+
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);
|
|
269
|
+
}
|
|
233
270
|
ensureDir() {
|
|
234
271
|
if (!existsSync(this.pendingDir)) {
|
|
235
272
|
mkdirSync(this.pendingDir, { recursive: true });
|
|
@@ -375,15 +412,13 @@ function transformMethodChain(chain, newBase, factoryMapper, methodMapper) {
|
|
|
375
412
|
import { createHash } from "crypto";
|
|
376
413
|
import { existsSync as existsSync2, mkdirSync as mkdirSync2, readFileSync as readFileSync2, writeFileSync as writeFileSync2 } from "fs";
|
|
377
414
|
import { join as join2 } from "path";
|
|
378
|
-
var AUDIT_DIR = ".schemashift";
|
|
379
|
-
var AUDIT_FILE = "audit-log.json";
|
|
380
415
|
var AUDIT_VERSION = 1;
|
|
381
416
|
var MigrationAuditLog = class {
|
|
382
417
|
logDir;
|
|
383
418
|
logPath;
|
|
384
419
|
constructor(projectPath) {
|
|
385
|
-
this.logDir = join2(projectPath,
|
|
386
|
-
this.logPath = join2(this.logDir,
|
|
420
|
+
this.logDir = join2(projectPath, SCHEMASHIFT_DIR);
|
|
421
|
+
this.logPath = join2(this.logDir, AUDIT_LOG_FILE);
|
|
387
422
|
}
|
|
388
423
|
/**
|
|
389
424
|
* Append a new entry to the audit log.
|
|
@@ -427,7 +462,11 @@ var MigrationAuditLog = class {
|
|
|
427
462
|
if (!content.trim()) {
|
|
428
463
|
return { version: AUDIT_VERSION, entries: [] };
|
|
429
464
|
}
|
|
430
|
-
|
|
465
|
+
const parsed = JSON.parse(content);
|
|
466
|
+
if (!this.isValidAuditLog(parsed)) {
|
|
467
|
+
return { version: AUDIT_VERSION, entries: [] };
|
|
468
|
+
}
|
|
469
|
+
return parsed;
|
|
431
470
|
} catch {
|
|
432
471
|
return { version: AUDIT_VERSION, entries: [] };
|
|
433
472
|
}
|
|
@@ -597,6 +636,13 @@ var MigrationAuditLog = class {
|
|
|
597
636
|
gitCommit: process.env.GITHUB_SHA || process.env.CI_COMMIT_SHA || void 0
|
|
598
637
|
};
|
|
599
638
|
}
|
|
639
|
+
isValidAuditLog(data) {
|
|
640
|
+
if (typeof data !== "object" || data === null) return false;
|
|
641
|
+
const obj = data;
|
|
642
|
+
if (typeof obj.version !== "number") return false;
|
|
643
|
+
if (!Array.isArray(obj.entries)) return false;
|
|
644
|
+
return true;
|
|
645
|
+
}
|
|
600
646
|
write(log) {
|
|
601
647
|
if (!existsSync2(this.logDir)) {
|
|
602
648
|
mkdirSync2(this.logDir, { recursive: true });
|
|
@@ -1040,6 +1086,12 @@ import { join as join4 } from "path";
|
|
|
1040
1086
|
// src/ecosystem.ts
|
|
1041
1087
|
import { existsSync as existsSync3, readFileSync as readFileSync3 } from "fs";
|
|
1042
1088
|
import { join as join3 } from "path";
|
|
1089
|
+
function parseMajorVersion(version) {
|
|
1090
|
+
const match = version.match(/(\d+)/);
|
|
1091
|
+
const num = match?.[1] ? Number.parseInt(match[1], 10) : 0;
|
|
1092
|
+
if (!Number.isFinite(num) || num < 0 || num > 999) return 0;
|
|
1093
|
+
return num;
|
|
1094
|
+
}
|
|
1043
1095
|
var ECOSYSTEM_RULES = [
|
|
1044
1096
|
// ORM integrations
|
|
1045
1097
|
{
|
|
@@ -1081,8 +1133,7 @@ var ECOSYSTEM_RULES = [
|
|
|
1081
1133
|
category: "api",
|
|
1082
1134
|
migrations: ["zod-v3->v4"],
|
|
1083
1135
|
check: (version) => {
|
|
1084
|
-
const
|
|
1085
|
-
const major = majorMatch?.[1] ? Number.parseInt(majorMatch[1], 10) : 0;
|
|
1136
|
+
const major = parseMajorVersion(version);
|
|
1086
1137
|
if (major < 11) {
|
|
1087
1138
|
return {
|
|
1088
1139
|
issue: `tRPC v${major} expects Zod v3 types. A v3 ZodType is not assignable to a v4 ZodType.`,
|
|
@@ -1115,8 +1166,7 @@ var ECOSYSTEM_RULES = [
|
|
|
1115
1166
|
category: "validation-util",
|
|
1116
1167
|
migrations: ["zod-v3->v4"],
|
|
1117
1168
|
check: (version) => {
|
|
1118
|
-
const
|
|
1119
|
-
const major = majorMatch?.[1] ? Number.parseInt(majorMatch[1], 10) : 0;
|
|
1169
|
+
const major = parseMajorVersion(version);
|
|
1120
1170
|
if (major < 4) {
|
|
1121
1171
|
return {
|
|
1122
1172
|
issue: `zod-validation-error v${major} is not compatible with Zod v4.`,
|
|
@@ -1406,8 +1456,7 @@ var ECOSYSTEM_RULES = [
|
|
|
1406
1456
|
category: "validation-util",
|
|
1407
1457
|
migrations: ["zod-v3->v4"],
|
|
1408
1458
|
check: (version) => {
|
|
1409
|
-
const
|
|
1410
|
-
const major = majorMatch?.[1] ? Number.parseInt(majorMatch[1], 10) : 0;
|
|
1459
|
+
const major = parseMajorVersion(version);
|
|
1411
1460
|
if (major < 4) {
|
|
1412
1461
|
return {
|
|
1413
1462
|
issue: "zod-to-json-schema v3 may not fully support Zod v4 schemas.",
|
|
@@ -1802,7 +1851,7 @@ async function loadConfig(configPath) {
|
|
|
1802
1851
|
include: ["**/*.ts", "**/*.tsx"],
|
|
1803
1852
|
exclude: ["**/node_modules/**", "**/dist/**", "**/*.d.ts"],
|
|
1804
1853
|
git: { enabled: false },
|
|
1805
|
-
backup: { enabled: true, dir:
|
|
1854
|
+
backup: { enabled: true, dir: BACKUP_DIR },
|
|
1806
1855
|
...result?.config
|
|
1807
1856
|
};
|
|
1808
1857
|
}
|
|
@@ -1913,6 +1962,75 @@ function suggestCrossFieldPattern(whenCode) {
|
|
|
1913
1962
|
return null;
|
|
1914
1963
|
}
|
|
1915
1964
|
|
|
1965
|
+
// src/dead-schema-detector.ts
|
|
1966
|
+
var DeadSchemaDetector = class {
|
|
1967
|
+
detect(sourceFiles) {
|
|
1968
|
+
const schemas = this.collectSchemaDefinitions(sourceFiles);
|
|
1969
|
+
const unusedSchemas = this.findUnusedSchemas(schemas, sourceFiles);
|
|
1970
|
+
return {
|
|
1971
|
+
unusedSchemas,
|
|
1972
|
+
totalSchemas: schemas.length,
|
|
1973
|
+
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.`
|
|
1974
|
+
};
|
|
1975
|
+
}
|
|
1976
|
+
collectSchemaDefinitions(sourceFiles) {
|
|
1977
|
+
const schemas = [];
|
|
1978
|
+
const schemaPattern = /(?:const|let|var|export\s+(?:const|let|var))\s+(\w+)\s*=\s*(?:z\.|yup\.|Yup\.|Joi\.|t\.|v\.|type\(|object\(|string\(|S\.)/;
|
|
1979
|
+
for (const file of sourceFiles) {
|
|
1980
|
+
const text = file.getFullText();
|
|
1981
|
+
const lines = text.split("\n");
|
|
1982
|
+
const filePath = file.getFilePath();
|
|
1983
|
+
for (let i = 0; i < lines.length; i++) {
|
|
1984
|
+
const line = lines[i];
|
|
1985
|
+
if (!line) continue;
|
|
1986
|
+
const match = schemaPattern.exec(line);
|
|
1987
|
+
if (match?.[1]) {
|
|
1988
|
+
schemas.push({
|
|
1989
|
+
schemaName: match[1],
|
|
1990
|
+
filePath,
|
|
1991
|
+
lineNumber: i + 1
|
|
1992
|
+
});
|
|
1993
|
+
}
|
|
1994
|
+
}
|
|
1995
|
+
}
|
|
1996
|
+
return schemas;
|
|
1997
|
+
}
|
|
1998
|
+
findUnusedSchemas(schemas, sourceFiles) {
|
|
1999
|
+
const fileContents = /* @__PURE__ */ new Map();
|
|
2000
|
+
for (const file of sourceFiles) {
|
|
2001
|
+
fileContents.set(file.getFilePath(), file.getFullText());
|
|
2002
|
+
}
|
|
2003
|
+
const unused = [];
|
|
2004
|
+
for (const schema of schemas) {
|
|
2005
|
+
const { schemaName, filePath } = schema;
|
|
2006
|
+
let referenceCount = 0;
|
|
2007
|
+
for (const [path, content] of fileContents) {
|
|
2008
|
+
const pattern = new RegExp(`\\b${schemaName}\\b`, "g");
|
|
2009
|
+
const matches = content.match(pattern);
|
|
2010
|
+
const matchCount = matches?.length ?? 0;
|
|
2011
|
+
if (path === filePath) {
|
|
2012
|
+
if (matchCount > 1) {
|
|
2013
|
+
referenceCount += matchCount - 1;
|
|
2014
|
+
}
|
|
2015
|
+
} else {
|
|
2016
|
+
referenceCount += matchCount;
|
|
2017
|
+
}
|
|
2018
|
+
}
|
|
2019
|
+
const fileContent = fileContents.get(filePath) ?? "";
|
|
2020
|
+
const exportPattern = new RegExp(
|
|
2021
|
+
`export\\s+(?:const|let|var)\\s+${schemaName}\\b|export\\s*\\{[^}]*\\b${schemaName}\\b`
|
|
2022
|
+
);
|
|
2023
|
+
if (exportPattern.test(fileContent)) {
|
|
2024
|
+
referenceCount++;
|
|
2025
|
+
}
|
|
2026
|
+
if (referenceCount === 0) {
|
|
2027
|
+
unused.push(schema);
|
|
2028
|
+
}
|
|
2029
|
+
}
|
|
2030
|
+
return unused;
|
|
2031
|
+
}
|
|
2032
|
+
};
|
|
2033
|
+
|
|
1916
2034
|
// src/dependency-graph.ts
|
|
1917
2035
|
import { existsSync as existsSync5, readdirSync as readdirSync2, readFileSync as readFileSync5 } from "fs";
|
|
1918
2036
|
import { join as join5, resolve } from "path";
|
|
@@ -2453,15 +2571,13 @@ var DetailedAnalyzer = class {
|
|
|
2453
2571
|
import { createHash as createHash2 } from "crypto";
|
|
2454
2572
|
import { existsSync as existsSync7, mkdirSync as mkdirSync3, readFileSync as readFileSync7, writeFileSync as writeFileSync3 } from "fs";
|
|
2455
2573
|
import { join as join7, relative } from "path";
|
|
2456
|
-
var SNAPSHOT_DIR = ".schemashift";
|
|
2457
|
-
var SNAPSHOT_FILE = "schema-snapshot.json";
|
|
2458
2574
|
var SNAPSHOT_VERSION = 1;
|
|
2459
2575
|
var DriftDetector = class {
|
|
2460
2576
|
snapshotDir;
|
|
2461
2577
|
snapshotPath;
|
|
2462
2578
|
constructor(projectPath) {
|
|
2463
|
-
this.snapshotDir = join7(projectPath,
|
|
2464
|
-
this.snapshotPath = join7(this.snapshotDir,
|
|
2579
|
+
this.snapshotDir = join7(projectPath, SCHEMASHIFT_DIR);
|
|
2580
|
+
this.snapshotPath = join7(this.snapshotDir, SCHEMA_SNAPSHOT_FILE);
|
|
2465
2581
|
}
|
|
2466
2582
|
/**
|
|
2467
2583
|
* Take a snapshot of the current schema state
|
|
@@ -2731,7 +2847,8 @@ var GovernanceEngine = class {
|
|
|
2731
2847
|
if (this.rules.has("naming-convention")) {
|
|
2732
2848
|
const config = this.rules.get("naming-convention") ?? {};
|
|
2733
2849
|
const pattern = config.pattern || ".*Schema$";
|
|
2734
|
-
|
|
2850
|
+
const regex = this.safeRegExp(pattern);
|
|
2851
|
+
if (regex && !regex.test(schemaName)) {
|
|
2735
2852
|
violations.push({
|
|
2736
2853
|
rule: "naming-convention",
|
|
2737
2854
|
message: `Schema "${schemaName}" does not match naming pattern: ${pattern}`,
|
|
@@ -2893,6 +3010,14 @@ var GovernanceEngine = class {
|
|
|
2893
3010
|
passed: violations.filter((v) => v.severity === "error").length === 0
|
|
2894
3011
|
};
|
|
2895
3012
|
}
|
|
3013
|
+
safeRegExp(pattern) {
|
|
3014
|
+
if (pattern.length > 500) return null;
|
|
3015
|
+
try {
|
|
3016
|
+
return new RegExp(pattern);
|
|
3017
|
+
} catch {
|
|
3018
|
+
return null;
|
|
3019
|
+
}
|
|
3020
|
+
}
|
|
2896
3021
|
detectFileLibrary(sourceFile) {
|
|
2897
3022
|
for (const imp of sourceFile.getImportDeclarations()) {
|
|
2898
3023
|
const lib = detectSchemaLibrary(imp.getModuleSpecifierValue());
|
|
@@ -3628,17 +3753,77 @@ var GraphExporter = class {
|
|
|
3628
3753
|
}
|
|
3629
3754
|
};
|
|
3630
3755
|
|
|
3756
|
+
// src/import-deduplicator.ts
|
|
3757
|
+
var ImportDeduplicator = class {
|
|
3758
|
+
detect(sourceFiles) {
|
|
3759
|
+
const allGroups = [];
|
|
3760
|
+
for (const file of sourceFiles) {
|
|
3761
|
+
const groups = this.findDuplicatesInFile(file);
|
|
3762
|
+
allGroups.push(...groups);
|
|
3763
|
+
}
|
|
3764
|
+
const totalDuplicates = allGroups.reduce((sum, g) => sum + g.occurrences.length, 0);
|
|
3765
|
+
return {
|
|
3766
|
+
duplicateGroups: allGroups,
|
|
3767
|
+
totalDuplicates,
|
|
3768
|
+
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."
|
|
3769
|
+
};
|
|
3770
|
+
}
|
|
3771
|
+
findDuplicatesInFile(sourceFile) {
|
|
3772
|
+
const imports = sourceFile.getImportDeclarations();
|
|
3773
|
+
const filePath = sourceFile.getFilePath();
|
|
3774
|
+
const bySource = /* @__PURE__ */ new Map();
|
|
3775
|
+
for (const imp of imports) {
|
|
3776
|
+
const source = imp.getModuleSpecifierValue();
|
|
3777
|
+
const namedImports = imp.getNamedImports().map((n) => n.getName());
|
|
3778
|
+
const namespaceImport = imp.getNamespaceImport()?.getText();
|
|
3779
|
+
const defaultImport = imp.getDefaultImport()?.getText();
|
|
3780
|
+
const importedNames = [];
|
|
3781
|
+
if (defaultImport) importedNames.push(defaultImport);
|
|
3782
|
+
if (namespaceImport) importedNames.push(`* as ${namespaceImport}`);
|
|
3783
|
+
importedNames.push(...namedImports);
|
|
3784
|
+
if (importedNames.length === 0) continue;
|
|
3785
|
+
const entry = {
|
|
3786
|
+
source,
|
|
3787
|
+
filePath,
|
|
3788
|
+
lineNumber: imp.getStartLineNumber(),
|
|
3789
|
+
importedNames
|
|
3790
|
+
};
|
|
3791
|
+
const existing = bySource.get(source);
|
|
3792
|
+
if (existing) {
|
|
3793
|
+
existing.push(entry);
|
|
3794
|
+
} else {
|
|
3795
|
+
bySource.set(source, [entry]);
|
|
3796
|
+
}
|
|
3797
|
+
}
|
|
3798
|
+
const groups = [];
|
|
3799
|
+
for (const [source, occurrences] of bySource) {
|
|
3800
|
+
if (occurrences.length <= 1) continue;
|
|
3801
|
+
const allNames = /* @__PURE__ */ new Set();
|
|
3802
|
+
for (const occ of occurrences) {
|
|
3803
|
+
for (const name of occ.importedNames) {
|
|
3804
|
+
allNames.add(name);
|
|
3805
|
+
}
|
|
3806
|
+
}
|
|
3807
|
+
const mergedNames = [...allNames].sort().join(", ");
|
|
3808
|
+
groups.push({
|
|
3809
|
+
source,
|
|
3810
|
+
occurrences,
|
|
3811
|
+
suggestion: `Merge into single import: import { ${mergedNames} } from '${source}';`
|
|
3812
|
+
});
|
|
3813
|
+
}
|
|
3814
|
+
return groups;
|
|
3815
|
+
}
|
|
3816
|
+
};
|
|
3817
|
+
|
|
3631
3818
|
// src/incremental.ts
|
|
3632
3819
|
import { existsSync as existsSync8, mkdirSync as mkdirSync4, readFileSync as readFileSync8, unlinkSync, writeFileSync as writeFileSync4 } from "fs";
|
|
3633
3820
|
import { join as join8 } from "path";
|
|
3634
|
-
var STATE_DIR = ".schemashift";
|
|
3635
|
-
var STATE_FILE = "incremental.json";
|
|
3636
3821
|
var IncrementalTracker = class {
|
|
3637
3822
|
stateDir;
|
|
3638
3823
|
statePath;
|
|
3639
3824
|
constructor(projectPath) {
|
|
3640
|
-
this.stateDir = join8(projectPath,
|
|
3641
|
-
this.statePath = join8(this.stateDir,
|
|
3825
|
+
this.stateDir = join8(projectPath, SCHEMASHIFT_DIR);
|
|
3826
|
+
this.statePath = join8(this.stateDir, INCREMENTAL_STATE_FILE);
|
|
3642
3827
|
}
|
|
3643
3828
|
start(files, from, to) {
|
|
3644
3829
|
const state = {
|
|
@@ -3675,7 +3860,9 @@ var IncrementalTracker = class {
|
|
|
3675
3860
|
getState() {
|
|
3676
3861
|
if (!existsSync8(this.statePath)) return null;
|
|
3677
3862
|
try {
|
|
3678
|
-
|
|
3863
|
+
const parsed = JSON.parse(readFileSync8(this.statePath, "utf-8"));
|
|
3864
|
+
if (!this.isValidState(parsed)) return null;
|
|
3865
|
+
return parsed;
|
|
3679
3866
|
} catch {
|
|
3680
3867
|
return null;
|
|
3681
3868
|
}
|
|
@@ -3722,6 +3909,11 @@ var IncrementalTracker = class {
|
|
|
3722
3909
|
unlinkSync(this.statePath);
|
|
3723
3910
|
}
|
|
3724
3911
|
}
|
|
3912
|
+
isValidState(data) {
|
|
3913
|
+
if (typeof data !== "object" || data === null) return false;
|
|
3914
|
+
const obj = data;
|
|
3915
|
+
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);
|
|
3916
|
+
}
|
|
3725
3917
|
saveState(state) {
|
|
3726
3918
|
if (!existsSync8(this.stateDir)) {
|
|
3727
3919
|
mkdirSync4(this.stateDir, { recursive: true });
|
|
@@ -4045,11 +4237,15 @@ var WebhookNotifier = class {
|
|
|
4045
4237
|
const signature = await computeSignature(payload, webhook.secret);
|
|
4046
4238
|
headers["X-SchemaShift-Signature"] = `sha256=${signature}`;
|
|
4047
4239
|
}
|
|
4240
|
+
const timeoutMs = webhook.timeoutMs ?? 1e4;
|
|
4241
|
+
const controller = new AbortController();
|
|
4242
|
+
const timeoutId = setTimeout(() => controller.abort(), timeoutMs);
|
|
4048
4243
|
try {
|
|
4049
4244
|
const response = await fetch(webhook.url, {
|
|
4050
4245
|
method: "POST",
|
|
4051
4246
|
headers,
|
|
4052
|
-
body: payload
|
|
4247
|
+
body: payload,
|
|
4248
|
+
signal: controller.signal
|
|
4053
4249
|
});
|
|
4054
4250
|
return {
|
|
4055
4251
|
success: response.ok,
|
|
@@ -4057,10 +4253,13 @@ var WebhookNotifier = class {
|
|
|
4057
4253
|
error: response.ok ? void 0 : `HTTP ${response.status}: ${response.statusText}`
|
|
4058
4254
|
};
|
|
4059
4255
|
} catch (err) {
|
|
4256
|
+
const message = err instanceof Error && err.name === "AbortError" ? `Webhook request timed out after ${timeoutMs}ms` : err instanceof Error ? err.message : String(err);
|
|
4060
4257
|
return {
|
|
4061
4258
|
success: false,
|
|
4062
|
-
error:
|
|
4259
|
+
error: message
|
|
4063
4260
|
};
|
|
4261
|
+
} finally {
|
|
4262
|
+
clearTimeout(timeoutId);
|
|
4064
4263
|
}
|
|
4065
4264
|
}
|
|
4066
4265
|
/**
|
|
@@ -4974,11 +5173,16 @@ var TypeDedupDetector = class {
|
|
|
4974
5173
|
}
|
|
4975
5174
|
};
|
|
4976
5175
|
export {
|
|
5176
|
+
AUDIT_LOG_FILE,
|
|
4977
5177
|
ApprovalManager,
|
|
5178
|
+
BACKUP_DIR,
|
|
4978
5179
|
BehavioralWarningAnalyzer,
|
|
4979
5180
|
BundleEstimator,
|
|
5181
|
+
CONFIG_FILE_NAMES,
|
|
4980
5182
|
CompatibilityAnalyzer,
|
|
4981
5183
|
ComplexityEstimator,
|
|
5184
|
+
DEFAULT_CONFIG_FILE,
|
|
5185
|
+
DeadSchemaDetector,
|
|
4982
5186
|
DetailedAnalyzer,
|
|
4983
5187
|
DriftDetector,
|
|
4984
5188
|
EcosystemAnalyzer,
|
|
@@ -4987,16 +5191,22 @@ export {
|
|
|
4987
5191
|
GovernanceEngine,
|
|
4988
5192
|
GovernanceFixer,
|
|
4989
5193
|
GraphExporter,
|
|
5194
|
+
INCREMENTAL_STATE_FILE,
|
|
5195
|
+
ImportDeduplicator,
|
|
4990
5196
|
IncrementalTracker,
|
|
4991
5197
|
MigrationAuditLog,
|
|
4992
5198
|
MigrationChain,
|
|
4993
5199
|
MonorepoResolver,
|
|
5200
|
+
PENDING_DIR,
|
|
4994
5201
|
PackageUpdater,
|
|
4995
5202
|
PerformanceAnalyzer,
|
|
4996
5203
|
PluginLoader,
|
|
5204
|
+
SCHEMASHIFT_DIR,
|
|
5205
|
+
SCHEMA_SNAPSHOT_FILE,
|
|
4997
5206
|
SchemaAnalyzer,
|
|
4998
5207
|
SchemaDependencyResolver,
|
|
4999
5208
|
StandardSchemaAdvisor,
|
|
5209
|
+
TESTS_DIR,
|
|
5000
5210
|
TestScaffolder,
|
|
5001
5211
|
TransformEngine,
|
|
5002
5212
|
TypeDedupDetector,
|