IlmHamroh
JavaScript Full-stack/24-qism. Node.js8/55-dars30 daqiqa
Mundarija (38)

Node.js process obyekti: argv, env, exit kodlari va signallar

Qisqacha: process — Node dasturining tashqi dunyo bilan aloqasi. Kirish: process.argv (buyruq qatori argumentlari, matnlar massivi) va process.env (muhit o'zgaruvchilari, doim matn). Chiqish: process.stdout (natija), process.stderr (xato va ogohlantirish) va exit kod — 0 muvaffaqiyat, boshqasi xato. Kodni process.exitCode = 2 bilan belgilang, process.exit() ni faqat zarur bo'lganda chaqiring — u ishni to'liq tugatmasdan uzadi. SIGINT (Ctrl+C) va SIGTERM signallarini process.on bilan ushlash mumkin, lekin Windows'da boshqa jarayon yuborgan signal ishlovchini chaqirmaydi (o'lchandi). Shu darsda valyuta CLI'si boshlanadi.

Bu darsda

  • process.argv dan foydalanuvchi argumentlarini ajratib olasiz va process.env bilan sozlama o'qiysiz.
  • Natijani stdout ga, xatoni stderr ga yozish nega muhimligini tushuntira olasiz.
  • Exit kodlarini to'g'ri qo'yasiz: exitCode va exit() farqi, exit va beforeExit hodisalari.
  • SIGINT ni ushlab, 130 kod bilan chiqasiz va Windows'dagi cheklovni bilasiz.
  • valyuta loyihasini noldan yaratasiz: TypeScript, ESLint, node:test, CBU kurslari, 10 ta test va birinchi commit.

Oldin bilishingiz kerak: Global obyektlar: Node va brauzer farqi, Birinchi avtomatik test: node:test, package.json III: scripts, TypeScript asoslari.

1. Nega bu kerak?

Terminalda har kuni buyruq yozasiz: git commit -m "...", npm i -D eslint. Har biri — dastur, va har biri uch narsani biladi: siz unga nima berdingiz (argumentlar), qanday muhitda ishlayapti (muhit o'zgaruvchilari) va oxirida muvaffaqiyatli tugadimi (exit kod).

Oxirgisi ko'rinmaydi, lekin juda muhim. CI tizimi testlar o'tdimi-yo'qligini aynan exit koddan biladi. npm run check ichida tip && lint && test yozilgan — && oldingi buyruqning exit kodi 0 bo'lsagina keyingisini ishga tushiradi. Exit kodi noto'g'ri bo'lgan dastur xato bilan tugasa ham, CI "hammasi yaxshi" deb o'ylaydi.

Bu darsda o'zingizning birinchi haqiqiy CLI'ingizni boshlaysiz — valyuta: Markaziy bank kurslarini terminalda ko'rsatadi va konvertatsiya qiladi. U to'g'ri argument o'qiydi, natijani to'g'ri oqimga yozadi, xatoda to'g'ri kod bilan chiqadi va Ctrl+C ga javob beradi.

O'xshatish: pochta bo'limi. Siz xat (argument) va manzil sozlamalari (muhit) bilan kelasiz. Xodim ishni bajaradi va kvitansiya beradi: "yetkazildi" yoki "manzil xato" (exit kod). Xatlarni bitta qutiga, shikoyatlarni boshqasiga soladi (stdout va stderr).

2. process — kirish va chiqishlar

process — Node'dagi global obyekt (Global obyektlar darsida ko'rdingiz). U joriy ishlayotgan jarayonni ifodalaydi. Jarayon (process) — operatsion tizimda ishlab turgan dastur nusxasi: o'z xotirasi, raqami (PID) va kirish-chiqishlari bilan.

Diagrammada jarayonning "eshiklari"ga qarang — chapda kiradigan, o'ngda chiqadigan:

flowchart LR
  A["argv<br/>kurs USD"] --> P["Node jarayoni<br/>process"]
  E["env<br/>VALYUTA_OFFLINE=1"] --> P
  S["signal<br/>Ctrl+C"] --> P
  P --> O["stdout<br/>natija"]
  P --> R["stderr<br/>xato"]
  P --> X["exit kod<br/>0 / 1 / 2 / 130"]

Har birini navbat bilan ko'ramiz.

3. process.argv — buyruq argumentlari

3.1 Massiv tuzilishi

process.argv — matnlar massivi. Birinchi ikki element doim Node'ning o'zi va fayl yo'li. Foydalanuvchi yozgani — uchinchidan boshlab:

js
console.log(process.argv.slice(2));
console.log(process.argv.length);
console.log(process.execPath === process.argv[0]);

node argv.mjs kurs USD --tez "100 so'm" — biz ishga tushirdik:

text
[ 'kurs', 'USD', '--tez', "100 so'm" ]
6
true

To'liq massiv: [node.exe yo'li, argv.mjs yo'li, 'kurs', 'USD', '--tez', "100 so'm"] — 6 ta. Yo'llar kompyuterga bog'liq, shuning uchun deyarli har CLI process.argv.slice(2) dan boshlaydi.

Uchta qoidaga e'tibor bering:

  • Hamma element — matn. 100 ham "100" bo'lib keladi; songa o'zingiz aylantirasiz.
  • Bo'sh joy argumentlarni ajratadi. Qo'shtirnoq ichidagi "100 so'm" bitta argument — buni terminal (shell) hal qiladi, Node emas.
  • --tez ham oddiy matn. Node bayroqlarni o'zi tahlil qilmaydi. Buning uchun util.parseArgs bor — uni util moduli darsida ko'rasiz. Hozir qo'lda ajratamiz.

3.2 Argumentlarni qo'lda tahlil qilish

Tahlil funksiyasini process dan ajratib yozsak, uni sinash oson: massivni o'zimiz beramiz. Bu blok saytda ham ishlaydi:

js
function parseCommand(args) {
  const [first, ...rest] = args;
  if (first === undefined || first === "--help") {
    return { type: "help" };
  }
  if (first === "kurs") return { type: "rates", codes: rest };
  const amount = Number(first);
  if (!Number.isFinite(amount) || rest[0] === undefined) {
    const message = `Tushunarsiz: ${args.join(" ")}`;
    return { type: "error", message };
  }
  const [from, to = "UZS"] = rest;
  return { type: "convert", amount, from, to };
}

console.log(parseCommand(["kurs", "USD", "EUR"]));
console.log(parseCommand(["100", "usd"]));
console.log(parseCommand(["yuz", "usd"]));
console.log(parseCommand([]));

Konsolda:

text
{ type: 'rates', codes: [ 'USD', 'EUR' ] }
{ type: 'convert', amount: 100, from: 'usd', to: 'UZS' }
{ type: 'error', message: 'Tushunarsiz: yuz usd' }
{ type: 'help' }

Bu naqsh — valyuta ning asosi: haqiqiy dasturda parseCommand(process.argv.slice(2)) chaqiriladi, testda esa istalgan massiv. Brauzer konsoli obyektlarni biroz boshqacha ko'rsatadi ({type: 'rates', codes: Array(2)}) — ma'nosi bir xil.

4. process.env — muhit o'zgaruvchilari

4.1 Har doim matn

Muhit o'zgaruvchisi (environment variable) — jarayonga tashqaridan beriladigan nomli sozlama. Masalan PATH — terminal dasturlarni qayerdan qidirishi. Next.js'da env darsida .env fayllarini ko'rgansiz; Node'da ularning hammasi oxirida process.env ga tushadi.

process.env oddiy obyektga o'xshaydi, lekin bitta muhim farqi bor — u faqat matn saqlaydi:

js
process.env.KURS = 11903.15;
console.log(typeof process.env.KURS, process.env.KURS);

process.env.YOQ = undefined;
console.log(process.env.YOQ, typeof process.env.YOQ);

delete process.env.YOQ;
console.log(process.env.YOQ);

Konsolda:

text
string 11903.15
undefined string
undefined

Ikkinchi qatorga diqqat bilan qarang: undefined string. undefined ni yozganda u "undefined" degan matn bo'ldi. Endi if (process.env.YOQ) rost chiqadi! O'zgaruvchini olib tashlash uchun faqat delete. Sonli sozlamani esa har doim tekshirib o'giring: Number(process.env.PORT ?? 3000).

4.2 Windows: katta-kichik harf farq qilmaydi

Windows'da muhit o'zgaruvchilari nomi katta-kichik harfga sezgir emas. Biz ishga tushirdik:

js
console.log(process.env.path === process.env.PATH);
text
true

Linux va macOS'da path va PATH — ikki xil o'zgaruvchi, natija false bo'ladi (hujjatga ko'ra; Linux'da sinalmagan). Shuning uchun nomlarni doim aynan bir xil — odatda KATTA harflar bilan yozing.

4.3 Muhit o'zgaruvchisini berish

Bitta buyruq uchun o'zgaruvchi berish shell'ga bog'liq:

Shell Yozilishi
Git Bash, Linux, macOS VALYUTA_OFFLINE=1 node src/cli.ts kurs
PowerShell $env:VALYUTA_OFFLINE=1; node src/cli.ts kurs
cmd.exe set VALYUTA_OFFLINE=1 && node src/cli.ts kurs

PowerShell va cmd'da o'zgaruvchi shu oyna yopilguncha qoladi. Git Bash'dagi birinchi usulda esa faqat shu bitta buyruqqa beriladi.

Tekshirib ko'ring: if (process.env.DEBUG === true) sharti DEBUG=true node app.mjs bilan ishga tushirilganda bajariladimi?

Javob

Yo'q. process.env.DEBUG — matn "true", mantiqiy true emas. "true" === true — false. To'g'risi: process.env.DEBUG === "true" yoki === "1".

5. stdout va stderr

Jarayonning ikkita chiqish oqimi bor. console.log — process.stdout ga yozadi, console.error va console.warn — process.stderr ga. Terminalda ikkalasi ham bir ekranga chiqadi, shuning uchun farq ko'rinmaydi. Farq yo'naltirganda paydo bo'ladi:

bash
valyuta kurs USD > kurs.txt

> faqat stdout'ni faylga yozadi. Agar "Ogohlantirish: API ishlamadi" ham stdout'ga ketganida, u kurs.txt ichiga tushib, faylni buzardi. stderr'ga yozilgani esa ekranda qoladi, fayl toza.

Qoida (CLI'lar uchun umumiy kelishuv):

  • stdout — faqat natija: boshqa dastur o'qishi mumkin bo'lgan ma'lumot.
  • stderr — qolgan hamma narsa: xato, ogohlantirish, jarayon haqida xabar.

process.stdout.write(matn) — console.log ning soddaroq akasi: oxiriga yangi qator qo'shmaydi va obyektni formatlamaydi. valyuta aynan shuni ishlatadi — chiqishni to'liq o'zi boshqaradi.

6. Exit kodlari

6.1 Kelishuvlar

Exit kod (chiqish kodi) — jarayon tugaganda OS'ga qaytaradigan butun son. Bash'da oxirgi buyruqning kodi — echo $?, PowerShell'da — $LASTEXITCODE.

Kod Ma'nosi Kim qo'yadi
0 muvaffaqiyat sukut
1 umumiy xato; ushlanmagan istisno Node, sizning kodingiz
2 noto'g'ri foydalanish (argument xato) CLI kelishuvi (bash, grep)
13 tugamagan top-level await Node
130 Ctrl+C bilan to'xtatildi (128 + 2) CLI kelishuvi
134 abort — masalan xotira tugadi Node/OS (Xotira limitlari)

128 + N — "N raqamli signal bilan tugadi" degan Unix kelishuvi. SIGINT raqami 2, shuning uchun 130.

6.2 process.exitCode — tinch chiqish

Eng yaxshi yo'l — kodni belgilash va jarayonga o'zi tugashiga imkon berish. Bu blok exit kod 3 bilan tugaydi (tekshiruvchi buni "xato" deb ko'radi, shuning uchun faqat chiqish solishtiriladi):

js
process.on("beforeExit", (code) => {
  console.log(`beforeExit, kod: ${code}`);
});
process.on("exit", (code) => {
  console.log(`exit hodisasi, kod: ${code}`);
  setTimeout(() => console.log("bu hech qachon chiqmaydi"), 0);
});

console.log("ish tugadi");
process.exitCode = 3;

Konsolda:

text
ish tugadi
beforeExit, kod: 3
exit hodisasi, kod: 3

echo $? — 3. Ikki hodisa farqi:

  • beforeExit — loop'da ish qolmaganda chiqadi. Bu yerda yana asinxron ish boshlash mumkin (masalan oxirgi logni yuborish) — shunda jarayon davom etadi. process.exit() chaqirilganda u chiqmaydi.
  • exit — jarayon albatta tugayapti. Ichida faqat sinxron kod ishlaydi: setTimeout ham, await ham bajarilmaydi — "bu hech qachon chiqmaydi" ko'rinmadi.

6.3 process.exit() — keskin to'xtatish

process.exit(kod) jarayonni darhol tugatadi — loop'da nima qolgan bo'lsa ham:

js
setTimeout(() => console.log("taymer ishladi"), 100);
console.log("exit() dan oldin");
process.exit(1);

Konsolda:

text
exit() dan oldin

Taymer ishlamadi. Agar u faylga yozish yoki tarmoqqa javob yuborish bo'lganida — ish yarim qolardi. Node hujjati ("A note on process I/O") ogohlantiradi: stdout ba'zi platformalarda va oqim turlarida asinxron, process.exit() hali yozilmagan ma'lumotni kesib yuborishi mumkin. Biz Windows'da 10 MB ni pipe orqali yozib exit(0) qildik — hammasi yetib keldi (10 000 000 bayt). Bu kafolat emas, faqat bitta platforma kuzatuvi. Shuning uchun:

Maslahat: Odatiy holda process.exitCode = kod va funksiyadan return. process.exit() faqat chindan to'xtatish kerak bo'lganda — masalan signal ishlovchisida, tozalash tugagach.

6.4 Node o'zi qo'yadigan kodlar

Ikkita holatni bilib qo'ying. Ushlanmagan xato yoki rad etilgan Promise — kod 1:

js
Promise.reject(new Error("API javob bermadi"));

Konsolda:

text
Error: API javob bermadi

Hech qachon tugamaydigan top-level await — kod 13. Biz ishga tushirdik:

js
await new Promise(() => {}); // hech qachon hal bo'lmaydi
console.log("yetib kelmaydi");
text
Warning: Detected unsettled top-level await at file:///C:/Users/…/tla.mjs:1
await new Promise(() => {}); // hech qachon hal bo'lmaydi
^

"Hal bo'lmagan top-level await aniqlandi". Loop'da kutadigan hech narsa yo'q (taymer ham, ulanish ham), Promise esa osilib qoldi — Node buni ko'rib, 13 bilan chiqadi.

Tekshirib ko'ring: CLI noto'g'ri valyuta kodi uchun xato xabarini chiqaradi, lekin exit kod 0. Bu nimaga olib keladi?

Javob

Skript yoki CI uni muvaffaqiyat deb hisoblaydi. Masalan valyuta 1 XYZ && echo saqlandi — "saqlandi" chiqadi, garchi hech narsa hisoblanmagan bo'lsa ham. Xato — nolga teng bo'lmagan kod (valyuta da noto'g'ri foydalanish uchun 2).

7. Signallar: SIGINT va SIGTERM

7.1 Signal nima

Signal — OS jarayonga yuboradigan qisqa xabar: "to'xta", "qayta yukla". Eng ko'p uchraydiganlari:

Signal Raqam Qachon Ushlash mumkinmi
SIGINT 2 terminalda Ctrl+C ha
SIGTERM 15 "iltimos, tugat" (Docker, kill) ha
SIGKILL 9 "darhol o'l" yo'q

Sukut bo'yicha Node SIGINT va SIGTERM olganda darhol tugaydi. process.on("SIGINT", fn) yozsangiz, sukut bekor bo'ladi — endi jarayonni to'xtatish sizning ishingiz. Shuning uchun ishlovchi oxirida process.exit() chaqiriladi — bu uning eng to'g'ri joyi:

ts
process.once("SIGINT", () => {
  process.stderr.write("\nTo'xtatildi\n");
  process.exit(130);
});

Serverlarda SIGTERM ishlovchisida ochiq ulanishlar yopiladi va keyin chiqiladi — bu "graceful shutdown" (ohista to'xtash), uni server darslarida ko'rasiz.

7.2 Windows topilmasi

Kanon tajribasi: bola jarayon SIGINT va SIGTERM uchun ishlovchi o'rnatadi va "tayyor" deydi. Ota jarayon unga child.kill(signal) yuboradi. Natija Windows 11 da:

text
SIGINT: stdout="tayyor", exit code=null, signal=SIGINT
SIGTERM: stdout="tayyor", exit code=null, signal=SIGTERM
SIGKILL: stdout="tayyor", exit code=null, signal=SIGKILL
process.platform: win32

Uchala holatda ham "ishlovchi: SIGINT" chiqmadi — jarayon ishlovchisiz, darhol o'ldirildi. Windows'da haqiqiy Unix signallari yo'q. Node ularni taqlid qiladi: SIGINT ishlovchisi faqat konsoldagi haqiqiy Ctrl+C da chaqiriladi, boshqa dastur yuborgan "signal" esa jarayonni shunchaki tugatadi.

Halol eslatma: kanon haqiqiy Ctrl+C ni sinay olmadi — avtomatik vosita konsolga tugma bosa olmaydi. Siz CLI qadamida o'zingiz bosib ko'rasiz. Linux'da kill -INT <pid> ishlovchini chaqiradi (hujjatga ko'ra; Linux'da sinalmagan).

8. process ning boshqa foydali qismlari

Xususiyat Nima beradi Misol qiymat (kanon mashinasi)
process.cwd() buyruq qaysi papkadan chaqirilgani C:/Users/…/valyuta
process.platform OS win32 (linux, darwin)
process.pid jarayon raqami har safar boshqa
process.memoryUsage() rss, heapTotal, heapUsed, external, arrayBuffers bo'sh skript: rss 37 MB
process.hrtime.bigint() nanosekundli monoton vaqt bigint

process.hrtime.bigint() — vaqtni o'lchash uchun. U Date.now() dan farqli: soat qo'lda o'zgartirilsa ham orqaga ketmaydi (monoton) va nanosekundgacha aniq. Natija — BigInt:

js
const start = process.hrtime.bigint();
JSON.parse(JSON.stringify(Array(100_000).fill({ kurs: 11903.15 })));
const ns = process.hrtime.bigint() - start;
console.log(`${Number(ns) / 1e6} ms`);

performance.now() ham shunday monoton va brauzerda ham bor — ko'p hollarda u qulayroq. memoryUsage() maydonlarini Xotira limitlari darsida batafsil ko'ramiz.

9. CLI qadami: valyuta loyihasi boshlanadi

Kanon holat 08 — valyuta ning birinchi ishlaydigan versiyasi. Quyidagi hamma fayl va chiqish shu holatdan.

9.1 Manba: CBU ochiq API

Markaziy bank kurslarni kalitsiz, ochiq JSON sifatida beradi: https://cbu.uz/uz/arkhiv-kursov-valyut/json/ — 74 valyuta, 25,5 KB. Bitta yozuv (keraksiz maydonlar olib tashlangan):

json
{ "Ccy": "USD", "CcyNm_UZ": "AQSH dollari", "Nominal": "1",
  "Rate": "11903.15", "Diff": "-62.36", "Date": "23.10.2026" }

Uchta e'tiborga loyiq joy: kurs matn ("11903.15"), nominal ham matn — va u har doim 1 emas (Indoneziya rupiyasi uchun "10": kurs 10 rupiya uchun). Sana kun.oy.yil ko'rinishida.

Kanon API'ni sinab, hujjatda yo'q xulqni topdi: noma'lum valyuta uchun javob — bo'sh massiv [] va kod 200 (xato emas!). Noto'g'ri yoki kelajakdagi sana so'ralsa ham — jim ravishda oxirgi kurs. Xulosa: CLI javobni o'zi tekshiradi va foydalanuvchiga javobdagi Date ni ko'rsatadi, so'ralgan sanani emas.

9.2 Loyiha skeleti

bash
mkdir valyuta
cd valyuta
git init
npm i -D --save-exact typescript@6.0.3 @types/node@24.19.2 \
  eslint@10.12.0 @eslint/js@10.0.1 typescript-eslint@8.71.2

Oxiridagi teskari chiziq buyruqni keyingi qatorda davom ettiradi (Git Bash). PowerShell'da hammasini bitta qatorga yozing.

Biz ishga tushirdik (npm 11.19.0):

text
added 98 packages, and audited 99 packages in 8s

98 paket, 47 MB — hammasi dev bog'liqlik. valyuta ning o'zi ishlashi uchun bitta ham paket kerak emas: fetch, fs, test — hammasi Node'da bor. package.json (kanon):

json
{
  "name": "valyuta",
  "version": "0.1.0",
  "description": "O'zbekiston Markaziy banki valyuta kurslari terminalda",
  "type": "module",
  "private": true,
  "engines": {
    "node": ">=24"
  },
  "scripts": {
    "start": "node src/cli.ts",
    "test": "node --test \"test/**/*.test.ts\"",
    "tip": "tsc --noEmit",
    "lint": "eslint . --max-warnings=0",
    "check": "npm run tip && npm run lint && npm test"
  },
  "devDependencies": {
    "@eslint/js": "10.0.1",
    "@types/node": "24.19.2",
    "eslint": "10.12.0",
    "typescript": "6.0.3",
    "typescript-eslint": "8.71.2"
  }
}

Muhim qarorlar:

  • "start": "node src/cli.ts" — build yo'q. Node 24 .ts faylni tiplarini o'chirib to'g'ridan bajaradi. Bu qanday ishlashi va chegaralari — TypeScript'ni Node'da to'g'ridan darsida.
  • "private": true — tasodifan npm'ga chiqib ketmasin. Nashrga O'z npm paketingiz darsida tayyorlaymiz; o'shanda nomni @login/valyuta ga o'zgartirasiz (login — npm'dagi nomingiz). Hozir loyiha lokal, nom — valyuta.
  • "engines": { "node": ">=24" } — type stripping'siz eski Node'da src/cli.ts ishlamaydi.
  • "type": "module" — loyiha boshidan ESM (ES Modules Node'da).

tsconfig.json:

jsonc
{
  "compilerOptions": {
    "target": "ES2024",
    "module": "NodeNext",
    "moduleResolution": "NodeNext",
    "types": ["node"],
    "strict": true,
    "noUncheckedIndexedAccess": true,
    "exactOptionalPropertyTypes": true,
    "erasableSyntaxOnly": true,
    "verbatimModuleSyntax": true,
    "allowImportingTsExtensions": true,
    "noEmit": true,
    "skipLibCheck": true
  },
  "include": ["src", "test"]
}

Yangi uchtasi: erasableSyntaxOnly — faqat o'chirib tashlash mumkin bo'lgan TS sintaksisi (enum yo'q), aynan Node type stripping talab qiladigani. allowImportingTsExtensions — importlarda ./kurs.ts deb yozishga ruxsat (Node aynan shu nomli faylni qidiradi). noEmit — tsc faqat tekshiradi, JS yozmaydi. Qolganlarini 15-qismda ko'rgansiz.

eslint.config.js — typescript-eslint'ning eng qat'iy to'plami (strictTypeChecked) va bitta istisno; istisnoning sababi pastda:

js
import js from "@eslint/js";
import { defineConfig } from "eslint/config";
import tseslint from "typescript-eslint";

export default defineConfig(
  { ignores: ["dist/", "fixtures/"] },
  js.configs.recommended,
  tseslint.configs.strictTypeChecked,
  { languageOptions: { parserOptions: { projectService: true } } },
  {
    // node:test `test()` Promise qaytaradi — runner o'zi kutadi
    // (typescript-eslint hujjatidagi yechim)
    rules: {
      "@typescript-eslint/no-floating-promises": [
        "error",
        {
          allowForKnownSafeCalls: [
            {
              from: "package",
              package: "node:test",
              name: ["test", "describe", "it", "suite"],
            },
          ],
        },
      ],
    },
  },
  {
    files: ["**/*.js"],
    extends: [tseslint.configs.disableTypeChecked],
  },
);

.gitignore da: node_modules/, dist/, *.tgz. Fixture — fixtures/kurslar-2026-10-23.json: CBU'ning 2026-10-23 dagi haqiqiy javobi (74 yozuv). Uni kurs materiallaridan (kurs/mashqlar/24/08-process/fixtures/) oling — testlar aynan shu sana va kurslarga tayanadi. O'zingiz curl -o fixtures/kurslar.json https://cbu.uz/uz/arkhiv-kursov-valyut/json/ bilan bugungisini olsangiz, testlardagi sana va raqamlarni moslashtirasiz.

9.3 src/kurs.ts — toza model

Bu modul process, fs, tarmoqni bilmaydi — faqat ma'lumot va hisob (Global obyektlar darsidagi qoida). Kanon kodi aynan (izohlar asl):

ts
// Kurs modeli: CBU javobini tekshirib o'qish va konvertatsiya
// (tarmoqsiz, toza funksiyalar)

export interface Kurs {
  kod: string; // "USD"
  nomi: string; // "AQSH dollari"
  nominal: number; // 1 yoki 10, 100 (masalan IDR — 10 birlik uchun)
  kurs: number; // nominal birlik necha so'm
  farq: number; // kechagiga nisbatan o'zgarish
  sana: string; // ISO: "2026-10-23"
}

export const SOM: Kurs = {
  kod: "UZS",
  nomi: "O'zbekiston so'mi",
  nominal: 1,
  kurs: 1,
  farq: 0,
  sana: "",
};

function matn(x: Record<string, unknown>, kalit: string): string {
  const q = x[kalit];
  if (typeof q !== "string")
    throw new TypeError(
      `CBU javobida "${kalit}" matn emas: ${JSON.stringify(q)}`,
    );
  return q;
}

function son(x: Record<string, unknown>, kalit: string): number {
  const n = Number(matn(x, kalit));
  if (!Number.isFinite(n))
    throw new TypeError(`CBU javobida "${kalit}" son emas`);
  return n;
}

/** "23.10.2026" → "2026-10-23" */
export function sanaIso(dmy: string): string {
  const m = /^(\d{2})\.(\d{2})\.(\d{4})$/.exec(dmy);
  if (!m) throw new TypeError(`Kutilmagan sana formati: ${dmy}`);
  return `${m[3] ?? ""}-${m[2] ?? ""}-${m[1] ?? ""}`;
}

export function yozuvniOqi(x: unknown): Kurs {
  if (typeof x !== "object" || x === null)
    throw new TypeError("CBU yozuvi obyekt emas");
  const y = x as Record<string, unknown>;
  const nominal = son(y, "Nominal");
  const kurs = son(y, "Rate");
  if (nominal <= 0 || kurs <= 0)
    throw new TypeError(
      `${matn(y, "Ccy")}: kurs yoki nominal musbat emas`,
    );
  return {
    kod: matn(y, "Ccy"),
    nomi: matn(y, "CcyNm_UZ"),
    nominal,
    kurs,
    farq: son(y, "Diff"),
    sana: sanaIso(matn(y, "Date")),
  };
}

export function royxatniOqi(x: unknown): Kurs[] {
  if (!Array.isArray(x))
    throw new TypeError("CBU javobi massiv emas");
  return x.map(yozuvniOqi);
}

/** 1 birlik necha so'm */
export function birlikKursi(k: Kurs): number {
  return k.kurs / k.nominal;
}

export function konvert(miqdor: number, dan: Kurs, ga: Kurs): number {
  return (miqdor * birlikKursi(dan)) / birlikKursi(ga);
}

/** 11903.15 → "11 903.15"
 *  (minglik — bo'sh joy, kasr — nuqta, 2 xona) */
export function sonniYoz(n: number, xona = 2): string {
  const [butun = "", kasr] = Math.abs(n).toFixed(xona).split(".");
  const guruh = butun.replace(/\B(?=(\d{3})+(?!\d))/g, " ");
  return (n < 0 ? "-" : "") + guruh + (kasr ? `.${kasr}` : "");
}

Telefonda o'qish oson bo'lishi uchun bu darsdagi kanon fayllarida uzun qatorlar bo'lingan (70 belgigacha) va izohlar ikki qatorga o'tkazilgan. Mantiq kanon bilan aynan bir xil. Faqat matn shablonlari (`...`) bo'linmagan — ularni bo'lsak, chiqish o'zgaradi. Asosiy g'oyalar: tashqi ma'lumot (unknown) tekshirilib Kurs ga aylanadi, noto'g'ri yozuv tushunarli TypeError beradi. Konvertatsiya doim so'm orqali: USD → so'm → EUR.

9.4 src/manba.ts — tarmoq va fayl

ts
// Kurslar manbasi: CBU ochiq API (kalitsiz)
// yoki fixtures/ dagi saqlangan javob
import { readFile } from "node:fs/promises";
import { royxatniOqi, type Kurs } from "./kurs.ts";

export const API = "https://cbu.uz/uz/arkhiv-kursov-valyut/json/";
export const FIXTURE = new URL(
  "../fixtures/kurslar-2026-10-23.json",
  import.meta.url,
);

export interface Natija {
  kurslar: Kurs[];
  manba: "api" | "fixture";
}

export async function apidanOl(asos = API): Promise<Kurs[]> {
  const javob = await fetch(asos);
  if (!javob.ok)
    throw new Error(`CBU API ${String(javob.status)} qaytardi`);
  return royxatniOqi(await javob.json());
}

export async function fixturedanOl(): Promise<Kurs[]> {
  return royxatniOqi(JSON.parse(await readFile(FIXTURE, "utf8")));
}

FIXTURE — import.meta.url ga nisbatan: CLI qaysi papkadan chaqirilsa ham fayl topiladi (Global obyektlar darsidagi cwd tuzog'i).

9.5 src/cli.ts — process bilan ishlaydigan qism

Darsning hamma tushunchasi shu faylda: argv, env, stdout/stderr, exit kodlari, signal.

ts
#!/usr/bin/env node
// valyuta — CBU kurslari terminalda. Exit kodlar:
// 0 — muvaffaqiyat, 1 — ish vaqtidagi xato,
// 2 — noto'g'ri foydalanish, 130 — Ctrl+C
import { SOM, konvert, sonniYoz, type Kurs } from "./kurs.ts";
import { apidanOl, fixturedanOl } from "./manba.ts";

const YORDAM = `Foydalanish:
  valyuta kurs [KOD...]          kurslar jadvali (masalan: valyuta kurs USD EUR)
  valyuta <miqdor> <KOD> [KOD]   konvertatsiya (sukut bo'yicha so'mga)

Muhit o'zgaruvchilari:
  VALYUTA_API      API manzili (sukut: CBU)
  VALYUTA_OFFLINE  1 bo'lsa — tarmoqsiz, fixtures/ dagi kurslar`;

class FoydalanishXatosi extends Error {}

async function kurslarniOl(env: NodeJS.ProcessEnv): Promise<Kurs[]> {
  if (env.VALYUTA_OFFLINE === "1") return fixturedanOl();
  try {
    return await apidanOl(env.VALYUTA_API);
  } catch (xato) {
    const sabab = xato instanceof Error ? xato.message : String(xato);
    process.stderr.write(
      `Ogohlantirish: API ishlamadi (${sabab}) — saqlangan kurslar ishlatildi\n`,
    );
    return fixturedanOl();
  }
}

function top(kurslar: Kurs[], kod: string): Kurs {
  const k = kod.toUpperCase();
  if (k === "UZS") return SOM;
  const topildi = kurslar.find((x) => x.kod === k);
  if (!topildi)
    throw new FoydalanishXatosi(`Noma'lum valyuta: ${kod}`);
  return topildi;
}

function jadval(kurslar: Kurs[]): string {
  return kurslar
    .map(
      (k) =>
        `${k.kod.padEnd(4)} ${String(k.nominal).padStart(3)} ${sonniYoz(k.kurs).padStart(12)} ${sonniYoz(k.farq).padStart(8)}  ${k.nomi}`,
    )
    .join("\n");
}

export async function main(
  argv: string[],
  env: NodeJS.ProcessEnv,
): Promise<number> {
  const [birinchi, ...qolgan] = argv;
  if (
    birinchi === undefined ||
    birinchi === "--help" ||
    birinchi === "-h"
  ) {
    process.stdout.write(YORDAM + "\n");
    return birinchi === undefined ? 2 : 0;
  }
  try {
    const kurslar = await kurslarniOl(env);
    if (birinchi === "kurs") {
      const tanlangan = qolgan.length
        ? qolgan.map((kod) => top(kurslar, kod))
        : kurslar;
      process.stdout.write(
        `${kurslar[0]?.sana ?? ""} holatiga\n${jadval(tanlangan)}\n`,
      );
      return 0;
    }
    const miqdor = Number(birinchi);
    const [dan, ga = "UZS"] = qolgan;
    if (!Number.isFinite(miqdor) || dan === undefined)
      throw new FoydalanishXatosi(
        `Tushunarsiz buyruq: ${argv.join(" ")}`,
      );
    const natija = konvert(
      miqdor,
      top(kurslar, dan),
      top(kurslar, ga),
    );
    process.stdout.write(
      `${sonniYoz(miqdor)} ${dan.toUpperCase()} = ${sonniYoz(natija)} ${ga.toUpperCase()}\n`,
    );
    return 0;
  } catch (xato) {
    if (xato instanceof FoydalanishXatosi) {
      process.stderr.write(`Xato: ${xato.message}\n\n${YORDAM}\n`);
      return 2;
    }
    process.stderr.write(
      `Xato: ${xato instanceof Error ? xato.message : String(xato)}\n`,
    );
    return 1;
  }
}

process.once("SIGINT", () => {
  process.stderr.write("\nTo'xtatildi\n");
  process.exit(130);
});

process.exitCode = await main(process.argv.slice(2), process.env);

Darsdagi har qoida qayerda ekanini ko'ring:

Qoida cli.ts da
argv — slice(2) oxirgi qator: main(process.argv.slice(2), …)
env — matn, aniq solishtirish env.VALYUTA_OFFLINE === "1"
natija → stdout, xato → stderr process.stdout.write / process.stderr.write
exitCode, exit() emas process.exitCode = await main(...) (top-level await)
exit() — faqat signalda SIGINT ishlovchisi, kod 130
noto'g'ri foydalanish — 2 FoydalanishXatosi → return 2

main argv va env'ni parametr sifatida oladi — process ga to'g'ridan yopishmaydi. «Argumentlarni qo'lda tahlil qilish» bo'limidagi parseCommand g'oyasi shu: testda istalgan argv va env berish mumkin. Birinchi qatordagi #!/usr/bin/env node — shebang: Unix tizimlariga "bu faylni node bilan ishga tushir" deydi. Uning ahamiyatini Workspaces darsida haqiqiy xato bilan ko'rasiz.

--help 0 qaytaradi (foydalanuvchi yordam so'radi — muvaffaqiyat), argumentsiz chaqiruv esa 2 (buyruq to'liq emas).

9.6 Ishga tushiramiz

Barcha chiqishlar haqiqiy — biz kanon holat 08 da ishga tushirdik (VALYUTA_OFFLINE=1, Git Bash):

bash
VALYUTA_OFFLINE=1 node src/cli.ts kurs USD EUR IDR
text
2026-10-23 holatiga
USD    1    11 903.15   -62.36  AQSH dollari
EUR    1    13 329.15  -108.12  EVRO
IDR   10         6.59    -0.06  Indoneziya rupiyasi

echo $? → 0. IDR qatorida nominal 10 — 6,59 so'm 10 rupiya uchun. Konvertatsiya:

bash
VALYUTA_OFFLINE=1 node src/cli.ts 100 usd
text
100.00 USD = 1 190 315.00 UZS

Noto'g'ri kod — stderr'ga xato va yordam, exit 2:

text
$ VALYUTA_OFFLINE=1 node src/cli.ts 1 XYZ
Xato: Noma'lum valyuta: XYZ

Foydalanish:
  valyuta kurs [KOD...]          kurslar jadvali (masalan: valyuta kurs USD EUR)
  valyuta <miqdor> <KOD> [KOD]   konvertatsiya (sukut bo'yicha so'mga)

Muhit o'zgaruvchilari:
  VALYUTA_API      API manzili (sukut: CBU)
  VALYUTA_OFFLINE  1 bo'lsa — tarmoqsiz, fixtures/ dagi kurslar
$ echo $?
2

Shu buyruqni 2>/dev/null bilan ishga tushirsak (stderr tashlab yuboriladi), ekran butunlay bo'sh qoladi, kod baribir 2. stdout toza — skript uchun bu to'g'ri xulq.

API ishlamasa — ogohlantirish stderr'ga va fixture. 127.0.0.1:9 — ataylab ishlamaydigan manzil (aniq sababi — fetch bu portni umuman bloklashi — Stack trace darsida ochiladi):

text
$ VALYUTA_API=http://127.0.0.1:9/ node src/cli.ts 1 USD
Ogohlantirish: API ishlamadi (fetch failed) — saqlangan kurslar ishlatildi
1.00 USD = 11 903.15 UZS

Endi Ctrl+C ni o'zingiz sinang. Tarmoq sekin bo'lganda node src/cli.ts kurs ni ishga tushirib darhol Ctrl+C bosing. Kutilgan natija — To'xtatildi va echo $? → 130. Kanon buni avtomatik sinay olmadi (yuqoridagi Windows bo'limiga qarang) — natijangizni mashqda yozib qo'ying.

Halol eslatma: process.exit(130) hech narsani kutmaydi. Keyingi darslarda valyuta faylga ham yozadi (Xavfsiz fayl operatsiyalari — vaqtinchalik .tmp fayl va rename). Ctrl+C aynan yozish paytida bosilsa, .tmp fayl diskda qolib ketishi mumkin. Asosiy fayl buzilmaydi, lekin chiqindi qoladi. Kanon buni sinamagan — o'sha darsda yana eslaymiz.

9.7 Testlar va lint topilmasi

Ikkita test fayli, jami 10 test. test/kurs.test.ts — modelning 5 testi (nominal, xato xabarlari, format). test/cli.test.ts — CLI'ni haqiqiy jarayon sifatida ishga tushiradi va exit kod, stdout, stderr ni tekshiradi:

ts
import assert from "node:assert/strict";
import { execFile } from "node:child_process";
import { test } from "node:test";
import { promisify } from "node:util";

const CLI = new URL(
  "../src/cli.ts",
  import.meta.url,
).pathname.replace(/^\/(\w:)/, "$1");

async function ishgaTushir(
  args: string[],
  env: Record<string, string> = { VALYUTA_OFFLINE: "1" },
) {
  try {
    const { stdout, stderr } = await promisify(execFile)(
      process.execPath,
      [CLI, ...args],
      { env: { ...process.env, ...env } },
    );
    return { kod: 0, stdout, stderr };
  } catch (x) {
    const e = x as { code: number; stdout: string; stderr: string };
    return { kod: e.code, stdout: e.stdout, stderr: e.stderr };
  }
}

test("noma'lum valyuta — stderr va exit 2", async () => {
  const r = await ishgaTushir(["1", "XYZ"]);
  assert.equal(r.kod, 2);
  assert.equal(r.stdout, "");
  assert.match(r.stderr, /Noma'lum valyuta: XYZ/);
});

(Fayldan bitta test ko'rsatilgan; qolgan to'rttasi — kurs USD jadvali, konvertatsiya, argumentsiz/--help kodlari va API yiqilganda fixture.) execFile(process.execPath, …) — joriy Node'ning o'zi bilan bola jarayon ochish; child_process darsida chuqur ko'rasiz. Hozir g'oya muhim: CLI'ning exit kodini faqat uni alohida jarayonda ishga tushirib tekshirish mumkin.

bash
npm run check

Biz ishga tushirdik — tip (tsc) xatosiz, lint xatosiz, test natijasining oxiri:

text
ℹ tests 10
ℹ suites 0
ℹ pass 10
ℹ fail 0
ℹ cancelled 0
ℹ skipped 0
ℹ todo 0

Kanon topilmasi. ESLint konfiguratsiyasidagi istisno blokisiz npm run lint 10 ta xato beradi — har test(...) chaqiruvida (biz qayta ishga tushirdik, chiqish qisqartirilgan):

text
test\cli.test.ts
  18:1  error  Promises must be awaited, end with a call to .catch, end with a call to .then with a rejection handler or be explicitly marked as ignored with the `void` operator  @typescript-eslint/no-floating-promises
✖ 10 problems (10 errors, 0 warnings)

Tarjimasi: "Promise kutilishi, .catch bilan tugashi … yoki void bilan ataylab e'tiborsiz belgilanishi kerak". Qoida to'g'ri: osilib qolgan Promise — xato yashirinadigan joy. Lekin node:test ning test() Promise qaytaradi va uni runner o'zi kutadi. typescript-eslint hujjatidagi yechim — allowForKnownSafeCalls: "node:test paketidagi test, describe, it, suite xavfsiz". Har chaqiruvga void yozishdan ko'ra bitta konfiguratsiya qatori toza.

9.8 O'lchov va commit

Kanon tekshiruvi (tekshiruv-08.txt, Node 24.21.0, i5-12500H):

Ko'rsatkich Qiymat
tip / lint / test 0 xato / 0 xato / 10 dan 10
prod bog'liqlik 0
dev bog'liqlik 5 (98 paket, 47 MB)
sovuq start --help 127 ms (mediana, n=10)
sovuq start kurs USD 171 ms

Bo'sh Node 73 ms edi (Node.js nima) — .ts faylni tiplarini o'chirib yuklash va fixture o'qish qolgan ~50–100 ms ni oladi. Bu raqamni build bilan solishtirishni ES Modules va CJS va ESM birga darslarida (holat 12) ko'rasiz.

Commit (kanondagi xabar):

bash
git add .
git commit -m "valyuta: CBU kurslari va konvertatsiya — argv, env, exit kodlari, Ctrl+C"

10. Ko'p uchraydigan xatolar

Belgi Sabab Davosi
Number(process.argv[1]) — NaN foydalanuvchi argumentlari indeks 2 dan process.argv.slice(2)
if (process.env.X) — "undefined" bilan ham rost env ga undefined yozilgan → "undefined" matni delete process.env.X
Xatoda ham exit kod 0, CI "yashil" exitCode qo'yilmagan xatoda process.exitCode = 1 (yoki 2)
Fayl yarim yozilgan, log yo'qolgan process.exit() asinxron ishni uzdi exitCode + tabiiy tugash
Ctrl+C ishlamay qoldi SIGINT ishlovchisi exit chaqirmaydi ishlovchi oxirida process.exit(130)
child.kill("SIGTERM") — ishlovchi chaqirilmadi (Windows) Windows'da Unix signallari yo'q ohista to'xtashni boshqa kanal bilan (IPC, HTTP) qiling
> natija.txt ichida ogohlantirish ogohlantirish stdout'ga yozilgan xabarlar — stderr'ga

11. Mashqlar

Mashqlar kurs/mashqlar/24/08-process/ da (fixture fayli ham shu yerda).

1-mashq (oson): exit kodni tanlang

valyuta kelishuvi bo'yicha qaysi kod:

  • valyuta kurs USD muvaffaqiyatli —
  • valyuta 100 ABC (bunday valyuta yo'q) —
  • Fixture fayli o'chirib yuborilgan, API ham ishlamadi —
  • Foydalanuvchi Ctrl+C bosdi —
  • valyuta --help —
Yechim
  • 0 — natija chiqdi.
  • 2 — foydalanuvchi xatosi (FoydalanishXatosi), yordam ham chiqadi.
  • 1 — ish vaqtidagi xato: buyruq to'g'ri, lekin ma'lumot olib bo'lmadi.
  • 130 — 128 + 2 (SIGINT).
  • 0 — yordam so'ralgan va berilgan. Argumentsiz valyuta esa 2.

2-mashq (o'rta): exit kod bilan tahlil

«Argumentlarni qo'lda tahlil qilish» bo'limidagi parseCommand ni oling. Uning ustiga run(args) funksiyasini yozing — u { out, err, code } qaytarsin. out — stdout'ga, err — stderr'ga ketadigan matn. help → kod 0 (agar --help bo'lsa) yoki 2 (argumentsiz). error → err da xabar, kod 2. rates → out da "kurslar: USD, EUR" (kodsiz bo'lsa "kurslar: hammasi"), kod 0. convert → out da "100 USD → UZS", kod 0. To'rt holatni sinang.

Yechim
js
function parseCommand(args) {
  const [first, ...rest] = args;
  if (first === undefined || first === "--help") {
    return { type: "help", asked: first === "--help" };
  }
  if (first === "kurs") return { type: "rates", codes: rest };
  const amount = Number(first);
  if (!Number.isFinite(amount) || rest[0] === undefined) {
    const message = `Tushunarsiz: ${args.join(" ")}`;
    return { type: "error", message };
  }
  const [from, to = "UZS"] = rest;
  return { type: "convert", amount, from, to };
}

function run(args) {
  const cmd = parseCommand(args);
  switch (cmd.type) {
    case "help": {
      const code = cmd.asked ? 0 : 2;
      return { out: "Foydalanish: ...", err: "", code };
    }
    case "error":
      return { out: "", err: `Xato: ${cmd.message}`, code: 2 };
    case "rates": {
      const list = cmd.codes.join(", ") || "hammasi";
      return { out: `kurslar: ${list}`, err: "", code: 0 };
    }
    default: {
      const from = cmd.from.toUpperCase();
      const line = `${cmd.amount} ${from} → ${cmd.to}`;
      return { out: line, err: "", code: 0 };
    }
  }
}

const samples = [
  ["--help"], [], ["yuz", "usd"], ["kurs"], ["100", "usd"],
];
for (const args of samples) {
  const r = run(args);
  console.log(r.code, r.out || r.err);
}

Konsolda:

text
0 Foydalanish: ...
2 Foydalanish: ...
2 Xato: Tushunarsiz: yuz usd
0 kurslar: hammasi
0 100 USD → UZS

run ham process ga tegmaydi — natijani qaytaradi xolos. Haqiqiy CLI'da faqat bitta joy qoladi: out ni stdout'ga, err ni stderr'ga yozish va process.exitCode = r.code. Bu bo'linish — valyuta dagi main() ning aynan o'zi.

3-mashq (qiyin): o'zini bola jarayon sifatida sinaydigan skript

salom.mjs yozing. Agar birinchi argument bola bo'lsa, u bola rolida ishlaydi: ikkinchi argument (ism) bo'lsa — stdout'ga Salom, <ism>!, kod 0; ism bo'lmasa — stderr'ga Xato: ism kerak, kod 2. SALOM_TIL=ru bo'lsa — Privet, <ism>!. Aks holda (ota rolida) u o'zini spawnSync(process.execPath, [import.meta.filename, "bola", …]) bilan uch marta ishga tushirsin va har natijani kod | stdout | stderr ko'rinishida chiqarsin.

Yechim
js
import { spawnSync } from "node:child_process";

const [role, name] = process.argv.slice(2);

if (role === "bola") {
  if (name === undefined) {
    process.stderr.write("Xato: ism kerak\n");
    process.exitCode = 2;
  } else {
    const word = process.env.SALOM_TIL === "ru" ? "Privet" : "Salom";
    process.stdout.write(`${word}, ${name}!\n`);
  }
} else {
  const cases = [
    { args: ["Aziz"], env: {} },
    { args: [], env: {} },
    { args: ["Malika"], env: { SALOM_TIL: "ru" } },
  ];
  for (const c of cases) {
    const r = spawnSync(process.execPath,
      [import.meta.filename, "bola", ...c.args],
      { encoding: "utf8", env: { ...process.env, ...c.env } });
    const [out, err] = [r.stdout.trim(), r.stderr.trim()];
    console.log(`${r.status} | ${out} | ${err}`);
  }
}

Konsolda:

text
0 | Salom, Aziz! |
2 |  | Xato: ism kerak
0 | Privet, Malika! |

Uchta muhim joy: bola process.exitCode bilan chiqadi (exit() emas); env bolaga { ...process.env, ...c.env } bilan beriladi (aks holda PATH ham yo'qoladi); ota bola kodini r.status dan oladi. valyuta testlari aynan shu usulda ishlaydi.

4-mashq: CLI qadami — valyuta ni yarating

«CLI qadami» bo'limidagi qadamlarni bajaring: skelet, uch modul, ikki test fayli, npm run check. Keyin:

  1. VALYUTA_OFFLINE=1 node src/cli.ts kurs USD EUR IDR — jadval kanondagi bilan bir xilmi?
  2. node src/cli.ts 1 XYZ; echo $? (PowerShell'da ; $LASTEXITCODE) — 2 chiqdimi?
  3. Ctrl+C ni sinang: node src/cli.ts kurs ni ishga tushirib darhol bosing. To'xtatildi va 130 chiqdimi? Natijangizni yozib qo'ying — kanon buni sinay olmagan.
  4. Commit: valyuta: CBU kurslari va konvertatsiya — argv, env, exit kodlari, Ctrl+C.
Yechim
  1. Ha — fixture bir xil bo'lsa, jadval «Ishga tushiramiz» bo'limidagidek. Fixtureni o'zingiz olgan bo'lsangiz, sana va kurslar boshqa bo'ladi — testlarni moslang.
  2. 2. Agar 0 chiqsa — process.exitCode qatorini tekshiring.
  3. Tarmoq tez bo'lsa, CLI Ctrl+C ga ulgurmasdan tugaydi. Sinash uchun VALYUTA_API ga javobi sekin manzil bering yoki main boshiga vaqtincha await new Promise((r) => setTimeout(r, 5000)); qo'shing (keyin olib tashlang). Kutilgan: To'xtatildi va 130. PowerShell'da Ctrl+C dan keyin $LASTEXITCODE ham 130 bo'lishi kerak; natija boshqacha bo'lsa — yozib qo'ying, bu readline va terminal darsida asqotadi.
  4. git log --oneline da bitta commit. npm pack --dry-run — 9 fayl, 9.3 kB: test va tsconfig.json ham paketga kirmoqda. Bu ataylab qoldirilgan kamchilik — CJS va ESM birga darsida files bilan tuzatamiz.

12. Real ishda

  • CI va skriptlar — har CLI'ning exit kodi avtomatlashtirishning asosi. eslint, tsc, node --test xatoda nolga teng bo'lmagan kod qaytaradi, shuning uchun npm run check va GitHub Actions xatoni sezadi.
  • Docker va Kubernetes — konteynerni to'xtatishda avval SIGTERM yuboriladi, ~10–30 soniyadan keyin SIGKILL. SIGTERM ni ushlamagan server so'rovlarni yarim yo'lda uzadi. Buni Docker qismida ko'rasiz.
  • 12-faktor ilova — sozlama koddan emas, muhit o'zgaruvchilaridan o'qiladi (Next.js'da env darsidagi g'oya); Node'da bu — process.env va --env-file.
  • Intervyu savoli: "process.exit() va process.exitCode farqi?" — birinchisi darhol uzadi (asinxron ish yo'qolishi mumkin), ikkinchisi kodni belgilaydi va jarayon tabiiy tugaydi.

Xulosa

  • process.argv.slice(2) — foydalanuvchi argumentlari, hammasi matn; tahlilni alohida funksiyaga ajrating.
  • process.env — faqat matn; undefined yozsangiz "undefined" bo'ladi, o'chirish — delete.
  • Natija — stdout, xato va ogohlantirish — stderr.
  • Exit kod: 0 muvaffaqiyat, 1 xato, 2 noto'g'ri foydalanish, 130 Ctrl+C; process.exitCode afzal, exit() — faqat signal ishlovchisida.
  • SIGINT/SIGTERM ni ushlash mumkin, SIGKILL ni yo'q; Windows'da boshqa jarayon yuborgan signal ishlovchini chaqirmaydi (o'lchandi).
  • valyuta boshlandi: 0 prod bog'liqlik, .ts to'g'ridan Node 24 da, 10 test, birinchi commit.

Keyingi dars: Node xotira limitlari: --max-old-space-size va heap statistikasi — process.memoryUsage() raqamlari nimani bildiradi va xotira tugaganda nima bo'ladi.

Manbalar

Ulashish:Telegram'da

Izohlar (0)

Izoh yozish uchun kiring.

  • Hozircha izoh yo'q. Birinchi bo'ling!
Node.js process obyekti: argv, env, exit kodlari va signallar — IlmHamroh