Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 29 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -60,11 +60,40 @@ The documentation for Nutrient DWS TypeScript Client is also available on [Conte
## Quick Start

```typescript
import { NutrientClient } from '@nutrient-sdk/dws-client-typescript';

const client = new NutrientClient({
apiKey: 'nutr_sk_your_secret_key'
});
```

### Working with URLs

Most methods accept URLs directly. The URL is passed to the server, which fetches the content—this avoids SSRF vulnerabilities since the client never fetches URLs itself.

```typescript
// Pass URL as a string
const result = await client.convert('https://example.com/document.pdf', 'docx');

// Or as an object (useful for TypeScript type narrowing)
const result = await client.convert({ type: 'url', url: 'https://example.com/document.pdf' }, 'docx');

// URLs also work with the workflow builder
const result = await client.workflow()
.addFilePart('https://example.com/document.pdf')
.outputPdf()
.execute();
```

**Exception:** The `sign()` method only accepts local files (file paths, Buffers, streams) because the underlying API endpoint doesn't support URL inputs. For signing remote files, fetch the content first:

```typescript
// Fetch and pass the bytes for signing
const response = await fetch('https://example.com/document.pdf');
const buffer = Buffer.from(await response.arrayBuffer());
const result = await client.sign(buffer, { /* signature options */ });
```

## Direct Methods

The client provides numerous methods for document processing:
Expand Down
12 changes: 0 additions & 12 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

7 changes: 2 additions & 5 deletions src/__tests__/integration.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@ import {
samplePNG,
TestDocumentGenerator,
} from './helpers';
import { getPdfPageCount, processFileInput } from '../inputs';

// Skip integration tests in CI/automated environments unless explicitly enabled with valid API key
const shouldRunIntegrationTests = Boolean(process.env['NUTRIENT_API_KEY']);
Expand Down Expand Up @@ -236,13 +235,11 @@ describeIntegration('Integration Tests with Live API - Direct Methods', () => {
describe('merge()', () => {
it('should merge multiple PDF files', async () => {
const result = await client.merge([samplePDF, samplePDF, samplePDF]);
const normalizedPdf = await processFileInput(samplePDF);
const pageCount = await getPdfPageCount(normalizedPdf);
expect(result).toBeDefined();
expect(result.buffer).toBeInstanceOf(Uint8Array);
expect(result.mimeType).toBe('application/pdf');
const normalizedResult = await processFileInput(result.buffer);
await expect(getPdfPageCount(normalizedResult)).resolves.toBe(pageCount * 3);
// Merged PDF should be larger than original
expect(result.buffer.length).toBeGreaterThan(samplePDF.length);
}, 60000);
});

Expand Down
21 changes: 7 additions & 14 deletions src/__tests__/unit/client.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -114,12 +114,6 @@ interface MockWorkflowWithOutputStage<T extends keyof OutputTypeMap | undefined
const mockValidateFileInput = inputsModule.validateFileInput as jest.MockedFunction<
typeof inputsModule.validateFileInput
>;
const mockIsValidPdf = inputsModule.isValidPdf as jest.MockedFunction<
typeof inputsModule.isValidPdf
>;
const mockGetPdfPageCount = inputsModule.getPdfPageCount as jest.MockedFunction<
typeof inputsModule.getPdfPageCount
>;
const mockSendRequest = httpModule.sendRequest as jest.MockedFunction<
typeof httpModule.sendRequest
>;
Expand Down Expand Up @@ -173,8 +167,6 @@ describe('NutrientClient', () => {
beforeEach(() => {
jest.clearAllMocks();
mockValidateFileInput.mockReturnValue(true);
mockIsValidPdf.mockResolvedValue(true);
mockGetPdfPageCount.mockResolvedValue(10);
mockSendRequest.mockResolvedValue({
data: TestDocumentGenerator.generateSimplePdf() as never,
status: 200,
Expand Down Expand Up @@ -239,6 +231,7 @@ describe('NutrientClient', () => {
}),
).toThrow('Base URL must be a string');
});

});

describe('workflow()', () => {
Expand Down Expand Up @@ -1056,8 +1049,7 @@ describe('NutrientClient', () => {

await client.addPage(file, count, index);

// Mock getPdfPageCount to test index logic
// This is a simplified test since we can't easily mock the internal implementation
// Verify the workflow was called with the correct page ranges
expect(mockWorkflowInstance.addFilePart).toHaveBeenCalledWith(file, {
pages: { end: 1, start: 0 },
});
Expand Down Expand Up @@ -1169,18 +1161,19 @@ describe('NutrientClient', () => {

it('should delete specific pages from a document', async () => {
const file = 'test-file.pdf';
const pageIndices = [3, 1, 1];
const pageIndices = [3, 1, 1]; // Delete pages 1 and 3 (duplicates removed)

await client.deletePages(file, pageIndices);

// Should keep: [0-0], [2-2], [4 to end]
expect(mockWorkflowInstance.addFilePart).toHaveBeenCalledWith(file, {
pages: { end: 0, start: 0 },
pages: { start: 0, end: 0 },
});
expect(mockWorkflowInstance.addFilePart).toHaveBeenCalledWith(file, {
pages: { end: 2, start: 2 },
pages: { start: 2, end: 2 },
});
expect(mockWorkflowInstance.addFilePart).toHaveBeenCalledWith(file, {
pages: { end: 9, start: 4 },
pages: { start: 4, end: -1 }, // -1 means "to end of document"
});
expect(mockWorkflowInstance.addFilePart).toHaveBeenCalledTimes(3);
expect(mockWorkflowInstance.outputPdf).toHaveBeenCalled();
Expand Down
Loading