@equinor/fusion-framework-vite-plugin-spa 1.0.0-next.0 → 1.0.0-next.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.
Files changed (38) hide show
  1. package/CHANGELOG.md +11 -0
  2. package/dist/esm/html/bootstrap.js +54 -0
  3. package/dist/esm/html/bootstrap.js.map +1 -0
  4. package/dist/esm/html/index.html.js +51 -0
  5. package/dist/esm/html/index.html.js.map +1 -0
  6. package/dist/esm/html/index.js +2 -0
  7. package/dist/esm/html/index.js.map +1 -0
  8. package/dist/esm/html/register-service-worker.js +58 -0
  9. package/dist/esm/html/register-service-worker.js.map +1 -0
  10. package/dist/esm/html/sw.js +172 -0
  11. package/dist/esm/html/sw.js.map +1 -0
  12. package/dist/esm/index.js +3 -0
  13. package/dist/esm/index.js.map +1 -0
  14. package/dist/esm/plugin.js +65 -0
  15. package/dist/esm/plugin.js.map +1 -0
  16. package/dist/esm/types.js +2 -0
  17. package/dist/esm/types.js.map +1 -0
  18. package/dist/esm/util/load-env.js +24 -0
  19. package/dist/esm/util/load-env.js.map +1 -0
  20. package/dist/esm/util/object-to-env.js +46 -0
  21. package/dist/esm/util/object-to-env.js.map +1 -0
  22. package/dist/esm/version.js +3 -0
  23. package/dist/esm/version.js.map +1 -0
  24. package/dist/tsconfig.tsbuildinfo +1 -0
  25. package/dist/types/html/bootstrap.d.ts +1 -0
  26. package/dist/types/html/index.d.ts +1 -0
  27. package/dist/types/html/index.html.d.ts +19 -0
  28. package/dist/types/html/register-service-worker.d.ts +3 -0
  29. package/dist/types/html/sw.d.ts +1 -0
  30. package/dist/types/index.d.ts +2 -0
  31. package/dist/types/plugin.d.ts +21 -0
  32. package/dist/types/types.d.ts +45 -0
  33. package/dist/types/util/load-env.d.ts +15 -0
  34. package/dist/types/util/object-to-env.d.ts +33 -0
  35. package/dist/types/version.d.ts +1 -0
  36. package/package.json +3 -3
  37. package/src/version.ts +1 -1
  38. package/tsconfig.json +17 -0
package/CHANGELOG.md CHANGED
@@ -1,5 +1,16 @@
1
1
  # @equinor/fusion-framework-vite-plugin-spa
2
2
 
3
+ ## 1.0.0-next.1
4
+
5
+ ### Patch Changes
6
+
7
+ - [#3075](https://github.com/equinor/fusion-framework/pull/3075) [`96ad5d3`](https://github.com/equinor/fusion-framework/commit/96ad5d3a3aafe7adf5bd7f8e48e58bb19aa95ba8) Thanks [@odinr](https://github.com/odinr)! - Add prepack script to run build before packaging
8
+
9
+ A `prepack` script was added to both the SPA and API service Vite plugin packages. This ensures the build step runs automatically before packaging, improving reliability of published artifacts.
10
+
11
+ - Updated dependencies [[`96ad5d3`](https://github.com/equinor/fusion-framework/commit/96ad5d3a3aafe7adf5bd7f8e48e58bb19aa95ba8)]:
12
+ - @equinor/fusion-framework-vite-plugin-api-service@1.0.0-next.1
13
+
3
14
  ## 1.0.0-next.0
4
15
 
5
16
  ### Major Changes
@@ -0,0 +1,54 @@
1
+ import { ModulesConfigurator } from '@equinor/fusion-framework-module';
2
+ import { configureHttpClient } from '@equinor/fusion-framework-module-http';
3
+ import { enableMSAL } from '@equinor/fusion-framework-module-msal';
4
+ import { enableServiceDiscovery, } from '@equinor/fusion-framework-module-service-discovery';
5
+ import { registerServiceWorker } from './register-service-worker.js';
6
+ // Allow dynamic import without vite
7
+ const importWithoutVite = (path) => import(/* @vite-ignore */ path);
8
+ // Create Fusion Framework configurator
9
+ const configurator = new ModulesConfigurator();
10
+ configurator.logger.level = import.meta.env.FUSION_SPA_LOG_LEVEL ?? 1;
11
+ const serviceDiscoveryUrl = new URL(import.meta.env.FUSION_SPA_SERVICE_DISCOVERY_URL, import.meta.env.FUSION_SPA_SERVICE_DISCOVERY_URL.startsWith('http')
12
+ ? undefined
13
+ : window.location.origin);
14
+ // define service discovery client - this is used in the service discovery module
15
+ configurator.addConfig(configureHttpClient('service_discovery', {
16
+ baseUri: String(serviceDiscoveryUrl),
17
+ defaultScopes: import.meta.env.FUSION_SPA_SERVICE_DISCOVERY_SCOPES,
18
+ }));
19
+ // setup service discovery - enable service discovery for the framework
20
+ enableServiceDiscovery(configurator, async (builder) => {
21
+ builder.configureServiceDiscoveryClientByClientKey('service_discovery');
22
+ });
23
+ // setup authentication
24
+ enableMSAL(configurator, (builder) => {
25
+ builder.setClientConfig({
26
+ tenantId: import.meta.env.FUSION_SPA_MSAL_TENANT_ID,
27
+ clientId: import.meta.env.FUSION_SPA_MSAL_CLIENT_ID,
28
+ redirectUri: import.meta.env.FUSION_SPA_MSAL_REDIRECT_URI,
29
+ });
30
+ builder.setRequiresAuth(Boolean(import.meta.env.FUSION_SPA_MSAL_REQUIRES_AUTH));
31
+ });
32
+ (async () => {
33
+ // initialize the framework - this will create the framework instance and configure the modules
34
+ const ref = await configurator.initialize();
35
+ // attach service discovery to the framework - append auth token to configured endpoints
36
+ await registerServiceWorker(ref);
37
+ // create a client for the portal service - this is used to fetch the portal manifest
38
+ const portalClient = await ref.serviceDiscovery.createClient('portals');
39
+ // fetch the portal manifest - this is used to load the portal template
40
+ const portalId = import.meta.env.FUSION_SPA_PORTAL_ID;
41
+ const portalTag = import.meta.env.FUSION_SPA_PORTAL_TAG ?? 'latest';
42
+ const portal_manifest = await portalClient.json(`/portals/${portalId}@${portalTag}`);
43
+ const portal_config = await portalClient.json(`/portals/${portalId}@${portalTag}/config`);
44
+ // create a entrypoint for the portal - this is used to render the portal
45
+ const el = document.createElement('div');
46
+ document.body.innerHTML = '';
47
+ document.body.appendChild(el);
48
+ // @todo: should test if the entrypoint is external or internal
49
+ // @todo: add proper return type
50
+ const { render } = await importWithoutVite(portal_manifest.build.entrypoint);
51
+ // render the portal - this will load the portal template and render it
52
+ render(el, { ref, manifest: portal_manifest, config: portal_config });
53
+ })();
54
+ //# sourceMappingURL=bootstrap.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bootstrap.js","sourceRoot":"","sources":["../../../src/html/bootstrap.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,51 @@
1
+ import { version } from '../version.js';
2
+ /**
3
+ * Represents an HTML template string used for generating the main structure of an SPA (Single Page Application).
4
+ *
5
+ * @see {@link https://vite.dev/guide/env-and-mode.html#html-constant-replacement}
6
+ *
7
+ * The template includes placeholders for dynamic values such as:
8
+ * - `%FUSION_SPA_TITLE%`: The title of the SPA.
9
+ * - `%MODE%`: The mode of the application (e.g., development, production).
10
+ * - `%FUSION_SPA_BOOTSTRAP%`: The path to the bootstrap script for initializing the SPA.
11
+ *
12
+ * Additionally, it includes:
13
+ * - A meta tag for the plugin version, dynamically populated using the `version` variable.
14
+ * - A link to the Equinor font stylesheet hosted on a CDN.
15
+ *
16
+ * @constant
17
+ * @type {string}
18
+ */
19
+ export const html = `
20
+ <!DOCTYPE html>
21
+ <html>
22
+ <head>
23
+ <title>%FUSION_SPA_TITLE%</title>
24
+ <meta name="mode" content="%MODE%">
25
+ <meta name="fusion-spa-plugin-version" content="${version}">
26
+ <link rel="stylesheet" href="https://cdn.eds.equinor.com/font/equinor-font.css" />
27
+ <script type="module" src="%FUSION_SPA_BOOTSTRAP%"></script>
28
+ <script>
29
+ // suppress console error for custom elements already defined.
30
+ // WebComponents should be added by the portal, but not removed from application
31
+ const _customElementsDefine = window.customElements.define;
32
+ window.customElements.define = (name, cl, conf) => {
33
+ if (!customElements.get(name)) {
34
+ _customElementsDefine.call(window.customElements, name, cl, conf);
35
+ }
36
+ };
37
+ </script>
38
+ <style>
39
+ html, body {
40
+ margin: 0;
41
+ padding: 0;
42
+ height: 100%;
43
+ font-family: 'EquinorFont', sans-serif;
44
+ }
45
+ </style>
46
+ </head>
47
+ <body></body>
48
+ </html>
49
+ `;
50
+ export default html;
51
+ //# sourceMappingURL=index.html.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.html.js","sourceRoot":"","sources":["../../../src/html/index.html.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,eAAe,CAAC;AAExC;;;;;;;;;;;;;;;;GAgBG;AACH,MAAM,CAAC,MAAM,IAAI,GAAG;;;;;;wDAMoC,OAAO;;;;;;;;;;;;;;;;;;;;;;;;CAwB9D,CAAC;AAEF,eAAe,IAAI,CAAC"}
@@ -0,0 +1,2 @@
1
+ export { registerServiceWorker } from './register-service-worker.js';
2
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/html/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,qBAAqB,EAAE,MAAM,8BAA8B,CAAC"}
@@ -0,0 +1,58 @@
1
+ export async function registerServiceWorker(framework) {
2
+ if ('serviceWorker' in navigator === false) {
3
+ console.warn('Service workers are not supported in this browser.');
4
+ return;
5
+ }
6
+ const resourceConfigs = import.meta.env.FUSION_SPA_SERVICE_WORKER_RESOURCES;
7
+ if (!resourceConfigs) {
8
+ console.warn('Service worker config is not defined.');
9
+ return;
10
+ }
11
+ try {
12
+ // register the service worker
13
+ const registration = await navigator.serviceWorker.register('/@fusion-spa-sw.js', {
14
+ type: 'module',
15
+ scope: '/',
16
+ });
17
+ // wait for the service worker to be ready
18
+ await navigator.serviceWorker.ready;
19
+ // allow the service worker to start receiving messages
20
+ navigator.serviceWorker.startMessages();
21
+ // send the config to the service worker
22
+ registration.active?.postMessage({
23
+ type: 'INIT_CONFIG',
24
+ config: resourceConfigs,
25
+ });
26
+ // listen for messages from the service worker
27
+ navigator.serviceWorker.addEventListener('message', async (event) => {
28
+ if (event.data.type === 'GET_TOKEN') {
29
+ try {
30
+ // extract scopes from the event data
31
+ const scopes = event.data.scopes;
32
+ if (!scopes || !Array.isArray(scopes)) {
33
+ throw new Error('Invalid scopes provided');
34
+ }
35
+ // request a token from the MSAL module
36
+ const token = await framework.auth.acquireToken({ scopes });
37
+ if (!token) {
38
+ throw new Error('Failed to acquire token');
39
+ }
40
+ // send the token back to the service worker
41
+ event.ports[0].postMessage({
42
+ accessToken: token.accessToken,
43
+ expiresOn: token.expiresOn?.getTime(),
44
+ });
45
+ }
46
+ catch (error) {
47
+ event.ports[0].postMessage({
48
+ error: error.message,
49
+ });
50
+ }
51
+ }
52
+ });
53
+ }
54
+ catch (error) {
55
+ console.error('Service Worker registration failed:', error);
56
+ }
57
+ }
58
+ //# sourceMappingURL=register-service-worker.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"register-service-worker.js","sourceRoot":"","sources":["../../../src/html/register-service-worker.ts"],"names":[],"mappings":"AAGA,MAAM,CAAC,KAAK,UAAU,qBAAqB,CAAC,SAAwC;IAClF,IAAI,eAAe,IAAI,SAAS,KAAK,KAAK,EAAE,CAAC;QAC3C,OAAO,CAAC,IAAI,CAAC,oDAAoD,CAAC,CAAC;QACnE,OAAO;IACT,CAAC;IAED,MAAM,eAAe,GAAG,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,mCAAmC,CAAC;IAC5E,IAAI,CAAC,eAAe,EAAE,CAAC;QACrB,OAAO,CAAC,IAAI,CAAC,uCAAuC,CAAC,CAAC;QACtD,OAAO;IACT,CAAC;IAED,IAAI,CAAC;QACH,8BAA8B;QAC9B,MAAM,YAAY,GAAG,MAAM,SAAS,CAAC,aAAa,CAAC,QAAQ,CAAC,oBAAoB,EAAE;YAChF,IAAI,EAAE,QAAQ;YACd,KAAK,EAAE,GAAG;SACX,CAAC,CAAC;QAEH,0CAA0C;QAC1C,MAAM,SAAS,CAAC,aAAa,CAAC,KAAK,CAAC;QAEpC,uDAAuD;QACvD,SAAS,CAAC,aAAa,CAAC,aAAa,EAAE,CAAC;QAExC,wCAAwC;QACxC,YAAY,CAAC,MAAM,EAAE,WAAW,CAAC;YAC/B,IAAI,EAAE,aAAa;YACnB,MAAM,EAAE,eAAe;SACxB,CAAC,CAAC;QAEH,8CAA8C;QAC9C,SAAS,CAAC,aAAa,CAAC,gBAAgB,CAAC,SAAS,EAAE,KAAK,EAAE,KAAK,EAAE,EAAE;YAClE,IAAI,KAAK,CAAC,IAAI,CAAC,IAAI,KAAK,WAAW,EAAE,CAAC;gBACpC,IAAI,CAAC;oBACH,qCAAqC;oBACrC,MAAM,MAAM,GAAG,KAAK,CAAC,IAAI,CAAC,MAAkB,CAAC;oBAC7C,IAAI,CAAC,MAAM,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC;wBACtC,MAAM,IAAI,KAAK,CAAC,yBAAyB,CAAC,CAAC;oBAC7C,CAAC;oBAED,uCAAuC;oBACvC,MAAM,KAAK,GAAG,MAAM,SAAS,CAAC,IAAI,CAAC,YAAY,CAAC,EAAE,MAAM,EAAE,CAAC,CAAC;oBAE5D,IAAI,CAAC,KAAK,EAAE,CAAC;wBACX,MAAM,IAAI,KAAK,CAAC,yBAAyB,CAAC,CAAC;oBAC7C,CAAC;oBAED,4CAA4C;oBAC5C,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,WAAW,CAAC;wBACzB,WAAW,EAAE,KAAK,CAAC,WAAW;wBAC9B,SAAS,EAAE,KAAK,CAAC,SAAS,EAAE,OAAO,EAAE;qBACtC,CAAC,CAAC;gBACL,CAAC;gBAAC,OAAO,KAAK,EAAE,CAAC;oBACf,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,WAAW,CAAC;wBACzB,KAAK,EAAG,KAAe,CAAC,OAAO;qBAChC,CAAC,CAAC;gBACL,CAAC;YACH,CAAC;QACH,CAAC,CAAC,CAAC;IACL,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,OAAO,CAAC,KAAK,CAAC,qCAAqC,EAAE,KAAK,CAAC,CAAC;IAC9D,CAAC;AACH,CAAC"}
@@ -0,0 +1,172 @@
1
+ /// <reference lib="webworker" />
2
+ /**
3
+ * A reference to the global scope of the service worker.
4
+ *
5
+ * The `self` variable is explicitly cast to `ServiceWorkerGlobalScope` to ensure
6
+ * type safety and provide access to service worker-specific APIs.
7
+ *
8
+ * This is necessary because `globalThis` is a generic global object and does not
9
+ * include service worker-specific properties and methods by default.
10
+ */
11
+ const self = globalThis;
12
+ /**
13
+ * An array of settings used for token injection.
14
+ * Each setting defines the configuration for injecting tokens
15
+ * into the application, such as authentication or API tokens.
16
+ */
17
+ let resourceConfigurations = [];
18
+ /**
19
+ * A cache for storing tokens, implemented as a `Map`.
20
+ * This cache is used to temporarily hold tokens for quick retrieval.
21
+ *
22
+ * @type {TokenCache} - A `Map` instance where the keys and values are determined by the `TokenCache` type definition.
23
+ */
24
+ const tokenCache = new Map();
25
+ /**
26
+ * Generates a unique key by sorting and concatenating an array of scope strings.
27
+ *
28
+ * @param scopes - An array of strings representing the scopes to be processed.
29
+ * @returns A single string representing the sorted and concatenated scopes, separated by commas.
30
+ */
31
+ function getScopeKey(scopes) {
32
+ return scopes.sort().join(',');
33
+ }
34
+ /**
35
+ * Checks if a token associated with the specified scopes is valid.
36
+ *
37
+ * This function determines the validity of a token by checking if it exists
38
+ * in the token cache and if its expiration time has not been reached.
39
+ *
40
+ * @param scopes - An array of strings representing the scopes for which the token is required.
41
+ * @returns `true` if a valid token exists for the given scopes; otherwise, `false`.
42
+ */
43
+ function isTokenValid(scopes) {
44
+ const scopeKey = getScopeKey(scopes);
45
+ if (!tokenCache.has(scopeKey)) {
46
+ return false;
47
+ }
48
+ const tokenData = tokenCache.get(scopeKey);
49
+ return tokenData !== undefined && Date.now() < tokenData.expiresOn;
50
+ }
51
+ /**
52
+ * Requests an access token from a client using the Service Worker's `clients` API.
53
+ * Communicates with the client via a `MessageChannel` to retrieve the token.
54
+ *
55
+ * @param scopes - An array of strings representing the scopes for which the token is requested.
56
+ * @returns A promise that resolves to the token object containing the `accessToken` and `expiresOn` properties.
57
+ * @throws An error if no clients are available or if the client responds with an error.
58
+ *
59
+ * @example
60
+ * ```typescript
61
+ * const token = await requestTokenFromClient(['scope1', 'scope2']);
62
+ * console.log(token.accessToken); // Access token string
63
+ * console.log(token.expiresOn); // Expiration timestamp
64
+ * ```
65
+ */
66
+ async function requestTokenFromClient(scopes) {
67
+ const clients = await self.clients.matchAll();
68
+ // ensure there are clients available
69
+ if (clients.length === 0) {
70
+ throw new Error('No clients available');
71
+ }
72
+ // create a message channel to communicate with the client
73
+ const messageChannel = new MessageChannel();
74
+ const token = await new Promise((resolve, reject) => {
75
+ messageChannel.port1.onmessage = (event) => {
76
+ if (event.data.error) {
77
+ reject(event.data.error);
78
+ }
79
+ resolve(event.data);
80
+ };
81
+ clients[0].postMessage({ type: 'GET_TOKEN', scopes }, [messageChannel.port2]);
82
+ });
83
+ if (!token) {
84
+ throw new Error('No token received');
85
+ }
86
+ // store the token in the cache
87
+ tokenCache.set(getScopeKey(scopes), token);
88
+ return token;
89
+ }
90
+ /**
91
+ * Retrieves an access token for the specified scopes. If no valid token is found,
92
+ * it requests a new one from the client.
93
+ *
94
+ * @param scopes - An array of strings representing the required scopes for the token.
95
+ * @returns A promise that resolves to the access token as a string.
96
+ * @throws An error if no access token is found after attempting to retrieve or request one.
97
+ */
98
+ async function getToken(scopes) {
99
+ // if no valid token is found, request a new one
100
+ if (!isTokenValid(scopes)) {
101
+ await requestTokenFromClient(scopes);
102
+ }
103
+ const scopeKey = getScopeKey(scopes);
104
+ const { accessToken } = tokenCache.get(scopeKey) || {};
105
+ if (!accessToken) {
106
+ throw new Error('No access token found');
107
+ }
108
+ return accessToken;
109
+ }
110
+ // Match request to proxy config
111
+ /**
112
+ * Retrieves the matching token injection configuration for a given URL.
113
+ *
114
+ * @param url - The URL to match against the token injection settings.
115
+ * @returns The matching `TokenInjectionSetting` if found, otherwise `undefined`.
116
+ *
117
+ * The function compares the provided URL with the `url` property of each
118
+ * `TokenInjectionSetting` in the `tokenInjectionSettings` array. If the
119
+ * provided URL starts with the resolved `config.url`, it is considered a match.
120
+ *
121
+ * Note:
122
+ * - If `config.url` starts with a `/`, it is resolved relative to the service
123
+ * worker's origin (`self.location.origin`).
124
+ * - The comparison is performed using fully resolved absolute URLs.
125
+ */
126
+ function getMatchingConfig(url) {
127
+ return resourceConfigurations.find((config) => {
128
+ const configUrl = new URL(config.url, config.url.startsWith('/') ? self.location.origin : undefined).href;
129
+ const requestUrl = new URL(url, self.location.origin).href;
130
+ return requestUrl.startsWith(configUrl);
131
+ });
132
+ }
133
+ // Install event
134
+ self.addEventListener('install', (event) => {
135
+ event.waitUntil(self.skipWaiting());
136
+ });
137
+ // Activate event
138
+ self.addEventListener('activate', (event) => {
139
+ event.waitUntil(self.clients.claim());
140
+ });
141
+ // Handle configuration from main thread
142
+ self.addEventListener('message', (event) => {
143
+ const { type, config } = event.data;
144
+ if (type === 'INIT_CONFIG') {
145
+ resourceConfigurations = config;
146
+ }
147
+ });
148
+ // Handle fetch events
149
+ self.addEventListener('fetch', (event) => {
150
+ const url = new URL(event.request.url);
151
+ const matchedConfig = getMatchingConfig(url.toString());
152
+ // only handle requests that match the config
153
+ if (matchedConfig) {
154
+ const requestHeaders = new Headers(event.request.headers);
155
+ const handleRequest = async () => {
156
+ // if the matched config has scopes, append the token to the request
157
+ if (matchedConfig.scopes) {
158
+ const token = await getToken(matchedConfig.scopes);
159
+ requestHeaders.set('Authorization', `Bearer ${token}`);
160
+ }
161
+ // if the matched config has a rewrite, rewrite the url
162
+ if (typeof matchedConfig.rewrite === 'string') {
163
+ url.pathname = url.pathname.replace(matchedConfig?.url, matchedConfig.rewrite);
164
+ }
165
+ // fetch the request with the modified url and headers
166
+ return fetch(url, { headers: requestHeaders });
167
+ };
168
+ event.respondWith(handleRequest());
169
+ }
170
+ });
171
+ export {};
172
+ //# sourceMappingURL=sw.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sw.js","sourceRoot":"","sources":["../../../src/html/sw.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ export { default, plugin as fusionSpaPlugin } from './plugin.js';
2
+ export * from './types.js';
3
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,IAAI,eAAe,EAAsB,MAAM,aAAa,CAAC;AAErF,cAAc,YAAY,CAAC"}
@@ -0,0 +1,65 @@
1
+ import mergeWith from 'lodash.mergewith';
2
+ import defaultTemplate from './html/index.html.js';
3
+ import { objectToEnv } from './util/object-to-env.js';
4
+ import { loadEnvironment } from './util/load-env.js';
5
+ /**
6
+ * Represents the default environment configuration for the Fusion SPA.
7
+ */
8
+ const defaultEnv = {
9
+ title: 'Fusion SPA',
10
+ bootstrap: '/@fusion-spa-bootstrap.js',
11
+ };
12
+ export const plugin = (options) => {
13
+ // SPA index template
14
+ const indexTemplate = options?.template ?? defaultTemplate;
15
+ const log = options?.logger;
16
+ return {
17
+ name: 'fusion-framework-plugin-spa',
18
+ resolveId(id) {
19
+ // resolve resource aliases to the correct path
20
+ switch (id) {
21
+ case '/@fusion-spa-bootstrap.js':
22
+ return new URL('./html/bootstrap.js', import.meta.url).pathname;
23
+ case '/@fusion-spa-sw.js':
24
+ return new URL('./html/sw.js', import.meta.url).pathname;
25
+ }
26
+ },
27
+ config(config, configEnv) {
28
+ const templateEnvPrefix = options?.templateEnvPrefix ?? 'FUSION_SPA_';
29
+ // generate environment variables from plugin options
30
+ const pluginEnvObj = { ...defaultEnv, ...options?.generateTemplateEnv?.(configEnv) };
31
+ const pluginEnv = objectToEnv(pluginEnvObj ?? defaultEnv, templateEnvPrefix);
32
+ log?.debug('plugin config environment\n', pluginEnv);
33
+ // load environment variables from files
34
+ const loadedEnv = loadEnvironment(config, configEnv, templateEnvPrefix);
35
+ log?.debug('plugin loaded environment\n', pluginEnv);
36
+ const env = mergeWith(pluginEnv, loadedEnv);
37
+ log?.debug('plugin environment\n', env);
38
+ // define environment variables
39
+ config.define ??= {};
40
+ for (const [key, value] of Object.entries(env)) {
41
+ config.define[`import.meta.env.${key}`] = value;
42
+ }
43
+ log?.info(`plugin configured for ${env.FUSION_SPA_PORTAL_ID}`);
44
+ },
45
+ configureServer(server) {
46
+ // Apply SPA fallback
47
+ server.middlewares.use(async (req, res, next) => {
48
+ // Skip if this is not a GET request or the request is not for HTML
49
+ if (!req.url || req.method !== 'GET' || !req.headers.accept?.includes('text/html')) {
50
+ return next();
51
+ }
52
+ const html = await server.transformIndexHtml(req.url, indexTemplate, req.originalUrl);
53
+ res.writeHead(200, {
54
+ 'content-type': 'text/html',
55
+ 'content-length': Buffer.byteLength(html),
56
+ 'cache-control': 'no-cache',
57
+ ...server.config.server.headers,
58
+ });
59
+ return res.end(html);
60
+ });
61
+ },
62
+ };
63
+ };
64
+ export default plugin;
65
+ //# sourceMappingURL=plugin.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"plugin.js","sourceRoot":"","sources":["../../src/plugin.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/types.ts"],"names":[],"mappings":""}
@@ -0,0 +1,24 @@
1
+ import { loadEnv } from 'vite';
2
+ import { resolve } from 'node:path';
3
+ /**
4
+ * Loads environment variables for a Vite project based on the provided configuration and environment.
5
+ *
6
+ * @see {@link http://vite.dev/guide/env-and-mode.html#env-files}
7
+ *
8
+ * @param config - The user configuration object, which includes properties such as `root` and `envDir`.
9
+ * - `root`: The root directory of the project. Defaults to the current working directory if not specified.
10
+ * - `envDir`: The directory containing environment files. If not specified, defaults to the root directory.
11
+ * @param env - The environment configuration object.
12
+ * - `mode`: The mode in which the application is running (e.g., 'development', 'production').
13
+ * @returns A record of environment variables prefixed with `FUSION_SPA_`.
14
+ */
15
+ export function loadEnvironment(config, env, namespace = 'FUSION_SPA_') {
16
+ // resolve the root directory
17
+ const resolvedRoot = resolve(config.root || process.cwd());
18
+ // resolve the environment directory
19
+ const envDir = config.envDir ? resolve(resolvedRoot, config.envDir) : resolvedRoot;
20
+ // load environment variables from the specified directory
21
+ return loadEnv(env.mode, envDir, namespace);
22
+ }
23
+ export default loadEnvironment;
24
+ //# sourceMappingURL=load-env.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"load-env.js","sourceRoot":"","sources":["../../../src/util/load-env.ts"],"names":[],"mappings":"AAAA,OAAO,EAAkB,OAAO,EAAmB,MAAM,MAAM,CAAC;AAChE,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAEpC;;;;;;;;;;;GAWG;AACH,MAAM,UAAU,eAAe,CAC7B,MAAkB,EAClB,GAAc,EACd,SAAS,GAAG,aAAa;IAEzB,6BAA6B;IAC7B,MAAM,YAAY,GAAG,OAAO,CAAC,MAAM,CAAC,IAAI,IAAI,OAAO,CAAC,GAAG,EAAE,CAAC,CAAC;IAC3D,oCAAoC;IACpC,MAAM,MAAM,GAAG,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,OAAO,CAAC,YAAY,EAAE,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,YAAY,CAAC;IACnF,0DAA0D;IAC1D,OAAO,OAAO,CAAC,GAAG,CAAC,IAAI,EAAE,MAAM,EAAE,SAAS,CAAC,CAAC;AAC9C,CAAC;AAED,eAAe,eAAe,CAAC"}
@@ -0,0 +1,46 @@
1
+ /**
2
+ * Converts a nested object into a flat object with keys in snake_case and uppercase.
3
+ * Nested keys are prefixed with their parent keys, separated by underscores.
4
+ * Non-object values are stringified.
5
+ *
6
+ * @param obj - The input object to be flattened and converted.
7
+ * @param prefix - An optional prefix to prepend to the keys (default is an empty string).
8
+ * @returns A flat object with snake_case, uppercase keys and stringified values.
9
+ *
10
+ * @example
11
+ * ```typescript
12
+ * const input = {
13
+ * someKey: "value",
14
+ * nestedObject: {
15
+ * anotherKey: 42,
16
+ * deepNested: {
17
+ * finalKey: true
18
+ * }
19
+ * }
20
+ * };
21
+ *
22
+ * const result = objectToEnv(input);
23
+ * console.log(result);
24
+ * // Output:
25
+ * // {
26
+ * // SOME_KEY: "\"value\"",
27
+ * // NESTED_OBJECT_ANOTHER_KEY: "42",
28
+ * // NESTED_OBJECT_DEEP_NESTED_FINAL_KEY: "true"
29
+ * // }
30
+ * ```
31
+ */
32
+ export function objectToEnv(obj, prefix = 'FUSION_SPA') {
33
+ return Object.entries(obj).reduce((result, [key, value]) => {
34
+ // Convert camelCase to snake_case and uppercase
35
+ const snakeKey = key.replace(/([A-Z])/g, '_$1').toUpperCase();
36
+ const newPrefix = prefix ? `${prefix.replace(/_$/, '')}_${snakeKey}` : snakeKey;
37
+ if (value && typeof value === 'object' && !Array.isArray(value)) {
38
+ // Recursively flatten nested objects
39
+ return Object.assign(result, objectToEnv(value, newPrefix));
40
+ }
41
+ // Stringify non-object values
42
+ return Object.assign(result, { [newPrefix]: JSON.stringify(value) });
43
+ }, {});
44
+ }
45
+ export default objectToEnv;
46
+ //# sourceMappingURL=object-to-env.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"object-to-env.js","sourceRoot":"","sources":["../../../src/util/object-to-env.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AACH,MAAM,UAAU,WAAW,CAAC,GAAW,EAAE,MAAM,GAAG,YAAY;IAC5D,OAAO,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,MAAM,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,KAAK,CAAC,EAAE,EAAE;QACzD,gDAAgD;QAChD,MAAM,QAAQ,GAAG,GAAG,CAAC,OAAO,CAAC,UAAU,EAAE,KAAK,CAAC,CAAC,WAAW,EAAE,CAAC;QAE9D,MAAM,SAAS,GAAG,MAAM,CAAC,CAAC,CAAC,GAAG,MAAM,CAAC,OAAO,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,QAAQ,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC;QAEhF,IAAI,KAAK,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;YAChE,qCAAqC;YACrC,OAAO,MAAM,CAAC,MAAM,CAAC,MAAM,EAAE,WAAW,CAAC,KAAK,EAAE,SAAS,CAAC,CAAC,CAAC;QAC9D,CAAC;QACD,8BAA8B;QAC9B,OAAO,MAAM,CAAC,MAAM,CAAC,MAAM,EAAE,EAAE,CAAC,SAAS,CAAC,EAAE,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;IACvE,CAAC,EAAE,EAAE,CAAC,CAAC;AACT,CAAC;AAED,eAAe,WAAW,CAAC"}
@@ -0,0 +1,3 @@
1
+ // Generated by genversion.
2
+ export const version = '1.0.0-next.1';
3
+ //# sourceMappingURL=version.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"version.js","sourceRoot":"","sources":["../../src/version.ts"],"names":[],"mappings":"AAAA,2BAA2B;AAC3B,MAAM,CAAC,MAAM,OAAO,GAAG,cAAc,CAAC"}