@yoonion/mimi-seed-mcp 0.19.2 → 0.19.3
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/checks/billing.d.ts +5 -1
- package/dist/checks/billing.js +133 -3
- package/dist/registers/checks.js +1 -1
- package/package.json +1 -1
package/dist/checks/billing.d.ts
CHANGED
|
@@ -4,7 +4,7 @@ export interface BillingEvidence {
|
|
|
4
4
|
module: string;
|
|
5
5
|
version?: string;
|
|
6
6
|
expression?: string;
|
|
7
|
-
source: 'literal' | 'variable' | 'version_catalog' | 'unresolved';
|
|
7
|
+
source: 'literal' | 'variable' | 'version_catalog' | 'transitive' | 'unresolved';
|
|
8
8
|
}
|
|
9
9
|
export interface BillingComplianceResult {
|
|
10
10
|
projectPath: string;
|
|
@@ -33,4 +33,8 @@ export interface BillingComplianceResult {
|
|
|
33
33
|
automaticExecution: false;
|
|
34
34
|
};
|
|
35
35
|
}
|
|
36
|
+
export declare function billingVersionFromPom(pom: string): {
|
|
37
|
+
module: string;
|
|
38
|
+
version: string;
|
|
39
|
+
} | null;
|
|
36
40
|
export declare function checkBillingCompliance(projectPath: string, now?: Date): Promise<BillingComplianceResult>;
|
package/dist/checks/billing.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import fs from 'node:fs/promises';
|
|
2
2
|
import path from 'node:path';
|
|
3
|
+
import { fetchWithTimeout } from '../lib/http.js';
|
|
3
4
|
const BILLING_MODULE = /com\.android\.billingclient:billing(?:-ktx)?/;
|
|
4
5
|
const LITERAL_DEPENDENCY = /com\.android\.billingclient:billing(?:-ktx)?:([0-9]+(?:\.[0-9A-Za-z_-]+){0,3})/g;
|
|
5
6
|
const VARIABLE_DEPENDENCY = /com\.android\.billingclient:billing(?:-ktx)?:\$\{?([A-Za-z_][A-Za-z0-9_.-]*)\}?/g;
|
|
@@ -41,7 +42,8 @@ async function walk(root, maxDepth = 7) {
|
|
|
41
42
|
else if (entry.isFile()
|
|
42
43
|
&& (entry.name === 'build.gradle'
|
|
43
44
|
|| entry.name === 'build.gradle.kts'
|
|
44
|
-
|| entry.name === 'libs.versions.toml'
|
|
45
|
+
|| entry.name === 'libs.versions.toml'
|
|
46
|
+
|| entry.name === 'package.json')) {
|
|
45
47
|
result.push(path.join(dir, entry.name));
|
|
46
48
|
}
|
|
47
49
|
}
|
|
@@ -114,6 +116,127 @@ function majorOf(version) {
|
|
|
114
116
|
const major = Number.parseInt(version.split('.')[0], 10);
|
|
115
117
|
return Number.isFinite(major) ? major : null;
|
|
116
118
|
}
|
|
119
|
+
export function billingVersionFromPom(pom) {
|
|
120
|
+
for (const match of pom.matchAll(/<dependency>([\s\S]*?)<\/dependency>/g)) {
|
|
121
|
+
const block = match[1];
|
|
122
|
+
const group = block.match(/<groupId>\s*([^<]+)\s*<\/groupId>/)?.[1]?.trim();
|
|
123
|
+
const artifact = block.match(/<artifactId>\s*([^<]+)\s*<\/artifactId>/)?.[1]?.trim();
|
|
124
|
+
const version = block.match(/<version>\s*([^<]+)\s*<\/version>/)?.[1]?.trim();
|
|
125
|
+
if (group === 'com.android.billingclient' && /^billing(?:-ktx)?$/.test(artifact ?? '') && version) {
|
|
126
|
+
return { module: `${group}:${artifact}`, version };
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
return null;
|
|
130
|
+
}
|
|
131
|
+
async function nearestNodePackage(packageDir, root) {
|
|
132
|
+
let current = packageDir;
|
|
133
|
+
while (isWithin(root, current)) {
|
|
134
|
+
const candidate = path.join(current, 'node_modules', 'react-native-iap');
|
|
135
|
+
try {
|
|
136
|
+
if ((await fs.stat(candidate)).isDirectory())
|
|
137
|
+
return candidate;
|
|
138
|
+
}
|
|
139
|
+
catch {
|
|
140
|
+
// Keep walking toward the project root; workspaces commonly hoist node_modules.
|
|
141
|
+
}
|
|
142
|
+
if (current === root)
|
|
143
|
+
break;
|
|
144
|
+
current = path.dirname(current);
|
|
145
|
+
}
|
|
146
|
+
return null;
|
|
147
|
+
}
|
|
148
|
+
async function reactNativeIapEvidence(root, manifestFile, manifestText) {
|
|
149
|
+
let manifest;
|
|
150
|
+
try {
|
|
151
|
+
manifest = JSON.parse(manifestText);
|
|
152
|
+
}
|
|
153
|
+
catch {
|
|
154
|
+
return null;
|
|
155
|
+
}
|
|
156
|
+
const dependencyGroups = ['dependencies', 'devDependencies', 'optionalDependencies']
|
|
157
|
+
.map((key) => manifest[key])
|
|
158
|
+
.filter((value) => Boolean(value) && typeof value === 'object');
|
|
159
|
+
const declaredVersion = dependencyGroups
|
|
160
|
+
.map((group) => group['react-native-iap'])
|
|
161
|
+
.find((value) => typeof value === 'string');
|
|
162
|
+
if (!declaredVersion)
|
|
163
|
+
return null;
|
|
164
|
+
const relativeManifest = path.relative(root, manifestFile).replace(/\\/g, '/');
|
|
165
|
+
const installedDir = await nearestNodePackage(path.dirname(manifestFile), root);
|
|
166
|
+
if (!installedDir) {
|
|
167
|
+
return {
|
|
168
|
+
file: relativeManifest,
|
|
169
|
+
module: 'com.android.billingclient:billing',
|
|
170
|
+
expression: `react-native-iap ${declaredVersion} is declared but not installed; transitive Billing version is unresolved`,
|
|
171
|
+
source: 'unresolved',
|
|
172
|
+
};
|
|
173
|
+
}
|
|
174
|
+
const directCandidates = [
|
|
175
|
+
path.join(installedDir, 'android', 'build.gradle'),
|
|
176
|
+
path.join(installedDir, 'android', 'build.gradle.kts'),
|
|
177
|
+
];
|
|
178
|
+
for (const candidate of directCandidates) {
|
|
179
|
+
try {
|
|
180
|
+
const text = await fs.readFile(candidate, 'utf8');
|
|
181
|
+
const direct = [...text.matchAll(LITERAL_DEPENDENCY)][0];
|
|
182
|
+
if (direct) {
|
|
183
|
+
return {
|
|
184
|
+
file: relativeManifest,
|
|
185
|
+
module: direct[0].slice(0, direct[0].lastIndexOf(':')),
|
|
186
|
+
version: direct[1],
|
|
187
|
+
expression: `react-native-iap ${declaredVersion} native dependency`,
|
|
188
|
+
source: 'transitive',
|
|
189
|
+
};
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
catch {
|
|
193
|
+
// Newer react-native-iap versions delegate Billing to the OpenIAP Maven artifact.
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
let openIapVersion;
|
|
197
|
+
try {
|
|
198
|
+
const versions = JSON.parse(await fs.readFile(path.join(installedDir, 'openiap-versions.json'), 'utf8'));
|
|
199
|
+
if (typeof versions.google === 'string')
|
|
200
|
+
openIapVersion = versions.google;
|
|
201
|
+
}
|
|
202
|
+
catch {
|
|
203
|
+
// Older releases may not use OpenIAP; fall through to an unresolved, safe result.
|
|
204
|
+
}
|
|
205
|
+
if (!openIapVersion || !/^[0-9A-Za-z][0-9A-Za-z._-]*$/.test(openIapVersion)) {
|
|
206
|
+
return {
|
|
207
|
+
file: relativeManifest,
|
|
208
|
+
module: 'com.android.billingclient:billing',
|
|
209
|
+
expression: `react-native-iap ${declaredVersion} detected; transitive Billing version is unresolved`,
|
|
210
|
+
source: 'unresolved',
|
|
211
|
+
};
|
|
212
|
+
}
|
|
213
|
+
const coordinate = `io.github.hyochan.openiap:openiap-google:${openIapVersion}`;
|
|
214
|
+
try {
|
|
215
|
+
const pomUrl = `https://repo.maven.apache.org/maven2/io/github/hyochan/openiap/openiap-google/${openIapVersion}/openiap-google-${openIapVersion}.pom`;
|
|
216
|
+
const response = await fetchWithTimeout(pomUrl, {}, { timeoutMs: 15_000, maxAttempts: 2 });
|
|
217
|
+
if (response.ok) {
|
|
218
|
+
const resolved = billingVersionFromPom(await response.text());
|
|
219
|
+
if (resolved) {
|
|
220
|
+
return {
|
|
221
|
+
file: relativeManifest,
|
|
222
|
+
module: resolved.module,
|
|
223
|
+
version: resolved.version,
|
|
224
|
+
expression: `react-native-iap ${declaredVersion} -> ${coordinate}`,
|
|
225
|
+
source: 'transitive',
|
|
226
|
+
};
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
catch {
|
|
231
|
+
// Network failure must not turn a known IAP dependency into not_used.
|
|
232
|
+
}
|
|
233
|
+
return {
|
|
234
|
+
file: relativeManifest,
|
|
235
|
+
module: 'com.android.billingclient:billing',
|
|
236
|
+
expression: `react-native-iap ${declaredVersion} -> ${coordinate}; Maven Billing version lookup failed`,
|
|
237
|
+
source: 'unresolved',
|
|
238
|
+
};
|
|
239
|
+
}
|
|
117
240
|
function policyAt(now) {
|
|
118
241
|
const deadlineEnd = (date) => new Date(`${date}T23:59:59.999Z`);
|
|
119
242
|
const next = BILLING_SUPPORT_SCHEDULE.find((row) => now <= deadlineEnd(row.submissionDeadline));
|
|
@@ -175,9 +298,16 @@ export async function checkBillingCompliance(projectPath, now = new Date()) {
|
|
|
175
298
|
.map(([, variables]) => variables.get(key))
|
|
176
299
|
.find((value) => value !== undefined);
|
|
177
300
|
const evidence = [];
|
|
301
|
+
for (const [file, text] of texts) {
|
|
302
|
+
if (path.basename(file) !== 'package.json')
|
|
303
|
+
continue;
|
|
304
|
+
const transitive = await reactNativeIapEvidence(root, file, text);
|
|
305
|
+
if (transitive && !evidence.some((row) => row.expression === transitive.expression))
|
|
306
|
+
evidence.push(transitive);
|
|
307
|
+
}
|
|
178
308
|
for (const [file, text] of texts) {
|
|
179
309
|
const relative = path.relative(root, file).replace(/\\/g, '/');
|
|
180
|
-
if (path.basename(file) === 'libs.versions.toml') {
|
|
310
|
+
if (path.basename(file) === 'libs.versions.toml' || path.basename(file) === 'package.json') {
|
|
181
311
|
continue;
|
|
182
312
|
}
|
|
183
313
|
const catalog = catalogFor(file);
|
|
@@ -276,7 +406,7 @@ export async function checkBillingCompliance(projectPath, now = new Date()) {
|
|
|
276
406
|
else if (unresolved || majors.length === 0) {
|
|
277
407
|
status = 'unresolved';
|
|
278
408
|
summary = 'A Billing dependency was found, but at least one version expression could not be resolved statically.';
|
|
279
|
-
actions.push('Resolve the reported Gradle
|
|
409
|
+
actions.push('Resolve the reported Gradle/version catalog expression or install the declared IAP package, then run the check again.');
|
|
280
410
|
}
|
|
281
411
|
else if (majors.some((major) => major === policy.minimumSupportedMajor)) {
|
|
282
412
|
status = 'warning';
|
package/dist/registers/checks.js
CHANGED
|
@@ -9,7 +9,7 @@ import { checkBillingCompliance } from '../checks/billing.js';
|
|
|
9
9
|
export function registerChecksTools(server) {
|
|
10
10
|
server.tool('android_check_billing_compliance', [
|
|
11
11
|
'로컬 Android 저장소의 Google Play Billing Library 버전을 읽기 전용으로 탐지하고 제출 정책 마감과 비교합니다.',
|
|
12
|
-
'build.gradle, build.gradle.kts, gradle/libs.versions.toml의 literal·변수·version catalog
|
|
12
|
+
'build.gradle, build.gradle.kts, gradle/libs.versions.toml의 literal·변수·version catalog와 react-native-iap의 OpenIAP 전이 의존성을 검사합니다.',
|
|
13
13
|
'코드를 자동 수정하지 않고 공식 Android CLI Skill 설치 명령과 업그레이드 프롬프트만 반환합니다.',
|
|
14
14
|
].join(' '), {
|
|
15
15
|
projectPath: z.string().optional().describe('검사할 프로젝트 절대경로 (기본: MCP 프로세스 현재 디렉터리)'),
|
package/package.json
CHANGED