@volley/vwr-loader 1.0.0-alpha.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 +205 -0
- package/dist/amplitudeFlagFetcher.d.ts +23 -0
- package/dist/amplitudeFlagFetcher.d.ts.map +1 -0
- package/dist/amplitudeFlagFetcher.js +60 -0
- package/dist/amplitudeFlagFetcher.js.map +1 -0
- package/dist/cli.js +177 -0
- package/dist/cli.js.map +1 -0
- package/dist/envDefaults.d.ts +9 -0
- package/dist/envDefaults.d.ts.map +1 -0
- package/dist/envDefaults.js +36 -0
- package/dist/envDefaults.js.map +1 -0
- package/dist/getDeviceId.d.ts +65 -0
- package/dist/getDeviceId.d.ts.map +1 -0
- package/dist/getDeviceId.js +196 -0
- package/dist/getDeviceId.js.map +1 -0
- package/dist/getShellVersion.d.ts +34 -0
- package/dist/getShellVersion.d.ts.map +1 -0
- package/dist/getShellVersion.js +84 -0
- package/dist/getShellVersion.js.map +1 -0
- package/dist/index.d.ts +9 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.html +25 -0
- package/dist/index.js +6 -0
- package/dist/index.js.map +1 -0
- package/dist/loadVwr.d.ts +19 -0
- package/dist/loadVwr.d.ts.map +1 -0
- package/dist/loadVwr.js +104 -0
- package/dist/loadVwr.js.map +1 -0
- package/dist/logger.d.ts +7 -0
- package/dist/logger.d.ts.map +1 -0
- package/dist/logger.js +6 -0
- package/dist/logger.js.map +1 -0
- package/dist/main.js +2 -0
- package/dist/main.js.map +1 -0
- package/dist/vwrConfig.d.ts +19 -0
- package/dist/vwrConfig.d.ts.map +1 -0
- package/dist/vwrConfig.js +172 -0
- package/dist/vwrConfig.js.map +1 -0
- package/package.json +54 -0
- package/src/amplitudeFlagFetcher.test.ts +209 -0
- package/src/amplitudeFlagFetcher.ts +88 -0
- package/src/envDefaults.ts +45 -0
- package/src/getDeviceId.test.ts +237 -0
- package/src/getDeviceId.ts +243 -0
- package/src/getShellVersion.test.ts +278 -0
- package/src/getShellVersion.ts +114 -0
- package/src/index.html +25 -0
- package/src/index.ts +8 -0
- package/src/loadVwr.ts +126 -0
- package/src/logger.ts +14 -0
- package/src/main.ts +26 -0
- package/src/vite-env.d.ts +15 -0
- package/src/vwrConfig.test.ts +316 -0
- package/src/vwrConfig.ts +293 -0
|
@@ -0,0 +1,316 @@
|
|
|
1
|
+
import { describe, expect, it, vi } from "vitest"
|
|
2
|
+
|
|
3
|
+
// Stub environment to use "production" which maps to ENV_DEFAULTS["production"]
|
|
4
|
+
vi.stubEnv("VITE_ENVIRONMENT", "production")
|
|
5
|
+
|
|
6
|
+
import type { VWRConfig, VWRConfigRequest } from "./vwrConfig"
|
|
7
|
+
|
|
8
|
+
describe("vwrConfig", async () => {
|
|
9
|
+
const { getVWRConfig, validateConfig } = await import("./vwrConfig")
|
|
10
|
+
describe("validateConfig", () => {
|
|
11
|
+
const config = {
|
|
12
|
+
vwrUrl: "https://vwr.example.com",
|
|
13
|
+
hubUrl: "https://hub.example.com",
|
|
14
|
+
launchUrl: "https://launch.example.com",
|
|
15
|
+
trustedDomains: ["example.com"],
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
it("returns false for empty vwrUrl", () => {
|
|
19
|
+
const invalidConfig = { ...config, vwrUrl: "" }
|
|
20
|
+
expect(validateConfig(invalidConfig)).toBe(false)
|
|
21
|
+
})
|
|
22
|
+
|
|
23
|
+
it("returns false for undefined vwrUrl", () => {
|
|
24
|
+
const invalidConfig: VWRConfig = JSON.parse(
|
|
25
|
+
JSON.stringify({ ...config, vwrUrl: undefined })
|
|
26
|
+
)
|
|
27
|
+
expect(validateConfig(invalidConfig)).toBe(false)
|
|
28
|
+
})
|
|
29
|
+
|
|
30
|
+
it("returns false for empty hubUrl", () => {
|
|
31
|
+
const invalidConfig = { ...config, hubUrl: "" }
|
|
32
|
+
expect(validateConfig(invalidConfig)).toBe(false)
|
|
33
|
+
})
|
|
34
|
+
|
|
35
|
+
it("returns false for undefined hubUrl", () => {
|
|
36
|
+
const invalidConfig: VWRConfig = JSON.parse(
|
|
37
|
+
JSON.stringify({ ...config, hubUrl: undefined })
|
|
38
|
+
)
|
|
39
|
+
expect(validateConfig(invalidConfig)).toBe(false)
|
|
40
|
+
})
|
|
41
|
+
|
|
42
|
+
it("returns true for empty launchUrl (optional field)", () => {
|
|
43
|
+
const validConfig = { ...config, launchUrl: "" }
|
|
44
|
+
expect(validateConfig(validConfig)).toBe(true)
|
|
45
|
+
})
|
|
46
|
+
|
|
47
|
+
it("returns true for undefined launchUrl (optional field)", () => {
|
|
48
|
+
const validConfig: VWRConfig = JSON.parse(
|
|
49
|
+
JSON.stringify({ ...config, launchUrl: undefined })
|
|
50
|
+
)
|
|
51
|
+
expect(validateConfig(validConfig)).toBe(true)
|
|
52
|
+
})
|
|
53
|
+
|
|
54
|
+
it("returns false for empty trustedDomains", () => {
|
|
55
|
+
const invalidConfig = { ...config, trustedDomains: [] }
|
|
56
|
+
expect(validateConfig(invalidConfig)).toBe(false)
|
|
57
|
+
})
|
|
58
|
+
|
|
59
|
+
it("returns false for undefined trustedDomains", () => {
|
|
60
|
+
const invalidConfig: VWRConfig = JSON.parse(
|
|
61
|
+
JSON.stringify({ ...config, trustedDomains: undefined })
|
|
62
|
+
)
|
|
63
|
+
expect(validateConfig(invalidConfig)).toBe(false)
|
|
64
|
+
})
|
|
65
|
+
|
|
66
|
+
it("returns false for non array trustedDomains", () => {
|
|
67
|
+
const invalidConfig: VWRConfig = JSON.parse(
|
|
68
|
+
JSON.stringify({ ...config, trustedDomains: "" })
|
|
69
|
+
)
|
|
70
|
+
expect(validateConfig(invalidConfig)).toBe(false)
|
|
71
|
+
})
|
|
72
|
+
})
|
|
73
|
+
|
|
74
|
+
describe("getVWRConfig", () => {
|
|
75
|
+
const configRequest: VWRConfigRequest = {
|
|
76
|
+
configUrl: "https://config.example.com",
|
|
77
|
+
configFile: "vwr-config.json",
|
|
78
|
+
platform: "web",
|
|
79
|
+
deviceId: "device-123",
|
|
80
|
+
environment: "production",
|
|
81
|
+
shellVersion: "1.0.0",
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
const fetchSpy = vi.spyOn(global, "fetch")
|
|
85
|
+
|
|
86
|
+
it("returns default config when no configs are found", async () => {
|
|
87
|
+
const config = await getVWRConfig(configRequest)
|
|
88
|
+
// Defaults come from ENV_DEFAULTS["production"]
|
|
89
|
+
expect(config.vwrUrl).toBe("https://vwr.volley.tv/v1/latest/vwr.js")
|
|
90
|
+
expect(config.hubUrl).toBe("https://game-clients.volley.tv/hub")
|
|
91
|
+
expect(config.launchUrl).toBeUndefined() // Optional, not set by default
|
|
92
|
+
expect(config.trustedDomains).toEqual([
|
|
93
|
+
"https://game-clients.volley.tv/hub",
|
|
94
|
+
"https://vwr.volley.tv/v1/latest/vwr.js",
|
|
95
|
+
])
|
|
96
|
+
})
|
|
97
|
+
|
|
98
|
+
it("returns local config when available", async () => {
|
|
99
|
+
fetchSpy.mockResolvedValueOnce(
|
|
100
|
+
new Response(
|
|
101
|
+
JSON.stringify({
|
|
102
|
+
vwrUrl: "https://local-vwr.example.com",
|
|
103
|
+
hubUrl: "https://local-hub.example.com",
|
|
104
|
+
launchUrl: "https://local-launch.example.com",
|
|
105
|
+
trustedDomains: [
|
|
106
|
+
"https://local-hub.example.com",
|
|
107
|
+
"https://local-vwr.example.com",
|
|
108
|
+
"https://local-launch.example.com",
|
|
109
|
+
],
|
|
110
|
+
})
|
|
111
|
+
)
|
|
112
|
+
)
|
|
113
|
+
fetchSpy.mockResolvedValueOnce(
|
|
114
|
+
new Response(
|
|
115
|
+
JSON.stringify({
|
|
116
|
+
vwrUrl: "https://device-vwr.example.com",
|
|
117
|
+
hubUrl: "https://device-hub.example.com",
|
|
118
|
+
launchUrl: "https://device-launch.example.com",
|
|
119
|
+
trustedDomains: [
|
|
120
|
+
"https://device-hub.example.com",
|
|
121
|
+
"https://device-vwr.example.com",
|
|
122
|
+
"https://device-launch.example.com",
|
|
123
|
+
],
|
|
124
|
+
})
|
|
125
|
+
)
|
|
126
|
+
)
|
|
127
|
+
fetchSpy.mockResolvedValueOnce(
|
|
128
|
+
new Response(
|
|
129
|
+
JSON.stringify({
|
|
130
|
+
vwrUrl: "https://shell-vwr.example.com",
|
|
131
|
+
hubUrl: "https://shell-hub.example.com",
|
|
132
|
+
launchUrl: "https://shell-launch.example.com",
|
|
133
|
+
trustedDomains: [
|
|
134
|
+
"https://shell-hub.example.com",
|
|
135
|
+
"https://shell-vwr.example.com",
|
|
136
|
+
"https://shell-launch.example.com",
|
|
137
|
+
],
|
|
138
|
+
})
|
|
139
|
+
)
|
|
140
|
+
)
|
|
141
|
+
fetchSpy.mockResolvedValueOnce(
|
|
142
|
+
new Response(
|
|
143
|
+
JSON.stringify({
|
|
144
|
+
vwrUrl: "https://environment-vwr.example.com",
|
|
145
|
+
hubUrl: "https://environment-hub.example.com",
|
|
146
|
+
launchUrl: "https://environment-launch.example.com",
|
|
147
|
+
trustedDomains: [
|
|
148
|
+
"https://environment-hub.example.com",
|
|
149
|
+
"https://environment-vwr.example.com",
|
|
150
|
+
"https://environment-launch.example.com",
|
|
151
|
+
],
|
|
152
|
+
})
|
|
153
|
+
)
|
|
154
|
+
)
|
|
155
|
+
const config = await getVWRConfig(configRequest)
|
|
156
|
+
expect(validateConfig(config)).toBe(true)
|
|
157
|
+
expect(config.vwrUrl).toBe("https://local-vwr.example.com")
|
|
158
|
+
expect(config.hubUrl).toBe("https://local-hub.example.com")
|
|
159
|
+
expect(config.launchUrl).toBe("https://local-launch.example.com")
|
|
160
|
+
expect(config.trustedDomains).toEqual([
|
|
161
|
+
"https://local-hub.example.com",
|
|
162
|
+
"https://local-vwr.example.com",
|
|
163
|
+
"https://local-launch.example.com",
|
|
164
|
+
])
|
|
165
|
+
})
|
|
166
|
+
|
|
167
|
+
it("returns device config when available", async () => {
|
|
168
|
+
fetchSpy.mockResolvedValueOnce(
|
|
169
|
+
new Response(
|
|
170
|
+
JSON.stringify({
|
|
171
|
+
vwrUrl: "https://device-vwr.example.com",
|
|
172
|
+
hubUrl: "https://device-hub.example.com",
|
|
173
|
+
launchUrl: "https://device-launch.example.com",
|
|
174
|
+
trustedDomains: [
|
|
175
|
+
"https://device-hub.example.com",
|
|
176
|
+
"https://device-vwr.example.com",
|
|
177
|
+
"https://device-launch.example.com",
|
|
178
|
+
],
|
|
179
|
+
})
|
|
180
|
+
)
|
|
181
|
+
)
|
|
182
|
+
fetchSpy.mockResolvedValueOnce(
|
|
183
|
+
new Response(
|
|
184
|
+
JSON.stringify({
|
|
185
|
+
vwrUrl: "https://shell-vwr.example.com",
|
|
186
|
+
hubUrl: "https://shell-hub.example.com",
|
|
187
|
+
launchUrl: "https://shell-launch.example.com",
|
|
188
|
+
trustedDomains: [
|
|
189
|
+
"https://shell-hub.example.com",
|
|
190
|
+
"https://shell-vwr.example.com",
|
|
191
|
+
"https://shell-launch.example.com",
|
|
192
|
+
],
|
|
193
|
+
})
|
|
194
|
+
)
|
|
195
|
+
)
|
|
196
|
+
fetchSpy.mockResolvedValueOnce(
|
|
197
|
+
new Response(
|
|
198
|
+
JSON.stringify({
|
|
199
|
+
vwrUrl: "https://environment-vwr.example.com",
|
|
200
|
+
hubUrl: "https://environment-hub.example.com",
|
|
201
|
+
launchUrl: "https://environment-launch.example.com",
|
|
202
|
+
trustedDomains: [
|
|
203
|
+
"https://environment-hub.example.com",
|
|
204
|
+
"https://environment-vwr.example.com",
|
|
205
|
+
"https://environment-launch.example.com",
|
|
206
|
+
],
|
|
207
|
+
})
|
|
208
|
+
)
|
|
209
|
+
)
|
|
210
|
+
const config = await getVWRConfig(configRequest)
|
|
211
|
+
expect(validateConfig(config)).toBe(true)
|
|
212
|
+
expect(config.vwrUrl).toBe("https://device-vwr.example.com")
|
|
213
|
+
expect(config.hubUrl).toBe("https://device-hub.example.com")
|
|
214
|
+
expect(config.launchUrl).toBe("https://device-launch.example.com")
|
|
215
|
+
expect(config.trustedDomains).toEqual([
|
|
216
|
+
"https://device-hub.example.com",
|
|
217
|
+
"https://device-vwr.example.com",
|
|
218
|
+
"https://device-launch.example.com",
|
|
219
|
+
])
|
|
220
|
+
})
|
|
221
|
+
|
|
222
|
+
it("returns shell config when available", async () => {
|
|
223
|
+
fetchSpy.mockResolvedValueOnce(
|
|
224
|
+
new Response(
|
|
225
|
+
JSON.stringify({
|
|
226
|
+
vwrUrl: "https://shell-vwr.example.com",
|
|
227
|
+
hubUrl: "https://shell-hub.example.com",
|
|
228
|
+
launchUrl: "https://shell-launch.example.com",
|
|
229
|
+
trustedDomains: [
|
|
230
|
+
"https://shell-hub.example.com",
|
|
231
|
+
"https://shell-vwr.example.com",
|
|
232
|
+
"https://shell-launch.example.com",
|
|
233
|
+
],
|
|
234
|
+
})
|
|
235
|
+
)
|
|
236
|
+
)
|
|
237
|
+
fetchSpy.mockResolvedValueOnce(
|
|
238
|
+
new Response(
|
|
239
|
+
JSON.stringify({
|
|
240
|
+
vwrUrl: "https://environment-vwr.example.com",
|
|
241
|
+
hubUrl: "https://environment-hub.example.com",
|
|
242
|
+
launchUrl: "https://environment-launch.example.com",
|
|
243
|
+
trustedDomains: [
|
|
244
|
+
"https://environment-hub.example.com",
|
|
245
|
+
"https://environment-vwr.example.com",
|
|
246
|
+
"https://environment-launch.example.com",
|
|
247
|
+
],
|
|
248
|
+
})
|
|
249
|
+
)
|
|
250
|
+
)
|
|
251
|
+
const config = await getVWRConfig(configRequest)
|
|
252
|
+
expect(validateConfig(config)).toBe(true)
|
|
253
|
+
expect(config.vwrUrl).toBe("https://shell-vwr.example.com")
|
|
254
|
+
expect(config.hubUrl).toBe("https://shell-hub.example.com")
|
|
255
|
+
expect(config.launchUrl).toBe("https://shell-launch.example.com")
|
|
256
|
+
expect(config.trustedDomains).toEqual([
|
|
257
|
+
"https://shell-hub.example.com",
|
|
258
|
+
"https://shell-vwr.example.com",
|
|
259
|
+
"https://shell-launch.example.com",
|
|
260
|
+
])
|
|
261
|
+
})
|
|
262
|
+
|
|
263
|
+
it("returns environment config when available", async () => {
|
|
264
|
+
fetchSpy.mockResolvedValueOnce(
|
|
265
|
+
new Response(
|
|
266
|
+
JSON.stringify({
|
|
267
|
+
vwrUrl: "https://environment-vwr.example.com",
|
|
268
|
+
hubUrl: "https://environment-hub.example.com",
|
|
269
|
+
launchUrl: "https://environment-launch.example.com",
|
|
270
|
+
trustedDomains: [
|
|
271
|
+
"https://environment-hub.example.com",
|
|
272
|
+
"https://environment-vwr.example.com",
|
|
273
|
+
"https://environment-launch.example.com",
|
|
274
|
+
],
|
|
275
|
+
})
|
|
276
|
+
)
|
|
277
|
+
)
|
|
278
|
+
const config = await getVWRConfig(configRequest)
|
|
279
|
+
expect(validateConfig(config)).toBe(true)
|
|
280
|
+
expect(config.vwrUrl).toBe("https://environment-vwr.example.com")
|
|
281
|
+
expect(config.hubUrl).toBe("https://environment-hub.example.com")
|
|
282
|
+
expect(config.launchUrl).toBe(
|
|
283
|
+
"https://environment-launch.example.com"
|
|
284
|
+
)
|
|
285
|
+
expect(config.trustedDomains).toEqual([
|
|
286
|
+
"https://environment-hub.example.com",
|
|
287
|
+
"https://environment-vwr.example.com",
|
|
288
|
+
"https://environment-launch.example.com",
|
|
289
|
+
])
|
|
290
|
+
})
|
|
291
|
+
|
|
292
|
+
it("returns default config for missing values", async () => {
|
|
293
|
+
fetchSpy.mockResolvedValueOnce(
|
|
294
|
+
new Response(
|
|
295
|
+
JSON.stringify({
|
|
296
|
+
vwrUrl: "https://environment-vwr.example.com",
|
|
297
|
+
hubUrl: "https://environment-hub.example.com",
|
|
298
|
+
trustedDomains: [
|
|
299
|
+
"https://environment-hub.example.com",
|
|
300
|
+
"https://environment-vwr.example.com",
|
|
301
|
+
],
|
|
302
|
+
})
|
|
303
|
+
)
|
|
304
|
+
)
|
|
305
|
+
const config = await getVWRConfig(configRequest)
|
|
306
|
+
expect(config.vwrUrl).toBe("https://environment-vwr.example.com")
|
|
307
|
+
expect(config.hubUrl).toBe("https://environment-hub.example.com")
|
|
308
|
+
// launchUrl is optional, not filled when missing
|
|
309
|
+
expect(config.launchUrl).toBeUndefined()
|
|
310
|
+
expect(config.trustedDomains).toEqual([
|
|
311
|
+
"https://environment-hub.example.com",
|
|
312
|
+
"https://environment-vwr.example.com",
|
|
313
|
+
])
|
|
314
|
+
})
|
|
315
|
+
})
|
|
316
|
+
})
|
package/src/vwrConfig.ts
ADDED
|
@@ -0,0 +1,293 @@
|
|
|
1
|
+
import { ENV_DEFAULTS } from "./envDefaults"
|
|
2
|
+
import { defaultLogger, type Logger } from "./logger"
|
|
3
|
+
|
|
4
|
+
export type VWRConfig = {
|
|
5
|
+
hubUrl: string
|
|
6
|
+
vwrUrl: string
|
|
7
|
+
launchUrl: string | undefined
|
|
8
|
+
trustedDomains: Array<string>
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export type VWRConfigRequest = {
|
|
12
|
+
configUrl: string
|
|
13
|
+
configFile: string
|
|
14
|
+
platform: string
|
|
15
|
+
deviceId: string
|
|
16
|
+
environment: string
|
|
17
|
+
shellVersion: string
|
|
18
|
+
timeout?: number
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
const DEFAULT_CONFIG_TIMEOUT = 2000
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Build a URL from a base and relative path, handling missing trailing slashes.
|
|
25
|
+
*
|
|
26
|
+
* The URL constructor resolves relative paths against the "directory" of the base URL,
|
|
27
|
+
* which is defined as everything up to (but not including) the last path segment.
|
|
28
|
+
* Without a trailing slash, the last segment is treated as a "file" and gets replaced.
|
|
29
|
+
*
|
|
30
|
+
* Example:
|
|
31
|
+
* new URL("a/b.json", "https://x.com/config") → "https://x.com/a/b.json" (wrong)
|
|
32
|
+
* new URL("a/b.json", "https://x.com/config/") → "https://x.com/config/a/b.json" (correct)
|
|
33
|
+
*
|
|
34
|
+
* This helper normalizes the base URL to ensure correct resolution.
|
|
35
|
+
*/
|
|
36
|
+
const buildUrl = (path: string, base: string): URL => {
|
|
37
|
+
const normalizedBase = base.endsWith("/") ? base : `${base}/`
|
|
38
|
+
return new URL(path, normalizedBase)
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export const getVWRConfig = async (
|
|
42
|
+
request: VWRConfigRequest,
|
|
43
|
+
logger: Logger = defaultLogger
|
|
44
|
+
): Promise<VWRConfig> => {
|
|
45
|
+
const timeout = request.timeout ?? DEFAULT_CONFIG_TIMEOUT
|
|
46
|
+
|
|
47
|
+
logger.info(
|
|
48
|
+
"[VWR Config] Fetching config with priority: local → device → shellVersion → environment → defaults"
|
|
49
|
+
)
|
|
50
|
+
|
|
51
|
+
// Fetch all configs in parallel for performance
|
|
52
|
+
const [localConfig, deviceConfig, shellVersionConfig, environmentConfig] =
|
|
53
|
+
await Promise.all([
|
|
54
|
+
tryGetLocalConfig(request.configFile, timeout, logger),
|
|
55
|
+
tryGetDeviceConfig(
|
|
56
|
+
request.configUrl,
|
|
57
|
+
request.configFile,
|
|
58
|
+
request.platform,
|
|
59
|
+
request.deviceId,
|
|
60
|
+
timeout,
|
|
61
|
+
logger
|
|
62
|
+
),
|
|
63
|
+
tryGetShellVersionConfig(
|
|
64
|
+
request.configUrl,
|
|
65
|
+
request.configFile,
|
|
66
|
+
request.environment,
|
|
67
|
+
request.platform,
|
|
68
|
+
request.shellVersion,
|
|
69
|
+
timeout,
|
|
70
|
+
logger
|
|
71
|
+
),
|
|
72
|
+
tryGetEnvironmentConfig(
|
|
73
|
+
request.configUrl,
|
|
74
|
+
request.configFile,
|
|
75
|
+
request.environment,
|
|
76
|
+
timeout,
|
|
77
|
+
logger
|
|
78
|
+
),
|
|
79
|
+
])
|
|
80
|
+
|
|
81
|
+
// Return first successful config in priority order
|
|
82
|
+
if (localConfig !== null) {
|
|
83
|
+
logger.info("[VWR Config] ✓ Using config from: local", localConfig)
|
|
84
|
+
return localConfig
|
|
85
|
+
}
|
|
86
|
+
if (deviceConfig !== null) {
|
|
87
|
+
logger.info("[VWR Config] ✓ Using config from: device", deviceConfig)
|
|
88
|
+
return deviceConfig
|
|
89
|
+
}
|
|
90
|
+
if (shellVersionConfig !== null) {
|
|
91
|
+
logger.info(
|
|
92
|
+
"[VWR Config] ✓ Using config from: shellVersion",
|
|
93
|
+
shellVersionConfig
|
|
94
|
+
)
|
|
95
|
+
return shellVersionConfig
|
|
96
|
+
}
|
|
97
|
+
if (environmentConfig !== null) {
|
|
98
|
+
logger.info(
|
|
99
|
+
"[VWR Config] ✓ Using config from: environment",
|
|
100
|
+
environmentConfig
|
|
101
|
+
)
|
|
102
|
+
return environmentConfig
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
// All fetches failed, use defaults
|
|
106
|
+
logger.warn(
|
|
107
|
+
"[VWR Config] All config fetches failed, using built-in defaults"
|
|
108
|
+
)
|
|
109
|
+
const defaultConfig = getDefaultConfig()
|
|
110
|
+
return defaultConfig
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
const tryGetLocalConfig = async (
|
|
114
|
+
configFile: string,
|
|
115
|
+
timeout: number,
|
|
116
|
+
logger: Logger
|
|
117
|
+
): Promise<VWRConfig | null> => {
|
|
118
|
+
try {
|
|
119
|
+
const url = buildUrl(configFile, ENV_DEFAULTS["local"]?.configUrl ?? "")
|
|
120
|
+
logger.info(`[VWR Config] Trying local: ${url}`)
|
|
121
|
+
return tryGetVWRConfig(url, timeout)
|
|
122
|
+
} catch (error) {
|
|
123
|
+
logger.error(
|
|
124
|
+
`[VWR Config] URL construction failed for local config: ${error}`
|
|
125
|
+
)
|
|
126
|
+
return null
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
const tryGetDeviceConfig = async (
|
|
131
|
+
configUrl: string,
|
|
132
|
+
configFile: string,
|
|
133
|
+
platform: string,
|
|
134
|
+
deviceId: string,
|
|
135
|
+
timeout: number,
|
|
136
|
+
logger: Logger
|
|
137
|
+
): Promise<VWRConfig | null> => {
|
|
138
|
+
try {
|
|
139
|
+
const url = buildUrl(
|
|
140
|
+
`device/${platform}/${deviceId}/${configFile}`,
|
|
141
|
+
configUrl
|
|
142
|
+
)
|
|
143
|
+
logger.info(`[VWR Config] Trying device: ${url}`)
|
|
144
|
+
return tryGetVWRConfig(url, timeout)
|
|
145
|
+
} catch (error) {
|
|
146
|
+
logger.error(
|
|
147
|
+
`[VWR Config] URL construction failed for device config: ${error}`
|
|
148
|
+
)
|
|
149
|
+
return null
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
const tryGetEnvironmentConfig = async (
|
|
154
|
+
configUrl: string,
|
|
155
|
+
configFile: string,
|
|
156
|
+
environment: string,
|
|
157
|
+
timeout: number,
|
|
158
|
+
logger: Logger
|
|
159
|
+
): Promise<VWRConfig | null> => {
|
|
160
|
+
try {
|
|
161
|
+
const url = buildUrl(
|
|
162
|
+
`environments/${environment}/${configFile}`,
|
|
163
|
+
configUrl
|
|
164
|
+
)
|
|
165
|
+
logger.info(`[VWR Config] Trying environment: ${url}`)
|
|
166
|
+
return tryGetVWRConfig(url, timeout)
|
|
167
|
+
} catch (error) {
|
|
168
|
+
logger.error(
|
|
169
|
+
`[VWR Config] URL construction failed for environment config: ${error}`
|
|
170
|
+
)
|
|
171
|
+
return null
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
const tryGetShellVersionConfig = async (
|
|
176
|
+
configUrl: string,
|
|
177
|
+
configFile: string,
|
|
178
|
+
environment: string,
|
|
179
|
+
platform: string,
|
|
180
|
+
shellVersion: string,
|
|
181
|
+
timeout: number,
|
|
182
|
+
logger: Logger
|
|
183
|
+
): Promise<VWRConfig | null> => {
|
|
184
|
+
try {
|
|
185
|
+
const url = buildUrl(
|
|
186
|
+
`shellVersion/${environment}/${platform}/${shellVersion}/${configFile}`,
|
|
187
|
+
configUrl
|
|
188
|
+
)
|
|
189
|
+
logger.info(`[VWR Config] Trying shellVersion: ${url}`)
|
|
190
|
+
return tryGetVWRConfig(url, timeout)
|
|
191
|
+
} catch (error) {
|
|
192
|
+
logger.error(
|
|
193
|
+
`[VWR Config] URL construction failed for shellVersion config: ${error}`
|
|
194
|
+
)
|
|
195
|
+
return null
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
const tryGetVWRConfig = async (
|
|
200
|
+
url: URL,
|
|
201
|
+
timeout: number
|
|
202
|
+
): Promise<VWRConfig | null> => {
|
|
203
|
+
const controller = new AbortController()
|
|
204
|
+
const timeoutId = setTimeout(() => controller.abort(), timeout)
|
|
205
|
+
|
|
206
|
+
try {
|
|
207
|
+
const response = await fetch(
|
|
208
|
+
new Request(url, {
|
|
209
|
+
headers: {
|
|
210
|
+
"Content-Type": "application/json",
|
|
211
|
+
},
|
|
212
|
+
signal: controller.signal,
|
|
213
|
+
})
|
|
214
|
+
)
|
|
215
|
+
|
|
216
|
+
if (!response.ok) {
|
|
217
|
+
clearTimeout(timeoutId)
|
|
218
|
+
return null
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
let config = await response.json()
|
|
222
|
+
config = parseConfig(config)
|
|
223
|
+
|
|
224
|
+
clearTimeout(timeoutId)
|
|
225
|
+
return config as VWRConfig
|
|
226
|
+
} catch {
|
|
227
|
+
clearTimeout(timeoutId)
|
|
228
|
+
return null
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
const parseConfig = (config: VWRConfig): VWRConfig => {
|
|
233
|
+
const defaultConfig = getDefaultConfig()
|
|
234
|
+
|
|
235
|
+
if (
|
|
236
|
+
!Array.isArray(config.trustedDomains) ||
|
|
237
|
+
config.trustedDomains.length === 0
|
|
238
|
+
) {
|
|
239
|
+
config.trustedDomains = defaultConfig.trustedDomains
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
if (!config.vwrUrl) {
|
|
243
|
+
config.vwrUrl = defaultConfig.vwrUrl
|
|
244
|
+
if (!config.trustedDomains.includes(defaultConfig.vwrUrl)) {
|
|
245
|
+
config.trustedDomains.push(defaultConfig.vwrUrl)
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
if (!config.hubUrl) {
|
|
250
|
+
config.hubUrl = defaultConfig.hubUrl
|
|
251
|
+
if (!config.trustedDomains.includes(defaultConfig.hubUrl)) {
|
|
252
|
+
config.trustedDomains.push(defaultConfig.hubUrl)
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
// launchUrl is optional - only add to trustedDomains if explicitly set
|
|
257
|
+
if (config.launchUrl && !config.trustedDomains.includes(config.launchUrl)) {
|
|
258
|
+
config.trustedDomains.push(config.launchUrl)
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
return config
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
export const validateConfig = (config: VWRConfig): boolean => {
|
|
265
|
+
if (!config.vwrUrl) return false
|
|
266
|
+
|
|
267
|
+
if (!config.hubUrl) return false
|
|
268
|
+
|
|
269
|
+
// launchUrl is optional, allow undefined
|
|
270
|
+
|
|
271
|
+
if (!Array.isArray(config.trustedDomains)) return false
|
|
272
|
+
else if (config.trustedDomains.length === 0) return false
|
|
273
|
+
|
|
274
|
+
return true
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
const getDefaultConfig = (): VWRConfig => {
|
|
278
|
+
const ENVIRONMENT = import.meta.env.VITE_ENVIRONMENT || "dev"
|
|
279
|
+
const defaults = ENV_DEFAULTS[ENVIRONMENT]
|
|
280
|
+
|
|
281
|
+
if (!defaults) {
|
|
282
|
+
throw new Error(
|
|
283
|
+
`[VWR Config] Unknown environment: ${ENVIRONMENT}. Valid: local, dev, staging, prod`
|
|
284
|
+
)
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
return {
|
|
288
|
+
hubUrl: defaults.hubUrl,
|
|
289
|
+
vwrUrl: defaults.vwrUrl,
|
|
290
|
+
launchUrl: undefined,
|
|
291
|
+
trustedDomains: [defaults.hubUrl, defaults.vwrUrl],
|
|
292
|
+
}
|
|
293
|
+
}
|