aso-mcp 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/LICENSE +21 -0
- package/README.md +227 -0
- package/build/cache/sqlite-cache.d.ts +10 -0
- package/build/cache/sqlite-cache.d.ts.map +1 -0
- package/build/cache/sqlite-cache.js +51 -0
- package/build/cache/sqlite-cache.js.map +1 -0
- package/build/data-sources/app-store-connect.d.ts +12 -0
- package/build/data-sources/app-store-connect.d.ts.map +1 -0
- package/build/data-sources/app-store-connect.js +415 -0
- package/build/data-sources/app-store-connect.js.map +1 -0
- package/build/data-sources/app-store.d.ts +7 -0
- package/build/data-sources/app-store.d.ts.map +1 -0
- package/build/data-sources/app-store.js +28 -0
- package/build/data-sources/app-store.js.map +1 -0
- package/build/data-sources/aso-scoring.d.ts +15 -0
- package/build/data-sources/aso-scoring.d.ts.map +1 -0
- package/build/data-sources/aso-scoring.js +140 -0
- package/build/data-sources/aso-scoring.js.map +1 -0
- package/build/data-sources/custom-scoring.d.ts +52 -0
- package/build/data-sources/custom-scoring.d.ts.map +1 -0
- package/build/data-sources/custom-scoring.js +83 -0
- package/build/data-sources/custom-scoring.js.map +1 -0
- package/build/server.d.ts +3 -0
- package/build/server.d.ts.map +1 -0
- package/build/server.js +69 -0
- package/build/server.js.map +1 -0
- package/build/tools/analyze-competitors.d.ts +3 -0
- package/build/tools/analyze-competitors.d.ts.map +1 -0
- package/build/tools/analyze-competitors.js +112 -0
- package/build/tools/analyze-competitors.js.map +1 -0
- package/build/tools/analyze-reviews.d.ts +3 -0
- package/build/tools/analyze-reviews.d.ts.map +1 -0
- package/build/tools/analyze-reviews.js +188 -0
- package/build/tools/analyze-reviews.js.map +1 -0
- package/build/tools/clear-cache.d.ts +3 -0
- package/build/tools/clear-cache.d.ts.map +1 -0
- package/build/tools/clear-cache.js +32 -0
- package/build/tools/clear-cache.js.map +1 -0
- package/build/tools/connect-get-app.d.ts +3 -0
- package/build/tools/connect-get-app.d.ts.map +1 -0
- package/build/tools/connect-get-app.js +56 -0
- package/build/tools/connect-get-app.js.map +1 -0
- package/build/tools/connect-get-metadata.d.ts +3 -0
- package/build/tools/connect-get-metadata.d.ts.map +1 -0
- package/build/tools/connect-get-metadata.js +73 -0
- package/build/tools/connect-get-metadata.js.map +1 -0
- package/build/tools/connect-list-localizations.d.ts +3 -0
- package/build/tools/connect-list-localizations.d.ts.map +1 -0
- package/build/tools/connect-list-localizations.js +55 -0
- package/build/tools/connect-list-localizations.js.map +1 -0
- package/build/tools/connect-setup.d.ts +3 -0
- package/build/tools/connect-setup.d.ts.map +1 -0
- package/build/tools/connect-setup.js +47 -0
- package/build/tools/connect-setup.js.map +1 -0
- package/build/tools/connect-update-metadata.d.ts +3 -0
- package/build/tools/connect-update-metadata.d.ts.map +1 -0
- package/build/tools/connect-update-metadata.js +176 -0
- package/build/tools/connect-update-metadata.js.map +1 -0
- package/build/tools/discover-keywords.d.ts +3 -0
- package/build/tools/discover-keywords.d.ts.map +1 -0
- package/build/tools/discover-keywords.js +195 -0
- package/build/tools/discover-keywords.js.map +1 -0
- package/build/tools/generate-aso-brief.d.ts +3 -0
- package/build/tools/generate-aso-brief.d.ts.map +1 -0
- package/build/tools/generate-aso-brief.js +295 -0
- package/build/tools/generate-aso-brief.js.map +1 -0
- package/build/tools/get-app-details.d.ts +3 -0
- package/build/tools/get-app-details.d.ts.map +1 -0
- package/build/tools/get-app-details.js +89 -0
- package/build/tools/get-app-details.js.map +1 -0
- package/build/tools/get-aso-report.d.ts +3 -0
- package/build/tools/get-aso-report.d.ts.map +1 -0
- package/build/tools/get-aso-report.js +168 -0
- package/build/tools/get-aso-report.js.map +1 -0
- package/build/tools/keyword-gap.d.ts +3 -0
- package/build/tools/keyword-gap.d.ts.map +1 -0
- package/build/tools/keyword-gap.js +116 -0
- package/build/tools/keyword-gap.js.map +1 -0
- package/build/tools/localized-keywords.d.ts +3 -0
- package/build/tools/localized-keywords.d.ts.map +1 -0
- package/build/tools/localized-keywords.js +107 -0
- package/build/tools/localized-keywords.js.map +1 -0
- package/build/tools/optimize-metadata.d.ts +3 -0
- package/build/tools/optimize-metadata.d.ts.map +1 -0
- package/build/tools/optimize-metadata.js +156 -0
- package/build/tools/optimize-metadata.js.map +1 -0
- package/build/tools/search-keywords.d.ts +3 -0
- package/build/tools/search-keywords.d.ts.map +1 -0
- package/build/tools/search-keywords.js +65 -0
- package/build/tools/search-keywords.js.map +1 -0
- package/build/tools/suggest-keywords.d.ts +3 -0
- package/build/tools/suggest-keywords.d.ts.map +1 -0
- package/build/tools/suggest-keywords.js +71 -0
- package/build/tools/suggest-keywords.js.map +1 -0
- package/build/tools/track-ranking.d.ts +3 -0
- package/build/tools/track-ranking.d.ts.map +1 -0
- package/build/tools/track-ranking.js +94 -0
- package/build/tools/track-ranking.js.map +1 -0
- package/build/types/index.d.ts +151 -0
- package/build/types/index.d.ts.map +1 -0
- package/build/types/index.js +2 -0
- package/build/types/index.js.map +1 -0
- package/build/utils/constants.d.ts +40 -0
- package/build/utils/constants.d.ts.map +1 -0
- package/build/utils/constants.js +35 -0
- package/build/utils/constants.js.map +1 -0
- package/build/utils/formatters.d.ts +9 -0
- package/build/utils/formatters.d.ts.map +1 -0
- package/build/utils/formatters.js +37 -0
- package/build/utils/formatters.js.map +1 -0
- package/build/utils/localization.d.ts +7 -0
- package/build/utils/localization.d.ts.map +1 -0
- package/build/utils/localization.js +63 -0
- package/build/utils/localization.js.map +1 -0
- package/build/utils/rate-limiter.d.ts +7 -0
- package/build/utils/rate-limiter.d.ts.map +1 -0
- package/build/utils/rate-limiter.js +23 -0
- package/build/utils/rate-limiter.js.map +1 -0
- package/package.json +60 -0
|
@@ -0,0 +1,415 @@
|
|
|
1
|
+
import jwt from "jsonwebtoken";
|
|
2
|
+
import fs from "fs";
|
|
3
|
+
import path from "path";
|
|
4
|
+
import os from "os";
|
|
5
|
+
import { withRateLimit } from "../utils/rate-limiter.js";
|
|
6
|
+
import { RATE_LIMITS } from "../utils/constants.js";
|
|
7
|
+
import { countryToLocale } from "../utils/localization.js";
|
|
8
|
+
const RL = RATE_LIMITS["app-store-connect"];
|
|
9
|
+
const BASE_URL = "https://api.appstoreconnect.apple.com";
|
|
10
|
+
const CONFIG_DIR = path.join(os.homedir(), ".aso-mcp");
|
|
11
|
+
const CONFIG_PATH = path.join(CONFIG_DIR, "connect-config.json");
|
|
12
|
+
// ─── Config Management ───
|
|
13
|
+
export function loadConfig() {
|
|
14
|
+
// Try env vars first
|
|
15
|
+
const issuerId = process.env.ASC_ISSUER_ID;
|
|
16
|
+
const apiKeyId = process.env.ASC_KEY_ID;
|
|
17
|
+
const privateKeyPath = process.env.ASC_PRIVATE_KEY_PATH;
|
|
18
|
+
if (issuerId && apiKeyId && privateKeyPath) {
|
|
19
|
+
return { issuerId, apiKeyId, privateKeyPath };
|
|
20
|
+
}
|
|
21
|
+
// Try config file
|
|
22
|
+
try {
|
|
23
|
+
if (fs.existsSync(CONFIG_PATH)) {
|
|
24
|
+
const raw = fs.readFileSync(CONFIG_PATH, "utf-8");
|
|
25
|
+
return JSON.parse(raw);
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
catch {
|
|
29
|
+
// ignore
|
|
30
|
+
}
|
|
31
|
+
return null;
|
|
32
|
+
}
|
|
33
|
+
export function saveConfig(config) {
|
|
34
|
+
if (!fs.existsSync(CONFIG_DIR)) {
|
|
35
|
+
fs.mkdirSync(CONFIG_DIR, { recursive: true });
|
|
36
|
+
}
|
|
37
|
+
fs.writeFileSync(CONFIG_PATH, JSON.stringify(config, null, 2), "utf-8");
|
|
38
|
+
}
|
|
39
|
+
// ─── JWT Token Generation ───
|
|
40
|
+
function generateToken(config) {
|
|
41
|
+
const privateKey = fs.readFileSync(config.privateKeyPath, "utf-8");
|
|
42
|
+
const now = Math.floor(Date.now() / 1000);
|
|
43
|
+
const payload = {
|
|
44
|
+
iss: config.issuerId,
|
|
45
|
+
iat: now,
|
|
46
|
+
exp: now + 20 * 60,
|
|
47
|
+
aud: "appstoreconnect-v1",
|
|
48
|
+
};
|
|
49
|
+
return jwt.sign(payload, privateKey, {
|
|
50
|
+
algorithm: "ES256",
|
|
51
|
+
header: {
|
|
52
|
+
alg: "ES256",
|
|
53
|
+
kid: config.apiKeyId,
|
|
54
|
+
typ: "JWT",
|
|
55
|
+
},
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
// ─── HTTP Wrapper ───
|
|
59
|
+
async function apiRequest(config, endpoint, method = "GET", body) {
|
|
60
|
+
return withRateLimit("app-store-connect", RL, async () => {
|
|
61
|
+
const token = generateToken(config);
|
|
62
|
+
const url = `${BASE_URL}${endpoint}`;
|
|
63
|
+
const headers = {
|
|
64
|
+
Authorization: `Bearer ${token}`,
|
|
65
|
+
"Content-Type": "application/json",
|
|
66
|
+
};
|
|
67
|
+
const res = await fetch(url, {
|
|
68
|
+
method,
|
|
69
|
+
headers,
|
|
70
|
+
body: body ? JSON.stringify(body) : undefined,
|
|
71
|
+
});
|
|
72
|
+
if (!res.ok) {
|
|
73
|
+
const errorBody = await res.text();
|
|
74
|
+
let errorMsg = `App Store Connect API error: ${res.status} ${res.statusText}`;
|
|
75
|
+
try {
|
|
76
|
+
const parsed = JSON.parse(errorBody);
|
|
77
|
+
if (parsed.errors?.[0]?.detail) {
|
|
78
|
+
errorMsg += ` — ${parsed.errors[0].detail}`;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
catch {
|
|
82
|
+
// ignore parse error
|
|
83
|
+
}
|
|
84
|
+
throw new Error(errorMsg);
|
|
85
|
+
}
|
|
86
|
+
const text = await res.text();
|
|
87
|
+
return text ? JSON.parse(text) : null;
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
// ─── Public API Functions ───
|
|
91
|
+
export async function validateCredentials(config) {
|
|
92
|
+
// Check .p8 file exists
|
|
93
|
+
if (!fs.existsSync(config.privateKeyPath)) {
|
|
94
|
+
throw new Error(`Private key file not found: ${config.privateKeyPath}`);
|
|
95
|
+
}
|
|
96
|
+
// Test API call — list apps (limit 1)
|
|
97
|
+
await apiRequest(config, "/v1/apps?limit=1");
|
|
98
|
+
return true;
|
|
99
|
+
}
|
|
100
|
+
export async function getApp(config, bundleId) {
|
|
101
|
+
// Find app by bundle ID
|
|
102
|
+
const appsResponse = await apiRequest(config, `/v1/apps?filter[bundleId]=${encodeURIComponent(bundleId)}&fields[apps]=name,bundleId,primaryLocale&limit=1`);
|
|
103
|
+
const app = appsResponse.data?.[0];
|
|
104
|
+
if (!app) {
|
|
105
|
+
throw new Error(`App not found for bundle ID: ${bundleId}`);
|
|
106
|
+
}
|
|
107
|
+
// Get latest editable version (PREPARE_FOR_SUBMISSION)
|
|
108
|
+
let versionId = null;
|
|
109
|
+
let versionState = null;
|
|
110
|
+
try {
|
|
111
|
+
const versionsResponse = await apiRequest(config, `/v1/apps/${app.id}/appStoreVersions?filter[appStoreState]=PREPARE_FOR_SUBMISSION&limit=1`);
|
|
112
|
+
const version = versionsResponse.data?.[0];
|
|
113
|
+
if (version) {
|
|
114
|
+
versionId = version.id;
|
|
115
|
+
versionState = version.attributes?.appStoreState ?? null;
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
catch {
|
|
119
|
+
// No editable version — not a fatal error
|
|
120
|
+
}
|
|
121
|
+
return {
|
|
122
|
+
id: app.id,
|
|
123
|
+
bundleId: app.attributes.bundleId,
|
|
124
|
+
name: app.attributes.name,
|
|
125
|
+
primaryLocale: app.attributes.primaryLocale ?? "en-US",
|
|
126
|
+
versionId,
|
|
127
|
+
versionState,
|
|
128
|
+
};
|
|
129
|
+
}
|
|
130
|
+
export async function getMetadata(config, appId, locale = "tr") {
|
|
131
|
+
const resolvedLocale = countryToLocale(locale);
|
|
132
|
+
// Fetch app info localizations (subtitle)
|
|
133
|
+
let subtitle = null;
|
|
134
|
+
let appInfoLocalizationId = null;
|
|
135
|
+
try {
|
|
136
|
+
const appInfosResponse = await apiRequest(config, `/v1/apps/${appId}/appInfos`);
|
|
137
|
+
const allInfos = appInfosResponse.data ?? [];
|
|
138
|
+
const editableInfo = allInfos.find((info) => info.attributes?.appStoreState !== "READY_FOR_SALE");
|
|
139
|
+
const appInfoId = editableInfo?.id ?? allInfos[0]?.id;
|
|
140
|
+
if (appInfoId) {
|
|
141
|
+
const locResponse = await apiRequest(config, `/v1/appInfos/${appInfoId}/appInfoLocalizations?filter[locale]=${resolvedLocale}`);
|
|
142
|
+
const loc = locResponse.data?.[0];
|
|
143
|
+
if (loc) {
|
|
144
|
+
appInfoLocalizationId = loc.id;
|
|
145
|
+
subtitle = loc.attributes?.subtitle ?? null;
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
catch {
|
|
150
|
+
// Subtitle not available
|
|
151
|
+
}
|
|
152
|
+
// Fetch version localizations (keywords, description, etc.)
|
|
153
|
+
let keywords = null;
|
|
154
|
+
let description = null;
|
|
155
|
+
let promotionalText = null;
|
|
156
|
+
let whatsNew = null;
|
|
157
|
+
let supportUrl = null;
|
|
158
|
+
let marketingUrl = null;
|
|
159
|
+
let versionLocalizationId = null;
|
|
160
|
+
try {
|
|
161
|
+
// Get the editable version
|
|
162
|
+
const versionsResponse = await apiRequest(config, `/v1/apps/${appId}/appStoreVersions?filter[appStoreState]=PREPARE_FOR_SUBMISSION&limit=1`);
|
|
163
|
+
const versionId = versionsResponse.data?.[0]?.id;
|
|
164
|
+
if (versionId) {
|
|
165
|
+
const verLocResponse = await apiRequest(config, `/v1/appStoreVersions/${versionId}/appStoreVersionLocalizations?filter[locale]=${resolvedLocale}`);
|
|
166
|
+
const verLoc = verLocResponse.data?.[0];
|
|
167
|
+
if (verLoc) {
|
|
168
|
+
versionLocalizationId = verLoc.id;
|
|
169
|
+
keywords = verLoc.attributes?.keywords ?? null;
|
|
170
|
+
description = verLoc.attributes?.description ?? null;
|
|
171
|
+
promotionalText = verLoc.attributes?.promotionalText ?? null;
|
|
172
|
+
whatsNew = verLoc.attributes?.whatsNew ?? null;
|
|
173
|
+
supportUrl = verLoc.attributes?.supportUrl ?? null;
|
|
174
|
+
marketingUrl = verLoc.attributes?.marketingUrl ?? null;
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
catch {
|
|
179
|
+
// Version localizations not available
|
|
180
|
+
}
|
|
181
|
+
return {
|
|
182
|
+
locale: resolvedLocale,
|
|
183
|
+
subtitle,
|
|
184
|
+
subtitleLength: subtitle?.length ?? 0,
|
|
185
|
+
keywords,
|
|
186
|
+
keywordsLength: keywords?.length ?? 0,
|
|
187
|
+
description,
|
|
188
|
+
descriptionLength: description?.length ?? 0,
|
|
189
|
+
promotionalText,
|
|
190
|
+
promotionalTextLength: promotionalText?.length ?? 0,
|
|
191
|
+
whatsNew,
|
|
192
|
+
whatsNewLength: whatsNew?.length ?? 0,
|
|
193
|
+
supportUrl,
|
|
194
|
+
marketingUrl,
|
|
195
|
+
appInfoLocalizationId,
|
|
196
|
+
versionLocalizationId,
|
|
197
|
+
};
|
|
198
|
+
}
|
|
199
|
+
// ─── Helper: Get App Info ID ───
|
|
200
|
+
async function getAppInfoId(config, appId) {
|
|
201
|
+
try {
|
|
202
|
+
const response = await apiRequest(config, `/v1/apps/${appId}/appInfos`);
|
|
203
|
+
const appInfos = response.data ?? [];
|
|
204
|
+
// Prefer editable appInfo (not READY_FOR_SALE) for creating new localizations
|
|
205
|
+
const editable = appInfos.find((info) => info.attributes?.appStoreState !== "READY_FOR_SALE");
|
|
206
|
+
return editable?.id ?? appInfos[0]?.id ?? null;
|
|
207
|
+
}
|
|
208
|
+
catch {
|
|
209
|
+
return null;
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
// ─── Helper: Get Editable Version ID ───
|
|
213
|
+
async function getEditableVersionId(config, appId) {
|
|
214
|
+
try {
|
|
215
|
+
const response = await apiRequest(config, `/v1/apps/${appId}/appStoreVersions?filter[appStoreState]=PREPARE_FOR_SUBMISSION&limit=1`);
|
|
216
|
+
return response.data?.[0]?.id ?? null;
|
|
217
|
+
}
|
|
218
|
+
catch {
|
|
219
|
+
return null;
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
// ─── Create Localization (POST) ───
|
|
223
|
+
async function createAppInfoLocalization(config, appInfoId, locale, attributes) {
|
|
224
|
+
const response = await apiRequest(config, "/v1/appInfoLocalizations", "POST", {
|
|
225
|
+
data: {
|
|
226
|
+
type: "appInfoLocalizations",
|
|
227
|
+
attributes: {
|
|
228
|
+
locale,
|
|
229
|
+
...attributes,
|
|
230
|
+
},
|
|
231
|
+
relationships: {
|
|
232
|
+
appInfo: {
|
|
233
|
+
data: { type: "appInfos", id: appInfoId },
|
|
234
|
+
},
|
|
235
|
+
},
|
|
236
|
+
},
|
|
237
|
+
});
|
|
238
|
+
return response.data.id;
|
|
239
|
+
}
|
|
240
|
+
async function createVersionLocalization(config, versionId, locale, attributes) {
|
|
241
|
+
const response = await apiRequest(config, "/v1/appStoreVersionLocalizations", "POST", {
|
|
242
|
+
data: {
|
|
243
|
+
type: "appStoreVersionLocalizations",
|
|
244
|
+
attributes: {
|
|
245
|
+
locale,
|
|
246
|
+
...attributes,
|
|
247
|
+
},
|
|
248
|
+
relationships: {
|
|
249
|
+
appStoreVersion: {
|
|
250
|
+
data: { type: "appStoreVersions", id: versionId },
|
|
251
|
+
},
|
|
252
|
+
},
|
|
253
|
+
},
|
|
254
|
+
});
|
|
255
|
+
return response.data.id;
|
|
256
|
+
}
|
|
257
|
+
// ─── Decode HTML Entities ───
|
|
258
|
+
function decodeHtmlEntities(text) {
|
|
259
|
+
return text
|
|
260
|
+
.replace(/&/g, "&")
|
|
261
|
+
.replace(/</g, "<")
|
|
262
|
+
.replace(/>/g, ">")
|
|
263
|
+
.replace(/"/g, '"')
|
|
264
|
+
.replace(/'/g, "'")
|
|
265
|
+
.replace(/'/g, "'")
|
|
266
|
+
.replace(///g, "/");
|
|
267
|
+
}
|
|
268
|
+
function sanitizeUpdates(updates) {
|
|
269
|
+
const sanitized = {};
|
|
270
|
+
for (const [key, value] of Object.entries(updates)) {
|
|
271
|
+
sanitized[key] =
|
|
272
|
+
value !== undefined ? decodeHtmlEntities(value) : undefined;
|
|
273
|
+
}
|
|
274
|
+
return sanitized;
|
|
275
|
+
}
|
|
276
|
+
// ─── Update Metadata ───
|
|
277
|
+
export async function updateMetadata(config, appId, locale = "tr", updates) {
|
|
278
|
+
const resolvedLocale = countryToLocale(locale);
|
|
279
|
+
// Sanitize: decode any HTML entities before sending to ASC API
|
|
280
|
+
updates = sanitizeUpdates(updates);
|
|
281
|
+
// Get current metadata
|
|
282
|
+
const before = await getMetadata(config, appId, locale);
|
|
283
|
+
// ─── Update/Create App Info Localization (name + subtitle) ───
|
|
284
|
+
const appInfoAttrs = {};
|
|
285
|
+
if (updates.name !== undefined)
|
|
286
|
+
appInfoAttrs.name = updates.name;
|
|
287
|
+
if (updates.subtitle !== undefined)
|
|
288
|
+
appInfoAttrs.subtitle = updates.subtitle;
|
|
289
|
+
if (Object.keys(appInfoAttrs).length > 0) {
|
|
290
|
+
if (before.appInfoLocalizationId) {
|
|
291
|
+
// PATCH existing
|
|
292
|
+
await apiRequest(config, `/v1/appInfoLocalizations/${before.appInfoLocalizationId}`, "PATCH", {
|
|
293
|
+
data: {
|
|
294
|
+
type: "appInfoLocalizations",
|
|
295
|
+
id: before.appInfoLocalizationId,
|
|
296
|
+
attributes: appInfoAttrs,
|
|
297
|
+
},
|
|
298
|
+
});
|
|
299
|
+
}
|
|
300
|
+
else {
|
|
301
|
+
// CREATE new app info localization — 'name' is required by the API
|
|
302
|
+
const appInfoId = await getAppInfoId(config, appId);
|
|
303
|
+
if (!appInfoId) {
|
|
304
|
+
throw new Error(`Could not find appInfo for app "${appId}". Cannot create localization.`);
|
|
305
|
+
}
|
|
306
|
+
if (!appInfoAttrs.name) {
|
|
307
|
+
throw new Error(`Cannot create new localization for "${resolvedLocale}" without a name. ` +
|
|
308
|
+
`Provide the 'name' parameter when adding a new locale.`);
|
|
309
|
+
}
|
|
310
|
+
await createAppInfoLocalization(config, appInfoId, resolvedLocale, appInfoAttrs);
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
// ─── Update/Create Version Localization (keywords, description, etc.) ───
|
|
314
|
+
const versionFields = {};
|
|
315
|
+
if (updates.keywords !== undefined)
|
|
316
|
+
versionFields.keywords = updates.keywords;
|
|
317
|
+
if (updates.description !== undefined)
|
|
318
|
+
versionFields.description = updates.description;
|
|
319
|
+
if (updates.promotionalText !== undefined)
|
|
320
|
+
versionFields.promotionalText = updates.promotionalText;
|
|
321
|
+
if (updates.whatsNew !== undefined)
|
|
322
|
+
versionFields.whatsNew = updates.whatsNew;
|
|
323
|
+
if (updates.supportUrl !== undefined)
|
|
324
|
+
versionFields.supportUrl = updates.supportUrl;
|
|
325
|
+
if (updates.marketingUrl !== undefined)
|
|
326
|
+
versionFields.marketingUrl = updates.marketingUrl;
|
|
327
|
+
if (Object.keys(versionFields).length > 0) {
|
|
328
|
+
if (before.versionLocalizationId) {
|
|
329
|
+
// PATCH existing
|
|
330
|
+
await apiRequest(config, `/v1/appStoreVersionLocalizations/${before.versionLocalizationId}`, "PATCH", {
|
|
331
|
+
data: {
|
|
332
|
+
type: "appStoreVersionLocalizations",
|
|
333
|
+
id: before.versionLocalizationId,
|
|
334
|
+
attributes: versionFields,
|
|
335
|
+
},
|
|
336
|
+
});
|
|
337
|
+
}
|
|
338
|
+
else {
|
|
339
|
+
// CREATE new version localization
|
|
340
|
+
const versionId = await getEditableVersionId(config, appId);
|
|
341
|
+
if (!versionId) {
|
|
342
|
+
throw new Error(`No PREPARE_FOR_SUBMISSION version found for app "${appId}". ` +
|
|
343
|
+
`Create a new version in App Store Connect first.`);
|
|
344
|
+
}
|
|
345
|
+
await createVersionLocalization(config, versionId, resolvedLocale, versionFields);
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
// Get updated metadata
|
|
349
|
+
const after = await getMetadata(config, appId, locale);
|
|
350
|
+
return { before, after };
|
|
351
|
+
}
|
|
352
|
+
export async function listLocalizations(config, appId) {
|
|
353
|
+
const summaries = [];
|
|
354
|
+
const localeMap = new Map();
|
|
355
|
+
// Get app info localizations
|
|
356
|
+
try {
|
|
357
|
+
const appInfosResponse = await apiRequest(config, `/v1/apps/${appId}/appInfos`);
|
|
358
|
+
const allInfos = appInfosResponse.data ?? [];
|
|
359
|
+
const editableInfo = allInfos.find((info) => info.attributes?.appStoreState !== "READY_FOR_SALE");
|
|
360
|
+
const appInfoId = editableInfo?.id ?? allInfos[0]?.id;
|
|
361
|
+
if (appInfoId) {
|
|
362
|
+
const locResponse = await apiRequest(config, `/v1/appInfos/${appInfoId}/appInfoLocalizations`);
|
|
363
|
+
for (const loc of locResponse.data ?? []) {
|
|
364
|
+
const locale = loc.attributes?.locale;
|
|
365
|
+
if (!locale)
|
|
366
|
+
continue;
|
|
367
|
+
localeMap.set(locale, {
|
|
368
|
+
locale,
|
|
369
|
+
hasSubtitle: !!loc.attributes?.subtitle,
|
|
370
|
+
appInfoLocalizationId: loc.id,
|
|
371
|
+
});
|
|
372
|
+
}
|
|
373
|
+
}
|
|
374
|
+
}
|
|
375
|
+
catch {
|
|
376
|
+
// ignore
|
|
377
|
+
}
|
|
378
|
+
// Get version localizations
|
|
379
|
+
try {
|
|
380
|
+
const versionsResponse = await apiRequest(config, `/v1/apps/${appId}/appStoreVersions?filter[appStoreState]=PREPARE_FOR_SUBMISSION&limit=1`);
|
|
381
|
+
const versionId = versionsResponse.data?.[0]?.id;
|
|
382
|
+
if (versionId) {
|
|
383
|
+
const verLocResponse = await apiRequest(config, `/v1/appStoreVersions/${versionId}/appStoreVersionLocalizations`);
|
|
384
|
+
for (const loc of verLocResponse.data ?? []) {
|
|
385
|
+
const locale = loc.attributes?.locale;
|
|
386
|
+
if (!locale)
|
|
387
|
+
continue;
|
|
388
|
+
const existing = localeMap.get(locale) ?? { locale };
|
|
389
|
+
existing.hasKeywords = !!loc.attributes?.keywords;
|
|
390
|
+
existing.hasDescription = !!loc.attributes?.description;
|
|
391
|
+
existing.hasPromotionalText = !!loc.attributes?.promotionalText;
|
|
392
|
+
existing.hasWhatsNew = !!loc.attributes?.whatsNew;
|
|
393
|
+
existing.versionLocalizationId = loc.id;
|
|
394
|
+
localeMap.set(locale, existing);
|
|
395
|
+
}
|
|
396
|
+
}
|
|
397
|
+
}
|
|
398
|
+
catch {
|
|
399
|
+
// ignore
|
|
400
|
+
}
|
|
401
|
+
for (const [, entry] of localeMap) {
|
|
402
|
+
summaries.push({
|
|
403
|
+
locale: entry.locale,
|
|
404
|
+
hasSubtitle: entry.hasSubtitle ?? false,
|
|
405
|
+
hasKeywords: entry.hasKeywords ?? false,
|
|
406
|
+
hasDescription: entry.hasDescription ?? false,
|
|
407
|
+
hasPromotionalText: entry.hasPromotionalText ?? false,
|
|
408
|
+
hasWhatsNew: entry.hasWhatsNew ?? false,
|
|
409
|
+
appInfoLocalizationId: entry.appInfoLocalizationId ?? null,
|
|
410
|
+
versionLocalizationId: entry.versionLocalizationId ?? null,
|
|
411
|
+
});
|
|
412
|
+
}
|
|
413
|
+
return summaries.sort((a, b) => a.locale.localeCompare(b.locale));
|
|
414
|
+
}
|
|
415
|
+
//# sourceMappingURL=app-store-connect.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"app-store-connect.js","sourceRoot":"","sources":["../../src/data-sources/app-store-connect.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export declare function searchApps(term: string, country?: string, num?: number): Promise<any[]>;
|
|
2
|
+
export declare function getAppDetails(appId: string | number, country?: string): Promise<any>;
|
|
3
|
+
export declare function getSimilarApps(appId: number, country?: string): Promise<any[]>;
|
|
4
|
+
export declare function getReviews(appId: number, country?: string, page?: number): Promise<any[]>;
|
|
5
|
+
export declare function getRatings(appId: number, country?: string): Promise<any>;
|
|
6
|
+
export declare function getSuggestions(term: string): Promise<string[]>;
|
|
7
|
+
//# sourceMappingURL=app-store.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"app-store.d.ts","sourceRoot":"","sources":["../../src/data-sources/app-store.ts"],"names":[],"mappings":"AAMA,wBAAsB,UAAU,CAC9B,IAAI,EAAE,MAAM,EACZ,OAAO,GAAE,MAAa,EACtB,GAAG,GAAE,MAAW,GACf,OAAO,CAAC,GAAG,EAAE,CAAC,CAIhB;AAED,wBAAsB,aAAa,CACjC,KAAK,EAAE,MAAM,GAAG,MAAM,EACtB,OAAO,GAAE,MAAa,GACrB,OAAO,CAAC,GAAG,CAAC,CAOd;AAED,wBAAsB,cAAc,CAClC,KAAK,EAAE,MAAM,EACb,OAAO,GAAE,MAAa,GACrB,OAAO,CAAC,GAAG,EAAE,CAAC,CAIhB;AAED,wBAAsB,UAAU,CAC9B,KAAK,EAAE,MAAM,EACb,OAAO,GAAE,MAAa,EACtB,IAAI,GAAE,MAAU,GACf,OAAO,CAAC,GAAG,EAAE,CAAC,CAIhB;AAED,wBAAsB,UAAU,CAC9B,KAAK,EAAE,MAAM,EACb,OAAO,GAAE,MAAa,GACrB,OAAO,CAAC,GAAG,CAAC,CAId;AAED,wBAAsB,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,CAIpE"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import store from "app-store-scraper";
|
|
2
|
+
import { withRateLimit } from "../utils/rate-limiter.js";
|
|
3
|
+
import { RATE_LIMITS } from "../utils/constants.js";
|
|
4
|
+
const RL = RATE_LIMITS["app-store-scraper"];
|
|
5
|
+
export async function searchApps(term, country = "tr", num = 10) {
|
|
6
|
+
return withRateLimit("app-store-scraper", RL, () => store.search({ term, num, country }));
|
|
7
|
+
}
|
|
8
|
+
export async function getAppDetails(appId, country = "tr") {
|
|
9
|
+
return withRateLimit("app-store-scraper", RL, () => {
|
|
10
|
+
if (typeof appId === "string" && !/^\d+$/.test(appId)) {
|
|
11
|
+
return store.app({ appId, country, ratings: true });
|
|
12
|
+
}
|
|
13
|
+
return store.app({ id: Number(appId), country, ratings: true });
|
|
14
|
+
});
|
|
15
|
+
}
|
|
16
|
+
export async function getSimilarApps(appId, country = "tr") {
|
|
17
|
+
return withRateLimit("app-store-scraper", RL, () => store.similar({ id: appId, country }));
|
|
18
|
+
}
|
|
19
|
+
export async function getReviews(appId, country = "tr", page = 1) {
|
|
20
|
+
return withRateLimit("app-store-scraper", RL, () => store.reviews({ id: appId, country, page, sort: store.sort.RECENT }));
|
|
21
|
+
}
|
|
22
|
+
export async function getRatings(appId, country = "tr") {
|
|
23
|
+
return withRateLimit("app-store-scraper", RL, () => store.ratings({ id: appId, country }));
|
|
24
|
+
}
|
|
25
|
+
export async function getSuggestions(term) {
|
|
26
|
+
return withRateLimit("app-store-scraper", RL, () => store.suggest({ term }));
|
|
27
|
+
}
|
|
28
|
+
//# sourceMappingURL=app-store.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"app-store.js","sourceRoot":"","sources":["../../src/data-sources/app-store.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,mBAAmB,CAAC;AACtC,OAAO,EAAE,aAAa,EAAE,MAAM,0BAA0B,CAAC;AACzD,OAAO,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAC;AAEpD,MAAM,EAAE,GAAG,WAAW,CAAC,mBAAmB,CAAC,CAAC;AAE5C,MAAM,CAAC,KAAK,UAAU,UAAU,CAC9B,IAAY,EACZ,UAAkB,IAAI,EACtB,MAAc,EAAE;IAEhB,OAAO,aAAa,CAAC,mBAAmB,EAAE,EAAE,EAAE,GAAG,EAAE,CACjD,KAAK,CAAC,MAAM,CAAC,EAAE,IAAI,EAAE,GAAG,EAAE,OAAO,EAAE,CAAC,CACrC,CAAC;AACJ,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,aAAa,CACjC,KAAsB,EACtB,UAAkB,IAAI;IAEtB,OAAO,aAAa,CAAC,mBAAmB,EAAE,EAAE,EAAE,GAAG,EAAE;QACjD,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC;YACtD,OAAO,KAAK,CAAC,GAAG,CAAC,EAAE,KAAK,EAAE,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC,CAAC;QACtD,CAAC;QACD,OAAO,KAAK,CAAC,GAAG,CAAC,EAAE,EAAE,EAAE,MAAM,CAAC,KAAK,CAAC,EAAE,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC,CAAC;IAClE,CAAC,CAAC,CAAC;AACL,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,cAAc,CAClC,KAAa,EACb,UAAkB,IAAI;IAEtB,OAAO,aAAa,CAAC,mBAAmB,EAAE,EAAE,EAAE,GAAG,EAAE,CACjD,KAAK,CAAC,OAAO,CAAC,EAAE,EAAE,EAAE,KAAK,EAAE,OAAO,EAAE,CAAC,CACtC,CAAC;AACJ,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,UAAU,CAC9B,KAAa,EACb,UAAkB,IAAI,EACtB,OAAe,CAAC;IAEhB,OAAO,aAAa,CAAC,mBAAmB,EAAE,EAAE,EAAE,GAAG,EAAE,CACjD,KAAK,CAAC,OAAO,CAAC,EAAE,EAAE,EAAE,KAAK,EAAE,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,CAAC,IAAI,CAAC,MAAM,EAAE,CAAC,CACrE,CAAC;AACJ,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,UAAU,CAC9B,KAAa,EACb,UAAkB,IAAI;IAEtB,OAAO,aAAa,CAAC,mBAAmB,EAAE,EAAE,EAAE,GAAG,EAAE,CACjD,KAAK,CAAC,OAAO,CAAC,EAAE,EAAE,EAAE,KAAK,EAAE,OAAO,EAAE,CAAC,CACtC,CAAC;AACJ,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,cAAc,CAAC,IAAY;IAC/C,OAAO,aAAa,CAAC,mBAAmB,EAAE,EAAE,EAAE,GAAG,EAAE,CACjD,KAAK,CAAC,OAAO,CAAC,EAAE,IAAI,EAAE,CAAC,CACxB,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
export declare function getScores(keyword: string, country?: string): Promise<{
|
|
2
|
+
traffic: number;
|
|
3
|
+
difficulty: number;
|
|
4
|
+
}>;
|
|
5
|
+
export declare function suggestKeywords(appId: string, strategy: "category" | "similar" | "competition", country?: string, num?: number): Promise<string[]>;
|
|
6
|
+
/**
|
|
7
|
+
* Score multiple keywords in parallel with a concurrency limit.
|
|
8
|
+
* Returns results in the same order as input keywords.
|
|
9
|
+
*/
|
|
10
|
+
export declare function batchGetScores(keywords: string[], country?: string, concurrency?: number): Promise<{
|
|
11
|
+
keyword: string;
|
|
12
|
+
traffic: number;
|
|
13
|
+
difficulty: number;
|
|
14
|
+
}[]>;
|
|
15
|
+
//# sourceMappingURL=aso-scoring.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"aso-scoring.d.ts","sourceRoot":"","sources":["../../src/data-sources/aso-scoring.ts"],"names":[],"mappings":"AA8DA,wBAAsB,SAAS,CAC7B,OAAO,EAAE,MAAM,EACf,OAAO,GAAE,MAAa,GACrB,OAAO,CAAC;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,UAAU,EAAE,MAAM,CAAA;CAAE,CAAC,CAoBlD;AAED,wBAAsB,eAAe,CACnC,KAAK,EAAE,MAAM,EACb,QAAQ,EAAE,UAAU,GAAG,SAAS,GAAG,aAAa,EAChD,OAAO,GAAE,MAAa,EACtB,GAAG,GAAE,MAAW,GACf,OAAO,CAAC,MAAM,EAAE,CAAC,CA0BnB;AAED;;;GAGG;AACH,wBAAsB,cAAc,CAClC,QAAQ,EAAE,MAAM,EAAE,EAClB,OAAO,GAAE,MAAa,EACtB,WAAW,GAAE,MAAU,GACtB,OAAO,CAAC;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC;IAAC,UAAU,EAAE,MAAM,CAAA;CAAE,EAAE,CAAC,CAmBrE"}
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
import { withRateLimit } from "../utils/rate-limiter.js";
|
|
2
|
+
import { RATE_LIMITS } from "../utils/constants.js";
|
|
3
|
+
import { searchApps, getSuggestions } from "./app-store.js";
|
|
4
|
+
import { calculateCompetitiveScore } from "./custom-scoring.js";
|
|
5
|
+
const RL = RATE_LIMITS["aso-scores"];
|
|
6
|
+
let asoModule = null;
|
|
7
|
+
let asoAvailable = true;
|
|
8
|
+
async function getAsoModule() {
|
|
9
|
+
if (!asoModule) {
|
|
10
|
+
const mod = await import("aso");
|
|
11
|
+
asoModule = mod.default || mod;
|
|
12
|
+
}
|
|
13
|
+
return asoModule;
|
|
14
|
+
}
|
|
15
|
+
async function getClient(country = "tr") {
|
|
16
|
+
const asoFn = await getAsoModule();
|
|
17
|
+
return asoFn("itunes", { country });
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* If the aso package returns 503, calculate our own scores from search results.
|
|
21
|
+
* Traffic: estimated from search result count + average review count
|
|
22
|
+
* Difficulty: calculated from rating/review strength of top-ranking apps
|
|
23
|
+
*/
|
|
24
|
+
async function fallbackScores(keyword, country) {
|
|
25
|
+
const results = await searchApps(keyword, country, 20);
|
|
26
|
+
if (results.length === 0) {
|
|
27
|
+
return { traffic: 1, difficulty: 1 };
|
|
28
|
+
}
|
|
29
|
+
// Traffic estimate: based on result quality
|
|
30
|
+
const avgReviews = results.reduce((s, a) => s + (a.reviews || 0), 0) /
|
|
31
|
+
results.length;
|
|
32
|
+
const traffic = Math.min(10, Math.max(1, Math.log10(Math.max(1, avgReviews)) * 1.8));
|
|
33
|
+
// Difficulty: competitive score
|
|
34
|
+
const difficulty = calculateCompetitiveScore(results.slice(0, 10).map((a) => ({
|
|
35
|
+
rating: a.score || 0,
|
|
36
|
+
reviews: a.reviews || 0,
|
|
37
|
+
free: a.free ?? true,
|
|
38
|
+
})));
|
|
39
|
+
return {
|
|
40
|
+
traffic: Math.round(traffic * 10) / 10,
|
|
41
|
+
difficulty: Math.round(difficulty * 10) / 10,
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
export async function getScores(keyword, country = "tr") {
|
|
45
|
+
// If the aso package has previously failed, use fallback directly
|
|
46
|
+
if (!asoAvailable) {
|
|
47
|
+
return fallbackScores(keyword, country);
|
|
48
|
+
}
|
|
49
|
+
return withRateLimit("aso-scores", RL, async () => {
|
|
50
|
+
try {
|
|
51
|
+
const client = await getClient(country);
|
|
52
|
+
const result = await client.scores(keyword);
|
|
53
|
+
return {
|
|
54
|
+
traffic: result.traffic ?? 0,
|
|
55
|
+
difficulty: result.difficulty ?? 0,
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
catch {
|
|
59
|
+
// aso package not working, switch to fallback
|
|
60
|
+
asoAvailable = false;
|
|
61
|
+
return fallbackScores(keyword, country);
|
|
62
|
+
}
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
export async function suggestKeywords(appId, strategy, country = "tr", num = 20) {
|
|
66
|
+
// If aso package unavailable, use App Store suggest + search based fallback
|
|
67
|
+
if (!asoAvailable) {
|
|
68
|
+
return fallbackSuggest(appId, strategy, country, num);
|
|
69
|
+
}
|
|
70
|
+
return withRateLimit("aso-scores", RL, async () => {
|
|
71
|
+
try {
|
|
72
|
+
const client = await getClient(country);
|
|
73
|
+
const strategyMap = {
|
|
74
|
+
category: client.CATEGORY,
|
|
75
|
+
similar: client.SIMILAR,
|
|
76
|
+
competition: client.COMPETITION,
|
|
77
|
+
};
|
|
78
|
+
return await client.suggest({
|
|
79
|
+
strategy: strategyMap[strategy],
|
|
80
|
+
appId,
|
|
81
|
+
num,
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
catch {
|
|
85
|
+
asoAvailable = false;
|
|
86
|
+
return fallbackSuggest(appId, strategy, country, num);
|
|
87
|
+
}
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* Score multiple keywords in parallel with a concurrency limit.
|
|
92
|
+
* Returns results in the same order as input keywords.
|
|
93
|
+
*/
|
|
94
|
+
export async function batchGetScores(keywords, country = "tr", concurrency = 5) {
|
|
95
|
+
const results = [];
|
|
96
|
+
for (let i = 0; i < keywords.length; i += concurrency) {
|
|
97
|
+
const batch = keywords.slice(i, i + concurrency);
|
|
98
|
+
const batchResults = await Promise.all(batch.map(async (kw) => {
|
|
99
|
+
try {
|
|
100
|
+
const scores = await getScores(kw, country);
|
|
101
|
+
return { keyword: kw, traffic: scores.traffic, difficulty: scores.difficulty };
|
|
102
|
+
}
|
|
103
|
+
catch {
|
|
104
|
+
return { keyword: kw, traffic: 0, difficulty: 0 };
|
|
105
|
+
}
|
|
106
|
+
}));
|
|
107
|
+
results.push(...batchResults);
|
|
108
|
+
}
|
|
109
|
+
return results;
|
|
110
|
+
}
|
|
111
|
+
/**
|
|
112
|
+
* When the aso package is unavailable, generate keyword suggestions via app-store-scraper.
|
|
113
|
+
* Sends keywords from the app's title + description to App Store suggest.
|
|
114
|
+
*/
|
|
115
|
+
async function fallbackSuggest(appId, _strategy, country, num) {
|
|
116
|
+
try {
|
|
117
|
+
// App Store autocomplete suggestions
|
|
118
|
+
const suggestions = await getSuggestions(appId);
|
|
119
|
+
if (suggestions.length >= num) {
|
|
120
|
+
return suggestions.slice(0, num);
|
|
121
|
+
}
|
|
122
|
+
// Additionally pull suggestions from simple keywords
|
|
123
|
+
const extraTerms = appId.split(/[.\-_]/).filter((t) => t.length > 2);
|
|
124
|
+
const allSuggestions = new Set(suggestions);
|
|
125
|
+
for (const term of extraTerms.slice(0, 3)) {
|
|
126
|
+
try {
|
|
127
|
+
const more = await getSuggestions(term);
|
|
128
|
+
more.forEach((s) => allSuggestions.add(s));
|
|
129
|
+
}
|
|
130
|
+
catch {
|
|
131
|
+
// continue
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
return [...allSuggestions].slice(0, num);
|
|
135
|
+
}
|
|
136
|
+
catch {
|
|
137
|
+
return [];
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
//# sourceMappingURL=aso-scoring.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"aso-scoring.js","sourceRoot":"","sources":["../../src/data-sources/aso-scoring.ts"],"names":[],"mappings":"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"}
|