fallow-type-aware 3.16.0 → 3.17.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/README.md +7 -0
- package/fallow-type-aware.mjs +4 -1
- package/package.json +3 -2
- package/src/generated-protocol.mjs +2 -2
- package/src/project-state.mjs +111 -10
- package/src/semantic.mjs +57 -29
- package/src/type-coupling.mjs +1 -2
- package/src/windows-child-process.mjs +25 -0
package/README.md
CHANGED
|
@@ -25,6 +25,13 @@ and SHA-256 declaration guard, but Fallow owns the final decision and fix
|
|
|
25
25
|
policy. The sidecar does not emit TypeScript compiler diagnostics as Fallow
|
|
26
26
|
findings and does not implement generic typed lint rules.
|
|
27
27
|
|
|
28
|
+
The raw TypeScript-Go host cannot currently expose Svelte virtual-module named
|
|
29
|
+
exports. If source code imports or re-exports such a name and checker resolution
|
|
30
|
+
has no declaration target, the sidecar returns
|
|
31
|
+
`svelte-virtual-module-exports` instead of claiming complete evidence. Run
|
|
32
|
+
`svelte-check` for framework diagnostics; Fallow stays fail-closed until a
|
|
33
|
+
supported host seam can preserve virtual source identity and mappings.
|
|
34
|
+
|
|
28
35
|
## Run locally
|
|
29
36
|
|
|
30
37
|
```sh
|
package/fallow-type-aware.mjs
CHANGED
|
@@ -1,8 +1,11 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
|
-
import {
|
|
3
|
+
import { installWindowsChildProcessPolicy } from "./src/windows-child-process.mjs";
|
|
4
|
+
|
|
5
|
+
installWindowsChildProcessPolicy();
|
|
4
6
|
|
|
5
7
|
try {
|
|
8
|
+
const { assertTypescriptBackendResolvable } = await import("./src/backend-preflight.mjs");
|
|
6
9
|
assertTypescriptBackendResolvable();
|
|
7
10
|
const { run } = await import("./src/cli.mjs");
|
|
8
11
|
await run({ input: process.stdin, output: process.stdout, args: process.argv.slice(2) });
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "fallow-type-aware",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.17.0",
|
|
4
4
|
"description": "Optional TypeScript-Go semantic refinement sidecar for Fallow",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -32,6 +32,7 @@
|
|
|
32
32
|
},
|
|
33
33
|
"devDependencies": {
|
|
34
34
|
"@codspeed/tinybench-plugin": "5.7.1",
|
|
35
|
-
"tinybench": "6.1.2"
|
|
35
|
+
"tinybench": "6.1.2",
|
|
36
|
+
"zod": "4.4.3"
|
|
36
37
|
}
|
|
37
38
|
}
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
// Generated from crates/api/type-aware-protocol.json. Do not edit.
|
|
2
2
|
export const TYPE_AWARE_PROTOCOL = Object.freeze({
|
|
3
3
|
schema_version: 1,
|
|
4
|
-
wire_protocol_version:
|
|
5
|
-
semantic_schema_version:
|
|
4
|
+
wire_protocol_version: 7,
|
|
5
|
+
semantic_schema_version: 3,
|
|
6
6
|
analysis_operation: "semantic-queries",
|
|
7
7
|
status_operation: "status",
|
|
8
8
|
query_operations: ["symbol-use", "symbol-trace", "api-surface", "symbol-impact", "type-coupling"],
|
package/src/project-state.mjs
CHANGED
|
@@ -2,18 +2,107 @@ import { createHash } from "node:crypto";
|
|
|
2
2
|
import { existsSync, readFileSync } from "node:fs";
|
|
3
3
|
import path from "node:path";
|
|
4
4
|
|
|
5
|
+
import { SymbolFlags } from "typescript/unstable/sync";
|
|
6
|
+
import {
|
|
7
|
+
isExportDeclaration,
|
|
8
|
+
isImportDeclaration,
|
|
9
|
+
isNamedExports,
|
|
10
|
+
isNamedImports,
|
|
11
|
+
} from "typescript/unstable/ast/is";
|
|
12
|
+
|
|
5
13
|
import { canonicalFileIdentity } from "./file-identity.mjs";
|
|
6
|
-
import { relativePath } from "./semantic-identity.mjs";
|
|
14
|
+
import { projectSourceFiles, relativePath } from "./semantic-identity.mjs";
|
|
7
15
|
|
|
8
16
|
const INFERRED_PROJECT = "<inferred>";
|
|
9
17
|
const compareText = (left, right) => Buffer.compare(Buffer.from(left), Buffer.from(right));
|
|
10
18
|
const slash = (value) => value.split(path.sep).join("/");
|
|
11
19
|
|
|
12
|
-
const
|
|
13
|
-
project.program.getConfigFileParsingDiagnostics()
|
|
14
|
-
project.program.getProgramDiagnostics()
|
|
15
|
-
project.program.getSyntacticDiagnostics()
|
|
16
|
-
project.program.getBindDiagnostics()
|
|
20
|
+
const structuralDiagnostics = (project) => [
|
|
21
|
+
...project.program.getConfigFileParsingDiagnostics(),
|
|
22
|
+
...project.program.getProgramDiagnostics(),
|
|
23
|
+
...project.program.getSyntacticDiagnostics(),
|
|
24
|
+
...project.program.getBindDiagnostics(),
|
|
25
|
+
];
|
|
26
|
+
|
|
27
|
+
const isProjectLocalDiagnostic = (project, diagnostic) => {
|
|
28
|
+
if (!diagnostic.fileName) return true;
|
|
29
|
+
const sourceFile = project.program.getSourceFile(diagnostic.fileName);
|
|
30
|
+
if (!sourceFile) return true;
|
|
31
|
+
return (
|
|
32
|
+
!project.program.isSourceFileDefaultLibrary(sourceFile) &&
|
|
33
|
+
!project.program.isSourceFileFromExternalLibrary(sourceFile)
|
|
34
|
+
);
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
const isSvelteSpecifier = (node) =>
|
|
38
|
+
typeof node.moduleSpecifier?.text === "string" && node.moduleSpecifier.text.endsWith(".svelte");
|
|
39
|
+
|
|
40
|
+
const isUnknownAlias = (checker, specifier) => {
|
|
41
|
+
const symbol = checker.getSymbolAtLocation(specifier.name);
|
|
42
|
+
if (!symbol) return true;
|
|
43
|
+
return checker.isUnknownSymbol(checker.getAliasedSymbol(symbol));
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
const concreteExportSymbol = (checker, symbol) => {
|
|
47
|
+
const target =
|
|
48
|
+
(symbol.flags & SymbolFlags.Alias) === 0 ? symbol : checker.getAliasedSymbol(symbol);
|
|
49
|
+
return !checker.isUnknownSymbol(target) && (target.declarations?.length ?? 0) > 0;
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
const hasProvableNamedExports = (project, declaration) => {
|
|
53
|
+
const moduleSymbol = project.checker.getSymbolAtLocation(declaration.moduleSpecifier);
|
|
54
|
+
if (!moduleSymbol) return false;
|
|
55
|
+
const hasConcreteModuleDeclaration = moduleSymbol.declarations?.some((moduleDeclaration) => {
|
|
56
|
+
const declarationPath =
|
|
57
|
+
moduleDeclaration.path ??
|
|
58
|
+
moduleDeclaration.fileName ??
|
|
59
|
+
moduleDeclaration.getSourceFile?.().fileName ??
|
|
60
|
+
"";
|
|
61
|
+
return declarationPath.endsWith(".d.svelte") || declarationPath.endsWith(".d.svelte.ts");
|
|
62
|
+
});
|
|
63
|
+
if (!hasConcreteModuleDeclaration) return false;
|
|
64
|
+
const namedExports = project.checker
|
|
65
|
+
.getExportsOfModule(moduleSymbol)
|
|
66
|
+
.filter((symbol) => symbol.name !== "default");
|
|
67
|
+
return namedExports.every((symbol) => concreteExportSymbol(project.checker, symbol));
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
const svelteDeclarationHasGap = (project, node) => {
|
|
71
|
+
if (!isSvelteSpecifier(node)) return false;
|
|
72
|
+
if (isImportDeclaration(node)) {
|
|
73
|
+
const bindings = node.importClause?.namedBindings;
|
|
74
|
+
return (
|
|
75
|
+
bindings !== undefined &&
|
|
76
|
+
isNamedImports(bindings) &&
|
|
77
|
+
bindings.elements.some((item) => isUnknownAlias(project.checker, item))
|
|
78
|
+
);
|
|
79
|
+
}
|
|
80
|
+
if (!isExportDeclaration(node)) return false;
|
|
81
|
+
if (!node.exportClause) return !hasProvableNamedExports(project, node);
|
|
82
|
+
if (!isNamedExports(node.exportClause)) return !hasProvableNamedExports(project, node);
|
|
83
|
+
return node.exportClause.elements.some((item) => isUnknownAlias(project.checker, item));
|
|
84
|
+
};
|
|
85
|
+
|
|
86
|
+
const sourceHasSvelteVirtualModuleGap = (project, sourceFile) => {
|
|
87
|
+
let gap = false;
|
|
88
|
+
const visit = (node) => {
|
|
89
|
+
if (gap) return;
|
|
90
|
+
gap = svelteDeclarationHasGap(project, node);
|
|
91
|
+
if (gap) return;
|
|
92
|
+
node.forEachChild((child) => {
|
|
93
|
+
visit(child);
|
|
94
|
+
return undefined;
|
|
95
|
+
});
|
|
96
|
+
};
|
|
97
|
+
visit(sourceFile);
|
|
98
|
+
return gap;
|
|
99
|
+
};
|
|
100
|
+
|
|
101
|
+
const hasSvelteVirtualModuleGap = (project) =>
|
|
102
|
+
projectSourceFiles(project).some(
|
|
103
|
+
(sourceFile) =>
|
|
104
|
+
sourceFile.text.includes(".svelte") && sourceHasSvelteVirtualModuleGap(project, sourceFile),
|
|
105
|
+
);
|
|
17
106
|
|
|
18
107
|
const configPath = (root, project) => {
|
|
19
108
|
const normalized = slash(project.configFileName);
|
|
@@ -173,15 +262,27 @@ const effectiveProjectConfigHash = (root, project) => {
|
|
|
173
262
|
};
|
|
174
263
|
|
|
175
264
|
export const projectState = (root, project, source) => {
|
|
176
|
-
const
|
|
265
|
+
const diagnostics = structuralDiagnostics(project);
|
|
266
|
+
const localDiagnosticCount = diagnostics.filter((diagnostic) =>
|
|
267
|
+
isProjectLocalDiagnostic(project, diagnostic),
|
|
268
|
+
).length;
|
|
269
|
+
const hasLocalDiagnostics = localDiagnosticCount > 0;
|
|
270
|
+
const svelteVirtualModuleGap = !hasLocalDiagnostics && hasSvelteVirtualModuleGap(project);
|
|
271
|
+
const reasonCode = hasLocalDiagnostics
|
|
272
|
+
? "blocking-diagnostics"
|
|
273
|
+
: svelteVirtualModuleGap
|
|
274
|
+
? "svelte-virtual-module-exports"
|
|
275
|
+
: diagnostics.length > 0
|
|
276
|
+
? "blocking-diagnostics"
|
|
277
|
+
: null;
|
|
177
278
|
return {
|
|
178
279
|
project,
|
|
179
280
|
config: configPath(root, project),
|
|
180
281
|
effective_config_hash: effectiveProjectConfigHash(root, project),
|
|
181
282
|
source,
|
|
182
|
-
status:
|
|
183
|
-
reason_code:
|
|
184
|
-
blocking_diagnostic_count:
|
|
283
|
+
status: reasonCode === null ? "complete" : "unavailable",
|
|
284
|
+
reason_code: reasonCode,
|
|
285
|
+
blocking_diagnostic_count: reasonCode === "blocking-diagnostics" ? diagnostics.length : 0,
|
|
185
286
|
source_file_count: project.program.getSourceFileNames().length,
|
|
186
287
|
program_reused: false,
|
|
187
288
|
candidate_count: 0,
|
package/src/semantic.mjs
CHANGED
|
@@ -157,6 +157,10 @@ const REASON_ACTIONS = new Map([
|
|
|
157
157
|
"blocking-diagnostics",
|
|
158
158
|
"Repair structural TypeScript diagnostics in every selected project and retry.",
|
|
159
159
|
],
|
|
160
|
+
[
|
|
161
|
+
"svelte-virtual-module-exports",
|
|
162
|
+
"Run svelte-check for framework diagnostics. See https://docs.fallow.tools/analysis/type-aware#svelte-virtual-module-exports for supported Svelte project setup.",
|
|
163
|
+
],
|
|
160
164
|
[
|
|
161
165
|
"unknown-entry-point",
|
|
162
166
|
"Refresh the package entry points or pass project-relative source entry points.",
|
|
@@ -166,21 +170,22 @@ const DEFAULT_REASON_ACTION =
|
|
|
166
170
|
"Narrow the query to a specific symbol, entry point, or healthy TypeScript project and retry.";
|
|
167
171
|
const REASON_PRIORITY = new Map([
|
|
168
172
|
["blocking-diagnostics", 0],
|
|
169
|
-
["
|
|
170
|
-
["
|
|
171
|
-
["
|
|
172
|
-
["
|
|
173
|
-
["unknown-
|
|
174
|
-
["
|
|
175
|
-
["
|
|
176
|
-
["
|
|
177
|
-
["
|
|
178
|
-
["
|
|
179
|
-
["
|
|
180
|
-
["
|
|
181
|
-
["
|
|
182
|
-
["
|
|
183
|
-
["
|
|
173
|
+
["svelte-virtual-module-exports", 1],
|
|
174
|
+
["incomplete-project-coverage", 2],
|
|
175
|
+
["framework-contract-provenance", 3],
|
|
176
|
+
["ambiguous-project", 4],
|
|
177
|
+
["unknown-symbol", 5],
|
|
178
|
+
["unknown-entry-point", 6],
|
|
179
|
+
["decorated-declaration", 7],
|
|
180
|
+
["optional-contract", 8],
|
|
181
|
+
["accessor-pair", 9],
|
|
182
|
+
["overload-set", 10],
|
|
183
|
+
["attached-comment", 11],
|
|
184
|
+
["abstract-declaration", 12],
|
|
185
|
+
["dynamic-member-access", 13],
|
|
186
|
+
["virtual-dispatch", 14],
|
|
187
|
+
["dynamic-behavior", 15],
|
|
188
|
+
["evidence-limit", 16],
|
|
184
189
|
]);
|
|
185
190
|
|
|
186
191
|
const actionForReason = (reasonCode) => REASON_ACTIONS.get(reasonCode) ?? DEFAULT_REASON_ACTION;
|
|
@@ -201,6 +206,22 @@ const combineOmissions = (omissions) => {
|
|
|
201
206
|
return [...counts].map(([reason_code, count]) => ({ reason_code, count }));
|
|
202
207
|
};
|
|
203
208
|
|
|
209
|
+
const projectStateReason = (state) => state.reason_code ?? "blocking-diagnostics";
|
|
210
|
+
|
|
211
|
+
const unavailableProjectOmissions = (states) =>
|
|
212
|
+
combineOmissions(
|
|
213
|
+
states
|
|
214
|
+
.filter((state) => state.status !== "complete")
|
|
215
|
+
.map((state) => ({ reason_code: projectStateReason(state), count: 1 })),
|
|
216
|
+
);
|
|
217
|
+
|
|
218
|
+
const unavailableProjectReason = (states) => {
|
|
219
|
+
if (states.length === 0) return "no-project";
|
|
220
|
+
return (
|
|
221
|
+
unavailableProjectOmissions(states).toSorted(compareOmissions)[0]?.reason_code ?? "no-project"
|
|
222
|
+
);
|
|
223
|
+
};
|
|
224
|
+
|
|
204
225
|
const resultStatus = (partial) => (partial ? "partial" : "complete");
|
|
205
226
|
const resultReason = (omissions) => (omissions.length > 0 ? omissions[0].reason_code : null);
|
|
206
227
|
const resultActions = (omissions) =>
|
|
@@ -681,8 +702,6 @@ const symbolResolutionError = (query, reasonCode, action) => ({
|
|
|
681
702
|
});
|
|
682
703
|
|
|
683
704
|
const isCompleteProjectState = (state) => state?.status === "complete";
|
|
684
|
-
const selectedProjectName = (state) => state?.config ?? "the selected project";
|
|
685
|
-
|
|
686
705
|
const owningProjectContexts = (statesByProject, absolutePath) =>
|
|
687
706
|
[...statesByProject.entries()]
|
|
688
707
|
.filter(([project]) => project.program.getSourceFile(absolutePath))
|
|
@@ -722,13 +741,16 @@ const selectSymbolContext = (
|
|
|
722
741
|
}
|
|
723
742
|
const completeOwners = owners.filter(({ state }) => isCompleteProjectState(state));
|
|
724
743
|
if (completeOwners.length === 0) {
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
"blocking-diagnostics",
|
|
730
|
-
`Repair structural diagnostics in ${selectedProjectName(owners[0].state)} and retry.`,
|
|
744
|
+
const selectedOwner = [...owners].toSorted((left, right) =>
|
|
745
|
+
compareOmissions(
|
|
746
|
+
{ reason_code: projectStateReason(left.state) },
|
|
747
|
+
{ reason_code: projectStateReason(right.state) },
|
|
731
748
|
),
|
|
749
|
+
)[0];
|
|
750
|
+
const reasonCode = projectStateReason(selectedOwner.state);
|
|
751
|
+
return {
|
|
752
|
+
...selectedOwner,
|
|
753
|
+
...symbolResolutionError(query, reasonCode, actionForReason(reasonCode)),
|
|
732
754
|
};
|
|
733
755
|
}
|
|
734
756
|
return { owners, completeOwners };
|
|
@@ -1377,6 +1399,13 @@ const signatureTypes = (project, type, anchor) =>
|
|
|
1377
1399
|
);
|
|
1378
1400
|
|
|
1379
1401
|
const checkerTypeChildren = (project, type, anchor, hasNamedDeclaration) => {
|
|
1402
|
+
if (type.isTypeParameter()) {
|
|
1403
|
+
const constraint = safeCheckerValue(
|
|
1404
|
+
() => project.checker.getConstraintOfTypeParameter(type),
|
|
1405
|
+
undefined,
|
|
1406
|
+
);
|
|
1407
|
+
return constraint ? [constraint] : [];
|
|
1408
|
+
}
|
|
1380
1409
|
const structural = [
|
|
1381
1410
|
...(type.getTypes() ?? []),
|
|
1382
1411
|
...safeCheckerValue(() => type.getAliasTypeArguments(), []),
|
|
@@ -1547,12 +1576,9 @@ const graphProjects = (snapshot, explicitProjects) =>
|
|
|
1547
1576
|
const readyProjectStates = (query, states) => {
|
|
1548
1577
|
const ready = states.filter((state) => state.status === "complete");
|
|
1549
1578
|
if (ready.length > 0) return { ready };
|
|
1579
|
+
const reasonCode = unavailableProjectReason(states);
|
|
1550
1580
|
return {
|
|
1551
|
-
error: unavailable(
|
|
1552
|
-
query,
|
|
1553
|
-
states.length === 0 ? "no-project" : "blocking-diagnostics",
|
|
1554
|
-
"Pass a healthy tsconfig containing the package entry points and retry.",
|
|
1555
|
-
),
|
|
1581
|
+
error: unavailable(query, reasonCode, actionForReason(reasonCode)),
|
|
1556
1582
|
};
|
|
1557
1583
|
};
|
|
1558
1584
|
|
|
@@ -1684,7 +1710,7 @@ const analyzeApiSurface = (root, query, states, evidenceLimit) => {
|
|
|
1684
1710
|
reason_code: "evidence-limit",
|
|
1685
1711
|
count: omissionCount,
|
|
1686
1712
|
},
|
|
1687
|
-
|
|
1713
|
+
...unavailableProjectOmissions(states),
|
|
1688
1714
|
{ reason_code: "unknown-entry-point", count: missingEntryPointCount },
|
|
1689
1715
|
],
|
|
1690
1716
|
});
|
|
@@ -1763,6 +1789,7 @@ const analyzeGraphSemanticQuery = (root, query, graphStates, evidenceLimit) => {
|
|
|
1763
1789
|
missingEntryPoints,
|
|
1764
1790
|
publicApiGraph,
|
|
1765
1791
|
readyProjectStates,
|
|
1792
|
+
unavailableProjectOmissions,
|
|
1766
1793
|
uniqueSorted,
|
|
1767
1794
|
},
|
|
1768
1795
|
);
|
|
@@ -1842,6 +1869,7 @@ const recordAbstainedProjectOutcome = (state, result) => {
|
|
|
1842
1869
|
"no-project",
|
|
1843
1870
|
"ambiguous-project",
|
|
1844
1871
|
"blocking-diagnostics",
|
|
1872
|
+
"svelte-virtual-module-exports",
|
|
1845
1873
|
"unknown-symbol",
|
|
1846
1874
|
"incomplete-project-coverage",
|
|
1847
1875
|
]);
|
package/src/type-coupling.mjs
CHANGED
|
@@ -118,7 +118,6 @@ export const analyzeTypeCoupling = ({ root, query, states, evidenceLimit }, serv
|
|
|
118
118
|
const highCouplingThreshold = percentile(degrees, 0.9);
|
|
119
119
|
const topContributors = topCouplingContributors(perFile);
|
|
120
120
|
const cycles = query.includeCycles ? findCycles(edges) : [];
|
|
121
|
-
const unavailableProjectCount = states.length - readiness.ready.length;
|
|
122
121
|
const missingEntryPointCount = services.missingEntryPoints(query, resolvedEntryPoints);
|
|
123
122
|
const nestedFileOmissionCount = nestedCouplingOmissionCount(perFile, evidenceLimit);
|
|
124
123
|
const boundFile = (entry) => boundCoupledFile(entry, evidenceLimit);
|
|
@@ -159,7 +158,7 @@ export const analyzeTypeCoupling = ({ root, query, states, evidenceLimit }, serv
|
|
|
159
158
|
Math.max(0, cycles.length - evidenceLimit) +
|
|
160
159
|
nestedFileOmissionCount,
|
|
161
160
|
},
|
|
162
|
-
|
|
161
|
+
...services.unavailableProjectOmissions(states),
|
|
163
162
|
{ reason_code: "unknown-entry-point", count: missingEntryPointCount },
|
|
164
163
|
],
|
|
165
164
|
});
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import childProcess from "node:child_process";
|
|
2
|
+
import { syncBuiltinESMExports } from "node:module";
|
|
3
|
+
|
|
4
|
+
const INSTALL_MARKER = Symbol.for("fallow.type-aware.windows-child-process-policy");
|
|
5
|
+
|
|
6
|
+
const hiddenOptions = (options) => ({ ...options, windowsHide: true });
|
|
7
|
+
|
|
8
|
+
/** Keep TypeScript-Go child processes hidden when the sidecar runs on Windows. */
|
|
9
|
+
export const installWindowsChildProcessPolicy = ({
|
|
10
|
+
childProcess: processApi = childProcess,
|
|
11
|
+
platform = process.platform,
|
|
12
|
+
syncBuiltinESMExports: syncExports = syncBuiltinESMExports,
|
|
13
|
+
} = {}) => {
|
|
14
|
+
if (platform !== "win32" || processApi[INSTALL_MARKER] === true) return;
|
|
15
|
+
|
|
16
|
+
const originalSpawn = processApi.spawn;
|
|
17
|
+
processApi.spawn = function spawnHidden(command, args, options) {
|
|
18
|
+
if (Array.isArray(args)) {
|
|
19
|
+
return originalSpawn.call(this, command, args, hiddenOptions(options));
|
|
20
|
+
}
|
|
21
|
+
return originalSpawn.call(this, command, hiddenOptions(args));
|
|
22
|
+
};
|
|
23
|
+
Object.defineProperty(processApi, INSTALL_MARKER, { value: true });
|
|
24
|
+
syncExports();
|
|
25
|
+
};
|