builtwith-api 1.0.6 → 3.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/dist/cli.js ADDED
@@ -0,0 +1,742 @@
1
+ #!/usr/bin/env node
2
+
3
+ // src/config.ts
4
+ var VALID_RESPONSE_TYPES = {
5
+ XML: "xml",
6
+ JSON: "json",
7
+ TXT: "txt",
8
+ CSV: "csv",
9
+ TSV: "tsv"
10
+ };
11
+
12
+ // src/schemas.ts
13
+ import { z } from "zod/v4";
14
+ var ResponseFormatSchema = z.enum(["xml", "json", "txt", "csv", "tsv"]);
15
+ var ClientOptionsSchema = z.strictObject({
16
+ responseFormat: ResponseFormatSchema.optional()
17
+ });
18
+ var DomainParamsSchema = z.strictObject({
19
+ hideAll: z.boolean().optional(),
20
+ hideDescriptionAndLinks: z.boolean().optional(),
21
+ onlyLiveTechnologies: z.boolean().optional(),
22
+ noMetaData: z.boolean().optional(),
23
+ noAttributeData: z.boolean().optional(),
24
+ noPII: z.boolean().optional(),
25
+ firstDetectedRange: z.string().optional(),
26
+ lastDetectedRange: z.string().optional()
27
+ });
28
+ var ListsParamsSchema = z.strictObject({
29
+ includeMetaData: z.boolean().optional(),
30
+ offset: z.string().optional(),
31
+ since: z.string().optional()
32
+ });
33
+ var TrendsParamsSchema = z.strictObject({
34
+ date: z.string().optional()
35
+ });
36
+ var CompanyToUrlParamsSchema = z.strictObject({
37
+ tld: z.string().optional(),
38
+ amount: z.number().optional()
39
+ });
40
+ var TrustParamsSchema = z.strictObject({
41
+ words: z.string().optional(),
42
+ live: z.boolean().optional()
43
+ });
44
+ var SingleLookupSchema = z.string().min(1);
45
+ var MultiLookupSchema = z.union([
46
+ z.string().min(1),
47
+ z.array(z.string().min(1)).min(1).max(16)
48
+ ]);
49
+ var FreeResponseSchema = z.object({
50
+ domain: z.string(),
51
+ first: z.number(),
52
+ last: z.number(),
53
+ groups: z.array(z.object({
54
+ name: z.string(),
55
+ live: z.number(),
56
+ dead: z.number(),
57
+ latest: z.number(),
58
+ oldest: z.number(),
59
+ categories: z.array(z.object({
60
+ live: z.number(),
61
+ dead: z.number(),
62
+ latest: z.number(),
63
+ oldest: z.number(),
64
+ name: z.string()
65
+ }))
66
+ }))
67
+ });
68
+ var TechnologySchema = z.object({
69
+ Name: z.string(),
70
+ Description: z.string(),
71
+ Link: z.string(),
72
+ Parent: z.string().optional(),
73
+ Tag: z.string(),
74
+ FirstDetected: z.number(),
75
+ LastDetected: z.number(),
76
+ IsPremium: z.string(),
77
+ Categories: z.array(z.string()).optional()
78
+ });
79
+ var PathSchema = z.object({
80
+ Technologies: z.array(TechnologySchema),
81
+ FirstIndexed: z.number(),
82
+ LastIndexed: z.number(),
83
+ Domain: z.string(),
84
+ Url: z.string(),
85
+ SubDomain: z.string()
86
+ });
87
+ var MetaSchema = z.object({
88
+ Majestic: z.number(),
89
+ Vertical: z.string(),
90
+ Social: z.array(z.string()),
91
+ CompanyName: z.string(),
92
+ Telephones: z.array(z.string()),
93
+ Emails: z.array(z.string()),
94
+ City: z.string(),
95
+ State: z.string(),
96
+ Postcode: z.string(),
97
+ Country: z.string(),
98
+ Names: z.array(z.object({
99
+ Name: z.string(),
100
+ Type: z.number(),
101
+ Level: z.string(),
102
+ Email: z.string()
103
+ })),
104
+ ARank: z.number(),
105
+ QRank: z.number()
106
+ });
107
+ var AttributesSchema = z.object({
108
+ MJRank: z.number(),
109
+ MJTLDRank: z.number(),
110
+ RefSN: z.number(),
111
+ RefIP: z.number(),
112
+ Sitemap: z.number(),
113
+ GTMTags: z.number(),
114
+ QubitTags: z.number(),
115
+ TealiumTags: z.number(),
116
+ AdobeTags: z.number(),
117
+ CDimensions: z.number(),
118
+ CGoals: z.number(),
119
+ CMetrics: z.number()
120
+ });
121
+ var DomainResponseSchema = z.object({
122
+ Results: z.array(z.object({
123
+ Result: z.object({
124
+ SpendHistory: z.array(z.object({
125
+ D: z.number(),
126
+ S: z.number()
127
+ })),
128
+ IsDB: z.string(),
129
+ Spend: z.number(),
130
+ Paths: z.array(PathSchema)
131
+ }),
132
+ Meta: MetaSchema,
133
+ Attributes: AttributesSchema,
134
+ FirstIndexed: z.number(),
135
+ LastIndexed: z.number(),
136
+ Lookup: z.string(),
137
+ SalesRevenue: z.number()
138
+ })),
139
+ Errors: z.array(z.string())
140
+ });
141
+ var ListsResponseSchema = z.object({
142
+ NextOffset: z.string(),
143
+ Results: z.array(z.object({
144
+ D: z.string(),
145
+ FI: z.number().optional(),
146
+ LI: z.number().optional(),
147
+ LOS: z.array(z.string()).optional(),
148
+ Q: z.number().optional(),
149
+ A: z.number().optional(),
150
+ U: z.number().optional(),
151
+ M: z.number().optional(),
152
+ SKU: z.number().optional(),
153
+ F: z.number().optional(),
154
+ E: z.number().optional(),
155
+ FD: z.number().optional(),
156
+ LD: z.number().optional(),
157
+ S: z.number().optional(),
158
+ R: z.number().optional(),
159
+ Country: z.string().optional()
160
+ }))
161
+ });
162
+ var RelationshipsResponseSchema = z.object({
163
+ Relationships: z.array(z.object({
164
+ Domain: z.string(),
165
+ Identifiers: z.array(z.object({
166
+ Value: z.string(),
167
+ Type: z.string(),
168
+ First: z.number(),
169
+ Last: z.number(),
170
+ Matches: z.array(z.object({
171
+ Domain: z.string(),
172
+ First: z.number(),
173
+ Last: z.number(),
174
+ Overlap: z.boolean()
175
+ }))
176
+ }))
177
+ })),
178
+ Errors: z.array(z.string()),
179
+ results: z.number(),
180
+ max_per_page: z.number(),
181
+ next_skip: z.number(),
182
+ more_results: z.boolean()
183
+ });
184
+ var KeywordsResponseSchema = z.object({
185
+ Keywords: z.array(z.object({
186
+ Domain: z.string(),
187
+ Keywords: z.array(z.string())
188
+ }))
189
+ });
190
+ var TrendsResponseSchema = z.object({
191
+ Tech: z.object({
192
+ icon: z.string(),
193
+ categories: z.array(z.string()),
194
+ tag: z.string(),
195
+ is_premium: z.string(),
196
+ name: z.string(),
197
+ description: z.string(),
198
+ link: z.string(),
199
+ trends_link: z.string(),
200
+ coverage: z.object({
201
+ ten_k: z.number(),
202
+ hundred_k: z.number(),
203
+ milly: z.number(),
204
+ live: z.number(),
205
+ expired: z.number()
206
+ })
207
+ })
208
+ });
209
+ var CompanyToUrlResponseSchema = z.array(z.object({
210
+ Domain: z.string(),
211
+ CompanyName: z.string(),
212
+ Spend: z.number(),
213
+ PageRank: z.number(),
214
+ BuiltWithRank: z.number(),
215
+ Parked: z.boolean(),
216
+ Country: z.string(),
217
+ State: z.string(),
218
+ Postcode: z.string(),
219
+ City: z.string(),
220
+ Socials: z.array(z.string())
221
+ }));
222
+ var TrustResponseSchema = z.object({
223
+ Domain: z.string(),
224
+ DBRecord: z.object({
225
+ EarliestRecord: z.number(),
226
+ LatestUpdate: z.number(),
227
+ PremiumTechs: z.number(),
228
+ LiveTechs: z.boolean(),
229
+ AffiliateLinks: z.boolean(),
230
+ PaymentOptions: z.boolean(),
231
+ Ecommerce: z.boolean(),
232
+ Parked: z.boolean(),
233
+ Spend: z.number(),
234
+ Established: z.boolean(),
235
+ DBIndexed: z.boolean()
236
+ }),
237
+ LiveRecord: z.object({}).passthrough().nullable(),
238
+ Status: z.number()
239
+ });
240
+ var TagsResponseSchema = z.array(z.object({
241
+ Value: z.string(),
242
+ Matches: z.array(z.object({
243
+ Domain: z.string(),
244
+ First: z.string(),
245
+ Last: z.string()
246
+ }))
247
+ }));
248
+ var RecommendationsResponseSchema = z.array(z.object({
249
+ Domain: z.string(),
250
+ Compiled: z.string(),
251
+ Recommendations: z.array(z.object({
252
+ link: z.string(),
253
+ name: z.string(),
254
+ tag: z.string(),
255
+ categories: z.array(z.string()),
256
+ stars: z.number(),
257
+ match: z.number()
258
+ }))
259
+ }));
260
+ var RedirectsResponseSchema = z.object({
261
+ Lookup: z.string(),
262
+ Inbound: z.array(z.object({
263
+ Domain: z.string(),
264
+ FirstDetected: z.string(),
265
+ LastDetected: z.string()
266
+ })),
267
+ Outbound: z.array(z.object({
268
+ Domain: z.string(),
269
+ FirstDetected: z.string(),
270
+ LastDetected: z.string()
271
+ }))
272
+ });
273
+ var ProductResponseSchema = z.object({
274
+ query: z.string(),
275
+ is_more: z.boolean(),
276
+ page: z.number(),
277
+ limit: z.number(),
278
+ results: z.number(),
279
+ shop_count: z.number(),
280
+ credits: z.number(),
281
+ used: z.number(),
282
+ remaining: z.number(),
283
+ used_this_query: z.number(),
284
+ next_page: z.string(),
285
+ shops: z.array(z.object({
286
+ Domain: z.string(),
287
+ Products: z.array(z.object({
288
+ Title: z.string(),
289
+ Url: z.string(),
290
+ Indexed: z.string(),
291
+ FirstIndexed: z.string(),
292
+ Price: z.number()
293
+ })),
294
+ Type: z.number(),
295
+ Spend: z.number()
296
+ }))
297
+ });
298
+
299
+ // src/params.ts
300
+ var toQueryString = (params) => Object.entries(params).filter((entry) => entry[1] !== undefined).map(([k, v]) => `${k}=${encodeURIComponent(v).replace(/%2C/gi, ",")}`).join("&");
301
+ var booleanFlag = (value) => value ? "yes" : undefined;
302
+ var booleanParams = (params, mapping) => Object.fromEntries(Object.entries(mapping).map(([jsKey, apiKey]) => [
303
+ apiKey,
304
+ booleanFlag(params?.[jsKey])
305
+ ]));
306
+ var cleanWords = (words) => words ? words.split(",").map((w) => w.trim()).join(",") : undefined;
307
+ var buildURL = (apiKey, format, path, params, subdomain = "api") => {
308
+ const base = `https://${subdomain}.builtwith.com/${path}/api.${format}?KEY=${apiKey}`;
309
+ const qs = toQueryString(params);
310
+ return qs ? `${base}&${qs}` : base;
311
+ };
312
+ var validateLookup = (url, { multi = false } = {}) => {
313
+ if (multi) {
314
+ MultiLookupSchema.parse(url);
315
+ } else {
316
+ SingleLookupSchema.parse(url);
317
+ }
318
+ if (Array.isArray(url)) {
319
+ if (!multi)
320
+ throw new Error("API does not allow for multi-domain LOOKUP");
321
+ if (url.length > 16)
322
+ throw new Error("Domain LOOKUP size too big (16 max)");
323
+ }
324
+ };
325
+
326
+ // src/request.ts
327
+ var TEXT_FORMATS = [
328
+ VALID_RESPONSE_TYPES.TXT,
329
+ VALID_RESPONSE_TYPES.XML,
330
+ VALID_RESPONSE_TYPES.CSV,
331
+ VALID_RESPONSE_TYPES.TSV
332
+ ];
333
+ var request = async (url, format, schema) => {
334
+ const res = await fetch(url);
335
+ if (!res.ok) {
336
+ const body = await res.text();
337
+ throw new Error(`BuiltWith API error ${res.status}: ${body}`);
338
+ }
339
+ if (TEXT_FORMATS.includes(format)) {
340
+ return res.text();
341
+ }
342
+ const data = await res.json();
343
+ return schema.parse(data);
344
+ };
345
+ var requestSafe = async (url, format, schema) => {
346
+ const res = await fetch(url);
347
+ if (!res.ok) {
348
+ const body = await res.text();
349
+ throw new Error(`BuiltWith API error ${res.status}: ${body}`);
350
+ }
351
+ if (TEXT_FORMATS.includes(format)) {
352
+ return res.text();
353
+ }
354
+ const raw = await res.text();
355
+ let data;
356
+ try {
357
+ data = JSON.parse(raw);
358
+ } catch {
359
+ console.warn("BuiltWith sent an invalid JSON payload. Falling back to text parsing.");
360
+ return raw;
361
+ }
362
+ return schema.parse(data);
363
+ };
364
+
365
+ // src/index.ts
366
+ var DOMAIN_BOOLEANS = {
367
+ hideAll: "HIDETEXT",
368
+ hideDescriptionAndLinks: "HIDEDL",
369
+ onlyLiveTechnologies: "LIVEONLY",
370
+ noMetaData: "NOMETA",
371
+ noAttributeData: "NOATTR",
372
+ noPII: "NOPII"
373
+ };
374
+ function createClient(apiKey, moduleParams = {}) {
375
+ if (!apiKey) {
376
+ throw new Error("You must initialize the BuiltWith module with an api key");
377
+ }
378
+ const parsed = ClientOptionsSchema.parse(moduleParams);
379
+ const format = parsed.responseFormat || "json";
380
+ if (format === VALID_RESPONSE_TYPES.TXT) {
381
+ console.warn("TXT response format is only supported for the BuiltWith Lists API. See: https://api.builtwith.com/lists-api");
382
+ }
383
+ const url = (path, params) => buildURL(apiKey, format, path, params);
384
+ const get = (bwURL, schema) => request(bwURL, format, schema);
385
+ const getSafe = (bwURL, schema) => requestSafe(bwURL, format, schema);
386
+ return {
387
+ free: async (lookup) => {
388
+ validateLookup(lookup);
389
+ return get(url("free1", { LOOKUP: lookup }), FreeResponseSchema);
390
+ },
391
+ domain: async (lookup, params) => {
392
+ validateLookup(lookup, { multi: true });
393
+ if (params)
394
+ DomainParamsSchema.parse(params);
395
+ return get(url("v22", {
396
+ LOOKUP: Array.isArray(lookup) ? lookup.join(",") : lookup,
397
+ ...booleanParams(params, DOMAIN_BOOLEANS),
398
+ FDRANGE: params?.firstDetectedRange,
399
+ LDRANGE: params?.lastDetectedRange
400
+ }), DomainResponseSchema);
401
+ },
402
+ lists: async (technology, params) => {
403
+ if (params)
404
+ ListsParamsSchema.parse(params);
405
+ return getSafe(url("lists12", {
406
+ TECH: technology,
407
+ META: params?.includeMetaData ? "yes" : undefined,
408
+ OFFSET: params?.offset,
409
+ SINCE: params?.since
410
+ }), ListsResponseSchema);
411
+ },
412
+ relationships: async (lookup) => {
413
+ validateLookup(lookup, { multi: true });
414
+ return get(url("rv4", {
415
+ LOOKUP: Array.isArray(lookup) ? lookup.join(",") : lookup
416
+ }), RelationshipsResponseSchema);
417
+ },
418
+ keywords: async (lookup) => {
419
+ validateLookup(lookup, { multi: true });
420
+ return get(url("kw2", {
421
+ LOOKUP: Array.isArray(lookup) ? lookup.join(",") : lookup
422
+ }), KeywordsResponseSchema);
423
+ },
424
+ trends: async (technology, params) => {
425
+ if (params)
426
+ TrendsParamsSchema.parse(params);
427
+ return getSafe(url("trends/v6", {
428
+ TECH: technology,
429
+ DATE: params?.date
430
+ }), TrendsResponseSchema);
431
+ },
432
+ companyToUrl: async (companyName, params) => {
433
+ if (params)
434
+ CompanyToUrlParamsSchema.parse(params);
435
+ return get(url("ctu3", {
436
+ COMPANY: companyName,
437
+ TLD: params?.tld,
438
+ AMOUNT: params?.amount
439
+ }), CompanyToUrlResponseSchema);
440
+ },
441
+ domainLive: async (lookup) => {
442
+ validateLookup(lookup);
443
+ return get(url("ddlv2", { LOOKUP: lookup }), DomainResponseSchema);
444
+ },
445
+ trust: async (lookup, params) => {
446
+ validateLookup(lookup);
447
+ if (params)
448
+ TrustParamsSchema.parse(params);
449
+ return get(url("trustv1", {
450
+ LOOKUP: lookup,
451
+ WORDS: cleanWords(params?.words),
452
+ LIVE: params?.live ? "yes" : undefined
453
+ }), TrustResponseSchema);
454
+ },
455
+ tags: async (lookup) => {
456
+ validateLookup(lookup);
457
+ return get(url("tag1", { LOOKUP: lookup }), TagsResponseSchema);
458
+ },
459
+ recommendations: async (lookup) => {
460
+ validateLookup(lookup);
461
+ return get(url("rec1", { LOOKUP: lookup }), RecommendationsResponseSchema);
462
+ },
463
+ redirects: async (lookup) => {
464
+ validateLookup(lookup);
465
+ return get(url("redirect1", { LOOKUP: lookup }), RedirectsResponseSchema);
466
+ },
467
+ product: async (query) => {
468
+ return get(url("productv1", { QUERY: query }), ProductResponseSchema);
469
+ }
470
+ };
471
+ }
472
+
473
+ // src/errors.ts
474
+ import { z as z2 } from "zod/v4";
475
+ function formatError(err) {
476
+ if (err instanceof z2.ZodError) {
477
+ const fields = err.issues.map((i) => {
478
+ const path = i.path.length > 0 ? i.path.join(".") : "response";
479
+ return ` ${path}: ${i.message}`;
480
+ });
481
+ return `BuiltWith API returned an unexpected response:
482
+ ${fields.join(`
483
+ `)}`;
484
+ }
485
+ if (err instanceof Error) {
486
+ return err.message;
487
+ }
488
+ return String(err);
489
+ }
490
+
491
+ // src/cli.ts
492
+ import { createRequire } from "node:module";
493
+ import { parseArgs } from "node:util";
494
+
495
+ // src/commands.ts
496
+ function splitLookup(value) {
497
+ const str = String(value);
498
+ return str.includes(",") ? str.split(",").map((s) => s.trim()) : str;
499
+ }
500
+ var commands = [
501
+ {
502
+ name: "free",
503
+ description: "Free lookup — basic technology profile for a domain",
504
+ args: [
505
+ { name: "lookup", description: "Domain to look up", type: "string", required: true }
506
+ ],
507
+ execute: (client, args) => client.free(String(args.lookup))
508
+ },
509
+ {
510
+ name: "domain",
511
+ description: "Detailed technology profile for one or more domains (comma-separated)",
512
+ args: [
513
+ { name: "lookup", description: "Domain(s) to look up (comma-separated for multiple)", type: "string", required: true },
514
+ { name: "hideAll", description: "Hide description text and links", type: "boolean", required: false },
515
+ { name: "hideDescriptionAndLinks", description: "Hide description and links only", type: "boolean", required: false },
516
+ { name: "onlyLiveTechnologies", description: "Only return live technologies", type: "boolean", required: false },
517
+ { name: "noMetaData", description: "Exclude metadata", type: "boolean", required: false },
518
+ { name: "noAttributeData", description: "Exclude attribute data", type: "boolean", required: false },
519
+ { name: "noPII", description: "Exclude personally identifiable information", type: "boolean", required: false },
520
+ { name: "firstDetectedRange", description: "Filter by first detected date range", type: "string", required: false },
521
+ { name: "lastDetectedRange", description: "Filter by last detected date range", type: "string", required: false }
522
+ ],
523
+ execute: (client, args) => {
524
+ const lookup = splitLookup(args.lookup);
525
+ const { lookup: _, ...params } = args;
526
+ return client.domain(lookup, Object.keys(params).length > 0 ? params : undefined);
527
+ }
528
+ },
529
+ {
530
+ name: "lists",
531
+ description: "List domains using a specific technology",
532
+ args: [
533
+ { name: "technology", description: "Technology name to search for", type: "string", required: true },
534
+ { name: "includeMetaData", description: "Include metadata in results", type: "boolean", required: false },
535
+ { name: "offset", description: "Pagination offset", type: "string", required: false },
536
+ { name: "since", description: "Only return results since this date", type: "string", required: false }
537
+ ],
538
+ execute: (client, args) => {
539
+ const { technology, ...params } = args;
540
+ return client.lists(String(technology), Object.keys(params).length > 0 ? params : undefined);
541
+ }
542
+ },
543
+ {
544
+ name: "relationships",
545
+ description: "Find related domains via shared identifiers",
546
+ args: [
547
+ { name: "lookup", description: "Domain(s) to look up (comma-separated for multiple)", type: "string", required: true }
548
+ ],
549
+ execute: (client, args) => client.relationships(splitLookup(args.lookup))
550
+ },
551
+ {
552
+ name: "keywords",
553
+ description: "Get keywords for one or more domains",
554
+ args: [
555
+ { name: "lookup", description: "Domain(s) to look up (comma-separated for multiple)", type: "string", required: true }
556
+ ],
557
+ execute: (client, args) => client.keywords(splitLookup(args.lookup))
558
+ },
559
+ {
560
+ name: "trends",
561
+ description: "Technology adoption trends",
562
+ args: [
563
+ { name: "technology", description: "Technology name", type: "string", required: true },
564
+ { name: "date", description: "Date filter", type: "string", required: false }
565
+ ],
566
+ execute: (client, args) => {
567
+ const { technology, ...params } = args;
568
+ return client.trends(String(technology), Object.keys(params).length > 0 ? params : undefined);
569
+ }
570
+ },
571
+ {
572
+ name: "companyToUrl",
573
+ description: "Find domains associated with a company name",
574
+ args: [
575
+ { name: "companyName", description: "Company name to search", type: "string", required: true },
576
+ { name: "tld", description: "Filter by top-level domain", type: "string", required: false },
577
+ { name: "amount", description: "Number of results to return", type: "number", required: false }
578
+ ],
579
+ execute: (client, args) => {
580
+ const { companyName, ...params } = args;
581
+ return client.companyToUrl(String(companyName), Object.keys(params).length > 0 ? params : undefined);
582
+ }
583
+ },
584
+ {
585
+ name: "domainLive",
586
+ description: "Live technology lookup for a domain",
587
+ args: [
588
+ { name: "lookup", description: "Domain to look up", type: "string", required: true }
589
+ ],
590
+ execute: (client, args) => client.domainLive(String(args.lookup))
591
+ },
592
+ {
593
+ name: "trust",
594
+ description: "Trust/verification score for a domain",
595
+ args: [
596
+ { name: "lookup", description: "Domain to look up", type: "string", required: true },
597
+ { name: "words", description: "Comma-separated keywords to check", type: "string", required: false },
598
+ { name: "live", description: "Include live analysis", type: "boolean", required: false }
599
+ ],
600
+ execute: (client, args) => {
601
+ const { lookup, ...params } = args;
602
+ return client.trust(String(lookup), Object.keys(params).length > 0 ? params : undefined);
603
+ }
604
+ },
605
+ {
606
+ name: "tags",
607
+ description: "Get tracking/analytics tags for a domain",
608
+ args: [
609
+ { name: "lookup", description: "Domain to look up", type: "string", required: true }
610
+ ],
611
+ execute: (client, args) => client.tags(String(args.lookup))
612
+ },
613
+ {
614
+ name: "recommendations",
615
+ description: "Get technology recommendations for a domain",
616
+ args: [
617
+ { name: "lookup", description: "Domain to look up", type: "string", required: true }
618
+ ],
619
+ execute: (client, args) => client.recommendations(String(args.lookup))
620
+ },
621
+ {
622
+ name: "redirects",
623
+ description: "Get redirect chains for a domain",
624
+ args: [
625
+ { name: "lookup", description: "Domain to look up", type: "string", required: true }
626
+ ],
627
+ execute: (client, args) => client.redirects(String(args.lookup))
628
+ },
629
+ {
630
+ name: "product",
631
+ description: "Search for products across e-commerce sites",
632
+ args: [
633
+ { name: "query", description: "Product search query", type: "string", required: true }
634
+ ],
635
+ execute: (client, args) => client.product(String(args.query))
636
+ }
637
+ ];
638
+
639
+ // src/cli.ts
640
+ var require2 = createRequire(import.meta.url);
641
+ var { version } = require2("../package.json");
642
+ var USAGE = `Usage: builtwith <command> <primary-arg> [--flag value ...]
643
+
644
+ Commands:
645
+ ${commands.map((c) => ` ${c.name.padEnd(16)} ${c.description}`).join(`
646
+ `)}
647
+
648
+ Options:
649
+ --api-key <key> BuiltWith API key (or set BUILTWITH_API_KEY env var)
650
+ --version Show version number
651
+ --help Show help
652
+
653
+ Examples:
654
+ builtwith free example.com
655
+ builtwith domain example.com --hideAll --onlyLiveTechnologies
656
+ builtwith domain "example.com,other.com"
657
+ builtwith lists Shopify --since 2024-01-01
658
+ builtwith trust example.com --words "shop,buy" --live
659
+ `;
660
+ function printCommandHelp(cmd) {
661
+ const primary = cmd.args.find((a) => a.required);
662
+ const flags = cmd.args.filter((a) => !a.required);
663
+ console.log(`Usage: builtwith ${cmd.name} <${primary?.name ?? "arg"}>${flags.length ? " [options]" : ""}
664
+ `);
665
+ console.log(` ${cmd.description}
666
+ `);
667
+ if (flags.length) {
668
+ console.log("Options:");
669
+ for (const f of flags) {
670
+ console.log(` --${f.name.padEnd(28)} ${f.description} (${f.type})`);
671
+ }
672
+ }
673
+ }
674
+ function run() {
675
+ const argv = process.argv.slice(2);
676
+ if (argv.includes("--version") || argv.includes("-V")) {
677
+ console.log(version);
678
+ process.exit(0);
679
+ }
680
+ if (argv.length === 0 || argv[0] === "--help" || argv[0] === "-h") {
681
+ console.log(USAGE);
682
+ process.exit(0);
683
+ }
684
+ const commandName = argv[0];
685
+ const cmd = commands.find((c) => c.name === commandName);
686
+ if (!cmd) {
687
+ console.error(`Unknown command: ${commandName}
688
+ `);
689
+ console.log(USAGE);
690
+ process.exit(1);
691
+ }
692
+ if (argv.includes("--help") || argv.includes("-h")) {
693
+ printCommandHelp(cmd);
694
+ process.exit(0);
695
+ }
696
+ const primaryArg = cmd.args.find((a) => a.required);
697
+ const primaryValue = argv[1];
698
+ if (primaryArg && (!primaryValue || primaryValue.startsWith("--"))) {
699
+ console.error(`Error: missing required argument <${primaryArg.name}>`);
700
+ process.exit(1);
701
+ }
702
+ const flagArgs = cmd.args.filter((a) => !a.required);
703
+ const options = {
704
+ "api-key": { type: "string" }
705
+ };
706
+ for (const f of flagArgs) {
707
+ options[f.name] = { type: f.type === "boolean" ? "boolean" : "string" };
708
+ }
709
+ const { values } = parseArgs({
710
+ args: argv.slice(2),
711
+ options,
712
+ strict: false
713
+ });
714
+ const apiKey = values["api-key"] || process.env.BUILTWITH_API_KEY;
715
+ if (!apiKey) {
716
+ console.error("Error: pass --api-key or set BUILTWITH_API_KEY environment variable.");
717
+ process.exit(1);
718
+ }
719
+ const args = {};
720
+ if (primaryArg) {
721
+ args[primaryArg.name] = primaryValue;
722
+ }
723
+ for (const f of flagArgs) {
724
+ if (values[f.name] !== undefined) {
725
+ if (f.type === "number") {
726
+ args[f.name] = Number(values[f.name]);
727
+ } else {
728
+ args[f.name] = values[f.name];
729
+ }
730
+ }
731
+ }
732
+ const client = createClient(apiKey);
733
+ cmd.execute(client, args).then((result) => {
734
+ console.log(JSON.stringify(result, null, 2));
735
+ }).catch((err) => {
736
+ console.error(formatError(err));
737
+ process.exit(1);
738
+ });
739
+ }
740
+ run();
741
+
742
+ //# debugId=D76DF713FC3000FE64756E2164756E21