npm package: @neo-edi/sdk
Description: TypeScript SDK for the Neo-EDI platform
Latest version: 1.0.19

# @neo-edi/sdk

Official TypeScript SDK for the Neo-EDI platform.

## Installation

```bash
npm install @neo-edi/sdk
# or
pnpm add @neo-edi/sdk
# or
yarn add @neo-edi/sdk
```

## Quick Start

```typescript
import { NeoEdiClient } from '@neo-edi/sdk';

const client = new NeoEdiClient({
  baseUrl: 'https://api.neo-edi.com',
  apiKey: 'nedk_xxxxxxxxxxxx'
});
```

## Partners

```typescript
// List all partners
const partners = await client.partners.list();

// List active partners only
const activePartners = await client.partners.list({ activeOnly: true });

// Get partner by ID
const partner = await client.partners.get('PARTNER_ABC123');

// Create partner
const newPartner = await client.partners.create({
  partnerName: 'Acme Supplies',
  emails: ['edi@acme.com', 'backup@acme.com'],
  phone: '+15551234567',
  address: {
    street: '123 Main St',
    city: 'Springfield',
    state: 'IL',
    zip: '62701',
    country: 'US'
  },
  ediQualifier: 'ZZ',
  ediIdentifier: 'ACME123'
});

// Update partner
await client.partners.update('PARTNER_ABC123', {
  partnerName: 'Acme Supplies Inc',
  isActive: true
});

// Delete (deactivate) partner
await client.partners.delete('PARTNER_ABC123');
```

## Customers

```typescript
// List customers
const customers = await client.customers.list({
  isActive: true,
  limit: 50
});

// Get customer by ID
const customer = await client.customers.get('ACME-001');

// Get customer by external identifier (e.g., org ID, account number)
// Identifier types: CODE, GLN, DUNS, ISA_ID, GS_ID, EXTERNAL
const customer = await client.customers.getByIdentifier('EXTERNAL', 'ORG_12345');
if (customer) {
  console.log('Found customer:', customer.customerName);
} else {
  console.log('Customer not found');
}

// Create customer
const newCustomer = await client.customers.create({
  customerName: 'Acme Corporation',
  customerMetadata: { industry: 'retail' }
});
// Returns customer with auto-generated id (e.g., 'cust_01HXYZ...')

// Update customer
const updated = await client.customers.update('ACME-001', {
  customerName: 'Acme Corp'
});

// Delete customer
await client.customers.delete('ACME-001');
```

## Sub-Customers (Customer Hierarchy)

```typescript
// Link existing customer as sub-customer
const result = await client.subCustomers.add('PARENT-001', 'CHILD-001');
// Returns: { hierarchyId, parentCustomerId, childCustomerId, created, activated }

// Create a new sub-customer under a parent
const newSub = await client.subCustomers.create('PARENT-001', {
  customerName: 'New Store Location',
  accountNumber: 'STORE-123',  // Optional: creates an EXTERNAL identifier
  isActive: true               // Optional: defaults to true
});
// Returns: { hierarchyId, parentCustomerId, childCustomerId, childCustomerName, identifier?, created }

// List sub-customers
const subs = await client.subCustomers.list('PARENT-001');

// Include inactive relationships
const allSubs = await client.subCustomers.list('PARENT-001', { includeInactive: true });

// Include identifiers for each sub-customer
const subsWithIds = await client.subCustomers.list('PARENT-001', { includeIdentifiers: true });
// Returns SubCustomerWithIdentifiers[] with identifiers array for each sub-customer

// Remove sub-customer relationship
await client.subCustomers.remove('PARENT-001', 'CHILD-001');
```

## Partner-Customer Relationships

```typescript
// Register customer with multiple partners
const result = await client.partnerCustomers.register({
  customerId: 'ACME-001',
  tradingPartnerIds: ['PARTNER-001', 'PARTNER-002'],
  isActive: true
});

// Get partner subscriptions for a customer
const subs = await client.partnerCustomers.getSubscriptions('ACME-001');

// Include inactive subscriptions
const allSubs = await client.partnerCustomers.getSubscriptions('ACME-001', {
  includeInactive: true
});
```

## Identifier Mappings

Configure identifier patterns for N8N email routing. Maps extracted identifiers to customers.
Each customer can only have one pattern per partner (adding/updating replaces existing).

```typescript
// Get all patterns for a partner
const patterns = await client.identifierMappings.get('PARTNER-001');

// Get patterns for a specific customer
const customerPatterns = await client.identifierMappings.getForCustomer('PARTNER-001', 'STORE_NYC');

// Add a pattern (replaces existing pattern for this customer)
await client.identifierMappings.add('PARTNER-001', {
  pattern: '^70923$',           // regex to match
  customerId: 'STORE_NYC',      // route to this customer
  priority: 1,                  // lower = higher priority
  description: 'NYC store ID'   // optional
});

// Update a pattern (alias for add)
await client.identifierMappings.update('PARTNER-001', {
  pattern: '^70924$',           // new pattern
  customerId: 'STORE_NYC',      // same customer
  priority: 1
});

// Remove a pattern
await client.identifierMappings.remove('PARTNER-001', '^70923$');
```

## Mapping Templates

```typescript
// List templates
const templates = await client.mappingTemplates.list({
  isPublic: true,
  targetTable: 'edi_852_headers'
});

// Create template
const template = await client.mappingTemplates.create({
  name: 'My 852 Template',
  targetTable: 'edi_852_headers',
  mappingConfig: {
    'CSV Column': 'db_column'
  }
});

// Update template
await client.mappingTemplates.update('template-id', {
  name: 'Updated Name'
});
```

## CSV Ingestion

```typescript
// Browser - File input
const fileInput = document.querySelector<HTMLInputElement>('input[type="file"]');
const file = fileInput?.files?.[0];

if (file) {
  const result = await client.ingest.uploadCsv(file, {
    partnerId: 'PARTNER-001',
    mappingConfig: {
      id: 'config-1',
      version: 1,
      tables: {
        edi_852_headers: {
          batchSize: 500,
          mapping: { 'Report Date': 'report_date' },
          defaults: {}
        }
      }
    }
  });
}

// Dry run (validation only)
const dryRunResult = await client.ingest.dryRun(file, options);
if (!dryRunResult.valid) {
  console.error('Validation errors:', dryRunResult.errors);
}
```

### Node.js File Upload

```typescript
import { readFileSync } from 'fs';

const buffer = readFileSync('data.csv');
const blob = new Blob([buffer], { type: 'text/csv' });

await client.ingest.uploadCsv(blob, options);
```

## GraphQL

```typescript
import { Queries } from '@neo-edi/sdk';

// Using pre-built queries
const headers = await client.graphql.query(
  Queries.GET_EDI_852_HEADERS,
  { filter: { status: 'pending' } }
);

// Custom query
const data = await client.graphql.query<{ edi852Headers: Edi852Header[] }>(`
  query GetHeaders($filter: Edi852HeaderFilterInput) {
    edi852Headers(filter: $filter) {
      id
      documentId
      reportDate
      status
    }
  }
`, { filter: { vendorPartnerId: 'VENDOR-001' } });
```

## Error Handling

```typescript
import { NeoEdiApiError, NeoEdiNetworkError } from '@neo-edi/sdk';

try {
  await client.customers.get('non-existent');
} catch (error) {
  if (error instanceof NeoEdiApiError) {
    console.error(`API Error: ${error.message}`);
    console.error(`Status: ${error.statusCode}`);
    console.error(`Code: ${error.code}`);
  } else if (error instanceof NeoEdiNetworkError) {
    console.error(`Network Error: ${error.message}`);
  }
}
```

## Configuration

```typescript
const client = new NeoEdiClient({
  baseUrl: 'https://api.neo-edi.com',
  apiKey: 'nedk_xxxxxxxxxxxx',
  timeout: 60000,  // 60 seconds
  headers: {
    'X-Custom-Header': 'value'
  }
});
```

## TypeScript Types

All types are exported for use:

```typescript
import type {
  EdiCustomer,
  EdiPartner,
  Edi852Header,
  EdiMappingTemplate,
  IdentifierPattern,
  CsvHeaderMappingCollection,
  CustomerIdentifierType,
  ApiResponse
} from '@neo-edi/sdk';
```

## License

MIT