@tscircuit/parts-engine 0.0.15 → 0.0.17

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.
@@ -1,25 +0,0 @@
1
- import { getFootprinterStringFromKicad } from "./get-footprinter-string-from-kicad"
2
- import { getJlcPackageFromFootprinterString } from "./get-jlc-package-from-footprinter-string"
3
-
4
- /**
5
- * Get a JLC-compatible package name from a footprint string, which could be
6
- * a KiCad footprint or a generic "footprinter string".
7
- */
8
- export const getJlcpcbPackageName = (
9
- footprint: string | undefined,
10
- ): string | undefined => {
11
- if (!footprint) return undefined
12
-
13
- if (footprint.startsWith("kicad:")) {
14
- const footprinterString = getFootprinterStringFromKicad(footprint)
15
- if (footprinterString) {
16
- return getJlcPackageFromFootprinterString(footprinterString)
17
- }
18
-
19
- // Fallback for un-matched KiCad strings
20
- return footprint
21
- }
22
-
23
- // Not a KiCad string, assume it's a footprinter string
24
- return getJlcPackageFromFootprinterString(footprint)
25
- }
@@ -1,291 +0,0 @@
1
- import type { PartsEngine, SupplierPartNumbers } from "@tscircuit/props"
2
- import { getJlcpcbPackageName } from "./footprint-translators/index"
3
- import {
4
- fetchEasyEDAComponent,
5
- EasyEdaJsonSchema,
6
- convertEasyEdaJsonToCircuitJson,
7
- } from "easyeda/browser"
8
-
9
- export const cache = new Map<string, any>()
10
-
11
- const getJlcPartsCached = async (name: any, params: any) => {
12
- const paramString = new URLSearchParams({
13
- ...params,
14
- json: "true",
15
- }).toString()
16
- if (cache.has(paramString)) {
17
- return cache.get(paramString)
18
- }
19
- const response = await fetch(
20
- `https://jlcsearch.tscircuit.com/${name}/list?${paramString}`,
21
- )
22
- const responseJson = await response.json()
23
- cache.set(paramString, responseJson)
24
- return responseJson
25
- }
26
-
27
- const withBasicPartPreference = (parts: any[] | undefined) => {
28
- if (!parts) return []
29
- return [...parts].sort(
30
- (a, b) => Number(b.is_basic ?? false) - Number(a.is_basic ?? false),
31
- )
32
- }
33
-
34
- export const jlcPartsEngine: PartsEngine = {
35
- findPart: async ({
36
- sourceComponent,
37
- footprinterString,
38
- }): Promise<SupplierPartNumbers> => {
39
- const jlcpcbPackage = getJlcpcbPackageName(footprinterString)
40
-
41
- if (
42
- sourceComponent.type === "source_component" &&
43
- sourceComponent.ftype === "simple_resistor"
44
- ) {
45
- const { resistors } = await getJlcPartsCached("resistors", {
46
- resistance: sourceComponent.resistance,
47
- package: jlcpcbPackage,
48
- })
49
-
50
- return {
51
- jlcpcb: withBasicPartPreference(resistors)
52
- .map((r: any) => `C${r.lcsc}`)
53
- .slice(0, 3),
54
- }
55
- } else if (
56
- sourceComponent.type === "source_component" &&
57
- sourceComponent.ftype === "simple_capacitor"
58
- ) {
59
- const { capacitors } = await getJlcPartsCached("capacitors", {
60
- capacitance: sourceComponent.capacitance,
61
- package: jlcpcbPackage,
62
- })
63
-
64
- return {
65
- jlcpcb: withBasicPartPreference(capacitors)
66
- .map((c: any) => `C${c.lcsc}`)
67
- .slice(0, 3),
68
- }
69
- } else if (
70
- sourceComponent.type === "source_component" &&
71
- sourceComponent.ftype === "simple_pin_header"
72
- ) {
73
- let pitch: number | undefined
74
- if (footprinterString?.includes("_p")) {
75
- pitch = Number(footprinterString.split("_p")[1])
76
- }
77
- const { headers } = await getJlcPartsCached(
78
- "headers",
79
- pitch
80
- ? {
81
- pitch: pitch,
82
- num_pins: sourceComponent.pin_count,
83
- gender: sourceComponent.gender,
84
- }
85
- : {
86
- num_pins: sourceComponent.pin_count,
87
- gender: sourceComponent.gender,
88
- },
89
- )
90
- return {
91
- jlcpcb: withBasicPartPreference(headers)
92
- .map((h: any) => `C${h.lcsc}`)
93
- .slice(0, 3),
94
- }
95
- } else if (
96
- sourceComponent.type === "source_component" &&
97
- sourceComponent.ftype === "simple_potentiometer"
98
- ) {
99
- const { potentiometers } = await getJlcPartsCached("potentiometers", {
100
- resistance: sourceComponent.max_resistance,
101
- package: jlcpcbPackage,
102
- })
103
- return {
104
- jlcpcb: withBasicPartPreference(potentiometers)
105
- .map((p: any) => `C${p.lcsc}`)
106
- .slice(0, 3),
107
- }
108
- } else if (
109
- sourceComponent.type === "source_component" &&
110
- sourceComponent.ftype === "simple_diode"
111
- ) {
112
- const { diodes } = await getJlcPartsCached("diodes", {
113
- package: jlcpcbPackage,
114
- })
115
- return {
116
- jlcpcb: withBasicPartPreference(diodes)
117
- .map((d: any) => `C${d.lcsc}`)
118
- .slice(0, 3),
119
- }
120
- } else if (
121
- sourceComponent.type === "source_component" &&
122
- sourceComponent.ftype === "simple_chip"
123
- ) {
124
- if (!jlcpcbPackage || !footprinterString) {
125
- return {}
126
- }
127
- const { chips } = await getJlcPartsCached("chips", {
128
- package: jlcpcbPackage,
129
- })
130
- return {
131
- jlcpcb: withBasicPartPreference(chips)
132
- .map((c: any) => `C${c.lcsc}`)
133
- .slice(0, 3),
134
- }
135
- } else if (
136
- sourceComponent.type === "source_component" &&
137
- sourceComponent.ftype === "simple_transistor"
138
- ) {
139
- const { transistors } = await getJlcPartsCached("transistors", {
140
- package: jlcpcbPackage,
141
- transistor_type: sourceComponent.transistor_type,
142
- })
143
- return {
144
- jlcpcb: withBasicPartPreference(transistors)
145
- .map((t: any) => `C${t.lcsc}`)
146
- .slice(0, 3),
147
- }
148
- } else if (
149
- sourceComponent.type === "source_component" &&
150
- sourceComponent.ftype === "simple_power_source"
151
- ) {
152
- const { power_sources } = await getJlcPartsCached("power_sources", {
153
- voltage: sourceComponent.voltage,
154
- package: jlcpcbPackage,
155
- })
156
- return {
157
- jlcpcb: withBasicPartPreference(power_sources)
158
- .map((p: any) => `C${p.lcsc}`)
159
- .slice(0, 3),
160
- }
161
- } else if (
162
- sourceComponent.type === "source_component" &&
163
- sourceComponent.ftype === "simple_inductor"
164
- ) {
165
- const { inductors } = await getJlcPartsCached("inductors", {
166
- inductance: sourceComponent.inductance,
167
- package: jlcpcbPackage,
168
- })
169
- return {
170
- jlcpcb: withBasicPartPreference(inductors)
171
- .map((i: any) => `C${i.lcsc}`)
172
- .slice(0, 3),
173
- }
174
- } else if (
175
- sourceComponent.type === "source_component" &&
176
- sourceComponent.ftype === "simple_crystal"
177
- ) {
178
- const { crystals } = await getJlcPartsCached("crystals", {
179
- frequency: sourceComponent.frequency,
180
- load_capacitance: sourceComponent.load_capacitance,
181
- package: jlcpcbPackage,
182
- })
183
- return {
184
- jlcpcb: withBasicPartPreference(crystals)
185
- .map((c: any) => `C${c.lcsc}`)
186
- .slice(0, 3),
187
- }
188
- } else if (
189
- sourceComponent.type === "source_component" &&
190
- sourceComponent.ftype === "simple_mosfet"
191
- ) {
192
- const { mosfets } = await getJlcPartsCached("mosfets", {
193
- package: jlcpcbPackage,
194
- mosfet_mode: sourceComponent.mosfet_mode,
195
- channel_type: sourceComponent.channel_type,
196
- })
197
- return {
198
- jlcpcb: withBasicPartPreference(mosfets)
199
- .map((m: any) => `C${m.lcsc}`)
200
- .slice(0, 3),
201
- }
202
- } else if (
203
- sourceComponent.type === "source_component" &&
204
- sourceComponent.ftype === "simple_resonator"
205
- ) {
206
- const { resonators } = await getJlcPartsCached("resonators", {
207
- frequency: sourceComponent.frequency,
208
- package: jlcpcbPackage,
209
- })
210
- return {
211
- jlcpcb: withBasicPartPreference(resonators)
212
- .map((r: any) => `C${r.lcsc}`)
213
- .slice(0, 3),
214
- }
215
- } else if (
216
- sourceComponent.type === "source_component" &&
217
- sourceComponent.ftype === "simple_switch"
218
- ) {
219
- const { switches } = await getJlcPartsCached("switches", {
220
- switch_type: sourceComponent.type,
221
- package: jlcpcbPackage,
222
- })
223
- return {
224
- jlcpcb: withBasicPartPreference(switches)
225
- .map((s: any) => `C${s.lcsc}`)
226
- .slice(0, 3),
227
- }
228
- } else if (
229
- sourceComponent.type === "source_component" &&
230
- sourceComponent.ftype === "simple_led"
231
- ) {
232
- const { leds } = await getJlcPartsCached("leds", {
233
- package: jlcpcbPackage,
234
- })
235
- return {
236
- jlcpcb: withBasicPartPreference(leds)
237
- .map((l: any) => `C${l.lcsc}`)
238
- .slice(0, 3),
239
- }
240
- } else if (
241
- sourceComponent.type === "source_component" &&
242
- sourceComponent.ftype === "simple_fuse"
243
- ) {
244
- const { fuses } = await getJlcPartsCached("fuses", {
245
- package: jlcpcbPackage,
246
- })
247
- return {
248
- jlcpcb: withBasicPartPreference(fuses)
249
- .map((l: any) => `C${l.lcsc}`)
250
- .slice(0, 3),
251
- }
252
- } else if (
253
- sourceComponent.type === "source_component" &&
254
- sourceComponent.ftype === "simple_connector" &&
255
- sourceComponent.standard === "usb_c"
256
- ) {
257
- const { usb_c_connectors } = await getJlcPartsCached(
258
- "usb_c_connectors",
259
- {},
260
- )
261
- return {
262
- jlcpcb: withBasicPartPreference(usb_c_connectors)
263
- .map((c: any) => `C${c.lcsc}`)
264
- .slice(0, 3),
265
- }
266
- }
267
- return {}
268
- },
269
-
270
- fetchPartCircuitJson: async ({
271
- supplierPartNumber,
272
- manufacturerPartNumber,
273
- }) => {
274
- let resolvedPartNumber = supplierPartNumber
275
-
276
- if (!resolvedPartNumber && manufacturerPartNumber) {
277
- const { components } = await getJlcPartsCached("components", {
278
- search: manufacturerPartNumber,
279
- })
280
- resolvedPartNumber = components?.[0]
281
- ? `C${components[0].lcsc}`
282
- : undefined
283
- }
284
-
285
- if (!resolvedPartNumber) return undefined
286
-
287
- const rawEasyEdaJson = await fetchEasyEDAComponent(resolvedPartNumber)
288
- const parsed = EasyEdaJsonSchema.parse(rawEasyEdaJson)
289
- return convertEasyEdaJsonToCircuitJson(parsed)
290
- },
291
- }
@@ -1,33 +0,0 @@
1
- import { test, expect } from "bun:test"
2
- import { jlcPartsEngine } from "../lib/jlc-parts-engine"
3
-
4
- test("fetchPartCircuitJson returns circuit json for USB-C connector (C165948)", async () => {
5
- const result = await jlcPartsEngine.fetchPartCircuitJson!({
6
- supplierPartNumber: "C165948",
7
- })
8
-
9
- expect(result).toBeDefined()
10
- expect(Array.isArray(result)).toBe(true)
11
-
12
- const types = result!.map((el: any) => el.type)
13
-
14
- // Has PCB elements
15
- expect(types.filter((t: string) => t === "pcb_smtpad").length).toBe(12)
16
- expect(types.filter((t: string) => t === "pcb_plated_hole").length).toBe(4)
17
- expect(types).toContain("pcb_silkscreen_path")
18
- expect(types).toContain("pcb_courtyard_outline")
19
- expect(types).toContain("cad_component")
20
- }, 20000)
21
-
22
- test("fetchPartCircuitJson works with manufacturerPartNumber (TYPE-C-31-M-12)", async () => {
23
- const result = await jlcPartsEngine.fetchPartCircuitJson!({
24
- manufacturerPartNumber: "TYPE-C-31-M-12",
25
- })
26
-
27
- expect(result).toBeDefined()
28
- expect(Array.isArray(result)).toBe(true)
29
-
30
- const types = result!.map((el: any) => el.type)
31
- expect(types.filter((t: string) => t === "pcb_smtpad").length).toBe(12)
32
- expect(types.filter((t: string) => t === "pcb_plated_hole").length).toBe(4)
33
- }, 20000)