ubuyfirst 0.1.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/LICENSE ADDED
@@ -0,0 +1,15 @@
1
+ Copyright (c) 2026 Harvest Mobile. All rights reserved.
2
+
3
+ You may install and run this software to access the uBuyFirst service through your
4
+ own uBuyFirst account.
5
+
6
+ You may not copy, modify, distribute, sublicense, sell, or create derivative works
7
+ of this software or any part of it, except where that right cannot be excluded by
8
+ law. No other rights are granted.
9
+
10
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
11
+ INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
12
+ PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE
13
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
14
+ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
15
+ USE OR OTHER DEALINGS IN THE SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,90 @@
1
+ # ubuyfirst
2
+
3
+ Command-line interface for the [uBuyFirst public API](https://app.ubuyfirst.com/api/docs) —
4
+ manage your saved searches, folders, blocklists and notification channels from a terminal or a
5
+ script, and back them up to a file.
6
+
7
+ Requires Node.js 22.12 or newer.
8
+
9
+ ## Install
10
+
11
+ ```bash
12
+ npx ubuyfirst --help # run without installing
13
+ npm install -g ubuyfirst # or install the `ubuyfirst` command
14
+ ```
15
+
16
+ ## Get an API key
17
+
18
+ Generate one on the **Settings → Account** page of your uBuyFirst account. The key is shown
19
+ once, so copy it before leaving the page. There is one key per account; regenerating it
20
+ immediately invalidates the old one.
21
+
22
+ Store it — the CLI reads the key from stdin so it never lands in your shell history:
23
+
24
+ ```bash
25
+ ubuyfirst config set-key # paste the key, then press Enter
26
+ ubuyfirst config show # config path, whether a key is stored, effective base URL
27
+ ```
28
+
29
+ Or pass it per-invocation with the `UBUYFIRST_API_KEY` environment variable, which takes
30
+ precedence over the stored key. `ubuyfirst config clear` removes what is stored.
31
+
32
+ ## Commands
33
+
34
+ ```
35
+ searches list, create, update, pause, resume, delete, export, import
36
+ folders list, create, rename, delete, move
37
+ blocklist list, add, remove (sellers, countries, items)
38
+ notifications list, toggle, settings
39
+ filters export, import
40
+ config set-key, show, clear
41
+ ```
42
+
43
+ Run `ubuyfirst <group> --help` for the flags of any group.
44
+
45
+ ```bash
46
+ ubuyfirst searches list --limit 20
47
+ ubuyfirst searches create --name "Leica M6" --keywords "leica m6" --price-max 2500
48
+ ubuyfirst searches pause abc123
49
+ ubuyfirst blocklist add --type sellers badseller99 anotherseller
50
+ ubuyfirst searches export > backup.json
51
+ ubuyfirst searches import backup.json --preview
52
+ ```
53
+
54
+ Bulk imports accept `--preview` to report what *would* change without writing anything.
55
+
56
+ ## Scripting
57
+
58
+ `--json` makes any command — including a failure — emit exactly one parseable JSON document
59
+ on stdout:
60
+
61
+ ```bash
62
+ ubuyfirst --json searches list | jq '.searches[].name'
63
+ ```
64
+
65
+ Exit codes are stable, so a script can branch on the failure without parsing text:
66
+
67
+ | Code | Meaning | Code | Meaning |
68
+ |---|---|---|---|
69
+ | 0 | success | 6 | ambiguous request |
70
+ | 1 | unexpected failure | 7 | account cap exceeded |
71
+ | 2 | bad or missing flags | 8 | rate limited — back off and retry |
72
+ | 3 | validation error | 9 | write failed |
73
+ | 4 | missing or invalid API key | 10 | server error — retriable |
74
+ | 5 | access denied | 11 | partly applied; see the output |
75
+
76
+ Codes 8 and 10 are the retriable ones. Client-side refusals also carry a `cli.`-prefixed error
77
+ code in `--json` output (`cli.usage`, `cli.no_key`, `cli.unexpected`) so you can tell them from
78
+ a server response.
79
+
80
+ ## Configuration precedence
81
+
82
+ | Setting | Order |
83
+ |---|---|
84
+ | API key | `UBUYFIRST_API_KEY` → stored config |
85
+ | Base URL | `--base-url` → `UBUYFIRST_API_URL` → stored config → `https://app.ubuyfirst.com` |
86
+
87
+ ## Licence
88
+
89
+ Proprietary — see [LICENSE](./LICENSE). You may install and run this software to access the
90
+ uBuyFirst service through your own account.
package/dist/cli.js ADDED
@@ -0,0 +1,63 @@
1
+ #!/usr/bin/env node
2
+ //#region src/node-floor.ts
3
+ /**
4
+ * The CLI's RUNTIME floor is Node >=22.12.0 — Commander 15's own minimum
5
+ * (verified at the npm registry). This is distinct from, and lower than, the
6
+ * BUILD floor (`devEngines.runtime` in package.json, `^22.18.0 || ^24.11.0 ||
7
+ * >=26.0.0` — tsdown's own floor). Never collapse the two into one number.
8
+ *
9
+ * npm's `engines` field only WARNS unless the consumer sets `engine-strict`,
10
+ * so the built entry (`cli.ts`) performs this check itself, before anything
11
+ * else runs, and exits with a message naming both versions.
12
+ */
13
+ const REQUIRED_NODE_VERSION = "22.12.0";
14
+ /**
15
+ * Strips a leading `v` (as in `process.version`) and any pre-release /
16
+ * build-metadata suffix (`-nightly...`, `+build...`), then parses the
17
+ * remaining `major.minor.patch` core. A missing or non-numeric segment
18
+ * parses as 0 rather than throwing.
19
+ */
20
+ function parseVersion(raw) {
21
+ const segments = (raw.replace(/^v/, "").split(/[-+]/)[0] ?? "").split(".");
22
+ const toNumber = (segment) => {
23
+ const value = Number(segment ?? "0");
24
+ return Number.isNaN(value) ? 0 : value;
25
+ };
26
+ return {
27
+ major: toNumber(segments[0]),
28
+ minor: toNumber(segments[1]),
29
+ patch: toNumber(segments[2])
30
+ };
31
+ }
32
+ function compareVersions(a, b) {
33
+ if (a.major !== b.major) return a.major - b.major;
34
+ if (a.minor !== b.minor) return a.minor - b.minor;
35
+ return a.patch - b.patch;
36
+ }
37
+ /**
38
+ * Checks `runningVersion` (defaults to `process.version`) against
39
+ * `REQUIRED_NODE_VERSION`. Pure and injectable so it is testable without
40
+ * switching Node binaries — the caller decides how to report a failure.
41
+ */
42
+ function checkNodeFloor(runningVersion = process.version) {
43
+ if (compareVersions(parseVersion(runningVersion), parseVersion("22.12.0")) < 0) return {
44
+ ok: false,
45
+ message: `ubuyfirst requires Node.js >=${REQUIRED_NODE_VERSION}; the running version is ${runningVersion}.`
46
+ };
47
+ return {
48
+ ok: true,
49
+ message: ""
50
+ };
51
+ }
52
+ //#endregion
53
+ //#region src/cli.ts
54
+ const floorCheck = checkNodeFloor(process.version);
55
+ if (!floorCheck.ok) {
56
+ process.stderr.write(`${floorCheck.message}\n`);
57
+ process.exitCode = 1;
58
+ } else {
59
+ const { runProgram } = await import("./program-YTm6g9Ut.js");
60
+ await runProgram(process.argv.slice(2));
61
+ }
62
+ //#endregion
63
+ export {};