kasy-cli 1.19.3 → 1.20.1
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/README.md +11 -3
- package/bin/kasy.js +1 -0
- package/lib/commands/new.js +87 -37
- package/lib/commands/run.js +14 -0
- package/lib/scaffold/backends/api/patch/lib/core/data/api/meta_ads_api.dart +1 -1
- package/lib/scaffold/backends/api/patch/lib/core/data/api/storage_api.dart +1 -1
- package/lib/scaffold/backends/api/patch/lib/core/data/entities/user_entity.dart +1 -1
- package/lib/scaffold/backends/api/patch/lib/features/authentication/api/authentication_api.dart +4 -5
- package/lib/scaffold/backends/api/patch/lib/features/feedbacks/api/feature_request_api.dart +1 -1
- package/lib/scaffold/backends/api/patch/lib/features/feedbacks/api/feature_vote_api.dart +1 -1
- package/lib/scaffold/backends/api/patch/lib/features/llm_chat/api/llm_chat_api.dart +1 -1
- package/lib/scaffold/backends/api/patch/lib/features/llm_chat/providers/llm_chat_notifier.dart +317 -0
- package/lib/scaffold/backends/api/patch/lib/features/notifications/api/device_api.dart +40 -1
- package/lib/scaffold/backends/api/patch/lib/features/notifications/api/entities/notifications_entity.dart +2 -0
- package/lib/scaffold/backends/api/patch/lib/features/onboarding/api/entities/user_info_entity.dart +1 -1
- package/lib/scaffold/backends/api/patch/lib/features/subscription/api/entities/subscription_entity.dart +1 -1
- package/lib/scaffold/backends/api/patch/lib/features/subscription/shared/maybeshow_premium.dart +0 -2
- package/lib/scaffold/backends/api/pubspec.yaml.tpl +2 -0
- package/lib/scaffold/backends/firebase/enable-auth-via-cli.js +11 -8
- package/lib/scaffold/backends/firebase/setup-from-scratch.js +91 -2
- package/lib/scaffold/backends/supabase/deploy.js +56 -3
- package/lib/scaffold/backends/supabase/patch/lib/core/data/api/storage_api.dart +5 -11
- package/lib/scaffold/backends/supabase/patch/lib/core/data/entities/user_entity.dart +2 -2
- package/lib/scaffold/backends/supabase/patch/lib/features/notifications/api/device_api.dart +31 -1
- package/lib/scaffold/backends/supabase/patch/lib/features/onboarding/api/entities/user_info_entity.dart +1 -1
- package/lib/scaffold/backends/supabase/patch/lib/features/subscription/api/entities/subscription_entity.dart +1 -1
- package/lib/scaffold/backends/supabase/pubspec.yaml.tpl +2 -0
- package/lib/scaffold/catalog.js +2 -2
- package/lib/scaffold/engine.js +5 -0
- package/lib/scaffold/generate.js +23 -3
- package/lib/scaffold/shared/generator-utils.js +303 -56
- package/lib/scaffold/shared/post-build.js +11 -0
- package/lib/utils/i18n/messages-en.js +6 -1
- package/lib/utils/i18n/messages-es.js +6 -1
- package/lib/utils/i18n/messages-pt.js +6 -1
- package/package.json +1 -1
- package/templates/firebase/android/app/src/main/res/drawable/background.png +0 -0
- package/templates/firebase/android/app/src/main/res/drawable-night/background.png +0 -0
- package/templates/firebase/android/app/src/main/res/drawable-night-v21/background.png +0 -0
- package/templates/firebase/android/app/src/main/res/drawable-v21/background.png +0 -0
- package/templates/firebase/android/app/src/main/res/values-night-v31/styles.xml +1 -1
- package/templates/firebase/android/app/src/main/res/values-v31/styles.xml +1 -1
- package/templates/firebase/ios/Runner/Assets.xcassets/LaunchBackground.imageset/background.png +0 -0
- package/templates/firebase/ios/Runner/Assets.xcassets/LaunchBackground.imageset/darkbackground.png +0 -0
- package/templates/firebase/lib/components/kasy_date_picker.dart +14 -8
- package/templates/firebase/lib/components/kasy_sidebar_pro.dart +1150 -0
- package/templates/firebase/lib/components/kasy_tabs.dart +156 -43
- package/templates/firebase/lib/components/kasy_text_field.dart +37 -34
- package/templates/firebase/lib/core/bottom_menu/bottom_menu.dart +13 -82
- package/templates/firebase/lib/core/bottom_menu/bottom_router.dart +6 -102
- package/templates/firebase/lib/core/bottom_menu/kasy_bottom_bar_factory.dart +8 -1
- package/templates/firebase/lib/core/dev_inspector/dev_inspector.dart +433 -243
- package/templates/firebase/lib/core/dev_inspector/dev_inspector_service.dart +198 -83
- package/templates/firebase/lib/core/icons/kasy_icons.dart +1 -0
- package/templates/firebase/lib/core/states/user_state_notifier.dart +8 -10
- package/templates/firebase/lib/core/theme/colors.dart +6 -2
- package/templates/firebase/lib/core/web_device_preview/web_device_preview.dart +119 -19
- package/templates/firebase/lib/core/widgets/kasy_hover.dart +68 -27
- package/templates/firebase/lib/features/home/home_components_page.dart +11 -14
- package/templates/firebase/lib/features/home/home_components_preview_registry.dart +121 -66
- package/templates/firebase/lib/features/home/home_page.dart +7 -8
- package/templates/firebase/lib/features/settings/settings_page.dart +27 -146
- package/templates/firebase/lib/features/settings/ui/components/admin/admin_bottom_sheet.dart +16 -3
- package/templates/firebase/lib/features/settings/ui/widgets/settings_tile.dart +22 -5
- package/templates/firebase/lib/i18n/en.i18n.json +3 -1
- package/templates/firebase/lib/i18n/es.i18n.json +3 -1
- package/templates/firebase/lib/i18n/pt.i18n.json +3 -1
- package/templates/firebase/lib/router.dart +60 -0
- package/templates/firebase/pubspec.yaml +6 -4
- package/templates/firebase/test/core/bottom_menu/detail_route_menu_test.dart +57 -0
- package/templates/firebase/web/index.html +7 -17
- package/lib/scaffold/backends/api/patch/lib/core/rating/widgets/review_popup.dart +0 -211
- package/lib/scaffold/backends/api/patch/lib/features/notifications/providers/models/notification.dart +0 -185
- package/lib/scaffold/backends/api/patch/lib/features/onboarding/ui/components/onboarding_notifications_setup.dart +0 -73
- package/lib/scaffold/backends/api/patch/lib/main.dart +0 -275
- package/lib/scaffold/backends/api/patch/lib/router.dart +0 -133
- package/lib/scaffold/backends/supabase/patch/lib/core/rating/widgets/review_popup.dart +0 -211
- package/lib/scaffold/backends/supabase/patch/lib/features/feedbacks/ui/component/add_feature_form.dart +0 -199
- package/lib/scaffold/backends/supabase/patch/lib/features/notifications/providers/models/notification.dart +0 -174
- package/lib/scaffold/backends/supabase/patch/lib/features/onboarding/ui/components/onboarding_notifications_setup.dart +0 -73
- package/lib/scaffold/backends/supabase/patch/lib/main.dart +0 -307
- package/lib/scaffold/backends/supabase/patch/lib/router.dart +0 -133
- package/templates/firebase/lib/firebase_options.dart +0 -75
|
@@ -197,6 +197,51 @@ async function enableGoogleSignIn(projectRef, webClientId, clientSecret) {
|
|
|
197
197
|
}
|
|
198
198
|
}
|
|
199
199
|
|
|
200
|
+
/**
|
|
201
|
+
* Enable Apple Sign-In on the Supabase project via Management API.
|
|
202
|
+
*
|
|
203
|
+
* For native iOS sign-in (signInWithIdToken) Supabase validates the ID token's
|
|
204
|
+
* audience against external_apple_client_id, a comma-separated list of allowed
|
|
205
|
+
* client IDs. For a native iOS app that audience is the app's bundle ID, so we
|
|
206
|
+
* set it there. No secret is required for native sign-in; external_apple_secret
|
|
207
|
+
* is only needed for the web OAuth flow (Sign in with Apple JS), which requires
|
|
208
|
+
* an Apple Developer Service ID and is configured manually later.
|
|
209
|
+
*
|
|
210
|
+
* Note: external_apple_additional_client_ids is NOT used here. The Management API
|
|
211
|
+
* only appends it onto external_apple_client_id on write and always returns it as
|
|
212
|
+
* null on read, which makes the configured value impossible to verify.
|
|
213
|
+
*
|
|
214
|
+
* @param {string} projectRef
|
|
215
|
+
* @param {string} bundleId - iOS bundle identifier (e.g. com.acme.app)
|
|
216
|
+
*/
|
|
217
|
+
async function enableAppleSignIn(projectRef, bundleId) {
|
|
218
|
+
if (!bundleId) return { ok: false, error: 'bundleId is required' };
|
|
219
|
+
const token = await getSupabaseAccessToken();
|
|
220
|
+
if (!token) return { ok: false, error: 'Could not retrieve Supabase access token' };
|
|
221
|
+
const payload = JSON.stringify({
|
|
222
|
+
external_apple_enabled: true,
|
|
223
|
+
external_apple_client_id: bundleId,
|
|
224
|
+
});
|
|
225
|
+
const result = await run(
|
|
226
|
+
`curl -s -X PATCH "https://api.supabase.com/v1/projects/${projectRef}/config/auth" ` +
|
|
227
|
+
`-H "Authorization: Bearer ${token}" ` +
|
|
228
|
+
`-H "Content-Type: application/json" ` +
|
|
229
|
+
`-d '${payload}'`,
|
|
230
|
+
process.cwd()
|
|
231
|
+
);
|
|
232
|
+
if (!result.ok) return { ok: false, error: result.error };
|
|
233
|
+
try {
|
|
234
|
+
const data = JSON.parse(result.stdout);
|
|
235
|
+
const allowedIds = String(data.external_apple_client_id || '')
|
|
236
|
+
.split(',')
|
|
237
|
+
.map((s) => s.trim());
|
|
238
|
+
if (data.external_apple_enabled === true && allowedIds.includes(bundleId)) return { ok: true };
|
|
239
|
+
return { ok: false, error: data.message || JSON.stringify(data) };
|
|
240
|
+
} catch {
|
|
241
|
+
return { ok: false, error: 'Could not parse Management API response' };
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
|
|
200
245
|
/**
|
|
201
246
|
* Configure auth settings via Supabase Management API:
|
|
202
247
|
* - Enable anonymous sign-in
|
|
@@ -386,12 +431,13 @@ async function createProjectAndGetKeys(projectName, dbPassword, region = 'sa-eas
|
|
|
386
431
|
|
|
387
432
|
/**
|
|
388
433
|
* Setup linked project: init, link, db push, anonymous sign-in, Google Sign-In,
|
|
389
|
-
* functions deploy, secrets.
|
|
434
|
+
* Apple Sign-In, functions deploy, secrets.
|
|
390
435
|
* Run AFTER generate.
|
|
391
436
|
* @param {object} secrets - { rcWebhookKey, metaAccessToken, metaDatasetId, firebaseProjectId }
|
|
392
|
-
* @param {object} google - { webClientId, clientSecret }
|
|
437
|
+
* @param {object} google - { webClientId, clientSecret } - optional, configures Google OAuth
|
|
438
|
+
* @param {object} apple - { bundleId } - optional, enables native iOS Apple Sign-In
|
|
393
439
|
*/
|
|
394
|
-
async function setupLinkedProject(projectDir, projectRef, dbPassword, secrets = {}, google = {}) {
|
|
440
|
+
async function setupLinkedProject(projectDir, projectRef, dbPassword, secrets = {}, google = {}, apple = {}) {
|
|
395
441
|
const steps = [];
|
|
396
442
|
|
|
397
443
|
const init = await ensureSupabaseInit(projectDir);
|
|
@@ -413,6 +459,12 @@ async function setupLinkedProject(projectDir, projectRef, dbPassword, secrets =
|
|
|
413
459
|
steps.push({ name: 'google sign-in', ok: googleResult.ok, detail: googleResult.error });
|
|
414
460
|
}
|
|
415
461
|
|
|
462
|
+
// Enable native iOS Apple Sign-In if a bundle ID was provided (no secret needed).
|
|
463
|
+
if (apple.bundleId) {
|
|
464
|
+
const appleResult = await enableAppleSignIn(projectRef, apple.bundleId);
|
|
465
|
+
steps.push({ name: 'apple sign-in', ok: appleResult.ok, detail: appleResult.error });
|
|
466
|
+
}
|
|
467
|
+
|
|
416
468
|
// Always deploy all edge functions (send-push-notification, revenuecat-webhook, etc.)
|
|
417
469
|
const fnResult = await deployFunctions(projectDir);
|
|
418
470
|
if (Array.isArray(fnResult)) {
|
|
@@ -473,6 +525,7 @@ module.exports = {
|
|
|
473
525
|
setupLinkedProject,
|
|
474
526
|
enableAnonymousSignIn,
|
|
475
527
|
enableGoogleSignIn,
|
|
528
|
+
enableAppleSignIn,
|
|
476
529
|
checkLoggedIn,
|
|
477
530
|
getOrgsList,
|
|
478
531
|
getProjectsByOrg,
|
|
@@ -11,24 +11,18 @@ final storageApiProvider = Provider<StorageApi>(
|
|
|
11
11
|
),
|
|
12
12
|
);
|
|
13
13
|
|
|
14
|
-
class StorageApi {
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
/// upload a file to firebase storage
|
|
18
|
-
/// and return a stream of the upload progress
|
|
14
|
+
abstract class StorageApi {
|
|
15
|
+
/// upload a file to storage and return a stream of the upload progress
|
|
19
16
|
Stream<UploadResult> uploadData(
|
|
20
17
|
Uint8List data,
|
|
21
18
|
String folder,
|
|
22
19
|
String filename, {
|
|
23
20
|
String? mimeType, // ex 'image/jpg'
|
|
24
21
|
bool isPublic = true,
|
|
25
|
-
})
|
|
26
|
-
throw UnimplementedError();
|
|
27
|
-
}
|
|
22
|
+
});
|
|
28
23
|
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
}
|
|
24
|
+
/// request to delete a file from path
|
|
25
|
+
Future<void> deleteFile(String? path);
|
|
32
26
|
}
|
|
33
27
|
|
|
34
28
|
class SupabaseStorageApi implements StorageApi {
|
|
@@ -29,7 +29,7 @@ class Credentials {
|
|
|
29
29
|
final String id;
|
|
30
30
|
// session access token — used internally for session tracking
|
|
31
31
|
// Supabase manages token refresh automatically via SupabaseClient
|
|
32
|
-
final String token;
|
|
32
|
+
final String? token;
|
|
33
33
|
|
|
34
34
|
Credentials({
|
|
35
35
|
required this.id,
|
|
@@ -42,7 +42,7 @@ class Credentials {
|
|
|
42
42
|
}
|
|
43
43
|
return Credentials(
|
|
44
44
|
id: json['id']! as String,
|
|
45
|
-
token: json['token'] as String
|
|
45
|
+
token: json['token'] as String?,
|
|
46
46
|
);
|
|
47
47
|
}
|
|
48
48
|
}
|
|
@@ -99,12 +99,18 @@ class FirebaseDeviceApi implements DeviceApi {
|
|
|
99
99
|
}
|
|
100
100
|
}
|
|
101
101
|
final token = await _messaging.getToken();
|
|
102
|
+
if (token == null) {
|
|
103
|
+
throw ApiError(
|
|
104
|
+
code: 0,
|
|
105
|
+
message: 'FCM token is null — check Firebase setup and notification permissions',
|
|
106
|
+
);
|
|
107
|
+
}
|
|
102
108
|
final os = Platform.isAndroid
|
|
103
109
|
? OperatingSystem.android //
|
|
104
110
|
: OperatingSystem.ios;
|
|
105
111
|
return DeviceEntity(
|
|
106
112
|
installationId: installationId,
|
|
107
|
-
token: token
|
|
113
|
+
token: token,
|
|
108
114
|
operatingSystem: os,
|
|
109
115
|
creationDate: DateTime.now(),
|
|
110
116
|
lastUpdateDate: DateTime.now(),
|
|
@@ -289,6 +295,7 @@ class FirebaseDeviceApi implements DeviceApi {
|
|
|
289
295
|
}
|
|
290
296
|
|
|
291
297
|
Future<String?> getIpAddress() async {
|
|
298
|
+
if (kIsWeb) return null;
|
|
292
299
|
try {
|
|
293
300
|
// First, try to find a public IP in network interfaces
|
|
294
301
|
final interfaces = await io.NetworkInterface.list();
|
|
@@ -333,6 +340,29 @@ class FirebaseDeviceApi implements DeviceApi {
|
|
|
333
340
|
/// Returns a map with all device information
|
|
334
341
|
@override
|
|
335
342
|
Future<Map<String, String>> fetchDeviceProperties() async {
|
|
343
|
+
// On web there is no native device layer (no NetworkInterface, no Platform):
|
|
344
|
+
// return static values so the app works as a PWA without throwing at runtime.
|
|
345
|
+
if (kIsWeb) {
|
|
346
|
+
final webLocale = PlatformDispatcher.instance.locale.toLanguageTag().replaceAll('-', '_');
|
|
347
|
+
return {
|
|
348
|
+
'appLongVersion': '',
|
|
349
|
+
'osVersion': 'web',
|
|
350
|
+
'deviceModel': 'browser',
|
|
351
|
+
'deviceLocale': webLocale,
|
|
352
|
+
'timezone': '',
|
|
353
|
+
'carrier': '',
|
|
354
|
+
'screenWidth': '',
|
|
355
|
+
'screenHeight': '',
|
|
356
|
+
'screenDensity': '',
|
|
357
|
+
'cpuCores': '',
|
|
358
|
+
'storageSize': '',
|
|
359
|
+
'freeStorage': '',
|
|
360
|
+
'deviceTimezone': '',
|
|
361
|
+
'mobileAdvertiserId': '',
|
|
362
|
+
'anonymousFbId': '',
|
|
363
|
+
'clientIpAddress': '',
|
|
364
|
+
};
|
|
365
|
+
}
|
|
336
366
|
try {
|
|
337
367
|
final deviceInfo = DeviceInfoPlugin();
|
|
338
368
|
final packageInfo = await PackageInfo.fromPlatform();
|
|
@@ -18,7 +18,7 @@ sealed class UserInfoEntity with _$UserInfoEntity {
|
|
|
18
18
|
@JsonKey(name: 'user_id') required String userId,
|
|
19
19
|
@JsonKey(name: 'info_key') required String key,
|
|
20
20
|
@JsonKey(name: 'info_value') required String value,
|
|
21
|
-
}) =
|
|
21
|
+
}) = UserInfoEntityData;
|
|
22
22
|
|
|
23
23
|
factory UserInfoEntity.fromJson(Map<String, Object?> json) =>
|
|
24
24
|
_$UserInfoEntityFromJson(json);
|
|
@@ -21,7 +21,7 @@ sealed class SubscriptionEntity with _$SubscriptionEntity {
|
|
|
21
21
|
const factory SubscriptionEntity({
|
|
22
22
|
@JsonKey(includeIfNull: false) String? id,
|
|
23
23
|
@JsonKey(name: 'user_id') String? userId,
|
|
24
|
-
@JsonKey(name: 'offer_id')
|
|
24
|
+
@JsonKey(name: 'offer_id') String? offerId,
|
|
25
25
|
@JsonKey(name: 'sku_id') required String skuId,
|
|
26
26
|
@JsonKey(name: 'creation_date') DateTime? creationDate,
|
|
27
27
|
@JsonKey(name: 'last_update_date') DateTime? lastUpdateDate,
|
|
@@ -52,6 +52,7 @@ dependencies:
|
|
|
52
52
|
intl: ^0.20.2
|
|
53
53
|
jiffy: ^6.4.4
|
|
54
54
|
json_annotation: ^4.9.0
|
|
55
|
+
local_auth: ^3.0.1
|
|
55
56
|
logger: ^2.6.2
|
|
56
57
|
lucide_icons_flutter: ^3.1.10
|
|
57
58
|
mixpanel_flutter: ^2.5.0
|
|
@@ -73,6 +74,7 @@ dependencies:
|
|
|
73
74
|
universal_html: ^2.3.0
|
|
74
75
|
universal_io: ^2.3.1
|
|
75
76
|
url_launcher: ^6.3.2
|
|
77
|
+
web: ^1.1.1
|
|
76
78
|
|
|
77
79
|
dev_dependencies:
|
|
78
80
|
build_runner: ^2.11.1
|
package/lib/scaffold/catalog.js
CHANGED
|
@@ -50,12 +50,12 @@ const FEATURE_CATALOG = [
|
|
|
50
50
|
{ id: 'revenuecat', displayName: 'RevenueCat', status: 'public', availableIn: ['firebase', 'supabase', 'api'], defaultInPresets: ['saas', 'full'], enhances: 'subscription' },
|
|
51
51
|
// features
|
|
52
52
|
{ id: 'onboarding', displayName: 'Onboarding', status: 'public', availableIn: ['firebase', 'supabase', 'api'], defaultInPresets: ['starter', 'saas', 'content', 'full'] },
|
|
53
|
-
{ id: 'web', displayName: 'Web Support (PWA)', status: 'public', availableIn: ['firebase'
|
|
53
|
+
{ id: 'web', displayName: 'Web Support (PWA)', status: 'public', availableIn: ['firebase', 'supabase', 'api'], defaultInPresets: ['full'] },
|
|
54
54
|
{ id: 'widget', displayName: 'Home Widget', status: 'public', availableIn: ['firebase', 'supabase', 'api'], defaultInPresets: ['full'] },
|
|
55
55
|
{ id: 'llm_chat', displayName: 'AI Chat', status: 'public', availableIn: ['firebase', 'supabase', 'api'], defaultInPresets: ['content', 'full'] },
|
|
56
56
|
{ id: 'local_notifications', displayName: 'Local Reminders', status: 'public', availableIn: ['firebase', 'supabase', 'api'], defaultInPresets: [] },
|
|
57
57
|
// feedback (Firebase/Supabase only)
|
|
58
|
-
{ id: 'feedback', displayName: 'Feature Requests', status: 'public', availableIn: ['firebase', 'supabase'],
|
|
58
|
+
{ id: 'feedback', displayName: 'Feature Requests', status: 'public', availableIn: ['firebase', 'supabase', 'api'], defaultInPresets: ['saas', 'full'], tag: 'requiresDb' },
|
|
59
59
|
// ci/cd
|
|
60
60
|
{ id: 'ci', displayName: 'CI/CD', status: 'public', availableIn: ['firebase', 'supabase', 'api'], defaultInPresets: ['full'] },
|
|
61
61
|
];
|
package/lib/scaffold/engine.js
CHANGED
|
@@ -41,6 +41,11 @@ const ALWAYS_EXCLUDE_BASENAMES = new Set([
|
|
|
41
41
|
'google-services.json', // Firebase Android config — client must generate their own
|
|
42
42
|
'GoogleService-Info.plist', // Firebase iOS config — client must generate their own
|
|
43
43
|
'firebase_options_dev.dart', // Dev Firebase options — internal only
|
|
44
|
+
'firebase_options.dart', // Kit's own Firebase options — dead code in generated
|
|
45
|
+
// projects (main.dart imports firebase_options_dev.dart,
|
|
46
|
+
// which flutterfire regenerates per-user). Shipping it
|
|
47
|
+
// would leak the kit's project id (fir-kit-8e56b) into
|
|
48
|
+
// every generated app. Never copy it.
|
|
44
49
|
]);
|
|
45
50
|
|
|
46
51
|
// File extensions that may contain generated Dart code — skip these
|
package/lib/scaffold/generate.js
CHANGED
|
@@ -48,6 +48,7 @@ const {
|
|
|
48
48
|
writeEnvExample,
|
|
49
49
|
writeEnvFileIfMissing,
|
|
50
50
|
writeFirebaserc,
|
|
51
|
+
cleanFirebaseJsonKitRefs,
|
|
51
52
|
writeFeaturesConfig,
|
|
52
53
|
writeKitSetup,
|
|
53
54
|
writeMakefile,
|
|
@@ -57,6 +58,7 @@ const {
|
|
|
57
58
|
removeFacebookSigninFromAuthPages,
|
|
58
59
|
removeAndroidFacebookMetadata,
|
|
59
60
|
writeNoOpAdminHomeWidgets,
|
|
61
|
+
patchLanguageSwitcherNoWidget,
|
|
60
62
|
writeNoOpFeatureRequestRepository,
|
|
61
63
|
writeNoOpSubscriptionStubs,
|
|
62
64
|
writeNoOpSentryUsages,
|
|
@@ -66,7 +68,7 @@ const {
|
|
|
66
68
|
removeDevelopmentTeam,
|
|
67
69
|
localizeReleaseDocs,
|
|
68
70
|
} = require('./shared/generator-utils');
|
|
69
|
-
const { pubGet, slangGenerate, buildRunner, dartFix, flutterfireConfigure, writeGoogleAuthOptions, writeGoogleIosUrlScheme, patchFirebaseServiceWorker } = require('./shared/post-build');
|
|
71
|
+
const { pubGet, slangGenerate, buildRunner, dartFix, flutterfireConfigure, writeGoogleAuthOptions, writeGoogleIosUrlScheme, patchFirebaseServiceWorker, deployFirestoreRules } = require('./shared/post-build');
|
|
70
72
|
const { FIREBASE_SOURCE_DIR } = require('./shared/backend-config');
|
|
71
73
|
|
|
72
74
|
/**
|
|
@@ -263,6 +265,9 @@ async function generateProject(targetDir, backend, options, hooks = {}) {
|
|
|
263
265
|
// (imports home_widget_mywidget_service which only exists when widget is selected)
|
|
264
266
|
if (!modules.includes('widget')) {
|
|
265
267
|
await writeNoOpAdminHomeWidgets(targetDir);
|
|
268
|
+
// The language switcher pushes locale changes to the home widget — strip
|
|
269
|
+
// that hook so it doesn't reference the (now absent) widget provider.
|
|
270
|
+
await patchLanguageSwitcherNoWidget(targetDir);
|
|
266
271
|
// Remove Android native widget files and unregister the receiver from the manifest
|
|
267
272
|
// so the widget does not appear in the Android widget picker when not selected
|
|
268
273
|
await removeAndroidWidgetArtifacts(targetDir, bundleId);
|
|
@@ -340,8 +345,10 @@ async function generateProject(targetDir, backend, options, hooks = {}) {
|
|
|
340
345
|
});
|
|
341
346
|
}
|
|
342
347
|
|
|
343
|
-
// iOS: register REVERSED_CLIENT_ID as a URL scheme in ios/Runner/Info.plist
|
|
344
|
-
|
|
348
|
+
// iOS: register REVERSED_CLIENT_ID as a URL scheme in ios/Runner/Info.plist.
|
|
349
|
+
// Skip for Supabase — REVERSED_CLIENT_ID isn't present until Google Sign-In is
|
|
350
|
+
// enabled later in new.js; that flow calls writeGoogleIosUrlSchemeFromClientId instead.
|
|
351
|
+
if (backend !== 'supabase' && !deferGoogleAuthPatches) {
|
|
345
352
|
const iosSchemeResult = await writeGoogleIosUrlScheme(targetDir);
|
|
346
353
|
steps.push({
|
|
347
354
|
name: 'google-ios-url-scheme',
|
|
@@ -362,6 +369,19 @@ async function generateProject(targetDir, backend, options, hooks = {}) {
|
|
|
362
369
|
}
|
|
363
370
|
|
|
364
371
|
await writeFirebaserc(targetDir, firebaseProjectId);
|
|
372
|
+
// Drop the stale firebase.json entry that still points at the kit project
|
|
373
|
+
// (the dead lib/firebase_options.dart we no longer ship).
|
|
374
|
+
await cleanFirebaseJsonKitRefs(targetDir);
|
|
375
|
+
|
|
376
|
+
// Deploy Firestore security rules so the app works immediately after
|
|
377
|
+
// `kasy new` without needing `kasy deploy` first. Fast (<30s), billing-free.
|
|
378
|
+
// Without this the project gets Firebase's default deny-all rules, causing
|
|
379
|
+
// every Firestore read to throw permission-denied and the app to log the user out.
|
|
380
|
+
if (firebaseProjectId) {
|
|
381
|
+
onProgress('firestore-rules');
|
|
382
|
+
const rulesResult = await deployFirestoreRules(targetDir, firebaseProjectId);
|
|
383
|
+
steps.push({ name: 'firestore-rules', ok: rulesResult.ok, detail: rulesResult.ok ? null : rulesResult.error });
|
|
384
|
+
}
|
|
365
385
|
|
|
366
386
|
// ── 3. Post-build específico do backend ────────────────────────────────────
|
|
367
387
|
if (postBuild) {
|