kasy-cli 1.15.0 → 1.17.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/bin/kasy.js +1 -0
- package/lib/commands/add.js +45 -12
- package/lib/commands/doctor.js +37 -6
- package/lib/commands/icon.js +29 -1
- package/lib/commands/new.js +34 -8
- package/lib/commands/remove.js +14 -3
- package/lib/commands/run.js +264 -3
- package/lib/scaffold/CHANGELOG.json +9 -0
- package/lib/scaffold/backends/api/patch/README.md +3 -2
- package/lib/scaffold/backends/api/pubspec.yaml.tpl +2 -0
- package/lib/scaffold/backends/supabase/patch/README.md +3 -2
- package/lib/scaffold/backends/supabase/pubspec.yaml.tpl +2 -0
- package/lib/scaffold/shared/generator-utils.js +52 -8
- package/lib/scaffold/shared/post-build.js +105 -31
- package/lib/scaffold/shared/template-strings.js +6 -0
- package/lib/utils/i18n/messages-en.js +34 -2
- package/lib/utils/i18n/messages-es.js +34 -2
- package/lib/utils/i18n/messages-pt.js +34 -2
- package/lib/utils/png-padding.js +134 -2
- package/package.json +1 -1
- package/templates/firebase/README.en.md +17 -7
- package/templates/firebase/README.es.md +17 -7
- package/templates/firebase/README.md +17 -7
- package/templates/firebase/android/app/src/main/kotlin/com/aicrus/firebase/kit/MainActivity.kt +15 -0
- package/templates/firebase/android/app/src/main/kotlin/com/aicrus/firebase/kit/MyWidget.kt +3 -19
- package/templates/firebase/android/app/src/main/kotlin/com/aicrus/firebase/kit/MyWidgetReceiver.kt +37 -0
- package/templates/firebase/android/app/src/main/kotlin/com/aicrus/firebase/kit/OpenAppAction.kt +26 -0
- package/templates/firebase/android/app/src/main/res/drawable-hdpi/android12splash.png +0 -0
- package/templates/firebase/android/app/src/main/res/drawable-mdpi/android12splash.png +0 -0
- package/templates/firebase/android/app/src/main/res/drawable-night-hdpi/android12splash.png +0 -0
- package/templates/firebase/android/app/src/main/res/drawable-night-mdpi/android12splash.png +0 -0
- package/templates/firebase/android/app/src/main/res/drawable-night-xhdpi/android12splash.png +0 -0
- package/templates/firebase/android/app/src/main/res/drawable-night-xxhdpi/android12splash.png +0 -0
- package/templates/firebase/android/app/src/main/res/drawable-night-xxxhdpi/android12splash.png +0 -0
- package/templates/firebase/android/app/src/main/res/drawable-xhdpi/android12splash.png +0 -0
- package/templates/firebase/android/app/src/main/res/drawable-xxhdpi/android12splash.png +0 -0
- package/templates/firebase/android/app/src/main/res/drawable-xxxhdpi/android12splash.png +0 -0
- package/templates/firebase/assets/images/splash_logo_dark_android12.png +0 -0
- package/templates/firebase/assets/images/splash_logo_light_android12.png +0 -0
- package/templates/firebase/docs/revenuecat-setup.es.md +28 -8
- package/templates/firebase/docs/revenuecat-setup.pt.md +28 -8
- package/templates/firebase/lib/core/home_widgets/home_widget_mywidget_service.dart +39 -41
- package/templates/firebase/lib/router.dart +15 -1
- package/templates/firebase/web/index.html +3 -0
|
@@ -13,6 +13,36 @@ const pkg = require('../../../package.json');
|
|
|
13
13
|
/** Backend IDs: firebase | supabase | api */
|
|
14
14
|
const BACKENDS = Object.freeze(['firebase', 'supabase', 'api']);
|
|
15
15
|
|
|
16
|
+
/**
|
|
17
|
+
* Resolve the default RC keys to bake into launch.json / Makefile.
|
|
18
|
+
*
|
|
19
|
+
* `kasy run` overrides these at runtime based on the device picked (test_ for
|
|
20
|
+
* simulator/emulator, appl_/goog_ for physical). The values here are only used
|
|
21
|
+
* when the user runs Flutter directly (e.g. F5 in VS Code, `make run`) without
|
|
22
|
+
* going through `kasy run`. We default to test_ since that's what works in the
|
|
23
|
+
* iOS Simulator and Android Emulator — the common dev case.
|
|
24
|
+
*
|
|
25
|
+
* Legacy support: projects generated before this refactor only ask for a single
|
|
26
|
+
* Android/iOS key (`rcAndroidKey` / `rcIosKey`). When those are passed we honor
|
|
27
|
+
* them as-is.
|
|
28
|
+
*/
|
|
29
|
+
function resolveDefaultRcKeys(answers) {
|
|
30
|
+
// Legacy single-key format (pre-test/prod split): keep behavior unchanged.
|
|
31
|
+
if ((answers.rcAndroidKey || answers.rcIosKey) && !answers.rcTestKey && !answers.rcIosProdKey && !answers.rcAndroidProdKey) {
|
|
32
|
+
return {
|
|
33
|
+
android: answers.rcAndroidKey || 'YOUR_REVENUECAT_ANDROID_KEY',
|
|
34
|
+
ios: answers.rcIosKey || 'YOUR_REVENUECAT_IOS_KEY',
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
const test = (answers.rcTestKey || '').trim();
|
|
38
|
+
const iosProd = (answers.rcIosProdKey || '').trim();
|
|
39
|
+
const androidProd = (answers.rcAndroidProdKey || '').trim();
|
|
40
|
+
return {
|
|
41
|
+
android: test || androidProd || 'YOUR_REVENUECAT_ANDROID_KEY',
|
|
42
|
+
ios: test || iosProd || 'YOUR_REVENUECAT_IOS_KEY',
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
|
|
16
46
|
/**
|
|
17
47
|
* Build dart-define args for dev and prod environments.
|
|
18
48
|
* Backend-specific vars: Firebase (none extra), Supabase (BACKEND_URL, SUPABASE_TOKEN), API (BACKEND_URL).
|
|
@@ -40,12 +70,11 @@ function buildDartDefines(backend, modules, answers) {
|
|
|
40
70
|
}
|
|
41
71
|
|
|
42
72
|
if (modules.includes('revenuecat')) {
|
|
43
|
-
const
|
|
44
|
-
|
|
45
|
-
dev.push(`--dart-define=
|
|
46
|
-
|
|
47
|
-
prod.push(`--dart-define=
|
|
48
|
-
prod.push(`--dart-define=RC_IOS_API_KEY=${iosKey}`);
|
|
73
|
+
const { android: androidDefault, ios: iosDefault } = resolveDefaultRcKeys(answers);
|
|
74
|
+
dev.push(`--dart-define=RC_ANDROID_API_KEY=${androidDefault}`);
|
|
75
|
+
dev.push(`--dart-define=RC_IOS_API_KEY=${iosDefault}`);
|
|
76
|
+
prod.push(`--dart-define=RC_ANDROID_API_KEY=${androidDefault}`);
|
|
77
|
+
prod.push(`--dart-define=RC_IOS_API_KEY=${iosDefault}`);
|
|
49
78
|
if (answers.revenuecatWeb) {
|
|
50
79
|
const webKey = answers.rcWebKey || 'YOUR_REVENUECAT_WEB_KEY';
|
|
51
80
|
dev.push(`--dart-define=RC_WEB_API_KEY=${webKey}`);
|
|
@@ -144,8 +173,11 @@ async function writeEnvExample(projectDir, modules, answers, language = 'en') {
|
|
|
144
173
|
if (modules.includes('revenuecat')) {
|
|
145
174
|
lines.push('');
|
|
146
175
|
lines.push(t.revenuecat);
|
|
147
|
-
lines.push(
|
|
148
|
-
lines.push(`
|
|
176
|
+
if (t.revenuecatTest) lines.push(t.revenuecatTest);
|
|
177
|
+
lines.push(`RC_TEST_KEY=${(answers.rcTestKey || '').trim()}`);
|
|
178
|
+
if (t.revenuecatProd) lines.push(t.revenuecatProd);
|
|
179
|
+
lines.push(`RC_IOS_PROD_KEY=${(answers.rcIosProdKey || '').trim()}`);
|
|
180
|
+
lines.push(`RC_ANDROID_PROD_KEY=${(answers.rcAndroidProdKey || '').trim()}`);
|
|
149
181
|
if (answers.revenuecatWeb) {
|
|
150
182
|
lines.push(`RC_WEB_API_KEY=${answers.rcWebKey || 'YOUR_REVENUECAT_WEB_KEY'}`);
|
|
151
183
|
}
|
|
@@ -468,6 +500,16 @@ const bool revenuecatWeb = ${revenuecatWeb};
|
|
|
468
500
|
async function writeKitSetup(projectDir, options) {
|
|
469
501
|
const { appName, bundleId, backend, modules = [], firebaseProjectId, supabaseUrl, supabaseAnonKey, moduleAnswers = {} } = options;
|
|
470
502
|
const revenuecatWeb = !!(modules.includes('revenuecat') && modules.includes('web') && moduleAnswers.revenuecatWeb);
|
|
503
|
+
// Booleans tracking which RC keys the user configured. We never persist key
|
|
504
|
+
// values to kit_setup.json — those live in .env (gitignored). These flags
|
|
505
|
+
// let `kasy doctor` warn about release readiness without re-reading .env.
|
|
506
|
+
const revenuecatKeys = modules.includes('revenuecat')
|
|
507
|
+
? {
|
|
508
|
+
test: !!(moduleAnswers.rcTestKey && moduleAnswers.rcTestKey.trim()),
|
|
509
|
+
iosProd: !!(moduleAnswers.rcIosProdKey && moduleAnswers.rcIosProdKey.trim()),
|
|
510
|
+
androidProd: !!(moduleAnswers.rcAndroidProdKey && moduleAnswers.rcAndroidProdKey.trim()),
|
|
511
|
+
}
|
|
512
|
+
: undefined;
|
|
471
513
|
const config = {
|
|
472
514
|
appName: appName || 'App',
|
|
473
515
|
bundleId: bundleId || 'com.example.app',
|
|
@@ -480,6 +522,7 @@ async function writeKitSetup(projectDir, options) {
|
|
|
480
522
|
storageProvider: backend === 'firebase' ? 'firebase' : backend === 'supabase' ? 'supabase' : 'api',
|
|
481
523
|
webCompat: modules.includes('web'),
|
|
482
524
|
revenuecatWeb,
|
|
525
|
+
...(revenuecatKeys ? { revenuecatKeys } : {}),
|
|
483
526
|
internationalization: true,
|
|
484
527
|
useSentry: modules.includes('sentry'),
|
|
485
528
|
withOnboarding: modules.includes('onboarding'),
|
|
@@ -1638,6 +1681,7 @@ async function localizeReleaseDocs(projectDir, language = 'en') {
|
|
|
1638
1681
|
module.exports = {
|
|
1639
1682
|
BACKENDS,
|
|
1640
1683
|
buildDartDefines,
|
|
1684
|
+
resolveDefaultRcKeys,
|
|
1641
1685
|
writeRouter,
|
|
1642
1686
|
writeVsCodeLaunch,
|
|
1643
1687
|
writeEnvExample,
|
|
@@ -665,49 +665,123 @@ async function validateFacebookAndroidStrings(projectDir) {
|
|
|
665
665
|
}
|
|
666
666
|
|
|
667
667
|
/**
|
|
668
|
-
*
|
|
669
|
-
*
|
|
668
|
+
* Read a project `.env` file into a plain object. Returns `{}` if the file is
|
|
669
|
+
* missing. Supports KEY=VALUE lines; ignores comments and blank lines.
|
|
670
670
|
*/
|
|
671
|
-
async function
|
|
672
|
-
const
|
|
673
|
-
if (!(await fs.pathExists(
|
|
674
|
-
return { ok: true, skipped: true, reason: 'no_makefile' };
|
|
675
|
-
}
|
|
676
|
-
|
|
671
|
+
async function readDotenv(projectDir) {
|
|
672
|
+
const envPath = path.join(projectDir, '.env');
|
|
673
|
+
if (!(await fs.pathExists(envPath))) return {};
|
|
677
674
|
let content;
|
|
678
675
|
try {
|
|
679
|
-
content = await fs.readFile(
|
|
680
|
-
} catch
|
|
681
|
-
return {
|
|
676
|
+
content = await fs.readFile(envPath, 'utf8');
|
|
677
|
+
} catch {
|
|
678
|
+
return {};
|
|
679
|
+
}
|
|
680
|
+
const env = {};
|
|
681
|
+
for (const rawLine of content.split('\n')) {
|
|
682
|
+
const line = rawLine.trim();
|
|
683
|
+
if (!line || line.startsWith('#')) continue;
|
|
684
|
+
const eq = line.indexOf('=');
|
|
685
|
+
if (eq === -1) continue;
|
|
686
|
+
const key = line.slice(0, eq).trim();
|
|
687
|
+
let value = line.slice(eq + 1).trim();
|
|
688
|
+
if ((value.startsWith('"') && value.endsWith('"')) || (value.startsWith("'") && value.endsWith("'"))) {
|
|
689
|
+
value = value.slice(1, -1);
|
|
690
|
+
}
|
|
691
|
+
if (key) env[key] = value;
|
|
682
692
|
}
|
|
693
|
+
return env;
|
|
694
|
+
}
|
|
683
695
|
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
const iosTest = iosKey.startsWith('test_');
|
|
697
|
-
const androidTest = androidKey.startsWith('test_');
|
|
696
|
+
/**
|
|
697
|
+
* Validate RevenueCat keys. The new `.env` is the source of truth:
|
|
698
|
+
* RC_TEST_KEY (test_xxx — simulator/emulator)
|
|
699
|
+
* RC_IOS_PROD_KEY (appl_xxx — physical iOS)
|
|
700
|
+
* RC_ANDROID_PROD_KEY (goog_xxx — physical Android)
|
|
701
|
+
*
|
|
702
|
+
* Legacy fallback: if none of the new vars are set we read the old
|
|
703
|
+
* RC_ANDROID_API_KEY/RC_IOS_API_KEY from `.env`, and finally the Makefile
|
|
704
|
+
* (oldest projects). Returns flags `doctor.js` uses to render granular hints.
|
|
705
|
+
*/
|
|
706
|
+
async function validateRevenueCat(projectDir, config = {}) {
|
|
707
|
+
const env = await readDotenv(projectDir);
|
|
698
708
|
|
|
709
|
+
// Webhook URL (only Supabase exposes it directly; Firebase points to Console).
|
|
699
710
|
let webhookUrl = null;
|
|
700
711
|
if (config.backendProvider === 'supabase' && config.supabaseProjectId) {
|
|
701
712
|
webhookUrl = `https://${config.supabaseProjectId}.supabase.co/functions/v1/revenuecat-webhook`;
|
|
702
713
|
}
|
|
703
714
|
|
|
715
|
+
const testKey = (env.RC_TEST_KEY || '').trim();
|
|
716
|
+
const iosProdKey = (env.RC_IOS_PROD_KEY || '').trim();
|
|
717
|
+
const androidProdKey = (env.RC_ANDROID_PROD_KEY || '').trim();
|
|
718
|
+
const hasNewKeys = !!(testKey || iosProdKey || androidProdKey);
|
|
719
|
+
|
|
720
|
+
// Legacy single-key path (for projects generated before the test/prod split).
|
|
721
|
+
if (!hasNewKeys) {
|
|
722
|
+
let iosKey = (env.RC_IOS_API_KEY || '').trim();
|
|
723
|
+
let androidKey = (env.RC_ANDROID_API_KEY || '').trim();
|
|
724
|
+
|
|
725
|
+
// Last resort: read from Makefile (oldest projects).
|
|
726
|
+
if (!iosKey && !androidKey) {
|
|
727
|
+
const makefilePath = path.join(projectDir, 'Makefile');
|
|
728
|
+
if (await fs.pathExists(makefilePath)) {
|
|
729
|
+
try {
|
|
730
|
+
const content = await fs.readFile(makefilePath, 'utf8');
|
|
731
|
+
const activeLines = content.split('\n').filter((l) => !/^\s*#/.test(l)).join('\n');
|
|
732
|
+
const iosMatch = activeLines.match(/RC_IOS_API_KEY=([^\s\\]+)/);
|
|
733
|
+
const androidMatch = activeLines.match(/RC_ANDROID_API_KEY=([^\s\\]+)/);
|
|
734
|
+
iosKey = iosMatch ? iosMatch[1].trim() : '';
|
|
735
|
+
androidKey = androidMatch ? androidMatch[1].trim() : '';
|
|
736
|
+
} catch { /* ignore */ }
|
|
737
|
+
}
|
|
738
|
+
}
|
|
739
|
+
|
|
740
|
+
const iosEmpty = !iosKey || iosKey === 'xxx';
|
|
741
|
+
const androidEmpty = !androidKey || androidKey === 'xxx';
|
|
742
|
+
const iosTest = iosKey.startsWith('test_');
|
|
743
|
+
const androidTest = androidKey.startsWith('test_');
|
|
744
|
+
|
|
745
|
+
return {
|
|
746
|
+
ok: !iosEmpty && !androidEmpty,
|
|
747
|
+
legacy: true,
|
|
748
|
+
iosKey,
|
|
749
|
+
androidKey,
|
|
750
|
+
iosEmpty,
|
|
751
|
+
androidEmpty,
|
|
752
|
+
bothTest: iosTest && androidTest,
|
|
753
|
+
webhookUrl,
|
|
754
|
+
};
|
|
755
|
+
}
|
|
756
|
+
|
|
757
|
+
// New 3-key scheme.
|
|
758
|
+
const testOk = !!testKey && /^test_/.test(testKey);
|
|
759
|
+
const testBadPrefix = !!testKey && !/^test_/.test(testKey);
|
|
760
|
+
const iosProdOk = !!iosProdKey && /^appl_/.test(iosProdKey);
|
|
761
|
+
const iosProdBadPrefix = !!iosProdKey && !/^appl_/.test(iosProdKey);
|
|
762
|
+
const androidProdOk = !!androidProdKey && /^goog_/.test(androidProdKey);
|
|
763
|
+
const androidProdBadPrefix = !!androidProdKey && !/^goog_/.test(androidProdKey);
|
|
764
|
+
|
|
765
|
+
// OK if user can run at least one device flow end-to-end. Test alone is fine
|
|
766
|
+
// for development; prod-only configurations also work but skip the simulator.
|
|
767
|
+
const anyOk = testOk || iosProdOk || androidProdOk;
|
|
768
|
+
|
|
704
769
|
return {
|
|
705
|
-
ok:
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
770
|
+
ok: anyOk,
|
|
771
|
+
legacy: false,
|
|
772
|
+
testKey,
|
|
773
|
+
iosProdKey,
|
|
774
|
+
androidProdKey,
|
|
775
|
+
testOk,
|
|
776
|
+
testBadPrefix,
|
|
777
|
+
iosProdOk,
|
|
778
|
+
iosProdBadPrefix,
|
|
779
|
+
androidProdOk,
|
|
780
|
+
androidProdBadPrefix,
|
|
781
|
+
testMissing: !testKey,
|
|
782
|
+
iosProdMissing: !iosProdKey,
|
|
783
|
+
androidProdMissing: !androidProdKey,
|
|
784
|
+
onlyTest: testOk && !iosProdOk && !androidProdOk,
|
|
711
785
|
webhookUrl,
|
|
712
786
|
};
|
|
713
787
|
}
|
|
@@ -33,6 +33,8 @@ const TEMPLATE_STRINGS = {
|
|
|
33
33
|
supabase: '# Supabase',
|
|
34
34
|
apiRest: '# API REST',
|
|
35
35
|
revenuecat: '# RevenueCat',
|
|
36
|
+
revenuecatTest: '# Test Store key (test_xxx) — auto-used on simulator/emulator',
|
|
37
|
+
revenuecatProd: '# Production keys — auto-used on physical devices',
|
|
36
38
|
sentry: '# Sentry (prod only)',
|
|
37
39
|
mixpanel: '# Mixpanel',
|
|
38
40
|
llmChat: '# LLM Chat — Cloud/Edge Function endpoint (API key stays on the server, not here)',
|
|
@@ -65,6 +67,8 @@ const TEMPLATE_STRINGS = {
|
|
|
65
67
|
supabase: '# Supabase',
|
|
66
68
|
apiRest: '# API REST',
|
|
67
69
|
revenuecat: '# RevenueCat',
|
|
70
|
+
revenuecatTest: '# Chave Test Store (test_xxx) — usada automaticamente em simulador/emulador',
|
|
71
|
+
revenuecatProd: '# Chaves de produção — usadas automaticamente em dispositivo físico',
|
|
68
72
|
sentry: '# Sentry (apenas prod)',
|
|
69
73
|
mixpanel: '# Mixpanel',
|
|
70
74
|
llmChat: '# LLM Chat — endpoint da Cloud/Edge Function (a chave de API fica no servidor, não aqui)',
|
|
@@ -97,6 +101,8 @@ const TEMPLATE_STRINGS = {
|
|
|
97
101
|
supabase: '# Supabase',
|
|
98
102
|
apiRest: '# API REST',
|
|
99
103
|
revenuecat: '# RevenueCat',
|
|
104
|
+
revenuecatTest: '# Clave Test Store (test_xxx) — se usa automáticamente en simulador/emulador',
|
|
105
|
+
revenuecatProd: '# Claves de producción — se usan automáticamente en dispositivo físico',
|
|
100
106
|
sentry: '# Sentry (solo prod)',
|
|
101
107
|
mixpanel: '# Mixpanel',
|
|
102
108
|
llmChat: '# LLM Chat — endpoint de Cloud/Edge Function (la clave de API queda en el servidor, no aquí)',
|
|
@@ -419,8 +419,15 @@ module.exports = {
|
|
|
419
419
|
|
|
420
420
|
'doctor.revenuecat.title': 'RevenueCat',
|
|
421
421
|
'doctor.revenuecat.keysOk': 'API keys configured (iOS + Android)',
|
|
422
|
-
'doctor.revenuecat.keysEmpty': '
|
|
423
|
-
'doctor.revenuecat.
|
|
422
|
+
'doctor.revenuecat.keysEmpty': 'No keys configured — set at least RC_TEST_KEY in .env (kasy run uses it on simulator/emulator)',
|
|
423
|
+
'doctor.revenuecat.testKeyOk': 'RC_TEST_KEY configured (test_) — used on simulator/emulator',
|
|
424
|
+
'doctor.revenuecat.testKeyMissing': 'RC_TEST_KEY missing — subscription flow will not work on simulator/emulator',
|
|
425
|
+
'doctor.revenuecat.iosProdOk': 'RC_IOS_PROD_KEY configured (appl_) — used on physical iPhone',
|
|
426
|
+
'doctor.revenuecat.iosProdMissing': 'RC_IOS_PROD_KEY missing — kasy run on physical iPhone will fall back to the test key',
|
|
427
|
+
'doctor.revenuecat.androidProdOk': 'RC_ANDROID_PROD_KEY configured (goog_) — used on physical Android',
|
|
428
|
+
'doctor.revenuecat.androidProdMissing': 'RC_ANDROID_PROD_KEY missing — kasy run on physical Android will fall back to the test key',
|
|
429
|
+
'doctor.revenuecat.prefixMismatch': 'Key has wrong prefix: {key} should start with {expected}',
|
|
430
|
+
'doctor.revenuecat.keysTest': 'Only Test Store keys (test_) configured — store releases require appl_/goog_, otherwise the app crashes',
|
|
424
431
|
'doctor.revenuecat.webhookUrlSupabase': 'Webhook URL (paste in RevenueCat → Integrations → Webhooks)',
|
|
425
432
|
'doctor.revenuecat.webhookUrlFirebase': 'Webhook URL: Firebase Console → Functions → subscriptionsOnRcPremiumUpdate',
|
|
426
433
|
|
|
@@ -488,6 +495,14 @@ module.exports = {
|
|
|
488
495
|
'new.firebase.q.revenuecat.webhookKey.hint': 'Save this value. In RevenueCat dashboard, paste as: Bearer <this-value>',
|
|
489
496
|
'new.firebase.q.revenuecat.metaToken': 'Meta Access Token (for Ads Conversions API, optional)',
|
|
490
497
|
'new.firebase.q.revenuecat.metaDataset': 'Meta Dataset ID / Pixel ID (optional)',
|
|
498
|
+
'new.firebase.q.revenuecat.test': 'Test Store key (test_xxx) — optional, works for both iOS+Android and runs in the simulator',
|
|
499
|
+
'new.firebase.q.revenuecat.test.invalid': 'Test Store key must start with test_ (e.g. test_xxxxxxxxxxxxxxxxxxxx).',
|
|
500
|
+
'new.firebase.q.revenuecat.iosProd': 'iOS production key (appl_xxx) — optional, used only on physical iPhone',
|
|
501
|
+
'new.firebase.q.revenuecat.iosProd.invalid': 'iOS production key must start with appl_ (e.g. appl_xxxxxxxxxxxxxxx).',
|
|
502
|
+
'new.firebase.q.revenuecat.androidProd': 'Android production key (goog_xxx) — optional, used only on physical Android',
|
|
503
|
+
'new.firebase.q.revenuecat.androidProd.invalid': 'Android production key must start with goog_ (e.g. goog_xxxxxxxxxxxxxxx).',
|
|
504
|
+
'new.firebase.q.revenuecat.atLeastOne': 'Configure at least one key (Test, iOS prod or Android prod). You can add the others later in .env.',
|
|
505
|
+
// Legacy keys — kept for projects/scripts that still reference them.
|
|
491
506
|
'new.firebase.q.revenuecat.android': 'RevenueCat API key for Android',
|
|
492
507
|
'new.firebase.q.revenuecat.ios': 'RevenueCat API key for iOS',
|
|
493
508
|
'new.firebase.q.paywall': 'Which paywall style?',
|
|
@@ -645,6 +660,8 @@ module.exports = {
|
|
|
645
660
|
// run command
|
|
646
661
|
'cli.command.run.description': 'Run your app on phone, simulator, or browser',
|
|
647
662
|
'run.launching': 'Launching Flutter app...',
|
|
663
|
+
'run.prompt.pickDevice': 'Multiple devices detected. Which one do you want to run on?',
|
|
664
|
+
'run.warn.nothingSelected': 'No device selected.',
|
|
648
665
|
'run.updateHint.prefix': 'Project improvements available —',
|
|
649
666
|
'run.updateHint.suffix': 'to see what\'s new',
|
|
650
667
|
'run.spinner.building': 'Starting Flutter…',
|
|
@@ -659,6 +676,12 @@ module.exports = {
|
|
|
659
676
|
'run.stage.buildSuccess': 'Build done — launching app…',
|
|
660
677
|
'run.error.notFlutterProject': 'No pubspec.yaml found. Run this command from inside a Flutter project.',
|
|
661
678
|
'run.error.flutterNotFound': 'Flutter not found. Make sure Flutter is installed and on your PATH.',
|
|
679
|
+
'run.rc.usingTest': 'RevenueCat: using test key (test_) — simulator/emulator',
|
|
680
|
+
'run.rc.usingProd': 'RevenueCat: using production keys — physical device',
|
|
681
|
+
'run.rc.fallbackToTest': 'RevenueCat: production key missing ({platform}) — falling back to test_; set RC_{var} in .env to test real in-app purchases',
|
|
682
|
+
'run.rc.forcedTest': 'RevenueCat: --rc=test forced',
|
|
683
|
+
'run.rc.forcedProd': 'RevenueCat: --rc=prod forced',
|
|
684
|
+
'run.rc.forcedProdMissing': 'RevenueCat: --rc=prod requested but RC_IOS_PROD_KEY/RC_ANDROID_PROD_KEY not set in .env',
|
|
662
685
|
|
|
663
686
|
// reset command
|
|
664
687
|
'cli.command.reset.description': 'Uninstall the app on a simulator/emulator/device so you can test as a fresh install',
|
|
@@ -746,6 +769,11 @@ module.exports = {
|
|
|
746
769
|
'icon.validated': 'PNG looks good',
|
|
747
770
|
'icon.copying': 'Copying icon to assets/images/icon.png...',
|
|
748
771
|
'icon.copied': 'Icon copied',
|
|
772
|
+
'icon.adaptive.generating': 'Generating Android adaptive variant (fills the launcher circle)...',
|
|
773
|
+
'icon.adaptive.generated': 'Android adaptive icon generated',
|
|
774
|
+
'icon.adaptive.failed': 'Failed to generate Android adaptive icon',
|
|
775
|
+
'icon.adaptive.fallbackTitle': 'Android background',
|
|
776
|
+
'icon.adaptive.fallbackColor': 'Could not sample a background color from the PNG (transparent). Used white. Edit icon_android.png if you want a different color.',
|
|
749
777
|
'icon.generating': 'Regenerating native icons (Android + iOS)...',
|
|
750
778
|
'icon.generated': 'Native icons regenerated',
|
|
751
779
|
'icon.done': 'Done. Uninstall the app from the device and reinstall to see the new icon.',
|
|
@@ -833,6 +861,10 @@ module.exports = {
|
|
|
833
861
|
'add.cancelled': 'Cancelled.',
|
|
834
862
|
'add.prompt.sentryDsn': 'Sentry DSN (leave blank to configure later):',
|
|
835
863
|
'add.prompt.mixpanelToken': 'Mixpanel Token (leave blank to configure later):',
|
|
864
|
+
'add.prompt.rcTestKey': 'Test Store key (test_xxx) — optional, works for both iOS+Android and on simulator:',
|
|
865
|
+
'add.prompt.rcIosProdKey': 'iOS production key (appl_xxx) — optional, only on physical device:',
|
|
866
|
+
'add.prompt.rcAndroidProdKey': 'Android production key (goog_xxx) — optional, only on physical device:',
|
|
867
|
+
// Legacy keys — kept for compatibility with old scripts.
|
|
836
868
|
'add.prompt.rcAndroidKey': 'RevenueCat Android API key (leave blank to configure later):',
|
|
837
869
|
'add.prompt.rcIosKey': 'RevenueCat iOS API key (leave blank to configure later):',
|
|
838
870
|
'add.note.facebook': 'Add your Facebook App ID and token in .vscode/launch.json (FB_APP_ID, FB_TOKEN).',
|
|
@@ -421,8 +421,15 @@ module.exports = {
|
|
|
421
421
|
|
|
422
422
|
'doctor.revenuecat.title': 'RevenueCat',
|
|
423
423
|
'doctor.revenuecat.keysOk': 'Claves de API configuradas (iOS + Android)',
|
|
424
|
-
'doctor.revenuecat.keysEmpty': '
|
|
425
|
-
'doctor.revenuecat.
|
|
424
|
+
'doctor.revenuecat.keysEmpty': 'Ninguna clave configurada — define al menos RC_TEST_KEY en .env (kasy run la usa en simulador/emulador)',
|
|
425
|
+
'doctor.revenuecat.testKeyOk': 'RC_TEST_KEY configurada (test_) — usada en simulador/emulador',
|
|
426
|
+
'doctor.revenuecat.testKeyMissing': 'RC_TEST_KEY ausente — el flujo de suscripción no funciona en simulador/emulador',
|
|
427
|
+
'doctor.revenuecat.iosProdOk': 'RC_IOS_PROD_KEY configurada (appl_) — usada en iPhone físico',
|
|
428
|
+
'doctor.revenuecat.iosProdMissing': 'RC_IOS_PROD_KEY ausente — kasy run en iPhone físico va a usar la clave de prueba',
|
|
429
|
+
'doctor.revenuecat.androidProdOk': 'RC_ANDROID_PROD_KEY configurada (goog_) — usada en Android físico',
|
|
430
|
+
'doctor.revenuecat.androidProdMissing': 'RC_ANDROID_PROD_KEY ausente — kasy run en Android físico va a usar la clave de prueba',
|
|
431
|
+
'doctor.revenuecat.prefixMismatch': 'Clave con prefijo incorrecto: {key} debe empezar con {expected}',
|
|
432
|
+
'doctor.revenuecat.keysTest': 'Solo claves Test Store (test_) configuradas — los releases en la tienda requieren appl_/goog_, sino el app crashea',
|
|
426
433
|
'doctor.revenuecat.webhookUrlSupabase': 'URL del webhook (pega en RevenueCat → Integrations → Webhooks)',
|
|
427
434
|
'doctor.revenuecat.webhookUrlFirebase': 'URL del webhook: Firebase Console → Functions → subscriptionsOnRcPremiumUpdate',
|
|
428
435
|
|
|
@@ -488,6 +495,14 @@ module.exports = {
|
|
|
488
495
|
'new.firebase.q.revenuecat.webhookKey.hint': 'Guarda este valor. En el panel RevenueCat, pega como: Bearer <este-valor>',
|
|
489
496
|
'new.firebase.q.revenuecat.metaToken': 'Meta Access Token (Conversions API, opcional)',
|
|
490
497
|
'new.firebase.q.revenuecat.metaDataset': 'Meta Dataset ID / Pixel ID (opcional)',
|
|
498
|
+
'new.firebase.q.revenuecat.test': 'Clave Test Store (test_xxx) — opcional, sirve para iOS+Android y funciona en simulador',
|
|
499
|
+
'new.firebase.q.revenuecat.test.invalid': 'Clave Test Store debe empezar con test_ (ej: test_xxxxxxxxxxxxxxxxxxxx).',
|
|
500
|
+
'new.firebase.q.revenuecat.iosProd': 'Clave iOS de producción (appl_xxx) — opcional, se usa solo en dispositivo físico',
|
|
501
|
+
'new.firebase.q.revenuecat.iosProd.invalid': 'Clave iOS de producción debe empezar con appl_ (ej: appl_xxxxxxxxxxxxxxx).',
|
|
502
|
+
'new.firebase.q.revenuecat.androidProd': 'Clave Android de producción (goog_xxx) — opcional, se usa solo en dispositivo físico',
|
|
503
|
+
'new.firebase.q.revenuecat.androidProd.invalid': 'Clave Android de producción debe empezar con goog_ (ej: goog_xxxxxxxxxxxxxxx).',
|
|
504
|
+
'new.firebase.q.revenuecat.atLeastOne': 'Configura al menos una clave (Test, iOS prod o Android prod). Puedes agregar las demás después en .env.',
|
|
505
|
+
// Legacy keys — kept for projects/scripts that still reference them.
|
|
491
506
|
'new.firebase.q.revenuecat.android': 'Clave API RevenueCat para Android',
|
|
492
507
|
'new.firebase.q.revenuecat.ios': 'Clave API RevenueCat para iOS',
|
|
493
508
|
'new.firebase.q.paywall': '¿Qué estilo de paywall?',
|
|
@@ -678,6 +693,8 @@ module.exports = {
|
|
|
678
693
|
'reset.warn.launcherCacheFailed': 'No se pudo limpiar la caché del launcher.',
|
|
679
694
|
'reset.warn.launcherNotDetected': 'Launcher por defecto no detectado — saltando limpieza de caché.',
|
|
680
695
|
'run.launching': 'Iniciando app Flutter...',
|
|
696
|
+
'run.prompt.pickDevice': 'Varios dispositivos detectados. ¿En cuál quieres ejecutar?',
|
|
697
|
+
'run.warn.nothingSelected': 'Ningún dispositivo seleccionado.',
|
|
681
698
|
'run.updateHint.prefix': 'Mejoras disponibles para el proyecto —',
|
|
682
699
|
'run.updateHint.suffix': 'para ver las novedades',
|
|
683
700
|
'run.spinner.building': 'Iniciando Flutter…',
|
|
@@ -692,6 +709,12 @@ module.exports = {
|
|
|
692
709
|
'run.stage.buildSuccess': 'Build listo — abriendo la app…',
|
|
693
710
|
'run.error.notFlutterProject': 'No se encontro pubspec.yaml. Ejecuta este comando dentro de un proyecto Flutter.',
|
|
694
711
|
'run.error.flutterNotFound': 'Flutter no encontrado. Verifica que Flutter este instalado y en el PATH.',
|
|
712
|
+
'run.rc.usingTest': 'RevenueCat: usando clave de prueba (test_) — simulador/emulador',
|
|
713
|
+
'run.rc.usingProd': 'RevenueCat: usando claves de producción — dispositivo físico',
|
|
714
|
+
'run.rc.fallbackToTest': 'RevenueCat: clave de producción ausente ({platform}) — usando test_; configura RC_{var} en .env para probar in-app purchase real',
|
|
715
|
+
'run.rc.forcedTest': 'RevenueCat: --rc=test forzado',
|
|
716
|
+
'run.rc.forcedProd': 'RevenueCat: --rc=prod forzado',
|
|
717
|
+
'run.rc.forcedProdMissing': 'RevenueCat: --rc=prod pedido pero RC_IOS_PROD_KEY/RC_ANDROID_PROD_KEY no configuradas en .env',
|
|
695
718
|
|
|
696
719
|
// doctor project checks
|
|
697
720
|
'doctor.project.title': 'Proyecto',
|
|
@@ -744,6 +767,11 @@ module.exports = {
|
|
|
744
767
|
'icon.validated': 'PNG está bien',
|
|
745
768
|
'icon.copying': 'Copiando ícono a assets/images/icon.png...',
|
|
746
769
|
'icon.copied': 'Ícono copiado',
|
|
770
|
+
'icon.adaptive.generating': 'Generando variante adaptive para Android (llena el círculo del launcher)...',
|
|
771
|
+
'icon.adaptive.generated': 'Adaptive icon Android generado',
|
|
772
|
+
'icon.adaptive.failed': 'Fallo al generar adaptive icon Android',
|
|
773
|
+
'icon.adaptive.fallbackTitle': 'Fondo Android',
|
|
774
|
+
'icon.adaptive.fallbackColor': 'No pude muestrear el color de fondo del PNG (transparente). Usé blanco. Edita icon_android.png si quieres otro color.',
|
|
747
775
|
'icon.generating': 'Regenerando íconos nativos (Android + iOS)...',
|
|
748
776
|
'icon.generated': 'Íconos nativos regenerados',
|
|
749
777
|
'icon.done': 'Listo. Desinstala el app del dispositivo y reinstala para ver el nuevo ícono.',
|
|
@@ -831,6 +859,10 @@ module.exports = {
|
|
|
831
859
|
'add.cancelled': 'Cancelado.',
|
|
832
860
|
'add.prompt.sentryDsn': 'Sentry DSN (deja en blanco para configurar después):',
|
|
833
861
|
'add.prompt.mixpanelToken': 'Mixpanel Token (deja en blanco para configurar después):',
|
|
862
|
+
'add.prompt.rcTestKey': 'Clave Test Store (test_xxx) — opcional, sirve para iOS+Android y simulador:',
|
|
863
|
+
'add.prompt.rcIosProdKey': 'Clave iOS de producción (appl_xxx) — opcional, solo en dispositivo físico:',
|
|
864
|
+
'add.prompt.rcAndroidProdKey': 'Clave Android de producción (goog_xxx) — opcional, solo en dispositivo físico:',
|
|
865
|
+
// Legacy keys — kept for compatibility with old scripts.
|
|
834
866
|
'add.prompt.rcAndroidKey': 'RevenueCat Android API key (deja en blanco para configurar después):',
|
|
835
867
|
'add.prompt.rcIosKey': 'RevenueCat iOS API key (deja en blanco para configurar después):',
|
|
836
868
|
'add.note.facebook': 'Agrega tu Facebook App ID y token en .vscode/launch.json (FB_APP_ID, FB_TOKEN).',
|
|
@@ -419,8 +419,15 @@ module.exports = {
|
|
|
419
419
|
|
|
420
420
|
'doctor.revenuecat.title': 'RevenueCat',
|
|
421
421
|
'doctor.revenuecat.keysOk': 'Chaves de API configuradas (iOS + Android)',
|
|
422
|
-
'doctor.revenuecat.keysEmpty': '
|
|
423
|
-
'doctor.revenuecat.
|
|
422
|
+
'doctor.revenuecat.keysEmpty': 'Nenhuma chave configurada — defina pelo menos RC_TEST_KEY no .env (kasy run usa em simulador/emulador)',
|
|
423
|
+
'doctor.revenuecat.testKeyOk': 'RC_TEST_KEY configurada (test_) — usada em simulador/emulador',
|
|
424
|
+
'doctor.revenuecat.testKeyMissing': 'RC_TEST_KEY ausente — fluxo de assinatura não funciona em simulador/emulador',
|
|
425
|
+
'doctor.revenuecat.iosProdOk': 'RC_IOS_PROD_KEY configurada (appl_) — usada em iPhone físico',
|
|
426
|
+
'doctor.revenuecat.iosProdMissing': 'RC_IOS_PROD_KEY ausente — kasy run em iPhone físico vai usar a chave de teste',
|
|
427
|
+
'doctor.revenuecat.androidProdOk': 'RC_ANDROID_PROD_KEY configurada (goog_) — usada em Android físico',
|
|
428
|
+
'doctor.revenuecat.androidProdMissing': 'RC_ANDROID_PROD_KEY ausente — kasy run em Android físico vai usar a chave de teste',
|
|
429
|
+
'doctor.revenuecat.prefixMismatch': 'Chave com prefixo errado: {key} deveria começar com {expected}',
|
|
430
|
+
'doctor.revenuecat.keysTest': 'Apenas chaves Test Store (test_) configuradas — releases na loja exigem appl_/goog_, senão o app crasha',
|
|
424
431
|
'doctor.revenuecat.webhookUrlSupabase': 'URL do webhook (cole no RevenueCat → Integrations → Webhooks)',
|
|
425
432
|
'doctor.revenuecat.webhookUrlFirebase': 'URL do webhook: Firebase Console → Functions → subscriptionsOnRcPremiumUpdate',
|
|
426
433
|
|
|
@@ -488,6 +495,14 @@ module.exports = {
|
|
|
488
495
|
'new.firebase.q.revenuecat.webhookKey.hint': 'Salve esse valor. No painel RevenueCat, cole como: Bearer <esse-valor>',
|
|
489
496
|
'new.firebase.q.revenuecat.metaToken': 'Meta Access Token (Conversions API, opcional)',
|
|
490
497
|
'new.firebase.q.revenuecat.metaDataset': 'Meta Dataset ID / Pixel ID (opcional)',
|
|
498
|
+
'new.firebase.q.revenuecat.test': 'Chave Test Store (test_xxx) — opcional, serve pra iOS+Android e funciona em simulador',
|
|
499
|
+
'new.firebase.q.revenuecat.test.invalid': 'Chave Test Store deve começar com test_ (ex: test_xxxxxxxxxxxxxxxxxxxx).',
|
|
500
|
+
'new.firebase.q.revenuecat.iosProd': 'Chave iOS de produção (appl_xxx) — opcional, usada só em dispositivo físico',
|
|
501
|
+
'new.firebase.q.revenuecat.iosProd.invalid': 'Chave iOS de produção deve começar com appl_ (ex: appl_xxxxxxxxxxxxxxx).',
|
|
502
|
+
'new.firebase.q.revenuecat.androidProd': 'Chave Android de produção (goog_xxx) — opcional, usada só em dispositivo físico',
|
|
503
|
+
'new.firebase.q.revenuecat.androidProd.invalid': 'Chave Android de produção deve começar com goog_ (ex: goog_xxxxxxxxxxxxxxx).',
|
|
504
|
+
'new.firebase.q.revenuecat.atLeastOne': 'Configure pelo menos uma chave (Test, iOS prod ou Android prod). Você pode adicionar as outras depois no .env.',
|
|
505
|
+
// Legacy keys — kept for projects/scripts that still reference them.
|
|
491
506
|
'new.firebase.q.revenuecat.android': 'Chave de API RevenueCat para Android',
|
|
492
507
|
'new.firebase.q.revenuecat.ios': 'Chave de API RevenueCat para iOS',
|
|
493
508
|
'new.firebase.q.paywall': 'Qual estilo de paywall?',
|
|
@@ -678,6 +693,8 @@ module.exports = {
|
|
|
678
693
|
'reset.warn.launcherCacheFailed': 'Não foi possível limpar o cache do launcher.',
|
|
679
694
|
'reset.warn.launcherNotDetected': 'Launcher padrão não detectado — pulando limpeza de cache.',
|
|
680
695
|
'run.launching': 'Iniciando app Flutter...',
|
|
696
|
+
'run.prompt.pickDevice': 'Vários dispositivos detectados. Em qual deles rodar?',
|
|
697
|
+
'run.warn.nothingSelected': 'Nenhum dispositivo selecionado.',
|
|
681
698
|
'run.updateHint.prefix': 'Melhorias disponíveis para o projeto —',
|
|
682
699
|
'run.updateHint.suffix': 'para ver o que há de novo',
|
|
683
700
|
'run.spinner.building': 'Iniciando Flutter…',
|
|
@@ -692,6 +709,12 @@ module.exports = {
|
|
|
692
709
|
'run.stage.buildSuccess': 'Build pronto — abrindo o app…',
|
|
693
710
|
'run.error.notFlutterProject': 'Nenhum pubspec.yaml encontrado. Execute este comando dentro de um projeto Flutter.',
|
|
694
711
|
'run.error.flutterNotFound': 'Flutter não encontrado. Verifique se o Flutter está instalado e no PATH.',
|
|
712
|
+
'run.rc.usingTest': 'RevenueCat: usando chave de teste (test_) — simulador/emulador',
|
|
713
|
+
'run.rc.usingProd': 'RevenueCat: usando chaves de produção — dispositivo físico',
|
|
714
|
+
'run.rc.fallbackToTest': 'RevenueCat: chave de produção ausente ({platform}) — caindo para test_; configure RC_{var} no .env para testar in-app purchase real',
|
|
715
|
+
'run.rc.forcedTest': 'RevenueCat: --rc=test forçado',
|
|
716
|
+
'run.rc.forcedProd': 'RevenueCat: --rc=prod forçado',
|
|
717
|
+
'run.rc.forcedProdMissing': 'RevenueCat: --rc=prod pedido mas RC_IOS_PROD_KEY/RC_ANDROID_PROD_KEY não configuradas no .env',
|
|
695
718
|
|
|
696
719
|
// doctor project checks
|
|
697
720
|
'doctor.project.title': 'Projeto',
|
|
@@ -744,6 +767,11 @@ module.exports = {
|
|
|
744
767
|
'icon.validated': 'PNG está bom',
|
|
745
768
|
'icon.copying': 'Copiando ícone para assets/images/icon.png...',
|
|
746
769
|
'icon.copied': 'Ícone copiado',
|
|
770
|
+
'icon.adaptive.generating': 'Gerando versão adaptive para Android (preenche o círculo do launcher)...',
|
|
771
|
+
'icon.adaptive.generated': 'Adaptive icon Android gerado',
|
|
772
|
+
'icon.adaptive.failed': 'Falha ao gerar adaptive icon Android',
|
|
773
|
+
'icon.adaptive.fallbackTitle': 'Fundo Android',
|
|
774
|
+
'icon.adaptive.fallbackColor': 'Não consegui amostrar a cor de fundo do PNG (transparente). Usei branco. Se quiser outra cor, edite icon_android.png à mão.',
|
|
747
775
|
'icon.generating': 'Regenerando ícones nativos (Android + iOS)...',
|
|
748
776
|
'icon.generated': 'Ícones nativos regenerados',
|
|
749
777
|
'icon.done': 'Pronto. Desinstale o app do dispositivo e reinstale para ver o novo ícone.',
|
|
@@ -831,6 +859,10 @@ module.exports = {
|
|
|
831
859
|
'add.cancelled': 'Cancelado.',
|
|
832
860
|
'add.prompt.sentryDsn': 'Sentry DSN (deixe em branco para configurar depois):',
|
|
833
861
|
'add.prompt.mixpanelToken': 'Mixpanel Token (deixe em branco para configurar depois):',
|
|
862
|
+
'add.prompt.rcTestKey': 'Chave Test Store (test_xxx) — opcional, serve pra iOS+Android e simulador:',
|
|
863
|
+
'add.prompt.rcIosProdKey': 'Chave iOS de produção (appl_xxx) — opcional, só em dispositivo físico:',
|
|
864
|
+
'add.prompt.rcAndroidProdKey': 'Chave Android de produção (goog_xxx) — opcional, só em dispositivo físico:',
|
|
865
|
+
// Legacy keys — kept for compatibility with old scripts.
|
|
834
866
|
'add.prompt.rcAndroidKey': 'RevenueCat Android API key (deixe em branco para configurar depois):',
|
|
835
867
|
'add.prompt.rcIosKey': 'RevenueCat iOS API key (deixe em branco para configurar depois):',
|
|
836
868
|
'add.note.facebook': 'Adicione seu Facebook App ID e token no .vscode/launch.json (FB_APP_ID, FB_TOKEN).',
|