@florinszilagyi/anaf-ts-sdk

中文文档
2.0.6发布于 2026/11/13MITTypeScriptESM + CJS构建来源已验证近 30 天下载 6011

Complete TypeScript SDK for Romanian ANAF API -E-Factura, Company checks

原始使用文档
文档目录37 节
  1. Features
  2. Installation
  3. Quick Start
  4. 1. AnafAuthenticator - OAuth 2.0 Authentication
  5. 2. AnafEfacturaClient - API Operations
  6. 3. UblBuilder - UBL XML Generation
  7. 4. AnafDetailsClient - Company Data Lookup
  8. 5. parseReceivedInvoice - Reading Received Invoices
  9. AnafDetailsClient Configuration
  10. Configuration Examples
  11. Use Cases for Custom URL
  12. Complete Example
  13. Development & Testing
  14. Prerequisites
  15. Environment Setup
  16. Local Development with ngrok
  17. OAuth Authentication Flow
  18. Automated Testing
  19. Manual OAuth Testing
  20. Testing Environment
  21. Token Management
  22. Troubleshooting
  23. API Coverage
  24. Authentication
  25. Document Operations
  26. Message Management
  27. Validation & Conversion
  28. UBL Generation
  29. Company Data Lookup
  30. Environment Configuration
  31. Error Handling
  32. TypeScript Support
  33. Security Best Practices
  34. Production Deployment
  35. License
  36. Contributing
  37. Support

ANAF e-Factura TypeScript SDK

A comprehensive TypeScript SDK for interacting with the Romanian ANAF e-Factura system. This SDK provides OAuth 2.0 authentication, document upload/download, validation, UBL generation, and company data lookup capabilities.

Features

  • OAuth 2.0 Authentication: Complete OAuth flow with USB token support
  • Document Operations: Upload, status checking, and download
  • Message Management: List and paginate invoice messages
  • Validation: XML validation and digital signature verification
  • PDF Conversion: Convert XML invoices to PDF format
  • UBL Generation: Create compliant UBL 2.1 XML invoices
  • Received-Invoice Parsing: Read incoming UBL 2.1 and CII documents into a typed header plus its lines
  • Company Data Lookup: Fetch Romanian company details from public ANAF API
  • TypeScript: Full type safety and IntelliSense support

Installation

pnpm add @florinszilagyi/anaf-ts-sdk

Quick Start

The SDK is organized into four main classes:

1. AnafAuthenticator - OAuth 2.0 Authentication

import { AnafAuthenticator } from '@florinszilagyi/anaf-ts-sdk';

const auth = new AnafAuthenticator({
  clientId: 'your-oauth-client-id',
  clientSecret: 'your-oauth-client-secret',
  redirectUri: 'https://your-app.com/oauth/callback',
});

// Get authorization URL (user will authenticate with USB token)
const authUrl = auth.getAuthorizationUrl();
console.log('Redirect user to:', authUrl);

// Exchange authorization code for tokens
const tokens = await auth.exchangeCodeForToken(authorizationCode);
console.log('Access token:', tokens.access_token);

// Refresh tokens when needed
const newTokens = await auth.refreshAccessToken(tokens.refresh_token);

2. AnafEfacturaClient - API Operations

The client is constructed once with a refresh token and resolves access tokens itself, so no call takes a token argument.

import { AnafEfacturaClient } from '@florinszilagyi/anaf-ts-sdk';

const client = new AnafEfacturaClient(
  {
    vatNumber: 'RO12345678',
    testMode: true, // Use test environment
    refreshToken: tokens.refresh_token,
  },
  auth // the AnafAuthenticator from step 1
);

// Upload a document
const uploadResult = await client.uploadDocument(xmlContent, {
  standard: 'UBL',
  executare: true,
});

// Check upload status
const status = await client.getUploadStatus(uploadResult.indexIncarcare);

// Download processed document (ZIP archive: invoice + detached signature)
if (status.idDescarcare) {
  const result = await client.downloadDocument(status.idDescarcare);

  // Or unwrap the archive and get the invoice XML directly
  const invoiceXml = await client.downloadDocumentXml(status.idDescarcare);

  // …which is what you need to render the invoice as a PDF
  const pdf = await client.convertXmlToPdf(invoiceXml, 'FACT1');
}

// List recent messages
const messages = await client.getMessages({
  zile: 7, // Last 7 days
  filtru: 'E', // Only errors
});

// Validate XML
const validation = await client.validateXml(xmlContent, 'FACT1');

// Convert XML to PDF
const pdfBuffer = await client.convertXmlToPdf(xmlContent, 'FACT1');

3. UblBuilder - UBL XML Generation

import { UblBuilder } from '@florinszilagyi/anaf-ts-sdk';

const builder = new UblBuilder();

const xml = builder.generateInvoiceXml({
  invoiceNumber: 'INV-2024-001',
  issueDate: new Date(),
  supplier: {
    registrationName: 'Company SRL',
    companyId: 'RO12345678',
    vatNumber: 'RO12345678',
    address: {
      street: 'Str. Example 1',
      city: 'Bucharest',
      postalZone: '010101',
    },
  },
  customer: {
    registrationName: 'Customer SRL',
    companyId: 'RO87654321',
    address: {
      street: 'Str. Customer 2',
      city: 'Cluj-Napoca',
      postalZone: '400001',
    },
  },
  lines: [
    {
      description: 'Product/Service',
      quantity: 1,
      unitPrice: 100,
      taxPercent: 19,
    },
  ],
  isSupplierVatPayer: true,
});

Optional parties and contacts:

const xml = builder.generateInvoiceXml({
  // ...the fields above
  note: 'Delegat: Ion Exemplu', // BT-22
  supplier: { /* ... */ contactName: 'Ana Exemplu' }, // BT-41
  customer: { /* ... */ contactName: 'Ion Exemplu' }, // BT-56
  // BG-11: only for a seller that has appointed a tax representative.
  taxRepresentative: {
    name: 'Reprezentant Exemplu SRL',
    vatNumber: 'RO11223344', // with country prefix
    address: { street: 'Str. Exemplu 5', city: 'Cluj-Napoca', postalZone: '400002', county: 'RO-CJ' },
  },
});

The builder throws AnafValidationError when the tax representative has no name, no prefixed VAT number, no ISO 3166-2:RO county code (RO-CJ) for a Romanian address, or when the supplier is not a VAT payer.

4. AnafDetailsClient - Company Data Lookup

import { AnafDetailsClient } from '@florinszilagyi/anaf-ts-sdk';

const detailsClient = new AnafDetailsClient({
  timeout: 30000,
  url: 'https://webservicesp.anaf.ro/api/PlatitorTvaRest/v9/tva', // Optional: custom ANAF API URL
});

// Fetch company data by VAT code (single company)
const result = await detailsClient.getCompanyData('RO12345678');
if (result.success) {
  console.log('Company:', result.data[0].name);
  console.log('Address:', result.data[0].address);
  console.log('VAT registered:', result.data[0].scpTva);
  console.log('Registration number:', result.data[0].registrationNumber);
  console.log('Phone:', result.data[0].contactPhone);
} else {
  console.error('Error:', result.error);
}

// Validate VAT code format
const isValid = await detailsClient.isValidVatCode('RO12345678');
console.log('Valid format:', isValid);

// Batch fetch multiple companies (single API call)
const batchResult = await detailsClient.batchGetCompanyData(['RO12345678', 'RO87654321', 'RO11111111']);
if (batchResult.success) {
  batchResult.data.forEach((company, index) => {
    console.log(`Company ${index + 1}:`, company.name);
    console.log(`VAT registered:`, company.scpTva);
  });
} else {
  console.error('Batch error:', batchResult.error);
}

// Bulk lookup: max 100 CUIs, a fixed request date, no e-Factura registry request
const bulk = await detailsClient.batchGetCompanyData(['12345678', '87654321'], {
  date: '2026-01-31',
  includeEfacturaRegistry: false,
});
if (bulk.success) {
  bulk.data.forEach((company) => console.log(company.vatCode, company.caenCode));
  console.log('Not found:', bulk.notFound);
} else if (bulk.retryable) {
  console.log('Retry later', bulk.statusCode, bulk.retryAfter);
} else {
  console.log('Final answer:', bulk.error, bulk.notFound);
}

// Configuration options
const customClient = new AnafDetailsClient({
  timeout: 60000, // 60 second timeout
  url: 'https://custom-anaf-proxy.example.com/api/tva', // Custom endpoint (e.g., proxy server)
});

5. parseReceivedInvoice - Reading Received Invoices

Parses a document received through SPV — UBL 2.1 Invoice/CreditNote or CII CrossIndustryInvoice — into a flat header plus its source lines. Every amount comes back as the exact decimal string the document published, never a JavaScript number, so nothing is lost to float representation before it reaches your own money handling.

import { parseReceivedInvoice } from '@florinszilagyi/anaf-ts-sdk';

const xml = await client.downloadDocumentXml(status.idDescarcare);
const invoice = parseReceivedInvoice(xml);

invoice.documentStandard; // 'ubl' | 'cii'
invoice.documentKind; // 'invoice' | 'credit_note' | 'corrective' | a raw type code
invoice.pdfStandard; // 'FACT1' | 'FCN' — which converter convertXmlToPdf needs
invoice.invoiceNumber; // 'EXMPL-2026-001'
invoice.issueDate; // '2026-08-01'
invoice.supplierName; // 'Exemplu SRL'
invoice.supplierVatCode; // 'RO12345678'
invoice.totalAmount; // '119.00' — a string, not 119
invoice.notes; // every invoice note (BT-22) in document order; [] when there are none
invoice.supplierContact; // { name, telephone, email } | null
invoice.customerContact; // { name, telephone, email } | null
invoice.taxRepresentative; // { name, vatCode, address } | null (BG-11)
invoice.sourceLines; // one ReceivedInvoiceLine per document line
invoice.sourceLineDiagnostics; // human-readable notes for malformed/absent line facts

Every header field is nullable. invoiceNumber, issueDate, totalAmount and the rest come back as null when the document does not publish them or publishes them in a form that does not validate — the parser never substitutes a default. Check the fields your own flow depends on before using them.

The parser refuses to guess. It throws:

  • AnafValidationError — the document is oversized (12 MB of UTF-8) or its root element is not a recognised invoice root.
  • AnafXmlParsingError — the XML is not well-formed, declares an external entity in its DTD (even an unreferenced one), or names an element with a reserved JavaScript property name such as __proto__.
  • AnafAmbiguousTaxTotalError — the document carries several VAT totals and none of them uniquely matches the document currency.

Fields it cannot read as a valid decimal, date or currency come back as null rather than coerced, and unreadable line facts are reported in sourceLineDiagnostics while the line itself is kept.

General and DTD entity expansion stays disabled (these documents are supplier-authored, and a recursive entity definition would balloon memory). The five predefined XML entities and decimal/hex character references — the way Romanian diacritics usually arrive — are decoded, exactly one level deep: & is the literal text &, never &.

AnafDetailsClient Configuration

The AnafDetailsClient supports the following configuration options:

OptionTypeDefaultDescription
timeoutnumber30000Request timeout in milliseconds
urlstring'https://webservicesp.anaf.ro/api/PlatitorTvaRest/v9/tva'ANAF API endpoint URL

Configuration Examples

// Default configuration
const client = new AnafDetailsClient();

// Custom timeout
const clientWithTimeout = new AnafDetailsClient({
  timeout: 60000, // 60 seconds
});

// Custom API endpoint (useful for proxy servers or testing)
const clientWithCustomUrl = new AnafDetailsClient({
  url: 'https://your-proxy.example.com/anaf-api',
  timeout: 45000,
});

// Minimal configuration
const minimalClient = new AnafDetailsClient({
  timeout: 15000, // Fast timeout for quick responses
});

Use Cases for Custom URL

  • Proxy Server: Route requests through your own proxy for logging/monitoring
  • Load Balancer: Distribute requests across multiple ANAF endpoints
  • Testing: Point to a mock server during development
  • Regional Endpoints: Use different ANAF regional servers if available
  • Corporate Firewall: Route through approved corporate gateways

Complete Example

import { AnafAuthenticator, AnafClient, AnafDetailsClient, UblBuilder } from '@florinszilagyi/anaf-ts-sdk';

// Setup
const auth = new AnafAuthenticator({
  clientId: process.env.ANAF_CLIENT_ID,
  clientSecret: process.env.ANAF_CLIENT_SECRET,
  redirectUri: 'https://myapp.com/oauth/callback',
});

const client = new AnafClient({
  vatNumber: 'RO12345678',
  testMode: true,
});

const detailsClient = new AnafDetailsClient();
const builder = new UblBuilder();

// 1. Get customer company data
const customerData = await detailsClient.getCompanyData('RO87654321');
if (!customerData.success) {
  throw new Error(`Customer not found: ${customerData.error}`);
}

// 2. Authentication (one-time setup)
const authUrl = auth.getAuthorizationUrl();
// Direct user to authUrl, they authenticate with USB token
const tokens = await auth.exchangeCodeForToken(authCode);

// 3. Generate invoice XML using fetched company data
const xml = builder.generateInvoiceXml({
  invoiceNumber: 'INV-2024-001',
  issueDate: new Date(),
  supplier: {
    registrationName: 'My Company SRL',
    companyId: 'RO12345678',
    vatNumber: 'RO12345678',
    address: {
      street: 'Str. Example 1',
      city: 'Bucharest',
      postalZone: '010101',
    },
  },
  customer: {
    registrationName: customerData.data[0].name,
    companyId: customerData.data[0].vatCode,
    vatNumber: customerData.data[0].vatCode,
    address: {
      street: customerData.data[0].address,
      city: 'Cluj-Napoca', // Parse from address if needed
      postalZone: customerData.data[0].postalCode || '400001',
    },
  },
  lines: [
    {
      description: 'Consulting Services',
      quantity: 1,
      unitPrice: 1000,
      taxPercent: customerData.data[0].scpTva ? 19 : 0, // Apply VAT if customer is VAT registered
    },
  ],
  isSupplierVatPayer: true,
});

// 4. Upload to ANAF
const uploadResult = await client.uploadDocument(tokens.access_token, xml);

// 5. Monitor status
const status = await client.getUploadStatus(tokens.access_token, uploadResult.index_incarcare);

// 6. Download result
if (status.id_descarcare) {
  const result = await client.downloadDocument(tokens.access_token, status.id_descarcare);
}

Development & Testing

Prerequisites

  1. USB Security Token: Required for ANAF authentication

    • Supported tokens: Any qualified certificate from Romanian CA
    • Install manufacturer drivers (SafeNet, Gemalto, etc.)
    • Certificate must be registered with ANAF SPV
  2. ANAF OAuth Application: Register at ANAF Portal

    • Navigate: Servicii Online → Înregistrare utilizatori → DEZVOLTATORI APLICAȚII
    • Register application with your callback URL

Environment Setup

Create a .env file in your project root:

ANAF_CLIENT_ID=your_oauth_client_id_here
ANAF_CLIENT_SECRET=your_oauth_client_secret_here

Local Development with ngrok

For local testing, you need a public HTTPS URL for OAuth callbacks:

  1. Install ngrok:

    # Using npm
    npm install -g ngrok
    
    # Or download from https://ngrok.com/
    
  2. Expose local server:

    # Start your local server on port 3000
    npm start
    
    # In another terminal, expose it publicly
    ngrok http 3000
    
  3. Update OAuth Settings:

    • Copy the ngrok HTTPS URL (e.g., https://abc123.ngrok.io)
    • Register callback URL: https://abc123.ngrok.io/oauth/callback
    • Update your AnafAuthenticator configuration:
    const auth = new AnafAuthenticator({
      clientId: process.env.ANAF_CLIENT_ID,
      clientSecret: process.env.ANAF_CLIENT_SECRET,
      redirectUri: 'https://abc123.ngrok.io/oauth/callback', // Your ngrok URL
    });
    

OAuth Authentication Flow

The complete OAuth flow with USB token authentication:

  1. Generate Authorization URL:

    const authUrl = auth.getAuthorizationUrl();
    console.log('Direct user to:', authUrl);
    
  2. User Authentication Process:

    • User clicks/visits the authorization URL
    • ANAF login page opens
    • Insert USB Token: User inserts USB security token
    • Enter PIN: User enters token PIN when prompted
    • Certificate Selection: Browser shows certificate selection dialog
    • Select Certificate: User selects appropriate certificate
    • Authorize Application: User grants permissions to your app
    • Redirect: Browser redirects to your callback URL with authorization code
  3. Handle Callback:

    // Your callback endpoint receives: ?code=AUTH_CODE&state=STATE
    app.get('/oauth/callback', async (req, res) => {
      const { code } = req.query;
    
      try {
        const tokens = await auth.exchangeCodeForToken(code);
        // Store tokens securely
        res.send('Authentication successful!');
      } catch (error) {
        res.status(400).send('Authentication failed');
      }
    });
    
  4. Use Access Token:

    // Token is valid for 1 hour
    const client = new AnafClient({ vatNumber: 'RO12345678' });
    const result = await client.uploadDocument(tokens.access_token, xmlContent);
    
  5. Refresh Tokens:

    // Refresh before expiration
    const newTokens = await auth.refreshAccessToken(tokens.refresh_token);
    

Automated Testing

The SDK includes comprehensive Jest tests with an integrated OAuth flow:

# Run all tests
pnpm test

# Run OAuth authentication tests with callback server
pnpm test:auth

# Run tests with coverage
pnpm test:coverage

Manual OAuth Testing

The test suite includes a helpful OAuth testing flow:

  1. Start Test:

    pnpm test:auth
    
  2. Callback Server: Automatically starts on http://localhost:4040

  3. Get OAuth URL: Test displays authorization URL in console

  4. Complete OAuth:

    • Copy URL to browser
    • Insert USB token when prompted
    • Enter PIN and select certificate
    • Authorize application
    • Browser redirects to localhost:4040/callback
  5. Automatic Token Handling: Test captures code and exchanges for tokens

Testing Environment

  • Test Environment: All tests use ANAF test environment
  • OAuth Endpoints: logincert.anaf.ro
  • API Endpoints: api.anaf.ro/test
  • Callback URL: http://localhost:4040/callback (for tests)

Token Management

  • Tokens are automatically saved to token.secret during tests
  • Access tokens expire in 1 hour
  • Refresh tokens have longer validity
  • Tests automatically refresh expired tokens
  • Invalid tokens are cleaned up automatically

Troubleshooting

USB Token Issues

❌ Certificate selection failed

Solutions:

  • Ensure USB token is properly inserted
  • Install manufacturer drivers
  • Try different browsers (Chrome recommended)
  • Check certificate validity in browser settings

OAuth Callback Issues

❌ Redirect URI mismatch

Solutions:

  • Verify callback URL matches registered URL exactly
  • Include protocol (https://) and path
  • For ngrok: use HTTPS URL, not HTTP
  • Check for trailing slashes

Network Issues

❌ Connection refused or timeout

Solutions:

  • Check internet connection
  • Verify firewall settings
  • For ngrok: ensure tunnel is active
  • Try different ngrok region: ngrok http 3000 --region eu

Token Expiration

❌ Access token expired

Solutions:

  • Use refresh token to get new access token
  • Implement automatic token refresh in your app
  • Store token expiration time and refresh proactively

API Coverage

The SDK implements all endpoints from the ANAF e-Factura OpenAPI specification:

Authentication

  • ✅ OAuth 2.0 authorization flow
  • ✅ Token exchange and refresh

Document Operations

  • ✅ Upload documents (/upload, /uploadb2c)
  • ✅ Check upload status (/stareMesaj)
  • ✅ Download processed documents (/descarcare)

Message Management

  • ✅ List messages with pagination (/listaMesajePaginatieFactura)
  • ✅ List recent messages (/listaMesajeFactura)

Validation & Conversion

  • ✅ XML validation (/validare/{standard})
  • ✅ Digital signature validation (/api/validate/signature)
  • ✅ XML to PDF conversion (/transformare/{standard})
  • ✅ XML to PDF without validation (/transformare/{standard}/DA)

UBL Generation

  • ✅ UBL 2.1 compliant XML generation
  • ✅ Romanian CIUS-RO specification support

Company Data Lookup

  • ✅ Fetch company data by VAT code
  • ✅ Validate VAT code format
  • ✅ Batch fetch multiple companies
  • ✅ Cache management

Environment Configuration

The SDK supports both test and production environments:

// Test environment (recommended for development)
const client = new AnafClient({
  vatNumber: 'RO12345678',
  testMode: true,
});

// Production environment
const client = new AnafClient({
  vatNumber: 'RO12345678',
  testMode: false,
});

Error Handling

The SDK provides specific error types for different scenarios:

import { AnafAuthenticationError, AnafValidationError, AnafApiError } from '@florinszilagyi/anaf-ts-sdk';

try {
  await client.uploadDocument(token, xml);
} catch (error) {
  if (error instanceof AnafAuthenticationError) {
    // Handle authentication issues - refresh token or re-authenticate
    console.log('Authentication failed:', error.message);
  } else if (error instanceof AnafValidationError) {
    // Handle validation errors - fix XML or parameters
    console.log('Validation error:', error.message);
  } else if (error instanceof AnafApiError) {
    // Handle API errors - check status, retry, or contact support
    console.log('API error:', error.message);
  }
}

TypeScript Support

The SDK is written in TypeScript and provides comprehensive type definitions:

import type { InvoiceInput, UploadStatus, ListMessagesResponse, ValidationResult, OAuthTokens } from '@florinszilagyi/anaf-ts-sdk';

Security Best Practices

  • Never commit tokens: Add token.secret and .env to .gitignore
  • Use HTTPS: Always use HTTPS for OAuth callbacks in production
  • Validate certificates: Ensure USB token certificates are valid and not expired
  • Secure token storage: Store tokens securely (encrypted, database, secure storage)
  • Implement refresh: Automatically refresh tokens before expiration
  • Test environment: Use test mode for development and staging

Production Deployment

When deploying to production:

  1. Register Production OAuth App:

    • Use your production domain for callback URL
    • Get separate client credentials for production
  2. Environment Configuration:

    const client = new AnafClient({
      vatNumber: 'RO12345678',
      testMode: false, // Production mode
    });
    
  3. Secure Callback Handling:

    • Use HTTPS for all OAuth callbacks
    • Validate state parameter
    • Implement CSRF protection
    • Log authentication events
  4. Token Management:

    • Store tokens securely (encrypted database)
    • Implement automatic refresh
    • Handle refresh token expiration gracefully
    • Monitor token usage and expiration

License

MIT License - see LICENSE file for details.

Contributing

  1. Fork the repository
  2. Create a feature branch
  3. Make your changes
  4. Add tests
  5. Run tests and linting
  6. Submit a pull request

Support

For questions about:


Perfect for: SaaS applications, accounting software, ERP integrations, invoicing systems

Requirements: USB security token, ANAF OAuth registration, Node.js 16+

版本与发布记录

@florinszilagyi/anaf-ts-sdk 共发布 16 个版本,首次发布于 2026/04/15;过去 12 个月发布了 16 个版本。最新版本附带 npm provenance 构建来源证明,可追溯到公开的 CI 构建记录。

版本发布日期距今
2.0.6latest2026/11/132 天前
2.0.52026/11/0214 天前
2.0.42026/11/0114 天前
2.0.32026/11/0114 天前
2.0.22026/11/0115 天前
2.0.12026/10/3116 天前

关于 @florinszilagyi/anaf-ts-sdk 的常见问题

@florinszilagyi/anaf-ts-sdk 是什么?

Complete TypeScript SDK for Romanian ANAF API -E-Factura, Company checks

如何安装 @florinszilagyi/anaf-ts-sdk?

在项目目录运行 npm install @florinszilagyi/anaf-ts-sdk,或使用 pnpm add / yarn add / bun add。

@florinszilagyi/anaf-ts-sdk 支持 TypeScript 吗?

支持。包内自带 TypeScript 类型声明(package.json 声明了 types 入口),安装后即可获得类型提示,无需再安装 @types 包。

@florinszilagyi/anaf-ts-sdk 支持 ESM 还是 CommonJS?

同时提供 ES Module 与 CommonJS 入口,import 和 require() 都可以使用,打包工具会自动选择合适的版本。

@florinszilagyi/anaf-ts-sdk 需要什么版本的 Node.js?

package.json 的 engines 字段要求 Node.js >=20.0.0。版本不符时 npm 默认只给出警告,开启 engine-strict 后会拒绝安装。

@florinszilagyi/anaf-ts-sdk 可以商用吗?

@florinszilagyi/anaf-ts-sdk 使用 MIT 许可。宽松许可:允许商业使用、修改和再分发,通常只需在分发时保留原版权与许可声明。以上为概括,不构成法律意见,请以许可原文为准。

@florinszilagyi/anaf-ts-sdk 的下载量有多少?

根据 npm 官方下载统计,@florinszilagyi/anaf-ts-sdk 在 2026/10/15 – 2026/11/13 这 30 天内被下载 6,011 次,日均约 200 次。下载量会把 CI 构建、镜像同步和作为其他包依赖的安装一并计入,适合横向比较热度,不能直接等同于使用人数。

@florinszilagyi/anaf-ts-sdk 还在维护吗?

最新版本 2.0.6 发布于 2026/11/13(2 天前);至今共发布 16 个版本,其中过去 12 个月发布了 16 个;首次发布于 2026/04/15。发布频率只是参考信号:功能稳定的工具可能很少需要更新,建议结合源码仓库的 issue 和提交记录判断。