domain-suggester 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 ADDED
@@ -0,0 +1,287 @@
1
+ # domain-suggester
2
+
3
+ Node.js SDK for the DomainHub Domain Suggester API.
4
+
5
+ This package is a thin client for the hosted API. It does **not** contain the private scoring engine or internal domain suggestion logic.
6
+
7
+ ## Installation
8
+
9
+ ```bash
10
+ npm install domain-suggester
11
+ ```
12
+
13
+ ## Requirements
14
+
15
+ - Node.js 18 or newer
16
+ - A valid API key
17
+
18
+ ## Authentication
19
+
20
+ Set your API key as an environment variable:
21
+
22
+ ```bash
23
+ export DOMAIN_SUGGESTER_API_KEY="dsg_live_your_api_key_here"
24
+ ```
25
+
26
+ ## Quick start
27
+
28
+ ```js
29
+ const { createClient } = require("@domainhub/domain-suggester");
30
+
31
+ async function main() {
32
+ const client = createClient({
33
+ apiKey: process.env.DOMAIN_SUGGESTER_API_KEY,
34
+ baseUrl: "https://suggest.domainhub.sbs"
35
+ });
36
+
37
+ const job = await client.createJob({
38
+ domains: [
39
+ "example.com",
40
+ "domain.com"
41
+ ],
42
+ config: {
43
+ prefixes: ["get", "my", "try"],
44
+ tlds: {
45
+ com: 0,
46
+ net: 1,
47
+ io: 2
48
+ }
49
+ },
50
+ options: {
51
+ topN: 25
52
+ }
53
+ });
54
+
55
+ console.log("Submitted job:", job);
56
+
57
+ const result = await client.waitForResult(job.jobId, {
58
+ intervalMs: 3000,
59
+ timeoutMs: 10 * 60 * 1000
60
+ });
61
+
62
+ console.log(JSON.stringify(result, null, 2));
63
+ }
64
+
65
+ main().catch((err) => {
66
+ console.error(err);
67
+ process.exit(1);
68
+ });
69
+ ```
70
+
71
+ ## Creating a client
72
+
73
+ ```js
74
+ const { createClient } = require("@domainhub/domain-suggester");
75
+
76
+ const client = createClient({
77
+ apiKey: process.env.DOMAIN_SUGGESTER_API_KEY,
78
+ baseUrl: "https://suggest.domainhub.sbs"
79
+ });
80
+ ```
81
+
82
+ ### Options
83
+
84
+ - `apiKey` — required, your customer API key
85
+ - `baseUrl` — optional, defaults to your API base URL if you set one in the SDK
86
+
87
+ ## API
88
+
89
+ ### `createClient(options)`
90
+
91
+ Creates a new SDK client.
92
+
93
+ ---
94
+
95
+ ### `client.createJob(payload)`
96
+
97
+ Creates a new suggestion job.
98
+
99
+ #### Example
100
+
101
+ ```js
102
+ const job = await client.createJob({
103
+ domains: ["example.com", "domain.com"],
104
+ config: {
105
+ prefixes: ["get", "my", "try"],
106
+ tlds: { com: 0, net: 1, io: 2 }
107
+ },
108
+ options: {
109
+ topN: 25,
110
+ debug: false,
111
+ ideal: "example"
112
+ }
113
+ });
114
+ ```
115
+
116
+ #### Payload shape
117
+
118
+ ```js
119
+ {
120
+ domains: string[],
121
+ config: {
122
+ prefixes: string[],
123
+ tlds: Record<string, number>
124
+ },
125
+ options: {
126
+ topN?: number,
127
+ debug?: boolean,
128
+ ideal?: string,
129
+ target?: string
130
+ }
131
+ }
132
+ ```
133
+
134
+ ---
135
+
136
+ ### `client.getJob(jobId)`
137
+
138
+ Returns job status.
139
+
140
+ #### Example
141
+
142
+ ```js
143
+ const status = await client.getJob(jobId);
144
+ console.log(status);
145
+ ```
146
+
147
+ ---
148
+
149
+ ### `client.getResult(jobId)`
150
+
151
+ Returns the final job result once completed.
152
+
153
+ #### Example
154
+
155
+ ```js
156
+ const result = await client.getResult(jobId);
157
+ console.log(result);
158
+ ```
159
+
160
+ ---
161
+
162
+ ### `client.listJobs()`
163
+
164
+ Returns the current customer's recent jobs.
165
+
166
+ #### Example
167
+
168
+ ```js
169
+ const jobs = await client.listJobs();
170
+ console.log(jobs);
171
+ ```
172
+
173
+ ---
174
+
175
+ ### `client.me()`
176
+
177
+ Returns current API key profile and usage information.
178
+
179
+ #### Example
180
+
181
+ ```js
182
+ const me = await client.me();
183
+ console.log(me);
184
+ ```
185
+
186
+ ---
187
+
188
+ ### `client.waitForResult(jobId, options)`
189
+
190
+ Polls until the job completes or fails.
191
+
192
+ #### Example
193
+
194
+ ```js
195
+ const result = await client.waitForResult(jobId, {
196
+ intervalMs: 3000,
197
+ timeoutMs: 10 * 60 * 1000
198
+ });
199
+ ```
200
+
201
+ #### Options
202
+
203
+ - `intervalMs` — polling interval in milliseconds
204
+ - `timeoutMs` — max total wait time in milliseconds
205
+
206
+ ## Example response
207
+
208
+ ### Job created
209
+
210
+ ```json
211
+ {
212
+ "ok": true,
213
+ "jobId": "123",
214
+ "status": "queued",
215
+ "pollUrl": "/v1/jobs/123",
216
+ "resultUrl": "/v1/jobs/123/result"
217
+ }
218
+ ```
219
+
220
+ ### Job status
221
+
222
+ ```json
223
+ {
224
+ "ok": true,
225
+ "jobId": "123",
226
+ "name": "suggest",
227
+ "state": "active",
228
+ "progress": 15,
229
+ "submittedAt": "2026-03-26T12:00:00.000Z",
230
+ "finishedOn": null,
231
+ "failedReason": null
232
+ }
233
+ ```
234
+
235
+ ### Job result
236
+
237
+ ```json
238
+ {
239
+ "ok": true,
240
+ "jobId": "123",
241
+ "state": "completed",
242
+ "result": {
243
+ "count": 25,
244
+ "durationMs": 8123,
245
+ "sortedDomains": [
246
+ "getexample.com",
247
+ "myexample.com"
248
+ ],
249
+ "detailedResults": [
250
+ {
251
+ "domain": "getexample.com",
252
+ "score": 12.34
253
+ }
254
+ ]
255
+ }
256
+ }
257
+ ```
258
+
259
+ ## Error handling
260
+
261
+ The SDK throws when the API returns a non-2xx response.
262
+
263
+ ```js
264
+ try {
265
+ await client.createJob(payload);
266
+ } catch (err) {
267
+ console.error(err.message);
268
+ console.error(err.status);
269
+ console.error(err.data);
270
+ }
271
+ ```
272
+
273
+ ## Security
274
+
275
+ - Keep your API key secret
276
+ - Never expose your API key in frontend/browser code
277
+ - Use this SDK only in trusted backend or local Node.js environments
278
+
279
+ ## Notes
280
+
281
+ - This SDK is only a transport client
282
+ - The private suggestion engine runs on the hosted service
283
+ - Request options such as `prefixes`, `tlds`, and `topN` are passed through to the API
284
+
285
+ ## Support
286
+
287
+ For support, contact DomainHub.
package/bin/cli.js ADDED
@@ -0,0 +1,56 @@
1
+ #!/usr/bin/env node
2
+ // bin/cli.js
3
+ const fs = require("fs");
4
+ const { Command } = require("commander");
5
+
6
+ const program = new Command();
7
+
8
+ program
9
+ .name("domain-suggester")
10
+ .description("CLI for the hosted domain suggester API")
11
+ .version("1.0.0");
12
+
13
+ program
14
+ .command("suggest")
15
+ .argument("<file>", "path to txt file with domains")
16
+ .option("--top <number>", "top N results", "25")
17
+ .option("--api-url <url>", "API base URL", "https://suggest.domainhub.sbs")
18
+ .action(async (file, options) => {
19
+ const apiKey = process.env.DOMAIN_SUGGESTER_API_KEY;
20
+
21
+ if (!apiKey) {
22
+ console.error("Missing DOMAIN_SUGGESTER_API_KEY");
23
+ process.exit(1);
24
+ }
25
+
26
+ const raw = fs.readFileSync(file, "utf8");
27
+ const domains = raw
28
+ .split(/\r?\n/)
29
+ .map(x => x.trim())
30
+ .filter(Boolean);
31
+
32
+ const res = await fetch(`${options.apiUrl}/v1/jobs`, {
33
+ method: "POST",
34
+ headers: {
35
+ "content-type": "application/json",
36
+ "authorization": `Bearer ${apiKey}`
37
+ },
38
+ body: JSON.stringify({
39
+ domains,
40
+ config: {
41
+ prefixes: ["get", "my", "try"],
42
+ tlds: { com: 0, net: 1, io: 2 }
43
+ },
44
+ options: {
45
+ topN: Number(options.top)
46
+ }
47
+ })
48
+ });
49
+
50
+ const text = await res.text();
51
+ console.log(text);
52
+
53
+ if (!res.ok) process.exit(1);
54
+ });
55
+
56
+ program.parse();
package/index.js ADDED
@@ -0,0 +1,2 @@
1
+ // index.js
2
+ module.exports = require("./lib/client");
package/lib/client.js ADDED
@@ -0,0 +1,120 @@
1
+ // lib/client.js
2
+ const {
3
+ AuthenticationError,
4
+ RateLimitError,
5
+ ValidationError,
6
+ NotFoundError,
7
+ ApiError
8
+ } = require("./errors");
9
+
10
+ class DomainSuggesterClient {
11
+ constructor({ apiKey, baseUrl = "https://suggest.domainhub.sbs" }) {
12
+ if (!apiKey) {
13
+ throw new Error("Missing apiKey");
14
+ }
15
+
16
+ if (typeof baseUrl !== "string" || !baseUrl) {
17
+ throw new Error("Missing or invalid baseUrl");
18
+ }
19
+ this.apiKey = apiKey;
20
+ this.baseUrl = baseUrl.replace(/\/+$/, "");
21
+ }
22
+
23
+ async request(path, options = {}) {
24
+ const res = await fetch(`${this.baseUrl}${path}`, {
25
+ ...options,
26
+ headers: {
27
+ authorization: `Bearer ${this.apiKey}`,
28
+ "content-type": "application/json",
29
+ ...(options.headers || {})
30
+ }
31
+ });
32
+
33
+ const text = await res.text();
34
+ let data;
35
+
36
+ try {
37
+ data = text ? JSON.parse(text) : null;
38
+ } catch {
39
+ data = { raw: text };
40
+ }
41
+
42
+ if (!res.ok) {
43
+ const message = data?.error || `Request failed with ${res.status}`;
44
+ const options = { status: res.status, data };
45
+
46
+ if (res.status === 400) throw new ValidationError(message, options);
47
+ if (res.status === 401) throw new AuthenticationError(message, options);
48
+ if (res.status === 404) throw new NotFoundError(message, options);
49
+ if (res.status === 429) throw new RateLimitError(message, options);
50
+
51
+ throw new ApiError(message, options);
52
+ }
53
+
54
+ return data;
55
+ }
56
+
57
+ async createJob(payload) {
58
+ return this.request("/v1/jobs", {
59
+ method: "POST",
60
+ body: JSON.stringify(payload)
61
+ });
62
+ }
63
+
64
+ async getJob(jobId) {
65
+ return this.request(`/v1/jobs/${jobId}`, {
66
+ method: "GET"
67
+ });
68
+ }
69
+
70
+ async getResult(jobId) {
71
+ return this.request(`/v1/jobs/${jobId}/result`, {
72
+ method: "GET"
73
+ });
74
+ }
75
+
76
+ async listJobs() {
77
+ return this.request("/v1/jobs", {
78
+ method: "GET"
79
+ });
80
+ }
81
+
82
+ async me() {
83
+ return this.request("/v1/me", {
84
+ method: "GET"
85
+ });
86
+ }
87
+
88
+ async waitForResult(jobId, { intervalMs = 3000, timeoutMs = 10 * 60 * 1000 } = {}) {
89
+ const started = Date.now();
90
+
91
+ while (true) {
92
+ const job = await this.getJob(jobId);
93
+
94
+ if (job.state === "completed") {
95
+ return this.getResult(jobId);
96
+ }
97
+
98
+ if (job.state === "failed") {
99
+ const err = new Error(job.failedReason || "Job failed");
100
+ err.job = job;
101
+ throw err;
102
+ }
103
+
104
+ if (Date.now() - started > timeoutMs) {
105
+ throw new Error("Timed out waiting for job result");
106
+ }
107
+
108
+ await new Promise((resolve) => setTimeout(resolve, intervalMs));
109
+ }
110
+ }
111
+ }
112
+
113
+ function createClient(options) {
114
+ return new DomainSuggesterClient(options);
115
+ }
116
+
117
+ module.exports = {
118
+ createClient,
119
+ DomainSuggesterClient
120
+ };
package/lib/errors.js ADDED
@@ -0,0 +1,54 @@
1
+ // lib/errors.js
2
+ class DomainSuggesterError extends Error {
3
+ constructor(message, options = {}) {
4
+ super(message);
5
+ this.name = "DomainSuggesterError";
6
+ this.status = options.status || null;
7
+ this.code = options.code || null;
8
+ this.data = options.data || null;
9
+ }
10
+ }
11
+
12
+ class AuthenticationError extends DomainSuggesterError {
13
+ constructor(message = "Unauthorized", options = {}) {
14
+ super(message, { ...options, status: options.status || 401 });
15
+ this.name = "AuthenticationError";
16
+ }
17
+ }
18
+
19
+ class RateLimitError extends DomainSuggesterError {
20
+ constructor(message = "Rate limit exceeded", options = {}) {
21
+ super(message, { ...options, status: options.status || 429 });
22
+ this.name = "RateLimitError";
23
+ }
24
+ }
25
+
26
+ class ValidationError extends DomainSuggesterError {
27
+ constructor(message = "Validation failed", options = {}) {
28
+ super(message, { ...options, status: options.status || 400 });
29
+ this.name = "ValidationError";
30
+ }
31
+ }
32
+
33
+ class NotFoundError extends DomainSuggesterError {
34
+ constructor(message = "Not found", options = {}) {
35
+ super(message, { ...options, status: options.status || 404 });
36
+ this.name = "NotFoundError";
37
+ }
38
+ }
39
+
40
+ class ApiError extends DomainSuggesterError {
41
+ constructor(message = "API request failed", options = {}) {
42
+ super(message, options);
43
+ this.name = "ApiError";
44
+ }
45
+ }
46
+
47
+ module.exports = {
48
+ DomainSuggesterError,
49
+ AuthenticationError,
50
+ RateLimitError,
51
+ ValidationError,
52
+ NotFoundError,
53
+ ApiError
54
+ };
package/package.json ADDED
@@ -0,0 +1,34 @@
1
+ {
2
+ "name": "domain-suggester",
3
+ "version": "1.0.0",
4
+ "description": "Node.js SDK for the DomainHub domain suggester API",
5
+ "main": "index.js",
6
+ "bin": {
7
+ "domain-suggester": "./bin/cli.js"
8
+ },
9
+ "type": "commonjs",
10
+ "files": [
11
+ "index.js",
12
+ "bin",
13
+ "lib",
14
+ "README.md"
15
+ ],
16
+ "keywords": [
17
+ "domain",
18
+ "sdk",
19
+ "api",
20
+ "domain-suggester",
21
+ "domainhub"
22
+ ],
23
+ "author": "DomainHub",
24
+ "license": "UNLICENSED",
25
+ "engines": {
26
+ "node": ">=18"
27
+ },
28
+ "dependencies": {
29
+ "commander": "^12.1.0"
30
+ },
31
+ "publishConfig": {
32
+ "access": "public"
33
+ }
34
+ }