keepa-api 0.2.0 → 0.2.3
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 +10 -10
- package/dist/client.d.ts +2 -2
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +3 -3
- package/dist/client.js.map +1 -1
- package/dist/core/resource.d.ts +3 -3
- package/dist/core/resource.d.ts.map +1 -1
- package/dist/core/resource.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/lib/marketplace.d.ts +2 -2
- package/dist/lib/marketplace.d.ts.map +1 -1
- package/dist/lib/marketplace.js +6 -5
- package/dist/lib/marketplace.js.map +1 -1
- package/dist/resources/products/error.d.ts +10 -0
- package/dist/resources/products/error.d.ts.map +1 -0
- package/dist/resources/products/error.js +14 -0
- package/dist/resources/products/error.js.map +1 -0
- package/dist/resources/products/index.d.ts +4 -2
- package/dist/resources/products/index.d.ts.map +1 -1
- package/dist/resources/products/index.js +3 -1
- package/dist/resources/products/index.js.map +1 -1
- package/dist/resources/products/product.type.d.ts +7 -0
- package/dist/resources/products/product.type.d.ts.map +1 -1
- package/dist/resources/products/product.util.d.ts +15 -0
- package/dist/resources/products/product.util.d.ts.map +1 -0
- package/dist/resources/products/product.util.js +56 -0
- package/dist/resources/products/product.util.js.map +1 -0
- package/dist/resources/products/products.d.ts +4 -16
- package/dist/resources/products/products.d.ts.map +1 -1
- package/dist/resources/products/products.js +12 -59
- package/dist/resources/products/products.js.map +1 -1
- package/package.json +9 -1
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# keepa-api
|
|
2
2
|
|
|
3
|
-
Lightweight TypeScript SDK for the [Keepa](https://keepa.com) REST API. Mirrors the organizational style of [openai-node](https://github.com/openai/openai-node) — a single `
|
|
3
|
+
Lightweight TypeScript SDK for the [Keepa](https://keepa.com) REST API. Mirrors the organizational style of [openai-node](https://github.com/openai/openai-node) — a single `KeepaClient` class exposes resources (`products`, etc.) that wrap each endpoint with typed inputs and responses.
|
|
4
4
|
|
|
5
5
|
Phase 1 ships the **Products** resource only. Categories, Search, and Bestsellers are planned but not yet implemented.
|
|
6
6
|
|
|
@@ -49,9 +49,9 @@ npm install /absolute/path/to/keepa-api-0.1.0.tgz
|
|
|
49
49
|
## Quickstart
|
|
50
50
|
|
|
51
51
|
```ts
|
|
52
|
-
import
|
|
52
|
+
import KeepaClient from 'keepa-api';
|
|
53
53
|
|
|
54
|
-
const keepa = new
|
|
54
|
+
const keepa = new KeepaClient({ apiKey: process.env.KEEPA_API_KEY });
|
|
55
55
|
|
|
56
56
|
const products = await keepa.products.list({
|
|
57
57
|
asins: ['B00MNV8E0C'],
|
|
@@ -65,7 +65,7 @@ The client reads `process.env.KEEPA_API_KEY` if you don't pass `apiKey` explicit
|
|
|
65
65
|
|
|
66
66
|
## API
|
|
67
67
|
|
|
68
|
-
### `new
|
|
68
|
+
### `new KeepaClient(options?)`
|
|
69
69
|
|
|
70
70
|
| Option | Type | Default | Notes |
|
|
71
71
|
|--------|------|---------|-------|
|
|
@@ -78,7 +78,7 @@ The client reads `process.env.KEEPA_API_KEY` if you don't pass `apiKey` explicit
|
|
|
78
78
|
| Param | Type | Default | Notes |
|
|
79
79
|
|-------|------|---------|-------|
|
|
80
80
|
| `asins` | `string[]` | (required) | Validated + uppercased. Throws on malformed input. |
|
|
81
|
-
| `marketplace` | `'US' \| '
|
|
81
|
+
| `marketplace` | `'US' \| 'GB' \| 'DE' \| 'FR' \| 'JP' \| 'CA' \| 'IT' \| 'ES' \| 'IN' \| 'MX' \| 'BR'` | `'US'` | Case-insensitive. Throws on unknown. |
|
|
82
82
|
| `days` | `number` | `1` | Days of price history. Must be a positive integer (validated pre-flight). |
|
|
83
83
|
|
|
84
84
|
The SDK maps Keepa's raw wire shape into a friendlier `KeepaProduct`:
|
|
@@ -88,7 +88,7 @@ The SDK maps Keepa's raw wire shape into a friendlier `KeepaProduct`:
|
|
|
88
88
|
|
|
89
89
|
The raw `salesRanks` record is preserved for consumers that need to walk the full rank history. Other Keepa fields (`title`, `parentAsin`, `categoryTree`, `variations`, `features`, …) pass through unchanged.
|
|
90
90
|
|
|
91
|
-
**Stub records:** Keepa returns one record per requested ASIN even when it has no data — these stubs have `title ===
|
|
91
|
+
**Stub records:** Keepa returns one record per requested ASIN even when it has no data — these stubs have `title === null`. Filter with `isFoundProduct` (below).
|
|
92
92
|
|
|
93
93
|
### Helpers
|
|
94
94
|
|
|
@@ -105,7 +105,7 @@ for (const product of real) {
|
|
|
105
105
|
|
|
106
106
|
| Helper | Signature | Returns |
|
|
107
107
|
|--------|-----------|---------|
|
|
108
|
-
| `isFoundProduct(product)` | `(product: KeepaProduct) => boolean` | `true` only if Keepa returned real data (`title
|
|
108
|
+
| `isFoundProduct(product)` | `(product: KeepaProduct) => boolean` | `true` only if Keepa returned real data (stubs have `title === null`). |
|
|
109
109
|
| `parseImagesCsv(csv)` | `(csv: string \| undefined) => string[]` | Build the full image-URL list from a raw Keepa imagesCSV. Used internally to fill `images`; exported for advanced use. |
|
|
110
110
|
| `extractBsr(salesRanks, rootCategory)` | `(salesRanks: Record<string, number[]> \| undefined, rootCategory: number \| undefined) => number \| null` | Most recent real BSR from Keepa's raw `[ts, rank, ...]` history. Used internally to fill `bsr`; exported for advanced use. |
|
|
111
111
|
| `extractImageUrl(imagesCSV)` | `(imagesCSV: string \| undefined) => string \| null` | Single URL — equivalent to `parseImagesCsv(imagesCSV)[0] ?? null`. Exported for advanced use. |
|
|
@@ -133,17 +133,17 @@ normalizeAsins(['B07XYZ']); // throws: Invalid ASIN(s): B07XYZ. ...
|
|
|
133
133
|
import { MARKETPLACE_DOMAINS, resolveDomainId } from 'keepa-api';
|
|
134
134
|
|
|
135
135
|
MARKETPLACE_DOMAINS.US; // 1
|
|
136
|
-
resolveDomainId('
|
|
136
|
+
resolveDomainId('gb'); // 2 (case-insensitive)
|
|
137
137
|
resolveDomainId(undefined); // 1 (defaults to US)
|
|
138
138
|
```
|
|
139
139
|
|
|
140
140
|
| Code | Domain ID | Code | Domain ID |
|
|
141
141
|
|------|-----------|------|-----------|
|
|
142
142
|
| US | 1 | IT | 8 |
|
|
143
|
-
|
|
|
143
|
+
| GB | 2 | ES | 9 |
|
|
144
144
|
| DE | 3 | IN | 10 |
|
|
145
145
|
| FR | 4 | MX | 11 |
|
|
146
|
-
| JP | 5 |
|
|
146
|
+
| JP | 5 | BR | 12 |
|
|
147
147
|
| CA | 6 | | |
|
|
148
148
|
|
|
149
149
|
### Errors
|
package/dist/client.d.ts
CHANGED
|
@@ -8,7 +8,7 @@ export interface ClientOptions {
|
|
|
8
8
|
/** Custom fetch implementation. Defaults to `globalThis.fetch`. */
|
|
9
9
|
fetch?: typeof globalThis.fetch;
|
|
10
10
|
}
|
|
11
|
-
export declare class
|
|
11
|
+
export declare class KeepaClient {
|
|
12
12
|
readonly apiKey: string;
|
|
13
13
|
readonly baseURL: string;
|
|
14
14
|
readonly fetch: typeof globalThis.fetch;
|
|
@@ -17,5 +17,5 @@ export declare class Keepa {
|
|
|
17
17
|
/** Internal: used by APIResource subclasses to perform a request. */
|
|
18
18
|
_request<T>(args: RequestArgs): Promise<T>;
|
|
19
19
|
}
|
|
20
|
-
export default
|
|
20
|
+
export default KeepaClient;
|
|
21
21
|
//# sourceMappingURL=client.d.ts.map
|
package/dist/client.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,WAAW,EAAiB,MAAM,mBAAmB,CAAC;AACpE,OAAO,EAAE,QAAQ,EAAE,MAAM,kCAAkC,CAAC;AAE5D,MAAM,WAAW,aAAa;IAC5B,gEAAgE;IAChE,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,uEAAuE;IACvE,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,mEAAmE;IACnE,KAAK,CAAC,EAAE,OAAO,UAAU,CAAC,KAAK,CAAC;CACjC;AAID,qBAAa,
|
|
1
|
+
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,WAAW,EAAiB,MAAM,mBAAmB,CAAC;AACpE,OAAO,EAAE,QAAQ,EAAE,MAAM,kCAAkC,CAAC;AAE5D,MAAM,WAAW,aAAa;IAC5B,gEAAgE;IAChE,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,uEAAuE;IACvE,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,mEAAmE;IACnE,KAAK,CAAC,EAAE,OAAO,UAAU,CAAC,KAAK,CAAC;CACjC;AAID,qBAAa,WAAW;IACtB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,KAAK,EAAE,OAAO,UAAU,CAAC,KAAK,CAAC;IAExC,QAAQ,CAAC,QAAQ,EAAE,QAAQ,CAAC;gBAEhB,OAAO,GAAE,aAAkB;IAuBvC,qEAAqE;IACrE,QAAQ,CAAC,CAAC,EAAE,IAAI,EAAE,WAAW,GAAG,OAAO,CAAC,CAAC,CAAC;CAQ3C;AAED,eAAe,WAAW,CAAC"}
|
package/dist/client.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { request } from './core/request.js';
|
|
2
2
|
import { Products } from './resources/products/products.js';
|
|
3
3
|
const DEFAULT_BASE_URL = 'https://api.keepa.com';
|
|
4
|
-
export class
|
|
4
|
+
export class KeepaClient {
|
|
5
5
|
apiKey;
|
|
6
6
|
baseURL;
|
|
7
7
|
fetch;
|
|
@@ -13,7 +13,7 @@ export class Keepa {
|
|
|
13
13
|
const envApiKey = typeof process !== 'undefined' ? process.env?.KEEPA_API_KEY : undefined;
|
|
14
14
|
const apiKey = options.apiKey || envApiKey;
|
|
15
15
|
if (!apiKey) {
|
|
16
|
-
throw new Error('Missing Keepa API key. Pass it as `new
|
|
16
|
+
throw new Error('Missing Keepa API key. Pass it as `new KeepaClient({ apiKey })` or set KEEPA_API_KEY in your environment.');
|
|
17
17
|
}
|
|
18
18
|
this.apiKey = apiKey;
|
|
19
19
|
// Strip a trailing slash so `${baseURL}${path}` never produces `//product`.
|
|
@@ -34,5 +34,5 @@ export class Keepa {
|
|
|
34
34
|
return request(config, args);
|
|
35
35
|
}
|
|
36
36
|
}
|
|
37
|
-
export default
|
|
37
|
+
export default KeepaClient;
|
|
38
38
|
//# sourceMappingURL=client.js.map
|
package/dist/client.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"client.js","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,mBAAmB,CAAC;AAE5C,OAAO,EAAE,QAAQ,EAAE,MAAM,kCAAkC,CAAC;AAW5D,MAAM,gBAAgB,GAAG,uBAAuB,CAAC;AAEjD,MAAM,OAAO,
|
|
1
|
+
{"version":3,"file":"client.js","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,mBAAmB,CAAC;AAE5C,OAAO,EAAE,QAAQ,EAAE,MAAM,kCAAkC,CAAC;AAW5D,MAAM,gBAAgB,GAAG,uBAAuB,CAAC;AAEjD,MAAM,OAAO,WAAW;IACb,MAAM,CAAS;IACf,OAAO,CAAS;IAChB,KAAK,CAA0B;IAE/B,QAAQ,CAAW;IAE5B,YAAY,UAAyB,EAAE;QACrC,qFAAqF;QACrF,sFAAsF;QACtF,uCAAuC;QACvC,MAAM,SAAS,GACb,OAAO,OAAO,KAAK,WAAW,CAAC,CAAC,CAAC,OAAO,CAAC,GAAG,EAAE,aAAa,CAAC,CAAC,CAAC,SAAS,CAAC;QAC1E,MAAM,MAAM,GAAG,OAAO,CAAC,MAAM,IAAI,SAAS,CAAC;QAC3C,IAAI,CAAC,MAAM,EAAE,CAAC;YACZ,MAAM,IAAI,KAAK,CACb,2GAA2G,CAC5G,CAAC;QACJ,CAAC;QACD,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;QACrB,4EAA4E;QAC5E,IAAI,CAAC,OAAO,GAAG,CAAC,OAAO,CAAC,OAAO,IAAI,gBAAgB,CAAC,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC;QACzE,sEAAsE;QACtE,0EAA0E;QAC1E,sEAAsE;QACtE,IAAI,CAAC,KAAK,GAAG,OAAO,CAAC,KAAK,IAAI,UAAU,CAAC,KAAK,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;QAEhE,IAAI,CAAC,QAAQ,GAAG,IAAI,QAAQ,CAAC,IAAI,CAAC,CAAC;IACrC,CAAC;IAED,qEAAqE;IACrE,QAAQ,CAAI,IAAiB;QAC3B,MAAM,MAAM,GAAkB;YAC5B,MAAM,EAAE,IAAI,CAAC,MAAM;YACnB,OAAO,EAAE,IAAI,CAAC,OAAO;YACrB,KAAK,EAAE,IAAI,CAAC,KAAK;SAClB,CAAC;QACF,OAAO,OAAO,CAAI,MAAM,EAAE,IAAI,CAAC,CAAC;IAClC,CAAC;CACF;AAED,eAAe,WAAW,CAAC"}
|
package/dist/core/resource.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { KeepaClient } from '../client.js';
|
|
2
2
|
export declare abstract class APIResource {
|
|
3
|
-
protected _client:
|
|
4
|
-
constructor(client:
|
|
3
|
+
protected _client: KeepaClient;
|
|
4
|
+
constructor(client: KeepaClient);
|
|
5
5
|
}
|
|
6
6
|
//# sourceMappingURL=resource.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"resource.d.ts","sourceRoot":"","sources":["../../src/core/resource.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,
|
|
1
|
+
{"version":3,"file":"resource.d.ts","sourceRoot":"","sources":["../../src/core/resource.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAEhD,8BAAsB,WAAW;IAC/B,SAAS,CAAC,OAAO,EAAE,WAAW,CAAC;gBAEnB,MAAM,EAAE,WAAW;CAGhC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"resource.js","sourceRoot":"","sources":["../../src/core/resource.ts"],"names":[],"mappings":"AAEA,MAAM,OAAgB,WAAW;IACrB,OAAO,
|
|
1
|
+
{"version":3,"file":"resource.js","sourceRoot":"","sources":["../../src/core/resource.ts"],"names":[],"mappings":"AAEA,MAAM,OAAgB,WAAW;IACrB,OAAO,CAAc;IAE/B,YAAY,MAAmB;QAC7B,IAAI,CAAC,OAAO,GAAG,MAAM,CAAC;IACxB,CAAC;CACF"}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export {
|
|
1
|
+
export { KeepaClient as default, KeepaClient, type ClientOptions } from './client.js';
|
|
2
2
|
export { KeepaError, APIError, RateLimitError, AuthenticationError, NetworkError, } from './core/error.js';
|
|
3
3
|
export { APIResource } from './core/resource.js';
|
|
4
4
|
export { MARKETPLACE_DOMAINS, resolveDomainId, type Marketplace, type DomainId, } from './lib/marketplace.js';
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,IAAI,OAAO,EAAE,WAAW,EAAE,KAAK,aAAa,EAAE,MAAM,aAAa,CAAC;AAEtF,OAAO,EACL,UAAU,EACV,QAAQ,EACR,cAAc,EACd,mBAAmB,EACnB,YAAY,GACb,MAAM,iBAAiB,CAAC;AAEzB,OAAO,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AAEjD,OAAO,EACL,mBAAmB,EACnB,eAAe,EACf,KAAK,WAAW,EAChB,KAAK,QAAQ,GACd,MAAM,sBAAsB,CAAC;AAE9B,OAAO,EACL,WAAW,EACX,UAAU,EACV,WAAW,EACX,cAAc,GACf,MAAM,eAAe,CAAC;AAEvB,cAAc,sBAAsB,CAAC;AAErC,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export {
|
|
1
|
+
export { KeepaClient as default, KeepaClient } from './client.js';
|
|
2
2
|
export { KeepaError, APIError, RateLimitError, AuthenticationError, NetworkError, } from './core/error.js';
|
|
3
3
|
export { APIResource } from './core/resource.js';
|
|
4
4
|
export { MARKETPLACE_DOMAINS, resolveDomainId, } from './lib/marketplace.js';
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,IAAI,OAAO,EAAE,WAAW,EAAsB,MAAM,aAAa,CAAC;AAEtF,OAAO,EACL,UAAU,EACV,QAAQ,EACR,cAAc,EACd,mBAAmB,EACnB,YAAY,GACb,MAAM,iBAAiB,CAAC;AAEzB,OAAO,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AAEjD,OAAO,EACL,mBAAmB,EACnB,eAAe,GAGhB,MAAM,sBAAsB,CAAC;AAE9B,OAAO,EACL,WAAW,EACX,UAAU,EACV,WAAW,EACX,cAAc,GACf,MAAM,eAAe,CAAC;AAEvB,cAAc,sBAAsB,CAAC;AAErC,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC"}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
export declare const MARKETPLACE_DOMAINS: {
|
|
2
2
|
readonly US: 1;
|
|
3
|
-
readonly
|
|
3
|
+
readonly GB: 2;
|
|
4
4
|
readonly DE: 3;
|
|
5
5
|
readonly FR: 4;
|
|
6
6
|
readonly JP: 5;
|
|
@@ -9,7 +9,7 @@ export declare const MARKETPLACE_DOMAINS: {
|
|
|
9
9
|
readonly ES: 9;
|
|
10
10
|
readonly IN: 10;
|
|
11
11
|
readonly MX: 11;
|
|
12
|
-
readonly
|
|
12
|
+
readonly BR: 12;
|
|
13
13
|
};
|
|
14
14
|
export type Marketplace = keyof typeof MARKETPLACE_DOMAINS;
|
|
15
15
|
export type DomainId = (typeof MARKETPLACE_DOMAINS)[Marketplace];
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"marketplace.d.ts","sourceRoot":"","sources":["../../src/lib/marketplace.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"marketplace.d.ts","sourceRoot":"","sources":["../../src/lib/marketplace.ts"],"names":[],"mappings":"AAIA,eAAO,MAAM,mBAAmB;;;;;;;;;;;;CAYtB,CAAC;AAEX,MAAM,MAAM,WAAW,GAAG,MAAM,OAAO,mBAAmB,CAAC;AAC3D,MAAM,MAAM,QAAQ,GAAG,CAAC,OAAO,mBAAmB,CAAC,CAAC,WAAW,CAAC,CAAC;AAIjE,wBAAgB,eAAe,CAAC,WAAW,EAAE,MAAM,GAAG,SAAS,GAAG,QAAQ,CASzE"}
|
package/dist/lib/marketplace.js
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
|
-
// The
|
|
2
|
-
//
|
|
3
|
-
//
|
|
1
|
+
// The full set of marketplaces Keepa currently supports. Codes are ISO 3166-1
|
|
2
|
+
// alpha-2; the numeric id is Keepa's `domain` query parameter value. Domain 7
|
|
3
|
+
// is reserved (formerly Amazon China, retired). Update the README marketplace
|
|
4
|
+
// table alongside any change here.
|
|
4
5
|
export const MARKETPLACE_DOMAINS = {
|
|
5
6
|
US: 1,
|
|
6
|
-
|
|
7
|
+
GB: 2,
|
|
7
8
|
DE: 3,
|
|
8
9
|
FR: 4,
|
|
9
10
|
JP: 5,
|
|
@@ -12,7 +13,7 @@ export const MARKETPLACE_DOMAINS = {
|
|
|
12
13
|
ES: 9,
|
|
13
14
|
IN: 10,
|
|
14
15
|
MX: 11,
|
|
15
|
-
|
|
16
|
+
BR: 12,
|
|
16
17
|
};
|
|
17
18
|
const SUPPORTED_LIST = Object.keys(MARKETPLACE_DOMAINS).join(', ');
|
|
18
19
|
export function resolveDomainId(marketplace) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"marketplace.js","sourceRoot":"","sources":["../../src/lib/marketplace.ts"],"names":[],"mappings":"AAAA,
|
|
1
|
+
{"version":3,"file":"marketplace.js","sourceRoot":"","sources":["../../src/lib/marketplace.ts"],"names":[],"mappings":"AAAA,8EAA8E;AAC9E,8EAA8E;AAC9E,8EAA8E;AAC9E,mCAAmC;AACnC,MAAM,CAAC,MAAM,mBAAmB,GAAG;IACjC,EAAE,EAAE,CAAC;IACL,EAAE,EAAE,CAAC;IACL,EAAE,EAAE,CAAC;IACL,EAAE,EAAE,CAAC;IACL,EAAE,EAAE,CAAC;IACL,EAAE,EAAE,CAAC;IACL,EAAE,EAAE,CAAC;IACL,EAAE,EAAE,CAAC;IACL,EAAE,EAAE,EAAE;IACN,EAAE,EAAE,EAAE;IACN,EAAE,EAAE,EAAE;CACE,CAAC;AAKX,MAAM,cAAc,GAAG,MAAM,CAAC,IAAI,CAAC,mBAAmB,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AAEnE,MAAM,UAAU,eAAe,CAAC,WAA+B;IAC7D,MAAM,GAAG,GAAG,CAAC,WAAW,IAAI,IAAI,CAAC,CAAC,WAAW,EAAiB,CAAC;IAC/D,MAAM,QAAQ,GAAG,mBAAmB,CAAC,GAAG,CAAC,CAAC;IAC1C,IAAI,CAAC,QAAQ,EAAE,CAAC;QACd,MAAM,IAAI,KAAK,CACb,wBAAwB,WAAW,iBAAiB,cAAc,EAAE,CACrE,CAAC;IACJ,CAAC;IACD,OAAO,QAAQ,CAAC;AAClB,CAAC"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { KeepaError } from '../../core/error.js';
|
|
2
|
+
/** Thrown by `Products.retrieve` when Keepa has no record for the requested ASIN
|
|
3
|
+
* (either no product object returned, or a stub with no title). The `asin`
|
|
4
|
+
* property carries the value the caller passed in, unchanged, so it can be
|
|
5
|
+
* surfaced in user-facing messages without re-deriving it. */
|
|
6
|
+
export declare class ProductNotFoundError extends KeepaError {
|
|
7
|
+
readonly asin: string;
|
|
8
|
+
constructor(asin: string);
|
|
9
|
+
}
|
|
10
|
+
//# sourceMappingURL=error.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"error.d.ts","sourceRoot":"","sources":["../../../src/resources/products/error.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,qBAAqB,CAAC;AAEjD;;;+DAG+D;AAC/D,qBAAa,oBAAqB,SAAQ,UAAU;IAClD,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;gBAEV,IAAI,EAAE,MAAM;CAKzB"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { KeepaError } from '../../core/error.js';
|
|
2
|
+
/** Thrown by `Products.retrieve` when Keepa has no record for the requested ASIN
|
|
3
|
+
* (either no product object returned, or a stub with no title). The `asin`
|
|
4
|
+
* property carries the value the caller passed in, unchanged, so it can be
|
|
5
|
+
* surfaced in user-facing messages without re-deriving it. */
|
|
6
|
+
export class ProductNotFoundError extends KeepaError {
|
|
7
|
+
asin;
|
|
8
|
+
constructor(asin) {
|
|
9
|
+
super(`Keepa: no product found for ASIN ${asin}`);
|
|
10
|
+
this.name = 'ProductNotFoundError';
|
|
11
|
+
this.asin = asin;
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
//# sourceMappingURL=error.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"error.js","sourceRoot":"","sources":["../../../src/resources/products/error.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,qBAAqB,CAAC;AAEjD;;;+DAG+D;AAC/D,MAAM,OAAO,oBAAqB,SAAQ,UAAU;IACzC,IAAI,CAAS;IAEtB,YAAY,IAAY;QACtB,KAAK,CAAC,oCAAoC,IAAI,EAAE,CAAC,CAAC;QAClD,IAAI,CAAC,IAAI,GAAG,sBAAsB,CAAC;QACnC,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;IACnB,CAAC;CACF"}
|
|
@@ -1,4 +1,6 @@
|
|
|
1
|
-
export { Products
|
|
2
|
-
export
|
|
1
|
+
export { Products } from './products.js';
|
|
2
|
+
export { extractBsr, isFoundProduct } from './product.util.js';
|
|
3
|
+
export { ProductNotFoundError } from './error.js';
|
|
4
|
+
export type { ProductListParams, ProductRetrieveParams, KeepaProduct, KeepaVariation, KeepaVariationAttribute, KeepaCategoryNode, } from './product.type.js';
|
|
3
5
|
export { PRODUCT_PATH, PRODUCT_LIST_CONTEXT, DEFAULT_DAYS, AMAZON_IMAGE_BASE, KEEPA_NO_DATA_SENTINEL, } from './constant.js';
|
|
4
6
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/resources/products/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,UAAU,EAAE,cAAc,EAAE,MAAM,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/resources/products/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AACzC,OAAO,EAAE,UAAU,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AAC/D,OAAO,EAAE,oBAAoB,EAAE,MAAM,YAAY,CAAC;AAElD,YAAY,EACV,iBAAiB,EACjB,qBAAqB,EACrB,YAAY,EACZ,cAAc,EACd,uBAAuB,EACvB,iBAAiB,GAClB,MAAM,mBAAmB,CAAC;AAM3B,OAAO,EACL,YAAY,EACZ,oBAAoB,EACpB,YAAY,EACZ,iBAAiB,EACjB,sBAAsB,GACvB,MAAM,eAAe,CAAC"}
|
|
@@ -1,4 +1,6 @@
|
|
|
1
|
-
export { Products
|
|
1
|
+
export { Products } from './products.js';
|
|
2
|
+
export { extractBsr, isFoundProduct } from './product.util.js';
|
|
3
|
+
export { ProductNotFoundError } from './error.js';
|
|
2
4
|
// NOTE: raw Keepa wire types (KeepaProductRaw, KeepaProductResponseRaw) are
|
|
3
5
|
// intentionally NOT re-exported. They are an implementation detail — Products.list
|
|
4
6
|
// maps them to the consumer-friendly KeepaProduct shape before returning.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/resources/products/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,UAAU,EAAE,cAAc,EAAE,MAAM,
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/resources/products/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AACzC,OAAO,EAAE,UAAU,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AAC/D,OAAO,EAAE,oBAAoB,EAAE,MAAM,YAAY,CAAC;AAWlD,4EAA4E;AAC5E,mFAAmF;AACnF,0EAA0E;AAE1E,OAAO,EACL,YAAY,EACZ,oBAAoB,EACpB,YAAY,EACZ,iBAAiB,EACjB,sBAAsB,GACvB,MAAM,eAAe,CAAC"}
|
|
@@ -6,6 +6,13 @@ export interface ProductListParams {
|
|
|
6
6
|
/** Days of price history to include. Defaults to 1. */
|
|
7
7
|
days?: number;
|
|
8
8
|
}
|
|
9
|
+
export interface ProductRetrieveParams {
|
|
10
|
+
asin: string;
|
|
11
|
+
/** Marketplace code (case-insensitive at runtime). Defaults to 'US'. */
|
|
12
|
+
marketplace?: Marketplace;
|
|
13
|
+
/** Days of price history to include. Defaults to 1. */
|
|
14
|
+
days?: number;
|
|
15
|
+
}
|
|
9
16
|
export interface KeepaCategoryNode {
|
|
10
17
|
catId: number;
|
|
11
18
|
name: string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"product.type.d.ts","sourceRoot":"","sources":["../../../src/resources/products/product.type.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,0BAA0B,CAAC;AAE5D,MAAM,WAAW,iBAAiB;IAChC,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB,wEAAwE;IACxE,WAAW,CAAC,EAAE,WAAW,CAAC;IAC1B,uDAAuD;IACvD,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,iBAAiB;IAChC,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,uBAAuB;IACtC,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,CAAC,EAAE,uBAAuB,EAAE,CAAC;CACxC;AAED;;;+DAG+D;AAC/D,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,YAAY,CAAC,EAAE,iBAAiB,EAAE,CAAC;IACnC,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,CAAC;IACtC,UAAU,CAAC,EAAE,cAAc,EAAE,CAAC;IAC9B,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAC;IAEpB,gFAAgF;IAChF,MAAM,EAAE,MAAM,EAAE,CAAC;IAEjB;oDACgD;IAChD,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;CACpB"}
|
|
1
|
+
{"version":3,"file":"product.type.d.ts","sourceRoot":"","sources":["../../../src/resources/products/product.type.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,0BAA0B,CAAC;AAE5D,MAAM,WAAW,iBAAiB;IAChC,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB,wEAAwE;IACxE,WAAW,CAAC,EAAE,WAAW,CAAC;IAC1B,uDAAuD;IACvD,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,qBAAqB;IACpC,IAAI,EAAE,MAAM,CAAC;IACb,wEAAwE;IACxE,WAAW,CAAC,EAAE,WAAW,CAAC;IAC1B,uDAAuD;IACvD,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,iBAAiB;IAChC,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,uBAAuB;IACtC,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,CAAC,EAAE,uBAAuB,EAAE,CAAC;CACxC;AAED;;;+DAG+D;AAC/D,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,YAAY,CAAC,EAAE,iBAAiB,EAAE,CAAC;IACnC,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,CAAC;IACtC,UAAU,CAAC,EAAE,cAAc,EAAE,CAAC;IAC9B,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAC;IAEpB,gFAAgF;IAChF,MAAM,EAAE,MAAM,EAAE,CAAC;IAEjB;oDACgD;IAChD,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;CACpB"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { KeepaProduct } from './product.type.js';
|
|
2
|
+
import type { KeepaProductRaw } from './product.raw.type.js';
|
|
3
|
+
/** Map Keepa's raw wire shape into the consumer-friendly KeepaProduct. */
|
|
4
|
+
export declare function toKeepaProduct(raw: KeepaProductRaw): KeepaProduct;
|
|
5
|
+
/** Returns true when Keepa returned an actual product record (not just a stub
|
|
6
|
+
* for an unknown ASIN). Stubs come back with `title: null`; real listings
|
|
7
|
+
* always have a string. Use as a `.filter()` predicate to drop empty matches. */
|
|
8
|
+
export declare function isFoundProduct(product: KeepaProduct): boolean;
|
|
9
|
+
/** Extract the most recent real BSR from Keepa's `[ts, rank, ts, rank, ...]` salesRanks array.
|
|
10
|
+
* Walks backward through rank entries (odd indices) and skips Keepa's `-1` sentinel which
|
|
11
|
+
* marks "no data captured at that timestamp". Returns `null` if every entry is sentinel.
|
|
12
|
+
* Most consumers won't need this — `Products.list` already fills `bsr` on every returned
|
|
13
|
+
* product. Useful when working with a raw Keepa response. */
|
|
14
|
+
export declare function extractBsr(salesRanks: Record<string, number[]> | undefined, rootCategory: number | undefined): number | null;
|
|
15
|
+
//# sourceMappingURL=product.util.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"product.util.d.ts","sourceRoot":"","sources":["../../../src/resources/products/product.util.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AACtD,OAAO,KAAK,EAAiB,eAAe,EAAE,MAAM,uBAAuB,CAAC;AAE5E,0EAA0E;AAC1E,wBAAgB,cAAc,CAAC,GAAG,EAAE,eAAe,GAAG,YAAY,CAcjE;AAYD;;kFAEkF;AAClF,wBAAgB,cAAc,CAAC,OAAO,EAAE,YAAY,GAAG,OAAO,CAE7D;AAED;;;;8DAI8D;AAC9D,wBAAgB,UAAU,CACxB,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,GAAG,SAAS,EAChD,YAAY,EAAE,MAAM,GAAG,SAAS,GAC/B,MAAM,GAAG,IAAI,CAaf"}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { AMAZON_IMAGE_BASE, KEEPA_NO_DATA_SENTINEL, VALID_IMAGE_FILENAME, } from './constant.js';
|
|
2
|
+
/** Map Keepa's raw wire shape into the consumer-friendly KeepaProduct. */
|
|
3
|
+
export function toKeepaProduct(raw) {
|
|
4
|
+
return {
|
|
5
|
+
asin: raw.asin,
|
|
6
|
+
title: raw.title,
|
|
7
|
+
description: raw.description,
|
|
8
|
+
parentAsin: raw.parentAsin,
|
|
9
|
+
categoryTree: raw.categoryTree,
|
|
10
|
+
rootCategory: raw.rootCategory,
|
|
11
|
+
salesRanks: raw.salesRanks,
|
|
12
|
+
variations: raw.variations,
|
|
13
|
+
features: raw.features,
|
|
14
|
+
images: rawImagesToUrls(raw.images),
|
|
15
|
+
bsr: extractBsr(raw.salesRanks, raw.rootCategory),
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
/** Convert Keepa's raw images array into full Amazon image URLs (the large
|
|
19
|
+
* variant). Filters entries whose filename doesn't match the expected
|
|
20
|
+
* alphanumeric image-name pattern as defense-in-depth. */
|
|
21
|
+
function rawImagesToUrls(images) {
|
|
22
|
+
if (!images || images.length === 0)
|
|
23
|
+
return [];
|
|
24
|
+
return images
|
|
25
|
+
.filter((img) => typeof img.l === 'string' && VALID_IMAGE_FILENAME.test(img.l))
|
|
26
|
+
.map((img) => `${AMAZON_IMAGE_BASE}/${img.l}`);
|
|
27
|
+
}
|
|
28
|
+
/** Returns true when Keepa returned an actual product record (not just a stub
|
|
29
|
+
* for an unknown ASIN). Stubs come back with `title: null`; real listings
|
|
30
|
+
* always have a string. Use as a `.filter()` predicate to drop empty matches. */
|
|
31
|
+
export function isFoundProduct(product) {
|
|
32
|
+
return product.title != null;
|
|
33
|
+
}
|
|
34
|
+
/** Extract the most recent real BSR from Keepa's `[ts, rank, ts, rank, ...]` salesRanks array.
|
|
35
|
+
* Walks backward through rank entries (odd indices) and skips Keepa's `-1` sentinel which
|
|
36
|
+
* marks "no data captured at that timestamp". Returns `null` if every entry is sentinel.
|
|
37
|
+
* Most consumers won't need this — `Products.list` already fills `bsr` on every returned
|
|
38
|
+
* product. Useful when working with a raw Keepa response. */
|
|
39
|
+
export function extractBsr(salesRanks, rootCategory) {
|
|
40
|
+
if (!salesRanks || rootCategory === undefined)
|
|
41
|
+
return null;
|
|
42
|
+
const ranks = salesRanks[String(rootCategory)];
|
|
43
|
+
if (!ranks || ranks.length < 2)
|
|
44
|
+
return null;
|
|
45
|
+
// Keepa pairs are [ts, rank, ts, rank, ...]. If length is even, the last index
|
|
46
|
+
// is a rank; if odd (truncated/schema drift), it's a dangling timestamp — skip
|
|
47
|
+
// back one slot so we always start on a rank.
|
|
48
|
+
const start = ranks.length % 2 === 0 ? ranks.length - 1 : ranks.length - 2;
|
|
49
|
+
for (let i = start; i >= 1; i -= 2) {
|
|
50
|
+
const rank = ranks[i];
|
|
51
|
+
if (rank !== undefined && rank !== KEEPA_NO_DATA_SENTINEL)
|
|
52
|
+
return rank;
|
|
53
|
+
}
|
|
54
|
+
return null;
|
|
55
|
+
}
|
|
56
|
+
//# sourceMappingURL=product.util.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"product.util.js","sourceRoot":"","sources":["../../../src/resources/products/product.util.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,iBAAiB,EACjB,sBAAsB,EACtB,oBAAoB,GACrB,MAAM,eAAe,CAAC;AAIvB,0EAA0E;AAC1E,MAAM,UAAU,cAAc,CAAC,GAAoB;IACjD,OAAO;QACL,IAAI,EAAE,GAAG,CAAC,IAAI;QACd,KAAK,EAAE,GAAG,CAAC,KAAK;QAChB,WAAW,EAAE,GAAG,CAAC,WAAW;QAC5B,UAAU,EAAE,GAAG,CAAC,UAAU;QAC1B,YAAY,EAAE,GAAG,CAAC,YAAY;QAC9B,YAAY,EAAE,GAAG,CAAC,YAAY;QAC9B,UAAU,EAAE,GAAG,CAAC,UAAU;QAC1B,UAAU,EAAE,GAAG,CAAC,UAAU;QAC1B,QAAQ,EAAE,GAAG,CAAC,QAAQ;QACtB,MAAM,EAAE,eAAe,CAAC,GAAG,CAAC,MAAM,CAAC;QACnC,GAAG,EAAE,UAAU,CAAC,GAAG,CAAC,UAAU,EAAE,GAAG,CAAC,YAAY,CAAC;KAClD,CAAC;AACJ,CAAC;AAED;;2DAE2D;AAC3D,SAAS,eAAe,CAAC,MAAmC;IAC1D,IAAI,CAAC,MAAM,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,EAAE,CAAC;IAC9C,OAAO,MAAM;SACV,MAAM,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,OAAO,GAAG,CAAC,CAAC,KAAK,QAAQ,IAAI,oBAAoB,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;SAC9E,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,iBAAiB,IAAI,GAAG,CAAC,CAAC,EAAE,CAAC,CAAC;AACnD,CAAC;AAED;;kFAEkF;AAClF,MAAM,UAAU,cAAc,CAAC,OAAqB;IAClD,OAAO,OAAO,CAAC,KAAK,IAAI,IAAI,CAAC;AAC/B,CAAC;AAED;;;;8DAI8D;AAC9D,MAAM,UAAU,UAAU,CACxB,UAAgD,EAChD,YAAgC;IAEhC,IAAI,CAAC,UAAU,IAAI,YAAY,KAAK,SAAS;QAAE,OAAO,IAAI,CAAC;IAC3D,MAAM,KAAK,GAAG,UAAU,CAAC,MAAM,CAAC,YAAY,CAAC,CAAC,CAAC;IAC/C,IAAI,CAAC,KAAK,IAAI,KAAK,CAAC,MAAM,GAAG,CAAC;QAAE,OAAO,IAAI,CAAC;IAC5C,+EAA+E;IAC/E,+EAA+E;IAC/E,8CAA8C;IAC9C,MAAM,KAAK,GAAG,KAAK,CAAC,MAAM,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC;IAC3E,KAAK,IAAI,CAAC,GAAG,KAAK,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC;QACnC,MAAM,IAAI,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC;QACtB,IAAI,IAAI,KAAK,SAAS,IAAI,IAAI,KAAK,sBAAsB;YAAE,OAAO,IAAI,CAAC;IACzE,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC"}
|
|
@@ -1,21 +1,9 @@
|
|
|
1
1
|
import { APIResource } from '../../core/resource.js';
|
|
2
|
-
import type { KeepaProduct, ProductListParams } from './product.type.js';
|
|
2
|
+
import type { KeepaProduct, ProductListParams, ProductRetrieveParams } from './product.type.js';
|
|
3
3
|
export declare class Products extends APIResource {
|
|
4
4
|
list(params: ProductListParams): Promise<KeepaProduct[]>;
|
|
5
|
+
/** Fetch a single product by ASIN. Throws `ProductNotFoundError` when Keepa
|
|
6
|
+
* has no record for the ASIN (no product returned, or a stub with no title). */
|
|
7
|
+
retrieve(params: ProductRetrieveParams): Promise<KeepaProduct>;
|
|
5
8
|
}
|
|
6
|
-
/** Returns true when Keepa returned an actual product record (not just a stub
|
|
7
|
-
* for an unknown ASIN). Use as a `.filter()` predicate to drop empty matches.
|
|
8
|
-
*
|
|
9
|
-
* We use `title.length > 0` because it's the strongest single correlate of
|
|
10
|
-
* "Keepa has data" — stubs for unknown ASINs come back without a title, and
|
|
11
|
-
* every real listing has one. If Keepa's stub format changes (e.g. they start
|
|
12
|
-
* returning a placeholder title), tighten this predicate to also require
|
|
13
|
-
* `categoryTree` or another non-string field. */
|
|
14
|
-
export declare function isFoundProduct(product: KeepaProduct): boolean;
|
|
15
|
-
/** Extract the most recent real BSR from Keepa's `[ts, rank, ts, rank, ...]` salesRanks array.
|
|
16
|
-
* Walks backward through rank entries (odd indices) and skips Keepa's `-1` sentinel which
|
|
17
|
-
* marks "no data captured at that timestamp". Returns `null` if every entry is sentinel.
|
|
18
|
-
* Most consumers won't need this — `Products.list` already fills `bsr` on every returned
|
|
19
|
-
* product. Useful when working with a raw Keepa response. */
|
|
20
|
-
export declare function extractBsr(salesRanks: Record<string, number[]> | undefined, rootCategory: number | undefined): number | null;
|
|
21
9
|
//# sourceMappingURL=products.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"products.d.ts","sourceRoot":"","sources":["../../../src/resources/products/products.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,wBAAwB,CAAC;
|
|
1
|
+
{"version":3,"file":"products.d.ts","sourceRoot":"","sources":["../../../src/resources/products/products.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,wBAAwB,CAAC;AAQrD,OAAO,KAAK,EACV,YAAY,EACZ,iBAAiB,EACjB,qBAAqB,EACtB,MAAM,mBAAmB,CAAC;AAK3B,qBAAa,QAAS,SAAQ,WAAW;IACjC,IAAI,CAAC,MAAM,EAAE,iBAAiB,GAAG,OAAO,CAAC,YAAY,EAAE,CAAC;IAwB9D;qFACiF;IAC3E,QAAQ,CAAC,MAAM,EAAE,qBAAqB,GAAG,OAAO,CAAC,YAAY,CAAC;CAQrE"}
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import { APIResource } from '../../core/resource.js';
|
|
2
2
|
import { resolveDomainId } from '../../lib/marketplace.js';
|
|
3
3
|
import { normalizeAsins } from '../../lib/asin.js';
|
|
4
|
-
import { PRODUCT_PATH, PRODUCT_LIST_CONTEXT, DEFAULT_DAYS,
|
|
4
|
+
import { PRODUCT_PATH, PRODUCT_LIST_CONTEXT, DEFAULT_DAYS, } from './constant.js';
|
|
5
|
+
import { ProductNotFoundError } from './error.js';
|
|
6
|
+
import { toKeepaProduct, isFoundProduct } from './product.util.js';
|
|
5
7
|
export class Products extends APIResource {
|
|
6
8
|
async list(params) {
|
|
7
9
|
if (params.asins.length === 0) {
|
|
@@ -24,64 +26,15 @@ export class Products extends APIResource {
|
|
|
24
26
|
});
|
|
25
27
|
return (data.products ?? []).map(toKeepaProduct);
|
|
26
28
|
}
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
rootCategory: raw.rootCategory,
|
|
37
|
-
salesRanks: raw.salesRanks,
|
|
38
|
-
variations: raw.variations,
|
|
39
|
-
features: raw.features,
|
|
40
|
-
images: rawImagesToUrls(raw.images),
|
|
41
|
-
bsr: extractBsr(raw.salesRanks, raw.rootCategory),
|
|
42
|
-
};
|
|
43
|
-
}
|
|
44
|
-
/** Convert Keepa's raw images array into full Amazon image URLs (the large
|
|
45
|
-
* variant). Filters entries whose filename doesn't match the expected
|
|
46
|
-
* alphanumeric image-name pattern as defense-in-depth. */
|
|
47
|
-
function rawImagesToUrls(images) {
|
|
48
|
-
if (!images || images.length === 0)
|
|
49
|
-
return [];
|
|
50
|
-
return images
|
|
51
|
-
.filter((img) => typeof img.l === 'string' && VALID_IMAGE_FILENAME.test(img.l))
|
|
52
|
-
.map((img) => `${AMAZON_IMAGE_BASE}/${img.l}`);
|
|
53
|
-
}
|
|
54
|
-
/** Returns true when Keepa returned an actual product record (not just a stub
|
|
55
|
-
* for an unknown ASIN). Use as a `.filter()` predicate to drop empty matches.
|
|
56
|
-
*
|
|
57
|
-
* We use `title.length > 0` because it's the strongest single correlate of
|
|
58
|
-
* "Keepa has data" — stubs for unknown ASINs come back without a title, and
|
|
59
|
-
* every real listing has one. If Keepa's stub format changes (e.g. they start
|
|
60
|
-
* returning a placeholder title), tighten this predicate to also require
|
|
61
|
-
* `categoryTree` or another non-string field. */
|
|
62
|
-
export function isFoundProduct(product) {
|
|
63
|
-
return typeof product.title === 'string' && product.title.length > 0;
|
|
64
|
-
}
|
|
65
|
-
/** Extract the most recent real BSR from Keepa's `[ts, rank, ts, rank, ...]` salesRanks array.
|
|
66
|
-
* Walks backward through rank entries (odd indices) and skips Keepa's `-1` sentinel which
|
|
67
|
-
* marks "no data captured at that timestamp". Returns `null` if every entry is sentinel.
|
|
68
|
-
* Most consumers won't need this — `Products.list` already fills `bsr` on every returned
|
|
69
|
-
* product. Useful when working with a raw Keepa response. */
|
|
70
|
-
export function extractBsr(salesRanks, rootCategory) {
|
|
71
|
-
if (!salesRanks || rootCategory === undefined)
|
|
72
|
-
return null;
|
|
73
|
-
const ranks = salesRanks[String(rootCategory)];
|
|
74
|
-
if (!ranks || ranks.length < 2)
|
|
75
|
-
return null;
|
|
76
|
-
// Keepa pairs are [ts, rank, ts, rank, ...]. If length is even, the last index
|
|
77
|
-
// is a rank; if odd (truncated/schema drift), it's a dangling timestamp — skip
|
|
78
|
-
// back one slot so we always start on a rank.
|
|
79
|
-
const start = ranks.length % 2 === 0 ? ranks.length - 1 : ranks.length - 2;
|
|
80
|
-
for (let i = start; i >= 1; i -= 2) {
|
|
81
|
-
const rank = ranks[i];
|
|
82
|
-
if (rank !== undefined && rank !== KEEPA_NO_DATA_SENTINEL)
|
|
83
|
-
return rank;
|
|
29
|
+
/** Fetch a single product by ASIN. Throws `ProductNotFoundError` when Keepa
|
|
30
|
+
* has no record for the ASIN (no product returned, or a stub with no title). */
|
|
31
|
+
async retrieve(params) {
|
|
32
|
+
const { asin, ...rest } = params;
|
|
33
|
+
const [product] = await this.list({ ...rest, asins: [asin] });
|
|
34
|
+
if (!product || !isFoundProduct(product)) {
|
|
35
|
+
throw new ProductNotFoundError(asin);
|
|
36
|
+
}
|
|
37
|
+
return product;
|
|
84
38
|
}
|
|
85
|
-
return null;
|
|
86
39
|
}
|
|
87
40
|
//# sourceMappingURL=products.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"products.js","sourceRoot":"","sources":["../../../src/resources/products/products.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,wBAAwB,CAAC;AACrD,OAAO,EAAE,eAAe,EAAE,MAAM,0BAA0B,CAAC;AAC3D,OAAO,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AACnD,OAAO,EACL,YAAY,EACZ,oBAAoB,EACpB,YAAY,
|
|
1
|
+
{"version":3,"file":"products.js","sourceRoot":"","sources":["../../../src/resources/products/products.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,wBAAwB,CAAC;AACrD,OAAO,EAAE,eAAe,EAAE,MAAM,0BAA0B,CAAC;AAC3D,OAAO,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AACnD,OAAO,EACL,YAAY,EACZ,oBAAoB,EACpB,YAAY,GACb,MAAM,eAAe,CAAC;AAOvB,OAAO,EAAE,oBAAoB,EAAE,MAAM,YAAY,CAAC;AAClD,OAAO,EAAE,cAAc,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AAEnE,MAAM,OAAO,QAAS,SAAQ,WAAW;IACvC,KAAK,CAAC,IAAI,CAAC,MAAyB;QAClC,IAAI,MAAM,CAAC,KAAK,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YAC9B,MAAM,IAAI,KAAK,CAAC,+BAA+B,CAAC,CAAC;QACnD,CAAC;QACD,IACE,MAAM,CAAC,IAAI,KAAK,SAAS;YACzB,CAAC,CAAC,MAAM,CAAC,SAAS,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,MAAM,CAAC,IAAI,GAAG,CAAC,CAAC,EACnD,CAAC;YACD,MAAM,IAAI,KAAK,CAAC,iBAAiB,MAAM,CAAC,IAAI,+BAA+B,CAAC,CAAC;QAC/E,CAAC;QACD,MAAM,KAAK,GAAG,cAAc,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;QAC3C,MAAM,MAAM,GAAG,eAAe,CAAC,MAAM,CAAC,WAAW,CAAC,CAAC;QACnD,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,OAAO,CAAC,QAAQ,CAA0B;YAChE,IAAI,EAAE,YAAY;YAClB,KAAK,EAAE;gBACL,MAAM;gBACN,IAAI,EAAE,KAAK;gBACX,IAAI,EAAE,MAAM,CAAC,IAAI,IAAI,YAAY;aAClC;YACD,OAAO,EAAE,oBAAoB;SAC9B,CAAC,CAAC;QACH,OAAO,CAAC,IAAI,CAAC,QAAQ,IAAI,EAAE,CAAC,CAAC,GAAG,CAAC,cAAc,CAAC,CAAC;IACnD,CAAC;IAED;qFACiF;IACjF,KAAK,CAAC,QAAQ,CAAC,MAA6B;QAC1C,MAAM,EAAE,IAAI,EAAE,GAAG,IAAI,EAAE,GAAG,MAAM,CAAC;QACjC,MAAM,CAAC,OAAO,CAAC,GAAG,MAAM,IAAI,CAAC,IAAI,CAAC,EAAE,GAAG,IAAI,EAAE,KAAK,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QAC9D,IAAI,CAAC,OAAO,IAAI,CAAC,cAAc,CAAC,OAAO,CAAC,EAAE,CAAC;YACzC,MAAM,IAAI,oBAAoB,CAAC,IAAI,CAAC,CAAC;QACvC,CAAC;QACD,OAAO,OAAO,CAAC;IACjB,CAAC;CACF"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "keepa-api",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.3",
|
|
4
4
|
"description": "TypeScript SDK for the Keepa API.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -35,6 +35,14 @@
|
|
|
35
35
|
"asin"
|
|
36
36
|
],
|
|
37
37
|
"license": "MIT",
|
|
38
|
+
"repository": {
|
|
39
|
+
"type": "git",
|
|
40
|
+
"url": "git+https://github.com/bilalyasin1616/keepa-api.git"
|
|
41
|
+
},
|
|
42
|
+
"bugs": {
|
|
43
|
+
"url": "https://github.com/bilalyasin1616/keepa-api/issues"
|
|
44
|
+
},
|
|
45
|
+
"homepage": "https://github.com/bilalyasin1616/keepa-api#readme",
|
|
38
46
|
"devDependencies": {
|
|
39
47
|
"@types/node": "^22.10.0",
|
|
40
48
|
"dotenv": "^16.4.0",
|