@tscircuit/parts-engine 0.0.26 → 0.0.28

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 CHANGED
@@ -9,6 +9,8 @@ import {
9
9
  digikeyPartsEngine,
10
10
  jlcPartsEngine,
11
11
  DigiKeyPartsEngine,
12
+ mouserPartsEngine,
13
+ MouserPartsEngine,
12
14
  } from "@tscircuit/parts-engine"
13
15
  ```
14
16
 
@@ -17,3 +19,9 @@ import {
17
19
  shape as jlcsearch while caching DigiKey Product Information V4 calls. Use
18
20
  `new DigiKeyPartsEngine({ platformFetch, apiBaseUrl })` to inject a platform
19
21
  fetch implementation or a test/self-hosted endpoint.
22
+
23
+ `mouserPartsEngine.findPart(...)` queries
24
+ `https://mousersearch.tscircuit.com`, which exposes the same category route
25
+ shape while caching Mouser Search API calls. Use
26
+ `new MouserPartsEngine({ platformFetch, apiBaseUrl })` to inject a platform
27
+ fetch implementation or a test/self-hosted endpoint.
package/dist/index.d.ts CHANGED
@@ -4409,4 +4409,39 @@ declare const withDigiKeyStockPreference: (parts: DigiKeySearchPart[] | undefine
4409
4409
 
4410
4410
  declare const digikeyPartsEngine: DigiKeyPartsEngine;
4411
4411
 
4412
- export { DigiKeyPartsEngine, type DigiKeyPartsEngineOptions, type DigiKeySearchPart, type EasyEdaProxyConfig, type FetchPartCircuitJsonParams, JlcPcbPartsEngine, type JlcPcbPartsEngineOptions, type PlatformFetch, cache, digikeyCache, digikeyPartsEngine, getDigiKeyPartsCached, getFetchWithEasyEdaProxy, jlcPartsEngine, withDigiKeyStockPreference };
4412
+ type MouserPartsEngineOptions = {
4413
+ platformFetch?: PlatformFetch;
4414
+ apiBaseUrl?: string;
4415
+ };
4416
+ type MouserSearchPart = {
4417
+ mouser_product_number: string;
4418
+ supplier_part_number?: string;
4419
+ mfr: string;
4420
+ manufacturer?: string;
4421
+ package?: string;
4422
+ description?: string;
4423
+ stock: number;
4424
+ price?: number;
4425
+ normally_stocking?: boolean;
4426
+ };
4427
+
4428
+ declare class MouserPartsEngine implements PartsEngine {
4429
+ private readonly platformFetch;
4430
+ private readonly apiBaseUrl;
4431
+ constructor(options?: MouserPartsEngineOptions);
4432
+ private getCategoryParts;
4433
+ private getKeywordParts;
4434
+ private toSupplierPartNumbers;
4435
+ findPart({ sourceComponent, footprinterString, }: Parameters<PartsEngine["findPart"]>[0]): Promise<{}>;
4436
+ }
4437
+
4438
+ declare const mouserCache: Map<string, unknown>;
4439
+ declare const getMouserPartsCached: (path: string, params: Record<string, string | number | boolean | undefined>, options?: {
4440
+ platformFetch?: PlatformFetch;
4441
+ apiBaseUrl?: string;
4442
+ }) => Promise<Record<string, MouserSearchPart[]>>;
4443
+ declare const withMouserStockPreference: (parts: MouserSearchPart[] | undefined) => MouserSearchPart[];
4444
+
4445
+ declare const mouserPartsEngine: MouserPartsEngine;
4446
+
4447
+ export { DigiKeyPartsEngine, type DigiKeyPartsEngineOptions, type DigiKeySearchPart, type EasyEdaProxyConfig, type FetchPartCircuitJsonParams, JlcPcbPartsEngine, type JlcPcbPartsEngineOptions, MouserPartsEngine, type MouserPartsEngineOptions, type MouserSearchPart, type PlatformFetch, cache, digikeyCache, digikeyPartsEngine, getDigiKeyPartsCached, getFetchWithEasyEdaProxy, getMouserPartsCached, jlcPartsEngine, mouserCache, mouserPartsEngine, withDigiKeyStockPreference, withMouserStockPreference };
package/dist/index.js CHANGED
@@ -9449,6 +9449,7 @@ var mil2mm = (mil) => {
9449
9449
  return mm(`${mil}mil`);
9450
9450
  return mm(mil);
9451
9451
  };
9452
+ var DEFAULT_PCB_THICKNESS_MM = 1.6;
9452
9453
  function generateArcFromSweep(startX, startY, endX, endY, radius, largeArcFlag, sweepFlag) {
9453
9454
  const start = { x: startX, y: startY };
9454
9455
  const end = { x: endX, y: endY };
@@ -9493,6 +9494,159 @@ function generateArcFromSweep(startX, startY, endX, endY, radius, largeArcFlag,
9493
9494
  return path;
9494
9495
  }
9495
9496
  var mil10ToMm = (value) => value * 10 * 0.0254;
9497
+ var getPinLabelValues = (labels) => {
9498
+ if (typeof labels === "string") return [labels];
9499
+ return [...labels];
9500
+ };
9501
+ var stripEasyEdaPolarityHintDecoration = (label) => label.toLowerCase().replace(/[^a-z0-9+-]/g, "");
9502
+ var getPinKeySortValue = (pinKey) => {
9503
+ const match = /^pin(\d+)$/i.exec(pinKey);
9504
+ return match ? Number(match[1]) : Number.MAX_SAFE_INTEGER;
9505
+ };
9506
+ var getPolarizedPinMetadata = (pinLabels) => {
9507
+ const labelsByPin = Object.entries(pinLabels).map(([pin, labels]) => ({
9508
+ pin,
9509
+ labels: getPinLabelValues(labels).map(stripEasyEdaPolarityHintDecoration)
9510
+ }));
9511
+ const anodePin = labelsByPin.find(
9512
+ ({ labels }) => labels.some((label) => ["a", "anode", "pos", "+"].includes(label))
9513
+ )?.pin;
9514
+ const cathodePin = labelsByPin.find(
9515
+ ({ labels }) => labels.some((label) => ["c", "k", "cathode", "neg", "-"].includes(label))
9516
+ )?.pin;
9517
+ if (!anodePin || !cathodePin || anodePin === cathodePin) return void 0;
9518
+ const polarizedPinEntries = [
9519
+ [anodePin, ["anode", "pos"]],
9520
+ [cathodePin, ["cathode", "neg"]]
9521
+ ].sort(
9522
+ ([pinA], [pinB]) => getPinKeySortValue(pinA) - getPinKeySortValue(pinB)
9523
+ );
9524
+ return {
9525
+ portHintsMap: Object.fromEntries(
9526
+ polarizedPinEntries.map(([pin, labels]) => [pin, [pin, ...labels]])
9527
+ ),
9528
+ pinLabels: Object.fromEntries(polarizedPinEntries)
9529
+ };
9530
+ };
9531
+ var normalizePinLabels = (inputPinLabels) => {
9532
+ const uniqueInputPinLabels = inputPinLabels.map((labels) => [
9533
+ ...new Set(labels)
9534
+ ]);
9535
+ const result = uniqueInputPinLabels.map(() => []);
9536
+ const desiredNumbers = uniqueInputPinLabels.map(() => null);
9537
+ for (let i = 0; i < uniqueInputPinLabels.length; i++) {
9538
+ for (const label of uniqueInputPinLabels[i]) {
9539
+ if (/^\d+$/.test(label)) {
9540
+ desiredNumbers[i] = Number.parseInt(label);
9541
+ break;
9542
+ }
9543
+ }
9544
+ }
9545
+ let highestPinNumber = 0;
9546
+ const acceptedDesiredNumbers = /* @__PURE__ */ new Set();
9547
+ for (let i = 0; i < desiredNumbers.length; i++) {
9548
+ const desiredNumber = desiredNumbers[i];
9549
+ if (desiredNumber === null || desiredNumber < 1) continue;
9550
+ if (!acceptedDesiredNumbers.has(desiredNumber)) {
9551
+ acceptedDesiredNumbers.add(desiredNumber);
9552
+ result[i].push(`pin${desiredNumber}`);
9553
+ highestPinNumber = Math.max(highestPinNumber, desiredNumber);
9554
+ continue;
9555
+ }
9556
+ let existingAltCount = 0;
9557
+ for (const label of result[i]) {
9558
+ if (label.startsWith(`pin${desiredNumber}_alt`)) {
9559
+ existingAltCount += 1;
9560
+ }
9561
+ }
9562
+ result[i].push(`pin${desiredNumber}_alt${existingAltCount + 1}`);
9563
+ }
9564
+ for (let i = 0; i < result.length; i++) {
9565
+ const firstLabel = result[i][0];
9566
+ if (firstLabel?.includes("_alt")) {
9567
+ highestPinNumber += 1;
9568
+ result[i].unshift(`pin${highestPinNumber}`);
9569
+ }
9570
+ }
9571
+ for (let i = 0; i < result.length; i++) {
9572
+ if (result[i].length === 0) {
9573
+ highestPinNumber += 1;
9574
+ result[i].push(`pin${highestPinNumber}`);
9575
+ }
9576
+ }
9577
+ const totalLabelCounts = {};
9578
+ for (const inputLabels of uniqueInputPinLabels) {
9579
+ for (const label of inputLabels) {
9580
+ if (/^\d+$/.test(label)) continue;
9581
+ totalLabelCounts[label] = (totalLabelCounts[label] ?? 0) + 1;
9582
+ }
9583
+ }
9584
+ const incrementalLabelCounts = {};
9585
+ for (let i = 0; i < uniqueInputPinLabels.length; i++) {
9586
+ for (const label of uniqueInputPinLabels[i]) {
9587
+ if (/^\d+$/.test(label)) continue;
9588
+ if (totalLabelCounts[label] === 1) {
9589
+ result[i].push(label);
9590
+ } else {
9591
+ incrementalLabelCounts[label] = (incrementalLabelCounts[label] ?? 0) + 1;
9592
+ result[i].push(`${label}${incrementalLabelCounts[label]}`);
9593
+ }
9594
+ }
9595
+ }
9596
+ return result;
9597
+ };
9598
+ var normalizeSymbolName = (name) => {
9599
+ const trimmedName = name.trim();
9600
+ if (trimmedName === "+") return "_POS";
9601
+ if (trimmedName === "-") return "_NEG";
9602
+ return trimmedName;
9603
+ };
9604
+ var categoryValueContainsDiode = (value) => {
9605
+ if (typeof value === "string") {
9606
+ return /(^|[^a-z])diodes?([^a-z]|$)/i.test(value);
9607
+ }
9608
+ if (Array.isArray(value)) {
9609
+ return value.some(categoryValueContainsDiode);
9610
+ }
9611
+ if (value && typeof value === "object") {
9612
+ return Object.values(value).some(categoryValueContainsDiode);
9613
+ }
9614
+ return false;
9615
+ };
9616
+ var isDiodeCategoryComponent = (betterEasy) => {
9617
+ const cPara = betterEasy.dataStr.head.c_para;
9618
+ return [
9619
+ betterEasy.tags,
9620
+ cPara.category,
9621
+ cPara.Category,
9622
+ cPara["LCSC Category"],
9623
+ cPara["JLCPCB Category"],
9624
+ betterEasy.category
9625
+ ].some(categoryValueContainsDiode);
9626
+ };
9627
+ var categoryValueContainsLed = (value) => {
9628
+ if (typeof value === "string") {
9629
+ return /(^|[^a-z])leds?([^a-z]|$)/i.test(value) || /light[-\s]?emitting\s+diodes?/i.test(value);
9630
+ }
9631
+ if (Array.isArray(value)) {
9632
+ return value.some(categoryValueContainsLed);
9633
+ }
9634
+ if (value && typeof value === "object") {
9635
+ return Object.values(value).some(categoryValueContainsLed);
9636
+ }
9637
+ return false;
9638
+ };
9639
+ var isLedCategoryComponent = (betterEasy) => {
9640
+ const cPara = betterEasy.dataStr.head.c_para;
9641
+ return [
9642
+ betterEasy.tags,
9643
+ cPara.category,
9644
+ cPara.Category,
9645
+ cPara["LCSC Category"],
9646
+ cPara["JLCPCB Category"],
9647
+ betterEasy.category
9648
+ ].some(categoryValueContainsLed);
9649
+ };
9496
9650
  var getBoundsCenter = (bounds) => ({
9497
9651
  x: (bounds.minX + bounds.maxX) / 2,
9498
9652
  y: (bounds.minY + bounds.maxY) / 2
@@ -9608,80 +9762,6 @@ var getCadModelOffsetMmFromBounds = (easyEdaJson, bounds, {
9608
9762
  y: modelCenter.y - targetOriginInModelFrame.y
9609
9763
  });
9610
9764
  };
9611
- var normalizePinLabels = (inputPinLabels) => {
9612
- const uniqueInputPinLabels = inputPinLabels.map((labels) => [
9613
- ...new Set(labels)
9614
- ]);
9615
- const result = uniqueInputPinLabels.map(() => []);
9616
- const desiredNumbers = uniqueInputPinLabels.map(() => null);
9617
- for (let i = 0; i < uniqueInputPinLabels.length; i++) {
9618
- for (const label of uniqueInputPinLabels[i]) {
9619
- if (/^\d+$/.test(label)) {
9620
- desiredNumbers[i] = Number.parseInt(label);
9621
- break;
9622
- }
9623
- }
9624
- }
9625
- let highestPinNumber = 0;
9626
- const acceptedDesiredNumbers = /* @__PURE__ */ new Set();
9627
- for (let i = 0; i < desiredNumbers.length; i++) {
9628
- const desiredNumber = desiredNumbers[i];
9629
- if (desiredNumber === null || desiredNumber < 1) continue;
9630
- if (!acceptedDesiredNumbers.has(desiredNumber)) {
9631
- acceptedDesiredNumbers.add(desiredNumber);
9632
- result[i].push(`pin${desiredNumber}`);
9633
- highestPinNumber = Math.max(highestPinNumber, desiredNumber);
9634
- continue;
9635
- }
9636
- let existingAltCount = 0;
9637
- for (const label of result[i]) {
9638
- if (label.startsWith(`pin${desiredNumber}_alt`)) {
9639
- existingAltCount += 1;
9640
- }
9641
- }
9642
- result[i].push(`pin${desiredNumber}_alt${existingAltCount + 1}`);
9643
- }
9644
- for (let i = 0; i < result.length; i++) {
9645
- const firstLabel = result[i][0];
9646
- if (firstLabel?.includes("_alt")) {
9647
- highestPinNumber += 1;
9648
- result[i].unshift(`pin${highestPinNumber}`);
9649
- }
9650
- }
9651
- for (let i = 0; i < result.length; i++) {
9652
- if (result[i].length === 0) {
9653
- highestPinNumber += 1;
9654
- result[i].push(`pin${highestPinNumber}`);
9655
- }
9656
- }
9657
- const totalLabelCounts = {};
9658
- for (const inputLabels of uniqueInputPinLabels) {
9659
- for (const label of inputLabels) {
9660
- if (/^\d+$/.test(label)) continue;
9661
- totalLabelCounts[label] = (totalLabelCounts[label] ?? 0) + 1;
9662
- }
9663
- }
9664
- const incrementalLabelCounts = {};
9665
- for (let i = 0; i < uniqueInputPinLabels.length; i++) {
9666
- for (const label of uniqueInputPinLabels[i]) {
9667
- if (/^\d+$/.test(label)) continue;
9668
- if (totalLabelCounts[label] === 1) {
9669
- result[i].push(label);
9670
- } else {
9671
- incrementalLabelCounts[label] = (incrementalLabelCounts[label] ?? 0) + 1;
9672
- result[i].push(`${label}${incrementalLabelCounts[label]}`);
9673
- }
9674
- }
9675
- }
9676
- return result;
9677
- };
9678
- var normalizeSymbolName = (name) => {
9679
- const trimmedName = name.trim();
9680
- if (trimmedName === "+") return "_POS";
9681
- if (trimmedName === "-") return "_NEG";
9682
- return trimmedName;
9683
- };
9684
- var DEFAULT_PCB_THICKNESS_MM = 1.6;
9685
9765
  var EASYEDA_STEP_MODEL_URL = "https://modules.easyeda.com/qAxj6KHrDKw4blvCG8QJPs7Y";
9686
9766
  var EASYEDA_OBJ_MODEL_URL = "https://modules.easyeda.com/3dmodel";
9687
9767
  var TSCIRCUIT_MODEL_CDN_URL = "https://modelcdn.tscircuit.com/easyeda_models";
@@ -9886,11 +9966,20 @@ var convertEasyEdaJsonToCircuitJson = (easyEdaJson, {
9886
9966
  return labels;
9887
9967
  });
9888
9968
  const normalizedPinLabels = normalizePinLabels(pinLabelSets);
9969
+ const normalizedPinLabelsByPin = Object.fromEntries(
9970
+ normalizedPinLabels.map((labels) => {
9971
+ const pin = labels.find((label) => /^pin\d+$/i.test(label));
9972
+ return [pin, labels.filter((label) => label !== pin)];
9973
+ })
9974
+ );
9975
+ const polarizedPinMetadata = pads.length === 2 && (isDiodeCategoryComponent(easyEdaJson) || isLedCategoryComponent(easyEdaJson)) ? getPolarizedPinMetadata(normalizedPinLabelsByPin) : void 0;
9889
9976
  pads.forEach((pad, index) => {
9890
9977
  const portHints = normalizedPinLabels[index];
9891
9978
  const pinNumber = Number.parseInt(
9892
9979
  portHints.find((hint) => hint.match(/pin\d+/)).replace("pin", "")
9893
9980
  );
9981
+ const canonicalPinName = `pin${pinNumber}`;
9982
+ const pcbPortHints = polarizedPinMetadata?.portHintsMap[canonicalPinName] ?? [canonicalPinName];
9894
9983
  circuitElements.push({
9895
9984
  type: "source_port",
9896
9985
  source_port_id: `source_port_${index + 1}`,
@@ -9907,7 +9996,7 @@ var convertEasyEdaJsonToCircuitJson = (easyEdaJson, {
9907
9996
  x: mil2mm(pad.center.x),
9908
9997
  y: mil2mm(pad.center.y),
9909
9998
  layers: ["top"],
9910
- port_hints: [`pin${pinNumber}`],
9999
+ port_hints: pcbPortHints,
9911
10000
  pcb_component_id: "pcb_component_1",
9912
10001
  pcb_port_id: `pcb_port_${index + 1}`
9913
10002
  };
@@ -10019,7 +10108,7 @@ var convertEasyEdaJsonToCircuitJson = (easyEdaJson, {
10019
10108
  radius: Math.min(mil2mm(pad.width), mil2mm(pad.height)) / 2
10020
10109
  },
10021
10110
  layer: "top",
10022
- port_hints: [`pin${pinNumber}`],
10111
+ port_hints: pcbPortHints,
10023
10112
  pcb_component_id: "pcb_component_1",
10024
10113
  pcb_port_id: `pcb_port_${index + 1}`
10025
10114
  });
@@ -10281,7 +10370,7 @@ var convertEasyEdaJsonToCircuitJson = (easyEdaJson, {
10281
10370
  };
10282
10371
  var safeNumber = (defaultValue = 0) => external_exports.union([external_exports.number(), external_exports.string()]).transform((val) => {
10283
10372
  const num = Number(val);
10284
- return isNaN(num) ? defaultValue : num;
10373
+ return Number.isNaN(num) ? defaultValue : num;
10285
10374
  }).default(defaultValue);
10286
10375
  var tenthmil = external_exports.union([external_exports.number(), external_exports.string()]).optional().transform(
10287
10376
  (n) => typeof n === "string" && n.endsWith("mil") ? n : `${Number.parseFloat(n) * 10}mil`
@@ -10428,6 +10517,7 @@ var ShapeItemSchema = external_exports.object({
10428
10517
  }
10429
10518
  case "PAD": {
10430
10519
  const [padShape, ...params] = shape.data.split("~");
10520
+ const rawPadNumber = params[6];
10431
10521
  const [
10432
10522
  centerX,
10433
10523
  centerY,
@@ -10435,10 +10525,11 @@ var ShapeItemSchema = external_exports.object({
10435
10525
  height,
10436
10526
  layermask,
10437
10527
  net,
10438
- number,
10528
+ numericPadNumber,
10439
10529
  holeRadius,
10440
10530
  ...rest
10441
10531
  ] = params.map((p) => Number.isNaN(Number(p)) ? p : Number(p));
10532
+ const padNumber = /^[+-]?(?:\d+(?:\.\d*)?|\.\d+)$/.test(rawPadNumber) ? numericPadNumber : rawPadNumber;
10442
10533
  const center = { x: centerX, y: centerY };
10443
10534
  let points;
10444
10535
  if (padShape === "RECT" || padShape === "POLYGON") {
@@ -10454,7 +10545,7 @@ var ShapeItemSchema = external_exports.object({
10454
10545
  height,
10455
10546
  layermask,
10456
10547
  net,
10457
- number,
10548
+ number: padNumber,
10458
10549
  holeRadius,
10459
10550
  points,
10460
10551
  rotation: rotation2,
@@ -10787,8 +10878,12 @@ var parsePath = (str) => {
10787
10878
  };
10788
10879
  var PathShapeSchema = external_exports.string().startsWith("PT~").transform(parsePath).pipe(PathShapeOutputSchema);
10789
10880
  var optionalEasyEdaTextField = (fieldSchema) => external_exports.preprocess((textField) => {
10790
- if (textField == null || textField === "" || textField === "undefined") {
10791
- return void 0;
10881
+ if (textField == null) return void 0;
10882
+ if (typeof textField === "string") {
10883
+ const trimmedTextField = textField.trim();
10884
+ if (trimmedTextField === "" || trimmedTextField === "undefined") {
10885
+ return void 0;
10886
+ }
10792
10887
  }
10793
10888
  return textField;
10794
10889
  }, fieldSchema);
@@ -10901,7 +10996,7 @@ var OwnerSchema = external_exports.object({
10901
10996
  });
10902
10997
  var HeadSchema = external_exports.object({
10903
10998
  docType: external_exports.preprocess((val) => val == null ? val : String(val), external_exports.string()),
10904
- editorVersion: external_exports.string(),
10999
+ editorVersion: external_exports.string().default(""),
10905
11000
  c_para: external_exports.record(external_exports.string(), external_exports.string().nullable()),
10906
11001
  x: external_exports.number(),
10907
11002
  y: external_exports.number(),
@@ -10914,7 +11009,7 @@ var HeadSchema = external_exports.object({
10914
11009
  }, external_exports.number()),
10915
11010
  importFlag: external_exports.number().optional(),
10916
11011
  c_spiceCmd: external_exports.any().optional(),
10917
- hasIdFlag: external_exports.boolean()
11012
+ hasIdFlag: external_exports.boolean().default(false)
10918
11013
  });
10919
11014
  var BBoxSchema = external_exports.object({
10920
11015
  x: external_exports.number(),
@@ -10939,9 +11034,17 @@ var DataStrSchema = external_exports.object({
10939
11034
  head: HeadSchema,
10940
11035
  canvas: external_exports.string(),
10941
11036
  shape: external_exports.array(SingleLetterShapeSchema),
10942
- BBox: BBoxSchema,
10943
- colors: external_exports.union([external_exports.array(external_exports.string()), external_exports.record(external_exports.string())])
10944
- });
11037
+ BBox: BBoxSchema.optional(),
11038
+ colors: external_exports.union([external_exports.array(external_exports.string()), external_exports.record(external_exports.string())]).default([])
11039
+ }).transform((data) => ({
11040
+ ...data,
11041
+ BBox: data.BBox ?? {
11042
+ x: data.head.x,
11043
+ y: data.head.y,
11044
+ width: 0,
11045
+ height: 0
11046
+ }
11047
+ }));
10945
11048
  var PackageDetailDataStrSchema = external_exports.object({
10946
11049
  head: HeadSchema,
10947
11050
  canvas: external_exports.string(),
@@ -11122,9 +11225,24 @@ async function fetchEasyEDAComponent(jlcpcbPartNumber, {
11122
11225
  if (!searchResult.success || !searchResult.result.lists.lcsc.length) {
11123
11226
  throw new Error("Component not found");
11124
11227
  }
11228
+ const requestedPartNumber = jlcpcbPartNumber.trim().toUpperCase();
11125
11229
  const bestMatchComponent = searchResult.result.lists.lcsc.find(
11126
- (component) => component.dataStr.head.c_para["Supplier Part"] === jlcpcbPartNumber
11127
- ) ?? searchResult.result.lists.lcsc[0];
11230
+ (component) => {
11231
+ const candidatePartNumbers = [
11232
+ component.dataStr?.head?.c_para?.["Supplier Part"],
11233
+ component.lcsc?.number,
11234
+ component.szlcsc?.number
11235
+ ];
11236
+ return candidatePartNumbers.some(
11237
+ (partNumber) => typeof partNumber === "string" && partNumber.trim().toUpperCase() === requestedPartNumber
11238
+ );
11239
+ }
11240
+ );
11241
+ if (!bestMatchComponent) {
11242
+ throw new Error(
11243
+ `No exact EasyEDA component match for "${jlcpcbPartNumber}"`
11244
+ );
11245
+ }
11128
11246
  const componentUUID = bestMatchComponent.uuid;
11129
11247
  const componentResponse = await fetch2(componentUrl(componentUUID), {
11130
11248
  method: "GET",
@@ -11725,15 +11843,200 @@ var DigiKeyPartsEngine = class {
11725
11843
 
11726
11844
  // lib/digikey-parts-engine/index.ts
11727
11845
  var digikeyPartsEngine = new DigiKeyPartsEngine();
11846
+
11847
+ // lib/mouser-parts-engine/mouser-parts-cache.ts
11848
+ var mouserCache = /* @__PURE__ */ new Map();
11849
+ var normalizeBaseUrl2 = (baseUrl) => baseUrl.replace(/\/$/, "");
11850
+ var getMouserPartsCached = async (path, params, options = {}) => {
11851
+ const platformFetch = options.platformFetch ?? globalThis.fetch;
11852
+ const baseUrl = normalizeBaseUrl2(
11853
+ options.apiBaseUrl ?? "https://mousersearch.tscircuit.com"
11854
+ );
11855
+ const url = new URL(path, `${baseUrl}/`);
11856
+ for (const [name, value] of Object.entries(params)) {
11857
+ if (value !== void 0 && value !== "") {
11858
+ url.searchParams.set(name, String(value));
11859
+ }
11860
+ }
11861
+ url.searchParams.set("json", "true");
11862
+ const cacheKey = url.toString();
11863
+ const cached = mouserCache.get(cacheKey);
11864
+ if (cached) return cached;
11865
+ const response = await platformFetch(url);
11866
+ if (!response.ok) {
11867
+ throw new Error(
11868
+ `Mouser search failed (${response.status}): ${await response.text()}`
11869
+ );
11870
+ }
11871
+ const responseJson = await response.json();
11872
+ mouserCache.set(cacheKey, responseJson);
11873
+ return responseJson;
11874
+ };
11875
+ var withMouserStockPreference = (parts) => [...parts ?? []].sort(
11876
+ (a, b) => Number(b.normally_stocking ?? false) - Number(a.normally_stocking ?? false) || (b.stock ?? 0) - (a.stock ?? 0)
11877
+ );
11878
+
11879
+ // lib/mouser-parts-engine/MouserPartsEngine.ts
11880
+ var MouserPartsEngine = class {
11881
+ platformFetch;
11882
+ apiBaseUrl;
11883
+ constructor(options = {}) {
11884
+ this.platformFetch = options.platformFetch;
11885
+ this.apiBaseUrl = options.apiBaseUrl;
11886
+ this.findPart = this.findPart.bind(this);
11887
+ }
11888
+ async getCategoryParts(path, responseKey, params) {
11889
+ const response = await getMouserPartsCached(path, params, {
11890
+ platformFetch: this.platformFetch,
11891
+ apiBaseUrl: this.apiBaseUrl
11892
+ });
11893
+ return response[responseKey] ?? [];
11894
+ }
11895
+ async getKeywordParts(query) {
11896
+ const response = await getMouserPartsCached(
11897
+ "/api/search",
11898
+ { q: query, limit: 20 },
11899
+ {
11900
+ platformFetch: this.platformFetch,
11901
+ apiBaseUrl: this.apiBaseUrl
11902
+ }
11903
+ );
11904
+ return response.components ?? [];
11905
+ }
11906
+ toSupplierPartNumbers(parts) {
11907
+ return {
11908
+ mouser: withMouserStockPreference(parts).map((part) => part.mouser_product_number).filter(Boolean).slice(0, 3)
11909
+ };
11910
+ }
11911
+ async findPart({
11912
+ sourceComponent,
11913
+ footprinterString
11914
+ }) {
11915
+ if (sourceComponent.type !== "source_component") return {};
11916
+ const packageName = getJlcpcbPackageName(footprinterString);
11917
+ if (sourceComponent.ftype === "simple_resistor") {
11918
+ return this.toSupplierPartNumbers(
11919
+ await this.getCategoryParts("/resistors/list", "resistors", {
11920
+ resistance: sourceComponent.resistance,
11921
+ package: packageName
11922
+ })
11923
+ );
11924
+ }
11925
+ if (sourceComponent.ftype === "simple_capacitor") {
11926
+ return this.toSupplierPartNumbers(
11927
+ await this.getCategoryParts("/capacitors/list", "capacitors", {
11928
+ capacitance: sourceComponent.capacitance,
11929
+ package: packageName
11930
+ })
11931
+ );
11932
+ }
11933
+ if (sourceComponent.ftype === "simple_pin_header") {
11934
+ return this.toSupplierPartNumbers(
11935
+ await this.getCategoryParts(
11936
+ "/headers/list",
11937
+ "headers",
11938
+ getPinHeaderSearchParams(sourceComponent, footprinterString)
11939
+ )
11940
+ );
11941
+ }
11942
+ if (sourceComponent.ftype === "simple_potentiometer") {
11943
+ return this.toSupplierPartNumbers(
11944
+ await this.getCategoryParts("/potentiometers/list", "potentiometers", {
11945
+ resistance: sourceComponent.max_resistance,
11946
+ package: packageName
11947
+ })
11948
+ );
11949
+ }
11950
+ if (sourceComponent.ftype === "simple_diode") {
11951
+ return this.toSupplierPartNumbers(
11952
+ await this.getCategoryParts("/diodes/list", "diodes", {
11953
+ package: packageName
11954
+ })
11955
+ );
11956
+ }
11957
+ if (sourceComponent.ftype === "simple_transistor") {
11958
+ return this.toSupplierPartNumbers(
11959
+ await this.getCategoryParts(
11960
+ "/bjt_transistors/list",
11961
+ "bjt_transistors",
11962
+ { package: packageName }
11963
+ )
11964
+ );
11965
+ }
11966
+ if (sourceComponent.ftype === "simple_mosfet") {
11967
+ return this.toSupplierPartNumbers(
11968
+ await this.getCategoryParts("/mosfets/list", "mosfets", {
11969
+ package: packageName,
11970
+ channel_type: sourceComponent.channel_type,
11971
+ mosfet_mode: sourceComponent.mosfet_mode
11972
+ })
11973
+ );
11974
+ }
11975
+ if (sourceComponent.ftype === "simple_switch") {
11976
+ return this.toSupplierPartNumbers(
11977
+ await this.getCategoryParts("/switches/list", "switches", {
11978
+ package: packageName
11979
+ })
11980
+ );
11981
+ }
11982
+ if (sourceComponent.ftype === "simple_led") {
11983
+ return this.toSupplierPartNumbers(
11984
+ await this.getCategoryParts("/leds/list", "leds", {
11985
+ package: packageName
11986
+ })
11987
+ );
11988
+ }
11989
+ if (sourceComponent.ftype === "simple_fuse") {
11990
+ return this.toSupplierPartNumbers(
11991
+ await this.getCategoryParts("/fuses/list", "fuses", {
11992
+ package: packageName
11993
+ })
11994
+ );
11995
+ }
11996
+ if (sourceComponent.ftype === "simple_connector" && sourceComponent.standard === "usb_c") {
11997
+ return this.toSupplierPartNumbers(
11998
+ await this.getCategoryParts(
11999
+ "/usb_c_connectors/list",
12000
+ "usb_c_connectors",
12001
+ { package: packageName }
12002
+ )
12003
+ );
12004
+ }
12005
+ const keywordByFtype = {
12006
+ simple_chip: "integrated circuit",
12007
+ simple_power_source: "power supply",
12008
+ simple_inductor: "inductor",
12009
+ simple_crystal: "crystal",
12010
+ simple_resonator: "resonator"
12011
+ };
12012
+ const keyword = keywordByFtype[sourceComponent.ftype];
12013
+ if (keyword) {
12014
+ return this.toSupplierPartNumbers(
12015
+ await this.getKeywordParts(
12016
+ [keyword, packageName].filter(Boolean).join(" ")
12017
+ )
12018
+ );
12019
+ }
12020
+ return {};
12021
+ }
12022
+ };
12023
+
12024
+ // lib/mouser-parts-engine/index.ts
12025
+ var mouserPartsEngine = new MouserPartsEngine();
11728
12026
  export {
11729
12027
  DigiKeyPartsEngine,
11730
12028
  JlcPcbPartsEngine,
12029
+ MouserPartsEngine,
11731
12030
  cache,
11732
12031
  digikeyCache,
11733
12032
  digikeyPartsEngine,
11734
12033
  getDigiKeyPartsCached,
11735
12034
  getFetchWithEasyEdaProxy,
12035
+ getMouserPartsCached,
11736
12036
  jlcPartsEngine,
11737
- withDigiKeyStockPreference
12037
+ mouserCache,
12038
+ mouserPartsEngine,
12039
+ withDigiKeyStockPreference,
12040
+ withMouserStockPreference
11738
12041
  };
11739
12042
  //# sourceMappingURL=index.js.map