@volley/vwr-loader 1.1.0 → 1.2.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/README.md +7 -8
- package/dist/__mocks__/@datadog/browser-logs.d.ts +58 -0
- package/dist/__mocks__/@datadog/browser-logs.d.ts.map +1 -0
- package/dist/__mocks__/@datadog/browser-logs.js +34 -0
- package/dist/__mocks__/@datadog/browser-logs.js.map +1 -0
- package/dist/__mocks__/@datadog/browser-rum.d.ts +25 -0
- package/dist/__mocks__/@datadog/browser-rum.d.ts.map +1 -0
- package/dist/__mocks__/@datadog/browser-rum.js +25 -0
- package/dist/__mocks__/@datadog/browser-rum.js.map +1 -0
- package/dist/amplitudeFlagFetcher.d.ts +8 -3
- package/dist/amplitudeFlagFetcher.d.ts.map +1 -1
- package/dist/amplitudeFlagFetcher.js +14 -12
- package/dist/amplitudeFlagFetcher.js.map +1 -1
- package/dist/assets/profiler-BRmTNL9s.js +2 -0
- package/dist/assets/profiler-BRmTNL9s.js.map +1 -0
- package/dist/assets/startRecording-CuFdVhxx.js +3 -0
- package/dist/assets/startRecording-CuFdVhxx.js.map +1 -0
- package/dist/cli.js +42 -8
- package/dist/cli.js.map +1 -1
- package/dist/datadog.d.ts +19 -0
- package/dist/datadog.d.ts.map +1 -0
- package/dist/datadog.js +39 -0
- package/dist/datadog.js.map +1 -0
- package/dist/envDefaults.d.ts.map +1 -1
- package/dist/envDefaults.js +27 -2
- package/dist/envDefaults.js.map +1 -1
- package/dist/errors/InitializationError.d.ts +74 -0
- package/dist/errors/InitializationError.d.ts.map +1 -0
- package/dist/errors/InitializationError.js +77 -0
- package/dist/errors/InitializationError.js.map +1 -0
- package/dist/errors/ensureError.d.ts +2 -0
- package/dist/errors/ensureError.d.ts.map +1 -0
- package/dist/errors/ensureError.js +7 -0
- package/dist/errors/ensureError.js.map +1 -0
- package/dist/errors/index.d.ts +12 -0
- package/dist/errors/index.d.ts.map +1 -0
- package/dist/errors/index.js +10 -0
- package/dist/errors/index.js.map +1 -0
- package/dist/getDeviceId.d.ts +1 -2
- package/dist/getDeviceId.d.ts.map +1 -1
- package/dist/getDeviceId.js +17 -15
- package/dist/getDeviceId.js.map +1 -1
- package/dist/getEnvironment.d.ts +1 -2
- package/dist/getEnvironment.d.ts.map +1 -1
- package/dist/getEnvironment.js +4 -4
- package/dist/getEnvironment.js.map +1 -1
- package/dist/getShellVersion.d.ts +1 -2
- package/dist/getShellVersion.d.ts.map +1 -1
- package/dist/getShellVersion.js +4 -4
- package/dist/getShellVersion.js.map +1 -1
- package/dist/index.d.ts +0 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js.map +1 -1
- package/dist/loadVwr.d.ts +4 -4
- package/dist/loadVwr.d.ts.map +1 -1
- package/dist/loadVwr.js +151 -37
- package/dist/loadVwr.js.map +1 -1
- package/dist/logger.d.ts +11 -6
- package/dist/logger.d.ts.map +1 -1
- package/dist/logger.js +107 -4
- package/dist/logger.js.map +1 -1
- package/dist/main.js +9 -1
- package/dist/main.js.map +1 -1
- package/dist/test-setup.d.ts +2 -0
- package/dist/test-setup.d.ts.map +1 -0
- package/dist/test-setup.js +5 -0
- package/dist/test-setup.js.map +1 -0
- package/dist/vwrConfig.d.ts +14 -2
- package/dist/vwrConfig.d.ts.map +1 -1
- package/dist/vwrConfig.js +74 -29
- package/dist/vwrConfig.js.map +1 -1
- package/package.json +59 -53
- package/src/__mocks__/@datadog/browser-logs.ts +35 -0
- package/src/__mocks__/@datadog/browser-rum.ts +27 -0
- package/src/amplitudeFlagFetcher.test.ts +28 -34
- package/src/amplitudeFlagFetcher.ts +22 -14
- package/src/datadog.ts +55 -0
- package/src/envDefaults.ts +32 -3
- package/src/errors/InitializationError.ts +105 -0
- package/src/errors/ensureError.ts +6 -0
- package/src/errors/index.ts +16 -0
- package/src/getDeviceId.test.ts +4 -5
- package/src/getDeviceId.ts +19 -18
- package/src/getEnvironment.test.ts +44 -106
- package/src/getEnvironment.ts +4 -5
- package/src/getShellVersion.test.ts +12 -6
- package/src/getShellVersion.ts +4 -7
- package/src/index.ts +0 -1
- package/src/loadVwr.test.ts +394 -0
- package/src/loadVwr.ts +243 -58
- package/src/logger.ts +118 -11
- package/src/main.test.ts +157 -0
- package/src/main.ts +79 -11
- package/src/test-setup.ts +5 -0
- package/src/vite-env.d.ts +2 -0
- package/src/vwrConfig.test.ts +104 -34
- package/src/vwrConfig.ts +115 -37
package/src/envDefaults.ts
CHANGED
|
@@ -13,6 +13,14 @@ const CONFIG_URL_DEFAULT = "https://vwr.volley.tv/config/"
|
|
|
13
13
|
const CONFIG_FILE_DEFAULT = "vwrConfig.json"
|
|
14
14
|
const VWR_URL_PATH_DEFAULT = "v1/latest/vwr.js"
|
|
15
15
|
|
|
16
|
+
// Parse comma-separated env var, returning undefined if empty to allow fallback
|
|
17
|
+
const parseTrustedOrigins = (
|
|
18
|
+
envVar: string | undefined
|
|
19
|
+
): string[] | undefined => {
|
|
20
|
+
const parsed = envVar?.split(",").filter(Boolean)
|
|
21
|
+
return parsed?.length ? parsed : undefined
|
|
22
|
+
}
|
|
23
|
+
|
|
16
24
|
const prodConfig: EnvConfig = {
|
|
17
25
|
hubUrl: "https://game-clients.volley.tv/hub",
|
|
18
26
|
vwrUrl: `https://vwr.volley.tv/${VWR_URL_PATH_DEFAULT}`,
|
|
@@ -21,6 +29,7 @@ const prodConfig: EnvConfig = {
|
|
|
21
29
|
platformApiUrl: "https://platform.volley-services.net",
|
|
22
30
|
platformAuthApiUrl: "https://auth.volley.tv",
|
|
23
31
|
amplitudeKey: "",
|
|
32
|
+
trustedOrigins: ["https://volley.tv", "https://vl.ly", "https://vly.gg"],
|
|
24
33
|
}
|
|
25
34
|
|
|
26
35
|
export const ENV_DEFAULTS: Record<string, EnvConfig> = {
|
|
@@ -37,9 +46,16 @@ export const ENV_DEFAULTS: Record<string, EnvConfig> = {
|
|
|
37
46
|
import.meta.env.VITE_PLATFORM_AUTH_API_URL ||
|
|
38
47
|
"https://auth-dev.volley.tv",
|
|
39
48
|
amplitudeKey: "client-uJJVW3zKPC1G9kqPhUumLnZN6eaY42iQ",
|
|
40
|
-
trustedOrigins:
|
|
41
|
-
|
|
42
|
-
)
|
|
49
|
+
trustedOrigins: parseTrustedOrigins(
|
|
50
|
+
import.meta.env.VITE_TRUSTED_ORIGINS
|
|
51
|
+
) || [
|
|
52
|
+
"http://localhost:5173",
|
|
53
|
+
"http://localhost:5174",
|
|
54
|
+
"https://vl.ly",
|
|
55
|
+
"https://dev.vl.ly",
|
|
56
|
+
"https://staging.vl.ly",
|
|
57
|
+
"https://vly.gg",
|
|
58
|
+
],
|
|
43
59
|
},
|
|
44
60
|
dev: {
|
|
45
61
|
hubUrl: "https://game-clients-dev.volley.tv/hub",
|
|
@@ -49,6 +65,13 @@ export const ENV_DEFAULTS: Record<string, EnvConfig> = {
|
|
|
49
65
|
platformApiUrl: "https://platform-dev.volley-services.net",
|
|
50
66
|
platformAuthApiUrl: "https://auth-dev.volley.tv",
|
|
51
67
|
amplitudeKey: "client-uJJVW3zKPC1G9kqPhUumLnZN6eaY42iQ",
|
|
68
|
+
trustedOrigins: [
|
|
69
|
+
"https://volley.tv",
|
|
70
|
+
"https://vl.ly",
|
|
71
|
+
"https://dev.vl.ly",
|
|
72
|
+
"https://staging.vl.ly",
|
|
73
|
+
"https://vly.gg",
|
|
74
|
+
],
|
|
52
75
|
},
|
|
53
76
|
staging: {
|
|
54
77
|
hubUrl: "https://game-clients-staging.volley.tv/hub",
|
|
@@ -58,6 +81,12 @@ export const ENV_DEFAULTS: Record<string, EnvConfig> = {
|
|
|
58
81
|
platformApiUrl: "https://platform-staging.volley-services.net",
|
|
59
82
|
platformAuthApiUrl: "https://auth-staging.volley.tv",
|
|
60
83
|
amplitudeKey: "",
|
|
84
|
+
trustedOrigins: [
|
|
85
|
+
"https://volley.tv",
|
|
86
|
+
"https://vl.ly",
|
|
87
|
+
"https://staging.vl.ly",
|
|
88
|
+
"https://vly.gg",
|
|
89
|
+
],
|
|
61
90
|
},
|
|
62
91
|
prod: prodConfig,
|
|
63
92
|
production: prodConfig, // Alias for prod
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Reason codes for VWR loader initialization failures.
|
|
3
|
+
* Each code maps to a specific failure point in the init flow.
|
|
4
|
+
*/
|
|
5
|
+
export const InitializationErrorReason = {
|
|
6
|
+
// Environment variable errors
|
|
7
|
+
ENV_VARS_MISSING: "ENV_VARS_MISSING",
|
|
8
|
+
ENV_UNKNOWN: "ENV_UNKNOWN",
|
|
9
|
+
|
|
10
|
+
// Device/platform errors
|
|
11
|
+
DEVICE_ID_FAILED: "DEVICE_ID_FAILED",
|
|
12
|
+
SHELL_VERSION_FAILED: "SHELL_VERSION_FAILED",
|
|
13
|
+
|
|
14
|
+
// Feature flag errors
|
|
15
|
+
FLAG_DISABLED: "FLAG_DISABLED",
|
|
16
|
+
FLAG_FETCH_FAILED: "FLAG_FETCH_FAILED",
|
|
17
|
+
|
|
18
|
+
// Runtime config errors
|
|
19
|
+
CONFIG_FETCH_FAILED: "CONFIG_FETCH_FAILED",
|
|
20
|
+
CONFIG_INVALID: "CONFIG_INVALID",
|
|
21
|
+
|
|
22
|
+
// Module loading errors
|
|
23
|
+
MODULE_FETCH_FAILED: "MODULE_FETCH_FAILED",
|
|
24
|
+
MODULE_INVALID: "MODULE_INVALID",
|
|
25
|
+
|
|
26
|
+
// VWR init errors
|
|
27
|
+
INIT_FAILED: "INIT_FAILED",
|
|
28
|
+
} as const
|
|
29
|
+
|
|
30
|
+
export type InitializationErrorReason =
|
|
31
|
+
(typeof InitializationErrorReason)[keyof typeof InitializationErrorReason]
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Context for initialization errors - used for structured logging
|
|
35
|
+
* and Datadog dashboard creation.
|
|
36
|
+
*/
|
|
37
|
+
export interface InitializationErrorContext {
|
|
38
|
+
cause?: Error
|
|
39
|
+
[key: string]: unknown
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Custom error for VWR loader initialization failures.
|
|
44
|
+
*
|
|
45
|
+
* Provides structured information for:
|
|
46
|
+
* - Error categorization (reason code)
|
|
47
|
+
* - RUM phase tracking
|
|
48
|
+
* - Datadog dashboard filtering
|
|
49
|
+
* - Original error chain preservation
|
|
50
|
+
*
|
|
51
|
+
* @example
|
|
52
|
+
* ```typescript
|
|
53
|
+
* throw new InitializationError(
|
|
54
|
+
* InitializationErrorReason.DEVICE_ID_FAILED,
|
|
55
|
+
* `Failed to retrieve device ID for platform: ${platform}`,
|
|
56
|
+
* {
|
|
57
|
+
* phase: "vwr-loader:device_id",
|
|
58
|
+
* platform,
|
|
59
|
+
* }
|
|
60
|
+
* );
|
|
61
|
+
* ```
|
|
62
|
+
*/
|
|
63
|
+
export class InitializationError extends Error {
|
|
64
|
+
public readonly name = "InitializationError"
|
|
65
|
+
|
|
66
|
+
constructor(
|
|
67
|
+
/** Structured reason code for categorization */
|
|
68
|
+
public readonly reason: InitializationErrorReason,
|
|
69
|
+
/** Human-readable error message */
|
|
70
|
+
public readonly message: string,
|
|
71
|
+
/** Context for logging and RUM tracking */
|
|
72
|
+
public readonly context: InitializationErrorContext = {}
|
|
73
|
+
) {
|
|
74
|
+
super(message)
|
|
75
|
+
|
|
76
|
+
// Ensure proper prototype chain for instanceof checks
|
|
77
|
+
Object.setPrototypeOf(this, InitializationError.prototype)
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Convert to a plain object for structured logging.
|
|
82
|
+
* Useful for Datadog Logs and JSON serialization.
|
|
83
|
+
*/
|
|
84
|
+
toJSON(): Record<string, unknown> {
|
|
85
|
+
return {
|
|
86
|
+
name: this.name,
|
|
87
|
+
reason: this.reason,
|
|
88
|
+
message: this.message,
|
|
89
|
+
...this.context,
|
|
90
|
+
cause: this.context.cause?.message,
|
|
91
|
+
stack: this.stack,
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* Get error context.
|
|
97
|
+
* Returns the context object.
|
|
98
|
+
*/
|
|
99
|
+
getContext(): Record<string, unknown> {
|
|
100
|
+
return {
|
|
101
|
+
...this.context,
|
|
102
|
+
reason: this.reason,
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
export { ensureError } from "./ensureError"
|
|
2
|
+
export type { InitializationErrorContext } from "./InitializationError"
|
|
3
|
+
export {
|
|
4
|
+
InitializationError,
|
|
5
|
+
InitializationErrorReason,
|
|
6
|
+
} from "./InitializationError"
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Type guard for VWR initialization errors.
|
|
10
|
+
* Checks error.name instead of instanceof to avoid runtime dependency on @volley/vwr.
|
|
11
|
+
*/
|
|
12
|
+
export function isVWRInitializationError(
|
|
13
|
+
error: unknown
|
|
14
|
+
): error is Error & { name: "VWRInitializationError"; reason: string } {
|
|
15
|
+
return error instanceof Error && error.name === "VWRInitializationError"
|
|
16
|
+
}
|
package/src/getDeviceId.test.ts
CHANGED
|
@@ -1,7 +1,10 @@
|
|
|
1
|
+
import * as uuid from "uuid"
|
|
1
2
|
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"
|
|
2
3
|
|
|
3
4
|
import { getDeviceId } from "./getDeviceId"
|
|
4
5
|
|
|
6
|
+
vi.mock("uuid")
|
|
7
|
+
|
|
5
8
|
describe("getDeviceId", () => {
|
|
6
9
|
beforeEach(() => {
|
|
7
10
|
vi.clearAllMocks()
|
|
@@ -247,11 +250,7 @@ describe("getDeviceId", () => {
|
|
|
247
250
|
|
|
248
251
|
it("generates and stores new device ID when none exists", async () => {
|
|
249
252
|
const mockUUID = "web-uuid-123"
|
|
250
|
-
|
|
251
|
-
value: vi.fn().mockReturnValue(mockUUID),
|
|
252
|
-
writable: true,
|
|
253
|
-
configurable: true,
|
|
254
|
-
})
|
|
253
|
+
vi.mocked(uuid.v4).mockReturnValue(mockUUID as any)
|
|
255
254
|
|
|
256
255
|
const result = await getDeviceId("WEB")
|
|
257
256
|
|
package/src/getDeviceId.ts
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { v4 as uuidv4 } from "uuid"
|
|
2
|
+
|
|
3
|
+
import { logger } from "./logger"
|
|
2
4
|
import type { LGDeviceIdResponse, MobileExtendedWindow } from "./types"
|
|
3
5
|
|
|
4
6
|
/**
|
|
@@ -25,21 +27,18 @@ import type { LGDeviceIdResponse, MobileExtendedWindow } from "./types"
|
|
|
25
27
|
* }
|
|
26
28
|
* ```
|
|
27
29
|
*/
|
|
28
|
-
export async function getDeviceId(
|
|
29
|
-
platform: string,
|
|
30
|
-
logger: Logger = defaultLogger
|
|
31
|
-
): Promise<string | null> {
|
|
30
|
+
export async function getDeviceId(platform: string): Promise<string | null> {
|
|
32
31
|
const normalizedPlatform = platform.toUpperCase()
|
|
33
32
|
switch (normalizedPlatform) {
|
|
34
33
|
case "FIRE_TV":
|
|
35
|
-
return await getFireTVDeviceId(
|
|
34
|
+
return await getFireTVDeviceId()
|
|
36
35
|
case "SAMSUNG_TV":
|
|
37
|
-
return await getSamsungDeviceId(
|
|
36
|
+
return await getSamsungDeviceId()
|
|
38
37
|
case "LG_TV":
|
|
39
|
-
return await getLGDeviceId(
|
|
38
|
+
return await getLGDeviceId()
|
|
40
39
|
case "ANDROID_MOBILE":
|
|
41
40
|
case "IOS_MOBILE":
|
|
42
|
-
return await getMobileDeviceId(
|
|
41
|
+
return await getMobileDeviceId()
|
|
43
42
|
case "WEB":
|
|
44
43
|
return getLocalStorageDeviceId()
|
|
45
44
|
default:
|
|
@@ -53,18 +52,20 @@ export async function getDeviceId(
|
|
|
53
52
|
* Get or create device ID from localStorage (for Web platform)
|
|
54
53
|
*/
|
|
55
54
|
function getLocalStorageDeviceId(): string {
|
|
56
|
-
|
|
57
|
-
if (
|
|
58
|
-
|
|
59
|
-
localStorage.setItem("volley_device_id", deviceId)
|
|
55
|
+
const existingDeviceId = localStorage.getItem("volley_device_id")
|
|
56
|
+
if (existingDeviceId) {
|
|
57
|
+
return existingDeviceId
|
|
60
58
|
}
|
|
61
|
-
|
|
59
|
+
|
|
60
|
+
const newDeviceId = uuidv4()
|
|
61
|
+
localStorage.setItem("volley_device_id", newDeviceId)
|
|
62
|
+
return newDeviceId
|
|
62
63
|
}
|
|
63
64
|
|
|
64
65
|
/**
|
|
65
66
|
* Attempt to get device ID from Capacitor DeviceInfo plugin (FireTV)
|
|
66
67
|
*/
|
|
67
|
-
async function getFireTVDeviceId(
|
|
68
|
+
async function getFireTVDeviceId(): Promise<string | null> {
|
|
68
69
|
try {
|
|
69
70
|
if (!window.Capacitor?.Plugins?.DeviceInfo?.getAndroidId) {
|
|
70
71
|
return null
|
|
@@ -91,7 +92,7 @@ async function getFireTVDeviceId(logger: Logger): Promise<string | null> {
|
|
|
91
92
|
* iOS exposes phoneDeviceId via window.iosAppContext
|
|
92
93
|
* Android exposes phoneDeviceId via window.androidAppContext (also check window.top for iframes)
|
|
93
94
|
*/
|
|
94
|
-
function getMobileDeviceId(
|
|
95
|
+
function getMobileDeviceId(): string | null {
|
|
95
96
|
// Try iOS first
|
|
96
97
|
const iosId = (window as MobileExtendedWindow).iosAppContext?.phoneDeviceId
|
|
97
98
|
if (iosId && iosId.trim()) {
|
|
@@ -126,7 +127,7 @@ function getMobileDeviceId(logger: Logger): string | null {
|
|
|
126
127
|
/**
|
|
127
128
|
* Attempt to get device ID from Samsung TV webapis
|
|
128
129
|
*/
|
|
129
|
-
async function getSamsungDeviceId(
|
|
130
|
+
async function getSamsungDeviceId(): Promise<string | null> {
|
|
130
131
|
try {
|
|
131
132
|
if (!window.webapis?.productinfo?.getDuid) {
|
|
132
133
|
return null
|
|
@@ -156,7 +157,7 @@ async function getSamsungDeviceId(logger: Logger): Promise<string | null> {
|
|
|
156
157
|
*/
|
|
157
158
|
const LG_LUNA_SERVICE_TIMEOUT = 5000
|
|
158
159
|
|
|
159
|
-
async function getLGDeviceId(
|
|
160
|
+
async function getLGDeviceId(): Promise<string | null> {
|
|
160
161
|
if (!window.webOS?.service?.request) {
|
|
161
162
|
const message = "LG webOS service request not available"
|
|
162
163
|
logger.error(message)
|
|
@@ -1,19 +1,21 @@
|
|
|
1
1
|
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"
|
|
2
2
|
|
|
3
3
|
import { type Environment, getEnvironment } from "./getEnvironment"
|
|
4
|
+
import { logger } from "./logger"
|
|
4
5
|
|
|
5
|
-
|
|
6
|
-
|
|
6
|
+
vi.mock("./logger", () => ({
|
|
7
|
+
logger: {
|
|
7
8
|
info: vi.fn(),
|
|
8
9
|
warn: vi.fn(),
|
|
9
10
|
error: vi.fn(),
|
|
10
|
-
|
|
11
|
+
debug: vi.fn(),
|
|
12
|
+
child: vi.fn(),
|
|
13
|
+
},
|
|
14
|
+
}))
|
|
11
15
|
|
|
16
|
+
describe("getEnvironment", () => {
|
|
12
17
|
beforeEach(() => {
|
|
13
18
|
vi.clearAllMocks()
|
|
14
|
-
mockLogger.info.mockClear()
|
|
15
|
-
mockLogger.warn.mockClear()
|
|
16
|
-
mockLogger.error.mockClear()
|
|
17
19
|
// Clean up window properties
|
|
18
20
|
delete (window as any).Capacitor
|
|
19
21
|
})
|
|
@@ -42,15 +44,11 @@ describe("getEnvironment", () => {
|
|
|
42
44
|
configurable: true,
|
|
43
45
|
})
|
|
44
46
|
|
|
45
|
-
const result = await getEnvironment(
|
|
46
|
-
"FIRE_TV",
|
|
47
|
-
undefined,
|
|
48
|
-
mockLogger
|
|
49
|
-
)
|
|
47
|
+
const result = await getEnvironment("FIRE_TV", undefined)
|
|
50
48
|
|
|
51
49
|
expect(result.environment).toBe("dev")
|
|
52
50
|
expect(result.source).toBe("native")
|
|
53
|
-
expect(
|
|
51
|
+
expect(logger.info).toHaveBeenCalledWith(
|
|
54
52
|
expect.stringContaining(
|
|
55
53
|
'Environment: "dev" (source: native BuildConfig.ENVIRONMENT="development")'
|
|
56
54
|
)
|
|
@@ -74,11 +72,7 @@ describe("getEnvironment", () => {
|
|
|
74
72
|
configurable: true,
|
|
75
73
|
})
|
|
76
74
|
|
|
77
|
-
const result = await getEnvironment(
|
|
78
|
-
"FIRE_TV",
|
|
79
|
-
undefined,
|
|
80
|
-
mockLogger
|
|
81
|
-
)
|
|
75
|
+
const result = await getEnvironment("FIRE_TV", undefined)
|
|
82
76
|
|
|
83
77
|
expect(result.environment).toBe("prod")
|
|
84
78
|
expect(result.source).toBe("native")
|
|
@@ -101,11 +95,7 @@ describe("getEnvironment", () => {
|
|
|
101
95
|
configurable: true,
|
|
102
96
|
})
|
|
103
97
|
|
|
104
|
-
const result = await getEnvironment(
|
|
105
|
-
"FIRE_TV",
|
|
106
|
-
undefined,
|
|
107
|
-
mockLogger
|
|
108
|
-
)
|
|
98
|
+
const result = await getEnvironment("FIRE_TV", undefined)
|
|
109
99
|
|
|
110
100
|
expect(result.environment).toBe("staging")
|
|
111
101
|
expect(result.source).toBe("native")
|
|
@@ -128,11 +118,7 @@ describe("getEnvironment", () => {
|
|
|
128
118
|
configurable: true,
|
|
129
119
|
})
|
|
130
120
|
|
|
131
|
-
const result = await getEnvironment(
|
|
132
|
-
"FIRE_TV",
|
|
133
|
-
undefined,
|
|
134
|
-
mockLogger
|
|
135
|
-
)
|
|
121
|
+
const result = await getEnvironment("FIRE_TV", undefined)
|
|
136
122
|
|
|
137
123
|
expect(result.environment).toBe("staging")
|
|
138
124
|
expect(result.source).toBe("native")
|
|
@@ -155,11 +141,7 @@ describe("getEnvironment", () => {
|
|
|
155
141
|
configurable: true,
|
|
156
142
|
})
|
|
157
143
|
|
|
158
|
-
const result = await getEnvironment(
|
|
159
|
-
"FIRE_TV",
|
|
160
|
-
undefined,
|
|
161
|
-
mockLogger
|
|
162
|
-
)
|
|
144
|
+
const result = await getEnvironment("FIRE_TV", undefined)
|
|
163
145
|
|
|
164
146
|
expect(result.environment).toBe("dev")
|
|
165
147
|
expect(result.source).toBe("native")
|
|
@@ -182,11 +164,7 @@ describe("getEnvironment", () => {
|
|
|
182
164
|
configurable: true,
|
|
183
165
|
})
|
|
184
166
|
|
|
185
|
-
const result = await getEnvironment(
|
|
186
|
-
"FIRE_TV",
|
|
187
|
-
undefined,
|
|
188
|
-
mockLogger
|
|
189
|
-
)
|
|
167
|
+
const result = await getEnvironment("FIRE_TV", undefined)
|
|
190
168
|
|
|
191
169
|
expect(result.environment).toBe("dev")
|
|
192
170
|
expect(result.source).toBe("native")
|
|
@@ -194,15 +172,11 @@ describe("getEnvironment", () => {
|
|
|
194
172
|
|
|
195
173
|
it("should fallback to build-time when native plugin unavailable", async () => {
|
|
196
174
|
// No Capacitor plugin available
|
|
197
|
-
const result = await getEnvironment(
|
|
198
|
-
"FIRE_TV",
|
|
199
|
-
"staging",
|
|
200
|
-
mockLogger
|
|
201
|
-
)
|
|
175
|
+
const result = await getEnvironment("FIRE_TV", "staging")
|
|
202
176
|
|
|
203
177
|
expect(result.environment).toBe("staging")
|
|
204
178
|
expect(result.source).toBe("build-time")
|
|
205
|
-
expect(
|
|
179
|
+
expect(logger.warn).toHaveBeenCalledWith(
|
|
206
180
|
expect.stringContaining(
|
|
207
181
|
"Failed to read environment from native, falling back to build-time"
|
|
208
182
|
)
|
|
@@ -226,12 +200,12 @@ describe("getEnvironment", () => {
|
|
|
226
200
|
configurable: true,
|
|
227
201
|
})
|
|
228
202
|
|
|
229
|
-
const result = await getEnvironment("FIRE_TV", "prod"
|
|
203
|
+
const result = await getEnvironment("FIRE_TV", "prod")
|
|
230
204
|
|
|
231
205
|
expect(result.environment).toBe("prod")
|
|
232
206
|
expect(result.source).toBe("build-time")
|
|
233
|
-
expect(
|
|
234
|
-
expect(
|
|
207
|
+
expect(logger.error).toHaveBeenCalled()
|
|
208
|
+
expect(logger.warn).toHaveBeenCalledWith(
|
|
235
209
|
expect.stringContaining("falling back to build-time")
|
|
236
210
|
)
|
|
237
211
|
})
|
|
@@ -253,11 +227,11 @@ describe("getEnvironment", () => {
|
|
|
253
227
|
configurable: true,
|
|
254
228
|
})
|
|
255
229
|
|
|
256
|
-
const result = await getEnvironment("FIRE_TV", "dev"
|
|
230
|
+
const result = await getEnvironment("FIRE_TV", "dev")
|
|
257
231
|
|
|
258
232
|
expect(result.environment).toBe("dev")
|
|
259
233
|
expect(result.source).toBe("build-time")
|
|
260
|
-
expect(
|
|
234
|
+
expect(logger.error).toHaveBeenCalledWith(
|
|
261
235
|
expect.stringContaining(
|
|
262
236
|
"DeviceInfo.getNativeShellAppEnvironment returned empty"
|
|
263
237
|
)
|
|
@@ -266,15 +240,11 @@ describe("getEnvironment", () => {
|
|
|
266
240
|
|
|
267
241
|
it("should fallback to default when both native and build-time fail", async () => {
|
|
268
242
|
// No Capacitor plugin, no build-time env
|
|
269
|
-
const result = await getEnvironment(
|
|
270
|
-
"FIRE_TV",
|
|
271
|
-
undefined,
|
|
272
|
-
mockLogger
|
|
273
|
-
)
|
|
243
|
+
const result = await getEnvironment("FIRE_TV", undefined)
|
|
274
244
|
|
|
275
245
|
expect(result.environment).toBe("dev")
|
|
276
246
|
expect(result.source).toBe("default")
|
|
277
|
-
expect(
|
|
247
|
+
expect(logger.warn).toHaveBeenCalledWith(
|
|
278
248
|
expect.stringContaining(
|
|
279
249
|
'Environment: "dev" (source: default fallback'
|
|
280
250
|
)
|
|
@@ -298,16 +268,8 @@ describe("getEnvironment", () => {
|
|
|
298
268
|
configurable: true,
|
|
299
269
|
})
|
|
300
270
|
|
|
301
|
-
const result1 = await getEnvironment(
|
|
302
|
-
|
|
303
|
-
undefined,
|
|
304
|
-
mockLogger
|
|
305
|
-
)
|
|
306
|
-
const result2 = await getEnvironment(
|
|
307
|
-
"FIRE_TV",
|
|
308
|
-
undefined,
|
|
309
|
-
mockLogger
|
|
310
|
-
)
|
|
271
|
+
const result1 = await getEnvironment("fire_tv", undefined)
|
|
272
|
+
const result2 = await getEnvironment("FIRE_TV", undefined)
|
|
311
273
|
|
|
312
274
|
expect(result1.environment).toBe("prod")
|
|
313
275
|
expect(result2.environment).toBe("prod")
|
|
@@ -320,15 +282,11 @@ describe("getEnvironment", () => {
|
|
|
320
282
|
platforms.forEach((platform) => {
|
|
321
283
|
describe(`${platform}`, () => {
|
|
322
284
|
it("should use build-time environment when provided", async () => {
|
|
323
|
-
const result = await getEnvironment(
|
|
324
|
-
platform,
|
|
325
|
-
"staging",
|
|
326
|
-
mockLogger
|
|
327
|
-
)
|
|
285
|
+
const result = await getEnvironment(platform, "staging")
|
|
328
286
|
|
|
329
287
|
expect(result.environment).toBe("staging")
|
|
330
288
|
expect(result.source).toBe("build-time")
|
|
331
|
-
expect(
|
|
289
|
+
expect(logger.info).toHaveBeenCalledWith(
|
|
332
290
|
expect.stringContaining(
|
|
333
291
|
'Environment: "staging" (source: build-time CLI injection)'
|
|
334
292
|
)
|
|
@@ -336,48 +294,32 @@ describe("getEnvironment", () => {
|
|
|
336
294
|
})
|
|
337
295
|
|
|
338
296
|
it("should use build-time 'prod' environment", async () => {
|
|
339
|
-
const result = await getEnvironment(
|
|
340
|
-
platform,
|
|
341
|
-
"prod",
|
|
342
|
-
mockLogger
|
|
343
|
-
)
|
|
297
|
+
const result = await getEnvironment(platform, "prod")
|
|
344
298
|
|
|
345
299
|
expect(result.environment).toBe("prod")
|
|
346
300
|
expect(result.source).toBe("build-time")
|
|
347
301
|
})
|
|
348
302
|
|
|
349
303
|
it("should use build-time 'dev' environment", async () => {
|
|
350
|
-
const result = await getEnvironment(
|
|
351
|
-
platform,
|
|
352
|
-
"dev",
|
|
353
|
-
mockLogger
|
|
354
|
-
)
|
|
304
|
+
const result = await getEnvironment(platform, "dev")
|
|
355
305
|
|
|
356
306
|
expect(result.environment).toBe("dev")
|
|
357
307
|
expect(result.source).toBe("build-time")
|
|
358
308
|
})
|
|
359
309
|
|
|
360
310
|
it("should use build-time 'local' environment", async () => {
|
|
361
|
-
const result = await getEnvironment(
|
|
362
|
-
platform,
|
|
363
|
-
"local",
|
|
364
|
-
mockLogger
|
|
365
|
-
)
|
|
311
|
+
const result = await getEnvironment(platform, "local")
|
|
366
312
|
|
|
367
313
|
expect(result.environment).toBe("local")
|
|
368
314
|
expect(result.source).toBe("build-time")
|
|
369
315
|
})
|
|
370
316
|
|
|
371
317
|
it("should fallback to default when build-time not provided", async () => {
|
|
372
|
-
const result = await getEnvironment(
|
|
373
|
-
platform,
|
|
374
|
-
undefined,
|
|
375
|
-
mockLogger
|
|
376
|
-
)
|
|
318
|
+
const result = await getEnvironment(platform, undefined)
|
|
377
319
|
|
|
378
320
|
expect(result.environment).toBe("dev")
|
|
379
321
|
expect(result.source).toBe("default")
|
|
380
|
-
expect(
|
|
322
|
+
expect(logger.warn).toHaveBeenCalledWith(
|
|
381
323
|
expect.stringContaining(
|
|
382
324
|
'Environment: "dev" (source: default fallback'
|
|
383
325
|
)
|
|
@@ -385,11 +327,7 @@ describe("getEnvironment", () => {
|
|
|
385
327
|
})
|
|
386
328
|
|
|
387
329
|
it("should fallback to default when build-time is invalid", async () => {
|
|
388
|
-
const result = await getEnvironment(
|
|
389
|
-
platform,
|
|
390
|
-
"invalid-env",
|
|
391
|
-
mockLogger
|
|
392
|
-
)
|
|
330
|
+
const result = await getEnvironment(platform, "invalid-env")
|
|
393
331
|
|
|
394
332
|
expect(result.environment).toBe("dev")
|
|
395
333
|
expect(result.source).toBe("default")
|
|
@@ -403,7 +341,7 @@ describe("getEnvironment", () => {
|
|
|
403
341
|
const validEnvs: Environment[] = ["local", "dev", "staging", "prod"]
|
|
404
342
|
|
|
405
343
|
for (const env of validEnvs) {
|
|
406
|
-
const result = await getEnvironment("WEB", env
|
|
344
|
+
const result = await getEnvironment("WEB", env)
|
|
407
345
|
expect(result.environment).toBe(env)
|
|
408
346
|
expect(result.source).toBe("build-time")
|
|
409
347
|
}
|
|
@@ -420,7 +358,7 @@ describe("getEnvironment", () => {
|
|
|
420
358
|
]
|
|
421
359
|
|
|
422
360
|
for (const env of invalidEnvs) {
|
|
423
|
-
const result = await getEnvironment("WEB", env
|
|
361
|
+
const result = await getEnvironment("WEB", env)
|
|
424
362
|
expect(result.environment).toBe("dev")
|
|
425
363
|
expect(result.source).toBe("default")
|
|
426
364
|
}
|
|
@@ -445,33 +383,33 @@ describe("getEnvironment", () => {
|
|
|
445
383
|
configurable: true,
|
|
446
384
|
})
|
|
447
385
|
|
|
448
|
-
await getEnvironment("FIRE_TV", undefined
|
|
386
|
+
await getEnvironment("FIRE_TV", undefined)
|
|
449
387
|
|
|
450
|
-
expect(
|
|
388
|
+
expect(logger.info).toHaveBeenCalledWith(
|
|
451
389
|
'[Shell] Environment: "prod" (source: native BuildConfig.ENVIRONMENT="production")'
|
|
452
390
|
)
|
|
453
391
|
})
|
|
454
392
|
|
|
455
393
|
it("should log build-time environment source", async () => {
|
|
456
|
-
await getEnvironment("SAMSUNG_TV", "staging"
|
|
394
|
+
await getEnvironment("SAMSUNG_TV", "staging")
|
|
457
395
|
|
|
458
|
-
expect(
|
|
396
|
+
expect(logger.info).toHaveBeenCalledWith(
|
|
459
397
|
'[Shell] Environment: "staging" (source: build-time CLI injection)'
|
|
460
398
|
)
|
|
461
399
|
})
|
|
462
400
|
|
|
463
401
|
it("should warn when falling back to default", async () => {
|
|
464
|
-
await getEnvironment("LG_TV", undefined
|
|
402
|
+
await getEnvironment("LG_TV", undefined)
|
|
465
403
|
|
|
466
|
-
expect(
|
|
404
|
+
expect(logger.warn).toHaveBeenCalledWith(
|
|
467
405
|
'[Shell] Environment: "dev" (source: default fallback - no native or build-time env found)'
|
|
468
406
|
)
|
|
469
407
|
})
|
|
470
408
|
|
|
471
409
|
it("should warn when native fails and falls back", async () => {
|
|
472
|
-
await getEnvironment("FIRE_TV", "prod"
|
|
410
|
+
await getEnvironment("FIRE_TV", "prod")
|
|
473
411
|
|
|
474
|
-
expect(
|
|
412
|
+
expect(logger.warn).toHaveBeenCalledWith(
|
|
475
413
|
"[Shell] Failed to read environment from native, falling back to build-time"
|
|
476
414
|
)
|
|
477
415
|
})
|
package/src/getEnvironment.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { logger } from "./logger"
|
|
2
2
|
|
|
3
3
|
export type Environment = "local" | "dev" | "staging" | "prod"
|
|
4
4
|
export type EnvironmentSource = "native" | "build-time" | "default"
|
|
@@ -26,14 +26,13 @@ export interface EnvironmentResult {
|
|
|
26
26
|
*/
|
|
27
27
|
export async function getEnvironment(
|
|
28
28
|
platform: string,
|
|
29
|
-
buildTimeEnv: string | undefined
|
|
30
|
-
logger: Logger = defaultLogger
|
|
29
|
+
buildTimeEnv: string | undefined
|
|
31
30
|
): Promise<EnvironmentResult> {
|
|
32
31
|
const normalizedPlatform = platform.toUpperCase()
|
|
33
32
|
|
|
34
33
|
// For Fire TV, try to read from native first (safest - prevents env mismatch)
|
|
35
34
|
if (normalizedPlatform === "FIRE_TV") {
|
|
36
|
-
const nativeEnv = await getFireTVEnvironment(
|
|
35
|
+
const nativeEnv = await getFireTVEnvironment()
|
|
37
36
|
if (nativeEnv) {
|
|
38
37
|
const mapped = mapNativeEnvironment(nativeEnv)
|
|
39
38
|
logger.info(
|
|
@@ -72,7 +71,7 @@ export async function getEnvironment(
|
|
|
72
71
|
/**
|
|
73
72
|
* Read environment from Fire TV native shell via Capacitor plugin.
|
|
74
73
|
*/
|
|
75
|
-
async function getFireTVEnvironment(
|
|
74
|
+
async function getFireTVEnvironment(): Promise<string | null> {
|
|
76
75
|
try {
|
|
77
76
|
if (
|
|
78
77
|
!window.Capacitor?.Plugins?.DeviceInfo?.getNativeShellAppEnvironment
|