@proveanything/smartlinks 1.0.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 +184 -0
- package/dist/index.d.ts +113 -0
- package/dist/index.js +105 -0
- package/examples/browser-demo.html +32 -0
- package/examples/node-demo.ts +22 -0
- package/examples/react-demo.tsx +44 -0
- package/package.json +21 -0
- package/src/index.ts +171 -0
- package/tsconfig.json +15 -0
package/README.md
ADDED
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
# @smartlinks/sdk
|
|
2
|
+
|
|
3
|
+
An official JavaScript/TypeScript client SDK for the Smartlinks API. This package provides a simple wrapper around the Smartlinks REST endpoints, allowing you to fetch Collection, Product, and App Configuration data in both browser and Node.js environments.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install @smartlinks/sdk
|
|
9
|
+
# or
|
|
10
|
+
yarn add @smartlinks/sdk
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
## Quickstart
|
|
14
|
+
|
|
15
|
+
```ts
|
|
16
|
+
import { ApiClient, CollectionResponse, ProductResponse } from "@smartlinks/sdk";
|
|
17
|
+
|
|
18
|
+
async function main() {
|
|
19
|
+
// Instantiate the client (no apiKey needed for public endpoints, but shown here for reference)
|
|
20
|
+
const client = new ApiClient("https://smartlinks.app/api/v1", "YOUR_API_KEY_HERE");
|
|
21
|
+
|
|
22
|
+
try {
|
|
23
|
+
// Fetch a collection by ID
|
|
24
|
+
const collection: CollectionResponse = await client.getCollection("abc123");
|
|
25
|
+
console.log("Collection:", collection);
|
|
26
|
+
|
|
27
|
+
// Fetch a product item by collection ID & product ID
|
|
28
|
+
const product: ProductResponse = await client.getProductItem("abc123", "prod789");
|
|
29
|
+
console.log("Product Item:", product);
|
|
30
|
+
} catch (err) {
|
|
31
|
+
console.error("Error fetching data:", err);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
main();
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
## API Reference
|
|
39
|
+
|
|
40
|
+
### Class: `ApiClient`
|
|
41
|
+
|
|
42
|
+
```ts
|
|
43
|
+
constructor(baseURL: string, apiKey?: string)
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
- **Parameters:**
|
|
47
|
+
- `baseURL` (`string`, required): The root URL of the Smartlinks API, e.g. `https://smartlinks.app/api/v1`.
|
|
48
|
+
- `apiKey` (`string`, optional): Your Bearer token. If omitted, requests will be sent without an `Authorization` header.
|
|
49
|
+
|
|
50
|
+
---
|
|
51
|
+
|
|
52
|
+
#### `getCollection(collectionId: string): Promise<CollectionResponse>`
|
|
53
|
+
|
|
54
|
+
Fetches a single collection by its ID.
|
|
55
|
+
|
|
56
|
+
- **Parameters:**
|
|
57
|
+
- `collectionId` (`string`, required): The unique identifier of the collection to fetch.
|
|
58
|
+
- **Returns:**
|
|
59
|
+
A `Promise` that resolves to a `CollectionResponse` object:
|
|
60
|
+
|
|
61
|
+
```ts
|
|
62
|
+
export interface CollectionResponse {
|
|
63
|
+
id: string;
|
|
64
|
+
name: string;
|
|
65
|
+
title: string;
|
|
66
|
+
logoImage: string;
|
|
67
|
+
}
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
- **Example:**
|
|
71
|
+
```ts
|
|
72
|
+
const client = new ApiClient("https://smartlinks.app/api/v1", "YOUR_API_KEY");
|
|
73
|
+
const collection = await client.getCollection("abc123");
|
|
74
|
+
console.log("Fetched collection:", collection);
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
---
|
|
78
|
+
|
|
79
|
+
#### `getProductItem(collectionId: string, productId: string): Promise<ProductResponse>`
|
|
80
|
+
|
|
81
|
+
Fetches a single product item within a collection.
|
|
82
|
+
|
|
83
|
+
- **Parameters:**
|
|
84
|
+
- `collectionId` (`string`, required): The parent collection’s ID.
|
|
85
|
+
- `productId` (`string`, required): The product item’s ID.
|
|
86
|
+
- **Returns:**
|
|
87
|
+
A `Promise` that resolves to a `ProductResponse` object:
|
|
88
|
+
|
|
89
|
+
```ts
|
|
90
|
+
export interface ProductResponse {
|
|
91
|
+
id: string;
|
|
92
|
+
name: string;
|
|
93
|
+
description: string;
|
|
94
|
+
heroImage: string;
|
|
95
|
+
}
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
- **Example:**
|
|
99
|
+
```ts
|
|
100
|
+
const client = new ApiClient("https://smartlinks.app/api/v1", "YOUR_API_KEY");
|
|
101
|
+
const product = await client.getProductItem("abc123", "prod789");
|
|
102
|
+
console.log("Fetched product:", product);
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
---
|
|
106
|
+
|
|
107
|
+
#### `getAppConfiguration(collectionId: string, appId: string): Promise<AppConfigurationResponse>`
|
|
108
|
+
|
|
109
|
+
Fetches a single app configuration within a collection.
|
|
110
|
+
|
|
111
|
+
- **Parameters:**
|
|
112
|
+
- `collectionId` (`string`, required): The parent collection’s ID.
|
|
113
|
+
- `appId` (`string`, required): The app configuration’s ID.
|
|
114
|
+
- **Returns:**
|
|
115
|
+
A `Promise` that resolves to an `AppConfigurationResponse` object:
|
|
116
|
+
|
|
117
|
+
```ts
|
|
118
|
+
export interface AppConfigurationResponse {
|
|
119
|
+
id: string;
|
|
120
|
+
name: string;
|
|
121
|
+
settings?: Record<string, any>;
|
|
122
|
+
}
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
- **Example:**
|
|
126
|
+
```ts
|
|
127
|
+
const client = new ApiClient("https://smartlinks.app/api/v1", "YOUR_API_KEY");
|
|
128
|
+
const config = await client.getAppConfiguration("abc123", "app456");
|
|
129
|
+
console.log("Fetched app configuration:", config);
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
---
|
|
133
|
+
|
|
134
|
+
## Authentication
|
|
135
|
+
|
|
136
|
+
All endpoints require a Bearer token passed in the `AUTHORIZATION` header. When instantiating `ApiClient`, optionally supply your token:
|
|
137
|
+
|
|
138
|
+
```ts
|
|
139
|
+
import { ApiClient } from "@smartlinks/sdk";
|
|
140
|
+
|
|
141
|
+
const apiKey = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...";
|
|
142
|
+
const client = new ApiClient("https://smartlinks.app/api/v1", apiKey);
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
If `apiKey` is omitted, requests will be sent without an `Authorization` header, which may cause a `401 Unauthorized` for protected endpoints.
|
|
146
|
+
|
|
147
|
+
## Error Handling
|
|
148
|
+
|
|
149
|
+
All methods throw an `Error` when the server responds with a non-2xx status. The thrown error message includes the numeric error code and message from the API. Example:
|
|
150
|
+
|
|
151
|
+
```ts
|
|
152
|
+
import { ApiClient } from "@smartlinks/sdk";
|
|
153
|
+
|
|
154
|
+
async function fetchData() {
|
|
155
|
+
const client = new ApiClient("https://smartlinks.app/api/v1", "INVALID_KEY");
|
|
156
|
+
|
|
157
|
+
try {
|
|
158
|
+
await client.getCollection("nonexistent");
|
|
159
|
+
} catch (err) {
|
|
160
|
+
// err.message might be: "Error 401: Unauthorized" or "Error 404: Not Found"
|
|
161
|
+
console.error("Request failed:", err);
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
fetchData();
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
## Examples
|
|
169
|
+
|
|
170
|
+
See the **examples/** folder for complete, runnable samples:
|
|
171
|
+
|
|
172
|
+
- [`examples/node-demo.ts`](examples/node-demo.ts)
|
|
173
|
+
- [`examples/browser-demo.html`](examples/browser-demo.html)
|
|
174
|
+
- [`examples/react-demo.tsx`](examples/react-demo.tsx)
|
|
175
|
+
|
|
176
|
+
## Changelog
|
|
177
|
+
|
|
178
|
+
### 1.0.0
|
|
179
|
+
|
|
180
|
+
- Initial release:
|
|
181
|
+
- `ApiClient` class with `getCollection`, `getProductItem`, and `getAppConfiguration` methods.
|
|
182
|
+
- Full TypeScript typings and JSDoc.
|
|
183
|
+
- Browser/Node fetch support.
|
|
184
|
+
- Error handling via thrown `Error` objects.
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Represents a Collection object.
|
|
3
|
+
*/
|
|
4
|
+
export interface CollectionResponse {
|
|
5
|
+
/** Unique identifier for the collection */
|
|
6
|
+
id: string;
|
|
7
|
+
/** Machine‐readable name of the collection */
|
|
8
|
+
name: string;
|
|
9
|
+
/** Human‐readable title of the collection */
|
|
10
|
+
title: string;
|
|
11
|
+
/** URL to the collection’s logo image */
|
|
12
|
+
logoImage: string;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Represents a Product Item object.
|
|
16
|
+
*/
|
|
17
|
+
export interface ProductResponse {
|
|
18
|
+
/** Unique identifier for the product */
|
|
19
|
+
id: string;
|
|
20
|
+
/** Name of the product */
|
|
21
|
+
name: string;
|
|
22
|
+
/** Detailed description of the product */
|
|
23
|
+
description: string;
|
|
24
|
+
/** URL to the product’s hero image */
|
|
25
|
+
heroImage: string;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Represents an App Configuration object.
|
|
29
|
+
*/
|
|
30
|
+
export interface AppConfigurationResponse {
|
|
31
|
+
/** Unique identifier for the app configuration */
|
|
32
|
+
id: string;
|
|
33
|
+
/** Name of the app configuration */
|
|
34
|
+
name: string;
|
|
35
|
+
/** Key‐value pairs representing configuration settings */
|
|
36
|
+
settings?: Record<string, any>;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Represents a standardized error response.
|
|
40
|
+
*/
|
|
41
|
+
export interface ErrorResponse {
|
|
42
|
+
/** Numeric error code */
|
|
43
|
+
code: number;
|
|
44
|
+
/** Human‐readable error message */
|
|
45
|
+
message: string;
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* ApiClient for the Smartlinks API.
|
|
49
|
+
* Supports both browser (native fetch) and Node (using cross-fetch).
|
|
50
|
+
*/
|
|
51
|
+
export declare class ApiClient {
|
|
52
|
+
private baseURL;
|
|
53
|
+
private apiKey?;
|
|
54
|
+
private bearerToken?;
|
|
55
|
+
/**
|
|
56
|
+
* Creates an instance of ApiClient.
|
|
57
|
+
* @param baseURL - The base URL of the Smartlinks API (e.g., https://smartlinks.app/api/v1)
|
|
58
|
+
* @param apiKey - (Optional) API key for X-API-Key header
|
|
59
|
+
* @param bearerToken - (Optional) Bearer token for AUTHORIZATION header
|
|
60
|
+
*
|
|
61
|
+
* @example
|
|
62
|
+
* // With both API key and bearer token
|
|
63
|
+
* const client = new ApiClient(
|
|
64
|
+
* 'https://smartlinks.app/api/v1',
|
|
65
|
+
* 'your-api-key',
|
|
66
|
+
* 'your-bearer-token'
|
|
67
|
+
* );
|
|
68
|
+
*
|
|
69
|
+
* // With only API key
|
|
70
|
+
* const client = new ApiClient(
|
|
71
|
+
* 'https://smartlinks.app/api/v1',
|
|
72
|
+
* 'your-api-key'
|
|
73
|
+
* );
|
|
74
|
+
*
|
|
75
|
+
* // With only bearer token
|
|
76
|
+
* const client = new ApiClient(
|
|
77
|
+
* 'https://smartlinks.app/api/v1',
|
|
78
|
+
* undefined,
|
|
79
|
+
* 'your-bearer-token'
|
|
80
|
+
* );
|
|
81
|
+
*/
|
|
82
|
+
constructor(baseURL: string, apiKey?: string, bearerToken?: string);
|
|
83
|
+
/**
|
|
84
|
+
* Retrieves a single Collection by its ID.
|
|
85
|
+
* @param collectionId - Identifier of the collection
|
|
86
|
+
* @returns Promise resolving to a CollectionResponse object
|
|
87
|
+
* @throws ErrorResponse if the request fails
|
|
88
|
+
*/
|
|
89
|
+
getCollection(collectionId: string): Promise<CollectionResponse>;
|
|
90
|
+
/**
|
|
91
|
+
* Retrieves a single Product Item by Collection ID and Product ID.
|
|
92
|
+
* @param collectionId - Identifier of the parent collection
|
|
93
|
+
* @param productId - Identifier of the product item
|
|
94
|
+
* @returns Promise resolving to a ProductResponse object
|
|
95
|
+
* @throws ErrorResponse if the request fails
|
|
96
|
+
*/
|
|
97
|
+
getProductItem(collectionId: string, productId: string): Promise<ProductResponse>;
|
|
98
|
+
/**
|
|
99
|
+
* Retrieves a single App Configuration by Collection ID and App ID.
|
|
100
|
+
* @param collectionId - Identifier of the parent collection
|
|
101
|
+
* @param appId - Identifier of the app configuration
|
|
102
|
+
* @returns Promise resolving to an AppConfigurationResponse object
|
|
103
|
+
* @throws ErrorResponse if the request fails
|
|
104
|
+
*/
|
|
105
|
+
getAppConfiguration(collectionId: string, appId: string): Promise<AppConfigurationResponse>;
|
|
106
|
+
/**
|
|
107
|
+
* Internal helper to perform a GET request and parse JSON.
|
|
108
|
+
* @param path - The path (relative to baseURL) to request
|
|
109
|
+
* @returns Promise resolving to the parsed JSON of type T
|
|
110
|
+
* @throws Error if network error or a non-2xx response is returned
|
|
111
|
+
*/
|
|
112
|
+
private request;
|
|
113
|
+
}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
import fetch from 'cross-fetch';
|
|
2
|
+
///////////////////////////
|
|
3
|
+
// ApiClient Class
|
|
4
|
+
///////////////////////////
|
|
5
|
+
/**
|
|
6
|
+
* ApiClient for the Smartlinks API.
|
|
7
|
+
* Supports both browser (native fetch) and Node (using cross-fetch).
|
|
8
|
+
*/
|
|
9
|
+
export class ApiClient {
|
|
10
|
+
/**
|
|
11
|
+
* Creates an instance of ApiClient.
|
|
12
|
+
* @param baseURL - The base URL of the Smartlinks API (e.g., https://smartlinks.app/api/v1)
|
|
13
|
+
* @param apiKey - (Optional) API key for X-API-Key header
|
|
14
|
+
* @param bearerToken - (Optional) Bearer token for AUTHORIZATION header
|
|
15
|
+
*
|
|
16
|
+
* @example
|
|
17
|
+
* // With both API key and bearer token
|
|
18
|
+
* const client = new ApiClient(
|
|
19
|
+
* 'https://smartlinks.app/api/v1',
|
|
20
|
+
* 'your-api-key',
|
|
21
|
+
* 'your-bearer-token'
|
|
22
|
+
* );
|
|
23
|
+
*
|
|
24
|
+
* // With only API key
|
|
25
|
+
* const client = new ApiClient(
|
|
26
|
+
* 'https://smartlinks.app/api/v1',
|
|
27
|
+
* 'your-api-key'
|
|
28
|
+
* );
|
|
29
|
+
*
|
|
30
|
+
* // With only bearer token
|
|
31
|
+
* const client = new ApiClient(
|
|
32
|
+
* 'https://smartlinks.app/api/v1',
|
|
33
|
+
* undefined,
|
|
34
|
+
* 'your-bearer-token'
|
|
35
|
+
* );
|
|
36
|
+
*/
|
|
37
|
+
constructor(baseURL, apiKey, bearerToken) {
|
|
38
|
+
this.baseURL = baseURL.replace(/\/+$/, ''); // Trim trailing slash
|
|
39
|
+
this.apiKey = apiKey;
|
|
40
|
+
this.bearerToken = bearerToken;
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Retrieves a single Collection by its ID.
|
|
44
|
+
* @param collectionId - Identifier of the collection
|
|
45
|
+
* @returns Promise resolving to a CollectionResponse object
|
|
46
|
+
* @throws ErrorResponse if the request fails
|
|
47
|
+
*/
|
|
48
|
+
async getCollection(collectionId) {
|
|
49
|
+
const path = `/public/collection/${encodeURIComponent(collectionId)}`;
|
|
50
|
+
return this.request(path);
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Retrieves a single Product Item by Collection ID and Product ID.
|
|
54
|
+
* @param collectionId - Identifier of the parent collection
|
|
55
|
+
* @param productId - Identifier of the product item
|
|
56
|
+
* @returns Promise resolving to a ProductResponse object
|
|
57
|
+
* @throws ErrorResponse if the request fails
|
|
58
|
+
*/
|
|
59
|
+
async getProductItem(collectionId, productId) {
|
|
60
|
+
const path = `/public/collection/${encodeURIComponent(collectionId)}/product/${encodeURIComponent(productId)}`;
|
|
61
|
+
return this.request(path);
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Retrieves a single App Configuration by Collection ID and App ID.
|
|
65
|
+
* @param collectionId - Identifier of the parent collection
|
|
66
|
+
* @param appId - Identifier of the app configuration
|
|
67
|
+
* @returns Promise resolving to an AppConfigurationResponse object
|
|
68
|
+
* @throws ErrorResponse if the request fails
|
|
69
|
+
*/
|
|
70
|
+
async getAppConfiguration(collectionId, appId) {
|
|
71
|
+
const path = `/public/collection/${encodeURIComponent(collectionId)}/app/${encodeURIComponent(appId)}`;
|
|
72
|
+
return this.request(path);
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Internal helper to perform a GET request and parse JSON.
|
|
76
|
+
* @param path - The path (relative to baseURL) to request
|
|
77
|
+
* @returns Promise resolving to the parsed JSON of type T
|
|
78
|
+
* @throws Error if network error or a non-2xx response is returned
|
|
79
|
+
*/
|
|
80
|
+
async request(path) {
|
|
81
|
+
const url = `${this.baseURL}${path}`;
|
|
82
|
+
const headers = {
|
|
83
|
+
'Content-Type': 'application/json',
|
|
84
|
+
};
|
|
85
|
+
if (this.apiKey) {
|
|
86
|
+
headers['X-API-Key'] = this.apiKey;
|
|
87
|
+
}
|
|
88
|
+
if (this.bearerToken) {
|
|
89
|
+
headers['AUTHORIZATION'] = `Bearer ${this.bearerToken}`;
|
|
90
|
+
}
|
|
91
|
+
const response = await fetch(url, { method: 'GET', headers });
|
|
92
|
+
if (!response.ok) {
|
|
93
|
+
// Attempt to parse error body; if it fails, throw generic
|
|
94
|
+
let errorBody;
|
|
95
|
+
try {
|
|
96
|
+
errorBody = (await response.json());
|
|
97
|
+
}
|
|
98
|
+
catch (_a) {
|
|
99
|
+
throw new Error(`Request failed with status ${response.status}`);
|
|
100
|
+
}
|
|
101
|
+
throw new Error(`Error ${errorBody.code}: ${errorBody.message}`);
|
|
102
|
+
}
|
|
103
|
+
return (await response.json());
|
|
104
|
+
}
|
|
105
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html>
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8" />
|
|
5
|
+
<title>Smartlinks SDK Browser Demo</title>
|
|
6
|
+
</head>
|
|
7
|
+
<body>
|
|
8
|
+
<h1>Smartlinks SDK Browser Demo</h1>
|
|
9
|
+
<pre id="output"></pre>
|
|
10
|
+
<script type="module">
|
|
11
|
+
import { ApiClient } from '../dist/index.js';
|
|
12
|
+
|
|
13
|
+
async function run() {
|
|
14
|
+
// You can provide either or both of these values:
|
|
15
|
+
const apiKey = 'YOUR_API_KEY'; // sent as X-API-Key header (optional)
|
|
16
|
+
const bearerToken = 'YOUR_BEARER_TOKEN'; // sent as AUTHORIZATION: Bearer ... (optional)
|
|
17
|
+
|
|
18
|
+
// Example: with both headers
|
|
19
|
+
const client = new ApiClient('https://smartlinks.app/api/v1', apiKey, bearerToken);
|
|
20
|
+
|
|
21
|
+
try {
|
|
22
|
+
const collection = await client.getCollection('abc123');
|
|
23
|
+
const product = await client.getProductItem('abc123', 'prod789');
|
|
24
|
+
document.getElementById('output').textContent = JSON.stringify({ collection, product }, null, 2);
|
|
25
|
+
} catch (err) {
|
|
26
|
+
document.getElementById('output').textContent = 'Error: ' + err.message;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
run();
|
|
30
|
+
</script>
|
|
31
|
+
</body>
|
|
32
|
+
</html>
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { ApiClient, CollectionResponse, ProductResponse } from "../src/index";
|
|
2
|
+
|
|
3
|
+
async function main() {
|
|
4
|
+
// You can provide either or both of these values:
|
|
5
|
+
const apiKey = "YOUR_API_KEY"; // sent as X-API-Key header (optional)
|
|
6
|
+
const bearerToken = "YOUR_BEARER_TOKEN"; // sent as AUTHORIZATION: Bearer ... (optional)
|
|
7
|
+
|
|
8
|
+
// Example: with both headers
|
|
9
|
+
const client = new ApiClient("https://smartlinks.app/api/v1", apiKey, bearerToken);
|
|
10
|
+
|
|
11
|
+
try {
|
|
12
|
+
const collection: CollectionResponse = await client.getCollection("abc123");
|
|
13
|
+
console.log("Collection:", collection);
|
|
14
|
+
|
|
15
|
+
const product: ProductResponse = await client.getProductItem("abc123", "prod789");
|
|
16
|
+
console.log("Product Item:", product);
|
|
17
|
+
} catch (err) {
|
|
18
|
+
console.error("Error fetching data:", err);
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
main();
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import React, { useEffect, useState } from 'react';
|
|
2
|
+
import { ApiClient, CollectionResponse, ProductResponse } from '@smartlinks/sdk';
|
|
3
|
+
|
|
4
|
+
// You can provide either or both of these values:
|
|
5
|
+
const apiKey = 'YOUR_API_KEY'; // sent as X-API-Key header (optional)
|
|
6
|
+
const bearerToken = 'YOUR_BEARER_TOKEN'; // sent as AUTHORIZATION: Bearer ... (optional)
|
|
7
|
+
|
|
8
|
+
// Example: with both headers
|
|
9
|
+
const client = new ApiClient('https://smartlinks.app/api/v1', apiKey, bearerToken);
|
|
10
|
+
|
|
11
|
+
const ReactDemo: React.FC = () => {
|
|
12
|
+
const [collection, setCollection] = useState<CollectionResponse | null>(null);
|
|
13
|
+
const [product, setProduct] = useState<ProductResponse | null>(null);
|
|
14
|
+
const [error, setError] = useState<string | null>(null);
|
|
15
|
+
|
|
16
|
+
useEffect(() => {
|
|
17
|
+
async function fetchData() {
|
|
18
|
+
try {
|
|
19
|
+
const col = await client.getCollection('abc123');
|
|
20
|
+
const prod = await client.getProductItem('abc123', 'prod789');
|
|
21
|
+
setCollection(col);
|
|
22
|
+
setProduct(prod);
|
|
23
|
+
} catch (err) {
|
|
24
|
+
setError((err as Error).message);
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
fetchData();
|
|
28
|
+
}, []);
|
|
29
|
+
|
|
30
|
+
if (error) return <div>Error: {error}</div>;
|
|
31
|
+
if (!collection || !product) return <div>Loading...</div>;
|
|
32
|
+
|
|
33
|
+
return (
|
|
34
|
+
<div>
|
|
35
|
+
<h1>{collection.title}</h1>
|
|
36
|
+
<img src={collection.logoImage} alt={collection.title} width="100" />
|
|
37
|
+
<h2>{product.name}</h2>
|
|
38
|
+
<img src={product.heroImage} alt={product.name} width="100" />
|
|
39
|
+
<p>{product.description}</p>
|
|
40
|
+
</div>
|
|
41
|
+
);
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
export default ReactDemo;
|
package/package.json
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@proveanything/smartlinks",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Official JavaScript/TypeScript SDK for the Smartlinks API",
|
|
5
|
+
"main": "dist/index.js",
|
|
6
|
+
"types": "dist/index.d.ts",
|
|
7
|
+
"scripts": {
|
|
8
|
+
"build": "tsc"
|
|
9
|
+
},
|
|
10
|
+
"author": "Glenn Shoosmith",
|
|
11
|
+
"license": "MIT",
|
|
12
|
+
"publishConfig": {
|
|
13
|
+
"access": "public"
|
|
14
|
+
},
|
|
15
|
+
"dependencies": {
|
|
16
|
+
"cross-fetch": "^3.1.5"
|
|
17
|
+
},
|
|
18
|
+
"devDependencies": {
|
|
19
|
+
"typescript": "^4.9.0"
|
|
20
|
+
}
|
|
21
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
import fetch from 'cross-fetch';
|
|
2
|
+
|
|
3
|
+
///////////////////////////
|
|
4
|
+
// Schema Interfaces
|
|
5
|
+
///////////////////////////
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Represents a Collection object.
|
|
9
|
+
*/
|
|
10
|
+
export interface CollectionResponse {
|
|
11
|
+
/** Unique identifier for the collection */
|
|
12
|
+
id: string;
|
|
13
|
+
/** Machine‐readable name of the collection */
|
|
14
|
+
name: string;
|
|
15
|
+
/** Human‐readable title of the collection */
|
|
16
|
+
title: string;
|
|
17
|
+
/** URL to the collection’s logo image */
|
|
18
|
+
logoImage: string;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Represents a Product Item object.
|
|
23
|
+
*/
|
|
24
|
+
export interface ProductResponse {
|
|
25
|
+
/** Unique identifier for the product */
|
|
26
|
+
id: string;
|
|
27
|
+
/** Name of the product */
|
|
28
|
+
name: string;
|
|
29
|
+
/** Detailed description of the product */
|
|
30
|
+
description: string;
|
|
31
|
+
/** URL to the product’s hero image */
|
|
32
|
+
heroImage: string;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Represents an App Configuration object.
|
|
37
|
+
*/
|
|
38
|
+
export interface AppConfigurationResponse {
|
|
39
|
+
/** Unique identifier for the app configuration */
|
|
40
|
+
id: string;
|
|
41
|
+
/** Name of the app configuration */
|
|
42
|
+
name: string;
|
|
43
|
+
/** Key‐value pairs representing configuration settings */
|
|
44
|
+
settings?: Record<string, any>;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Represents a standardized error response.
|
|
49
|
+
*/
|
|
50
|
+
export interface ErrorResponse {
|
|
51
|
+
/** Numeric error code */
|
|
52
|
+
code: number;
|
|
53
|
+
/** Human‐readable error message */
|
|
54
|
+
message: string;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
///////////////////////////
|
|
58
|
+
// ApiClient Class
|
|
59
|
+
///////////////////////////
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* ApiClient for the Smartlinks API.
|
|
63
|
+
* Supports both browser (native fetch) and Node (using cross-fetch).
|
|
64
|
+
*/
|
|
65
|
+
export class ApiClient {
|
|
66
|
+
private baseURL: string;
|
|
67
|
+
private apiKey?: string;
|
|
68
|
+
private bearerToken?: string;
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Creates an instance of ApiClient.
|
|
72
|
+
* @param baseURL - The base URL of the Smartlinks API (e.g., https://smartlinks.app/api/v1)
|
|
73
|
+
* @param apiKey - (Optional) API key for X-API-Key header
|
|
74
|
+
* @param bearerToken - (Optional) Bearer token for AUTHORIZATION header
|
|
75
|
+
*
|
|
76
|
+
* @example
|
|
77
|
+
* // With both API key and bearer token
|
|
78
|
+
* const client = new ApiClient(
|
|
79
|
+
* 'https://smartlinks.app/api/v1',
|
|
80
|
+
* 'your-api-key',
|
|
81
|
+
* 'your-bearer-token'
|
|
82
|
+
* );
|
|
83
|
+
*
|
|
84
|
+
* // With only API key
|
|
85
|
+
* const client = new ApiClient(
|
|
86
|
+
* 'https://smartlinks.app/api/v1',
|
|
87
|
+
* 'your-api-key'
|
|
88
|
+
* );
|
|
89
|
+
*
|
|
90
|
+
* // With only bearer token
|
|
91
|
+
* const client = new ApiClient(
|
|
92
|
+
* 'https://smartlinks.app/api/v1',
|
|
93
|
+
* undefined,
|
|
94
|
+
* 'your-bearer-token'
|
|
95
|
+
* );
|
|
96
|
+
*/
|
|
97
|
+
constructor(baseURL: string, apiKey?: string, bearerToken?: string) {
|
|
98
|
+
this.baseURL = baseURL.replace(/\/+$/, ''); // Trim trailing slash
|
|
99
|
+
this.apiKey = apiKey;
|
|
100
|
+
this.bearerToken = bearerToken;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* Retrieves a single Collection by its ID.
|
|
105
|
+
* @param collectionId - Identifier of the collection
|
|
106
|
+
* @returns Promise resolving to a CollectionResponse object
|
|
107
|
+
* @throws ErrorResponse if the request fails
|
|
108
|
+
*/
|
|
109
|
+
public async getCollection(collectionId: string): Promise<CollectionResponse> {
|
|
110
|
+
const path = `/public/collection/${encodeURIComponent(collectionId)}`;
|
|
111
|
+
return this.request<CollectionResponse>(path);
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* Retrieves a single Product Item by Collection ID and Product ID.
|
|
116
|
+
* @param collectionId - Identifier of the parent collection
|
|
117
|
+
* @param productId - Identifier of the product item
|
|
118
|
+
* @returns Promise resolving to a ProductResponse object
|
|
119
|
+
* @throws ErrorResponse if the request fails
|
|
120
|
+
*/
|
|
121
|
+
public async getProductItem(collectionId: string, productId: string): Promise<ProductResponse> {
|
|
122
|
+
const path = `/public/collection/${encodeURIComponent(collectionId)}/product/${encodeURIComponent(productId)}`;
|
|
123
|
+
return this.request<ProductResponse>(path);
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* Retrieves a single App Configuration by Collection ID and App ID.
|
|
128
|
+
* @param collectionId - Identifier of the parent collection
|
|
129
|
+
* @param appId - Identifier of the app configuration
|
|
130
|
+
* @returns Promise resolving to an AppConfigurationResponse object
|
|
131
|
+
* @throws ErrorResponse if the request fails
|
|
132
|
+
*/
|
|
133
|
+
public async getAppConfiguration(collectionId: string, appId: string): Promise<AppConfigurationResponse> {
|
|
134
|
+
const path = `/public/collection/${encodeURIComponent(collectionId)}/app/${encodeURIComponent(appId)}`;
|
|
135
|
+
return this.request<AppConfigurationResponse>(path);
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
/**
|
|
139
|
+
* Internal helper to perform a GET request and parse JSON.
|
|
140
|
+
* @param path - The path (relative to baseURL) to request
|
|
141
|
+
* @returns Promise resolving to the parsed JSON of type T
|
|
142
|
+
* @throws Error if network error or a non-2xx response is returned
|
|
143
|
+
*/
|
|
144
|
+
private async request<T>(path: string): Promise<T> {
|
|
145
|
+
const url = `${this.baseURL}${path}`;
|
|
146
|
+
const headers: Record<string, string> = {
|
|
147
|
+
'Content-Type': 'application/json',
|
|
148
|
+
};
|
|
149
|
+
if (this.apiKey) {
|
|
150
|
+
headers['X-API-Key'] = this.apiKey;
|
|
151
|
+
}
|
|
152
|
+
if (this.bearerToken) {
|
|
153
|
+
headers['AUTHORIZATION'] = `Bearer ${this.bearerToken}`;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
const response = await fetch(url, { method: 'GET', headers });
|
|
157
|
+
|
|
158
|
+
if (!response.ok) {
|
|
159
|
+
// Attempt to parse error body; if it fails, throw generic
|
|
160
|
+
let errorBody: ErrorResponse;
|
|
161
|
+
try {
|
|
162
|
+
errorBody = (await response.json()) as ErrorResponse;
|
|
163
|
+
} catch {
|
|
164
|
+
throw new Error(`Request failed with status ${response.status}`);
|
|
165
|
+
}
|
|
166
|
+
throw new Error(`Error ${errorBody.code}: ${errorBody.message}`);
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
return (await response.json()) as T;
|
|
170
|
+
}
|
|
171
|
+
}
|
package/tsconfig.json
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"target": "ES2017",
|
|
4
|
+
"module": "ES6",
|
|
5
|
+
"moduleResolution": "node",
|
|
6
|
+
"declaration": true,
|
|
7
|
+
"outDir": "dist",
|
|
8
|
+
"strict": true,
|
|
9
|
+
"esModuleInterop": true,
|
|
10
|
+
"forceConsistentCasingInFileNames": true
|
|
11
|
+
},
|
|
12
|
+
"include": [
|
|
13
|
+
"src/**/*"
|
|
14
|
+
]
|
|
15
|
+
}
|