amaprice 1.0.7 → 1.0.8
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 +2 -0
- package/package.json +1 -1
- package/src/commands/history.js +6 -2
- package/src/commands/price.js +1 -1
- package/src/commands/tier.js +6 -3
- package/src/commands/track.js +1 -1
- package/src/url.js +66 -1
package/README.md
CHANGED
|
@@ -49,6 +49,8 @@ amaprice list
|
|
|
49
49
|
- piped stdin (`echo "<url-or-asin>" | amaprice price`)
|
|
50
50
|
- interactive prompt (run command without argument)
|
|
51
51
|
|
|
52
|
+
Short links from Amazon apps (for example `amzn.eu`, `amzn.to`, `a.co`) are accepted and resolved automatically.
|
|
53
|
+
|
|
52
54
|
## Commands
|
|
53
55
|
|
|
54
56
|
| Command | Description |
|
package/package.json
CHANGED
package/src/commands/history.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
const { extractAsin } = require('../url');
|
|
1
|
+
const { extractAsin, normalizeAmazonInput } = require('../url');
|
|
2
2
|
const { getPriceHistory } = require('../db');
|
|
3
3
|
const { formatPrice } = require('../format');
|
|
4
4
|
|
|
@@ -9,7 +9,11 @@ module.exports = function (program) {
|
|
|
9
9
|
.option('--limit <n>', 'Number of entries to show', '30')
|
|
10
10
|
.option('--json', 'Output as JSON')
|
|
11
11
|
.action(async (urlOrAsin, opts) => {
|
|
12
|
-
|
|
12
|
+
let asin = extractAsin(urlOrAsin);
|
|
13
|
+
if (!asin) {
|
|
14
|
+
const normalized = await normalizeAmazonInput(urlOrAsin);
|
|
15
|
+
asin = normalized?.asin ?? null;
|
|
16
|
+
}
|
|
13
17
|
if (!asin) {
|
|
14
18
|
console.error('Error: Could not extract ASIN from input.');
|
|
15
19
|
process.exit(1);
|
package/src/commands/price.js
CHANGED
|
@@ -11,7 +11,7 @@ module.exports = function (program) {
|
|
|
11
11
|
.option('--json', 'Output as JSON')
|
|
12
12
|
.action(async (inputParts, opts) => {
|
|
13
13
|
const input = await resolveCliInput(inputParts);
|
|
14
|
-
const normalized = normalizeAmazonInput(input);
|
|
14
|
+
const normalized = await normalizeAmazonInput(input);
|
|
15
15
|
if (!normalized) {
|
|
16
16
|
console.error('Error: Input must be an Amazon product URL or a valid ASIN.');
|
|
17
17
|
process.exit(1);
|
package/src/commands/tier.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
const { extractAsin } = require('../url');
|
|
1
|
+
const { extractAsin, normalizeAmazonInput } = require('../url');
|
|
2
2
|
const { getProductByAsin, updateProductByAsin } = require('../db');
|
|
3
3
|
const { normalizeTier, computeNextScrapeAt } = require('../tiering');
|
|
4
4
|
|
|
@@ -12,7 +12,11 @@ module.exports = function (program) {
|
|
|
12
12
|
.option('--deactivate', 'Disable background sync for this product')
|
|
13
13
|
.option('--json', 'Output as JSON')
|
|
14
14
|
.action(async (urlOrAsin, tierArg, opts) => {
|
|
15
|
-
|
|
15
|
+
let asin = extractAsin(urlOrAsin);
|
|
16
|
+
if (!asin) {
|
|
17
|
+
const normalized = await normalizeAmazonInput(urlOrAsin);
|
|
18
|
+
asin = normalized?.asin ?? null;
|
|
19
|
+
}
|
|
16
20
|
if (!asin) {
|
|
17
21
|
console.error('Error: Could not extract ASIN from input.');
|
|
18
22
|
process.exit(1);
|
|
@@ -72,4 +76,3 @@ module.exports = function (program) {
|
|
|
72
76
|
}
|
|
73
77
|
});
|
|
74
78
|
};
|
|
75
|
-
|
package/src/commands/track.js
CHANGED
|
@@ -15,7 +15,7 @@ module.exports = function (program) {
|
|
|
15
15
|
.option('--json', 'Output as JSON')
|
|
16
16
|
.action(async (inputParts, opts) => {
|
|
17
17
|
const input = await resolveCliInput(inputParts);
|
|
18
|
-
const normalized = normalizeAmazonInput(input);
|
|
18
|
+
const normalized = await normalizeAmazonInput(input);
|
|
19
19
|
if (!normalized) {
|
|
20
20
|
console.error('Error: Input must be an Amazon product URL or a valid ASIN.');
|
|
21
21
|
process.exit(1);
|
package/src/url.js
CHANGED
|
@@ -3,6 +3,7 @@ const AMAZON_DOMAINS = [
|
|
|
3
3
|
'amazon.it', 'amazon.es', 'amazon.nl', 'amazon.co.jp',
|
|
4
4
|
'amazon.ca', 'amazon.com.au', 'amazon.in', 'amazon.com.br',
|
|
5
5
|
];
|
|
6
|
+
const AMAZON_SHORT_DOMAINS = ['amzn.eu', 'amzn.to', 'a.co'];
|
|
6
7
|
|
|
7
8
|
const ASIN_REGEX = /(?:\/(?:dp|gp\/product|ASIN)\/)([A-Z0-9]{10})/i;
|
|
8
9
|
|
|
@@ -15,6 +16,16 @@ function isAmazonUrl(url) {
|
|
|
15
16
|
}
|
|
16
17
|
}
|
|
17
18
|
|
|
19
|
+
function isAmazonShortUrl(url) {
|
|
20
|
+
try {
|
|
21
|
+
const parsed = new URL(url);
|
|
22
|
+
const hostname = parsed.hostname.replace(/^www\./, '');
|
|
23
|
+
return AMAZON_SHORT_DOMAINS.includes(hostname);
|
|
24
|
+
} catch {
|
|
25
|
+
return false;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
18
29
|
function extractAsin(urlOrAsin) {
|
|
19
30
|
// If it's already a bare ASIN (10 alphanumeric chars)
|
|
20
31
|
if (/^[A-Z0-9]{10}$/i.test(urlOrAsin)) {
|
|
@@ -37,7 +48,47 @@ function canonicalUrl(asin, domain = 'amazon.de') {
|
|
|
37
48
|
return `https://www.${domain}/dp/${asin}`;
|
|
38
49
|
}
|
|
39
50
|
|
|
40
|
-
function
|
|
51
|
+
async function resolveAmazonShortUrl(url, maxRedirects = 8) {
|
|
52
|
+
if (!isAmazonShortUrl(url)) return url;
|
|
53
|
+
|
|
54
|
+
let current = url;
|
|
55
|
+
for (let i = 0; i < maxRedirects; i += 1) {
|
|
56
|
+
let response;
|
|
57
|
+
try {
|
|
58
|
+
response = await fetch(current, {
|
|
59
|
+
method: 'GET',
|
|
60
|
+
redirect: 'manual',
|
|
61
|
+
});
|
|
62
|
+
} catch {
|
|
63
|
+
return current;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
const location = response.headers?.get ? response.headers.get('location') : null;
|
|
67
|
+
try {
|
|
68
|
+
await response.body?.cancel?.();
|
|
69
|
+
} catch {
|
|
70
|
+
// Ignore body cancellation failures.
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
if (!location || response.status < 300 || response.status > 399) {
|
|
74
|
+
return current;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
try {
|
|
78
|
+
current = new URL(location, current).toString();
|
|
79
|
+
} catch {
|
|
80
|
+
return current;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
if (isAmazonUrl(current) && extractAsin(current)) {
|
|
84
|
+
return current;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
return current;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
async function normalizeAmazonInput(input, defaultDomain = 'amazon.de') {
|
|
41
92
|
const raw = String(input || '').trim();
|
|
42
93
|
if (!raw) return null;
|
|
43
94
|
|
|
@@ -51,14 +102,28 @@ function normalizeAmazonInput(input, defaultDomain = 'amazon.de') {
|
|
|
51
102
|
};
|
|
52
103
|
}
|
|
53
104
|
|
|
105
|
+
const resolved = await resolveAmazonShortUrl(raw);
|
|
106
|
+
const resolvedAsin = extractAsin(resolved);
|
|
107
|
+
if (resolvedAsin) {
|
|
108
|
+
const domain = isAmazonUrl(resolved) ? extractDomain(resolved) : defaultDomain;
|
|
109
|
+
return {
|
|
110
|
+
asin: resolvedAsin,
|
|
111
|
+
domain,
|
|
112
|
+
url: canonicalUrl(resolvedAsin, domain),
|
|
113
|
+
};
|
|
114
|
+
}
|
|
115
|
+
|
|
54
116
|
return null;
|
|
55
117
|
}
|
|
56
118
|
|
|
57
119
|
module.exports = {
|
|
58
120
|
isAmazonUrl,
|
|
121
|
+
isAmazonShortUrl,
|
|
59
122
|
extractAsin,
|
|
60
123
|
extractDomain,
|
|
61
124
|
canonicalUrl,
|
|
125
|
+
resolveAmazonShortUrl,
|
|
62
126
|
normalizeAmazonInput,
|
|
63
127
|
AMAZON_DOMAINS,
|
|
128
|
+
AMAZON_SHORT_DOMAINS,
|
|
64
129
|
};
|