@volley/vwr-loader 1.0.4 → 1.0.6
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 +45 -0
- package/dist/amplitudeFlagFetcher.d.ts +4 -2
- package/dist/amplitudeFlagFetcher.d.ts.map +1 -1
- package/dist/amplitudeFlagFetcher.js +26 -19
- package/dist/amplitudeFlagFetcher.js.map +1 -1
- package/dist/cli.js +86 -42
- package/dist/cli.js.map +1 -1
- package/dist/envDefaults.d.ts.map +1 -1
- package/dist/envDefaults.js +10 -0
- package/dist/envDefaults.js.map +1 -1
- package/dist/getDeviceId.d.ts.map +1 -1
- package/dist/getDeviceId.js +5 -8
- package/dist/getDeviceId.js.map +1 -1
- package/dist/getEnvironment.d.ts +25 -0
- package/dist/getEnvironment.d.ts.map +1 -0
- package/dist/getEnvironment.js +105 -0
- package/dist/getEnvironment.js.map +1 -0
- package/dist/loadVwr.d.ts +6 -1
- package/dist/loadVwr.d.ts.map +1 -1
- package/dist/loadVwr.js +14 -4
- package/dist/loadVwr.js.map +1 -1
- package/dist/main.js.map +1 -1
- package/dist/vwrConfig.d.ts.map +1 -1
- package/dist/vwrConfig.js +139 -128
- package/dist/vwrConfig.js.map +1 -1
- package/package.json +2 -2
- package/src/amplitudeFlagFetcher.test.ts +36 -64
- package/src/amplitudeFlagFetcher.ts +31 -22
- package/src/envDefaults.ts +10 -0
- package/src/getDeviceId.ts +9 -9
- package/src/getEnvironment.test.ts +457 -0
- package/src/getEnvironment.ts +140 -0
- package/src/loadVwr.ts +22 -4
- package/src/vwrConfig.test.ts +2 -2
- package/src/vwrConfig.ts +191 -209
|
@@ -0,0 +1,457 @@
|
|
|
1
|
+
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"
|
|
2
|
+
|
|
3
|
+
import { type Environment, getEnvironment } from "./getEnvironment"
|
|
4
|
+
|
|
5
|
+
describe("getEnvironment", () => {
|
|
6
|
+
const mockLogger = {
|
|
7
|
+
info: vi.fn(),
|
|
8
|
+
warn: vi.fn(),
|
|
9
|
+
error: vi.fn(),
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
beforeEach(() => {
|
|
13
|
+
vi.clearAllMocks()
|
|
14
|
+
mockLogger.info.mockClear()
|
|
15
|
+
mockLogger.warn.mockClear()
|
|
16
|
+
mockLogger.error.mockClear()
|
|
17
|
+
// Clean up window properties
|
|
18
|
+
delete (window as any).Capacitor
|
|
19
|
+
})
|
|
20
|
+
|
|
21
|
+
afterEach(() => {
|
|
22
|
+
vi.restoreAllMocks()
|
|
23
|
+
delete (window as any).Capacitor
|
|
24
|
+
})
|
|
25
|
+
|
|
26
|
+
describe("Fire TV - Native Environment Detection", () => {
|
|
27
|
+
it("should read environment from native BuildConfig for Fire TV", async () => {
|
|
28
|
+
// Setup Capacitor plugin mock
|
|
29
|
+
Object.defineProperty(window, "Capacitor", {
|
|
30
|
+
value: {
|
|
31
|
+
Plugins: {
|
|
32
|
+
DeviceInfo: {
|
|
33
|
+
getNativeShellAppEnvironment: vi
|
|
34
|
+
.fn()
|
|
35
|
+
.mockResolvedValue({
|
|
36
|
+
environment: "development",
|
|
37
|
+
}),
|
|
38
|
+
},
|
|
39
|
+
},
|
|
40
|
+
},
|
|
41
|
+
writable: true,
|
|
42
|
+
configurable: true,
|
|
43
|
+
})
|
|
44
|
+
|
|
45
|
+
const result = await getEnvironment(
|
|
46
|
+
"FIRE_TV",
|
|
47
|
+
undefined,
|
|
48
|
+
mockLogger
|
|
49
|
+
)
|
|
50
|
+
|
|
51
|
+
expect(result.environment).toBe("dev")
|
|
52
|
+
expect(result.source).toBe("native")
|
|
53
|
+
expect(mockLogger.info).toHaveBeenCalledWith(
|
|
54
|
+
expect.stringContaining(
|
|
55
|
+
'Environment: "dev" (source: native BuildConfig.ENVIRONMENT="development")'
|
|
56
|
+
)
|
|
57
|
+
)
|
|
58
|
+
})
|
|
59
|
+
|
|
60
|
+
it("should map 'production' to 'prod'", async () => {
|
|
61
|
+
Object.defineProperty(window, "Capacitor", {
|
|
62
|
+
value: {
|
|
63
|
+
Plugins: {
|
|
64
|
+
DeviceInfo: {
|
|
65
|
+
getNativeShellAppEnvironment: vi
|
|
66
|
+
.fn()
|
|
67
|
+
.mockResolvedValue({
|
|
68
|
+
environment: "production",
|
|
69
|
+
}),
|
|
70
|
+
},
|
|
71
|
+
},
|
|
72
|
+
},
|
|
73
|
+
writable: true,
|
|
74
|
+
configurable: true,
|
|
75
|
+
})
|
|
76
|
+
|
|
77
|
+
const result = await getEnvironment(
|
|
78
|
+
"FIRE_TV",
|
|
79
|
+
undefined,
|
|
80
|
+
mockLogger
|
|
81
|
+
)
|
|
82
|
+
|
|
83
|
+
expect(result.environment).toBe("prod")
|
|
84
|
+
expect(result.source).toBe("native")
|
|
85
|
+
})
|
|
86
|
+
|
|
87
|
+
it("should map 'staging' to 'staging'", async () => {
|
|
88
|
+
Object.defineProperty(window, "Capacitor", {
|
|
89
|
+
value: {
|
|
90
|
+
Plugins: {
|
|
91
|
+
DeviceInfo: {
|
|
92
|
+
getNativeShellAppEnvironment: vi
|
|
93
|
+
.fn()
|
|
94
|
+
.mockResolvedValue({
|
|
95
|
+
environment: "staging",
|
|
96
|
+
}),
|
|
97
|
+
},
|
|
98
|
+
},
|
|
99
|
+
},
|
|
100
|
+
writable: true,
|
|
101
|
+
configurable: true,
|
|
102
|
+
})
|
|
103
|
+
|
|
104
|
+
const result = await getEnvironment(
|
|
105
|
+
"FIRE_TV",
|
|
106
|
+
undefined,
|
|
107
|
+
mockLogger
|
|
108
|
+
)
|
|
109
|
+
|
|
110
|
+
expect(result.environment).toBe("staging")
|
|
111
|
+
expect(result.source).toBe("native")
|
|
112
|
+
})
|
|
113
|
+
|
|
114
|
+
it("should handle native env already in vwr format (dev)", async () => {
|
|
115
|
+
Object.defineProperty(window, "Capacitor", {
|
|
116
|
+
value: {
|
|
117
|
+
Plugins: {
|
|
118
|
+
DeviceInfo: {
|
|
119
|
+
getNativeShellAppEnvironment: vi
|
|
120
|
+
.fn()
|
|
121
|
+
.mockResolvedValue({
|
|
122
|
+
environment: "dev",
|
|
123
|
+
}),
|
|
124
|
+
},
|
|
125
|
+
},
|
|
126
|
+
},
|
|
127
|
+
writable: true,
|
|
128
|
+
configurable: true,
|
|
129
|
+
})
|
|
130
|
+
|
|
131
|
+
const result = await getEnvironment(
|
|
132
|
+
"FIRE_TV",
|
|
133
|
+
undefined,
|
|
134
|
+
mockLogger
|
|
135
|
+
)
|
|
136
|
+
|
|
137
|
+
expect(result.environment).toBe("dev")
|
|
138
|
+
expect(result.source).toBe("native")
|
|
139
|
+
})
|
|
140
|
+
|
|
141
|
+
it("should fallback to dev when native returns unknown value", async () => {
|
|
142
|
+
Object.defineProperty(window, "Capacitor", {
|
|
143
|
+
value: {
|
|
144
|
+
Plugins: {
|
|
145
|
+
DeviceInfo: {
|
|
146
|
+
getNativeShellAppEnvironment: vi
|
|
147
|
+
.fn()
|
|
148
|
+
.mockResolvedValue({
|
|
149
|
+
environment: "unknown-env",
|
|
150
|
+
}),
|
|
151
|
+
},
|
|
152
|
+
},
|
|
153
|
+
},
|
|
154
|
+
writable: true,
|
|
155
|
+
configurable: true,
|
|
156
|
+
})
|
|
157
|
+
|
|
158
|
+
const result = await getEnvironment(
|
|
159
|
+
"FIRE_TV",
|
|
160
|
+
undefined,
|
|
161
|
+
mockLogger
|
|
162
|
+
)
|
|
163
|
+
|
|
164
|
+
expect(result.environment).toBe("dev")
|
|
165
|
+
expect(result.source).toBe("native")
|
|
166
|
+
})
|
|
167
|
+
|
|
168
|
+
it("should fallback to build-time when native plugin unavailable", async () => {
|
|
169
|
+
// No Capacitor plugin available
|
|
170
|
+
const result = await getEnvironment(
|
|
171
|
+
"FIRE_TV",
|
|
172
|
+
"staging",
|
|
173
|
+
mockLogger
|
|
174
|
+
)
|
|
175
|
+
|
|
176
|
+
expect(result.environment).toBe("staging")
|
|
177
|
+
expect(result.source).toBe("build-time")
|
|
178
|
+
expect(mockLogger.warn).toHaveBeenCalledWith(
|
|
179
|
+
expect.stringContaining(
|
|
180
|
+
"Failed to read environment from native, falling back to build-time"
|
|
181
|
+
)
|
|
182
|
+
)
|
|
183
|
+
})
|
|
184
|
+
|
|
185
|
+
it("should fallback to build-time when native plugin throws error", async () => {
|
|
186
|
+
Object.defineProperty(window, "Capacitor", {
|
|
187
|
+
value: {
|
|
188
|
+
Plugins: {
|
|
189
|
+
DeviceInfo: {
|
|
190
|
+
getNativeShellAppEnvironment: vi
|
|
191
|
+
.fn()
|
|
192
|
+
.mockRejectedValue(
|
|
193
|
+
new Error("Native plugin error")
|
|
194
|
+
),
|
|
195
|
+
},
|
|
196
|
+
},
|
|
197
|
+
},
|
|
198
|
+
writable: true,
|
|
199
|
+
configurable: true,
|
|
200
|
+
})
|
|
201
|
+
|
|
202
|
+
const result = await getEnvironment("FIRE_TV", "prod", mockLogger)
|
|
203
|
+
|
|
204
|
+
expect(result.environment).toBe("prod")
|
|
205
|
+
expect(result.source).toBe("build-time")
|
|
206
|
+
expect(mockLogger.error).toHaveBeenCalled()
|
|
207
|
+
expect(mockLogger.warn).toHaveBeenCalledWith(
|
|
208
|
+
expect.stringContaining("falling back to build-time")
|
|
209
|
+
)
|
|
210
|
+
})
|
|
211
|
+
|
|
212
|
+
it("should fallback to build-time when native returns empty", async () => {
|
|
213
|
+
Object.defineProperty(window, "Capacitor", {
|
|
214
|
+
value: {
|
|
215
|
+
Plugins: {
|
|
216
|
+
DeviceInfo: {
|
|
217
|
+
getNativeShellAppEnvironment: vi
|
|
218
|
+
.fn()
|
|
219
|
+
.mockResolvedValue({
|
|
220
|
+
environment: "",
|
|
221
|
+
}),
|
|
222
|
+
},
|
|
223
|
+
},
|
|
224
|
+
},
|
|
225
|
+
writable: true,
|
|
226
|
+
configurable: true,
|
|
227
|
+
})
|
|
228
|
+
|
|
229
|
+
const result = await getEnvironment("FIRE_TV", "dev", mockLogger)
|
|
230
|
+
|
|
231
|
+
expect(result.environment).toBe("dev")
|
|
232
|
+
expect(result.source).toBe("build-time")
|
|
233
|
+
expect(mockLogger.error).toHaveBeenCalledWith(
|
|
234
|
+
expect.stringContaining(
|
|
235
|
+
"DeviceInfo.getNativeShellAppEnvironment returned empty"
|
|
236
|
+
)
|
|
237
|
+
)
|
|
238
|
+
})
|
|
239
|
+
|
|
240
|
+
it("should fallback to default when both native and build-time fail", async () => {
|
|
241
|
+
// No Capacitor plugin, no build-time env
|
|
242
|
+
const result = await getEnvironment(
|
|
243
|
+
"FIRE_TV",
|
|
244
|
+
undefined,
|
|
245
|
+
mockLogger
|
|
246
|
+
)
|
|
247
|
+
|
|
248
|
+
expect(result.environment).toBe("dev")
|
|
249
|
+
expect(result.source).toBe("default")
|
|
250
|
+
expect(mockLogger.warn).toHaveBeenCalledWith(
|
|
251
|
+
expect.stringContaining(
|
|
252
|
+
'Environment: "dev" (source: default fallback'
|
|
253
|
+
)
|
|
254
|
+
)
|
|
255
|
+
})
|
|
256
|
+
|
|
257
|
+
it("should be case-insensitive for Fire TV platform", async () => {
|
|
258
|
+
Object.defineProperty(window, "Capacitor", {
|
|
259
|
+
value: {
|
|
260
|
+
Plugins: {
|
|
261
|
+
DeviceInfo: {
|
|
262
|
+
getNativeShellAppEnvironment: vi
|
|
263
|
+
.fn()
|
|
264
|
+
.mockResolvedValue({
|
|
265
|
+
environment: "production",
|
|
266
|
+
}),
|
|
267
|
+
},
|
|
268
|
+
},
|
|
269
|
+
},
|
|
270
|
+
writable: true,
|
|
271
|
+
configurable: true,
|
|
272
|
+
})
|
|
273
|
+
|
|
274
|
+
const result1 = await getEnvironment(
|
|
275
|
+
"fire_tv",
|
|
276
|
+
undefined,
|
|
277
|
+
mockLogger
|
|
278
|
+
)
|
|
279
|
+
const result2 = await getEnvironment(
|
|
280
|
+
"FIRE_TV",
|
|
281
|
+
undefined,
|
|
282
|
+
mockLogger
|
|
283
|
+
)
|
|
284
|
+
|
|
285
|
+
expect(result1.environment).toBe("prod")
|
|
286
|
+
expect(result2.environment).toBe("prod")
|
|
287
|
+
})
|
|
288
|
+
})
|
|
289
|
+
|
|
290
|
+
describe("Non-Fire TV Platforms - Build-time Environment", () => {
|
|
291
|
+
const platforms = ["SAMSUNG_TV", "LG_TV", "MOBILE", "WEB"]
|
|
292
|
+
|
|
293
|
+
platforms.forEach((platform) => {
|
|
294
|
+
describe(`${platform}`, () => {
|
|
295
|
+
it("should use build-time environment when provided", async () => {
|
|
296
|
+
const result = await getEnvironment(
|
|
297
|
+
platform,
|
|
298
|
+
"staging",
|
|
299
|
+
mockLogger
|
|
300
|
+
)
|
|
301
|
+
|
|
302
|
+
expect(result.environment).toBe("staging")
|
|
303
|
+
expect(result.source).toBe("build-time")
|
|
304
|
+
expect(mockLogger.info).toHaveBeenCalledWith(
|
|
305
|
+
expect.stringContaining(
|
|
306
|
+
'Environment: "staging" (source: build-time CLI injection)'
|
|
307
|
+
)
|
|
308
|
+
)
|
|
309
|
+
})
|
|
310
|
+
|
|
311
|
+
it("should use build-time 'prod' environment", async () => {
|
|
312
|
+
const result = await getEnvironment(
|
|
313
|
+
platform,
|
|
314
|
+
"prod",
|
|
315
|
+
mockLogger
|
|
316
|
+
)
|
|
317
|
+
|
|
318
|
+
expect(result.environment).toBe("prod")
|
|
319
|
+
expect(result.source).toBe("build-time")
|
|
320
|
+
})
|
|
321
|
+
|
|
322
|
+
it("should use build-time 'dev' environment", async () => {
|
|
323
|
+
const result = await getEnvironment(
|
|
324
|
+
platform,
|
|
325
|
+
"dev",
|
|
326
|
+
mockLogger
|
|
327
|
+
)
|
|
328
|
+
|
|
329
|
+
expect(result.environment).toBe("dev")
|
|
330
|
+
expect(result.source).toBe("build-time")
|
|
331
|
+
})
|
|
332
|
+
|
|
333
|
+
it("should use build-time 'local' environment", async () => {
|
|
334
|
+
const result = await getEnvironment(
|
|
335
|
+
platform,
|
|
336
|
+
"local",
|
|
337
|
+
mockLogger
|
|
338
|
+
)
|
|
339
|
+
|
|
340
|
+
expect(result.environment).toBe("local")
|
|
341
|
+
expect(result.source).toBe("build-time")
|
|
342
|
+
})
|
|
343
|
+
|
|
344
|
+
it("should fallback to default when build-time not provided", async () => {
|
|
345
|
+
const result = await getEnvironment(
|
|
346
|
+
platform,
|
|
347
|
+
undefined,
|
|
348
|
+
mockLogger
|
|
349
|
+
)
|
|
350
|
+
|
|
351
|
+
expect(result.environment).toBe("dev")
|
|
352
|
+
expect(result.source).toBe("default")
|
|
353
|
+
expect(mockLogger.warn).toHaveBeenCalledWith(
|
|
354
|
+
expect.stringContaining(
|
|
355
|
+
'Environment: "dev" (source: default fallback'
|
|
356
|
+
)
|
|
357
|
+
)
|
|
358
|
+
})
|
|
359
|
+
|
|
360
|
+
it("should fallback to default when build-time is invalid", async () => {
|
|
361
|
+
const result = await getEnvironment(
|
|
362
|
+
platform,
|
|
363
|
+
"invalid-env",
|
|
364
|
+
mockLogger
|
|
365
|
+
)
|
|
366
|
+
|
|
367
|
+
expect(result.environment).toBe("dev")
|
|
368
|
+
expect(result.source).toBe("default")
|
|
369
|
+
})
|
|
370
|
+
})
|
|
371
|
+
})
|
|
372
|
+
})
|
|
373
|
+
|
|
374
|
+
describe("Environment Validation", () => {
|
|
375
|
+
it("should accept valid environments", async () => {
|
|
376
|
+
const validEnvs: Environment[] = [
|
|
377
|
+
"local",
|
|
378
|
+
"dev",
|
|
379
|
+
"qa",
|
|
380
|
+
"staging",
|
|
381
|
+
"prod",
|
|
382
|
+
]
|
|
383
|
+
|
|
384
|
+
for (const env of validEnvs) {
|
|
385
|
+
const result = await getEnvironment("WEB", env, mockLogger)
|
|
386
|
+
expect(result.environment).toBe(env)
|
|
387
|
+
expect(result.source).toBe("build-time")
|
|
388
|
+
}
|
|
389
|
+
})
|
|
390
|
+
|
|
391
|
+
it("should reject invalid environments", async () => {
|
|
392
|
+
const invalidEnvs = [
|
|
393
|
+
"development",
|
|
394
|
+
"production",
|
|
395
|
+
"test",
|
|
396
|
+
"",
|
|
397
|
+
"random",
|
|
398
|
+
]
|
|
399
|
+
|
|
400
|
+
for (const env of invalidEnvs) {
|
|
401
|
+
const result = await getEnvironment("WEB", env, mockLogger)
|
|
402
|
+
expect(result.environment).toBe("dev")
|
|
403
|
+
expect(result.source).toBe("default")
|
|
404
|
+
}
|
|
405
|
+
})
|
|
406
|
+
})
|
|
407
|
+
|
|
408
|
+
describe("Logging", () => {
|
|
409
|
+
it("should log native environment source", async () => {
|
|
410
|
+
Object.defineProperty(window, "Capacitor", {
|
|
411
|
+
value: {
|
|
412
|
+
Plugins: {
|
|
413
|
+
DeviceInfo: {
|
|
414
|
+
getNativeShellAppEnvironment: vi
|
|
415
|
+
.fn()
|
|
416
|
+
.mockResolvedValue({
|
|
417
|
+
environment: "production",
|
|
418
|
+
}),
|
|
419
|
+
},
|
|
420
|
+
},
|
|
421
|
+
},
|
|
422
|
+
writable: true,
|
|
423
|
+
configurable: true,
|
|
424
|
+
})
|
|
425
|
+
|
|
426
|
+
await getEnvironment("FIRE_TV", undefined, mockLogger)
|
|
427
|
+
|
|
428
|
+
expect(mockLogger.info).toHaveBeenCalledWith(
|
|
429
|
+
'[Shell] Environment: "prod" (source: native BuildConfig.ENVIRONMENT="production")'
|
|
430
|
+
)
|
|
431
|
+
})
|
|
432
|
+
|
|
433
|
+
it("should log build-time environment source", async () => {
|
|
434
|
+
await getEnvironment("SAMSUNG_TV", "staging", mockLogger)
|
|
435
|
+
|
|
436
|
+
expect(mockLogger.info).toHaveBeenCalledWith(
|
|
437
|
+
'[Shell] Environment: "staging" (source: build-time CLI injection)'
|
|
438
|
+
)
|
|
439
|
+
})
|
|
440
|
+
|
|
441
|
+
it("should warn when falling back to default", async () => {
|
|
442
|
+
await getEnvironment("LG_TV", undefined, mockLogger)
|
|
443
|
+
|
|
444
|
+
expect(mockLogger.warn).toHaveBeenCalledWith(
|
|
445
|
+
'[Shell] Environment: "dev" (source: default fallback - no native or build-time env found)'
|
|
446
|
+
)
|
|
447
|
+
})
|
|
448
|
+
|
|
449
|
+
it("should warn when native fails and falls back", async () => {
|
|
450
|
+
await getEnvironment("FIRE_TV", "prod", mockLogger)
|
|
451
|
+
|
|
452
|
+
expect(mockLogger.warn).toHaveBeenCalledWith(
|
|
453
|
+
"[Shell] Failed to read environment from native, falling back to build-time"
|
|
454
|
+
)
|
|
455
|
+
})
|
|
456
|
+
})
|
|
457
|
+
})
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
import { defaultLogger, type Logger } from "./logger"
|
|
2
|
+
|
|
3
|
+
export type Environment = "local" | "dev" | "qa" | "staging" | "prod"
|
|
4
|
+
export type EnvironmentSource = "native" | "build-time" | "default"
|
|
5
|
+
|
|
6
|
+
export interface EnvironmentResult {
|
|
7
|
+
environment: Environment
|
|
8
|
+
source: EnvironmentSource
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Get the environment for the current platform.
|
|
13
|
+
*
|
|
14
|
+
* Precedence:
|
|
15
|
+
* 1. Native shell (Fire TV only) - reads from BuildConfig.ENVIRONMENT
|
|
16
|
+
* 2. Build-time injection (VITE_ENVIRONMENT) - CLI override
|
|
17
|
+
* 3. Default fallback ("dev")
|
|
18
|
+
*
|
|
19
|
+
* This ensures production APKs cannot accidentally run dev VWR,
|
|
20
|
+
* since the native Android build variant determines the environment.
|
|
21
|
+
*
|
|
22
|
+
* @param platform - Platform identifier ('FIRE_TV', 'SAMSUNG_TV', 'LG_TV', 'MOBILE', 'WEB')
|
|
23
|
+
* @param buildTimeEnv - Optional build-time injected environment (from CLI)
|
|
24
|
+
* @param logger - Optional logger for reporting
|
|
25
|
+
* @returns Environment result with source information for logging
|
|
26
|
+
*/
|
|
27
|
+
export async function getEnvironment(
|
|
28
|
+
platform: string,
|
|
29
|
+
buildTimeEnv: string | undefined,
|
|
30
|
+
logger: Logger = defaultLogger
|
|
31
|
+
): Promise<EnvironmentResult> {
|
|
32
|
+
const normalizedPlatform = platform.toUpperCase()
|
|
33
|
+
|
|
34
|
+
// For Fire TV, try to read from native first (safest - prevents env mismatch)
|
|
35
|
+
if (normalizedPlatform === "FIRE_TV") {
|
|
36
|
+
const nativeEnv = await getFireTVEnvironment(logger)
|
|
37
|
+
if (nativeEnv) {
|
|
38
|
+
const mapped = mapNativeEnvironment(nativeEnv)
|
|
39
|
+
logger.info(
|
|
40
|
+
`[Shell] Environment: "${mapped}" (source: native BuildConfig.ENVIRONMENT="${nativeEnv}")`
|
|
41
|
+
)
|
|
42
|
+
return { environment: mapped, source: "native" }
|
|
43
|
+
}
|
|
44
|
+
// Fall through to build-time if native fails
|
|
45
|
+
logger.warn(
|
|
46
|
+
"[Shell] Failed to read environment from native, falling back to build-time"
|
|
47
|
+
)
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// Build-time injection (CLI --env flag)
|
|
51
|
+
if (buildTimeEnv) {
|
|
52
|
+
const normalizedBuildTimeEnv = buildTimeEnv.toLowerCase()
|
|
53
|
+
if (isValidEnvironment(normalizedBuildTimeEnv)) {
|
|
54
|
+
logger.info(
|
|
55
|
+
`[Shell] Environment: "${normalizedBuildTimeEnv}" (source: build-time CLI injection)`
|
|
56
|
+
)
|
|
57
|
+
return {
|
|
58
|
+
environment: normalizedBuildTimeEnv as Environment,
|
|
59
|
+
source: "build-time",
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// Default fallback
|
|
65
|
+
const defaultEnv: Environment = "dev"
|
|
66
|
+
logger.warn(
|
|
67
|
+
`[Shell] Environment: "${defaultEnv}" (source: default fallback - no native or build-time env found)`
|
|
68
|
+
)
|
|
69
|
+
return { environment: defaultEnv, source: "default" }
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Read environment from Fire TV native shell via Capacitor plugin.
|
|
74
|
+
*/
|
|
75
|
+
async function getFireTVEnvironment(logger: Logger): Promise<string | null> {
|
|
76
|
+
try {
|
|
77
|
+
if (
|
|
78
|
+
!window.Capacitor?.Plugins?.DeviceInfo?.getNativeShellAppEnvironment
|
|
79
|
+
) {
|
|
80
|
+
logger.warn(
|
|
81
|
+
"[Shell] DeviceInfo.getNativeShellAppEnvironment not available"
|
|
82
|
+
)
|
|
83
|
+
return null
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
const result =
|
|
87
|
+
await window.Capacitor.Plugins.DeviceInfo.getNativeShellAppEnvironment()
|
|
88
|
+
if (result?.environment && result.environment.trim()) {
|
|
89
|
+
return result.environment
|
|
90
|
+
} else {
|
|
91
|
+
logger.error(
|
|
92
|
+
"[Shell] DeviceInfo.getNativeShellAppEnvironment returned empty"
|
|
93
|
+
)
|
|
94
|
+
}
|
|
95
|
+
} catch (error) {
|
|
96
|
+
logger.error(
|
|
97
|
+
"[Shell] DeviceInfo.getNativeShellAppEnvironment failed:",
|
|
98
|
+
{ error }
|
|
99
|
+
)
|
|
100
|
+
}
|
|
101
|
+
return null
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* Map native Android BuildConfig.ENVIRONMENT values to vwr-loader environment names.
|
|
106
|
+
*
|
|
107
|
+
* Android BuildConfig values:
|
|
108
|
+
* - "development" (debug build)
|
|
109
|
+
* - "staging" (staging build)
|
|
110
|
+
* - "production" (release build)
|
|
111
|
+
*
|
|
112
|
+
* VWR environment names:
|
|
113
|
+
* - "dev"
|
|
114
|
+
* - "staging"
|
|
115
|
+
* - "prod"
|
|
116
|
+
*/
|
|
117
|
+
function mapNativeEnvironment(nativeEnv: string): Environment {
|
|
118
|
+
const normalized = nativeEnv.toLowerCase()
|
|
119
|
+
switch (normalized) {
|
|
120
|
+
case "development":
|
|
121
|
+
return "dev"
|
|
122
|
+
case "staging":
|
|
123
|
+
return "staging"
|
|
124
|
+
case "production":
|
|
125
|
+
return "prod"
|
|
126
|
+
default:
|
|
127
|
+
// If it's already in vwr format, use it
|
|
128
|
+
if (isValidEnvironment(normalized)) {
|
|
129
|
+
return normalized as Environment
|
|
130
|
+
}
|
|
131
|
+
return "dev"
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
function isValidEnvironment(env: string): boolean {
|
|
136
|
+
return ["local", "dev", "qa", "staging", "prod"].includes(env)
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
// Note: Window.Capacitor type is declared in getDeviceId.ts
|
|
140
|
+
// We just need to add the getNativeShellAppEnvironment method type there
|
package/src/loadVwr.ts
CHANGED
|
@@ -3,13 +3,17 @@ import type * as VWR from "@volley/vwr"
|
|
|
3
3
|
import { fetchAmplitudeFlags } from "./amplitudeFlagFetcher"
|
|
4
4
|
import { ENV_DEFAULTS } from "./envDefaults"
|
|
5
5
|
import { getDeviceId } from "./getDeviceId"
|
|
6
|
+
import { getEnvironment } from "./getEnvironment"
|
|
6
7
|
import { getShellVersion } from "./getShellVersion"
|
|
7
8
|
import { defaultLogger, type Logger } from "./logger"
|
|
8
9
|
import type { VWRConfig, VWRConfigRequest } from "./vwrConfig"
|
|
9
10
|
import { getVWRConfig, validateConfig } from "./vwrConfig"
|
|
10
11
|
|
|
11
12
|
// Vite injects these at build time
|
|
12
|
-
|
|
13
|
+
// VITE_ENVIRONMENT is optional for platforms that support native env reading (Fire TV)
|
|
14
|
+
const BUILD_TIME_ENVIRONMENT = import.meta.env.VITE_ENVIRONMENT as
|
|
15
|
+
| string
|
|
16
|
+
| undefined
|
|
13
17
|
const PLATFORM = import.meta.env.VITE_PLATFORM
|
|
14
18
|
const CONFIG_URL = import.meta.env.VITE_CONFIG_URL
|
|
15
19
|
const CONFIG_FILE = import.meta.env.VITE_CONFIG_FILE
|
|
@@ -28,15 +32,20 @@ export type { Logger }
|
|
|
28
32
|
* this reduces startup latency from ~2-4s to ~500ms by skipping config fetches.
|
|
29
33
|
*
|
|
30
34
|
* Flow:
|
|
31
|
-
* 1. Get deviceId, shellVersion (fast, local)
|
|
35
|
+
* 1. Get deviceId, shellVersion, environment (fast, local/native)
|
|
32
36
|
* 2. Check vwr-enabled flag (single request, ~500ms)
|
|
33
37
|
* 3. If OFF → throw immediately (no config fetches)
|
|
34
38
|
* 4. If ON → fetch config, load VWR module, initialize
|
|
35
39
|
*
|
|
40
|
+
* Environment resolution:
|
|
41
|
+
* - Fire TV: reads from native BuildConfig.ENVIRONMENT (prevents env mismatch)
|
|
42
|
+
* - Other platforms: uses build-time injected VITE_ENVIRONMENT
|
|
43
|
+
* - Fallback: "dev"
|
|
44
|
+
*
|
|
36
45
|
* @param logger - Optional logger, defaults to console
|
|
37
46
|
*/
|
|
38
47
|
export const loadVwr = async (logger: Logger = defaultLogger) => {
|
|
39
|
-
if (!
|
|
48
|
+
if (!PLATFORM || !CONFIG_URL || !CONFIG_FILE) {
|
|
40
49
|
throw new Error("[Shell] Build config not injected properly")
|
|
41
50
|
}
|
|
42
51
|
|
|
@@ -48,6 +57,14 @@ export const loadVwr = async (logger: Logger = defaultLogger) => {
|
|
|
48
57
|
}
|
|
49
58
|
const shellVersion = await getShellVersion(PLATFORM, logger)
|
|
50
59
|
|
|
60
|
+
// Get environment from native (Fire TV) or build-time injection
|
|
61
|
+
// This ensures prod APKs cannot accidentally run dev VWR
|
|
62
|
+
const { environment: ENVIRONMENT } = await getEnvironment(
|
|
63
|
+
PLATFORM,
|
|
64
|
+
BUILD_TIME_ENVIRONMENT,
|
|
65
|
+
logger
|
|
66
|
+
)
|
|
67
|
+
|
|
51
68
|
// Get amplitude key: injected at build time OR from envDefaults
|
|
52
69
|
// Precedence: build-time injection > envDefaults
|
|
53
70
|
let amplitudeKey = AMPLITUDE_KEY
|
|
@@ -68,7 +85,8 @@ export const loadVwr = async (logger: Logger = defaultLogger) => {
|
|
|
68
85
|
apiKey: amplitudeKey,
|
|
69
86
|
timeout: 2000,
|
|
70
87
|
},
|
|
71
|
-
shellVersion
|
|
88
|
+
shellVersion,
|
|
89
|
+
logger
|
|
72
90
|
)
|
|
73
91
|
|
|
74
92
|
logger.info("[Shell] Flags fetched", { flags })
|
package/src/vwrConfig.test.ts
CHANGED
|
@@ -90,8 +90,8 @@ describe("vwrConfig", async () => {
|
|
|
90
90
|
expect(config.hubUrl).toBe("https://game-clients.volley.tv/hub")
|
|
91
91
|
expect(config.launchUrl).toBeUndefined() // Optional, not set by default
|
|
92
92
|
expect(config.trustedDomains).toEqual([
|
|
93
|
-
"https://game-clients.volley.tv
|
|
94
|
-
"https://vwr.volley.tv
|
|
93
|
+
"https://game-clients.volley.tv",
|
|
94
|
+
"https://vwr.volley.tv",
|
|
95
95
|
])
|
|
96
96
|
})
|
|
97
97
|
|