- होम
- Documentation
- नेटिव
- pi-natives (N-API) में पोर्टिंग — फील्ड नोट्स
pi-natives (N-API) में पोर्टिंग — फील्ड नोट्स
यह हॉट पाथ्स को crates/pi-natives में ले जाने और उन्हें JS बाइंडिंग्स के माध्यम से जोड़ने के लिए एक व्यावहारिक गाइड है। यह इसलिए मौजूद है ताकि एक ही विफलताएँ दोबारा न हों।
कब पोर्ट करें
Section titled “कब पोर्ट करें”जब इनमें से कोई भी सत्य हो तो पोर्ट करें:
- हॉट पाथ रेंडर लूप्स, तेज UI अपडेट्स, या बड़े बैचेस में चलता है।
- JS आवंटन प्रमुख हैं (स्ट्रिंग चर्न, regex बैकट्रैकिंग, बड़े arrays)।
- आपके पास पहले से JS बेसलाइन है और आप दोनों संस्करणों को साथ-साथ बेंचमार्क कर सकते हैं।
- कार्य CPU-बाउंड है या ब्लॉकिंग I/O है जो libuv थ्रेड पूल पर चल सकता है।
- कार्य async I/O है जो Tokio के रनटाइम पर चल सकता है (जैसे, shell execution)।
ऐसे पोर्ट्स से बचें जो JS-only स्टेट या डायनामिक imports पर निर्भर करते हैं। N-API exports शुद्ध, data-in/data-out होने चाहिए। लंबे समय तक चलने वाले कार्य task::blocking (CPU-बाउंड/ब्लॉकिंग I/O) या task::future (async I/O) के माध्यम से cancellation के साथ जाने चाहिए।
एक native export की संरचना
Section titled “एक native export की संरचना”Rust पक्ष:
- कार्यान्वयन
crates/pi-natives/src/<module>.rsमें होता है। यदि आप एक नया मॉड्यूल जोड़ते हैं, तो इसेcrates/pi-natives/src/lib.rsमें रजिस्टर करें। #[napi]के साथ एक्सपोर्ट करें; snake_case exports स्वचालित रूप से camelCase में बदल जाते हैं। स्पष्टjs_nameका उपयोग केवल वास्तविक aliases/गैर-डिफ़ॉल्ट नामों के लिए करें। structs के लिए#[napi(object)]का उपयोग करें।- CPU-बाउंड या ब्लॉकिंग कार्य के लिए
task::blocking(tag, cancel_token, work)(देखेंcrates/pi-natives/src/task.rs) का उपयोग करें। async कार्य के लिए जिसे Tokio की आवश्यकता है (जैसे, shell sessions)task::future(env, tag, work)का उपयोग करें। जब आपtimeoutMsयाAbortSignalएक्सपोज़ करते हैं तोCancelTokenपास करें।
JS पक्ष:
packages/natives/src/bindings.tsमें बेसNativeBindingsइंटरफ़ेस होता है।packages/natives/src/<module>/types.tsTS प्रकार परिभाषित करता है और declaration merging के माध्यम सेNativeBindingsको augment करता है।packages/natives/src/native.tsdeclarations को सक्रिय करने के लिए प्रत्येक<module>/types.tsफाइल को import करता है।packages/natives/src/<module>/index.tspackages/natives/src/native.tsसेnativeबाइंडिंग को wrap करता है।packages/natives/src/native.tsaddon लोड करता है औरvalidateNativeआवश्यक exports को enforce करता है।packages/natives/src/index.tspackages/*में कॉलर्स के लिए wrapper को re-export करता है।
पोर्टिंग चेकलिस्ट
Section titled “पोर्टिंग चेकलिस्ट”- Rust कार्यान्वयन जोड़ें
- मुख्य लॉजिक को एक सामान्य Rust फंक्शन में रखें।
- यदि यह एक नया मॉड्यूल है, तो इसे
crates/pi-natives/src/lib.rsमें जोड़ें। - इसे
#[napi]के साथ एक्सपोज़ करें ताकि डिफ़ॉल्ट snake_case -> camelCase मैपिंग सुसंगत रहे। - सिग्नेचर owned और सरल रखें:
String,Vec<String>,Uint8Array, या बड़े string/byte inputs के लिएEither<JsString, Uint8Array>। - CPU-बाउंड या ब्लॉकिंग कार्य के लिए
task::blockingका उपयोग करें; async कार्य के लिएtask::futureका उपयोग करें। एकCancelTokenपास करें और लंबे लूप्स के अंदरheartbeat()कॉल करें।
- JS बाइंडिंग्स जोड़ें
packages/natives/src/<module>/types.tsमें types औरNativeBindingsaugmentation जोड़ें।- Declaration merging ट्रिगर करने के लिए
packages/natives/src/native.tsमें./<module>/typesimport करें। packages/natives/src/<module>/index.tsमें एक wrapper जोड़ें जोnativeको कॉल करता है।packages/natives/src/index.tsसे re-export करें।
- Native validation अपडेट करें
validateNative(packages/natives/src/native.ts) मेंcheckFn("newExport")जोड़ें।
- बेंचमार्क जोड़ें
- बेंचमार्क को स्वामी पैकेज के पास रखें (
packages/tui/bench,packages/natives/bench, याpackages/coding-agent/bench)। - एक ही रन में JS बेसलाइन और native संस्करण दोनों शामिल करें।
Bun.nanoseconds()और एक निश्चित iteration count का उपयोग करें।- बेंचमार्क inputs छोटे और यथार्थवादी रखें (हॉट पाथ में दिखने वाला वास्तविक डेटा)।
- Native बाइनरी बिल्ड करें
bun --cwd=packages/natives run buildbun --cwd=packages/natives run buildका उपयोग करें और यदि आप टेस्टिंग के दौरान loader diagnostics चाहते हैं तोPI_DEV=1सेट करें।
- बेंचमार्क चलाएं
bun run packages/<pkg>/bench/<bench>.ts(याbun --cwd=packages/natives run bench)
- उपयोग पर निर्णय लें
- यदि native धीमा है, तो JS रखें और native export को अप्रयुक्त छोड़ दें।
- यदि native तेज है, तो कॉल साइट्स को native wrapper पर स्विच करें।
समस्या बिंदु और उनसे बचने के तरीके
Section titled “समस्या बिंदु और उनसे बचने के तरीके”1) पुराना pi_natives.node नए exports को रोकता है
Section titled “1) पुराना pi_natives.node नए exports को रोकता है”लोडर packages/natives/native में प्लेटफॉर्म-टैग्ड बाइनरी (pi_natives.<platform>-<arch>.node) को प्राथमिकता देता है। PI_DEV=1 अब केवल loader diagnostics सक्षम करता है; यह अब एक अलग dev addon फ़ाइलनाम पर स्विच नहीं करता। एक फ़ॉलबैक pi_natives.node भी है। संकलित बाइनरी ~/.xcsh/natives/<version>/pi_natives.<platform>-<arch>.node में extract होती हैं। यदि इनमें से कोई भी पुरानी है, तो exports अपडेट नहीं होंगे।
समाधान: रीबिल्ड करने से पहले पुरानी फ़ाइल हटाएं।
rm packages/natives/native/pi_natives.linux-x64.noderm packages/natives/native/pi_natives.nodebun --cwd=packages/natives run buildयदि आप एक संकलित बाइनरी चला रहे हैं, तो कैश्ड addon डायरेक्टरी हटाएं:
rm -rf ~/.xcsh/natives/<version>फिर सत्यापित करें कि export बाइनरी में मौजूद है:
bun -e 'const tag = `${process.platform}-${process.arch}`; const mod = require(`./packages/natives/native/pi_natives.${tag}.node`); console.log(Object.keys(mod).includes("newExport"));'2) validateNative से “Missing exports” त्रुटियाँ
Section titled “2) validateNative से “Missing exports” त्रुटियाँ”यह अच्छी बात है — यह मूक बेमेलों को रोकता है। जब आप यह देखें:
Native addon missing exports ... Missing: visibleWidthइसका मतलब है कि आपकी बाइनरी पुरानी है, Rust export नाम (या उपयोग किए जाने पर स्पष्ट alias) JS नाम से मेल नहीं खाता, या export कभी compile नहीं हुआ। बिल्ड और नामकरण बेमेल को ठीक करें, validation को कमजोर न करें।
3) Rust सिग्नेचर बेमेल
Section titled “3) Rust सिग्नेचर बेमेल”इसे सरल और owned रखें। String, Vec<String>, और Uint8Array काम करते हैं। पब्लिक exports में &str जैसे references से बचें। यदि आपको स्ट्रक्चर्ड डेटा चाहिए, तो इसे #[napi(object)] structs में wrap करें।
4) बेंचमार्किंग गलतियाँ
Section titled “4) बेंचमार्किंग गलतियाँ”- विभिन्न inputs या allocations की तुलना न करें।
- JS और native दोनों को समान input arrays का उपयोग करने दें।
- दोनों को एक ही बेंचमार्क फ़ाइल में चलाएं ताकि विषमता से बचा जा सके।
बेंचमार्क टेम्पलेट
Section titled “बेंचमार्क टेम्पलेट”const ITERATIONS = 2000;
function bench(name: string, fn: () => void): number { const start = Bun.nanoseconds(); for (let i = 0; i < ITERATIONS; i++) fn(); const elapsed = (Bun.nanoseconds() - start) / 1e6; console.log(`${name}: ${elapsed.toFixed(2)}ms total (${(elapsed / ITERATIONS).toFixed(6)}ms/op)`); return elapsed;}
bench("feature/js", () => { jsImpl(sample);});
bench("feature/native", () => { nativeImpl(sample);});सत्यापन चेकलिस्ट
Section titled “सत्यापन चेकलिस्ट”validateNativeपास होता है (कोई missing exports नहीं)।NativeBindingspackages/natives/src/<module>/types.tsमें augment किया गया है और wrapperpackages/natives/src/index.tsमें re-export किया गया है।Object.keys(require(...))में आपका नया export शामिल है।- PR/नोट्स में बेंच नंबर दर्ज हैं।
- कॉल साइट केवल तभी अपडेट किया गया जब native तेज या बराबर हो।
अंगूठे का नियम
Section titled “अंगूठे का नियम”- यदि native धीमा है, तो स्विच न करें। भविष्य के कार्य के लिए export रखें, लेकिन TUI को तेज पाथ पर रहना चाहिए।
- यदि native तेज है, तो कॉल साइट स्विच करें और regressions पकड़ने के लिए बेंचमार्क बनाए रखें।