@jlcpcb/core 0.1.0

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 (44) hide show
  1. package/CHANGELOG.md +10 -0
  2. package/README.md +474 -0
  3. package/package.json +48 -0
  4. package/src/api/easyeda-community.ts +259 -0
  5. package/src/api/easyeda.ts +153 -0
  6. package/src/api/index.ts +7 -0
  7. package/src/api/jlc.ts +185 -0
  8. package/src/constants/design-rules.ts +119 -0
  9. package/src/constants/footprints.ts +68 -0
  10. package/src/constants/index.ts +7 -0
  11. package/src/constants/kicad.ts +147 -0
  12. package/src/converter/category-router.ts +638 -0
  13. package/src/converter/footprint-mapper.ts +236 -0
  14. package/src/converter/footprint.ts +949 -0
  15. package/src/converter/global-lib-table.ts +394 -0
  16. package/src/converter/index.ts +46 -0
  17. package/src/converter/lib-table.ts +181 -0
  18. package/src/converter/svg-arc.ts +179 -0
  19. package/src/converter/symbol-templates.ts +214 -0
  20. package/src/converter/symbol.ts +1682 -0
  21. package/src/converter/value-normalizer.ts +262 -0
  22. package/src/index.ts +25 -0
  23. package/src/parsers/easyeda-shapes.ts +628 -0
  24. package/src/parsers/http-client.ts +96 -0
  25. package/src/parsers/index.ts +38 -0
  26. package/src/parsers/utils.ts +29 -0
  27. package/src/services/component-service.ts +100 -0
  28. package/src/services/fix-service.ts +50 -0
  29. package/src/services/index.ts +9 -0
  30. package/src/services/library-service.ts +696 -0
  31. package/src/types/component.ts +61 -0
  32. package/src/types/easyeda-community.ts +78 -0
  33. package/src/types/easyeda.ts +356 -0
  34. package/src/types/index.ts +12 -0
  35. package/src/types/jlc.ts +84 -0
  36. package/src/types/kicad.ts +136 -0
  37. package/src/types/mcp.ts +77 -0
  38. package/src/types/project.ts +60 -0
  39. package/src/utils/conversion.ts +104 -0
  40. package/src/utils/file-system.ts +143 -0
  41. package/src/utils/index.ts +8 -0
  42. package/src/utils/logger.ts +96 -0
  43. package/src/utils/validation.ts +110 -0
  44. package/tsconfig.json +9 -0
@@ -0,0 +1,136 @@
1
+ /**
2
+ * KiCad-specific types for schematic and PCB manipulation
3
+ */
4
+
5
+ export interface PinDefinition {
6
+ number: string;
7
+ name: string;
8
+ electricalType: 'input' | 'output' | 'bidirectional' | 'power_in' | 'power_out' | 'passive' | 'unspecified' | 'open_collector' | 'open_emitter' | 'no_connect';
9
+ x: number;
10
+ y: number;
11
+ rotation: number;
12
+ length: number;
13
+ }
14
+
15
+ export interface PadDefinition {
16
+ number: string;
17
+ type: 'thru_hole' | 'smd' | 'connect' | 'np_thru_hole';
18
+ shape: 'circle' | 'rect' | 'oval' | 'trapezoid' | 'roundrect' | 'custom';
19
+ x: number;
20
+ y: number;
21
+ width: number;
22
+ height: number;
23
+ drill?: number;
24
+ layers: string[];
25
+ }
26
+
27
+ export interface BoundingBox {
28
+ x: number;
29
+ y: number;
30
+ width: number;
31
+ height: number;
32
+ }
33
+
34
+ export interface KiCadSymbol {
35
+ name: string;
36
+ library: string;
37
+ pins: PinDefinition[];
38
+ properties: Record<string, string>;
39
+ }
40
+
41
+ export interface KiCadFootprint {
42
+ name: string;
43
+ library: string;
44
+ pads: PadDefinition[];
45
+ layers: string[];
46
+ courtyard: BoundingBox;
47
+ properties: Record<string, string>;
48
+ }
49
+
50
+ export interface SchematicComponent {
51
+ reference: string;
52
+ value: string;
53
+ symbol: string;
54
+ footprint: string;
55
+ position: { x: number; y: number };
56
+ rotation: number;
57
+ properties: Record<string, string>;
58
+ }
59
+
60
+ export interface Wire {
61
+ startX: number;
62
+ startY: number;
63
+ endX: number;
64
+ endY: number;
65
+ }
66
+
67
+ export interface NetLabel {
68
+ name: string;
69
+ x: number;
70
+ y: number;
71
+ rotation: number;
72
+ }
73
+
74
+ export interface SchematicSheet {
75
+ name: string;
76
+ filename: string;
77
+ position: { x: number; y: number };
78
+ }
79
+
80
+ export interface KiCadSchematic {
81
+ version: string;
82
+ components: SchematicComponent[];
83
+ wires: Wire[];
84
+ labels: NetLabel[];
85
+ sheets: SchematicSheet[];
86
+ }
87
+
88
+ export interface KiCadPCB {
89
+ version: string;
90
+ layers: PCBLayer[];
91
+ components: PCBComponent[];
92
+ traces: Trace[];
93
+ vias: Via[];
94
+ zones: Zone[];
95
+ boardOutline: BoardOutline;
96
+ }
97
+
98
+ export interface PCBLayer {
99
+ number: number;
100
+ name: string;
101
+ type: 'signal' | 'power' | 'mixed' | 'user';
102
+ }
103
+
104
+ export interface PCBComponent {
105
+ reference: string;
106
+ footprint: string;
107
+ position: { x: number; y: number };
108
+ rotation: number;
109
+ layer: 'F.Cu' | 'B.Cu';
110
+ }
111
+
112
+ export interface Trace {
113
+ net: string;
114
+ width: number;
115
+ layer: string;
116
+ points: { x: number; y: number }[];
117
+ }
118
+
119
+ export interface Via {
120
+ net: string;
121
+ position: { x: number; y: number };
122
+ size: number;
123
+ drill: number;
124
+ layers: [string, string];
125
+ }
126
+
127
+ export interface Zone {
128
+ net: string;
129
+ layer: string;
130
+ priority: number;
131
+ outline: { x: number; y: number }[];
132
+ }
133
+
134
+ export interface BoardOutline {
135
+ points: { x: number; y: number }[];
136
+ }
@@ -0,0 +1,77 @@
1
+ /**
2
+ * MCP protocol types for AI-EDA tools
3
+ */
4
+
5
+ export interface MCPToolResult {
6
+ content: MCPContent[];
7
+ isError?: boolean;
8
+ }
9
+
10
+ export interface MCPContent {
11
+ type: 'text' | 'image' | 'resource';
12
+ text?: string;
13
+ data?: string;
14
+ mimeType?: string;
15
+ }
16
+
17
+ export interface MCPToolDefinition {
18
+ name: string;
19
+ description: string;
20
+ inputSchema: {
21
+ type: 'object';
22
+ properties: Record<string, MCPPropertySchema>;
23
+ required?: string[];
24
+ };
25
+ }
26
+
27
+ export interface MCPPropertySchema {
28
+ type: 'string' | 'number' | 'boolean' | 'array' | 'object';
29
+ description?: string;
30
+ enum?: string[];
31
+ items?: MCPPropertySchema;
32
+ default?: unknown;
33
+ }
34
+
35
+ export interface MCPServerInfo {
36
+ name: string;
37
+ version: string;
38
+ capabilities: {
39
+ tools?: boolean;
40
+ resources?: boolean;
41
+ prompts?: boolean;
42
+ };
43
+ }
44
+
45
+ // Tool-specific types
46
+ export interface ComponentSearchParams {
47
+ query: string;
48
+ category?: string;
49
+ inStock?: boolean;
50
+ limit?: number;
51
+ }
52
+
53
+ export interface LibraryFetchParams {
54
+ lcscPartNumber: string;
55
+ outputDir?: string;
56
+ include3D?: boolean;
57
+ }
58
+
59
+ export interface DatasheetDownloadParams {
60
+ lcscPartNumber: string;
61
+ outputPath?: string;
62
+ }
63
+
64
+ export interface SchematicParams {
65
+ projectPath: string;
66
+ sheetName?: string;
67
+ }
68
+
69
+ export interface PCBParams {
70
+ projectPath: string;
71
+ }
72
+
73
+ export interface ExportParams {
74
+ projectPath: string;
75
+ format: 'jlcpcb' | 'pcbway' | 'oshpark' | 'generic';
76
+ outputDir?: string;
77
+ }
@@ -0,0 +1,60 @@
1
+ /**
2
+ * Project structure and configuration types
3
+ */
4
+
5
+ import type { ComponentSelection } from './component.js';
6
+
7
+ export interface EDAProject {
8
+ name: string;
9
+ version: string;
10
+ created: string;
11
+ modified: string;
12
+ constraints: DesignConstraints;
13
+ components: ComponentSelection[];
14
+ status: ProjectStatus;
15
+ }
16
+
17
+ export interface DesignConstraints {
18
+ boardSize?: { width: number; height: number; unit: 'mm' | 'inch' };
19
+ layers: number;
20
+ powerSource: PowerSourceSpec;
21
+ interfaces: InterfaceSpec[];
22
+ environment?: EnvironmentSpec;
23
+ manufacturingClass?: number; // IPC class 1/2/3
24
+ }
25
+
26
+ export interface PowerSourceSpec {
27
+ type: 'usb' | 'battery' | 'dc_jack' | 'poe' | 'other';
28
+ voltage: { min: number; max: number };
29
+ current?: number;
30
+ details?: string;
31
+ }
32
+
33
+ export interface InterfaceSpec {
34
+ type: 'usb' | 'uart' | 'spi' | 'i2c' | 'ethernet' | 'wifi' | 'bluetooth' | 'can' | 'rs485' | 'gpio' | 'adc' | 'dac' | 'pwm' | 'other';
35
+ count?: number;
36
+ details?: string;
37
+ }
38
+
39
+ export interface EnvironmentSpec {
40
+ tempMin: number;
41
+ tempMax: number;
42
+ indoor: boolean;
43
+ certifications?: string[];
44
+ }
45
+
46
+ export interface ProjectStatus {
47
+ phase: 'specification' | 'component_selection' | 'schematic' | 'pcb_layout' | 'validation' | 'manufacturing';
48
+ schematicComplete: boolean;
49
+ pcbComplete: boolean;
50
+ validated: boolean;
51
+ exported: boolean;
52
+ }
53
+
54
+ export interface ProjectConfig {
55
+ kicadProjectDir: string;
56
+ librariesDir: string;
57
+ datasheetsDir: string;
58
+ productionDir: string;
59
+ docsDir: string;
60
+ }
@@ -0,0 +1,104 @@
1
+ /**
2
+ * Unit conversion utilities for EDA operations
3
+ */
4
+
5
+ // EasyEDA uses 10mil units (0.254mm per unit)
6
+ export const EASYEDA_TO_MM = 0.254;
7
+ export const MM_TO_EASYEDA = 1 / EASYEDA_TO_MM;
8
+
9
+ // KiCad uses mm as default
10
+ export const MM_TO_MIL = 39.3701;
11
+ export const MIL_TO_MM = 0.0254;
12
+ export const MM_TO_INCH = 0.0393701;
13
+ export const INCH_TO_MM = 25.4;
14
+
15
+ /**
16
+ * Convert EasyEDA units to millimeters
17
+ */
18
+ export function easyedaToMm(value: number): number {
19
+ return value * EASYEDA_TO_MM;
20
+ }
21
+
22
+ /**
23
+ * Convert millimeters to EasyEDA units
24
+ */
25
+ export function mmToEasyeda(value: number): number {
26
+ return value * MM_TO_EASYEDA;
27
+ }
28
+
29
+ /**
30
+ * Convert mils to millimeters
31
+ */
32
+ export function milToMm(value: number): number {
33
+ return value * MIL_TO_MM;
34
+ }
35
+
36
+ /**
37
+ * Convert millimeters to mils
38
+ */
39
+ export function mmToMil(value: number): number {
40
+ return value * MM_TO_MIL;
41
+ }
42
+
43
+ /**
44
+ * Convert inches to millimeters
45
+ */
46
+ export function inchToMm(value: number): number {
47
+ return value * INCH_TO_MM;
48
+ }
49
+
50
+ /**
51
+ * Convert millimeters to inches
52
+ */
53
+ export function mmToInch(value: number): number {
54
+ return value * MM_TO_INCH;
55
+ }
56
+
57
+ /**
58
+ * Round to a specific number of decimal places
59
+ */
60
+ export function roundTo(value: number, decimals: number = 4): number {
61
+ const multiplier = Math.pow(10, decimals);
62
+ return Math.round(value * multiplier) / multiplier;
63
+ }
64
+
65
+ /**
66
+ * Convert angle from degrees to radians
67
+ */
68
+ export function degToRad(degrees: number): number {
69
+ return (degrees * Math.PI) / 180;
70
+ }
71
+
72
+ /**
73
+ * Convert angle from radians to degrees
74
+ */
75
+ export function radToDeg(radians: number): number {
76
+ return (radians * 180) / Math.PI;
77
+ }
78
+
79
+ /**
80
+ * Normalize angle to 0-360 range
81
+ */
82
+ export function normalizeAngle(angle: number): number {
83
+ return ((angle % 360) + 360) % 360;
84
+ }
85
+
86
+ /**
87
+ * Calculate trace width for given current and temperature rise
88
+ * Using IPC-2221 standard for external layers
89
+ * @param currentAmps - Current in amperes
90
+ * @param tempRiseC - Temperature rise in Celsius (default 10)
91
+ * @param copperOz - Copper weight in oz/ft² (default 1)
92
+ * @returns Trace width in mm
93
+ */
94
+ export function calculateTraceWidth(
95
+ currentAmps: number,
96
+ tempRiseC: number = 10,
97
+ copperOz: number = 1
98
+ ): number {
99
+ // IPC-2221 formula for external layers
100
+ const area = Math.pow(currentAmps / (0.048 * Math.pow(tempRiseC, 0.44)), 1 / 0.725);
101
+ const thickness = copperOz * 0.035; // mm
102
+ const width = area / (thickness * 1000); // convert from mils² to mm
103
+ return roundTo(width, 3);
104
+ }
@@ -0,0 +1,143 @@
1
+ /**
2
+ * File system operation helpers
3
+ */
4
+
5
+ import { readdir, readFile, writeFile, mkdir, access, stat } from 'fs/promises';
6
+ import { join, dirname, extname, basename } from 'path';
7
+
8
+ /**
9
+ * Check if a path exists
10
+ */
11
+ export async function pathExists(path: string): Promise<boolean> {
12
+ try {
13
+ await access(path);
14
+ return true;
15
+ } catch {
16
+ return false;
17
+ }
18
+ }
19
+
20
+ /**
21
+ * Check if a path is a directory
22
+ */
23
+ export async function isDirectory(path: string): Promise<boolean> {
24
+ try {
25
+ const stats = await stat(path);
26
+ return stats.isDirectory();
27
+ } catch {
28
+ return false;
29
+ }
30
+ }
31
+
32
+ /**
33
+ * Check if a path is a file
34
+ */
35
+ export async function isFile(path: string): Promise<boolean> {
36
+ try {
37
+ const stats = await stat(path);
38
+ return stats.isFile();
39
+ } catch {
40
+ return false;
41
+ }
42
+ }
43
+
44
+ /**
45
+ * Ensure a directory exists, creating it if necessary
46
+ */
47
+ export async function ensureDir(path: string): Promise<void> {
48
+ await mkdir(path, { recursive: true });
49
+ }
50
+
51
+ /**
52
+ * Ensure the parent directory of a file exists
53
+ */
54
+ export async function ensureParentDir(filePath: string): Promise<void> {
55
+ const parentDir = dirname(filePath);
56
+ await ensureDir(parentDir);
57
+ }
58
+
59
+ /**
60
+ * Read a JSON file and parse it
61
+ */
62
+ export async function readJson<T = unknown>(path: string): Promise<T> {
63
+ const content = await readFile(path, 'utf-8');
64
+ return JSON.parse(content) as T;
65
+ }
66
+
67
+ /**
68
+ * Write an object to a JSON file
69
+ */
70
+ export async function writeJson(path: string, data: unknown, pretty: boolean = true): Promise<void> {
71
+ await ensureParentDir(path);
72
+ const content = pretty ? JSON.stringify(data, null, 2) : JSON.stringify(data);
73
+ await writeFile(path, content, 'utf-8');
74
+ }
75
+
76
+ /**
77
+ * Read a text file
78
+ */
79
+ export async function readText(path: string): Promise<string> {
80
+ return readFile(path, 'utf-8');
81
+ }
82
+
83
+ /**
84
+ * Write text to a file
85
+ */
86
+ export async function writeText(path: string, content: string): Promise<void> {
87
+ await ensureParentDir(path);
88
+ await writeFile(path, content, 'utf-8');
89
+ }
90
+
91
+ /**
92
+ * Write binary data to a file
93
+ */
94
+ export async function writeBinary(path: string, data: Buffer | Uint8Array): Promise<void> {
95
+ await ensureParentDir(path);
96
+ await writeFile(path, data);
97
+ }
98
+
99
+ /**
100
+ * List files in a directory with optional filtering
101
+ */
102
+ export async function listFiles(
103
+ dir: string,
104
+ options: { extensions?: string[]; recursive?: boolean } = {}
105
+ ): Promise<string[]> {
106
+ const { extensions, recursive = false } = options;
107
+ const files: string[] = [];
108
+
109
+ async function scanDir(currentDir: string): Promise<void> {
110
+ const entries = await readdir(currentDir, { withFileTypes: true });
111
+
112
+ for (const entry of entries) {
113
+ const fullPath = join(currentDir, entry.name);
114
+
115
+ if (entry.isDirectory() && recursive) {
116
+ await scanDir(fullPath);
117
+ } else if (entry.isFile()) {
118
+ if (!extensions || extensions.includes(extname(entry.name))) {
119
+ files.push(fullPath);
120
+ }
121
+ }
122
+ }
123
+ }
124
+
125
+ await scanDir(dir);
126
+ return files;
127
+ }
128
+
129
+ /**
130
+ * Get the filename without extension
131
+ */
132
+ export function getBaseName(filePath: string): string {
133
+ return basename(filePath, extname(filePath));
134
+ }
135
+
136
+ /**
137
+ * Copy a file
138
+ */
139
+ export async function copyFile(src: string, dest: string): Promise<void> {
140
+ await ensureParentDir(dest);
141
+ const content = await readFile(src);
142
+ await writeFile(dest, content);
143
+ }
@@ -0,0 +1,8 @@
1
+ /**
2
+ * ai-eda-common utilities
3
+ */
4
+
5
+ export * from './conversion.js';
6
+ export * from './validation.js';
7
+ export * from './file-system.js';
8
+ export * from './logger.js';
@@ -0,0 +1,96 @@
1
+ /**
2
+ * Consistent logging utility for AI-EDA packages
3
+ */
4
+
5
+ export type LogLevel = 'debug' | 'info' | 'warn' | 'error';
6
+
7
+ export interface LoggerOptions {
8
+ level: LogLevel;
9
+ prefix?: string;
10
+ timestamps?: boolean;
11
+ }
12
+
13
+ const LOG_LEVELS: Record<LogLevel, number> = {
14
+ debug: 0,
15
+ info: 1,
16
+ warn: 2,
17
+ error: 3,
18
+ };
19
+
20
+ export class Logger {
21
+ private level: LogLevel;
22
+ private prefix: string;
23
+ private timestamps: boolean;
24
+
25
+ constructor(options: Partial<LoggerOptions> = {}) {
26
+ this.level = options.level ?? 'info';
27
+ this.prefix = options.prefix ?? '';
28
+ this.timestamps = options.timestamps ?? false;
29
+ }
30
+
31
+ private shouldLog(level: LogLevel): boolean {
32
+ return LOG_LEVELS[level] >= LOG_LEVELS[this.level];
33
+ }
34
+
35
+ private formatMessage(level: LogLevel, message: string): string {
36
+ const parts: string[] = [];
37
+
38
+ if (this.timestamps) {
39
+ parts.push(`[${new Date().toISOString()}]`);
40
+ }
41
+
42
+ parts.push(`[${level.toUpperCase()}]`);
43
+
44
+ if (this.prefix) {
45
+ parts.push(`[${this.prefix}]`);
46
+ }
47
+
48
+ parts.push(message);
49
+
50
+ return parts.join(' ');
51
+ }
52
+
53
+ debug(message: string, ...args: unknown[]): void {
54
+ if (this.shouldLog('debug')) {
55
+ console.error(this.formatMessage('debug', message), ...args);
56
+ }
57
+ }
58
+
59
+ info(message: string, ...args: unknown[]): void {
60
+ if (this.shouldLog('info')) {
61
+ console.error(this.formatMessage('info', message), ...args);
62
+ }
63
+ }
64
+
65
+ warn(message: string, ...args: unknown[]): void {
66
+ if (this.shouldLog('warn')) {
67
+ console.error(this.formatMessage('warn', message), ...args);
68
+ }
69
+ }
70
+
71
+ error(message: string, ...args: unknown[]): void {
72
+ if (this.shouldLog('error')) {
73
+ console.error(this.formatMessage('error', message), ...args);
74
+ }
75
+ }
76
+
77
+ setLevel(level: LogLevel): void {
78
+ this.level = level;
79
+ }
80
+
81
+ child(prefix: string): Logger {
82
+ return new Logger({
83
+ level: this.level,
84
+ prefix: this.prefix ? `${this.prefix}:${prefix}` : prefix,
85
+ timestamps: this.timestamps,
86
+ });
87
+ }
88
+ }
89
+
90
+ // Default logger instance
91
+ export const logger = new Logger({ prefix: 'ai-eda' });
92
+
93
+ // Create package-specific loggers
94
+ export function createLogger(packageName: string): Logger {
95
+ return logger.child(packageName);
96
+ }