easyeda-mcp-pro 1.8.0 → 1.9.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 +38 -74
- package/dist/circuit/circuit-ir.d.ts +6 -6
- package/dist/config/profiles.js +6 -6
- package/dist/config/profiles.js.map +1 -1
- package/dist/config/version.d.ts +1 -1
- package/dist/config/version.js +1 -1
- package/dist/design-rules/index.d.ts +2 -0
- package/dist/design-rules/index.js +1 -0
- package/dist/design-rules/index.js.map +1 -1
- package/dist/design-rules/isolation-emc-review.d.ts +73 -0
- package/dist/design-rules/isolation-emc-review.js +170 -0
- package/dist/design-rules/isolation-emc-review.js.map +1 -0
- package/dist/design-rules/trace-current-review.d.ts +41 -0
- package/dist/design-rules/trace-current-review.js +144 -0
- package/dist/design-rules/trace-current-review.js.map +1 -0
- package/dist/design-rules/trace-width.d.ts +26 -4
- package/dist/design-rules/trace-width.js +46 -18
- package/dist/design-rules/trace-width.js.map +1 -1
- package/dist/engineering-review/report.d.ts +119 -0
- package/dist/engineering-review/report.js +297 -0
- package/dist/engineering-review/report.js.map +1 -0
- package/dist/live/easyeda-smoke.js +9 -1
- package/dist/live/easyeda-smoke.js.map +1 -1
- package/dist/net-validation/datasheet-review.d.ts +69 -0
- package/dist/net-validation/datasheet-review.js +146 -0
- package/dist/net-validation/datasheet-review.js.map +1 -0
- package/dist/package-build-manifest.json +218 -68
- package/dist/pcb-constraints/manufacturing-profile-review.d.ts +51 -0
- package/dist/pcb-constraints/manufacturing-profile-review.js +218 -0
- package/dist/pcb-constraints/manufacturing-profile-review.js.map +1 -0
- package/dist/pcb-constraints/types.d.ts +8 -2
- package/dist/pcb-constraints/validation.js +157 -36
- package/dist/pcb-constraints/validation.js.map +1 -1
- package/dist/pcb-layout/differential-pair.d.ts +66 -0
- package/dist/pcb-layout/differential-pair.js +275 -0
- package/dist/pcb-layout/differential-pair.js.map +1 -0
- package/dist/pcb-layout/placement-evidence.d.ts +53 -0
- package/dist/pcb-layout/placement-evidence.js +134 -0
- package/dist/pcb-layout/placement-evidence.js.map +1 -0
- package/dist/pcb-layout/planner.js +48 -20
- package/dist/pcb-layout/planner.js.map +1 -1
- package/dist/pcb-layout/route-review.d.ts +70 -0
- package/dist/pcb-layout/route-review.js +182 -0
- package/dist/pcb-layout/route-review.js.map +1 -0
- package/dist/server/resources-prompts.js +4 -1
- package/dist/server/resources-prompts.js.map +1 -1
- package/dist/tools/L1_board.js +157 -41
- package/dist/tools/L1_board.js.map +1 -1
- package/dist/tools/L1_design_rules.js +194 -9
- package/dist/tools/L1_design_rules.js.map +1 -1
- package/dist/tools/L1_drc_erc.js +109 -10
- package/dist/tools/L1_drc_erc.js.map +1 -1
- package/dist/tools/L1_engineering_review.d.ts +6 -0
- package/dist/tools/L1_engineering_review.js +46 -0
- package/dist/tools/L1_engineering_review.js.map +1 -0
- package/dist/tools/L1_export.js +83 -1
- package/dist/tools/L1_export.js.map +1 -1
- package/dist/tools/L1_pcb_constraints.d.ts +6 -0
- package/dist/tools/L1_pcb_constraints.js +177 -37
- package/dist/tools/L1_pcb_constraints.js.map +1 -1
- package/dist/tools/L1_pcb_route_review.d.ts +9 -0
- package/dist/tools/L1_pcb_route_review.js +423 -0
- package/dist/tools/L1_pcb_route_review.js.map +1 -0
- package/dist/tools/L2_schematic_layout.d.ts +2 -2
- package/dist/tools/register.js +4 -0
- package/dist/tools/register.js.map +1 -1
- package/easyeda-bridge-extension.checksums.json +8 -8
- package/easyeda-bridge-extension.eext +0 -0
- package/package.json +1 -1
- package/skills/design-validation/SKILL.md +23 -0
- package/skills/design-validation/references/engineering-review.md +116 -0
- package/skills/easyeda-component-review/SKILL.md +11 -0
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
import { calculateTraceWidth } from './trace-width.js';
|
|
2
|
+
const THERMAL_LIMITATIONS = [
|
|
3
|
+
'IPC-2221 empirical estimate, not an IPC-2152 calculation or validated thermal limit.',
|
|
4
|
+
'Copper weight is nominal: finished minimum plated copper thickness can differ.',
|
|
5
|
+
'Actual temperature depends on board material/thickness, copper planes, adjacent heat, airflow and duty cycle.',
|
|
6
|
+
'Via transitions, plane connections and local neck-downs require separate review.',
|
|
7
|
+
'Fabricator trace/space capability and electrical thermal adequacy are different checks.',
|
|
8
|
+
];
|
|
9
|
+
const INPUT_UNITS = {
|
|
10
|
+
currentA: 'A',
|
|
11
|
+
temperatureRiseC: '°C',
|
|
12
|
+
copperWeightOz: 'oz/ft²',
|
|
13
|
+
traceWidthMm: 'mm',
|
|
14
|
+
neckDownWidthMm: 'mm',
|
|
15
|
+
manufacturerMinWidthMm: 'mm',
|
|
16
|
+
};
|
|
17
|
+
function hasCompleteModelInput(input) {
|
|
18
|
+
return (input.currentA !== undefined &&
|
|
19
|
+
input.temperatureRiseC !== undefined &&
|
|
20
|
+
input.layer !== undefined &&
|
|
21
|
+
input.copperWeightOz !== undefined);
|
|
22
|
+
}
|
|
23
|
+
function thermalFinding(estimate, narrowest) {
|
|
24
|
+
if (estimate === undefined) {
|
|
25
|
+
return {
|
|
26
|
+
id: 'THERMAL_ESTIMATE',
|
|
27
|
+
status: 'UNKNOWN',
|
|
28
|
+
message: 'Current, temperature rise, layer and copper weight must all be supplied.',
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
if (narrowest === undefined) {
|
|
32
|
+
return {
|
|
33
|
+
id: 'THERMAL_ESTIMATE',
|
|
34
|
+
status: 'UNKNOWN',
|
|
35
|
+
message: 'Only a reference width was calculated; actual routed width was not supplied.',
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
if (narrowest < estimate) {
|
|
39
|
+
return {
|
|
40
|
+
id: 'THERMAL_ESTIMATE',
|
|
41
|
+
status: 'WARNING',
|
|
42
|
+
message: 'Supplied width is below the IPC-2221 empirical estimate; review current path and thermal design.',
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
return {
|
|
46
|
+
id: 'THERMAL_ESTIMATE',
|
|
47
|
+
status: 'UNKNOWN',
|
|
48
|
+
message: 'Supplied width meets the empirical reference, but thermal adequacy is not verified.',
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
function fabricationFinding(input) {
|
|
52
|
+
const { traceWidthMm, neckDownWidthMm, manufacturerMinWidthMm } = input;
|
|
53
|
+
const narrowest = traceWidthMm === undefined
|
|
54
|
+
? undefined
|
|
55
|
+
: Math.min(traceWidthMm, neckDownWidthMm ?? traceWidthMm);
|
|
56
|
+
if (manufacturerMinWidthMm === undefined || narrowest === undefined) {
|
|
57
|
+
return {
|
|
58
|
+
id: 'FABRICATION_WIDTH',
|
|
59
|
+
status: 'UNKNOWN',
|
|
60
|
+
message: 'No verified manufacturer minimum or complete routed geometry is available.',
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
if (narrowest < manufacturerMinWidthMm) {
|
|
64
|
+
return {
|
|
65
|
+
id: 'FABRICATION_WIDTH',
|
|
66
|
+
status: 'WARNING',
|
|
67
|
+
message: 'Supplied routed width is below the supplied fabricator trace-width minimum.',
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
return {
|
|
71
|
+
id: 'FABRICATION_WIDTH',
|
|
72
|
+
status: 'UNKNOWN',
|
|
73
|
+
message: 'The supplied trace width meets the claimed fabricator minimum, but the process was not verified.',
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
function validateReviewInput(input) {
|
|
77
|
+
for (const field of [
|
|
78
|
+
'currentA',
|
|
79
|
+
'temperatureRiseC',
|
|
80
|
+
'copperWeightOz',
|
|
81
|
+
'traceWidthMm',
|
|
82
|
+
'neckDownWidthMm',
|
|
83
|
+
'manufacturerMinWidthMm',
|
|
84
|
+
]) {
|
|
85
|
+
const value = input[field];
|
|
86
|
+
if (value !== undefined && (!Number.isFinite(value) || value <= 0)) {
|
|
87
|
+
throw new Error(field + ' must be a finite positive number');
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
export function reviewTraceCurrent(input) {
|
|
92
|
+
validateReviewInput(input);
|
|
93
|
+
const estimate = hasCompleteModelInput(input)
|
|
94
|
+
? calculateTraceWidth({
|
|
95
|
+
currentA: input.currentA,
|
|
96
|
+
temperatureRiseC: input.temperatureRiseC,
|
|
97
|
+
layer: input.layer,
|
|
98
|
+
copperWeightOz: input.copperWeightOz,
|
|
99
|
+
}).traceWidthMm
|
|
100
|
+
: undefined;
|
|
101
|
+
// Width of a single straight segment is not proof of the entire current path.
|
|
102
|
+
const narrowest = input.traceWidthMm === undefined
|
|
103
|
+
? undefined
|
|
104
|
+
: Math.min(input.traceWidthMm, input.neckDownWidthMm ?? input.traceWidthMm);
|
|
105
|
+
const findings = [
|
|
106
|
+
thermalFinding(estimate, narrowest),
|
|
107
|
+
fabricationFinding(input),
|
|
108
|
+
{
|
|
109
|
+
id: 'ROUTE_GEOMETRY',
|
|
110
|
+
status: 'UNKNOWN',
|
|
111
|
+
message: input.neckDownWidthMm === undefined
|
|
112
|
+
? 'The narrowest actual segment has not been established by live EasyEDA routing measurements.'
|
|
113
|
+
: 'The supplied neck-down is not verified against the complete native route.',
|
|
114
|
+
},
|
|
115
|
+
{
|
|
116
|
+
id: 'VIA_BOTTLENECK',
|
|
117
|
+
status: 'UNKNOWN',
|
|
118
|
+
message: 'No verified via barrel/transition geometry or parallel-current evidence is available.',
|
|
119
|
+
},
|
|
120
|
+
];
|
|
121
|
+
const limitations = [
|
|
122
|
+
...THERMAL_LIMITATIONS,
|
|
123
|
+
input.environment === 'vacuum'
|
|
124
|
+
? 'Vacuum thermal conditions are not covered by this empirical lookup.'
|
|
125
|
+
: input.environment === 'forced-air'
|
|
126
|
+
? 'Forced airflow was supplied as context but is not modeled by this lookup.'
|
|
127
|
+
: input.environment === undefined || input.environment === 'unspecified'
|
|
128
|
+
? 'Ambient conditions were not specified.'
|
|
129
|
+
: 'Still-air assumptions have not been validated against the actual enclosure.',
|
|
130
|
+
];
|
|
131
|
+
return {
|
|
132
|
+
status: findings.some(({ status }) => status === 'WARNING') ? 'WARNING' : 'UNKNOWN',
|
|
133
|
+
method: 'IPC2221_EMPIRICAL_REFERENCE',
|
|
134
|
+
evidence: 'caller_supplied_unverified',
|
|
135
|
+
source: 'IPC-2221 empirical current-versus-conductor-area equation; see IPC-2152 for thermal design guidance.',
|
|
136
|
+
inputs: input,
|
|
137
|
+
inputUnits: INPUT_UNITS,
|
|
138
|
+
...(estimate === undefined ? {} : { estimatedMinimumWidthMm: estimate }),
|
|
139
|
+
...(narrowest === undefined ? {} : { suppliedNarrowestWidthMm: narrowest }),
|
|
140
|
+
findings,
|
|
141
|
+
limitations,
|
|
142
|
+
};
|
|
143
|
+
}
|
|
144
|
+
//# sourceMappingURL=trace-current-review.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"trace-current-review.js","sourceRoot":"","sources":["../../src/design-rules/trace-current-review.ts"],"names":[],"mappings":"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"}
|
|
@@ -10,16 +10,17 @@
|
|
|
10
10
|
* layer) conductors or 0.024 for internal conductors.
|
|
11
11
|
*
|
|
12
12
|
* This is an estimate, not a certified value: the curve does not model adjacent-trace
|
|
13
|
-
* heating, via/plane proximity, forced airflow, or altitude. IPC-2152
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
* high-reliability current paths.
|
|
13
|
+
* heating, via/plane proximity, forced airflow, or altitude. IPC-2152 provides
|
|
14
|
+
* measured thermal guidance for a wider range of PCB conditions, not universally
|
|
15
|
+
* more conservative estimates. Verify against the actual fabrication process and
|
|
16
|
+
* thermal guidance for safety-critical and high-reliability current paths.
|
|
17
17
|
*
|
|
18
18
|
* @module
|
|
19
19
|
*/
|
|
20
20
|
export type ConductorLayer = 'external' | 'internal';
|
|
21
21
|
/** Standard copper thickness in mils per oz/ft² of copper weight (1oz ≈ 1.378 mils / 34.8 µm). */
|
|
22
22
|
export declare const COPPER_THICKNESS_MILS_PER_OZ = 1.378;
|
|
23
|
+
export declare const TRACE_WIDTH_METHOD: "IPC2221_EMPIRICAL_REFERENCE";
|
|
23
24
|
export interface TraceWidthInput {
|
|
24
25
|
/** Required current-carrying capacity, in amps. */
|
|
25
26
|
currentA: number;
|
|
@@ -38,6 +39,18 @@ export interface TraceWidthResult {
|
|
|
38
39
|
k: number;
|
|
39
40
|
source: string;
|
|
40
41
|
caveat: string;
|
|
42
|
+
method: typeof TRACE_WIDTH_METHOD;
|
|
43
|
+
inputs: TraceWidthInput;
|
|
44
|
+
units: {
|
|
45
|
+
currentA: 'A';
|
|
46
|
+
temperatureRiseC: '°C';
|
|
47
|
+
copperWeightOz: 'oz/ft²';
|
|
48
|
+
traceWidthMm: 'mm';
|
|
49
|
+
traceWidthMils: 'mil';
|
|
50
|
+
copperThicknessMils: 'mil';
|
|
51
|
+
requiredAreaMils2: 'mil²';
|
|
52
|
+
};
|
|
53
|
+
limitations: string[];
|
|
41
54
|
}
|
|
42
55
|
/** Compute the minimum trace width for a given current, temperature rise, layer, and copper weight. */
|
|
43
56
|
export declare function calculateTraceWidth(input: TraceWidthInput): TraceWidthResult;
|
|
@@ -53,6 +66,15 @@ export interface MaxCurrentResult {
|
|
|
53
66
|
maxCurrentA: number;
|
|
54
67
|
source: string;
|
|
55
68
|
caveat: string;
|
|
69
|
+
method: typeof TRACE_WIDTH_METHOD;
|
|
70
|
+
inputs: MaxCurrentInput;
|
|
71
|
+
units: {
|
|
72
|
+
maxCurrentA: 'A';
|
|
73
|
+
traceWidthMils: 'mil';
|
|
74
|
+
temperatureRiseC: '°C';
|
|
75
|
+
copperWeightOz: 'oz/ft²';
|
|
76
|
+
};
|
|
77
|
+
limitations: string[];
|
|
56
78
|
}
|
|
57
79
|
/** Reverse lookup: the maximum current a given trace width can carry under the same model. */
|
|
58
80
|
export declare function calculateMaxCurrent(input: MaxCurrentInput): MaxCurrentResult;
|
|
@@ -10,10 +10,10 @@
|
|
|
10
10
|
* layer) conductors or 0.024 for internal conductors.
|
|
11
11
|
*
|
|
12
12
|
* This is an estimate, not a certified value: the curve does not model adjacent-trace
|
|
13
|
-
* heating, via/plane proximity, forced airflow, or altitude. IPC-2152
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
* high-reliability current paths.
|
|
13
|
+
* heating, via/plane proximity, forced airflow, or altitude. IPC-2152 provides
|
|
14
|
+
* measured thermal guidance for a wider range of PCB conditions, not universally
|
|
15
|
+
* more conservative estimates. Verify against the actual fabrication process and
|
|
16
|
+
* thermal guidance for safety-critical and high-reliability current paths.
|
|
17
17
|
*
|
|
18
18
|
* @module
|
|
19
19
|
*/
|
|
@@ -24,9 +24,15 @@ const K_INTERNAL = 0.024;
|
|
|
24
24
|
/** Standard copper thickness in mils per oz/ft² of copper weight (1oz ≈ 1.378 mils / 34.8 µm). */
|
|
25
25
|
export const COPPER_THICKNESS_MILS_PER_OZ = 1.378;
|
|
26
26
|
const IPC2221_SOURCE = 'IPC-2221 (formerly MIL-STD-275): I = k·ΔT^0.44·A^0.725';
|
|
27
|
+
export const TRACE_WIDTH_METHOD = 'IPC2221_EMPIRICAL_REFERENCE';
|
|
28
|
+
const TRACE_WIDTH_LIMITATIONS = [
|
|
29
|
+
'Empirical IPC-2221 estimate only; not measured PCB or IPC-2152 thermal output.',
|
|
30
|
+
'Nominal copper weight does not verify finished copper thickness.',
|
|
31
|
+
'Copper planes, airflow, via bottlenecks, neck-downs and neighboring heat are not modeled.',
|
|
32
|
+
];
|
|
27
33
|
const IPC2221_CAVEAT = 'Estimate only — does not model adjacent-trace heating, via/plane proximity, or airflow. ' +
|
|
28
|
-
'
|
|
29
|
-
'
|
|
34
|
+
'IPC-2152 includes board stackup, copper planes, environment and thermal coupling; ' +
|
|
35
|
+
'it is not uniformly more conservative. Validate the board and fabrication process.';
|
|
30
36
|
function kFor(layer) {
|
|
31
37
|
return layer === 'external' ? K_EXTERNAL : K_INTERNAL;
|
|
32
38
|
}
|
|
@@ -34,19 +40,22 @@ function round(value, decimals) {
|
|
|
34
40
|
const factor = 10 ** decimals;
|
|
35
41
|
return Math.round(value * factor) / factor;
|
|
36
42
|
}
|
|
43
|
+
function assertPositiveFinite(name, value) {
|
|
44
|
+
if (!Number.isFinite(value) || value <= 0) {
|
|
45
|
+
throw new Error(name + ' must be a finite positive number');
|
|
46
|
+
}
|
|
47
|
+
}
|
|
37
48
|
/** Compute the minimum trace width for a given current, temperature rise, layer, and copper weight. */
|
|
38
49
|
export function calculateTraceWidth(input) {
|
|
39
50
|
const { currentA, temperatureRiseC, layer, copperWeightOz } = input;
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
throw new Error('temperatureRiseC must be a positive number');
|
|
44
|
-
if (!(copperWeightOz > 0))
|
|
45
|
-
throw new Error('copperWeightOz must be a positive number');
|
|
51
|
+
assertPositiveFinite('currentA', currentA);
|
|
52
|
+
assertPositiveFinite('temperatureRiseC', temperatureRiseC);
|
|
53
|
+
assertPositiveFinite('copperWeightOz', copperWeightOz);
|
|
46
54
|
const k = kFor(layer);
|
|
47
55
|
const areaMils2 = Math.pow(currentA / (k * Math.pow(temperatureRiseC, 0.44)), 1 / 0.725);
|
|
48
56
|
const thicknessMils = copperWeightOz * COPPER_THICKNESS_MILS_PER_OZ;
|
|
49
57
|
const widthMils = areaMils2 / thicknessMils;
|
|
58
|
+
assertPositiveFinite('computedTraceWidthMils', widthMils);
|
|
50
59
|
return {
|
|
51
60
|
requiredAreaMils2: round(areaMils2, 3),
|
|
52
61
|
copperThicknessMils: round(thicknessMils, 3),
|
|
@@ -55,25 +64,44 @@ export function calculateTraceWidth(input) {
|
|
|
55
64
|
k,
|
|
56
65
|
source: IPC2221_SOURCE,
|
|
57
66
|
caveat: IPC2221_CAVEAT,
|
|
67
|
+
method: TRACE_WIDTH_METHOD,
|
|
68
|
+
inputs: { ...input },
|
|
69
|
+
units: {
|
|
70
|
+
currentA: 'A',
|
|
71
|
+
temperatureRiseC: '°C',
|
|
72
|
+
copperWeightOz: 'oz/ft²',
|
|
73
|
+
traceWidthMm: 'mm',
|
|
74
|
+
traceWidthMils: 'mil',
|
|
75
|
+
copperThicknessMils: 'mil',
|
|
76
|
+
requiredAreaMils2: 'mil²',
|
|
77
|
+
},
|
|
78
|
+
limitations: [...TRACE_WIDTH_LIMITATIONS],
|
|
58
79
|
};
|
|
59
80
|
}
|
|
60
81
|
/** Reverse lookup: the maximum current a given trace width can carry under the same model. */
|
|
61
82
|
export function calculateMaxCurrent(input) {
|
|
62
83
|
const { traceWidthMils, temperatureRiseC, layer, copperWeightOz } = input;
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
throw new Error('temperatureRiseC must be a positive number');
|
|
67
|
-
if (!(copperWeightOz > 0))
|
|
68
|
-
throw new Error('copperWeightOz must be a positive number');
|
|
84
|
+
assertPositiveFinite('traceWidthMils', traceWidthMils);
|
|
85
|
+
assertPositiveFinite('temperatureRiseC', temperatureRiseC);
|
|
86
|
+
assertPositiveFinite('copperWeightOz', copperWeightOz);
|
|
69
87
|
const k = kFor(layer);
|
|
70
88
|
const thicknessMils = copperWeightOz * COPPER_THICKNESS_MILS_PER_OZ;
|
|
71
89
|
const areaMils2 = traceWidthMils * thicknessMils;
|
|
72
90
|
const maxCurrentA = k * Math.pow(temperatureRiseC, 0.44) * Math.pow(areaMils2, 0.725);
|
|
91
|
+
assertPositiveFinite('computedMaxCurrentA', maxCurrentA);
|
|
73
92
|
return {
|
|
74
93
|
maxCurrentA: round(maxCurrentA, 3),
|
|
75
94
|
source: IPC2221_SOURCE,
|
|
76
95
|
caveat: IPC2221_CAVEAT,
|
|
96
|
+
method: TRACE_WIDTH_METHOD,
|
|
97
|
+
inputs: { ...input },
|
|
98
|
+
units: {
|
|
99
|
+
maxCurrentA: 'A',
|
|
100
|
+
traceWidthMils: 'mil',
|
|
101
|
+
temperatureRiseC: '°C',
|
|
102
|
+
copperWeightOz: 'oz/ft²',
|
|
103
|
+
},
|
|
104
|
+
limitations: [...TRACE_WIDTH_LIMITATIONS],
|
|
77
105
|
};
|
|
78
106
|
}
|
|
79
107
|
//# sourceMappingURL=trace-width.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"trace-width.js","sourceRoot":"","sources":["../../src/design-rules/trace-width.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAIH,yEAAyE;AACzE,MAAM,UAAU,GAAG,KAAK,CAAC;AACzB,2DAA2D;AAC3D,MAAM,UAAU,GAAG,KAAK,CAAC;AAEzB,kGAAkG;AAClG,MAAM,CAAC,MAAM,4BAA4B,GAAG,KAAK,CAAC;AAElD,MAAM,cAAc,GAAG,wDAAwD,CAAC;AAChF,MAAM,cAAc,GAClB,0FAA0F;IAC1F,
|
|
1
|
+
{"version":3,"file":"trace-width.js","sourceRoot":"","sources":["../../src/design-rules/trace-width.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import type { PcbConstraintInput } from '../pcb-constraints/types.js';
|
|
3
|
+
/** Evidence report only; never an electrical or manufacturing sign-off. */
|
|
4
|
+
export declare const reviewStatusSchema: z.ZodEnum<{
|
|
5
|
+
UNKNOWN: "UNKNOWN";
|
|
6
|
+
WARNING: "WARNING";
|
|
7
|
+
PASS: "PASS";
|
|
8
|
+
FAIL: "FAIL";
|
|
9
|
+
NOT_APPLICABLE: "NOT_APPLICABLE";
|
|
10
|
+
}>;
|
|
11
|
+
export declare const engineeringReviewSchema: z.ZodObject<{
|
|
12
|
+
project_id: z.ZodString;
|
|
13
|
+
overall_status: z.ZodEnum<{
|
|
14
|
+
UNKNOWN: "UNKNOWN";
|
|
15
|
+
WARNING: "WARNING";
|
|
16
|
+
PASS: "PASS";
|
|
17
|
+
FAIL: "FAIL";
|
|
18
|
+
NOT_APPLICABLE: "NOT_APPLICABLE";
|
|
19
|
+
}>;
|
|
20
|
+
pcb_context: z.ZodEnum<{
|
|
21
|
+
unverified: "unverified";
|
|
22
|
+
observed: "observed";
|
|
23
|
+
}>;
|
|
24
|
+
runtime: z.ZodObject<{
|
|
25
|
+
bridge_connected: z.ZodBoolean;
|
|
26
|
+
registry_mismatch: z.ZodNullable<z.ZodBoolean>;
|
|
27
|
+
extension_version_mismatch: z.ZodNullable<z.ZodBoolean>;
|
|
28
|
+
}, z.core.$strip>;
|
|
29
|
+
groups: z.ZodObject<{
|
|
30
|
+
blocking: z.ZodArray<z.ZodString>;
|
|
31
|
+
advisories: z.ZodArray<z.ZodString>;
|
|
32
|
+
unknowns: z.ZodArray<z.ZodString>;
|
|
33
|
+
passed: z.ZodArray<z.ZodString>;
|
|
34
|
+
not_applicable: z.ZodArray<z.ZodString>;
|
|
35
|
+
}, z.core.$strip>;
|
|
36
|
+
source_version: z.ZodObject<{
|
|
37
|
+
easyeda: z.ZodNullable<z.ZodString>;
|
|
38
|
+
extension: z.ZodNullable<z.ZodString>;
|
|
39
|
+
}, z.core.$strip>;
|
|
40
|
+
observations: z.ZodArray<z.ZodObject<{
|
|
41
|
+
method: z.ZodString;
|
|
42
|
+
status: z.ZodEnum<{
|
|
43
|
+
unavailable: "unavailable";
|
|
44
|
+
observed: "observed";
|
|
45
|
+
not_run: "not_run";
|
|
46
|
+
}>;
|
|
47
|
+
guidance: z.ZodOptional<z.ZodString>;
|
|
48
|
+
}, z.core.$strip>>;
|
|
49
|
+
input_evidence: z.ZodArray<z.ZodObject<{
|
|
50
|
+
field: z.ZodString;
|
|
51
|
+
source: z.ZodString;
|
|
52
|
+
quality: z.ZodEnum<{
|
|
53
|
+
unavailable: "unavailable";
|
|
54
|
+
measured: "measured";
|
|
55
|
+
supplied: "supplied";
|
|
56
|
+
unverified: "unverified";
|
|
57
|
+
}>;
|
|
58
|
+
unit: z.ZodOptional<z.ZodString>;
|
|
59
|
+
age: z.ZodEnum<{
|
|
60
|
+
unverified: "unverified";
|
|
61
|
+
current_request: "current_request";
|
|
62
|
+
}>;
|
|
63
|
+
value: z.ZodOptional<z.ZodUnion<readonly [z.ZodString, z.ZodNumber, z.ZodBoolean]>>;
|
|
64
|
+
}, z.core.$strip>>;
|
|
65
|
+
findings: z.ZodArray<z.ZodObject<{
|
|
66
|
+
id: z.ZodString;
|
|
67
|
+
rule_id: z.ZodString;
|
|
68
|
+
rule_revision: z.ZodString;
|
|
69
|
+
requirement_source: z.ZodString;
|
|
70
|
+
scope: z.ZodString;
|
|
71
|
+
status: z.ZodEnum<{
|
|
72
|
+
UNKNOWN: "UNKNOWN";
|
|
73
|
+
WARNING: "WARNING";
|
|
74
|
+
PASS: "PASS";
|
|
75
|
+
FAIL: "FAIL";
|
|
76
|
+
NOT_APPLICABLE: "NOT_APPLICABLE";
|
|
77
|
+
}>;
|
|
78
|
+
severity: z.ZodEnum<{
|
|
79
|
+
info: "info";
|
|
80
|
+
error: "error";
|
|
81
|
+
warning: "warning";
|
|
82
|
+
}>;
|
|
83
|
+
message: z.ZodString;
|
|
84
|
+
remediation: z.ZodString;
|
|
85
|
+
confidence: z.ZodEnum<{
|
|
86
|
+
unverified: "unverified";
|
|
87
|
+
observed: "observed";
|
|
88
|
+
derived: "derived";
|
|
89
|
+
}>;
|
|
90
|
+
evidence_fields: z.ZodArray<z.ZodString>;
|
|
91
|
+
affected_entities: z.ZodArray<z.ZodString>;
|
|
92
|
+
}, z.core.$strip>>;
|
|
93
|
+
summary: z.ZodObject<{
|
|
94
|
+
PASS: z.ZodNumber;
|
|
95
|
+
FAIL: z.ZodNumber;
|
|
96
|
+
WARNING: z.ZodNumber;
|
|
97
|
+
UNKNOWN: z.ZodNumber;
|
|
98
|
+
NOT_APPLICABLE: z.ZodNumber;
|
|
99
|
+
}, z.core.$strip>;
|
|
100
|
+
human_review_required: z.ZodArray<z.ZodString>;
|
|
101
|
+
engineering_sign_off: z.ZodLiteral<false>;
|
|
102
|
+
}, z.core.$strip>;
|
|
103
|
+
export type EngineeringReview = z.infer<typeof engineeringReviewSchema>;
|
|
104
|
+
export type ReviewFinding = EngineeringReview['findings'][number];
|
|
105
|
+
export type ReviewObservation = EngineeringReview['observations'][number];
|
|
106
|
+
type ReviewBuildInput = {
|
|
107
|
+
projectId: string;
|
|
108
|
+
boardData: PcbConstraintInput;
|
|
109
|
+
observations: ReviewObservation[];
|
|
110
|
+
bridgeConnected: boolean;
|
|
111
|
+
registryMismatch?: boolean;
|
|
112
|
+
extensionVersionMismatch?: boolean;
|
|
113
|
+
sourceChangedDuringRead?: boolean;
|
|
114
|
+
easyedaVersion?: string;
|
|
115
|
+
extensionVersion?: string;
|
|
116
|
+
evidenceMode: 'native' | 'supplied';
|
|
117
|
+
};
|
|
118
|
+
export declare function buildEngineeringReview(input: ReviewBuildInput): EngineeringReview;
|
|
119
|
+
export {};
|