amaprice 1.0.3 → 1.0.5
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 +12 -3
- package/bin/cli.js +10 -0
- package/package.json +1 -1
- package/src/commands/list.js +1 -1
- package/src/commands/price.js +10 -7
- package/src/commands/track.js +9 -6
- package/src/input.js +35 -0
- package/src/url.js +25 -1
package/README.md
CHANGED
|
@@ -12,13 +12,21 @@ npm install -g amaprice
|
|
|
12
12
|
|
|
13
13
|
```bash
|
|
14
14
|
# One-shot price lookup
|
|
15
|
+
amaprice "https://www.amazon.de/dp/B0DZ5P7JD6"
|
|
16
|
+
amaprice B0DZ5P7JD6
|
|
15
17
|
amaprice price "https://www.amazon.de/dp/B0DZ5P7JD6"
|
|
18
|
+
amaprice price B0DZ5P7JD6
|
|
19
|
+
amaprice price
|
|
20
|
+
# then paste full Amazon URL or ASIN when prompted
|
|
16
21
|
|
|
17
22
|
# JSON output (for scripts / AI agents)
|
|
18
23
|
amaprice price "https://www.amazon.de/dp/B0DZ5P7JD6" --json
|
|
19
24
|
|
|
20
25
|
# Track a product's price over time
|
|
21
26
|
amaprice track "https://www.amazon.de/dp/B0DZ5P7JD6"
|
|
27
|
+
amaprice track B0DZ5P7JD6
|
|
28
|
+
amaprice track
|
|
29
|
+
# then paste full Amazon URL or ASIN when prompted
|
|
22
30
|
|
|
23
31
|
# View price history
|
|
24
32
|
amaprice history B0DZ5P7JD6
|
|
@@ -31,14 +39,15 @@ amaprice list
|
|
|
31
39
|
|
|
32
40
|
| Command | Description |
|
|
33
41
|
|---|---|
|
|
34
|
-
| `amaprice
|
|
35
|
-
| `amaprice
|
|
42
|
+
| `amaprice [url\|asin]` | Shortcut for `amaprice price [url\|asin]` |
|
|
43
|
+
| `amaprice price [url\|asin]` | One-shot price lookup (or prompt if omitted) |
|
|
44
|
+
| `amaprice track [url\|asin]` | Track a product's price (or prompt if omitted) |
|
|
36
45
|
| `amaprice history <url\|asin>` | Show price history (`--limit N`, default 30) |
|
|
37
46
|
| `amaprice list` | Show all tracked products with latest price |
|
|
38
47
|
|
|
39
48
|
All commands support `--json` for machine-readable output.
|
|
40
49
|
|
|
41
|
-
|
|
50
|
+
If your URL contains query parameters (`?` / `&`), either wrap it in quotes or run the command without an argument and paste the full URL into the prompt.
|
|
42
51
|
|
|
43
52
|
## Community Price Database
|
|
44
53
|
|
package/bin/cli.js
CHANGED
|
@@ -2,6 +2,16 @@
|
|
|
2
2
|
|
|
3
3
|
const { program } = require('commander');
|
|
4
4
|
const pkg = require('../package.json');
|
|
5
|
+
const KNOWN_COMMANDS = new Set(['price', 'track', 'history', 'list', 'help']);
|
|
6
|
+
|
|
7
|
+
const userArgs = process.argv.slice(2);
|
|
8
|
+
if (userArgs.length > 0) {
|
|
9
|
+
const firstArg = userArgs[0];
|
|
10
|
+
// Convenience mode: treat `amaprice <url-or-asin>` as `amaprice price <url-or-asin>`.
|
|
11
|
+
if (!firstArg.startsWith('-') && !KNOWN_COMMANDS.has(firstArg)) {
|
|
12
|
+
process.argv.splice(2, 0, 'price');
|
|
13
|
+
}
|
|
14
|
+
}
|
|
5
15
|
|
|
6
16
|
program
|
|
7
17
|
.name('amaprice')
|
package/package.json
CHANGED
package/src/commands/list.js
CHANGED
|
@@ -22,7 +22,7 @@ module.exports = function (program) {
|
|
|
22
22
|
}))));
|
|
23
23
|
} else {
|
|
24
24
|
if (products.length === 0) {
|
|
25
|
-
console.log('No tracked products. Use `amaprice track <url>` to start tracking.');
|
|
25
|
+
console.log('No tracked products. Use `amaprice track <url-or-asin>` to start tracking.');
|
|
26
26
|
return;
|
|
27
27
|
}
|
|
28
28
|
|
package/src/commands/price.js
CHANGED
|
@@ -1,20 +1,23 @@
|
|
|
1
|
-
const {
|
|
1
|
+
const { normalizeAmazonInput } = require('../url');
|
|
2
|
+
const { resolveCliInput } = require('../input');
|
|
2
3
|
const { scrapePrice } = require('../scraper');
|
|
3
4
|
const { upsertProduct, insertPrice } = require('../db');
|
|
4
5
|
|
|
5
6
|
module.exports = function (program) {
|
|
6
7
|
program
|
|
7
|
-
.command('price
|
|
8
|
-
.description('One-shot price lookup for an Amazon product')
|
|
8
|
+
.command('price [input...]')
|
|
9
|
+
.description('One-shot price lookup for an Amazon product URL or ASIN')
|
|
9
10
|
.option('--json', 'Output as JSON')
|
|
10
|
-
.action(async (
|
|
11
|
-
|
|
12
|
-
|
|
11
|
+
.action(async (inputParts, opts) => {
|
|
12
|
+
const input = await resolveCliInput(inputParts);
|
|
13
|
+
const normalized = normalizeAmazonInput(input);
|
|
14
|
+
if (!normalized) {
|
|
15
|
+
console.error('Error: Input must be an Amazon product URL or a valid ASIN.');
|
|
13
16
|
process.exit(1);
|
|
14
17
|
}
|
|
15
18
|
|
|
16
19
|
try {
|
|
17
|
-
const result = await scrapePrice(url);
|
|
20
|
+
const result = await scrapePrice(normalized.url);
|
|
18
21
|
|
|
19
22
|
if (opts.json) {
|
|
20
23
|
console.log(JSON.stringify({
|
package/src/commands/track.js
CHANGED
|
@@ -1,20 +1,23 @@
|
|
|
1
|
-
const {
|
|
1
|
+
const { normalizeAmazonInput } = require('../url');
|
|
2
|
+
const { resolveCliInput } = require('../input');
|
|
2
3
|
const { scrapePrice } = require('../scraper');
|
|
3
4
|
const { upsertProduct, insertPrice } = require('../db');
|
|
4
5
|
|
|
5
6
|
module.exports = function (program) {
|
|
6
7
|
program
|
|
7
|
-
.command('track
|
|
8
|
+
.command('track [input...]')
|
|
8
9
|
.description('Save product + current price to Supabase')
|
|
9
10
|
.option('--json', 'Output as JSON')
|
|
10
|
-
.action(async (
|
|
11
|
-
|
|
12
|
-
|
|
11
|
+
.action(async (inputParts, opts) => {
|
|
12
|
+
const input = await resolveCliInput(inputParts);
|
|
13
|
+
const normalized = normalizeAmazonInput(input);
|
|
14
|
+
if (!normalized) {
|
|
15
|
+
console.error('Error: Input must be an Amazon product URL or a valid ASIN.');
|
|
13
16
|
process.exit(1);
|
|
14
17
|
}
|
|
15
18
|
|
|
16
19
|
try {
|
|
17
|
-
const result = await scrapePrice(url);
|
|
20
|
+
const result = await scrapePrice(normalized.url);
|
|
18
21
|
|
|
19
22
|
if (!result.price) {
|
|
20
23
|
console.error('Error: Could not extract price from the page.');
|
package/src/input.js
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
const readline = require('node:readline');
|
|
2
|
+
|
|
3
|
+
async function readStdin() {
|
|
4
|
+
let data = '';
|
|
5
|
+
for await (const chunk of process.stdin) {
|
|
6
|
+
data += chunk;
|
|
7
|
+
}
|
|
8
|
+
return data.trim();
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
function ask(promptText) {
|
|
12
|
+
return new Promise((resolve) => {
|
|
13
|
+
const rl = readline.createInterface({
|
|
14
|
+
input: process.stdin,
|
|
15
|
+
output: process.stdout,
|
|
16
|
+
});
|
|
17
|
+
rl.question(promptText, (answer) => {
|
|
18
|
+
rl.close();
|
|
19
|
+
resolve(String(answer || '').trim());
|
|
20
|
+
});
|
|
21
|
+
});
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
async function resolveCliInput(inputParts, promptText = 'Amazon URL or ASIN: ') {
|
|
25
|
+
const joined = Array.isArray(inputParts) ? inputParts.join(' ').trim() : String(inputParts || '').trim();
|
|
26
|
+
if (joined) return joined;
|
|
27
|
+
|
|
28
|
+
if (!process.stdin.isTTY) {
|
|
29
|
+
return readStdin();
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
return ask(promptText);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
module.exports = { resolveCliInput };
|
package/src/url.js
CHANGED
|
@@ -37,4 +37,28 @@ function canonicalUrl(asin, domain = 'amazon.de') {
|
|
|
37
37
|
return `https://www.${domain}/dp/${asin}`;
|
|
38
38
|
}
|
|
39
39
|
|
|
40
|
-
|
|
40
|
+
function normalizeAmazonInput(input, defaultDomain = 'amazon.de') {
|
|
41
|
+
const raw = String(input || '').trim();
|
|
42
|
+
if (!raw) return null;
|
|
43
|
+
|
|
44
|
+
const asin = extractAsin(raw);
|
|
45
|
+
if (asin) {
|
|
46
|
+
const domain = isAmazonUrl(raw) ? extractDomain(raw) : defaultDomain;
|
|
47
|
+
return {
|
|
48
|
+
asin,
|
|
49
|
+
domain,
|
|
50
|
+
url: canonicalUrl(asin, domain),
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
return null;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
module.exports = {
|
|
58
|
+
isAmazonUrl,
|
|
59
|
+
extractAsin,
|
|
60
|
+
extractDomain,
|
|
61
|
+
canonicalUrl,
|
|
62
|
+
normalizeAmazonInput,
|
|
63
|
+
AMAZON_DOMAINS,
|
|
64
|
+
};
|