ushman-equiv 0.4.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/AGENTS.md +81 -0
- package/LICENSE.md +21 -0
- package/README.md +201 -0
- package/bin/ushman-equiv +19 -0
- package/dist/analysis-context.d.ts +102 -0
- package/dist/analysis-context.d.ts.map +1 -0
- package/dist/analysis-context.js +708 -0
- package/dist/ast-guards.d.ts +24 -0
- package/dist/ast-guards.d.ts.map +1 -0
- package/dist/ast-guards.js +83 -0
- package/dist/candidate-boot.d.ts +30 -0
- package/dist/candidate-boot.d.ts.map +1 -0
- package/dist/candidate-boot.js +262 -0
- package/dist/canonicalize.d.ts +19 -0
- package/dist/canonicalize.d.ts.map +1 -0
- package/dist/canonicalize.js +525 -0
- package/dist/cli.d.ts +4 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +312 -0
- package/dist/equiv-execution-context.d.ts +25 -0
- package/dist/equiv-execution-context.d.ts.map +1 -0
- package/dist/equiv-execution-context.js +82 -0
- package/dist/index.d.ts +13 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +11 -0
- package/dist/run.d.ts +8 -0
- package/dist/run.d.ts.map +1 -0
- package/dist/run.js +129 -0
- package/dist/shared.d.ts +9 -0
- package/dist/shared.d.ts.map +1 -0
- package/dist/shared.js +47 -0
- package/dist/tier-i-import-graph.d.ts +7 -0
- package/dist/tier-i-import-graph.d.ts.map +1 -0
- package/dist/tier-i-import-graph.js +34 -0
- package/dist/tier-l-child-runtime.d.ts +2 -0
- package/dist/tier-l-child-runtime.d.ts.map +1 -0
- package/dist/tier-l-child-runtime.js +62 -0
- package/dist/tier-l-module-load.d.ts +6 -0
- package/dist/tier-l-module-load.d.ts.map +1 -0
- package/dist/tier-l-module-load.js +139 -0
- package/dist/tier-l-stub-source.d.ts +11 -0
- package/dist/tier-l-stub-source.d.ts.map +1 -0
- package/dist/tier-l-stub-source.js +246 -0
- package/dist/tier-r-replay.d.ts +6 -0
- package/dist/tier-r-replay.d.ts.map +1 -0
- package/dist/tier-r-replay.js +382 -0
- package/dist/tier-s-symbol-diff.d.ts +19 -0
- package/dist/tier-s-symbol-diff.d.ts.map +1 -0
- package/dist/tier-s-symbol-diff.js +156 -0
- package/dist/types.d.ts +91 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +19 -0
- package/dist/workspace.d.ts +63 -0
- package/dist/workspace.d.ts.map +1 -0
- package/dist/workspace.js +459 -0
- package/package.json +64 -0
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { type ArrayPattern, type AssignmentPattern, type CallExpression, type ClassDeclaration, type ExportAllDeclaration, type ExportDefaultDeclaration, type ExportNamedDeclaration, type ExportSpecifier, type FunctionDeclaration, type Identifier, type ImportDeclaration, type ImportDefaultSpecifier, type ImportNamespaceSpecifier, type ImportSpecifier, type Node, type ObjectPattern, type ObjectProperty, type RestElement, type StringLiteral, type VariableDeclaration, type VariableDeclarator } from '@babel/types';
|
|
2
|
+
export declare const isRecord: (value: unknown) => value is Record<string, unknown>;
|
|
3
|
+
export declare const asNode: (value: unknown) => Node | null;
|
|
4
|
+
export declare const isArrayPatternNode: (value: unknown) => value is ArrayPattern;
|
|
5
|
+
export declare const isAssignmentPatternNode: (value: unknown) => value is AssignmentPattern;
|
|
6
|
+
export declare const isCallExpressionNode: (value: unknown) => value is CallExpression;
|
|
7
|
+
export declare const isClassDeclarationNode: (value: unknown) => value is ClassDeclaration;
|
|
8
|
+
export declare const isExportAllDeclarationNode: (value: unknown) => value is ExportAllDeclaration;
|
|
9
|
+
export declare const isExportDefaultDeclarationNode: (value: unknown) => value is ExportDefaultDeclaration;
|
|
10
|
+
export declare const isExportNamedDeclarationNode: (value: unknown) => value is ExportNamedDeclaration;
|
|
11
|
+
export declare const isExportSpecifierNode: (value: unknown) => value is ExportSpecifier;
|
|
12
|
+
export declare const isFunctionDeclarationNode: (value: unknown) => value is FunctionDeclaration;
|
|
13
|
+
export declare const isIdentifierNode: (value: unknown) => value is Identifier;
|
|
14
|
+
export declare const isImportDeclarationNode: (value: unknown) => value is ImportDeclaration;
|
|
15
|
+
export declare const isImportDefaultSpecifierNode: (value: unknown) => value is ImportDefaultSpecifier;
|
|
16
|
+
export declare const isImportNamespaceSpecifierNode: (value: unknown) => value is ImportNamespaceSpecifier;
|
|
17
|
+
export declare const isImportSpecifierNode: (value: unknown) => value is ImportSpecifier;
|
|
18
|
+
export declare const isObjectPatternNode: (value: unknown) => value is ObjectPattern;
|
|
19
|
+
export declare const isObjectPropertyNode: (value: unknown) => value is ObjectProperty;
|
|
20
|
+
export declare const isRestElementNode: (value: unknown) => value is RestElement;
|
|
21
|
+
export declare const isStringLiteralNode: (value: unknown) => value is StringLiteral;
|
|
22
|
+
export declare const isVariableDeclarationNode: (value: unknown) => value is VariableDeclaration;
|
|
23
|
+
export declare const isVariableDeclaratorNode: (value: unknown) => value is VariableDeclarator;
|
|
24
|
+
//# sourceMappingURL=ast-guards.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ast-guards.d.ts","sourceRoot":"","sources":["../src/ast-guards.ts"],"names":[],"mappings":"AAAA,OAAO,EAqBH,KAAK,YAAY,EACjB,KAAK,iBAAiB,EACtB,KAAK,cAAc,EACnB,KAAK,gBAAgB,EACrB,KAAK,oBAAoB,EACzB,KAAK,wBAAwB,EAC7B,KAAK,sBAAsB,EAC3B,KAAK,eAAe,EACpB,KAAK,mBAAmB,EACxB,KAAK,UAAU,EACf,KAAK,iBAAiB,EACtB,KAAK,sBAAsB,EAC3B,KAAK,wBAAwB,EAC7B,KAAK,eAAe,EACpB,KAAK,IAAI,EACT,KAAK,aAAa,EAClB,KAAK,cAAc,EACnB,KAAK,WAAW,EAChB,KAAK,aAAa,EAClB,KAAK,mBAAmB,EACxB,KAAK,kBAAkB,EAC1B,MAAM,cAAc,CAAC;AAEtB,eAAO,MAAM,QAAQ,GAAI,OAAO,OAAO,KAAG,KAAK,IAAI,MAAM,CAAC,MAAM,EAAE,OAAO,CACD,CAAC;AAEzE,eAAO,MAAM,MAAM,GAAI,OAAO,OAAO,KAAG,IAAI,GAAG,IAA6D,CAAC;AAE7G,eAAO,MAAM,kBAAkB,GAAI,OAAO,OAAO,KAAG,KAAK,IAAI,YAG5D,CAAC;AAEF,eAAO,MAAM,uBAAuB,GAAI,OAAO,OAAO,KAAG,KAAK,IAAI,iBAGjE,CAAC;AAEF,eAAO,MAAM,oBAAoB,GAAI,OAAO,OAAO,KAAG,KAAK,IAAI,cAG9D,CAAC;AAEF,eAAO,MAAM,sBAAsB,GAAI,OAAO,OAAO,KAAG,KAAK,IAAI,gBAGhE,CAAC;AAEF,eAAO,MAAM,0BAA0B,GAAI,OAAO,OAAO,KAAG,KAAK,IAAI,oBAGpE,CAAC;AAEF,eAAO,MAAM,8BAA8B,GAAI,OAAO,OAAO,KAAG,KAAK,IAAI,wBAGxE,CAAC;AAEF,eAAO,MAAM,4BAA4B,GAAI,OAAO,OAAO,KAAG,KAAK,IAAI,sBAGtE,CAAC;AAEF,eAAO,MAAM,qBAAqB,GAAI,OAAO,OAAO,KAAG,KAAK,IAAI,eAG/D,CAAC;AAEF,eAAO,MAAM,yBAAyB,GAAI,OAAO,OAAO,KAAG,KAAK,IAAI,mBAGnE,CAAC;AAEF,eAAO,MAAM,gBAAgB,GAAI,OAAO,OAAO,KAAG,KAAK,IAAI,UAG1D,CAAC;AAEF,eAAO,MAAM,uBAAuB,GAAI,OAAO,OAAO,KAAG,KAAK,IAAI,iBAGjE,CAAC;AAEF,eAAO,MAAM,4BAA4B,GAAI,OAAO,OAAO,KAAG,KAAK,IAAI,sBAGtE,CAAC;AAEF,eAAO,MAAM,8BAA8B,GAAI,OAAO,OAAO,KAAG,KAAK,IAAI,wBAGxE,CAAC;AAEF,eAAO,MAAM,qBAAqB,GAAI,OAAO,OAAO,KAAG,KAAK,IAAI,eAG/D,CAAC;AAEF,eAAO,MAAM,mBAAmB,GAAI,OAAO,OAAO,KAAG,KAAK,IAAI,aAG7D,CAAC;AAEF,eAAO,MAAM,oBAAoB,GAAI,OAAO,OAAO,KAAG,KAAK,IAAI,cAG9D,CAAC;AAEF,eAAO,MAAM,iBAAiB,GAAI,OAAO,OAAO,KAAG,KAAK,IAAI,WAG3D,CAAC;AAEF,eAAO,MAAM,mBAAmB,GAAI,OAAO,OAAO,KAAG,KAAK,IAAI,aAG7D,CAAC;AAEF,eAAO,MAAM,yBAAyB,GAAI,OAAO,OAAO,KAAG,KAAK,IAAI,mBAGnE,CAAC;AAEF,eAAO,MAAM,wBAAwB,GAAI,OAAO,OAAO,KAAG,KAAK,IAAI,kBAGlE,CAAC"}
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import { isArrayPattern, isAssignmentPattern, isCallExpression, isClassDeclaration, isExportAllDeclaration, isExportDefaultDeclaration, isExportNamedDeclaration, isExportSpecifier, isFunctionDeclaration, isIdentifier, isImportDeclaration, isImportDefaultSpecifier, isImportNamespaceSpecifier, isImportSpecifier, isObjectPattern, isObjectProperty, isRestElement, isStringLiteral, isVariableDeclaration, isVariableDeclarator, } from '@babel/types';
|
|
2
|
+
export const isRecord = (value) => value !== null && typeof value === 'object' && !Array.isArray(value);
|
|
3
|
+
export const asNode = (value) => (isRecord(value) ? value : null);
|
|
4
|
+
export const isArrayPatternNode = (value) => {
|
|
5
|
+
const node = asNode(value);
|
|
6
|
+
return node !== null && isArrayPattern(node);
|
|
7
|
+
};
|
|
8
|
+
export const isAssignmentPatternNode = (value) => {
|
|
9
|
+
const node = asNode(value);
|
|
10
|
+
return node !== null && isAssignmentPattern(node);
|
|
11
|
+
};
|
|
12
|
+
export const isCallExpressionNode = (value) => {
|
|
13
|
+
const node = asNode(value);
|
|
14
|
+
return node !== null && isCallExpression(node);
|
|
15
|
+
};
|
|
16
|
+
export const isClassDeclarationNode = (value) => {
|
|
17
|
+
const node = asNode(value);
|
|
18
|
+
return node !== null && isClassDeclaration(node);
|
|
19
|
+
};
|
|
20
|
+
export const isExportAllDeclarationNode = (value) => {
|
|
21
|
+
const node = asNode(value);
|
|
22
|
+
return node !== null && isExportAllDeclaration(node);
|
|
23
|
+
};
|
|
24
|
+
export const isExportDefaultDeclarationNode = (value) => {
|
|
25
|
+
const node = asNode(value);
|
|
26
|
+
return node !== null && isExportDefaultDeclaration(node);
|
|
27
|
+
};
|
|
28
|
+
export const isExportNamedDeclarationNode = (value) => {
|
|
29
|
+
const node = asNode(value);
|
|
30
|
+
return node !== null && isExportNamedDeclaration(node);
|
|
31
|
+
};
|
|
32
|
+
export const isExportSpecifierNode = (value) => {
|
|
33
|
+
const node = asNode(value);
|
|
34
|
+
return node !== null && isExportSpecifier(node);
|
|
35
|
+
};
|
|
36
|
+
export const isFunctionDeclarationNode = (value) => {
|
|
37
|
+
const node = asNode(value);
|
|
38
|
+
return node !== null && isFunctionDeclaration(node);
|
|
39
|
+
};
|
|
40
|
+
export const isIdentifierNode = (value) => {
|
|
41
|
+
const node = asNode(value);
|
|
42
|
+
return node !== null && isIdentifier(node);
|
|
43
|
+
};
|
|
44
|
+
export const isImportDeclarationNode = (value) => {
|
|
45
|
+
const node = asNode(value);
|
|
46
|
+
return node !== null && isImportDeclaration(node);
|
|
47
|
+
};
|
|
48
|
+
export const isImportDefaultSpecifierNode = (value) => {
|
|
49
|
+
const node = asNode(value);
|
|
50
|
+
return node !== null && isImportDefaultSpecifier(node);
|
|
51
|
+
};
|
|
52
|
+
export const isImportNamespaceSpecifierNode = (value) => {
|
|
53
|
+
const node = asNode(value);
|
|
54
|
+
return node !== null && isImportNamespaceSpecifier(node);
|
|
55
|
+
};
|
|
56
|
+
export const isImportSpecifierNode = (value) => {
|
|
57
|
+
const node = asNode(value);
|
|
58
|
+
return node !== null && isImportSpecifier(node);
|
|
59
|
+
};
|
|
60
|
+
export const isObjectPatternNode = (value) => {
|
|
61
|
+
const node = asNode(value);
|
|
62
|
+
return node !== null && isObjectPattern(node);
|
|
63
|
+
};
|
|
64
|
+
export const isObjectPropertyNode = (value) => {
|
|
65
|
+
const node = asNode(value);
|
|
66
|
+
return node !== null && isObjectProperty(node);
|
|
67
|
+
};
|
|
68
|
+
export const isRestElementNode = (value) => {
|
|
69
|
+
const node = asNode(value);
|
|
70
|
+
return node !== null && isRestElement(node);
|
|
71
|
+
};
|
|
72
|
+
export const isStringLiteralNode = (value) => {
|
|
73
|
+
const node = asNode(value);
|
|
74
|
+
return node !== null && isStringLiteral(node);
|
|
75
|
+
};
|
|
76
|
+
export const isVariableDeclarationNode = (value) => {
|
|
77
|
+
const node = asNode(value);
|
|
78
|
+
return node !== null && isVariableDeclaration(node);
|
|
79
|
+
};
|
|
80
|
+
export const isVariableDeclaratorNode = (value) => {
|
|
81
|
+
const node = asNode(value);
|
|
82
|
+
return node !== null && isVariableDeclarator(node);
|
|
83
|
+
};
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import type { ChildProcess } from 'node:child_process';
|
|
2
|
+
import type { EquivMode } from './types.ts';
|
|
3
|
+
export type CandidateBootSmoke = {
|
|
4
|
+
readonly mode: EquivMode;
|
|
5
|
+
};
|
|
6
|
+
export type CandidateBootSmokeResult = (CandidateBootSmoke & {
|
|
7
|
+
readonly diagnostics?: never;
|
|
8
|
+
readonly ok: true;
|
|
9
|
+
}) | (CandidateBootSmoke & {
|
|
10
|
+
readonly diagnostics: string;
|
|
11
|
+
readonly ok: false;
|
|
12
|
+
});
|
|
13
|
+
type CandidateBootRuntime = {
|
|
14
|
+
readonly resolveOpenPort: () => Promise<number>;
|
|
15
|
+
readonly spawnModeProcess: (options: {
|
|
16
|
+
readonly mode: EquivMode;
|
|
17
|
+
readonly port: number;
|
|
18
|
+
readonly workspaceRoot: string;
|
|
19
|
+
}) => ChildProcess;
|
|
20
|
+
};
|
|
21
|
+
export declare const smokeCandidateBootWithDependencies: ({ mode, workspaceRoot, }: {
|
|
22
|
+
readonly mode?: EquivMode;
|
|
23
|
+
readonly workspaceRoot: string;
|
|
24
|
+
}, dependencies?: Partial<CandidateBootRuntime>) => Promise<CandidateBootSmokeResult>;
|
|
25
|
+
export declare const smokeCandidateBoot: ({ mode, workspaceRoot, }: {
|
|
26
|
+
readonly mode?: EquivMode;
|
|
27
|
+
readonly workspaceRoot: string;
|
|
28
|
+
}) => Promise<CandidateBootSmokeResult>;
|
|
29
|
+
export {};
|
|
30
|
+
//# sourceMappingURL=candidate-boot.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"candidate-boot.d.ts","sourceRoot":"","sources":["../src/candidate-boot.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAIvD,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAY5C,MAAM,MAAM,kBAAkB,GAAG;IAC7B,QAAQ,CAAC,IAAI,EAAE,SAAS,CAAC;CAC5B,CAAC;AAEF,MAAM,MAAM,wBAAwB,GAC9B,CAAC,kBAAkB,GAAG;IAAE,QAAQ,CAAC,WAAW,CAAC,EAAE,KAAK,CAAC;IAAC,QAAQ,CAAC,EAAE,EAAE,IAAI,CAAA;CAAE,CAAC,GAC1E,CAAC,kBAAkB,GAAG;IAAE,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,EAAE,EAAE,KAAK,CAAA;CAAE,CAAC,CAAC;AAElF,KAAK,oBAAoB,GAAG;IACxB,QAAQ,CAAC,eAAe,EAAE,MAAM,OAAO,CAAC,MAAM,CAAC,CAAC;IAChD,QAAQ,CAAC,gBAAgB,EAAE,CAAC,OAAO,EAAE;QACjC,QAAQ,CAAC,IAAI,EAAE,SAAS,CAAC;QACzB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;QACtB,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;KAClC,KAAK,YAAY,CAAC;CACtB,CAAC;AAqRF,eAAO,MAAM,kCAAkC,GAC3C,0BAGG;IACC,QAAQ,CAAC,IAAI,CAAC,EAAE,SAAS,CAAC;IAC1B,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;CAClC,EACD,eAAc,OAAO,CAAC,oBAAoB,CAAM,KACjD,OAAO,CAAC,wBAAwB,CAiDlC,CAAC;AAEF,eAAO,MAAM,kBAAkB,GAAU,0BAGtC;IACC,QAAQ,CAAC,IAAI,CAAC,EAAE,SAAS,CAAC;IAC1B,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;CAClC,KAAG,OAAO,CAAC,wBAAwB,CAO/B,CAAC"}
|
|
@@ -0,0 +1,262 @@
|
|
|
1
|
+
import { spawn } from 'node:child_process';
|
|
2
|
+
import { createServer } from 'node:net';
|
|
3
|
+
import { captureChildText, readPositiveIntegerEnv } from "./shared.js";
|
|
4
|
+
import { DEFAULT_EQUIV_MODE } from "./workspace.js";
|
|
5
|
+
const BUN_EXECUTABLE = process.env.BUN_EXECUTABLE ?? 'bun';
|
|
6
|
+
const BOOT_TIMEOUT_MS = readPositiveIntegerEnv('USHMAN_EQUIV_BOOT_TIMEOUT_MS', 15_000);
|
|
7
|
+
const BUILD_TIMEOUT_MS = readPositiveIntegerEnv('USHMAN_EQUIV_BUILD_TIMEOUT_MS', 120_000);
|
|
8
|
+
const BOOT_PORT_RETRY_LIMIT = readPositiveIntegerEnv('USHMAN_EQUIV_BOOT_PORT_RETRY_LIMIT', 3);
|
|
9
|
+
const MAX_LOG_BYTES = readPositiveIntegerEnv('USHMAN_EQUIV_MAX_LOG_BYTES', 32 * 1024);
|
|
10
|
+
const READY_POLL_INTERVAL_MS = readPositiveIntegerEnv('USHMAN_EQUIV_READY_POLL_INTERVAL_MS', 150);
|
|
11
|
+
const ADDRESS_IN_USE_CODE = 'EADDRINUSE';
|
|
12
|
+
const sleep = (delayMs) => new Promise((resolve) => {
|
|
13
|
+
setTimeout(resolve, delayMs);
|
|
14
|
+
});
|
|
15
|
+
const waitForExit = async (child, timeoutMs) => {
|
|
16
|
+
if (child.exitCode !== null) {
|
|
17
|
+
return true;
|
|
18
|
+
}
|
|
19
|
+
return await new Promise((resolve) => {
|
|
20
|
+
const onExit = () => {
|
|
21
|
+
clearTimeout(timeoutId);
|
|
22
|
+
resolve(true);
|
|
23
|
+
};
|
|
24
|
+
const timeoutId = setTimeout(() => {
|
|
25
|
+
child.removeListener('exit', onExit);
|
|
26
|
+
resolve(false);
|
|
27
|
+
}, timeoutMs);
|
|
28
|
+
child.once('exit', onExit);
|
|
29
|
+
});
|
|
30
|
+
};
|
|
31
|
+
const resolveOpenPort = async () => new Promise((resolve, reject) => {
|
|
32
|
+
const server = createServer();
|
|
33
|
+
server.once('error', reject);
|
|
34
|
+
server.listen(0, '127.0.0.1', () => {
|
|
35
|
+
const address = server.address();
|
|
36
|
+
if (!address || typeof address === 'string') {
|
|
37
|
+
reject(new Error('Failed to resolve an open port.'));
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
server.close((error) => {
|
|
41
|
+
if (error) {
|
|
42
|
+
reject(error);
|
|
43
|
+
return;
|
|
44
|
+
}
|
|
45
|
+
resolve(address.port);
|
|
46
|
+
});
|
|
47
|
+
});
|
|
48
|
+
});
|
|
49
|
+
const closeChild = async (child) => {
|
|
50
|
+
if (child.exitCode !== null) {
|
|
51
|
+
return;
|
|
52
|
+
}
|
|
53
|
+
child.kill('SIGTERM');
|
|
54
|
+
if (await waitForExit(child, 2_000)) {
|
|
55
|
+
return;
|
|
56
|
+
}
|
|
57
|
+
child.kill('SIGKILL');
|
|
58
|
+
await waitForExit(child, 2_000);
|
|
59
|
+
};
|
|
60
|
+
const formatDiagnostics = ({ context, stderr, stdout, }) => [context, stderr ? `stderr:\n${stderr}` : null, stdout ? `stdout:\n${stdout}` : null]
|
|
61
|
+
.filter((line) => Boolean(line))
|
|
62
|
+
.join('\n\n');
|
|
63
|
+
const formatRetryExhaustedDiagnostics = ({ attempts, diagnostics, }) => [
|
|
64
|
+
`Candidate boot hit ${ADDRESS_IN_USE_CODE} ${attempts} time${attempts === 1 ? '' : 's'} while retrying strict port allocation.`,
|
|
65
|
+
diagnostics,
|
|
66
|
+
].join('\n\n');
|
|
67
|
+
const runBuild = async (workspaceRoot) => {
|
|
68
|
+
const child = spawn(BUN_EXECUTABLE, ['run', 'build'], {
|
|
69
|
+
cwd: workspaceRoot,
|
|
70
|
+
stdio: ['ignore', 'pipe', 'pipe'],
|
|
71
|
+
});
|
|
72
|
+
const readStdout = captureChildText({
|
|
73
|
+
maxBytes: MAX_LOG_BYTES,
|
|
74
|
+
stream: child.stdout,
|
|
75
|
+
});
|
|
76
|
+
const readStderr = captureChildText({
|
|
77
|
+
maxBytes: MAX_LOG_BYTES,
|
|
78
|
+
stream: child.stderr,
|
|
79
|
+
});
|
|
80
|
+
const buildState = await Promise.race([
|
|
81
|
+
new Promise((resolve) => {
|
|
82
|
+
child.once('error', (error) => resolve({
|
|
83
|
+
error,
|
|
84
|
+
finished: false,
|
|
85
|
+
}));
|
|
86
|
+
}),
|
|
87
|
+
waitForExit(child, BUILD_TIMEOUT_MS).then((finished) => ({
|
|
88
|
+
error: null,
|
|
89
|
+
finished,
|
|
90
|
+
})),
|
|
91
|
+
]);
|
|
92
|
+
if (buildState.error) {
|
|
93
|
+
return formatDiagnostics({
|
|
94
|
+
context: `bun run build failed to start: ${buildState.error.message}`,
|
|
95
|
+
stderr: readStderr(),
|
|
96
|
+
stdout: readStdout(),
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
if (!buildState.finished) {
|
|
100
|
+
await closeChild(child);
|
|
101
|
+
return formatDiagnostics({
|
|
102
|
+
context: `bun run build timed out after ${BUILD_TIMEOUT_MS}ms.`,
|
|
103
|
+
stderr: readStderr(),
|
|
104
|
+
stdout: readStdout(),
|
|
105
|
+
});
|
|
106
|
+
}
|
|
107
|
+
if (child.exitCode !== 0) {
|
|
108
|
+
return formatDiagnostics({
|
|
109
|
+
context: `bun run build exited with code ${child.exitCode ?? 'unknown'}.`,
|
|
110
|
+
stderr: readStderr(),
|
|
111
|
+
stdout: readStdout(),
|
|
112
|
+
});
|
|
113
|
+
}
|
|
114
|
+
return null;
|
|
115
|
+
};
|
|
116
|
+
const spawnModeProcess = ({ mode, port, workspaceRoot, }) => spawn(BUN_EXECUTABLE, [
|
|
117
|
+
'run',
|
|
118
|
+
mode === 'preview' ? 'preview' : 'dev',
|
|
119
|
+
'--',
|
|
120
|
+
'--host',
|
|
121
|
+
'127.0.0.1',
|
|
122
|
+
'--port',
|
|
123
|
+
String(port),
|
|
124
|
+
'--strictPort',
|
|
125
|
+
], {
|
|
126
|
+
cwd: workspaceRoot,
|
|
127
|
+
env: {
|
|
128
|
+
...process.env,
|
|
129
|
+
NODE_ENV: mode === 'preview' ? 'production' : 'development',
|
|
130
|
+
},
|
|
131
|
+
stdio: ['ignore', 'pipe', 'pipe'],
|
|
132
|
+
});
|
|
133
|
+
const shouldRetryPortContention = (diagnostics) => diagnostics.includes(ADDRESS_IN_USE_CODE);
|
|
134
|
+
const waitForServerReady = async ({ child, onError, processLabel, stderr, stdout, url, }) => {
|
|
135
|
+
const startedAt = Date.now();
|
|
136
|
+
while (Date.now() - startedAt < BOOT_TIMEOUT_MS) {
|
|
137
|
+
const childError = onError();
|
|
138
|
+
if (childError) {
|
|
139
|
+
throw new Error(formatDiagnostics({
|
|
140
|
+
context: `${processLabel} failed to start: ${childError.message}`,
|
|
141
|
+
stderr: stderr(),
|
|
142
|
+
stdout: stdout(),
|
|
143
|
+
}));
|
|
144
|
+
}
|
|
145
|
+
if (child.exitCode !== null) {
|
|
146
|
+
throw new Error(formatDiagnostics({
|
|
147
|
+
context: `${processLabel} exited with code ${child.exitCode}.`,
|
|
148
|
+
stderr: stderr(),
|
|
149
|
+
stdout: stdout(),
|
|
150
|
+
}));
|
|
151
|
+
}
|
|
152
|
+
try {
|
|
153
|
+
const response = await fetch(url);
|
|
154
|
+
if (response.ok || response.status === 404) {
|
|
155
|
+
return;
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
catch { }
|
|
159
|
+
await sleep(READY_POLL_INTERVAL_MS);
|
|
160
|
+
}
|
|
161
|
+
throw new Error(formatDiagnostics({
|
|
162
|
+
context: `Timed out waiting for ${url}.`,
|
|
163
|
+
stderr: stderr(),
|
|
164
|
+
stdout: stdout(),
|
|
165
|
+
}));
|
|
166
|
+
};
|
|
167
|
+
const attemptCandidateBoot = async ({ mode = DEFAULT_EQUIV_MODE, runtime, workspaceRoot, }) => {
|
|
168
|
+
const port = await runtime.resolveOpenPort();
|
|
169
|
+
const child = runtime.spawnModeProcess({
|
|
170
|
+
mode,
|
|
171
|
+
port,
|
|
172
|
+
workspaceRoot,
|
|
173
|
+
});
|
|
174
|
+
const readStdout = captureChildText({
|
|
175
|
+
maxBytes: MAX_LOG_BYTES,
|
|
176
|
+
stream: child.stdout,
|
|
177
|
+
});
|
|
178
|
+
const readStderr = captureChildText({
|
|
179
|
+
maxBytes: MAX_LOG_BYTES,
|
|
180
|
+
stream: child.stderr,
|
|
181
|
+
});
|
|
182
|
+
let childError = null;
|
|
183
|
+
child.once('error', (error) => {
|
|
184
|
+
childError = error;
|
|
185
|
+
});
|
|
186
|
+
const url = `http://127.0.0.1:${port}/`;
|
|
187
|
+
const processLabel = mode === 'preview' ? 'Preview process' : 'Dev process';
|
|
188
|
+
try {
|
|
189
|
+
await waitForServerReady({
|
|
190
|
+
child,
|
|
191
|
+
onError: () => childError,
|
|
192
|
+
processLabel,
|
|
193
|
+
stderr: readStderr,
|
|
194
|
+
stdout: readStdout,
|
|
195
|
+
url,
|
|
196
|
+
});
|
|
197
|
+
return {
|
|
198
|
+
ok: true,
|
|
199
|
+
};
|
|
200
|
+
}
|
|
201
|
+
catch (error) {
|
|
202
|
+
return {
|
|
203
|
+
diagnostics: error instanceof Error ? error.message : String(error),
|
|
204
|
+
ok: false,
|
|
205
|
+
};
|
|
206
|
+
}
|
|
207
|
+
finally {
|
|
208
|
+
await closeChild(child);
|
|
209
|
+
}
|
|
210
|
+
};
|
|
211
|
+
// Internal test seam for simulating boot races without widening the public package API.
|
|
212
|
+
export const smokeCandidateBootWithDependencies = async ({ mode = DEFAULT_EQUIV_MODE, workspaceRoot, }, dependencies = {}) => {
|
|
213
|
+
if (mode === 'preview') {
|
|
214
|
+
const buildFailure = await runBuild(workspaceRoot);
|
|
215
|
+
if (buildFailure) {
|
|
216
|
+
return {
|
|
217
|
+
diagnostics: buildFailure,
|
|
218
|
+
mode,
|
|
219
|
+
ok: false,
|
|
220
|
+
};
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
const runtime = {
|
|
224
|
+
resolveOpenPort: dependencies.resolveOpenPort ?? resolveOpenPort,
|
|
225
|
+
spawnModeProcess: dependencies.spawnModeProcess ?? spawnModeProcess,
|
|
226
|
+
};
|
|
227
|
+
for (let attempt = 1; attempt <= BOOT_PORT_RETRY_LIMIT; attempt += 1) {
|
|
228
|
+
const attemptResult = await attemptCandidateBoot({
|
|
229
|
+
mode,
|
|
230
|
+
runtime,
|
|
231
|
+
workspaceRoot,
|
|
232
|
+
});
|
|
233
|
+
if (attemptResult.ok) {
|
|
234
|
+
return {
|
|
235
|
+
mode,
|
|
236
|
+
ok: true,
|
|
237
|
+
};
|
|
238
|
+
}
|
|
239
|
+
if (!shouldRetryPortContention(attemptResult.diagnostics)) {
|
|
240
|
+
return {
|
|
241
|
+
diagnostics: attemptResult.diagnostics,
|
|
242
|
+
mode,
|
|
243
|
+
ok: false,
|
|
244
|
+
};
|
|
245
|
+
}
|
|
246
|
+
if (attempt === BOOT_PORT_RETRY_LIMIT) {
|
|
247
|
+
return {
|
|
248
|
+
diagnostics: formatRetryExhaustedDiagnostics({
|
|
249
|
+
attempts: attempt,
|
|
250
|
+
diagnostics: attemptResult.diagnostics,
|
|
251
|
+
}),
|
|
252
|
+
mode,
|
|
253
|
+
ok: false,
|
|
254
|
+
};
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
throw new Error('Invariant: candidate boot retry loop exited without returning.');
|
|
258
|
+
};
|
|
259
|
+
export const smokeCandidateBoot = async ({ mode = DEFAULT_EQUIV_MODE, workspaceRoot, }) => smokeCandidateBootWithDependencies({
|
|
260
|
+
mode,
|
|
261
|
+
workspaceRoot,
|
|
262
|
+
}, {});
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
type CanonicalizeOptions = {
|
|
2
|
+
readonly exemptFields?: readonly string[];
|
|
3
|
+
readonly maxBytes?: number;
|
|
4
|
+
readonly precision?: number;
|
|
5
|
+
};
|
|
6
|
+
/**
|
|
7
|
+
* Converts replay values into the stable characterize-compatible wire shape.
|
|
8
|
+
* `exemptFields` uses dot-notation paths and `maxBytes` aborts once the
|
|
9
|
+
* canonicalized payload would exceed the configured UTF-8 byte budget.
|
|
10
|
+
*/
|
|
11
|
+
export declare const canonicalize: (value: unknown, options?: CanonicalizeOptions) => unknown;
|
|
12
|
+
/**
|
|
13
|
+
* Hydrates a canonicalized replay payload back into runtime values for replay.
|
|
14
|
+
*/
|
|
15
|
+
export declare const hydrateCanonicalized: (value: unknown, state?: {
|
|
16
|
+
refs: Map<number, object>;
|
|
17
|
+
}) => unknown;
|
|
18
|
+
export {};
|
|
19
|
+
//# sourceMappingURL=canonicalize.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"canonicalize.d.ts","sourceRoot":"","sources":["../src/canonicalize.ts"],"names":[],"mappings":"AASA,KAAK,mBAAmB,GAAG;IACvB,QAAQ,CAAC,YAAY,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IAC1C,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;CAC/B,CAAC;AAsXF;;;;GAIG;AACH,eAAO,MAAM,YAAY,GAAI,OAAO,OAAO,EAAE,UAAS,mBAAwB,KAAG,OAShF,CAAC;AAwNF;;GAEG;AACH,eAAO,MAAM,oBAAoB,GAAI,OAAO,OAAO,EAAE;;CAA8B,KAAG,OAsBrF,CAAC"}
|