@tscircuit/parts-engine 0.0.16 → 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.
- package/dist/index.d.ts +4262 -0
- package/dist/index.js +10644 -0
- package/dist/index.js.map +1 -0
- package/package.json +5 -2
- package/.github/workflows/bun-build.yml +0 -26
- package/.github/workflows/bun-formatcheck.yml +0 -26
- package/.github/workflows/bun-pver-release.yml +0 -28
- package/.github/workflows/bun-test.yml +0 -28
- package/.github/workflows/bun-typecheck.yml +0 -26
- package/biome.json +0 -49
- package/bun.lock +0 -319
- package/index.ts +0 -1
- package/lib/footprint-translators/get-footprinter-string-from-kicad.ts +0 -21
- package/lib/footprint-translators/get-jlc-package-from-footprinter-string.ts +0 -12
- package/lib/footprint-translators/index.ts +0 -25
- package/lib/jlc-parts-engine.ts +0 -291
- package/tests/fetch-part-circuitjson-from-easyeda.test.ts +0 -33
- package/tests/jlc-parts-engine.test.ts +0 -502
- package/tsconfig.json +0 -28
package/lib/jlc-parts-engine.ts
DELETED
|
@@ -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)
|