@tydung26/product-kit 1.4.0 → 1.5.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/scripts/market-intel/search-app-store.d.ts +7 -0
- package/dist/scripts/market-intel/search-app-store.d.ts.map +1 -0
- package/dist/scripts/market-intel/search-app-store.js +91 -0
- package/dist/scripts/market-intel/search-app-store.js.map +1 -0
- package/dist/scripts/market-intel/search-google-play.d.ts +7 -0
- package/dist/scripts/market-intel/search-google-play.d.ts.map +1 -0
- package/dist/scripts/market-intel/search-google-play.js +195 -0
- package/dist/scripts/market-intel/search-google-play.js.map +1 -0
- package/dist/scripts/market-intel/search-product-hunt.d.ts +7 -0
- package/dist/scripts/market-intel/search-product-hunt.d.ts.map +1 -0
- package/dist/scripts/market-intel/search-product-hunt.js +236 -0
- package/dist/scripts/market-intel/search-product-hunt.js.map +1 -0
- package/dist/scripts/market-intel/search-yc-launch.d.ts +7 -0
- package/dist/scripts/market-intel/search-yc-launch.d.ts.map +1 -0
- package/dist/scripts/market-intel/search-yc-launch.js +229 -0
- package/dist/scripts/market-intel/search-yc-launch.js.map +1 -0
- package/dist/scripts/market-intel/shared-types.d.ts +44 -0
- package/dist/scripts/market-intel/shared-types.d.ts.map +1 -0
- package/dist/scripts/market-intel/shared-types.js +63 -0
- package/dist/scripts/market-intel/shared-types.js.map +1 -0
- package/package.json +8 -9
- package/skills/market-intel/SKILL.md +184 -61
- package/skills/market-intel/scripts/search-app-store.py +117 -0
- package/skills/market-intel/scripts/search-google-play.py +179 -0
- package/skills/market-intel/scripts/search-product-hunt.py +194 -0
- package/skills/market-intel/scripts/search-yc-launch.py +160 -0
- package/dist/commands/config/index.d.ts +0 -3
- package/dist/commands/config/index.d.ts.map +0 -1
- package/dist/commands/config/index.js +0 -34
- package/dist/commands/config/index.js.map +0 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"search-app-store.d.ts","sourceRoot":"","sources":["../../../src/scripts/market-intel/search-app-store.ts"],"names":[],"mappings":"AAAA;;;;GAIG"}
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* App Store crawler via iTunes Search API.
|
|
4
|
+
* Usage: node search-app-store.mjs "<keywords>" [limit]
|
|
5
|
+
* Output: JSON CrawlResult to stdout
|
|
6
|
+
*/
|
|
7
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
8
|
+
const shared_types_js_1 = require("./shared-types.js");
|
|
9
|
+
async function fetchReviews(trackId) {
|
|
10
|
+
try {
|
|
11
|
+
const url = `https://itunes.apple.com/rss/customerreviews/id=${trackId}/sortBy=mostRecent/json`;
|
|
12
|
+
const res = await (0, shared_types_js_1.safeFetch)(url);
|
|
13
|
+
if (!res.ok)
|
|
14
|
+
return [];
|
|
15
|
+
const data = await res.json();
|
|
16
|
+
const entries = data?.feed?.entry || [];
|
|
17
|
+
// first entry is often metadata, skip it
|
|
18
|
+
return entries
|
|
19
|
+
.filter((e) => e.content?.label && e["im:rating"]?.label)
|
|
20
|
+
.slice(0, 6)
|
|
21
|
+
.map((e) => {
|
|
22
|
+
const rating = parseInt(e["im:rating"].label, 10);
|
|
23
|
+
return {
|
|
24
|
+
text: (0, shared_types_js_1.truncate)(e.content.label, 300),
|
|
25
|
+
rating,
|
|
26
|
+
sentiment: rating >= 4 ? "positive" : rating <= 2 ? "negative" : "neutral",
|
|
27
|
+
};
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
catch {
|
|
31
|
+
return [];
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
/** Parse pricing from iTunes price + formattedPrice fields */
|
|
35
|
+
function parsePricing(app) {
|
|
36
|
+
const isFree = app.price === 0;
|
|
37
|
+
return {
|
|
38
|
+
free: isFree,
|
|
39
|
+
monthly: undefined,
|
|
40
|
+
yearly: undefined,
|
|
41
|
+
other: isFree ? undefined : app.formattedPrice,
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
async function main() {
|
|
45
|
+
const { query, limit } = (0, shared_types_js_1.parseArgs)();
|
|
46
|
+
const errors = [];
|
|
47
|
+
// Search iTunes API
|
|
48
|
+
const searchUrl = `https://itunes.apple.com/search?term=${encodeURIComponent(query)}&entity=software&limit=${limit}&country=us`;
|
|
49
|
+
let searchData;
|
|
50
|
+
try {
|
|
51
|
+
const res = await (0, shared_types_js_1.safeFetch)(searchUrl);
|
|
52
|
+
if (!res.ok) {
|
|
53
|
+
return (0, shared_types_js_1.outputError)("app_store", query, `iTunes API returned ${res.status}`);
|
|
54
|
+
}
|
|
55
|
+
searchData = await res.json();
|
|
56
|
+
}
|
|
57
|
+
catch (err) {
|
|
58
|
+
return (0, shared_types_js_1.outputError)("app_store", query, `Failed to fetch iTunes API: ${err instanceof Error ? err.message : String(err)}`);
|
|
59
|
+
}
|
|
60
|
+
if (!searchData.results?.length) {
|
|
61
|
+
return (0, shared_types_js_1.outputError)("app_store", query, "No results found");
|
|
62
|
+
}
|
|
63
|
+
// Process each app
|
|
64
|
+
const results = [];
|
|
65
|
+
for (const app of searchData.results) {
|
|
66
|
+
const reviews = await fetchReviews(app.trackId);
|
|
67
|
+
results.push({
|
|
68
|
+
name: app.trackName,
|
|
69
|
+
url: app.trackViewUrl,
|
|
70
|
+
description: (0, shared_types_js_1.truncate)(app.description),
|
|
71
|
+
tagline: undefined,
|
|
72
|
+
rating: app.averageUserRating
|
|
73
|
+
? Math.round(app.averageUserRating * 10) / 10
|
|
74
|
+
: undefined,
|
|
75
|
+
reviewCount: app.userRatingCount,
|
|
76
|
+
pricing: parsePricing(app),
|
|
77
|
+
features: [], // iTunes API doesn't expose feature lists
|
|
78
|
+
reviews,
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
const result = {
|
|
82
|
+
platform: "app_store",
|
|
83
|
+
query,
|
|
84
|
+
timestamp: new Date().toISOString(),
|
|
85
|
+
results,
|
|
86
|
+
errors,
|
|
87
|
+
};
|
|
88
|
+
(0, shared_types_js_1.outputResult)(result);
|
|
89
|
+
}
|
|
90
|
+
main();
|
|
91
|
+
//# sourceMappingURL=search-app-store.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"search-app-store.js","sourceRoot":"","sources":["../../../src/scripts/market-intel/search-app-store.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"search-google-play.d.ts","sourceRoot":"","sources":["../../../src/scripts/market-intel/search-google-play.ts"],"names":[],"mappings":"AAAA;;;;GAIG"}
|
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Google Play Store crawler via HTML scraping.
|
|
4
|
+
* Usage: node search-google-play.mjs "<keywords>" [limit]
|
|
5
|
+
* Output: JSON CrawlResult to stdout
|
|
6
|
+
*/
|
|
7
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
8
|
+
if (k2 === undefined) k2 = k;
|
|
9
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
10
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
11
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
12
|
+
}
|
|
13
|
+
Object.defineProperty(o, k2, desc);
|
|
14
|
+
}) : (function(o, m, k, k2) {
|
|
15
|
+
if (k2 === undefined) k2 = k;
|
|
16
|
+
o[k2] = m[k];
|
|
17
|
+
}));
|
|
18
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
19
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
20
|
+
}) : function(o, v) {
|
|
21
|
+
o["default"] = v;
|
|
22
|
+
});
|
|
23
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
24
|
+
var ownKeys = function(o) {
|
|
25
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
26
|
+
var ar = [];
|
|
27
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
28
|
+
return ar;
|
|
29
|
+
};
|
|
30
|
+
return ownKeys(o);
|
|
31
|
+
};
|
|
32
|
+
return function (mod) {
|
|
33
|
+
if (mod && mod.__esModule) return mod;
|
|
34
|
+
var result = {};
|
|
35
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
36
|
+
__setModuleDefault(result, mod);
|
|
37
|
+
return result;
|
|
38
|
+
};
|
|
39
|
+
})();
|
|
40
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
41
|
+
const cheerio = __importStar(require("cheerio"));
|
|
42
|
+
const shared_types_js_1 = require("./shared-types.js");
|
|
43
|
+
/** Extract app URLs from Google Play search results page */
|
|
44
|
+
function extractAppUrls($) {
|
|
45
|
+
const urls = [];
|
|
46
|
+
// Google Play search results link to /store/apps/details?id=...
|
|
47
|
+
$('a[href*="/store/apps/details"]').each((_, el) => {
|
|
48
|
+
const href = $(el).attr("href");
|
|
49
|
+
if (href && !urls.includes(href)) {
|
|
50
|
+
const fullUrl = href.startsWith("http")
|
|
51
|
+
? href
|
|
52
|
+
: `https://play.google.com${href}`;
|
|
53
|
+
// Normalize to just the id parameter
|
|
54
|
+
const match = fullUrl.match(/id=([^&]+)/);
|
|
55
|
+
if (match) {
|
|
56
|
+
const normalized = `https://play.google.com/store/apps/details?id=${match[1]}`;
|
|
57
|
+
if (!urls.includes(normalized))
|
|
58
|
+
urls.push(normalized);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
});
|
|
62
|
+
return urls;
|
|
63
|
+
}
|
|
64
|
+
/** Extract structured data from an app detail page */
|
|
65
|
+
function extractAppDetails($, url) {
|
|
66
|
+
// Try JSON-LD first (most reliable)
|
|
67
|
+
let name = "";
|
|
68
|
+
let description = "";
|
|
69
|
+
let rating;
|
|
70
|
+
let reviewCount;
|
|
71
|
+
$('script[type="application/ld+json"]').each((_, el) => {
|
|
72
|
+
try {
|
|
73
|
+
const data = JSON.parse($(el).html() || "");
|
|
74
|
+
if (data["@type"] === "SoftwareApplication" || data.name) {
|
|
75
|
+
name = data.name || "";
|
|
76
|
+
description = data.description || "";
|
|
77
|
+
if (data.aggregateRating) {
|
|
78
|
+
rating = parseFloat(data.aggregateRating.ratingValue);
|
|
79
|
+
reviewCount = parseInt(data.aggregateRating.ratingCount, 10);
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
catch {
|
|
84
|
+
// ignore malformed JSON-LD
|
|
85
|
+
}
|
|
86
|
+
});
|
|
87
|
+
// Fallback to meta tags
|
|
88
|
+
if (!name) {
|
|
89
|
+
name =
|
|
90
|
+
$('meta[property="og:title"]').attr("content") ||
|
|
91
|
+
$("h1").first().text().trim() ||
|
|
92
|
+
"";
|
|
93
|
+
}
|
|
94
|
+
if (!description) {
|
|
95
|
+
description =
|
|
96
|
+
$('meta[property="og:description"]').attr("content") ||
|
|
97
|
+
$('meta[name="description"]').attr("content") ||
|
|
98
|
+
"";
|
|
99
|
+
}
|
|
100
|
+
if (!name)
|
|
101
|
+
return null;
|
|
102
|
+
// Extract pricing from page text
|
|
103
|
+
const priceText = $('[itemprop="price"]').attr("content") ||
|
|
104
|
+
$('[class*="price"]').first().text().trim() ||
|
|
105
|
+
"";
|
|
106
|
+
const isFree = priceText === "0" ||
|
|
107
|
+
priceText.toLowerCase().includes("free") ||
|
|
108
|
+
!priceText;
|
|
109
|
+
// Extract reviews from the page
|
|
110
|
+
const reviews = [];
|
|
111
|
+
$('[class*="review"] [class*="body"], [data-review-id] [class*="content"]')
|
|
112
|
+
.slice(0, 6)
|
|
113
|
+
.each((_, el) => {
|
|
114
|
+
const text = $(el).text().trim();
|
|
115
|
+
if (text.length > 20) {
|
|
116
|
+
reviews.push({
|
|
117
|
+
text: (0, shared_types_js_1.truncate)(text, 300),
|
|
118
|
+
sentiment: "neutral",
|
|
119
|
+
});
|
|
120
|
+
}
|
|
121
|
+
});
|
|
122
|
+
return {
|
|
123
|
+
name,
|
|
124
|
+
url,
|
|
125
|
+
description: (0, shared_types_js_1.truncate)(description),
|
|
126
|
+
rating: rating ? Math.round(rating * 10) / 10 : undefined,
|
|
127
|
+
reviewCount: reviewCount || undefined,
|
|
128
|
+
pricing: {
|
|
129
|
+
free: isFree,
|
|
130
|
+
other: isFree ? undefined : priceText || undefined,
|
|
131
|
+
},
|
|
132
|
+
features: [],
|
|
133
|
+
reviews,
|
|
134
|
+
};
|
|
135
|
+
}
|
|
136
|
+
async function main() {
|
|
137
|
+
const { query, limit } = (0, shared_types_js_1.parseArgs)();
|
|
138
|
+
const errors = [];
|
|
139
|
+
// Fetch search results page
|
|
140
|
+
const searchUrl = `https://play.google.com/store/search?q=${encodeURIComponent(query)}&c=apps&hl=en&gl=us`;
|
|
141
|
+
let searchHtml;
|
|
142
|
+
try {
|
|
143
|
+
const res = await (0, shared_types_js_1.safeFetch)(searchUrl);
|
|
144
|
+
if (!res.ok) {
|
|
145
|
+
return (0, shared_types_js_1.outputError)("google_play", query, `Google Play search returned ${res.status}`);
|
|
146
|
+
}
|
|
147
|
+
searchHtml = await res.text();
|
|
148
|
+
}
|
|
149
|
+
catch (err) {
|
|
150
|
+
return (0, shared_types_js_1.outputError)("google_play", query, `Failed to fetch Google Play: ${err instanceof Error ? err.message : String(err)}`);
|
|
151
|
+
}
|
|
152
|
+
const $ = cheerio.load(searchHtml);
|
|
153
|
+
const appUrls = extractAppUrls($).slice(0, limit);
|
|
154
|
+
if (appUrls.length === 0) {
|
|
155
|
+
errors.push("No app URLs found in search results — page may be JS-rendered");
|
|
156
|
+
// Try to extract what we can from search page itself
|
|
157
|
+
const result = {
|
|
158
|
+
platform: "google_play",
|
|
159
|
+
query,
|
|
160
|
+
timestamp: new Date().toISOString(),
|
|
161
|
+
results: [],
|
|
162
|
+
errors,
|
|
163
|
+
};
|
|
164
|
+
return (0, shared_types_js_1.outputResult)(result);
|
|
165
|
+
}
|
|
166
|
+
// Fetch each app detail page
|
|
167
|
+
const results = [];
|
|
168
|
+
for (const url of appUrls) {
|
|
169
|
+
try {
|
|
170
|
+
const res = await (0, shared_types_js_1.safeFetch)(url);
|
|
171
|
+
if (!res.ok) {
|
|
172
|
+
errors.push(`Failed to fetch ${url}: ${res.status}`);
|
|
173
|
+
continue;
|
|
174
|
+
}
|
|
175
|
+
const html = await res.text();
|
|
176
|
+
const page$ = cheerio.load(html);
|
|
177
|
+
const entry = extractAppDetails(page$, url);
|
|
178
|
+
if (entry)
|
|
179
|
+
results.push(entry);
|
|
180
|
+
}
|
|
181
|
+
catch (err) {
|
|
182
|
+
errors.push(`Error fetching ${url}: ${err instanceof Error ? err.message : String(err)}`);
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
const result = {
|
|
186
|
+
platform: "google_play",
|
|
187
|
+
query,
|
|
188
|
+
timestamp: new Date().toISOString(),
|
|
189
|
+
results,
|
|
190
|
+
errors,
|
|
191
|
+
};
|
|
192
|
+
(0, shared_types_js_1.outputResult)(result);
|
|
193
|
+
}
|
|
194
|
+
main();
|
|
195
|
+
//# sourceMappingURL=search-google-play.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"search-google-play.js","sourceRoot":"","sources":["../../../src/scripts/market-intel/search-google-play.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"search-product-hunt.d.ts","sourceRoot":"","sources":["../../../src/scripts/market-intel/search-product-hunt.ts"],"names":[],"mappings":"AAAA;;;;GAIG"}
|
|
@@ -0,0 +1,236 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Product Hunt crawler via HTML scraping + __NEXT_DATA__ extraction.
|
|
4
|
+
* Usage: node search-product-hunt.mjs "<keywords>" [limit]
|
|
5
|
+
* Output: JSON CrawlResult to stdout
|
|
6
|
+
*/
|
|
7
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
8
|
+
if (k2 === undefined) k2 = k;
|
|
9
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
10
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
11
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
12
|
+
}
|
|
13
|
+
Object.defineProperty(o, k2, desc);
|
|
14
|
+
}) : (function(o, m, k, k2) {
|
|
15
|
+
if (k2 === undefined) k2 = k;
|
|
16
|
+
o[k2] = m[k];
|
|
17
|
+
}));
|
|
18
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
19
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
20
|
+
}) : function(o, v) {
|
|
21
|
+
o["default"] = v;
|
|
22
|
+
});
|
|
23
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
24
|
+
var ownKeys = function(o) {
|
|
25
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
26
|
+
var ar = [];
|
|
27
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
28
|
+
return ar;
|
|
29
|
+
};
|
|
30
|
+
return ownKeys(o);
|
|
31
|
+
};
|
|
32
|
+
return function (mod) {
|
|
33
|
+
if (mod && mod.__esModule) return mod;
|
|
34
|
+
var result = {};
|
|
35
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
36
|
+
__setModuleDefault(result, mod);
|
|
37
|
+
return result;
|
|
38
|
+
};
|
|
39
|
+
})();
|
|
40
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
41
|
+
const cheerio = __importStar(require("cheerio"));
|
|
42
|
+
const shared_types_js_1 = require("./shared-types.js");
|
|
43
|
+
/** Extract product URLs from search results page */
|
|
44
|
+
function extractPostUrls($) {
|
|
45
|
+
const urls = [];
|
|
46
|
+
// Product Hunt post links: /posts/<slug>
|
|
47
|
+
$('a[href*="/posts/"]').each((_, el) => {
|
|
48
|
+
const href = $(el).attr("href");
|
|
49
|
+
if (href) {
|
|
50
|
+
const fullUrl = href.startsWith("http")
|
|
51
|
+
? href
|
|
52
|
+
: `https://www.producthunt.com${href}`;
|
|
53
|
+
// Deduplicate and filter out non-post pages
|
|
54
|
+
if (fullUrl.includes("/posts/") && !urls.includes(fullUrl)) {
|
|
55
|
+
urls.push(fullUrl);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
});
|
|
59
|
+
return urls;
|
|
60
|
+
}
|
|
61
|
+
/** Try to extract __NEXT_DATA__ JSON from page */
|
|
62
|
+
function extractNextData($) {
|
|
63
|
+
const script = $("#__NEXT_DATA__").html();
|
|
64
|
+
if (!script)
|
|
65
|
+
return null;
|
|
66
|
+
try {
|
|
67
|
+
return JSON.parse(script);
|
|
68
|
+
}
|
|
69
|
+
catch {
|
|
70
|
+
return null;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
/** Deep search an object for a key matching a predicate (max 10 levels) */
|
|
74
|
+
function deepFind(obj, predicate, depth = 0) {
|
|
75
|
+
if (!obj || typeof obj !== "object" || depth > 10)
|
|
76
|
+
return null;
|
|
77
|
+
for (const [key, value] of Object.entries(obj)) {
|
|
78
|
+
if (predicate(key, value))
|
|
79
|
+
return value;
|
|
80
|
+
const found = deepFind(value, predicate, depth + 1);
|
|
81
|
+
if (found)
|
|
82
|
+
return found;
|
|
83
|
+
}
|
|
84
|
+
return null;
|
|
85
|
+
}
|
|
86
|
+
/** Extract post details from a Product Hunt post page */
|
|
87
|
+
function extractPostFromPage($, url) {
|
|
88
|
+
// Strategy 1: __NEXT_DATA__ (most reliable)
|
|
89
|
+
const nextData = extractNextData($);
|
|
90
|
+
if (nextData) {
|
|
91
|
+
// Find the post object in the nested data
|
|
92
|
+
const post = deepFind(nextData, (key, val) => key === "post" &&
|
|
93
|
+
typeof val === "object" &&
|
|
94
|
+
val !== null &&
|
|
95
|
+
"name" in val);
|
|
96
|
+
if (post) {
|
|
97
|
+
return {
|
|
98
|
+
name: post.name,
|
|
99
|
+
url,
|
|
100
|
+
description: (0, shared_types_js_1.truncate)(post.description || ""),
|
|
101
|
+
tagline: post.tagline,
|
|
102
|
+
rating: post.reviewsRating
|
|
103
|
+
? Math.round(post.reviewsRating * 10) / 10
|
|
104
|
+
: undefined,
|
|
105
|
+
reviewCount: post.reviewsCount || post.votesCount,
|
|
106
|
+
pricing: {
|
|
107
|
+
free: true, // default assumption, PH doesn't always expose pricing
|
|
108
|
+
other: post.pricing || undefined,
|
|
109
|
+
},
|
|
110
|
+
features: post.topics?.map((t) => t.name).filter(Boolean).slice(0, 5) || [],
|
|
111
|
+
reviews: [],
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
// Strategy 2: HTML meta tags fallback
|
|
116
|
+
const name = $('meta[property="og:title"]').attr("content")?.replace(" | Product Hunt", "") ||
|
|
117
|
+
$("h1").first().text().trim() ||
|
|
118
|
+
"";
|
|
119
|
+
const description = $('meta[property="og:description"]').attr("content") || "";
|
|
120
|
+
if (!name)
|
|
121
|
+
return null;
|
|
122
|
+
return {
|
|
123
|
+
name,
|
|
124
|
+
url,
|
|
125
|
+
description: (0, shared_types_js_1.truncate)(description),
|
|
126
|
+
tagline: $('meta[name="twitter:description"]').attr("content") || undefined,
|
|
127
|
+
pricing: { free: true },
|
|
128
|
+
features: [],
|
|
129
|
+
reviews: [],
|
|
130
|
+
};
|
|
131
|
+
}
|
|
132
|
+
/** Extract reviews/comments from post page */
|
|
133
|
+
function extractComments($) {
|
|
134
|
+
const reviews = [];
|
|
135
|
+
// Product Hunt comments are typically in divs with specific data attributes
|
|
136
|
+
$('[class*="comment"] p, [data-test="comment-body"], [class*="CommentBody"]')
|
|
137
|
+
.slice(0, 6)
|
|
138
|
+
.each((_, el) => {
|
|
139
|
+
const text = $(el).text().trim();
|
|
140
|
+
if (text.length > 20) {
|
|
141
|
+
reviews.push({
|
|
142
|
+
text: (0, shared_types_js_1.truncate)(text, 300),
|
|
143
|
+
sentiment: "neutral",
|
|
144
|
+
});
|
|
145
|
+
}
|
|
146
|
+
});
|
|
147
|
+
return reviews;
|
|
148
|
+
}
|
|
149
|
+
async function main() {
|
|
150
|
+
const { query, limit } = (0, shared_types_js_1.parseArgs)();
|
|
151
|
+
const errors = [];
|
|
152
|
+
// Fetch search results page
|
|
153
|
+
const searchUrl = `https://www.producthunt.com/search?q=${encodeURIComponent(query)}`;
|
|
154
|
+
let searchHtml;
|
|
155
|
+
try {
|
|
156
|
+
const res = await (0, shared_types_js_1.safeFetch)(searchUrl);
|
|
157
|
+
if (!res.ok) {
|
|
158
|
+
return (0, shared_types_js_1.outputError)("product_hunt", query, `Product Hunt search returned ${res.status}`);
|
|
159
|
+
}
|
|
160
|
+
searchHtml = await res.text();
|
|
161
|
+
}
|
|
162
|
+
catch (err) {
|
|
163
|
+
return (0, shared_types_js_1.outputError)("product_hunt", query, `Failed to fetch Product Hunt: ${err instanceof Error ? err.message : String(err)}`);
|
|
164
|
+
}
|
|
165
|
+
const $ = cheerio.load(searchHtml);
|
|
166
|
+
let postUrls = extractPostUrls($).slice(0, limit);
|
|
167
|
+
// Try extracting from __NEXT_DATA__ on the search page itself
|
|
168
|
+
if (postUrls.length === 0) {
|
|
169
|
+
const nextData = extractNextData($);
|
|
170
|
+
if (nextData) {
|
|
171
|
+
// Search for post objects in __NEXT_DATA__
|
|
172
|
+
const posts = [];
|
|
173
|
+
const findPosts = (obj) => {
|
|
174
|
+
if (!obj || typeof obj !== "object")
|
|
175
|
+
return;
|
|
176
|
+
if (Array.isArray(obj)) {
|
|
177
|
+
for (const item of obj)
|
|
178
|
+
findPosts(item);
|
|
179
|
+
return;
|
|
180
|
+
}
|
|
181
|
+
const record = obj;
|
|
182
|
+
if (record.slug && record.name && typeof record.name === "string") {
|
|
183
|
+
posts.push(record);
|
|
184
|
+
}
|
|
185
|
+
for (const value of Object.values(record)) {
|
|
186
|
+
findPosts(value);
|
|
187
|
+
}
|
|
188
|
+
};
|
|
189
|
+
findPosts(nextData);
|
|
190
|
+
postUrls = posts
|
|
191
|
+
.slice(0, limit)
|
|
192
|
+
.map((p) => `https://www.producthunt.com/posts/${p.slug}`);
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
if (postUrls.length === 0) {
|
|
196
|
+
errors.push("No post URLs found — page may require JS rendering");
|
|
197
|
+
return (0, shared_types_js_1.outputResult)({
|
|
198
|
+
platform: "product_hunt",
|
|
199
|
+
query,
|
|
200
|
+
timestamp: new Date().toISOString(),
|
|
201
|
+
results: [],
|
|
202
|
+
errors,
|
|
203
|
+
});
|
|
204
|
+
}
|
|
205
|
+
// Fetch each post page
|
|
206
|
+
const results = [];
|
|
207
|
+
for (const url of postUrls) {
|
|
208
|
+
try {
|
|
209
|
+
const res = await (0, shared_types_js_1.safeFetch)(url);
|
|
210
|
+
if (!res.ok) {
|
|
211
|
+
errors.push(`Failed to fetch ${url}: ${res.status}`);
|
|
212
|
+
continue;
|
|
213
|
+
}
|
|
214
|
+
const html = await res.text();
|
|
215
|
+
const page$ = cheerio.load(html);
|
|
216
|
+
const entry = extractPostFromPage(page$, url);
|
|
217
|
+
if (entry) {
|
|
218
|
+
entry.reviews = extractComments(page$);
|
|
219
|
+
results.push(entry);
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
catch (err) {
|
|
223
|
+
errors.push(`Error fetching ${url}: ${err instanceof Error ? err.message : String(err)}`);
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
const result = {
|
|
227
|
+
platform: "product_hunt",
|
|
228
|
+
query,
|
|
229
|
+
timestamp: new Date().toISOString(),
|
|
230
|
+
results,
|
|
231
|
+
errors,
|
|
232
|
+
};
|
|
233
|
+
(0, shared_types_js_1.outputResult)(result);
|
|
234
|
+
}
|
|
235
|
+
main();
|
|
236
|
+
//# sourceMappingURL=search-product-hunt.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"search-product-hunt.js","sourceRoot":"","sources":["../../../src/scripts/market-intel/search-product-hunt.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"search-yc-launch.d.ts","sourceRoot":"","sources":["../../../src/scripts/market-intel/search-yc-launch.ts"],"names":[],"mappings":"AAAA;;;;GAIG"}
|