@sudobility/auth_lib 0.0.101 → 0.0.102

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.
@@ -0,0 +1,184 @@
1
+ /**
2
+ * Firebase's web config, read at BUILD time from the app's native services
3
+ * files and written into the bundle — so a React Native app on Firebase's JS
4
+ * SDK is configured from `GoogleService-Info.plist` / `google-services.json`,
5
+ * the same files native Firebase (analytics, crashlytics, messaging, remote
6
+ * config, performance) already reads, rather than from a second copy of the
7
+ * same values in `.env`.
8
+ *
9
+ * Used from an app's `babel.config.js`:
10
+ *
11
+ * const { firebaseServiceConfigPlugin } =
12
+ * require('@sudobility/auth_lib/build/firebase-service-config');
13
+ * ...
14
+ * plugins: [
15
+ * firebaseServiceConfigPlugin(api, {
16
+ * ios: 'ios/MyApp/GoogleService-Info.plist',
17
+ * android: 'android/app/google-services.json',
18
+ * androidPackage: 'com.example.myapp',
19
+ * }),
20
+ * ...
21
+ * ]
22
+ *
23
+ * The plugin replaces these `process.env` references with literals for the
24
+ * platform Metro is bundling (Babel's `caller.platform`):
25
+ *
26
+ * FIREBASE_API_KEY, FIREBASE_AUTH_DOMAIN, FIREBASE_PROJECT_ID,
27
+ * FIREBASE_STORAGE_BUCKET, FIREBASE_MESSAGING_SENDER_ID, FIREBASE_APP_ID,
28
+ * GOOGLE_OAUTH_CLIENT_ID (the iOS-type client), GOOGLE_WEB_CLIENT_ID
29
+ *
30
+ * Each platform gets its OWN app's values — the iOS app's API key and app id
31
+ * on iOS, the Android app's on Android — which the Auth API accepts from the
32
+ * JS SDK (checked for every app in the family). macOS and Windows read the
33
+ * iOS file — always, even where a macOS plist exists: a desktop signs in with
34
+ * the iOS-type Google client, and one file for the Apple-and-desktop family
35
+ * means one set of values to keep right. With no platform
36
+ * (jest) every name becomes `undefined`, so the code's defaults apply.
37
+ *
38
+ * The library reads only the files the app names; it reads no environment.
39
+ */
40
+ 'use strict';
41
+
42
+ const fs = require('fs');
43
+ const path = require('path');
44
+
45
+ const NAMES = [
46
+ 'FIREBASE_API_KEY',
47
+ 'FIREBASE_AUTH_DOMAIN',
48
+ 'FIREBASE_PROJECT_ID',
49
+ 'FIREBASE_STORAGE_BUCKET',
50
+ 'FIREBASE_MESSAGING_SENDER_ID',
51
+ 'FIREBASE_APP_ID',
52
+ 'GOOGLE_OAUTH_CLIENT_ID',
53
+ 'GOOGLE_WEB_CLIENT_ID',
54
+ ];
55
+
56
+ /** `<key>K</key><string>V</string>` pairs — all a GoogleService-Info.plist holds. */
57
+ function parsePlist(text) {
58
+ const out = {};
59
+ const re = /<key>([^<]+)<\/key>\s*<string>([^<]*)<\/string>/g;
60
+ let m;
61
+ while ((m = re.exec(text)) !== null) out[m[1]] = m[2];
62
+ return out;
63
+ }
64
+
65
+ /** The values a GoogleService-Info.plist gives. */
66
+ function fromPlist(file) {
67
+ const p = parsePlist(fs.readFileSync(file, 'utf8'));
68
+ const projectId = p.PROJECT_ID || '';
69
+ return {
70
+ FIREBASE_API_KEY: p.API_KEY || '',
71
+ FIREBASE_AUTH_DOMAIN: projectId ? `${projectId}.firebaseapp.com` : '',
72
+ FIREBASE_PROJECT_ID: projectId,
73
+ FIREBASE_STORAGE_BUCKET: p.STORAGE_BUCKET || '',
74
+ FIREBASE_MESSAGING_SENDER_ID: p.GCM_SENDER_ID || '',
75
+ FIREBASE_APP_ID: p.GOOGLE_APP_ID || '',
76
+ GOOGLE_OAUTH_CLIENT_ID: p.CLIENT_ID || '',
77
+ GOOGLE_WEB_CLIENT_ID: '',
78
+ };
79
+ }
80
+
81
+ /**
82
+ * The values a google-services.json gives, for the client registered to
83
+ * `androidPackage` (a file may list several apps of one project).
84
+ */
85
+ function fromServicesJson(file, androidPackage) {
86
+ const j = JSON.parse(fs.readFileSync(file, 'utf8'));
87
+ const clients = j.client || [];
88
+ const client =
89
+ clients.find(
90
+ c =>
91
+ c.client_info &&
92
+ c.client_info.android_client_info &&
93
+ c.client_info.android_client_info.package_name === androidPackage
94
+ ) || (androidPackage ? null : clients[0]);
95
+ if (!client) {
96
+ throw new Error(
97
+ `google-services.json (${file}) has no client for package "${androidPackage}"`
98
+ );
99
+ }
100
+ const projectId = (j.project_info && j.project_info.project_id) || '';
101
+ const oauth = client.oauth_client || [];
102
+ const other =
103
+ (client.services &&
104
+ client.services.appinvite_service &&
105
+ client.services.appinvite_service.other_platform_oauth_client) ||
106
+ [];
107
+ const web = [...oauth, ...other].find(c => c.client_type === 3);
108
+ const ios = [...oauth, ...other].find(c => c.client_type === 2);
109
+ return {
110
+ FIREBASE_API_KEY: (client.api_key && client.api_key[0] && client.api_key[0].current_key) || '',
111
+ FIREBASE_AUTH_DOMAIN: projectId ? `${projectId}.firebaseapp.com` : '',
112
+ FIREBASE_PROJECT_ID: projectId,
113
+ FIREBASE_STORAGE_BUCKET: (j.project_info && j.project_info.storage_bucket) || '',
114
+ FIREBASE_MESSAGING_SENDER_ID: (j.project_info && j.project_info.project_number) || '',
115
+ FIREBASE_APP_ID: (client.client_info && client.client_info.mobilesdk_app_id) || '',
116
+ GOOGLE_OAUTH_CLIENT_ID: (ios && ios.client_id) || '',
117
+ GOOGLE_WEB_CLIENT_ID: (web && web.client_id) || '',
118
+ };
119
+ }
120
+
121
+ /**
122
+ * The values for one platform, or null when the platform is unknown.
123
+ * `files` paths are relative to `root` (the app; default: cwd).
124
+ */
125
+ function firebaseServiceConfig(platform, files, root) {
126
+ const base = root || process.cwd();
127
+ const at = p => path.resolve(base, p);
128
+ switch (platform) {
129
+ case 'ios':
130
+ return fromPlist(at(files.ios));
131
+ case 'android':
132
+ return fromServicesJson(at(files.android), files.androidPackage);
133
+ case 'macos':
134
+ case 'windows':
135
+ return fromPlist(at(files.ios));
136
+ default:
137
+ return null;
138
+ }
139
+ }
140
+
141
+ const cache = new Map();
142
+
143
+ /**
144
+ * A Babel plugin entry that inlines the platform's values. Reads the files
145
+ * once per platform per process.
146
+ */
147
+ function firebaseServiceConfigPlugin(api, files) {
148
+ const platform = api.caller(caller => (caller && caller.platform) || null);
149
+ const root = files.root || process.cwd();
150
+ const key = `${root}|${platform}`;
151
+ if (!cache.has(key)) cache.set(key, firebaseServiceConfig(platform, files, root));
152
+ const values = cache.get(key);
153
+
154
+ return function inlineFirebaseServiceConfig({ types: t }) {
155
+ return {
156
+ name: 'inline-firebase-service-config',
157
+ visitor: {
158
+ MemberExpression(p) {
159
+ const node = p.node;
160
+ if (
161
+ !t.isMemberExpression(node.object) ||
162
+ !t.isIdentifier(node.object.object, { name: 'process' }) ||
163
+ !t.isIdentifier(node.object.property, { name: 'env' })
164
+ ) {
165
+ return;
166
+ }
167
+ const name = node.computed
168
+ ? t.isStringLiteral(node.property) && node.property.value
169
+ : t.isIdentifier(node.property) && node.property.name;
170
+ if (!name || !NAMES.includes(name)) return;
171
+ const value = values && values[name];
172
+ p.replaceWith(value ? t.stringLiteral(value) : t.identifier('undefined'));
173
+ },
174
+ },
175
+ };
176
+ };
177
+ }
178
+
179
+ module.exports = {
180
+ FIREBASE_SERVICE_CONFIG_NAMES: NAMES,
181
+ firebaseServiceConfig,
182
+ firebaseServiceConfigPlugin,
183
+ parsePlist,
184
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sudobility/auth_lib",
3
- "version": "0.0.101",
3
+ "version": "0.0.102",
4
4
  "description": "Firebase authentication utilities with token refresh handling",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -23,6 +23,10 @@
23
23
  "import": "./dist/signin/index.js",
24
24
  "types": "./dist/signin/index.d.ts"
25
25
  },
26
+ "./build/firebase-service-config": {
27
+ "require": "./build/firebase-service-config.cjs",
28
+ "default": "./build/firebase-service-config.cjs"
29
+ },
26
30
  "./account": {
27
31
  "import": "./dist/account/index.js",
28
32
  "types": "./dist/account/index.d.ts"
@@ -38,6 +42,7 @@
38
42
  },
39
43
  "files": [
40
44
  "dist",
45
+ "build/firebase-service-config.cjs",
41
46
  "README.md"
42
47
  ],
43
48
  "scripts": {
@@ -73,22 +78,23 @@
73
78
  }
74
79
  },
75
80
  "devDependencies": {
76
- "firebase": "^12.7.0",
81
+ "@babel/core": "^7",
82
+ "@eslint/js": "^9.0.0",
77
83
  "@sudobility/di": "^1.5.67",
78
84
  "@sudobility/types": "^1.9.68",
79
85
  "@tanstack/react-query": "^5.80.7",
80
- "@types/node": "^22.0.0",
81
- "vitest": "^4.0.4",
82
- "@eslint/js": "^9.0.0",
83
86
  "@types/bun": "^1.2.8",
87
+ "@types/node": "^22.0.0",
84
88
  "@types/react": "^19.2.5",
85
89
  "@typescript-eslint/eslint-plugin": "^8.0.0",
86
90
  "@typescript-eslint/parser": "^8.0.0",
87
91
  "eslint": "^9.0.0",
88
92
  "eslint-config-prettier": "^10.0.0",
89
93
  "eslint-plugin-prettier": "^5.0.0",
94
+ "firebase": "^12.7.0",
90
95
  "prettier": "^3.0.0",
91
- "typescript": "~5.9.3"
96
+ "typescript": "~5.9.3",
97
+ "vitest": "^4.0.4"
92
98
  },
93
99
  "publishConfig": {
94
100
  "access": "public"