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
8 changes: 3 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,6 @@ The release contains two builds:

```javascript
const config = {
apiUrl: 'https://your-simface-api.run.app',
projectId: 'your-project-id',
apiKey: 'your-api-key',
};
Expand Down Expand Up @@ -175,7 +174,7 @@ Parameters:

| Parameter | Type | Description |
|-----------|------|-------------|
| `config` | `SimFaceConfig` | SDK configuration (`apiUrl`, `projectId`, `apiKey`) |
| `config` | `SimFaceConfig` | SDK configuration (`projectId`, `apiKey`, optional `apiUrl`) |
| `clientId` | `string` | Unique identifier for the user |
| `workflowOptions` | `SimFaceWorkflowOptions` | Optional popup/embedded-agnostic capture behavior |
| `captureElement` | `SimFaceCaptureElement` | Optional embedded `simface-capture` element |
Expand All @@ -190,7 +189,7 @@ Parameters:

| Parameter | Type | Description |
|-----------|------|-------------|
| `config` | `SimFaceConfig` | SDK configuration (`apiUrl`, `projectId`, `apiKey`) |
| `config` | `SimFaceConfig` | SDK configuration (`projectId`, `apiKey`, optional `apiUrl`) |
| `clientId` | `string` | Unique identifier for the user |
| `workflowOptions` | `SimFaceWorkflowOptions` | Optional popup/embedded-agnostic capture behavior |
| `captureElement` | `SimFaceCaptureElement` | Optional embedded `simface-capture` element |
Expand Down Expand Up @@ -237,7 +236,6 @@ Use the `simface-capture` Web Component directly when you want the host applicat

const captureEl = document.querySelector('simface-capture');
const client = new SimFaceAPIClient({
apiUrl: 'https://your-simface-api.run.app',
projectId: 'your-project-id',
apiKey: 'your-api-key',
});
Expand Down Expand Up @@ -296,9 +294,9 @@ This is more flexible, but it also means the host owns more of the workflow.

```typescript
interface SimFaceConfig {
apiUrl: string;
projectId: string;
apiKey: string;
apiUrl?: string; // Defaults to the hosted SimFace backend
}
```

Expand Down
2 changes: 1 addition & 1 deletion src/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,6 @@ import { enroll, verify } from './index.js';

describe('SDK entrypoints', () => {
const config = {
apiUrl: 'https://example.invalid',
projectId: 'project-1',
apiKey: 'api-key',
};
Expand Down Expand Up @@ -60,6 +59,7 @@ describe('SDK entrypoints', () => {

const result = await enroll(config, 'user-1', workflowOptions, captureComponent);

expect(apiClientConstructor).toHaveBeenCalledWith(config);
expect(captureMocks.captureFromCamera).toHaveBeenCalledWith(workflowOptions, captureComponent);
expect(apiClientMethodMocks.validateAPIKey).toHaveBeenCalledTimes(1);
expect(apiClientMethodMocks.enroll).toHaveBeenCalledWith('user-1', blob);
Expand Down
6 changes: 4 additions & 2 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,13 @@
* Usage:
* import { enroll, verify } from '@simprints/simface-sdk';
*
* const config = { projectId: '...', apiKey: '...' };
*
* // Enroll a new user
* const result = await enroll({ apiUrl: '...', projectId: '...', apiKey: '...' }, 'user-123');
* const result = await enroll(config, 'user-123');
*
* // Verify an existing user
* const result = await verify({ apiUrl: '...', projectId: '...', apiKey: '...' }, 'user-123');
* const result = await verify(config, 'user-123');
*/

import type {
Expand Down
38 changes: 37 additions & 1 deletion src/services/api-client.test.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
import { describe, it, expect, vi } from 'vitest';
import { SimFaceAPIClient } from '../services/api-client.js';
import { DEFAULT_SIMFACE_API_URL } from '../shared/api-url.js';

const DEFAULT_API_URL = DEFAULT_SIMFACE_API_URL;
Comment on lines +3 to +5

const mockConfig = {
apiUrl: 'https://api.example.com',
projectId: 'test-project',
apiKey: 'test-key',
apiUrl: 'https://api.example.com',
};

describe('SimFaceAPIClient', () => {
Expand Down Expand Up @@ -141,6 +144,39 @@ describe('SimFaceAPIClient', () => {
});

describe('URL handling', () => {
it('should use the hosted backend when apiUrl is omitted', async () => {
global.fetch = vi.fn().mockResolvedValue({
ok: true,
json: () => Promise.resolve({ valid: true }),
});

const client = new SimFaceAPIClient({
projectId: mockConfig.projectId,
apiKey: mockConfig.apiKey,
});
await client.validateAPIKey();

expect(fetch).toHaveBeenCalledWith(
`${DEFAULT_API_URL}/api/v1/auth/validate`,
expect.anything(),
);
});

it('should treat a blank apiUrl as missing and use the hosted backend', async () => {
global.fetch = vi.fn().mockResolvedValue({
ok: true,
json: () => Promise.resolve({ valid: true }),
});

const client = new SimFaceAPIClient({ ...mockConfig, apiUrl: ' ' });
await client.validateAPIKey();

expect(fetch).toHaveBeenCalledWith(
`${DEFAULT_API_URL}/api/v1/auth/validate`,
expect.anything(),
);
});

it('should strip trailing slash from apiUrl', async () => {
global.fetch = vi.fn().mockResolvedValue({
ok: true,
Expand Down
3 changes: 2 additions & 1 deletion src/services/api-client.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import type { SimFaceConfig, ValidateResult, EnrollResult, VerifyResult, APIError } from '../types/index.js';
import { resolveApiUrl } from '../shared/api-url.js';

async function getAPIErrorMessage(response: Response, fallback: string): Promise<string> {
try {
Expand All @@ -19,7 +20,7 @@ export class SimFaceAPIClient {
private readonly apiKey: string;

constructor(config: SimFaceConfig) {
this.apiUrl = config.apiUrl.replace(/\/$/, '');
this.apiUrl = resolveApiUrl(config.apiUrl);
this.projectId = config.projectId;
this.apiKey = config.apiKey;
}
Expand Down
10 changes: 10 additions & 0 deletions src/shared/api-url.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
export const DEFAULT_SIMFACE_API_URL = 'https://simface-api-85584555549.europe-west1.run.app';

export function resolveApiUrl(apiUrl?: string): string {
if (typeof apiUrl !== 'string') {
return DEFAULT_SIMFACE_API_URL;
}

const normalizedApiUrl = apiUrl.trim().replace(/\/$/, '');
return normalizedApiUrl || DEFAULT_SIMFACE_API_URL;
}
4 changes: 2 additions & 2 deletions src/types/index.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
/** Configuration for the SimFace SDK. */
export interface SimFaceConfig {
/** Base URL of the SimFace API backend. */
apiUrl: string;
/** Unique identifier for the project. */
projectId: string;
/** Short-lived API credential for authentication. Do not hardcode long-lived secrets into public client bundles. */
apiKey: string;
/** Optional base URL of the SimFace API backend. Defaults to the hosted SimFace backend. */
apiUrl?: string;
}

/** Result of an enrollment operation. */
Expand Down
Loading