@equinor/fusion-framework-module-app 8.1.0 → 8.1.2
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/dist/esm/AppConfigurator.js +3 -1
- package/dist/esm/AppConfigurator.js.map +1 -1
- package/dist/esm/__tests__/actions.test.js +27 -0
- package/dist/esm/__tests__/actions.test.js.map +1 -0
- package/dist/esm/app/actions.js +1 -1
- package/dist/esm/app/actions.js.map +1 -1
- package/dist/esm/version.js +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/dist/types/AppConfigurator.d.ts +7 -1
- package/dist/types/__tests__/actions.test.d.ts +1 -0
- package/dist/types/version.d.ts +1 -1
- package/package.json +12 -9
- package/CHANGELOG.md +0 -1469
- package/src/AppClient.ts +0 -409
- package/src/AppConfig.ts +0 -99
- package/src/AppConfigSelector.ts +0 -25
- package/src/AppConfigurator.ts +0 -145
- package/src/AppModuleProvider.ts +0 -283
- package/src/__tests__/AppModuleProvider.test.ts +0 -47
- package/src/__tests__/MockAppClient.test.ts +0 -97
- package/src/app/App.ts +0 -984
- package/src/app/actions.ts +0 -112
- package/src/app/create-reducer.ts +0 -62
- package/src/app/create-state.ts +0 -52
- package/src/app/events.ts +0 -108
- package/src/app/filter-empty.ts +0 -11
- package/src/app/flows/handle-fetch-config.ts +0 -48
- package/src/app/flows/handle-fetch-manifest.ts +0 -53
- package/src/app/flows/handle-fetch-settings.ts +0 -50
- package/src/app/flows/handle-import-application.ts +0 -37
- package/src/app/flows/handle-update-settings.ts +0 -39
- package/src/app/flows/index.ts +0 -5
- package/src/app/index.ts +0 -10
- package/src/app/types.ts +0 -51
- package/src/enable-app-module.ts +0 -40
- package/src/errors/AppBuildError.ts +0 -47
- package/src/errors/AppConfigError.ts +0 -47
- package/src/errors/AppManifestError.ts +0 -47
- package/src/errors/AppScriptModuleError.ts +0 -20
- package/src/errors/AppSettingsError.ts +0 -47
- package/src/errors/app-error-type.ts +0 -9
- package/src/errors.ts +0 -6
- package/src/events.ts +0 -18
- package/src/index.ts +0 -40
- package/src/mock/MockAppClient.ts +0 -77
- package/src/mock/index.ts +0 -13
- package/src/module.ts +0 -59
- package/src/schemas.ts +0 -181
- package/src/types.ts +0 -263
- package/src/version.ts +0 -2
- package/tsconfig.json +0 -30
- package/vitest.config.ts +0 -11
package/src/AppClient.ts
DELETED
|
@@ -1,409 +0,0 @@
|
|
|
1
|
-
import { catchError, map, type Observable, type ObservableInput, tap } from 'rxjs';
|
|
2
|
-
|
|
3
|
-
import { Query } from '@equinor/fusion-query';
|
|
4
|
-
import { queryValue } from '@equinor/fusion-query/operators';
|
|
5
|
-
|
|
6
|
-
import {
|
|
7
|
-
HttpJsonResponseError,
|
|
8
|
-
HttpResponseError,
|
|
9
|
-
type IHttpClient,
|
|
10
|
-
} from '@equinor/fusion-framework-module-http';
|
|
11
|
-
import { jsonSelector } from '@equinor/fusion-framework-module-http/selectors';
|
|
12
|
-
|
|
13
|
-
import { ApiApplicationBuildSchema, ApiApplicationSchema } from './schemas';
|
|
14
|
-
|
|
15
|
-
import type {
|
|
16
|
-
AppBuildManifest,
|
|
17
|
-
AppConfig,
|
|
18
|
-
AppManifest,
|
|
19
|
-
AppSettings,
|
|
20
|
-
ConfigEnvironment,
|
|
21
|
-
} from './types';
|
|
22
|
-
import { AppBuildError, AppConfigError, AppManifestError, AppSettingsError } from './errors';
|
|
23
|
-
import { AppConfigSelector } from './AppConfigSelector';
|
|
24
|
-
|
|
25
|
-
/**
|
|
26
|
-
* Contract for an app service client that fetches application manifests,
|
|
27
|
-
* build metadata, configurations, and per-user settings from the Fusion apps API.
|
|
28
|
-
*
|
|
29
|
-
* All methods return `ObservableInput` so consumers can use either `Observable`
|
|
30
|
-
* or `Promise`-based consumption patterns.
|
|
31
|
-
*/
|
|
32
|
-
export interface IAppClient extends Disposable {
|
|
33
|
-
/**
|
|
34
|
-
* Fetches the manifest for a single application.
|
|
35
|
-
*
|
|
36
|
-
* @param args - Object containing the `appKey` and an optional version `tag`.
|
|
37
|
-
* @returns An observable that emits the resolved {@link AppManifest}.
|
|
38
|
-
* @throws {AppManifestError} When the manifest cannot be loaded (404, 401, 410, or unknown).
|
|
39
|
-
*/
|
|
40
|
-
getAppManifest: (args: { appKey: string; tag?: string }) => ObservableInput<AppManifest>;
|
|
41
|
-
|
|
42
|
-
/**
|
|
43
|
-
* Fetches the build metadata (entry point, version, asset path) for an application.
|
|
44
|
-
*
|
|
45
|
-
* @param args - Object containing the `appKey` and an optional version `tag`.
|
|
46
|
-
* @returns An observable that emits the resolved {@link AppBuildManifest}.
|
|
47
|
-
* @throws {AppBuildError} When the build metadata cannot be loaded.
|
|
48
|
-
*/
|
|
49
|
-
getAppBuild: (args: { appKey: string; tag?: string }) => ObservableInput<AppBuildManifest>;
|
|
50
|
-
|
|
51
|
-
/**
|
|
52
|
-
* Fetches manifests for all registered applications.
|
|
53
|
-
*
|
|
54
|
-
* @param args - Optional filter; set `filterByCurrentUser` to `true` to return
|
|
55
|
-
* only apps the authenticated user has access to.
|
|
56
|
-
* @returns An observable that emits an array of {@link AppManifest} objects.
|
|
57
|
-
*/
|
|
58
|
-
getAppManifests: (args?: { filterByCurrentUser?: boolean }) => ObservableInput<AppManifest[]>;
|
|
59
|
-
|
|
60
|
-
/**
|
|
61
|
-
* Fetches the runtime configuration (environment variables and endpoints) for an application.
|
|
62
|
-
*
|
|
63
|
-
* @template TType - Shape of the `environment` record in the returned config.
|
|
64
|
-
* @param args - Object containing the `appKey` and an optional version `tag`.
|
|
65
|
-
* @returns An observable that emits the resolved {@link AppConfig}.
|
|
66
|
-
* @throws {AppConfigError} When the configuration cannot be loaded.
|
|
67
|
-
*/
|
|
68
|
-
getAppConfig: <TType extends ConfigEnvironment = ConfigEnvironment>(args: {
|
|
69
|
-
appKey: string;
|
|
70
|
-
tag?: string;
|
|
71
|
-
}) => ObservableInput<AppConfig<TType>>;
|
|
72
|
-
|
|
73
|
-
/**
|
|
74
|
-
* Fetches per-user settings for an application.
|
|
75
|
-
*
|
|
76
|
-
* @param args - Object containing the `appKey`.
|
|
77
|
-
* @returns An observable that emits the {@link AppSettings} record.
|
|
78
|
-
* @throws {AppSettingsError} When settings cannot be loaded.
|
|
79
|
-
*/
|
|
80
|
-
getAppSettings: (args: { appKey: string }) => ObservableInput<AppSettings>;
|
|
81
|
-
|
|
82
|
-
/**
|
|
83
|
-
* Persists updated per-user settings for an application via PUT.
|
|
84
|
-
*
|
|
85
|
-
* @param args - Object containing the `appKey` and the `settings` payload to save.
|
|
86
|
-
* @returns An observable that emits the persisted {@link AppSettings}.
|
|
87
|
-
* @throws {AppSettingsError} When the update request fails.
|
|
88
|
-
*/
|
|
89
|
-
updateAppSettings: (args: {
|
|
90
|
-
appKey: string;
|
|
91
|
-
settings: AppSettings;
|
|
92
|
-
}) => ObservableInput<AppSettings>;
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
/**
|
|
96
|
-
* Transforms a raw API application response into an {@link AppManifest},
|
|
97
|
-
* adding backwards-compatible `key` and `name` getters.
|
|
98
|
-
*
|
|
99
|
-
* @returns An object conforming to the AppManifest interface.
|
|
100
|
-
*/
|
|
101
|
-
const ApplicationSchema = ApiApplicationSchema.transform((x): AppManifest => {
|
|
102
|
-
const { category, ...props } = x;
|
|
103
|
-
return {
|
|
104
|
-
...props,
|
|
105
|
-
// TODO(#5124): remove deprecated appKey
|
|
106
|
-
get key() {
|
|
107
|
-
return props.appKey;
|
|
108
|
-
},
|
|
109
|
-
// TODO(#5124): remove deprecated name
|
|
110
|
-
get name() {
|
|
111
|
-
return props.displayName;
|
|
112
|
-
},
|
|
113
|
-
categoryId: category?.id,
|
|
114
|
-
category,
|
|
115
|
-
} as AppManifest;
|
|
116
|
-
});
|
|
117
|
-
|
|
118
|
-
/**
|
|
119
|
-
* Default implementation of {@link IAppClient} that communicates with the
|
|
120
|
-
* Fusion app service API over HTTP.
|
|
121
|
-
*
|
|
122
|
-
* Uses {@link Query} internally for request deduplication and caching
|
|
123
|
-
* (1-minute expiry by default). Responses are validated against Zod schemas
|
|
124
|
-
* ({@link ApiApplicationSchema}, {@link ApiApplicationBuildSchema}) and
|
|
125
|
-
* HTTP errors are mapped to typed error classes.
|
|
126
|
-
*
|
|
127
|
-
* @example
|
|
128
|
-
* ```ts
|
|
129
|
-
* const httpClient = await http.createClient('apps');
|
|
130
|
-
* const appClient = new AppClient(httpClient);
|
|
131
|
-
* appClient.getAppManifest({ appKey: 'my-app' }).subscribe(console.log);
|
|
132
|
-
* ```
|
|
133
|
-
*/
|
|
134
|
-
export class AppClient implements IAppClient {
|
|
135
|
-
#manifest: Query<AppManifest, { appKey: string }>;
|
|
136
|
-
#manifests: Query<AppManifest[], { filterByCurrentUser?: boolean } | undefined>;
|
|
137
|
-
#config: Query<AppConfig, { appKey: string; tag?: string }>;
|
|
138
|
-
#build: Query<AppBuildManifest, { appKey: string; tag?: string }>;
|
|
139
|
-
#settings: Query<AppSettings, { appKey: string; settings?: AppSettings }>;
|
|
140
|
-
#client: IHttpClient;
|
|
141
|
-
|
|
142
|
-
/**
|
|
143
|
-
* Creates a new {@link AppClient} backed by the given HTTP client.
|
|
144
|
-
* @param client - The HTTP client used to communicate with the Fusion app service API.
|
|
145
|
-
*/
|
|
146
|
-
constructor(client: IHttpClient) {
|
|
147
|
-
this.#client = client;
|
|
148
|
-
|
|
149
|
-
const expire = 1 * 60 * 1000;
|
|
150
|
-
|
|
151
|
-
this.#build = new Query<AppBuildManifest, { appKey: string; tag?: string }>({
|
|
152
|
-
client: {
|
|
153
|
-
fn: ({ appKey, tag }) => {
|
|
154
|
-
return client.json(`/apps/${appKey}/builds/${tag}`, {
|
|
155
|
-
headers: {
|
|
156
|
-
'Api-Version': '1.0',
|
|
157
|
-
},
|
|
158
|
-
selector: async (res: Response) =>
|
|
159
|
-
ApiApplicationBuildSchema.parse(await jsonSelector(res)) as AppBuildManifest,
|
|
160
|
-
});
|
|
161
|
-
},
|
|
162
|
-
},
|
|
163
|
-
queueOperator: 'merge',
|
|
164
|
-
key: ({ appKey, tag }) => `${appKey}@${tag}`,
|
|
165
|
-
expire,
|
|
166
|
-
});
|
|
167
|
-
|
|
168
|
-
this.#manifest = new Query<AppManifest, { appKey: string; tag?: string }>({
|
|
169
|
-
client: {
|
|
170
|
-
fn: ({ appKey, tag }) => {
|
|
171
|
-
return client.json$(tag ? `/apps/${appKey}@${tag}` : `/persons/me/apps/${appKey}`, {
|
|
172
|
-
headers: {
|
|
173
|
-
'Api-Version': '1.0',
|
|
174
|
-
},
|
|
175
|
-
selector: async (res: Response) => ApplicationSchema.parse(await jsonSelector(res)),
|
|
176
|
-
});
|
|
177
|
-
},
|
|
178
|
-
},
|
|
179
|
-
queueOperator: 'merge',
|
|
180
|
-
key: ({ appKey }) => appKey,
|
|
181
|
-
expire,
|
|
182
|
-
});
|
|
183
|
-
|
|
184
|
-
this.#manifests = new Query<AppManifest[], { filterByCurrentUser?: boolean } | undefined>({
|
|
185
|
-
client: {
|
|
186
|
-
fn: (filter) => {
|
|
187
|
-
const path = filter?.filterByCurrentUser
|
|
188
|
-
? '/persons/me/apps'
|
|
189
|
-
: '/apps?=$expand=category,admins,owners,keywords';
|
|
190
|
-
return client.json(path, {
|
|
191
|
-
headers: {
|
|
192
|
-
'Api-Version': '1.0',
|
|
193
|
-
},
|
|
194
|
-
selector: async (res: Response) => {
|
|
195
|
-
const response = (await jsonSelector(res)) as { value: AppManifest[] };
|
|
196
|
-
return ApplicationSchema.array().parse(response.value);
|
|
197
|
-
},
|
|
198
|
-
});
|
|
199
|
-
},
|
|
200
|
-
},
|
|
201
|
-
key: (filter) => (filter?.filterByCurrentUser ? 'currentUser' : 'all'),
|
|
202
|
-
expire,
|
|
203
|
-
});
|
|
204
|
-
|
|
205
|
-
this.#config = new Query<AppConfig, { appKey: string; tag?: string }>({
|
|
206
|
-
client: {
|
|
207
|
-
fn: ({ appKey, tag = 'latest' }) => {
|
|
208
|
-
return client.json(`/apps/${appKey}/builds/${tag}/config`, {
|
|
209
|
-
selector: AppConfigSelector,
|
|
210
|
-
headers: {
|
|
211
|
-
'Api-Version': '1.0',
|
|
212
|
-
},
|
|
213
|
-
});
|
|
214
|
-
},
|
|
215
|
-
},
|
|
216
|
-
queueOperator: 'merge',
|
|
217
|
-
key: (args) => JSON.stringify(args),
|
|
218
|
-
expire,
|
|
219
|
-
});
|
|
220
|
-
|
|
221
|
-
this.#settings = new Query<AppSettings, { appKey: string }>({
|
|
222
|
-
client: {
|
|
223
|
-
fn: ({ appKey }) => {
|
|
224
|
-
return client.json<AppSettings>(`/persons/me/apps/${appKey}/settings`, {
|
|
225
|
-
headers: {
|
|
226
|
-
'Api-Version': '1.0',
|
|
227
|
-
},
|
|
228
|
-
});
|
|
229
|
-
},
|
|
230
|
-
},
|
|
231
|
-
key: (args) => args.appKey,
|
|
232
|
-
expire,
|
|
233
|
-
});
|
|
234
|
-
}
|
|
235
|
-
|
|
236
|
-
/**
|
|
237
|
-
* Fetches the build metadata for an application.
|
|
238
|
-
* @param args - Object containing the `appKey` and an optional version `tag`.
|
|
239
|
-
* @returns An observable that emits the resolved {@link AppBuildManifest}.
|
|
240
|
-
* @throws {AppBuildError} When the build metadata cannot be loaded.
|
|
241
|
-
*/
|
|
242
|
-
getAppBuild(args: { appKey: string; tag?: string }): Observable<AppBuildManifest> {
|
|
243
|
-
// unwrap the build manifest and map errors to a typed AppBuildError
|
|
244
|
-
return this.#build.query(args).pipe(
|
|
245
|
-
// unwrap the query result into the build manifest
|
|
246
|
-
map((res) => res.value as AppBuildManifest),
|
|
247
|
-
// map http/unknown errors into a typed AppBuildError
|
|
248
|
-
catchError((err) => {
|
|
249
|
-
const cause = err?.cause || err;
|
|
250
|
-
|
|
251
|
-
// rethrow already-typed errors as-is
|
|
252
|
-
if (cause instanceof AppBuildError) {
|
|
253
|
-
throw cause;
|
|
254
|
-
}
|
|
255
|
-
// map http errors to a typed AppBuildError
|
|
256
|
-
if (cause instanceof HttpJsonResponseError || cause instanceof HttpResponseError) {
|
|
257
|
-
throw AppBuildError.fromHttpResponse(cause.response, { cause });
|
|
258
|
-
}
|
|
259
|
-
throw new AppBuildError('unknown', 'failed to load build', { cause });
|
|
260
|
-
}),
|
|
261
|
-
);
|
|
262
|
-
}
|
|
263
|
-
|
|
264
|
-
/**
|
|
265
|
-
* Fetches the manifest for a single application.
|
|
266
|
-
* @param args - Object containing the `appKey` and an optional version `tag`.
|
|
267
|
-
* @returns An observable that emits the resolved {@link AppManifest}.
|
|
268
|
-
* @throws {AppManifestError} When the manifest cannot be loaded.
|
|
269
|
-
*/
|
|
270
|
-
getAppManifest(args: { appKey: string; tag?: string }): Observable<AppManifest> {
|
|
271
|
-
// unwrap the manifest and map errors to a typed AppManifestError
|
|
272
|
-
return this.#manifest.query(args).pipe(
|
|
273
|
-
queryValue,
|
|
274
|
-
// map http/unknown errors into a typed AppManifestError
|
|
275
|
-
catchError((err) => {
|
|
276
|
-
const cause = err?.cause || err;
|
|
277
|
-
|
|
278
|
-
// rethrow already-typed errors as-is
|
|
279
|
-
if (cause instanceof AppManifestError) {
|
|
280
|
-
throw cause;
|
|
281
|
-
}
|
|
282
|
-
|
|
283
|
-
// map http errors to a typed AppManifestError
|
|
284
|
-
if (cause instanceof HttpJsonResponseError || cause instanceof HttpResponseError) {
|
|
285
|
-
throw AppManifestError.fromHttpResponse(cause.response, { cause });
|
|
286
|
-
}
|
|
287
|
-
|
|
288
|
-
throw new AppManifestError('unknown', 'failed to load manifest', { cause });
|
|
289
|
-
}),
|
|
290
|
-
);
|
|
291
|
-
}
|
|
292
|
-
|
|
293
|
-
/**
|
|
294
|
-
* Fetches manifests for all registered applications.
|
|
295
|
-
* @param args - Optional filter; set `filterByCurrentUser` to `true` to return only apps the authenticated user has access to.
|
|
296
|
-
* @returns An observable that emits an array of {@link AppManifest} objects.
|
|
297
|
-
*/
|
|
298
|
-
getAppManifests(args: { filterByCurrentUser?: boolean } | undefined): Observable<AppManifest[]> {
|
|
299
|
-
// unwrap the query result into the manifest array
|
|
300
|
-
return this.#manifests.query(args).pipe(queryValue);
|
|
301
|
-
}
|
|
302
|
-
|
|
303
|
-
/**
|
|
304
|
-
* Fetches the runtime configuration for an application.
|
|
305
|
-
* @template TType - Shape of the `environment` record in the returned config.
|
|
306
|
-
* @param args - Object containing the `appKey` and an optional version `tag`.
|
|
307
|
-
* @returns An observable that emits the resolved {@link AppConfig}.
|
|
308
|
-
* @throws {AppConfigError} When the configuration cannot be loaded.
|
|
309
|
-
*/
|
|
310
|
-
getAppConfig<TType extends ConfigEnvironment = ConfigEnvironment>(args: {
|
|
311
|
-
appKey: string;
|
|
312
|
-
tag?: string;
|
|
313
|
-
}): Observable<AppConfig<TType>> {
|
|
314
|
-
// unwrap the config and map errors to a typed AppConfigError
|
|
315
|
-
return this.#config.query(args).pipe(
|
|
316
|
-
// unwrap the query result into the config
|
|
317
|
-
map((res) => res.value as AppConfig<TType>),
|
|
318
|
-
// map http/unknown errors into a typed AppConfigError
|
|
319
|
-
catchError((err) => {
|
|
320
|
-
/** handle both direct errors and errors wrapped in a `cause` property */
|
|
321
|
-
const cause = err?.cause || err;
|
|
322
|
-
|
|
323
|
-
// rethrow already-typed errors as-is
|
|
324
|
-
if (cause instanceof AppConfigError) {
|
|
325
|
-
throw cause;
|
|
326
|
-
}
|
|
327
|
-
// map http errors to a typed AppConfigError
|
|
328
|
-
if (cause instanceof HttpJsonResponseError || cause instanceof HttpResponseError) {
|
|
329
|
-
throw AppConfigError.fromHttpResponse(cause.response, { cause });
|
|
330
|
-
}
|
|
331
|
-
throw new AppConfigError('unknown', 'failed to load config', { cause });
|
|
332
|
-
}),
|
|
333
|
-
);
|
|
334
|
-
}
|
|
335
|
-
|
|
336
|
-
/**
|
|
337
|
-
* Fetches per-user settings for an application.
|
|
338
|
-
* @param args - Object containing the `appKey`.
|
|
339
|
-
* @returns An observable that emits the {@link AppSettings} record.
|
|
340
|
-
* @throws {AppSettingsError} When settings cannot be loaded.
|
|
341
|
-
*/
|
|
342
|
-
getAppSettings(args: { appKey: string }): Observable<AppSettings> {
|
|
343
|
-
// unwrap the settings and map errors to a typed AppSettingsError
|
|
344
|
-
return this.#settings.query(args).pipe(
|
|
345
|
-
queryValue,
|
|
346
|
-
// map http/unknown errors into a typed AppSettingsError
|
|
347
|
-
catchError((err) => {
|
|
348
|
-
/** handle both direct errors and errors wrapped in a `cause` property */
|
|
349
|
-
const cause = err?.cause || err;
|
|
350
|
-
|
|
351
|
-
// rethrow already-typed errors as-is
|
|
352
|
-
if (cause instanceof AppSettingsError) {
|
|
353
|
-
throw cause;
|
|
354
|
-
}
|
|
355
|
-
// map http errors to a typed AppSettingsError
|
|
356
|
-
if (cause instanceof HttpJsonResponseError || cause instanceof HttpResponseError) {
|
|
357
|
-
throw AppSettingsError.fromHttpResponse(cause.response, { cause });
|
|
358
|
-
}
|
|
359
|
-
throw new AppSettingsError('unknown', 'failed to load settings', { cause });
|
|
360
|
-
}),
|
|
361
|
-
);
|
|
362
|
-
}
|
|
363
|
-
|
|
364
|
-
/**
|
|
365
|
-
* Persists updated per-user settings for an application via PUT.
|
|
366
|
-
* @param args - Object containing the `appKey` and the `settings` payload to save.
|
|
367
|
-
* @returns An observable that emits the persisted {@link AppSettings}.
|
|
368
|
-
*/
|
|
369
|
-
updateAppSettings(args: { appKey: string; settings: AppSettings }): Observable<AppSettings> {
|
|
370
|
-
const { appKey, settings } = args;
|
|
371
|
-
return (
|
|
372
|
-
this.#client
|
|
373
|
-
.json$<AppSettings>(`/persons/me/apps/${appKey}/settings`, {
|
|
374
|
-
method: 'PUT',
|
|
375
|
-
body: settings,
|
|
376
|
-
headers: {
|
|
377
|
-
'Api-Version': '1.0',
|
|
378
|
-
},
|
|
379
|
-
})
|
|
380
|
-
// update the settings cache with the persisted value
|
|
381
|
-
.pipe(
|
|
382
|
-
tap((value) => {
|
|
383
|
-
// update cache with new settings
|
|
384
|
-
this.#settings.mutate(
|
|
385
|
-
{ appKey },
|
|
386
|
-
{
|
|
387
|
-
value,
|
|
388
|
-
updated: Date.now(),
|
|
389
|
-
},
|
|
390
|
-
);
|
|
391
|
-
}),
|
|
392
|
-
)
|
|
393
|
-
);
|
|
394
|
-
}
|
|
395
|
-
|
|
396
|
-
/**
|
|
397
|
-
* Disposes of the client, completing all internal query streams.
|
|
398
|
-
*/
|
|
399
|
-
[Symbol.dispose]() {
|
|
400
|
-
console.warn('AppClient disposed');
|
|
401
|
-
this.#manifest.complete();
|
|
402
|
-
this.#manifests.complete();
|
|
403
|
-
this.#config.complete();
|
|
404
|
-
this.#settings.complete();
|
|
405
|
-
this.#build.complete();
|
|
406
|
-
}
|
|
407
|
-
}
|
|
408
|
-
|
|
409
|
-
export default AppClient;
|
package/src/AppConfig.ts
DELETED
|
@@ -1,99 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Recursively freezes an object and all of its nested properties.
|
|
3
|
-
*
|
|
4
|
-
* @template T - Object type being frozen.
|
|
5
|
-
* @param obj - The object to deep-freeze.
|
|
6
|
-
* @returns The same object, deeply frozen.
|
|
7
|
-
*/
|
|
8
|
-
const deepFreeze = <T extends Record<string, unknown>>(obj: T): T => {
|
|
9
|
-
// recursively freeze any nested, unfrozen object properties
|
|
10
|
-
for (const property of Object.keys(obj)) {
|
|
11
|
-
// only descend into unfrozen object properties
|
|
12
|
-
if (
|
|
13
|
-
typeof obj[property] === 'object' &&
|
|
14
|
-
obj[property] !== null &&
|
|
15
|
-
!Object.isFrozen(obj[property])
|
|
16
|
-
) {
|
|
17
|
-
deepFreeze(obj[property] as Record<string, unknown>);
|
|
18
|
-
}
|
|
19
|
-
}
|
|
20
|
-
return Object.freeze(obj);
|
|
21
|
-
};
|
|
22
|
-
|
|
23
|
-
/**
|
|
24
|
-
* Arbitrary key-value record representing environment-specific variables
|
|
25
|
-
* injected into an application's runtime configuration.
|
|
26
|
-
*/
|
|
27
|
-
export type ConfigEnvironment = Record<string, unknown>;
|
|
28
|
-
|
|
29
|
-
/**
|
|
30
|
-
* A named endpoint from the application configuration, containing a URL
|
|
31
|
-
* and the OAuth scopes required to call it.
|
|
32
|
-
*/
|
|
33
|
-
export type ConfigEndPoint = {
|
|
34
|
-
url: string;
|
|
35
|
-
scopes: string[];
|
|
36
|
-
};
|
|
37
|
-
|
|
38
|
-
/**
|
|
39
|
-
* Class representing the application configuration.
|
|
40
|
-
*
|
|
41
|
-
* @template TEnvironment - The type of the environment configuration, extending `ConfigEnvironment`.
|
|
42
|
-
*
|
|
43
|
-
* @remarks
|
|
44
|
-
* The `AppConfig` class provides a way to manage application configuration, including environment settings and endpoints.
|
|
45
|
-
*
|
|
46
|
-
* @example
|
|
47
|
-
* ```typescript
|
|
48
|
-
* const config = new AppConfig({
|
|
49
|
-
* environment: { ... },
|
|
50
|
-
* endpoints: {
|
|
51
|
-
* api: { url: 'https://api.example.com' }
|
|
52
|
-
* }
|
|
53
|
-
* });
|
|
54
|
-
*
|
|
55
|
-
* console.log(config.getEndpoint('api')); // { url: 'https://api.example.com' }
|
|
56
|
-
* ```
|
|
57
|
-
*/
|
|
58
|
-
export class AppConfig<TEnvironment extends ConfigEnvironment = ConfigEnvironment> {
|
|
59
|
-
#endpoints: Record<string, ConfigEndPoint>;
|
|
60
|
-
#environment: TEnvironment;
|
|
61
|
-
|
|
62
|
-
/**
|
|
63
|
-
* The environment configuration for the application.
|
|
64
|
-
* @returns The frozen environment configuration.
|
|
65
|
-
*/
|
|
66
|
-
get environment(): TEnvironment {
|
|
67
|
-
return this.#environment;
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
/**
|
|
71
|
-
* The configuration endpoints for the application.
|
|
72
|
-
* @returns The frozen map of configuration endpoints.
|
|
73
|
-
*/
|
|
74
|
-
get endpoints(): Record<string, ConfigEndPoint> {
|
|
75
|
-
return this.#endpoints;
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
/**
|
|
79
|
-
* Creates a new {@link AppConfig}, deep-freezing the provided environment and endpoints.
|
|
80
|
-
* @param config - The environment and endpoints to initialize the configuration with.
|
|
81
|
-
*/
|
|
82
|
-
constructor(config: {
|
|
83
|
-
environment?: TEnvironment | null;
|
|
84
|
-
endpoints?: Record<string, ConfigEndPoint>;
|
|
85
|
-
}) {
|
|
86
|
-
this.#environment = deepFreeze(config.environment ?? {}) as TEnvironment;
|
|
87
|
-
this.#endpoints = deepFreeze(config.endpoints ?? {});
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
/**
|
|
91
|
-
* Retrieves the configuration endpoint associated with the given key.
|
|
92
|
-
*
|
|
93
|
-
* @param key - The key corresponding to the desired configuration endpoint.
|
|
94
|
-
* @returns The configuration endpoint if found, otherwise `undefined`.
|
|
95
|
-
*/
|
|
96
|
-
getEndpoint(key: string): ConfigEndPoint | undefined {
|
|
97
|
-
return this.#endpoints[key];
|
|
98
|
-
}
|
|
99
|
-
}
|
package/src/AppConfigSelector.ts
DELETED
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
jsonSelector,
|
|
3
|
-
type ResponseSelector,
|
|
4
|
-
} from '@equinor/fusion-framework-module-http/selectors';
|
|
5
|
-
|
|
6
|
-
import { AppConfig } from './AppConfig';
|
|
7
|
-
import { ApiAppConfigSchema } from './schemas';
|
|
8
|
-
|
|
9
|
-
/**
|
|
10
|
-
* Asynchronously selects and parses the application configuration from the given response.
|
|
11
|
-
*
|
|
12
|
-
* @param response - The response object to select and parse the application configuration from.
|
|
13
|
-
* @returns A promise that resolves to an instance of `AppConfig` containing the parsed configuration data.
|
|
14
|
-
*
|
|
15
|
-
* @throws Will throw an error if the response cannot be parsed or does not conform to the expected schema.
|
|
16
|
-
*/
|
|
17
|
-
export const AppConfigSelector: ResponseSelector<AppConfig> = async (response) => {
|
|
18
|
-
// Select the JSON data from the response
|
|
19
|
-
const raw = await jsonSelector(response);
|
|
20
|
-
|
|
21
|
-
// Parse the JSON data using the API application configuration schema
|
|
22
|
-
const data = ApiAppConfigSchema.parse(raw);
|
|
23
|
-
|
|
24
|
-
return new AppConfig(data);
|
|
25
|
-
};
|
package/src/AppConfigurator.ts
DELETED
|
@@ -1,145 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
BaseConfigBuilder,
|
|
3
|
-
type ConfigBuilderCallback,
|
|
4
|
-
type ModuleInitializerArgs,
|
|
5
|
-
} from '@equinor/fusion-framework-module';
|
|
6
|
-
import type { HttpModule, IHttpClient } from '@equinor/fusion-framework-module-http';
|
|
7
|
-
import type { ServiceDiscoveryModule } from '@equinor/fusion-framework-module-service-discovery';
|
|
8
|
-
|
|
9
|
-
import AppClient, { type IAppClient } from './AppClient';
|
|
10
|
-
|
|
11
|
-
/**
|
|
12
|
-
* Resolved configuration for the app module.
|
|
13
|
-
*
|
|
14
|
-
* Produced by {@link AppConfigurator} during module initialization and consumed
|
|
15
|
-
* by {@link AppModuleProvider} at runtime.
|
|
16
|
-
*/
|
|
17
|
-
export interface AppModuleConfig {
|
|
18
|
-
/** HTTP client used to communicate with the Fusion app service API. */
|
|
19
|
-
client: IAppClient;
|
|
20
|
-
/** Base URI for fetching application script bundles (e.g., `'/apps-proxy'`). */
|
|
21
|
-
assetUri?: string;
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
/**
|
|
25
|
-
* Public interface for configuring the app module before initialization.
|
|
26
|
-
*
|
|
27
|
-
* Consumers use this interface (via the callback in {@link enableAppModule}) to
|
|
28
|
-
* override the default HTTP client or asset URI.
|
|
29
|
-
*/
|
|
30
|
-
export interface IAppConfigurator {
|
|
31
|
-
/**
|
|
32
|
-
* Sets the app service client used to fetch manifests, configs, and settings.
|
|
33
|
-
*
|
|
34
|
-
* @param client_or_cb - A promise resolving to an {@link IAppClient}, or a callback
|
|
35
|
-
* that receives module initializer args and returns one.
|
|
36
|
-
*/
|
|
37
|
-
setClient: (
|
|
38
|
-
client_or_cb:
|
|
39
|
-
| Promise<AppModuleConfig['client']>
|
|
40
|
-
| ConfigBuilderCallback<AppModuleConfig['client']>,
|
|
41
|
-
) => void;
|
|
42
|
-
|
|
43
|
-
/**
|
|
44
|
-
* Sets the base URI used to proxy-load application script bundles.
|
|
45
|
-
*
|
|
46
|
-
* @param base_or_cb - A static URI string or a callback returning one.
|
|
47
|
-
*/
|
|
48
|
-
setAssetUri: (base_or_cb: string | ConfigBuilderCallback<string>) => void;
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
/**
|
|
52
|
-
* Configuration builder for the app module.
|
|
53
|
-
*
|
|
54
|
-
* Extends {@link BaseConfigBuilder} to assemble an {@link AppModuleConfig} during
|
|
55
|
-
* framework initialization. If no explicit client is set, a default one is created
|
|
56
|
-
* via service discovery. The default `assetUri` is `'/apps-proxy'`.
|
|
57
|
-
*/
|
|
58
|
-
export class AppConfigurator
|
|
59
|
-
extends BaseConfigBuilder<AppModuleConfig>
|
|
60
|
-
implements IAppConfigurator
|
|
61
|
-
{
|
|
62
|
-
/** Default cache expiration time, in milliseconds, for the app service client. */
|
|
63
|
-
defaultExpireTime = 1 * 60 * 1000;
|
|
64
|
-
|
|
65
|
-
/**
|
|
66
|
-
* Creates the default HTTP client for the app service, preferring a pre-configured
|
|
67
|
-
* client from the http module and falling back to service discovery.
|
|
68
|
-
*
|
|
69
|
-
* WARNING: this function will be remove in future
|
|
70
|
-
*
|
|
71
|
-
* @param init - Module initializer args providing access to the http and service discovery modules.
|
|
72
|
-
* @returns A promise resolving to the {@link IHttpClient} used to communicate with the app service.
|
|
73
|
-
*/
|
|
74
|
-
protected async _createHttpClient(
|
|
75
|
-
init: ModuleInitializerArgs<IAppConfigurator, [HttpModule, ServiceDiscoveryModule]>,
|
|
76
|
-
): Promise<IHttpClient> {
|
|
77
|
-
const http = await init.requireInstance('http');
|
|
78
|
-
const serviceName = 'apps';
|
|
79
|
-
/** check if the http provider has configure a client */
|
|
80
|
-
if (http.hasClient(serviceName)) {
|
|
81
|
-
return http.createClient(serviceName);
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
/** load service discovery module */
|
|
85
|
-
const serviceDiscovery = await init.requireInstance('serviceDiscovery');
|
|
86
|
-
|
|
87
|
-
// TODO(#5125) - remove when refactor portal service!
|
|
88
|
-
/** resolve and create a client from discovery */
|
|
89
|
-
return await serviceDiscovery.createClient(serviceName);
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
/**
|
|
93
|
-
* Sets the app service client used to fetch manifests, configs, and settings.
|
|
94
|
-
* @param client_or_cb - A promise resolving to an {@link IAppClient}, or a callback
|
|
95
|
-
* that receives module initializer args and returns one.
|
|
96
|
-
*/
|
|
97
|
-
public setClient(
|
|
98
|
-
client_or_cb:
|
|
99
|
-
| Promise<AppModuleConfig['client']>
|
|
100
|
-
| ConfigBuilderCallback<AppModuleConfig['client']>,
|
|
101
|
-
) {
|
|
102
|
-
const cb = typeof client_or_cb === 'object' ? () => client_or_cb : client_or_cb;
|
|
103
|
-
this._set('client', cb);
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
/**
|
|
107
|
-
* Sets the base URI used to proxy-load application script bundles.
|
|
108
|
-
*
|
|
109
|
-
* TODO(#5132) - explain why, used in import of resources aka proxy url
|
|
110
|
-
*
|
|
111
|
-
* @param base_or_cb - A static URI string or a callback returning one.
|
|
112
|
-
*/
|
|
113
|
-
public setAssetUri(base_or_cb: string | ConfigBuilderCallback<string>) {
|
|
114
|
-
const cb = typeof base_or_cb === 'string' ? async () => base_or_cb : base_or_cb;
|
|
115
|
-
this._set('assetUri', cb);
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
/**
|
|
119
|
-
* Builds the resolved {@link AppModuleConfig}, applying default client and asset URI
|
|
120
|
-
* when they haven't been explicitly configured.
|
|
121
|
-
* @param init - Module initializer args used to build the default client.
|
|
122
|
-
* @param initial - Optional initial partial configuration.
|
|
123
|
-
* @returns The resolved module config.
|
|
124
|
-
*/
|
|
125
|
-
protected _createConfig(
|
|
126
|
-
init: ModuleInitializerArgs<IAppConfigurator, [HttpModule, ServiceDiscoveryModule]>,
|
|
127
|
-
initial?: Partial<AppModuleConfig>,
|
|
128
|
-
) {
|
|
129
|
-
// fall back to a default client created via the http/service discovery modules
|
|
130
|
-
if (!this._has('client')) {
|
|
131
|
-
this.setClient(async () => {
|
|
132
|
-
const httpClient = await this._createHttpClient(init);
|
|
133
|
-
const appClient = new AppClient(httpClient);
|
|
134
|
-
return appClient;
|
|
135
|
-
});
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
// fall back to the default proxy asset uri
|
|
139
|
-
if (!this._has('assetUri')) {
|
|
140
|
-
this.setAssetUri('/apps-proxy');
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
return super._createConfig(init, initial);
|
|
144
|
-
}
|
|
145
|
-
}
|