@tscircuit/parts-engine 0.0.24 → 0.0.26

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
@@ -1,2 +1,19 @@
1
1
  # parts-engine
2
- The tscircuit platform parts engine
2
+
3
+ The tscircuit platform parts engine.
4
+
5
+ ## Supplier engines
6
+
7
+ ```ts
8
+ import {
9
+ digikeyPartsEngine,
10
+ jlcPartsEngine,
11
+ DigiKeyPartsEngine,
12
+ } from "@tscircuit/parts-engine"
13
+ ```
14
+
15
+ `digikeyPartsEngine.findPart(...)` queries
16
+ `https://digikeysearch.tscircuit.com`, which provides the same category route
17
+ shape as jlcsearch while caching DigiKey Product Information V4 calls. Use
18
+ `new DigiKeyPartsEngine({ platformFetch, apiBaseUrl })` to inject a platform
19
+ fetch implementation or a test/self-hosted endpoint.
package/dist/index.d.ts CHANGED
@@ -4374,4 +4374,39 @@ declare const getFetchWithEasyEdaProxy: ({ platformFetch: upstreamFetch, easyEda
4374
4374
 
4375
4375
  declare const jlcPartsEngine: JlcPcbPartsEngine;
4376
4376
 
4377
- export { type EasyEdaProxyConfig, type FetchPartCircuitJsonParams, JlcPcbPartsEngine, type JlcPcbPartsEngineOptions, type PlatformFetch, cache, getFetchWithEasyEdaProxy, jlcPartsEngine };
4377
+ type DigiKeyPartsEngineOptions = {
4378
+ platformFetch?: PlatformFetch;
4379
+ apiBaseUrl?: string;
4380
+ };
4381
+ type DigiKeySearchPart = {
4382
+ digikey_product_number: string;
4383
+ supplier_part_number?: string;
4384
+ mfr: string;
4385
+ manufacturer?: string;
4386
+ package?: string;
4387
+ description?: string;
4388
+ stock: number;
4389
+ price?: number;
4390
+ normally_stocking?: boolean;
4391
+ };
4392
+
4393
+ declare class DigiKeyPartsEngine implements PartsEngine {
4394
+ private readonly platformFetch;
4395
+ private readonly apiBaseUrl;
4396
+ constructor(options?: DigiKeyPartsEngineOptions);
4397
+ private getCategoryParts;
4398
+ private getKeywordParts;
4399
+ private toSupplierPartNumbers;
4400
+ findPart({ sourceComponent, footprinterString, }: Parameters<PartsEngine["findPart"]>[0]): Promise<{}>;
4401
+ }
4402
+
4403
+ declare const digikeyCache: Map<string, unknown>;
4404
+ declare const getDigiKeyPartsCached: (path: string, params: Record<string, string | number | boolean | undefined>, options?: {
4405
+ platformFetch?: PlatformFetch;
4406
+ apiBaseUrl?: string;
4407
+ }) => Promise<Record<string, DigiKeySearchPart[]>>;
4408
+ declare const withDigiKeyStockPreference: (parts: DigiKeySearchPart[] | undefined) => DigiKeySearchPart[];
4409
+
4410
+ declare const digikeyPartsEngine: DigiKeyPartsEngine;
4411
+
4412
+ export { DigiKeyPartsEngine, type DigiKeyPartsEngineOptions, type DigiKeySearchPart, type EasyEdaProxyConfig, type FetchPartCircuitJsonParams, JlcPcbPartsEngine, type JlcPcbPartsEngineOptions, type PlatformFetch, cache, digikeyCache, digikeyPartsEngine, getDigiKeyPartsCached, getFetchWithEasyEdaProxy, jlcPartsEngine, withDigiKeyStockPreference };
package/dist/index.js CHANGED
@@ -11282,12 +11282,18 @@ var getFetchWithEasyEdaProxy = ({
11282
11282
  const proxyRequestBody = METHODS_WITHOUT_BODY.has(
11283
11283
  mergedTargetRequest.method
11284
11284
  ) ? void 0 : await mergedTargetRequest.clone().arrayBuffer();
11285
- return upstreamFetch(easyEdaProxyConfig.proxyEndpointUrl, {
11285
+ const response = await upstreamFetch(easyEdaProxyConfig.proxyEndpointUrl, {
11286
11286
  method: mergedTargetRequest.method,
11287
11287
  headers: proxyRequestHeaders,
11288
11288
  body: proxyRequestBody,
11289
11289
  signal: requestInit?.signal
11290
11290
  });
11291
+ if (response.status === 401) {
11292
+ const proxyErrorResponse = await response.json().catch(() => null);
11293
+ const errorCode = typeof proxyErrorResponse === "object" && proxyErrorResponse !== null && "error_code" in proxyErrorResponse && typeof proxyErrorResponse.error_code === "string" ? proxyErrorResponse.error_code : "unauthorized";
11294
+ throw new Error(`EasyEDA proxy request failed: ${errorCode} (HTTP 401)`);
11295
+ }
11296
+ return response;
11291
11297
  };
11292
11298
  };
11293
11299
 
@@ -11539,10 +11545,195 @@ var JlcPcbPartsEngine = class {
11539
11545
 
11540
11546
  // lib/jlc-parts-engine/index.ts
11541
11547
  var jlcPartsEngine = new JlcPcbPartsEngine();
11548
+
11549
+ // lib/digikey-parts-engine/digikey-parts-cache.ts
11550
+ var digikeyCache = /* @__PURE__ */ new Map();
11551
+ var normalizeBaseUrl = (baseUrl) => baseUrl.replace(/\/$/, "");
11552
+ var getDigiKeyPartsCached = async (path, params, options = {}) => {
11553
+ const platformFetch = options.platformFetch ?? globalThis.fetch;
11554
+ const baseUrl = normalizeBaseUrl(
11555
+ options.apiBaseUrl ?? "https://digikeysearch.tscircuit.com"
11556
+ );
11557
+ const url = new URL(path, `${baseUrl}/`);
11558
+ for (const [name, value] of Object.entries(params)) {
11559
+ if (value !== void 0 && value !== "") {
11560
+ url.searchParams.set(name, String(value));
11561
+ }
11562
+ }
11563
+ url.searchParams.set("json", "true");
11564
+ const cacheKey = url.toString();
11565
+ const cached = digikeyCache.get(cacheKey);
11566
+ if (cached) return cached;
11567
+ const response = await platformFetch(url);
11568
+ if (!response.ok) {
11569
+ throw new Error(
11570
+ `DigiKey search failed (${response.status}): ${await response.text()}`
11571
+ );
11572
+ }
11573
+ const responseJson = await response.json();
11574
+ digikeyCache.set(cacheKey, responseJson);
11575
+ return responseJson;
11576
+ };
11577
+ var withDigiKeyStockPreference = (parts) => [...parts ?? []].sort(
11578
+ (a, b) => Number(b.normally_stocking ?? false) - Number(a.normally_stocking ?? false) || (b.stock ?? 0) - (a.stock ?? 0)
11579
+ );
11580
+
11581
+ // lib/digikey-parts-engine/DigiKeyPartsEngine.ts
11582
+ var DigiKeyPartsEngine = class {
11583
+ platformFetch;
11584
+ apiBaseUrl;
11585
+ constructor(options = {}) {
11586
+ this.platformFetch = options.platformFetch;
11587
+ this.apiBaseUrl = options.apiBaseUrl;
11588
+ this.findPart = this.findPart.bind(this);
11589
+ }
11590
+ async getCategoryParts(path, responseKey, params) {
11591
+ const response = await getDigiKeyPartsCached(path, params, {
11592
+ platformFetch: this.platformFetch,
11593
+ apiBaseUrl: this.apiBaseUrl
11594
+ });
11595
+ return response[responseKey] ?? [];
11596
+ }
11597
+ async getKeywordParts(query) {
11598
+ const response = await getDigiKeyPartsCached(
11599
+ "/api/search",
11600
+ { q: query, limit: 20 },
11601
+ {
11602
+ platformFetch: this.platformFetch,
11603
+ apiBaseUrl: this.apiBaseUrl
11604
+ }
11605
+ );
11606
+ return response.components ?? [];
11607
+ }
11608
+ toSupplierPartNumbers(parts) {
11609
+ return {
11610
+ digikey: withDigiKeyStockPreference(parts).map((part) => part.digikey_product_number).filter(Boolean).slice(0, 3)
11611
+ };
11612
+ }
11613
+ async findPart({
11614
+ sourceComponent,
11615
+ footprinterString
11616
+ }) {
11617
+ if (sourceComponent.type !== "source_component") return {};
11618
+ const packageName = getJlcpcbPackageName(footprinterString);
11619
+ if (sourceComponent.ftype === "simple_resistor") {
11620
+ return this.toSupplierPartNumbers(
11621
+ await this.getCategoryParts("/resistors/list", "resistors", {
11622
+ resistance: sourceComponent.resistance,
11623
+ package: packageName
11624
+ })
11625
+ );
11626
+ }
11627
+ if (sourceComponent.ftype === "simple_capacitor") {
11628
+ return this.toSupplierPartNumbers(
11629
+ await this.getCategoryParts("/capacitors/list", "capacitors", {
11630
+ capacitance: sourceComponent.capacitance,
11631
+ package: packageName
11632
+ })
11633
+ );
11634
+ }
11635
+ if (sourceComponent.ftype === "simple_pin_header") {
11636
+ return this.toSupplierPartNumbers(
11637
+ await this.getCategoryParts(
11638
+ "/headers/list",
11639
+ "headers",
11640
+ getPinHeaderSearchParams(sourceComponent, footprinterString)
11641
+ )
11642
+ );
11643
+ }
11644
+ if (sourceComponent.ftype === "simple_potentiometer") {
11645
+ return this.toSupplierPartNumbers(
11646
+ await this.getCategoryParts("/potentiometers/list", "potentiometers", {
11647
+ resistance: sourceComponent.max_resistance,
11648
+ package: packageName
11649
+ })
11650
+ );
11651
+ }
11652
+ if (sourceComponent.ftype === "simple_diode") {
11653
+ return this.toSupplierPartNumbers(
11654
+ await this.getCategoryParts("/diodes/list", "diodes", {
11655
+ package: packageName
11656
+ })
11657
+ );
11658
+ }
11659
+ if (sourceComponent.ftype === "simple_transistor") {
11660
+ return this.toSupplierPartNumbers(
11661
+ await this.getCategoryParts(
11662
+ "/bjt_transistors/list",
11663
+ "bjt_transistors",
11664
+ { package: packageName }
11665
+ )
11666
+ );
11667
+ }
11668
+ if (sourceComponent.ftype === "simple_mosfet") {
11669
+ return this.toSupplierPartNumbers(
11670
+ await this.getCategoryParts("/mosfets/list", "mosfets", {
11671
+ package: packageName,
11672
+ channel_type: sourceComponent.channel_type,
11673
+ mosfet_mode: sourceComponent.mosfet_mode
11674
+ })
11675
+ );
11676
+ }
11677
+ if (sourceComponent.ftype === "simple_switch") {
11678
+ return this.toSupplierPartNumbers(
11679
+ await this.getCategoryParts("/switches/list", "switches", {
11680
+ package: packageName
11681
+ })
11682
+ );
11683
+ }
11684
+ if (sourceComponent.ftype === "simple_led") {
11685
+ return this.toSupplierPartNumbers(
11686
+ await this.getCategoryParts("/leds/list", "leds", {
11687
+ package: packageName
11688
+ })
11689
+ );
11690
+ }
11691
+ if (sourceComponent.ftype === "simple_fuse") {
11692
+ return this.toSupplierPartNumbers(
11693
+ await this.getCategoryParts("/fuses/list", "fuses", {
11694
+ package: packageName
11695
+ })
11696
+ );
11697
+ }
11698
+ if (sourceComponent.ftype === "simple_connector" && sourceComponent.standard === "usb_c") {
11699
+ return this.toSupplierPartNumbers(
11700
+ await this.getCategoryParts(
11701
+ "/usb_c_connectors/list",
11702
+ "usb_c_connectors",
11703
+ { package: packageName }
11704
+ )
11705
+ );
11706
+ }
11707
+ const keywordByFtype = {
11708
+ simple_chip: "integrated circuit",
11709
+ simple_power_source: "power supply",
11710
+ simple_inductor: "inductor",
11711
+ simple_crystal: "crystal",
11712
+ simple_resonator: "resonator"
11713
+ };
11714
+ const keyword = keywordByFtype[sourceComponent.ftype];
11715
+ if (keyword) {
11716
+ return this.toSupplierPartNumbers(
11717
+ await this.getKeywordParts(
11718
+ [keyword, packageName].filter(Boolean).join(" ")
11719
+ )
11720
+ );
11721
+ }
11722
+ return {};
11723
+ }
11724
+ };
11725
+
11726
+ // lib/digikey-parts-engine/index.ts
11727
+ var digikeyPartsEngine = new DigiKeyPartsEngine();
11542
11728
  export {
11729
+ DigiKeyPartsEngine,
11543
11730
  JlcPcbPartsEngine,
11544
11731
  cache,
11732
+ digikeyCache,
11733
+ digikeyPartsEngine,
11734
+ getDigiKeyPartsCached,
11545
11735
  getFetchWithEasyEdaProxy,
11546
- jlcPartsEngine
11736
+ jlcPartsEngine,
11737
+ withDigiKeyStockPreference
11547
11738
  };
11548
11739
  //# sourceMappingURL=index.js.map