easyeda-mcp-pro 0.3.2 → 0.5.2

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.
Files changed (173) hide show
  1. package/README.md +369 -52
  2. package/dist/bom-quality/adapter.d.ts +90 -0
  3. package/dist/bom-quality/adapter.js +295 -0
  4. package/dist/bom-quality/adapter.js.map +1 -0
  5. package/dist/bom-quality/index.d.ts +10 -0
  6. package/dist/bom-quality/index.js +9 -0
  7. package/dist/bom-quality/index.js.map +1 -0
  8. package/dist/bom-quality/quality.d.ts +28 -0
  9. package/dist/bom-quality/quality.js +179 -0
  10. package/dist/bom-quality/quality.js.map +1 -0
  11. package/dist/bom-quality/types.d.ts +94 -0
  12. package/dist/bom-quality/types.js +12 -0
  13. package/dist/bom-quality/types.js.map +1 -0
  14. package/dist/bridge/manager.d.ts +41 -0
  15. package/dist/bridge/manager.js +334 -47
  16. package/dist/bridge/manager.js.map +1 -1
  17. package/dist/bridge/protocol.d.ts +48 -1
  18. package/dist/bridge/protocol.js +43 -1
  19. package/dist/bridge/protocol.js.map +1 -1
  20. package/dist/bridge/types.d.ts +6 -0
  21. package/dist/bridge/types.js +6 -0
  22. package/dist/bridge/types.js.map +1 -1
  23. package/dist/catalog/errors.d.ts +49 -0
  24. package/dist/catalog/errors.js +60 -0
  25. package/dist/catalog/errors.js.map +1 -0
  26. package/dist/catalog/index.d.ts +15 -0
  27. package/dist/catalog/index.js +17 -0
  28. package/dist/catalog/index.js.map +1 -0
  29. package/dist/catalog/schema.d.ts +268 -0
  30. package/dist/catalog/schema.js +245 -0
  31. package/dist/catalog/schema.js.map +1 -0
  32. package/dist/catalog/starter.d.ts +159 -0
  33. package/dist/catalog/starter.js +579 -0
  34. package/dist/catalog/starter.js.map +1 -0
  35. package/dist/catalog/validation.d.ts +48 -0
  36. package/dist/catalog/validation.js +263 -0
  37. package/dist/catalog/validation.js.map +1 -0
  38. package/dist/circuit/circuit-ir.d.ts +460 -0
  39. package/dist/circuit/circuit-ir.js +292 -0
  40. package/dist/circuit/circuit-ir.js.map +1 -0
  41. package/dist/circuit/compiler.d.ts +55 -0
  42. package/dist/circuit/compiler.js +197 -0
  43. package/dist/circuit/compiler.js.map +1 -0
  44. package/dist/circuit/design-intent.d.ts +94 -0
  45. package/dist/circuit/design-intent.js +118 -0
  46. package/dist/circuit/design-intent.js.map +1 -0
  47. package/dist/circuit/errors.d.ts +50 -0
  48. package/dist/circuit/errors.js +61 -0
  49. package/dist/circuit/errors.js.map +1 -0
  50. package/dist/circuit/index.d.ts +17 -0
  51. package/dist/circuit/index.js +18 -0
  52. package/dist/circuit/index.js.map +1 -0
  53. package/dist/circuit/types.d.ts +186 -0
  54. package/dist/circuit/types.js +123 -0
  55. package/dist/circuit/types.js.map +1 -0
  56. package/dist/cli/auto-setup.d.ts +5 -0
  57. package/dist/cli/auto-setup.js +355 -0
  58. package/dist/cli/auto-setup.js.map +1 -0
  59. package/dist/cli/client-definitions.d.ts +34 -0
  60. package/dist/cli/client-definitions.js +194 -0
  61. package/dist/cli/client-definitions.js.map +1 -0
  62. package/dist/cli/local-setup.d.ts +5 -1
  63. package/dist/cli/local-setup.js +40 -6
  64. package/dist/cli/local-setup.js.map +1 -1
  65. package/dist/config/env.d.ts +6 -1
  66. package/dist/config/env.js +93 -5
  67. package/dist/config/env.js.map +1 -1
  68. package/dist/config/profiles.js +4 -4
  69. package/dist/config/version.d.ts +1 -1
  70. package/dist/config/version.js +1 -1
  71. package/dist/export-manifest/errors.d.ts +55 -0
  72. package/dist/export-manifest/errors.js +69 -0
  73. package/dist/export-manifest/errors.js.map +1 -0
  74. package/dist/export-manifest/index.d.ts +10 -0
  75. package/dist/export-manifest/index.js +9 -0
  76. package/dist/export-manifest/index.js.map +1 -0
  77. package/dist/export-manifest/types.d.ts +170 -0
  78. package/dist/export-manifest/types.js +36 -0
  79. package/dist/export-manifest/types.js.map +1 -0
  80. package/dist/export-manifest/validation.d.ts +39 -0
  81. package/dist/export-manifest/validation.js +298 -0
  82. package/dist/export-manifest/validation.js.map +1 -0
  83. package/dist/index.js +20 -0
  84. package/dist/index.js.map +1 -1
  85. package/dist/net-validation/errors.d.ts +89 -0
  86. package/dist/net-validation/errors.js +69 -0
  87. package/dist/net-validation/errors.js.map +1 -0
  88. package/dist/net-validation/fixtures.d.ts +38 -0
  89. package/dist/net-validation/fixtures.js +215 -0
  90. package/dist/net-validation/fixtures.js.map +1 -0
  91. package/dist/net-validation/index.d.ts +10 -0
  92. package/dist/net-validation/index.js +9 -0
  93. package/dist/net-validation/index.js.map +1 -0
  94. package/dist/net-validation/schema.d.ts +118 -0
  95. package/dist/net-validation/schema.js +140 -0
  96. package/dist/net-validation/schema.js.map +1 -0
  97. package/dist/net-validation/validation.d.ts +45 -0
  98. package/dist/net-validation/validation.js +369 -0
  99. package/dist/net-validation/validation.js.map +1 -0
  100. package/dist/pcb-constraints/errors.d.ts +39 -0
  101. package/dist/pcb-constraints/errors.js +133 -0
  102. package/dist/pcb-constraints/errors.js.map +1 -0
  103. package/dist/pcb-constraints/index.d.ts +9 -0
  104. package/dist/pcb-constraints/index.js +9 -0
  105. package/dist/pcb-constraints/index.js.map +1 -0
  106. package/dist/pcb-constraints/types.d.ts +102 -0
  107. package/dist/pcb-constraints/types.js +7 -0
  108. package/dist/pcb-constraints/types.js.map +1 -0
  109. package/dist/pcb-constraints/validation.d.ts +70 -0
  110. package/dist/pcb-constraints/validation.js +309 -0
  111. package/dist/pcb-constraints/validation.js.map +1 -0
  112. package/dist/server/factory.js +2 -0
  113. package/dist/server/factory.js.map +1 -1
  114. package/dist/server/transports/http.d.ts +20 -1
  115. package/dist/server/transports/http.js +156 -34
  116. package/dist/server/transports/http.js.map +1 -1
  117. package/dist/storage/database.d.ts +25 -0
  118. package/dist/storage/database.js +126 -51
  119. package/dist/storage/database.js.map +1 -1
  120. package/dist/tools/{L0_diagnostics.d.ts → L0_diagnostics_api.d.ts} +2 -2
  121. package/dist/tools/L0_diagnostics_api.js +152 -0
  122. package/dist/tools/L0_diagnostics_api.js.map +1 -0
  123. package/dist/tools/L0_diagnostics_core.d.ts +6 -0
  124. package/dist/tools/{L0_diagnostics.js → L0_diagnostics_core.js} +50 -124
  125. package/dist/tools/L0_diagnostics_core.js.map +1 -0
  126. package/dist/tools/L1_board.js +20 -4
  127. package/dist/tools/L1_board.js.map +1 -1
  128. package/dist/tools/{L1_bom.d.ts → L1_bom_core.d.ts} +2 -2
  129. package/dist/tools/{L1_bom.js → L1_bom_core.js} +21 -101
  130. package/dist/tools/L1_bom_core.js.map +1 -0
  131. package/dist/tools/{L1_schematic.d.ts → L1_bom_sourcing.d.ts} +2 -2
  132. package/dist/tools/L1_bom_sourcing.js +265 -0
  133. package/dist/tools/L1_bom_sourcing.js.map +1 -0
  134. package/dist/tools/L1_drc_erc.js +15 -3
  135. package/dist/tools/L1_drc_erc.js.map +1 -1
  136. package/dist/tools/L1_export.js +20 -4
  137. package/dist/tools/L1_export.js.map +1 -1
  138. package/dist/tools/L1_pcb_constraints.d.ts +6 -0
  139. package/dist/tools/L1_pcb_constraints.js +234 -0
  140. package/dist/tools/L1_pcb_constraints.js.map +1 -0
  141. package/dist/tools/L1_pcb_write.js +26 -12
  142. package/dist/tools/L1_pcb_write.js.map +1 -1
  143. package/dist/tools/L1_schematic_read.d.ts +6 -0
  144. package/dist/tools/{L1_schematic.js → L1_schematic_read.js} +125 -190
  145. package/dist/tools/L1_schematic_read.js.map +1 -0
  146. package/dist/tools/L1_schematic_write.d.ts +6 -0
  147. package/dist/tools/L1_schematic_write.js +516 -0
  148. package/dist/tools/L1_schematic_write.js.map +1 -0
  149. package/dist/tools/register.js +15 -7
  150. package/dist/tools/register.js.map +1 -1
  151. package/dist/tools/registry.d.ts +41 -7
  152. package/dist/tools/registry.js +111 -12
  153. package/dist/tools/registry.js.map +1 -1
  154. package/dist/tools/types.d.ts +16 -0
  155. package/dist/utils/logger.d.ts +1 -1
  156. package/dist/utils/logger.js +1 -1
  157. package/dist/utils/logger.js.map +1 -1
  158. package/dist/vendors/base-http-client.d.ts +48 -0
  159. package/dist/vendors/base-http-client.js +75 -0
  160. package/dist/vendors/base-http-client.js.map +1 -0
  161. package/dist/vendors/digikey/client.js +10 -19
  162. package/dist/vendors/digikey/client.js.map +1 -1
  163. package/dist/vendors/jlcpcb/client.js +41 -70
  164. package/dist/vendors/jlcpcb/client.js.map +1 -1
  165. package/dist/vendors/lcsc/client.js +5 -18
  166. package/dist/vendors/lcsc/client.js.map +1 -1
  167. package/dist/vendors/mouser/client.js +4 -12
  168. package/dist/vendors/mouser/client.js.map +1 -1
  169. package/easyeda-bridge-extension.eext +0 -0
  170. package/package.json +41 -26
  171. package/dist/tools/L0_diagnostics.js.map +0 -1
  172. package/dist/tools/L1_bom.js.map +0 -1
  173. package/dist/tools/L1_schematic.js.map +0 -1
@@ -0,0 +1,295 @@
1
+ /**
2
+ * BOM quality — supplier adapter layer.
3
+ *
4
+ * Provides a common {@link SupplierAdapter} interface over each vendor
5
+ * client so quality checks can query any configured supplier uniformly.
6
+ *
7
+ * Each adapter:
8
+ * - normalises part data into {@link SupplierQueryResult}
9
+ * - records a query timestamp for provenance
10
+ * - wraps transient errors with an exponential-backoff retry
11
+ *
12
+ * @module
13
+ */
14
+ import { EasyEdaMcpError } from '../schemas/common.js';
15
+ import { getLogger } from '../utils/logger.js';
16
+ // ── Retry helper ───────────────────────────────────────────────────────────
17
+ const RETRY_BASE_MS = 200;
18
+ const RETRY_MAX_MS = 2_000;
19
+ const RETRY_ATTEMPTS = 2;
20
+ async function withRetry(label, fn, logger, attempts = RETRY_ATTEMPTS) {
21
+ let lastError;
22
+ for (let i = 0; i <= attempts; i++) {
23
+ try {
24
+ return await fn(i);
25
+ }
26
+ catch (err) {
27
+ lastError = err;
28
+ // Only retry on retryable errors (rate limits, 5xx)
29
+ const retryable = err instanceof EasyEdaMcpError ? err.retryable === true : true; // unknown errors are not retried
30
+ if (!retryable || i === attempts) {
31
+ throw err;
32
+ }
33
+ const delay = Math.min(RETRY_BASE_MS * 2 ** i, RETRY_MAX_MS);
34
+ logger.debug({ delay, attempt: i + 1, label }, 'retrying supplier query');
35
+ await new Promise((r) => setTimeout(r, delay));
36
+ }
37
+ }
38
+ throw lastError;
39
+ }
40
+ function toLifecycle(raw) {
41
+ if (typeof raw === 'boolean')
42
+ return raw ? 'discontinued' : 'active';
43
+ if (typeof raw === 'string') {
44
+ const lc = raw.toLowerCase();
45
+ if (lc === 'active' || lc === 'production')
46
+ return 'active';
47
+ if (lc.includes('discon') || lc.includes('obsolete') || lc === 'eol')
48
+ return 'discontinued';
49
+ }
50
+ return 'unknown';
51
+ }
52
+ // ── LCSC adapter ───────────────────────────────────────────────────────────
53
+ export class LcscAdapter {
54
+ kind = 'lcsc';
55
+ displayName = 'LCSC';
56
+ client;
57
+ logger;
58
+ constructor(client) {
59
+ this.client = client;
60
+ this.logger = getLogger();
61
+ }
62
+ isAvailable() {
63
+ return this.client !== null;
64
+ }
65
+ buildFoundResult(detail, now) {
66
+ const unitPrice = detail.priceBreaks?.[0]?.unitPrice ??
67
+ (typeof detail.price === 'number'
68
+ ? detail.price
69
+ : typeof detail.price === 'string'
70
+ ? parseFloat(detail.price)
71
+ : undefined);
72
+ return {
73
+ supplier: 'lcsc',
74
+ found: true,
75
+ lcsc: detail.lcsc,
76
+ mpn: detail.manufacturer || undefined,
77
+ manufacturer: detail.manufacturer || undefined,
78
+ description: detail.description || undefined,
79
+ lifecycle: detail.discontinued ? 'discontinued' : 'active',
80
+ stock: detail.stockCount ?? detail.stock ?? 0,
81
+ unitPrice,
82
+ currency: 'USD',
83
+ leadTimeDays: detail.leadTime,
84
+ queriedAt: now,
85
+ confidence: 'high',
86
+ };
87
+ }
88
+ async queryPart(identifier) {
89
+ const client = this.client;
90
+ if (!client)
91
+ return null;
92
+ if (!identifier.lcsc && !identifier.mpn)
93
+ return null;
94
+ const lcscCode = identifier.lcsc;
95
+ if (!lcscCode)
96
+ return null; // LCSC requires an LCSC code
97
+ const now = new Date().toISOString();
98
+ try {
99
+ const detail = await withRetry(`lcsc.getPartDetail(${lcscCode})`, () => client.getPartDetail(lcscCode), this.logger);
100
+ if (!detail) {
101
+ return {
102
+ supplier: 'lcsc',
103
+ found: false,
104
+ lifecycle: 'unknown',
105
+ stock: 0,
106
+ queriedAt: now,
107
+ confidence: 'medium',
108
+ };
109
+ }
110
+ return this.buildFoundResult(detail, now);
111
+ }
112
+ catch (err) {
113
+ this.logger.warn({ err, lcscCode }, 'lcsc adapter query failed');
114
+ // Return an "unavailable" result rather than throwing
115
+ return {
116
+ supplier: 'lcsc',
117
+ found: false,
118
+ lifecycle: 'unknown',
119
+ stock: 0,
120
+ queriedAt: now,
121
+ confidence: 'low',
122
+ };
123
+ }
124
+ }
125
+ }
126
+ // ── Mouser adapter ─────────────────────────────────────────────────────────
127
+ export class MouserAdapter {
128
+ kind = 'mouser';
129
+ displayName = 'Mouser';
130
+ client;
131
+ logger;
132
+ constructor(client) {
133
+ this.client = client;
134
+ this.logger = getLogger();
135
+ }
136
+ isAvailable() {
137
+ return this.client !== null;
138
+ }
139
+ buildFoundResult(part, now) {
140
+ return {
141
+ supplier: 'mouser',
142
+ found: true,
143
+ mpn: part.manufacturer || undefined,
144
+ manufacturer: part.manufacturer || undefined,
145
+ description: part.description || undefined,
146
+ lifecycle: 'active',
147
+ stock: part.availability ?? 0,
148
+ unitPrice: part.priceBreaks?.[0]?.price,
149
+ currency: 'USD',
150
+ leadTimeDays: part.leadTime ? parseInt(part.leadTime, 10) || undefined : undefined,
151
+ queriedAt: now,
152
+ confidence: 'high',
153
+ };
154
+ }
155
+ async queryPart(identifier) {
156
+ const client = this.client;
157
+ if (!client)
158
+ return null;
159
+ const mpn = identifier.mpn;
160
+ if (!mpn)
161
+ return null; // Mouser requires an MPN
162
+ const now = new Date().toISOString();
163
+ try {
164
+ const results = await withRetry(`mouser.searchByPartNumber(${mpn})`, () => client.searchByPartNumber(mpn), this.logger);
165
+ if (!results || results.length === 0) {
166
+ return {
167
+ supplier: 'mouser',
168
+ found: false,
169
+ lifecycle: 'unknown',
170
+ stock: 0,
171
+ queriedAt: now,
172
+ confidence: 'medium',
173
+ };
174
+ }
175
+ const part = results[0];
176
+ if (!part) {
177
+ return {
178
+ supplier: 'mouser',
179
+ found: false,
180
+ lifecycle: 'unknown',
181
+ stock: 0,
182
+ queriedAt: now,
183
+ confidence: 'medium',
184
+ };
185
+ }
186
+ return this.buildFoundResult(part, now);
187
+ }
188
+ catch (err) {
189
+ this.logger.warn({ err, mpn }, 'mouser adapter query failed');
190
+ return {
191
+ supplier: 'mouser',
192
+ found: false,
193
+ lifecycle: 'unknown',
194
+ stock: 0,
195
+ queriedAt: now,
196
+ confidence: 'low',
197
+ };
198
+ }
199
+ }
200
+ }
201
+ // ── DigiKey adapter ────────────────────────────────────────────────────────
202
+ export class DigiKeyAdapter {
203
+ kind = 'digikey';
204
+ displayName = 'DigiKey';
205
+ client;
206
+ logger;
207
+ constructor(client) {
208
+ this.client = client;
209
+ this.logger = getLogger();
210
+ }
211
+ isAvailable() {
212
+ return this.client !== null;
213
+ }
214
+ buildFoundResult(part, mpn, now) {
215
+ return {
216
+ supplier: 'digikey',
217
+ found: true,
218
+ mpn: part.manufacturerPartNumber || undefined,
219
+ manufacturer: part.manufacturer || undefined,
220
+ description: part.description || undefined,
221
+ lifecycle: toLifecycle(part.rohsStatus), // RoHS active is a loose proxy
222
+ stock: part.quantityAvailable ?? 0,
223
+ unitPrice: part.unitPrice || undefined,
224
+ currency: 'USD',
225
+ queriedAt: now,
226
+ confidence: part.manufacturerPartNumber?.toLowerCase() === mpn.toLowerCase() ? 'high' : 'medium',
227
+ };
228
+ }
229
+ async queryPart(identifier) {
230
+ const client = this.client;
231
+ if (!client)
232
+ return null;
233
+ const mpn = identifier.mpn;
234
+ if (!mpn)
235
+ return null; // DigiKey requires MPN or keyword
236
+ const now = new Date().toISOString();
237
+ try {
238
+ // Use keyword search as a proxy — DigiKey doesn't expose a
239
+ // direct MPN-lookup endpoint in the current client.
240
+ const results = await withRetry(`digikey.searchByKeyword(${mpn})`, () => client.searchByKeyword(mpn), this.logger);
241
+ if (!results || results.length === 0) {
242
+ return {
243
+ supplier: 'digikey',
244
+ found: false,
245
+ lifecycle: 'unknown',
246
+ stock: 0,
247
+ queriedAt: now,
248
+ confidence: 'medium',
249
+ };
250
+ }
251
+ // Take the first result — keyword search returns best match first
252
+ const part = results[0];
253
+ if (!part) {
254
+ return {
255
+ supplier: 'digikey',
256
+ found: false,
257
+ lifecycle: 'unknown',
258
+ stock: 0,
259
+ queriedAt: now,
260
+ confidence: 'medium',
261
+ };
262
+ }
263
+ return this.buildFoundResult(part, mpn, now);
264
+ }
265
+ catch (err) {
266
+ this.logger.warn({ err, mpn }, 'digikey adapter query failed');
267
+ return {
268
+ supplier: 'digikey',
269
+ found: false,
270
+ lifecycle: 'unknown',
271
+ stock: 0,
272
+ queriedAt: now,
273
+ confidence: 'low',
274
+ };
275
+ }
276
+ }
277
+ }
278
+ /**
279
+ * Build the adapter map from a ToolContext vendors object.
280
+ * Each adapter wraps the corresponding vendor client (or null if disabled).
281
+ */
282
+ export function createAdapters(vendors) {
283
+ return {
284
+ lcsc: new LcscAdapter(vendors.lcsc),
285
+ mouser: new MouserAdapter(vendors.mouser),
286
+ digikey: new DigiKeyAdapter(vendors.digikey),
287
+ };
288
+ }
289
+ /**
290
+ * Return the subset of adapters that are currently configured / available.
291
+ */
292
+ export function availableAdapters(adapters) {
293
+ return Object.values(adapters).filter((a) => a.isAvailable());
294
+ }
295
+ //# sourceMappingURL=adapter.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"adapter.js","sourceRoot":"","sources":["../../src/bom-quality/adapter.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,10 @@
1
+ /**
2
+ * BOM quality — public API barrel.
3
+ *
4
+ * @module
5
+ */
6
+ export type { SupplierKind, PartLifecycle, BomEntry, SupplierQueryResult, BomQualityIssueType, BomQualityIssue, BomQualityReport, BomQualityConfig, } from './types.js';
7
+ export { DEFAULT_BOM_QUALITY_CONFIG } from './types.js';
8
+ export type { SupplierAdapter, AdapterMap } from './adapter.js';
9
+ export { LcscAdapter, MouserAdapter, DigiKeyAdapter, createAdapters, availableAdapters, } from './adapter.js';
10
+ export { generateBomQualityReport } from './quality.js';
@@ -0,0 +1,9 @@
1
+ /**
2
+ * BOM quality — public API barrel.
3
+ *
4
+ * @module
5
+ */
6
+ export { DEFAULT_BOM_QUALITY_CONFIG } from './types.js';
7
+ export { LcscAdapter, MouserAdapter, DigiKeyAdapter, createAdapters, availableAdapters, } from './adapter.js';
8
+ export { generateBomQualityReport } from './quality.js';
9
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/bom-quality/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAaH,OAAO,EAAE,0BAA0B,EAAE,MAAM,YAAY,CAAC;AAGxD,OAAO,EACL,WAAW,EACX,aAAa,EACb,cAAc,EACd,cAAc,EACd,iBAAiB,GAClB,MAAM,cAAc,CAAC;AAEtB,OAAO,EAAE,wBAAwB,EAAE,MAAM,cAAc,CAAC"}
@@ -0,0 +1,28 @@
1
+ /**
2
+ * BOM quality — report generation.
3
+ *
4
+ * Analyses BOM entries against supplier data to surface issues:
5
+ * 1. **Unavailable** — part not found or discontinued at any supplier.
6
+ * 2. **Single-source** — part found at exactly one supplier.
7
+ * 3. **Missing MPN** — no manufacturer part number.
8
+ * 4. **Missing footprint** — no package / footprint.
9
+ * 5. **Low stock** — stock below threshold at every supplier.
10
+ *
11
+ * @module
12
+ */
13
+ import type { BomEntry, BomQualityReport, BomQualityConfig } from './types.js';
14
+ import type { AdapterMap } from './adapter.js';
15
+ /**
16
+ * Generate a BOM quality report for the given entries.
17
+ *
18
+ * For each entry the function:
19
+ * 1. Queries every available supplier that can handle the entry.
20
+ * 2. Collects supplier results (with error resilience).
21
+ * 3. Runs the five quality checks.
22
+ * 4. Returns a structured report with a summary.
23
+ *
24
+ * Supplier failures are caught gracefully — the report always succeeds
25
+ * (non-null), with `hasSupplierErrors` set to `true` and any affected
26
+ * entries marked via their issues.
27
+ */
28
+ export declare function generateBomQualityReport(bomId: string, entries: BomEntry[], adapters: AdapterMap, config?: BomQualityConfig): Promise<BomQualityReport>;
@@ -0,0 +1,179 @@
1
+ /**
2
+ * BOM quality — report generation.
3
+ *
4
+ * Analyses BOM entries against supplier data to surface issues:
5
+ * 1. **Unavailable** — part not found or discontinued at any supplier.
6
+ * 2. **Single-source** — part found at exactly one supplier.
7
+ * 3. **Missing MPN** — no manufacturer part number.
8
+ * 4. **Missing footprint** — no package / footprint.
9
+ * 5. **Low stock** — stock below threshold at every supplier.
10
+ *
11
+ * @module
12
+ */
13
+ import { DEFAULT_BOM_QUALITY_CONFIG } from './types.js';
14
+ import { availableAdapters } from './adapter.js';
15
+ import { getLogger } from '../utils/logger.js';
16
+ // ── Issue factory ──────────────────────────────────────────────────────────
17
+ function issue(type, severity, reference, message, details) {
18
+ return { type, severity, reference, message, details };
19
+ }
20
+ function unavailableIssue(ref, supplier, reason) {
21
+ return issue('unavailable', 'error', ref, `Part not available at ${supplier}: ${reason}`, {
22
+ supplier,
23
+ reason,
24
+ });
25
+ }
26
+ function singleSourceIssue(ref, suppliers) {
27
+ return issue('single_source', 'warning', ref, `Part is single-sourced (only found at ${suppliers.join(', ')})`, { suppliers });
28
+ }
29
+ function missingMpnIssue(ref) {
30
+ return issue('missing_mpn', 'warning', ref, 'No manufacturer part number (MPN) specified — replaceability is limited');
31
+ }
32
+ function missingFootprintIssue(ref) {
33
+ return issue('missing_footprint', 'warning', ref, 'No footprint / package specified — PCB layout verification is not possible');
34
+ }
35
+ function lowStockIssue(ref, supplier, stock) {
36
+ return issue('low_stock', 'warning', ref, `Low stock at ${supplier}: ${stock} units`, {
37
+ supplier,
38
+ stock,
39
+ });
40
+ }
41
+ // ── Supplier query scope ───────────────────────────────────────────────────
42
+ /**
43
+ * Determine which supplier identifiers to query based on what the entry has.
44
+ * - If an LCSC code is present, query LCSC.
45
+ * - If an MPN is present, query Mouser and DigiKey.
46
+ */
47
+ function queryTargets(entry) {
48
+ const targets = [];
49
+ if (entry.lcsc) {
50
+ targets.push({ supplier: 'lcsc', identifier: { lcsc: entry.lcsc } });
51
+ }
52
+ if (entry.mpn) {
53
+ targets.push({ supplier: 'mouser', identifier: { mpn: entry.mpn } });
54
+ targets.push({ supplier: 'digikey', identifier: { mpn: entry.mpn } });
55
+ }
56
+ return targets;
57
+ }
58
+ // ── Supplier query helper ────────────────────────────────────────────────
59
+ async function querySuppliersForEntry(entry, adapterByKind, _logger) {
60
+ const targets = queryTargets(entry);
61
+ const supplierData = [];
62
+ let hasErrors = false;
63
+ for (const target of targets) {
64
+ const adapter = adapterByKind.get(target.supplier);
65
+ if (!adapter)
66
+ continue;
67
+ try {
68
+ const result = await adapter.queryPart(target.identifier);
69
+ if (result) {
70
+ supplierData.push(result);
71
+ if (result.confidence === 'low')
72
+ hasErrors = true;
73
+ }
74
+ }
75
+ catch {
76
+ // Should not happen (adapters catch internally), but safeguard
77
+ hasErrors = true;
78
+ }
79
+ }
80
+ return { supplierData, hasErrors, queriedCount: targets.length };
81
+ }
82
+ // ── Main entry point ───────────────────────────────────────────────────────
83
+ /**
84
+ * Generate a BOM quality report for the given entries.
85
+ *
86
+ * For each entry the function:
87
+ * 1. Queries every available supplier that can handle the entry.
88
+ * 2. Collects supplier results (with error resilience).
89
+ * 3. Runs the five quality checks.
90
+ * 4. Returns a structured report with a summary.
91
+ *
92
+ * Supplier failures are caught gracefully — the report always succeeds
93
+ * (non-null), with `hasSupplierErrors` set to `true` and any affected
94
+ * entries marked via their issues.
95
+ */
96
+ export async function generateBomQualityReport(bomId, entries, adapters, config = DEFAULT_BOM_QUALITY_CONFIG) {
97
+ const now = new Date().toISOString();
98
+ let hasSupplierErrors = false;
99
+ // Resolve available adapters once
100
+ const adapterList = availableAdapters(adapters);
101
+ const adapterByKind = new Map(adapterList.map((a) => [a.kind, a]));
102
+ const logger = getLogger();
103
+ const reportEntries = await Promise.all(entries.map(async (entry) => {
104
+ // ── 1. Query applicable suppliers ──────────────────────────────────
105
+ const { supplierData, hasErrors, queriedCount } = await querySuppliersForEntry(entry, adapterByKind, logger);
106
+ if (hasErrors)
107
+ hasSupplierErrors = true;
108
+ // ── 2. Run quality checks ──────────────────────────────────────────
109
+ const issues = [];
110
+ // Check: unavailable (not found or discontinued at any queried supplier)
111
+ const foundSuppliers = supplierData.filter((s) => s.found);
112
+ const discontinuedSuppliers = supplierData.filter((s) => s.found && s.lifecycle === 'discontinued');
113
+ if (supplierData.length > 0 && foundSuppliers.length === 0) {
114
+ // Queried at least one supplier, none had it
115
+ const queried = supplierData.map((s) => s.supplier).join(', ');
116
+ issues.push(unavailableIssue(entry.reference, queried, 'not found at any supplier'));
117
+ }
118
+ for (const ds of discontinuedSuppliers) {
119
+ issues.push(unavailableIssue(entry.reference, ds.supplier, 'discontinued / obsolete'));
120
+ }
121
+ // Check: single-source
122
+ const uniqueFoundSuppliers = [...new Set(foundSuppliers.map((s) => s.supplier))];
123
+ if (uniqueFoundSuppliers.length === 1 && queriedCount > 1) {
124
+ // Only flag single-source when we actually queried more than one supplier
125
+ // (i.e. the entry had identifiers for multiple suppliers)
126
+ issues.push(singleSourceIssue(entry.reference, uniqueFoundSuppliers));
127
+ }
128
+ // Check: missing MPN
129
+ if (config.requireMpn && !entry.mpn && !entry.manufacturer) {
130
+ issues.push(missingMpnIssue(entry.reference));
131
+ }
132
+ // Check: missing footprint
133
+ if (config.requireFootprint && !entry.footprint) {
134
+ issues.push(missingFootprintIssue(entry.reference));
135
+ }
136
+ // Check: low stock (at every found supplier)
137
+ if (foundSuppliers.length > 0) {
138
+ const allLow = foundSuppliers.every((s) => s.stock < config.lowStockThreshold);
139
+ if (allLow) {
140
+ for (const fs of foundSuppliers) {
141
+ issues.push(lowStockIssue(entry.reference, fs.supplier, fs.stock));
142
+ }
143
+ }
144
+ }
145
+ return {
146
+ reference: entry.reference,
147
+ description: entry.value ?? '',
148
+ footprint: entry.footprint ?? '',
149
+ quantity: entry.quantity,
150
+ lcsc: entry.lcsc,
151
+ mpn: entry.mpn,
152
+ manufacturer: entry.manufacturer,
153
+ supplierData,
154
+ issues,
155
+ };
156
+ }));
157
+ // ── 3. Aggregate summary ────────────────────────────────────────────────
158
+ const allIssues = reportEntries.flatMap((e) => e.issues);
159
+ const summary = {
160
+ totalIssues: allIssues.length,
161
+ errors: allIssues.filter((i) => i.severity === 'error').length,
162
+ warnings: allIssues.filter((i) => i.severity === 'warning').length,
163
+ info: allIssues.filter((i) => i.severity === 'info').length,
164
+ unavailableCount: allIssues.filter((i) => i.type === 'unavailable').length,
165
+ singleSourceCount: allIssues.filter((i) => i.type === 'single_source').length,
166
+ missingMpnCount: allIssues.filter((i) => i.type === 'missing_mpn').length,
167
+ missingFootprintCount: allIssues.filter((i) => i.type === 'missing_footprint').length,
168
+ lowStockCount: allIssues.filter((i) => i.type === 'low_stock').length,
169
+ };
170
+ return {
171
+ bomId,
172
+ generatedAt: now,
173
+ totalEntries: entries.length,
174
+ entries: reportEntries,
175
+ summary,
176
+ hasSupplierErrors,
177
+ };
178
+ }
179
+ //# sourceMappingURL=quality.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"quality.js","sourceRoot":"","sources":["../../src/bom-quality/quality.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,94 @@
1
+ /**
2
+ * BOM quality — core type definitions.
3
+ *
4
+ * @module
5
+ */
6
+ /** Supported supplier identifiers. */
7
+ export type SupplierKind = 'lcsc' | 'mouser' | 'digikey' | 'jlcpcb';
8
+ /** Lifecycle / status of a part at a supplier. */
9
+ export type PartLifecycle = 'active' | 'discontinued' | 'unknown';
10
+ /** BOM entry as passed to quality check, extended with metadata. */
11
+ export interface BomEntry {
12
+ reference: string;
13
+ value?: string;
14
+ footprint?: string;
15
+ lcsc?: string;
16
+ mpn?: string;
17
+ manufacturer?: string;
18
+ quantity: number;
19
+ /** Source of this entry (e.g. 'bridge', 'manual'). */
20
+ source: string;
21
+ /** ISO-8601 timestamp when this entry was obtained. */
22
+ fetchedAt: string;
23
+ }
24
+ /** Normalised result from a single supplier query. */
25
+ export interface SupplierQueryResult {
26
+ supplier: SupplierKind;
27
+ found: boolean;
28
+ lcsc?: string;
29
+ mpn?: string;
30
+ manufacturer?: string;
31
+ description?: string;
32
+ lifecycle: PartLifecycle;
33
+ stock: number;
34
+ unitPrice?: number;
35
+ currency?: string;
36
+ leadTimeDays?: number;
37
+ /** ISO-8601 timestamp of this supplier response. */
38
+ queriedAt: string;
39
+ /** Human-readable confidence (e.g. 'high', 'medium', 'low'). */
40
+ confidence: 'high' | 'medium' | 'low';
41
+ }
42
+ /** Issue type identifiers for the BOM quality report. */
43
+ export type BomQualityIssueType = 'unavailable' | 'single_source' | 'missing_mpn' | 'missing_footprint' | 'low_stock';
44
+ /** A single quality issue found for a BOM entry. */
45
+ export interface BomQualityIssue {
46
+ type: BomQualityIssueType;
47
+ severity: 'error' | 'warning' | 'info';
48
+ reference: string;
49
+ message: string;
50
+ details?: Record<string, unknown>;
51
+ }
52
+ /** Complete BOM quality report. */
53
+ export interface BomQualityReport {
54
+ bomId: string;
55
+ generatedAt: string;
56
+ totalEntries: number;
57
+ entries: Array<{
58
+ reference: string;
59
+ description: string;
60
+ footprint: string;
61
+ quantity: number;
62
+ lcsc?: string;
63
+ mpn?: string;
64
+ manufacturer?: string;
65
+ /** Supplier-specific data collected during the check. */
66
+ supplierData: SupplierQueryResult[];
67
+ issues: BomQualityIssue[];
68
+ }>;
69
+ summary: {
70
+ totalIssues: number;
71
+ errors: number;
72
+ warnings: number;
73
+ info: number;
74
+ unavailableCount: number;
75
+ singleSourceCount: number;
76
+ missingMpnCount: number;
77
+ missingFootprintCount: number;
78
+ lowStockCount: number;
79
+ };
80
+ /** Whether any suppliers returned errors during the check. */
81
+ hasSupplierErrors: boolean;
82
+ }
83
+ /** Configuration for BOM quality checking. */
84
+ export interface BomQualityConfig {
85
+ /** Stock threshold below which a part is considered low-stock. */
86
+ lowStockThreshold: number;
87
+ /** Whether to treat a missing LCSC code as an error. */
88
+ requireLcsc: boolean;
89
+ /** Whether to treat a missing MPN as a warning. */
90
+ requireMpn: boolean;
91
+ /** Whether to treat a missing footprint as a warning. */
92
+ requireFootprint: boolean;
93
+ }
94
+ export declare const DEFAULT_BOM_QUALITY_CONFIG: BomQualityConfig;
@@ -0,0 +1,12 @@
1
+ /**
2
+ * BOM quality — core type definitions.
3
+ *
4
+ * @module
5
+ */
6
+ export const DEFAULT_BOM_QUALITY_CONFIG = {
7
+ lowStockThreshold: 100,
8
+ requireLcsc: false,
9
+ requireMpn: true,
10
+ requireFootprint: true,
11
+ };
12
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/bom-quality/types.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAuGH,MAAM,CAAC,MAAM,0BAA0B,GAAqB;IAC1D,iBAAiB,EAAE,GAAG;IACtB,WAAW,EAAE,KAAK;IAClB,UAAU,EAAE,IAAI;IAChB,gBAAgB,EAAE,IAAI;CACvB,CAAC"}