@hashproof/sdk 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,248 @@
1
+ # @hashproof/sdk
2
+
3
+ > Published as `@hashproof/sdk` on npm. The source directory is `packages/sdk-js` (legacy name).
4
+
5
+ Official JavaScript/TypeScript SDK for the [Hashproof](https://hashproof.ai) Content Provenance API.
6
+
7
+ Hashproof provides content provenance infrastructure built on the C2PA standard. This SDK wraps the REST API with a type-safe client for storing, resolving, verifying, and signing digital assets.
8
+
9
+ ## Installation
10
+
11
+ ```bash
12
+ npm install @hashproof/sdk
13
+ # or
14
+ pnpm add @hashproof/sdk
15
+ # or
16
+ yarn add @hashproof/sdk
17
+ ```
18
+
19
+ ## Quick Start
20
+
21
+ ```ts
22
+ import { HashproofClient } from '@hashproof/sdk';
23
+ import { readFileSync } from 'node:fs';
24
+
25
+ const client = new HashproofClient({ apiKey: 'hpsk_...' });
26
+
27
+ // Verify a file's provenance
28
+ const file = readFileSync('photo.jpg');
29
+ const result = await client.verify(file);
30
+
31
+ if (result.hasProvenance) {
32
+ console.log(`Provenance found via: ${result.source}`);
33
+ console.log(`Trust status: ${result.trustStatus}`);
34
+ console.log(`Signer: ${result.manifest?.signatureInfo.subject}`);
35
+ } else {
36
+ console.log('No provenance found for this file.');
37
+ }
38
+ ```
39
+
40
+ ## Configuration
41
+
42
+ ```ts
43
+ const client = new HashproofClient({
44
+ apiKey: 'hpsk_...', // Required. Your Hashproof API key.
45
+ baseUrl: 'https://api.hashproof.ai', // Optional. Defaults to production.
46
+ timeout: 30000, // Optional. Request timeout in ms.
47
+ });
48
+ ```
49
+
50
+ ## API Reference
51
+
52
+ ### `store(file, options?)`
53
+
54
+ Upload a digital asset and extract/store its C2PA manifest. Perceptual fingerprints are computed automatically.
55
+
56
+ ```ts
57
+ const result = await client.store(fileBuffer, { title: 'Press Photo' });
58
+ console.log(result.manifestId);
59
+ console.log(result.softBindings); // computed perceptual hashes
60
+ ```
61
+
62
+ **Parameters:**
63
+ | Name | Type | Description |
64
+ |------|------|-------------|
65
+ | `file` | `Buffer \| Blob` | The digital asset to store |
66
+ | `options.title` | `string` | Optional human-readable title |
67
+
68
+ **Returns:** `Promise<StoreResult>`
69
+
70
+ ---
71
+
72
+ ### `resolve(file)` / `resolve(fingerprint, algorithm?)`
73
+
74
+ Resolve provenance for a file or pre-computed fingerprint. Finds matching manifests via perceptual hashing (soft binding).
75
+
76
+ ```ts
77
+ // By file upload
78
+ const result = await client.resolve(imageBuffer);
79
+
80
+ // By pre-computed fingerprint (no upload needed)
81
+ const result = await client.resolve('a1b2c3d4...', 'phash-dct-64');
82
+
83
+ for (const match of result.matches) {
84
+ console.log(`${match.similarity * 100}% similar — ${match.manifest.title}`);
85
+ }
86
+ ```
87
+
88
+ **Parameters (file overload):**
89
+ | Name | Type | Description |
90
+ |------|------|-------------|
91
+ | `file` | `Buffer \| Blob` | The file to resolve |
92
+
93
+ **Parameters (fingerprint overload):**
94
+ | Name | Type | Description |
95
+ |------|------|-------------|
96
+ | `fingerprint` | `string` | Hex-encoded perceptual hash |
97
+ | `algorithm` | `string` | Algorithm name (default: `phash-dct-64`) |
98
+
99
+ **Returns:** `Promise<ResolveResult>`
100
+
101
+ ---
102
+
103
+ ### `verify(file)`
104
+
105
+ One-call provenance verification. Checks embedded C2PA manifests, hard binding (hash match), and soft binding (perceptual hash resolution).
106
+
107
+ ```ts
108
+ const result = await client.verify(imageBuffer);
109
+ console.log(result.hasProvenance); // boolean
110
+ console.log(result.source); // 'embedded' | 'resolved' | 'none'
111
+ console.log(result.trustStatus); // 'trusted' | 'untrusted' | 'unknown'
112
+ ```
113
+
114
+ **Parameters:**
115
+ | Name | Type | Description |
116
+ |------|------|-------------|
117
+ | `file` | `Buffer \| Blob` | The file to verify |
118
+
119
+ **Returns:** `Promise<VerifyResult>`
120
+
121
+ ---
122
+
123
+ ### `getManifest(id)`
124
+
125
+ Retrieve a manifest by its UUID.
126
+
127
+ ```ts
128
+ const manifest = await client.getManifest('550e8400-...');
129
+ ```
130
+
131
+ **Returns:** `Promise<ManifestData>`
132
+
133
+ ---
134
+
135
+ ### `getManifestByHash(hash)`
136
+
137
+ Retrieve a manifest by its hard binding hash (SHA-256 of the original asset).
138
+
139
+ ```ts
140
+ const manifest = await client.getManifestByHash('e3b0c44298fc...');
141
+ ```
142
+
143
+ **Returns:** `Promise<ManifestData>`
144
+
145
+ ---
146
+
147
+ ### `listManifests(options?)`
148
+
149
+ List manifests with pagination and search.
150
+
151
+ ```ts
152
+ const page = await client.listManifests({ page: 1, perPage: 50, search: 'photo' });
153
+ console.log(`${page.total} total manifests`);
154
+ ```
155
+
156
+ **Parameters:**
157
+ | Name | Type | Description |
158
+ |------|------|-------------|
159
+ | `options.page` | `number` | Page number (default: 1) |
160
+ | `options.perPage` | `number` | Results per page (default: 20) |
161
+ | `options.search` | `string` | Filter by title |
162
+
163
+ **Returns:** `Promise<PaginatedResponse<ManifestData>>`
164
+
165
+ ---
166
+
167
+ ### `sign(file, options?)`
168
+
169
+ Sign a digital asset with a C2PA manifest using Hashproof managed signing. Requires the `sign` scope on your API key.
170
+
171
+ ```ts
172
+ const result = await client.sign(imageBuffer, { title: 'Press Photo' });
173
+ console.log(result.manifestId);
174
+ ```
175
+
176
+ **Parameters:**
177
+ | Name | Type | Description |
178
+ |------|------|-------------|
179
+ | `file` | `Buffer \| Blob` | The file to sign |
180
+ | `options.title` | `string` | Optional title |
181
+
182
+ **Returns:** `Promise<SignResult>`
183
+
184
+ ---
185
+
186
+ ### `computeFingerprint(file, algorithm?)`
187
+
188
+ Compute a perceptual fingerprint without resolving. Useful for offline computation followed by later resolution.
189
+
190
+ ```ts
191
+ const fp = await client.computeFingerprint(imageBuffer);
192
+ console.log(fp.fingerprint); // hex string
193
+ console.log(fp.algorithm); // 'phash-dct-64'
194
+
195
+ // Later, resolve without re-uploading:
196
+ const matches = await client.resolve(fp.fingerprint, fp.algorithm);
197
+ ```
198
+
199
+ **Parameters:**
200
+ | Name | Type | Description |
201
+ |------|------|-------------|
202
+ | `file` | `Buffer \| Blob` | The file to fingerprint |
203
+ | `algorithm` | `string` | Algorithm (default: `phash-dct-64`) |
204
+
205
+ **Returns:** `Promise<FingerprintResult>`
206
+
207
+ ## Error Handling
208
+
209
+ All API errors are thrown as `HashproofApiError` instances:
210
+
211
+ ```ts
212
+ import { HashproofClient, HashproofApiError } from '@hashproof/sdk';
213
+
214
+ try {
215
+ await client.getManifest('nonexistent');
216
+ } catch (err) {
217
+ if (err instanceof HashproofApiError) {
218
+ console.error(err.message); // "Manifest not found"
219
+ console.error(err.statusCode); // 404
220
+ console.error(err.code); // "NOT_FOUND"
221
+ }
222
+ }
223
+ ```
224
+
225
+ ## TypeScript
226
+
227
+ All types are exported from the package for full type safety:
228
+
229
+ ```ts
230
+ import type {
231
+ ManifestData,
232
+ VerifyResult,
233
+ ResolveResult,
234
+ ValidationStatus,
235
+ PaginatedResponse,
236
+ } from '@hashproof/sdk';
237
+ ```
238
+
239
+ ## Supported Runtimes
240
+
241
+ - Node.js 18+ (uses native `fetch`)
242
+ - Deno
243
+ - Bun
244
+ - Modern browsers (via bundler)
245
+
246
+ ## License
247
+
248
+ MIT
package/dist/index.cjs ADDED
@@ -0,0 +1,335 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/index.ts
21
+ var index_exports = {};
22
+ __export(index_exports, {
23
+ HashproofApiError: () => HashproofApiError,
24
+ HashproofClient: () => HashproofClient
25
+ });
26
+ module.exports = __toCommonJS(index_exports);
27
+
28
+ // src/types.ts
29
+ var HashproofApiError = class extends Error {
30
+ /** HTTP status code. */
31
+ statusCode;
32
+ /** Machine-readable error code. */
33
+ code;
34
+ /** Additional error details. */
35
+ details;
36
+ constructor(message, statusCode, code, details) {
37
+ super(message);
38
+ this.name = "HashproofApiError";
39
+ this.statusCode = statusCode;
40
+ this.code = code;
41
+ this.details = details;
42
+ }
43
+ };
44
+
45
+ // src/index.ts
46
+ var DEFAULT_BASE_URL = "https://api.hashproof.ai";
47
+ var DEFAULT_TIMEOUT = 3e4;
48
+ var HashproofClient = class {
49
+ apiKey;
50
+ baseUrl;
51
+ timeout;
52
+ /**
53
+ * Create a new HashproofClient instance.
54
+ *
55
+ * @param options - Client configuration options.
56
+ * @param options.apiKey - Your Hashproof API key (starts with `hpsk_`).
57
+ * @param options.baseUrl - Base URL for the API. Defaults to `https://api.hashproof.ai`.
58
+ * @param options.timeout - Request timeout in milliseconds. Defaults to 30000.
59
+ * @throws {Error} If no API key is provided.
60
+ */
61
+ constructor(options) {
62
+ if (!options.apiKey) {
63
+ throw new Error('Hashproof API key is required. Pass { apiKey: "hpsk_..." } to the constructor.');
64
+ }
65
+ this.apiKey = options.apiKey;
66
+ this.baseUrl = (options.baseUrl || DEFAULT_BASE_URL).replace(/\/+$/, "");
67
+ this.timeout = options.timeout ?? DEFAULT_TIMEOUT;
68
+ }
69
+ // ============================================================
70
+ // Manifest Operations
71
+ // ============================================================
72
+ /**
73
+ * Store a C2PA manifest by uploading a digital asset.
74
+ *
75
+ * The API extracts the embedded C2PA manifest from the file,
76
+ * computes perceptual fingerprints (soft bindings), and stores
77
+ * everything for future resolution and verification.
78
+ *
79
+ * @param file - The file to store as a Buffer or Blob.
80
+ * @param options - Optional parameters.
81
+ * @param options.title - A human-readable title for the asset.
82
+ * @returns The stored manifest data along with computed soft bindings.
83
+ *
84
+ * @example
85
+ * ```ts
86
+ * const result = await client.store(fileBuffer, { title: 'My Photo' });
87
+ * console.log(result.manifestId);
88
+ * ```
89
+ */
90
+ async store(file, options) {
91
+ const formData = new FormData();
92
+ const blob = file instanceof Blob ? file : new Blob([file]);
93
+ formData.append("file", blob, "upload");
94
+ if (options?.title) {
95
+ formData.append("title", options.title);
96
+ }
97
+ return this.request("POST", "/v1/manifests", formData);
98
+ }
99
+ async resolve(fileOrFingerprint, algorithm) {
100
+ if (typeof fileOrFingerprint === "string") {
101
+ const params = new URLSearchParams({ fingerprint: fileOrFingerprint });
102
+ if (algorithm) params.set("algorithm", algorithm);
103
+ return this.request("GET", `/v1/resolve?${params.toString()}`);
104
+ }
105
+ const formData = new FormData();
106
+ const blob = fileOrFingerprint instanceof Blob ? fileOrFingerprint : new Blob([fileOrFingerprint]);
107
+ formData.append("file", blob, "upload");
108
+ if (algorithm) {
109
+ formData.append("algorithm", algorithm);
110
+ }
111
+ return this.request("POST", "/v1/resolve", formData);
112
+ }
113
+ /**
114
+ * Verify the provenance of a digital asset.
115
+ *
116
+ * This is the main one-call verification endpoint. It checks:
117
+ * 1. Embedded C2PA manifests in the file
118
+ * 2. Hard binding (SHA-256 hash match) against stored manifests
119
+ * 3. Soft binding (perceptual hash) resolution
120
+ *
121
+ * @param file - The file to verify as a Buffer or Blob.
122
+ * @returns Verification result including provenance source, manifest data, and trust status.
123
+ *
124
+ * @example
125
+ * ```ts
126
+ * const result = await client.verify(imageBuffer);
127
+ * if (result.hasProvenance) {
128
+ * console.log(`Source: ${result.source}, Trust: ${result.trustStatus}`);
129
+ * }
130
+ * ```
131
+ */
132
+ async verify(file) {
133
+ const formData = new FormData();
134
+ const blob = file instanceof Blob ? file : new Blob([file]);
135
+ formData.append("file", blob, "upload");
136
+ return this.request("POST", "/v1/verify", formData);
137
+ }
138
+ /**
139
+ * Retrieve a manifest by its ID.
140
+ *
141
+ * @param id - The manifest UUID.
142
+ * @returns The full manifest data.
143
+ *
144
+ * @example
145
+ * ```ts
146
+ * const manifest = await client.getManifest('550e8400-e29b-41d4-a716-446655440000');
147
+ * console.log(manifest.title, manifest.validationStatus);
148
+ * ```
149
+ */
150
+ async getManifest(id) {
151
+ return this.request("GET", `/v1/manifests/${encodeURIComponent(id)}`);
152
+ }
153
+ /**
154
+ * Retrieve a manifest by its hard binding hash (SHA-256).
155
+ *
156
+ * @param hash - The SHA-256 hash of the original asset.
157
+ * @returns The matching manifest data.
158
+ *
159
+ * @example
160
+ * ```ts
161
+ * const manifest = await client.getManifestByHash('e3b0c44298fc1c149afbf4c8996fb924...');
162
+ * ```
163
+ */
164
+ async getManifestByHash(hash) {
165
+ const params = new URLSearchParams({ hash });
166
+ return this.request("GET", `/v1/manifests?${params.toString()}`);
167
+ }
168
+ /**
169
+ * List manifests with optional pagination and search.
170
+ *
171
+ * @param options - Pagination and search options.
172
+ * @param options.page - Page number (1-indexed). Defaults to 1.
173
+ * @param options.perPage - Results per page. Defaults to 20.
174
+ * @param options.search - Search term to filter manifests by title.
175
+ * @returns A paginated list of manifests.
176
+ *
177
+ * @example
178
+ * ```ts
179
+ * const page = await client.listManifests({ page: 1, search: 'photo' });
180
+ * console.log(`${page.total} manifests found`);
181
+ * ```
182
+ */
183
+ async listManifests(options) {
184
+ const params = new URLSearchParams();
185
+ if (options?.page) params.set("page", String(options.page));
186
+ if (options?.perPage) params.set("perPage", String(options.perPage));
187
+ if (options?.search) params.set("search", options.search);
188
+ const query = params.toString();
189
+ return this.request(
190
+ "GET",
191
+ `/v1/manifests${query ? `?${query}` : ""}`
192
+ );
193
+ }
194
+ // ============================================================
195
+ // Signing
196
+ // ============================================================
197
+ /**
198
+ * Sign a digital asset with a C2PA manifest using Hashproof's managed signing.
199
+ *
200
+ * This is the "Let's Encrypt for C2PA" endpoint -- you upload a file and
201
+ * Hashproof creates a signed C2PA manifest on your behalf, without you
202
+ * needing to manage certificates or PKI.
203
+ *
204
+ * Requires the `sign` scope on your API key.
205
+ *
206
+ * @param file - The file to sign as a Buffer or Blob.
207
+ * @param options - Optional parameters.
208
+ * @param options.title - A human-readable title for the asset.
209
+ * @returns The signing result including the new manifest ID.
210
+ *
211
+ * @example
212
+ * ```ts
213
+ * const result = await client.sign(imageBuffer, { title: 'Press Photo 2024' });
214
+ * console.log(result.manifestId);
215
+ * ```
216
+ */
217
+ async sign(file, options) {
218
+ const formData = new FormData();
219
+ const blob = file instanceof Blob ? file : new Blob([file]);
220
+ formData.append("file", blob, "upload");
221
+ if (options?.title) {
222
+ formData.append("title", options.title);
223
+ }
224
+ return this.request("POST", "/v1/sign", formData);
225
+ }
226
+ // ============================================================
227
+ // Fingerprinting
228
+ // ============================================================
229
+ /**
230
+ * Compute a perceptual fingerprint for a file without resolving.
231
+ *
232
+ * Useful when you want to compute and store a fingerprint client-side
233
+ * for later resolution via `resolve(fingerprint, algorithm)`.
234
+ *
235
+ * @param file - The file to fingerprint as a Buffer or Blob.
236
+ * @param algorithm - The algorithm to use. Defaults to `phash-dct-64`.
237
+ * @returns The computed fingerprint, algorithm, and bit length.
238
+ *
239
+ * @example
240
+ * ```ts
241
+ * const fp = await client.computeFingerprint(imageBuffer, 'phash-dct-64');
242
+ * console.log(fp.fingerprint); // hex string
243
+ * ```
244
+ */
245
+ async computeFingerprint(file, algorithm) {
246
+ const formData = new FormData();
247
+ const blob = file instanceof Blob ? file : new Blob([file]);
248
+ formData.append("file", blob, "upload");
249
+ if (algorithm) {
250
+ formData.append("algorithm", algorithm);
251
+ }
252
+ return this.request("POST", "/v1/fingerprint", formData);
253
+ }
254
+ // ============================================================
255
+ // Internal HTTP Client
256
+ // ============================================================
257
+ /**
258
+ * Make an authenticated HTTP request to the Hashproof API.
259
+ * @internal
260
+ */
261
+ async request(method, path, body) {
262
+ const url = `${this.baseUrl}${path}`;
263
+ const headers = {
264
+ Authorization: `Bearer ${this.apiKey}`,
265
+ Accept: "application/json"
266
+ };
267
+ let requestBody;
268
+ if (body instanceof FormData) {
269
+ requestBody = body;
270
+ } else if (body) {
271
+ headers["Content-Type"] = "application/json";
272
+ requestBody = JSON.stringify(body);
273
+ }
274
+ const controller = new AbortController();
275
+ const timeoutId = setTimeout(() => controller.abort(), this.timeout);
276
+ let response;
277
+ try {
278
+ response = await fetch(url, {
279
+ method,
280
+ headers,
281
+ body: requestBody,
282
+ signal: controller.signal
283
+ });
284
+ } catch (err) {
285
+ clearTimeout(timeoutId);
286
+ if (err instanceof DOMException && err.name === "AbortError") {
287
+ throw new HashproofApiError(
288
+ `Request timed out after ${this.timeout}ms`,
289
+ 0,
290
+ "TIMEOUT"
291
+ );
292
+ }
293
+ throw new HashproofApiError(
294
+ `Network error: ${err instanceof Error ? err.message : String(err)}`,
295
+ 0,
296
+ "NETWORK_ERROR"
297
+ );
298
+ } finally {
299
+ clearTimeout(timeoutId);
300
+ }
301
+ const responseBody = await response.text();
302
+ let parsed;
303
+ try {
304
+ parsed = JSON.parse(responseBody);
305
+ } catch {
306
+ if (!response.ok) {
307
+ throw new HashproofApiError(
308
+ `HTTP ${response.status}: ${responseBody.slice(0, 200)}`,
309
+ response.status,
310
+ "UNEXPECTED_RESPONSE"
311
+ );
312
+ }
313
+ throw new HashproofApiError(
314
+ "Failed to parse API response as JSON",
315
+ response.status,
316
+ "PARSE_ERROR"
317
+ );
318
+ }
319
+ if (!response.ok) {
320
+ const errBody = parsed;
321
+ throw new HashproofApiError(
322
+ errBody.error || `HTTP ${response.status}`,
323
+ errBody.statusCode || response.status,
324
+ errBody.code || "API_ERROR",
325
+ errBody.details
326
+ );
327
+ }
328
+ return parsed;
329
+ }
330
+ };
331
+ // Annotate the CommonJS export names for ESM import in node:
332
+ 0 && (module.exports = {
333
+ HashproofApiError,
334
+ HashproofClient
335
+ });
@@ -0,0 +1,231 @@
1
+ import * as _hashproof_shared from '@hashproof/shared';
2
+ import { ResolveResult, VerifyResult, ManifestData, PaginatedResponse } from '@hashproof/shared';
3
+ export { ApiError, ApiKey, ApiScope, Assertion, ComplianceDetail, ComplianceReport, HardBinding, Ingredient, ManifestData, MerkleAnchor, MerkleProof, PaginatedResponse, ResolveMatch, ResolveResult, SignatureInfo, SoftBinding, SoftBindingAlgorithm, SubscriptionTier, UsageSummary, ValidationError, ValidationResult, ValidationStatus, VerifyResult } from '@hashproof/shared';
4
+
5
+ /** Options for constructing the HashproofClient. */
6
+ interface HashproofClientOptions {
7
+ /** Your Hashproof API key (starts with hpsk_). */
8
+ apiKey: string;
9
+ /** Base URL for the API. Defaults to https://api.hashproof.ai. */
10
+ baseUrl?: string;
11
+ /** Request timeout in milliseconds. Defaults to 30000. */
12
+ timeout?: number;
13
+ }
14
+ /** Result returned from the store() method. */
15
+ interface StoreResult {
16
+ manifestId: string;
17
+ manifest: _hashproof_shared.ManifestData;
18
+ softBindings: Array<{
19
+ algorithm: string;
20
+ value: string;
21
+ }>;
22
+ }
23
+ /** Result returned from the sign() method. */
24
+ interface SignResult {
25
+ manifestId: string;
26
+ manifest: _hashproof_shared.ManifestData;
27
+ signedAssetUrl: string | null;
28
+ message: string;
29
+ }
30
+ /** Result returned from the computeFingerprint() method. */
31
+ interface FingerprintResult {
32
+ fingerprint: string;
33
+ algorithm: _hashproof_shared.SoftBindingAlgorithm;
34
+ bitLength: number;
35
+ }
36
+ /** Error thrown by the SDK for API error responses. */
37
+ declare class HashproofApiError extends Error {
38
+ /** HTTP status code. */
39
+ readonly statusCode: number;
40
+ /** Machine-readable error code. */
41
+ readonly code: string;
42
+ /** Additional error details. */
43
+ readonly details?: Record<string, unknown>;
44
+ constructor(message: string, statusCode: number, code: string, details?: Record<string, unknown>);
45
+ }
46
+
47
+ /**
48
+ * Official JavaScript/TypeScript client for the Hashproof Content Provenance API.
49
+ *
50
+ * @example
51
+ * ```ts
52
+ * import { HashproofClient } from '@hashproof/sdk';
53
+ *
54
+ * const client = new HashproofClient({ apiKey: 'hpsk_...' });
55
+ * const result = await client.verify(fileBuffer);
56
+ * console.log(result.hasProvenance);
57
+ * ```
58
+ */
59
+ declare class HashproofClient {
60
+ private readonly apiKey;
61
+ private readonly baseUrl;
62
+ private readonly timeout;
63
+ /**
64
+ * Create a new HashproofClient instance.
65
+ *
66
+ * @param options - Client configuration options.
67
+ * @param options.apiKey - Your Hashproof API key (starts with `hpsk_`).
68
+ * @param options.baseUrl - Base URL for the API. Defaults to `https://api.hashproof.ai`.
69
+ * @param options.timeout - Request timeout in milliseconds. Defaults to 30000.
70
+ * @throws {Error} If no API key is provided.
71
+ */
72
+ constructor(options: HashproofClientOptions);
73
+ /**
74
+ * Store a C2PA manifest by uploading a digital asset.
75
+ *
76
+ * The API extracts the embedded C2PA manifest from the file,
77
+ * computes perceptual fingerprints (soft bindings), and stores
78
+ * everything for future resolution and verification.
79
+ *
80
+ * @param file - The file to store as a Buffer or Blob.
81
+ * @param options - Optional parameters.
82
+ * @param options.title - A human-readable title for the asset.
83
+ * @returns The stored manifest data along with computed soft bindings.
84
+ *
85
+ * @example
86
+ * ```ts
87
+ * const result = await client.store(fileBuffer, { title: 'My Photo' });
88
+ * console.log(result.manifestId);
89
+ * ```
90
+ */
91
+ store(file: Buffer | Blob, options?: {
92
+ title?: string;
93
+ }): Promise<StoreResult>;
94
+ /**
95
+ * Resolve provenance for a file or fingerprint.
96
+ *
97
+ * When called with a Buffer/Blob, the file is uploaded and a perceptual
98
+ * fingerprint is computed server-side, then matched against the manifest
99
+ * repository.
100
+ *
101
+ * When called with a fingerprint string, a lightweight lookup is performed
102
+ * without any file upload.
103
+ *
104
+ * @param fileOrFingerprint - A file (Buffer/Blob) or a hex fingerprint string.
105
+ * @param algorithm - The soft binding algorithm to use (default: `phash-dct-64`).
106
+ * @returns Matching manifests sorted by similarity.
107
+ *
108
+ * @example
109
+ * ```ts
110
+ * // Resolve by file upload
111
+ * const result = await client.resolve(imageBuffer);
112
+ *
113
+ * // Resolve by pre-computed fingerprint
114
+ * const result = await client.resolve('a1b2c3d4e5f6...', 'phash-dct-64');
115
+ * ```
116
+ */
117
+ resolve(file: Buffer | Blob): Promise<ResolveResult>;
118
+ resolve(fingerprint: string, algorithm?: string): Promise<ResolveResult>;
119
+ /**
120
+ * Verify the provenance of a digital asset.
121
+ *
122
+ * This is the main one-call verification endpoint. It checks:
123
+ * 1. Embedded C2PA manifests in the file
124
+ * 2. Hard binding (SHA-256 hash match) against stored manifests
125
+ * 3. Soft binding (perceptual hash) resolution
126
+ *
127
+ * @param file - The file to verify as a Buffer or Blob.
128
+ * @returns Verification result including provenance source, manifest data, and trust status.
129
+ *
130
+ * @example
131
+ * ```ts
132
+ * const result = await client.verify(imageBuffer);
133
+ * if (result.hasProvenance) {
134
+ * console.log(`Source: ${result.source}, Trust: ${result.trustStatus}`);
135
+ * }
136
+ * ```
137
+ */
138
+ verify(file: Buffer | Blob): Promise<VerifyResult>;
139
+ /**
140
+ * Retrieve a manifest by its ID.
141
+ *
142
+ * @param id - The manifest UUID.
143
+ * @returns The full manifest data.
144
+ *
145
+ * @example
146
+ * ```ts
147
+ * const manifest = await client.getManifest('550e8400-e29b-41d4-a716-446655440000');
148
+ * console.log(manifest.title, manifest.validationStatus);
149
+ * ```
150
+ */
151
+ getManifest(id: string): Promise<ManifestData>;
152
+ /**
153
+ * Retrieve a manifest by its hard binding hash (SHA-256).
154
+ *
155
+ * @param hash - The SHA-256 hash of the original asset.
156
+ * @returns The matching manifest data.
157
+ *
158
+ * @example
159
+ * ```ts
160
+ * const manifest = await client.getManifestByHash('e3b0c44298fc1c149afbf4c8996fb924...');
161
+ * ```
162
+ */
163
+ getManifestByHash(hash: string): Promise<ManifestData>;
164
+ /**
165
+ * List manifests with optional pagination and search.
166
+ *
167
+ * @param options - Pagination and search options.
168
+ * @param options.page - Page number (1-indexed). Defaults to 1.
169
+ * @param options.perPage - Results per page. Defaults to 20.
170
+ * @param options.search - Search term to filter manifests by title.
171
+ * @returns A paginated list of manifests.
172
+ *
173
+ * @example
174
+ * ```ts
175
+ * const page = await client.listManifests({ page: 1, search: 'photo' });
176
+ * console.log(`${page.total} manifests found`);
177
+ * ```
178
+ */
179
+ listManifests(options?: {
180
+ page?: number;
181
+ perPage?: number;
182
+ search?: string;
183
+ }): Promise<PaginatedResponse<ManifestData>>;
184
+ /**
185
+ * Sign a digital asset with a C2PA manifest using Hashproof's managed signing.
186
+ *
187
+ * This is the "Let's Encrypt for C2PA" endpoint -- you upload a file and
188
+ * Hashproof creates a signed C2PA manifest on your behalf, without you
189
+ * needing to manage certificates or PKI.
190
+ *
191
+ * Requires the `sign` scope on your API key.
192
+ *
193
+ * @param file - The file to sign as a Buffer or Blob.
194
+ * @param options - Optional parameters.
195
+ * @param options.title - A human-readable title for the asset.
196
+ * @returns The signing result including the new manifest ID.
197
+ *
198
+ * @example
199
+ * ```ts
200
+ * const result = await client.sign(imageBuffer, { title: 'Press Photo 2024' });
201
+ * console.log(result.manifestId);
202
+ * ```
203
+ */
204
+ sign(file: Buffer | Blob, options?: {
205
+ title?: string;
206
+ }): Promise<SignResult>;
207
+ /**
208
+ * Compute a perceptual fingerprint for a file without resolving.
209
+ *
210
+ * Useful when you want to compute and store a fingerprint client-side
211
+ * for later resolution via `resolve(fingerprint, algorithm)`.
212
+ *
213
+ * @param file - The file to fingerprint as a Buffer or Blob.
214
+ * @param algorithm - The algorithm to use. Defaults to `phash-dct-64`.
215
+ * @returns The computed fingerprint, algorithm, and bit length.
216
+ *
217
+ * @example
218
+ * ```ts
219
+ * const fp = await client.computeFingerprint(imageBuffer, 'phash-dct-64');
220
+ * console.log(fp.fingerprint); // hex string
221
+ * ```
222
+ */
223
+ computeFingerprint(file: Buffer | Blob, algorithm?: string): Promise<FingerprintResult>;
224
+ /**
225
+ * Make an authenticated HTTP request to the Hashproof API.
226
+ * @internal
227
+ */
228
+ private request;
229
+ }
230
+
231
+ export { type FingerprintResult, HashproofApiError, HashproofClient, type HashproofClientOptions, type SignResult, type StoreResult };
@@ -0,0 +1,231 @@
1
+ import * as _hashproof_shared from '@hashproof/shared';
2
+ import { ResolveResult, VerifyResult, ManifestData, PaginatedResponse } from '@hashproof/shared';
3
+ export { ApiError, ApiKey, ApiScope, Assertion, ComplianceDetail, ComplianceReport, HardBinding, Ingredient, ManifestData, MerkleAnchor, MerkleProof, PaginatedResponse, ResolveMatch, ResolveResult, SignatureInfo, SoftBinding, SoftBindingAlgorithm, SubscriptionTier, UsageSummary, ValidationError, ValidationResult, ValidationStatus, VerifyResult } from '@hashproof/shared';
4
+
5
+ /** Options for constructing the HashproofClient. */
6
+ interface HashproofClientOptions {
7
+ /** Your Hashproof API key (starts with hpsk_). */
8
+ apiKey: string;
9
+ /** Base URL for the API. Defaults to https://api.hashproof.ai. */
10
+ baseUrl?: string;
11
+ /** Request timeout in milliseconds. Defaults to 30000. */
12
+ timeout?: number;
13
+ }
14
+ /** Result returned from the store() method. */
15
+ interface StoreResult {
16
+ manifestId: string;
17
+ manifest: _hashproof_shared.ManifestData;
18
+ softBindings: Array<{
19
+ algorithm: string;
20
+ value: string;
21
+ }>;
22
+ }
23
+ /** Result returned from the sign() method. */
24
+ interface SignResult {
25
+ manifestId: string;
26
+ manifest: _hashproof_shared.ManifestData;
27
+ signedAssetUrl: string | null;
28
+ message: string;
29
+ }
30
+ /** Result returned from the computeFingerprint() method. */
31
+ interface FingerprintResult {
32
+ fingerprint: string;
33
+ algorithm: _hashproof_shared.SoftBindingAlgorithm;
34
+ bitLength: number;
35
+ }
36
+ /** Error thrown by the SDK for API error responses. */
37
+ declare class HashproofApiError extends Error {
38
+ /** HTTP status code. */
39
+ readonly statusCode: number;
40
+ /** Machine-readable error code. */
41
+ readonly code: string;
42
+ /** Additional error details. */
43
+ readonly details?: Record<string, unknown>;
44
+ constructor(message: string, statusCode: number, code: string, details?: Record<string, unknown>);
45
+ }
46
+
47
+ /**
48
+ * Official JavaScript/TypeScript client for the Hashproof Content Provenance API.
49
+ *
50
+ * @example
51
+ * ```ts
52
+ * import { HashproofClient } from '@hashproof/sdk';
53
+ *
54
+ * const client = new HashproofClient({ apiKey: 'hpsk_...' });
55
+ * const result = await client.verify(fileBuffer);
56
+ * console.log(result.hasProvenance);
57
+ * ```
58
+ */
59
+ declare class HashproofClient {
60
+ private readonly apiKey;
61
+ private readonly baseUrl;
62
+ private readonly timeout;
63
+ /**
64
+ * Create a new HashproofClient instance.
65
+ *
66
+ * @param options - Client configuration options.
67
+ * @param options.apiKey - Your Hashproof API key (starts with `hpsk_`).
68
+ * @param options.baseUrl - Base URL for the API. Defaults to `https://api.hashproof.ai`.
69
+ * @param options.timeout - Request timeout in milliseconds. Defaults to 30000.
70
+ * @throws {Error} If no API key is provided.
71
+ */
72
+ constructor(options: HashproofClientOptions);
73
+ /**
74
+ * Store a C2PA manifest by uploading a digital asset.
75
+ *
76
+ * The API extracts the embedded C2PA manifest from the file,
77
+ * computes perceptual fingerprints (soft bindings), and stores
78
+ * everything for future resolution and verification.
79
+ *
80
+ * @param file - The file to store as a Buffer or Blob.
81
+ * @param options - Optional parameters.
82
+ * @param options.title - A human-readable title for the asset.
83
+ * @returns The stored manifest data along with computed soft bindings.
84
+ *
85
+ * @example
86
+ * ```ts
87
+ * const result = await client.store(fileBuffer, { title: 'My Photo' });
88
+ * console.log(result.manifestId);
89
+ * ```
90
+ */
91
+ store(file: Buffer | Blob, options?: {
92
+ title?: string;
93
+ }): Promise<StoreResult>;
94
+ /**
95
+ * Resolve provenance for a file or fingerprint.
96
+ *
97
+ * When called with a Buffer/Blob, the file is uploaded and a perceptual
98
+ * fingerprint is computed server-side, then matched against the manifest
99
+ * repository.
100
+ *
101
+ * When called with a fingerprint string, a lightweight lookup is performed
102
+ * without any file upload.
103
+ *
104
+ * @param fileOrFingerprint - A file (Buffer/Blob) or a hex fingerprint string.
105
+ * @param algorithm - The soft binding algorithm to use (default: `phash-dct-64`).
106
+ * @returns Matching manifests sorted by similarity.
107
+ *
108
+ * @example
109
+ * ```ts
110
+ * // Resolve by file upload
111
+ * const result = await client.resolve(imageBuffer);
112
+ *
113
+ * // Resolve by pre-computed fingerprint
114
+ * const result = await client.resolve('a1b2c3d4e5f6...', 'phash-dct-64');
115
+ * ```
116
+ */
117
+ resolve(file: Buffer | Blob): Promise<ResolveResult>;
118
+ resolve(fingerprint: string, algorithm?: string): Promise<ResolveResult>;
119
+ /**
120
+ * Verify the provenance of a digital asset.
121
+ *
122
+ * This is the main one-call verification endpoint. It checks:
123
+ * 1. Embedded C2PA manifests in the file
124
+ * 2. Hard binding (SHA-256 hash match) against stored manifests
125
+ * 3. Soft binding (perceptual hash) resolution
126
+ *
127
+ * @param file - The file to verify as a Buffer or Blob.
128
+ * @returns Verification result including provenance source, manifest data, and trust status.
129
+ *
130
+ * @example
131
+ * ```ts
132
+ * const result = await client.verify(imageBuffer);
133
+ * if (result.hasProvenance) {
134
+ * console.log(`Source: ${result.source}, Trust: ${result.trustStatus}`);
135
+ * }
136
+ * ```
137
+ */
138
+ verify(file: Buffer | Blob): Promise<VerifyResult>;
139
+ /**
140
+ * Retrieve a manifest by its ID.
141
+ *
142
+ * @param id - The manifest UUID.
143
+ * @returns The full manifest data.
144
+ *
145
+ * @example
146
+ * ```ts
147
+ * const manifest = await client.getManifest('550e8400-e29b-41d4-a716-446655440000');
148
+ * console.log(manifest.title, manifest.validationStatus);
149
+ * ```
150
+ */
151
+ getManifest(id: string): Promise<ManifestData>;
152
+ /**
153
+ * Retrieve a manifest by its hard binding hash (SHA-256).
154
+ *
155
+ * @param hash - The SHA-256 hash of the original asset.
156
+ * @returns The matching manifest data.
157
+ *
158
+ * @example
159
+ * ```ts
160
+ * const manifest = await client.getManifestByHash('e3b0c44298fc1c149afbf4c8996fb924...');
161
+ * ```
162
+ */
163
+ getManifestByHash(hash: string): Promise<ManifestData>;
164
+ /**
165
+ * List manifests with optional pagination and search.
166
+ *
167
+ * @param options - Pagination and search options.
168
+ * @param options.page - Page number (1-indexed). Defaults to 1.
169
+ * @param options.perPage - Results per page. Defaults to 20.
170
+ * @param options.search - Search term to filter manifests by title.
171
+ * @returns A paginated list of manifests.
172
+ *
173
+ * @example
174
+ * ```ts
175
+ * const page = await client.listManifests({ page: 1, search: 'photo' });
176
+ * console.log(`${page.total} manifests found`);
177
+ * ```
178
+ */
179
+ listManifests(options?: {
180
+ page?: number;
181
+ perPage?: number;
182
+ search?: string;
183
+ }): Promise<PaginatedResponse<ManifestData>>;
184
+ /**
185
+ * Sign a digital asset with a C2PA manifest using Hashproof's managed signing.
186
+ *
187
+ * This is the "Let's Encrypt for C2PA" endpoint -- you upload a file and
188
+ * Hashproof creates a signed C2PA manifest on your behalf, without you
189
+ * needing to manage certificates or PKI.
190
+ *
191
+ * Requires the `sign` scope on your API key.
192
+ *
193
+ * @param file - The file to sign as a Buffer or Blob.
194
+ * @param options - Optional parameters.
195
+ * @param options.title - A human-readable title for the asset.
196
+ * @returns The signing result including the new manifest ID.
197
+ *
198
+ * @example
199
+ * ```ts
200
+ * const result = await client.sign(imageBuffer, { title: 'Press Photo 2024' });
201
+ * console.log(result.manifestId);
202
+ * ```
203
+ */
204
+ sign(file: Buffer | Blob, options?: {
205
+ title?: string;
206
+ }): Promise<SignResult>;
207
+ /**
208
+ * Compute a perceptual fingerprint for a file without resolving.
209
+ *
210
+ * Useful when you want to compute and store a fingerprint client-side
211
+ * for later resolution via `resolve(fingerprint, algorithm)`.
212
+ *
213
+ * @param file - The file to fingerprint as a Buffer or Blob.
214
+ * @param algorithm - The algorithm to use. Defaults to `phash-dct-64`.
215
+ * @returns The computed fingerprint, algorithm, and bit length.
216
+ *
217
+ * @example
218
+ * ```ts
219
+ * const fp = await client.computeFingerprint(imageBuffer, 'phash-dct-64');
220
+ * console.log(fp.fingerprint); // hex string
221
+ * ```
222
+ */
223
+ computeFingerprint(file: Buffer | Blob, algorithm?: string): Promise<FingerprintResult>;
224
+ /**
225
+ * Make an authenticated HTTP request to the Hashproof API.
226
+ * @internal
227
+ */
228
+ private request;
229
+ }
230
+
231
+ export { type FingerprintResult, HashproofApiError, HashproofClient, type HashproofClientOptions, type SignResult, type StoreResult };
package/dist/index.js ADDED
@@ -0,0 +1,307 @@
1
+ // src/types.ts
2
+ var HashproofApiError = class extends Error {
3
+ /** HTTP status code. */
4
+ statusCode;
5
+ /** Machine-readable error code. */
6
+ code;
7
+ /** Additional error details. */
8
+ details;
9
+ constructor(message, statusCode, code, details) {
10
+ super(message);
11
+ this.name = "HashproofApiError";
12
+ this.statusCode = statusCode;
13
+ this.code = code;
14
+ this.details = details;
15
+ }
16
+ };
17
+
18
+ // src/index.ts
19
+ var DEFAULT_BASE_URL = "https://api.hashproof.ai";
20
+ var DEFAULT_TIMEOUT = 3e4;
21
+ var HashproofClient = class {
22
+ apiKey;
23
+ baseUrl;
24
+ timeout;
25
+ /**
26
+ * Create a new HashproofClient instance.
27
+ *
28
+ * @param options - Client configuration options.
29
+ * @param options.apiKey - Your Hashproof API key (starts with `hpsk_`).
30
+ * @param options.baseUrl - Base URL for the API. Defaults to `https://api.hashproof.ai`.
31
+ * @param options.timeout - Request timeout in milliseconds. Defaults to 30000.
32
+ * @throws {Error} If no API key is provided.
33
+ */
34
+ constructor(options) {
35
+ if (!options.apiKey) {
36
+ throw new Error('Hashproof API key is required. Pass { apiKey: "hpsk_..." } to the constructor.');
37
+ }
38
+ this.apiKey = options.apiKey;
39
+ this.baseUrl = (options.baseUrl || DEFAULT_BASE_URL).replace(/\/+$/, "");
40
+ this.timeout = options.timeout ?? DEFAULT_TIMEOUT;
41
+ }
42
+ // ============================================================
43
+ // Manifest Operations
44
+ // ============================================================
45
+ /**
46
+ * Store a C2PA manifest by uploading a digital asset.
47
+ *
48
+ * The API extracts the embedded C2PA manifest from the file,
49
+ * computes perceptual fingerprints (soft bindings), and stores
50
+ * everything for future resolution and verification.
51
+ *
52
+ * @param file - The file to store as a Buffer or Blob.
53
+ * @param options - Optional parameters.
54
+ * @param options.title - A human-readable title for the asset.
55
+ * @returns The stored manifest data along with computed soft bindings.
56
+ *
57
+ * @example
58
+ * ```ts
59
+ * const result = await client.store(fileBuffer, { title: 'My Photo' });
60
+ * console.log(result.manifestId);
61
+ * ```
62
+ */
63
+ async store(file, options) {
64
+ const formData = new FormData();
65
+ const blob = file instanceof Blob ? file : new Blob([file]);
66
+ formData.append("file", blob, "upload");
67
+ if (options?.title) {
68
+ formData.append("title", options.title);
69
+ }
70
+ return this.request("POST", "/v1/manifests", formData);
71
+ }
72
+ async resolve(fileOrFingerprint, algorithm) {
73
+ if (typeof fileOrFingerprint === "string") {
74
+ const params = new URLSearchParams({ fingerprint: fileOrFingerprint });
75
+ if (algorithm) params.set("algorithm", algorithm);
76
+ return this.request("GET", `/v1/resolve?${params.toString()}`);
77
+ }
78
+ const formData = new FormData();
79
+ const blob = fileOrFingerprint instanceof Blob ? fileOrFingerprint : new Blob([fileOrFingerprint]);
80
+ formData.append("file", blob, "upload");
81
+ if (algorithm) {
82
+ formData.append("algorithm", algorithm);
83
+ }
84
+ return this.request("POST", "/v1/resolve", formData);
85
+ }
86
+ /**
87
+ * Verify the provenance of a digital asset.
88
+ *
89
+ * This is the main one-call verification endpoint. It checks:
90
+ * 1. Embedded C2PA manifests in the file
91
+ * 2. Hard binding (SHA-256 hash match) against stored manifests
92
+ * 3. Soft binding (perceptual hash) resolution
93
+ *
94
+ * @param file - The file to verify as a Buffer or Blob.
95
+ * @returns Verification result including provenance source, manifest data, and trust status.
96
+ *
97
+ * @example
98
+ * ```ts
99
+ * const result = await client.verify(imageBuffer);
100
+ * if (result.hasProvenance) {
101
+ * console.log(`Source: ${result.source}, Trust: ${result.trustStatus}`);
102
+ * }
103
+ * ```
104
+ */
105
+ async verify(file) {
106
+ const formData = new FormData();
107
+ const blob = file instanceof Blob ? file : new Blob([file]);
108
+ formData.append("file", blob, "upload");
109
+ return this.request("POST", "/v1/verify", formData);
110
+ }
111
+ /**
112
+ * Retrieve a manifest by its ID.
113
+ *
114
+ * @param id - The manifest UUID.
115
+ * @returns The full manifest data.
116
+ *
117
+ * @example
118
+ * ```ts
119
+ * const manifest = await client.getManifest('550e8400-e29b-41d4-a716-446655440000');
120
+ * console.log(manifest.title, manifest.validationStatus);
121
+ * ```
122
+ */
123
+ async getManifest(id) {
124
+ return this.request("GET", `/v1/manifests/${encodeURIComponent(id)}`);
125
+ }
126
+ /**
127
+ * Retrieve a manifest by its hard binding hash (SHA-256).
128
+ *
129
+ * @param hash - The SHA-256 hash of the original asset.
130
+ * @returns The matching manifest data.
131
+ *
132
+ * @example
133
+ * ```ts
134
+ * const manifest = await client.getManifestByHash('e3b0c44298fc1c149afbf4c8996fb924...');
135
+ * ```
136
+ */
137
+ async getManifestByHash(hash) {
138
+ const params = new URLSearchParams({ hash });
139
+ return this.request("GET", `/v1/manifests?${params.toString()}`);
140
+ }
141
+ /**
142
+ * List manifests with optional pagination and search.
143
+ *
144
+ * @param options - Pagination and search options.
145
+ * @param options.page - Page number (1-indexed). Defaults to 1.
146
+ * @param options.perPage - Results per page. Defaults to 20.
147
+ * @param options.search - Search term to filter manifests by title.
148
+ * @returns A paginated list of manifests.
149
+ *
150
+ * @example
151
+ * ```ts
152
+ * const page = await client.listManifests({ page: 1, search: 'photo' });
153
+ * console.log(`${page.total} manifests found`);
154
+ * ```
155
+ */
156
+ async listManifests(options) {
157
+ const params = new URLSearchParams();
158
+ if (options?.page) params.set("page", String(options.page));
159
+ if (options?.perPage) params.set("perPage", String(options.perPage));
160
+ if (options?.search) params.set("search", options.search);
161
+ const query = params.toString();
162
+ return this.request(
163
+ "GET",
164
+ `/v1/manifests${query ? `?${query}` : ""}`
165
+ );
166
+ }
167
+ // ============================================================
168
+ // Signing
169
+ // ============================================================
170
+ /**
171
+ * Sign a digital asset with a C2PA manifest using Hashproof's managed signing.
172
+ *
173
+ * This is the "Let's Encrypt for C2PA" endpoint -- you upload a file and
174
+ * Hashproof creates a signed C2PA manifest on your behalf, without you
175
+ * needing to manage certificates or PKI.
176
+ *
177
+ * Requires the `sign` scope on your API key.
178
+ *
179
+ * @param file - The file to sign as a Buffer or Blob.
180
+ * @param options - Optional parameters.
181
+ * @param options.title - A human-readable title for the asset.
182
+ * @returns The signing result including the new manifest ID.
183
+ *
184
+ * @example
185
+ * ```ts
186
+ * const result = await client.sign(imageBuffer, { title: 'Press Photo 2024' });
187
+ * console.log(result.manifestId);
188
+ * ```
189
+ */
190
+ async sign(file, options) {
191
+ const formData = new FormData();
192
+ const blob = file instanceof Blob ? file : new Blob([file]);
193
+ formData.append("file", blob, "upload");
194
+ if (options?.title) {
195
+ formData.append("title", options.title);
196
+ }
197
+ return this.request("POST", "/v1/sign", formData);
198
+ }
199
+ // ============================================================
200
+ // Fingerprinting
201
+ // ============================================================
202
+ /**
203
+ * Compute a perceptual fingerprint for a file without resolving.
204
+ *
205
+ * Useful when you want to compute and store a fingerprint client-side
206
+ * for later resolution via `resolve(fingerprint, algorithm)`.
207
+ *
208
+ * @param file - The file to fingerprint as a Buffer or Blob.
209
+ * @param algorithm - The algorithm to use. Defaults to `phash-dct-64`.
210
+ * @returns The computed fingerprint, algorithm, and bit length.
211
+ *
212
+ * @example
213
+ * ```ts
214
+ * const fp = await client.computeFingerprint(imageBuffer, 'phash-dct-64');
215
+ * console.log(fp.fingerprint); // hex string
216
+ * ```
217
+ */
218
+ async computeFingerprint(file, algorithm) {
219
+ const formData = new FormData();
220
+ const blob = file instanceof Blob ? file : new Blob([file]);
221
+ formData.append("file", blob, "upload");
222
+ if (algorithm) {
223
+ formData.append("algorithm", algorithm);
224
+ }
225
+ return this.request("POST", "/v1/fingerprint", formData);
226
+ }
227
+ // ============================================================
228
+ // Internal HTTP Client
229
+ // ============================================================
230
+ /**
231
+ * Make an authenticated HTTP request to the Hashproof API.
232
+ * @internal
233
+ */
234
+ async request(method, path, body) {
235
+ const url = `${this.baseUrl}${path}`;
236
+ const headers = {
237
+ Authorization: `Bearer ${this.apiKey}`,
238
+ Accept: "application/json"
239
+ };
240
+ let requestBody;
241
+ if (body instanceof FormData) {
242
+ requestBody = body;
243
+ } else if (body) {
244
+ headers["Content-Type"] = "application/json";
245
+ requestBody = JSON.stringify(body);
246
+ }
247
+ const controller = new AbortController();
248
+ const timeoutId = setTimeout(() => controller.abort(), this.timeout);
249
+ let response;
250
+ try {
251
+ response = await fetch(url, {
252
+ method,
253
+ headers,
254
+ body: requestBody,
255
+ signal: controller.signal
256
+ });
257
+ } catch (err) {
258
+ clearTimeout(timeoutId);
259
+ if (err instanceof DOMException && err.name === "AbortError") {
260
+ throw new HashproofApiError(
261
+ `Request timed out after ${this.timeout}ms`,
262
+ 0,
263
+ "TIMEOUT"
264
+ );
265
+ }
266
+ throw new HashproofApiError(
267
+ `Network error: ${err instanceof Error ? err.message : String(err)}`,
268
+ 0,
269
+ "NETWORK_ERROR"
270
+ );
271
+ } finally {
272
+ clearTimeout(timeoutId);
273
+ }
274
+ const responseBody = await response.text();
275
+ let parsed;
276
+ try {
277
+ parsed = JSON.parse(responseBody);
278
+ } catch {
279
+ if (!response.ok) {
280
+ throw new HashproofApiError(
281
+ `HTTP ${response.status}: ${responseBody.slice(0, 200)}`,
282
+ response.status,
283
+ "UNEXPECTED_RESPONSE"
284
+ );
285
+ }
286
+ throw new HashproofApiError(
287
+ "Failed to parse API response as JSON",
288
+ response.status,
289
+ "PARSE_ERROR"
290
+ );
291
+ }
292
+ if (!response.ok) {
293
+ const errBody = parsed;
294
+ throw new HashproofApiError(
295
+ errBody.error || `HTTP ${response.status}`,
296
+ errBody.statusCode || response.status,
297
+ errBody.code || "API_ERROR",
298
+ errBody.details
299
+ );
300
+ }
301
+ return parsed;
302
+ }
303
+ };
304
+ export {
305
+ HashproofApiError,
306
+ HashproofClient
307
+ };
package/package.json ADDED
@@ -0,0 +1,54 @@
1
+ {
2
+ "name": "@hashproof/sdk",
3
+ "version": "0.2.0",
4
+ "description": "Official JavaScript/TypeScript SDK for the Hashproof Content Provenance API",
5
+ "type": "module",
6
+ "main": "./dist/index.cjs",
7
+ "module": "./dist/index.js",
8
+ "types": "./dist/index.d.ts",
9
+ "exports": {
10
+ ".": {
11
+ "import": {
12
+ "types": "./dist/index.d.ts",
13
+ "default": "./dist/index.js"
14
+ },
15
+ "require": {
16
+ "types": "./dist/index.d.cts",
17
+ "default": "./dist/index.cjs"
18
+ }
19
+ }
20
+ },
21
+ "files": [
22
+ "dist"
23
+ ],
24
+ "dependencies": {
25
+ "@hashproof/shared": "0.1.0"
26
+ },
27
+ "devDependencies": {
28
+ "typescript": "^5.4.0",
29
+ "tsup": "^8.0.0",
30
+ "vitest": "^1.3.0",
31
+ "@types/node": "^20.11.0"
32
+ },
33
+ "keywords": [
34
+ "hashproof",
35
+ "c2pa",
36
+ "content-provenance",
37
+ "content-authenticity",
38
+ "sdk"
39
+ ],
40
+ "license": "MIT",
41
+ "repository": {
42
+ "type": "git",
43
+ "url": "https://github.com/hashproof/hashproof",
44
+ "directory": "packages/sdk-js"
45
+ },
46
+ "scripts": {
47
+ "build": "tsup src/index.ts --format cjs,esm --dts --out-dir dist",
48
+ "build:tsc": "tsc",
49
+ "test": "vitest run",
50
+ "test:watch": "vitest",
51
+ "typecheck": "tsc --noEmit",
52
+ "lint": "eslint src/"
53
+ }
54
+ }