@equinor/fusion-framework-vite-plugin-spa 1.0.0-next.9 → 1.0.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 CHANGED
@@ -1,77 +1,253 @@
1
+ ---
2
+ title: Fusion Framework Vite SPA Plugin
3
+ description: >
4
+ A powerful Vite plugin for building Single Page Applications (SPAs) with the Fusion Framework. It automates HTML template generation, bootstraps authentication and service discovery, and streamlines portal loading and API proxying. Designed for seamless integration with the Fusion Framework CLI, this plugin provides flexible configuration for both standard and advanced SPA scenarios.
5
+ tags:
6
+ - fusion-framework
7
+ - cli
8
+ - app-development
9
+ - portal-development
10
+ - dev-server
11
+ - authentication
12
+ - configuration
13
+ - service-discovery
14
+ - equinor
15
+ - non-production
16
+ - documentation
17
+ keywords:
18
+ - fusion-framework
19
+ - vite
20
+ - spa
21
+ - plugin
22
+ - development
23
+ - non-production
24
+ ---
25
+
1
26
  # Fusion Framework Vite SPA Plugin
2
27
 
3
- This plugin is used to build a single page application (SPA) with Vite.
28
+ [![npm version](https://img.shields.io/npm/v/@equinor/fusion-framework-vite-plugin-spa.svg?style=flat)](https://www.npmjs.com/package/@equinor/fusion-framework-vite-plugin-spa)
29
+ [![License: ISC](https://img.shields.io/badge/License-ISC-blue.svg)](./LICENSE)
4
30
 
5
- ## Usage
31
+ A powerful Vite plugin for building Single Page Applications (SPAs) with the Fusion Framework. It automates HTML template generation, bootstraps authentication and service discovery, and streamlines portal loading and API proxying. Designed for seamless integration with the Fusion Framework CLI, this plugin provides flexible configuration for both standard and advanced SPA scenarios.
6
32
 
7
- ```ts
8
- import { defineConfig } from 'vite';
9
- import { plugin as fusionSpaPlugin } from '@equinor/fusion-framework-vite-plugin-spa';
33
+ > [!CAUTION]
34
+ > This plugin is intended for use in non-production environments only.
10
35
 
11
- export default defineConfig({
12
- plugins: [fusionSpaPlugin()],
13
- });
36
+ > [!WARNING]
37
+ > This plugin is developed for usage with [`@equinor/fusion-framework-cli`](https://github.com/equinor/fusion-framework/tree/main/packages/cli) and this documentation is intended for deeper understanding of the plugin's capabilities and configuration options.
38
+ >
39
+ > **NOTE:** _Just because there are buttons available doesn't mean you need to press them 🐒_
40
+ >
41
+ > The plugin is written in a modular fashion, allowing for easy customization and extension __IF__ the developer has a deep understanding of the Fusion Framework and its internals.
42
+
43
+ ## Table of Contents
44
+
45
+ - [Fusion Framework Vite SPA Plugin](#fusion-framework-vite-spa-plugin)
46
+ - [Table of Contents](#table-of-contents)
47
+ - [What It Does](#what-it-does)
48
+ - [How the Plugin Works](#how-the-plugin-works)
49
+ - [Getting Started](#getting-started)
50
+ - [Configuration Options](#configuration-options)
51
+ - [Basic Configuration](#basic-configuration)
52
+ - [Service Discovery](#service-discovery)
53
+ - [MSAL](#msal)
54
+ - [Service Worker](#service-worker)
55
+ - [Configuring through `.env` File](#configuring-through-env-file)
56
+ - [How Environment Variables Work](#how-environment-variables-work)
57
+ - [Naming Convention](#naming-convention)
58
+ - [Example Conversion](#example-conversion)
59
+ - [Complete `.env` Example](#complete-env-example)
60
+ - [Advanced Customization](#advanced-customization)
61
+ - [Providing a Custom Template](#providing-a-custom-template)
62
+ - [Providing Custom Bootstrap](#providing-custom-bootstrap)
63
+ - [Examples](#examples)
64
+ - [Basic SPA Configuration](#basic-spa-configuration)
65
+ - [Using with API Service Plugin](#using-with-api-service-plugin)
66
+ - [Troubleshooting \& FAQ](#troubleshooting--faq)
67
+ - [Common Issues](#common-issues)
68
+ - [Known Issues](#known-issues)
69
+ - [Best Practices \& FAQ](#best-practices--faq)
70
+ - [Contributing](#contributing)
71
+
72
+
73
+ ## What It Does
74
+
75
+ The plugin:
76
+
77
+ 1. **Bootstraps the Fusion Framework** - Initializes core modules including MSAL authentication and service discovery
78
+ 2. **Renders a Configured Portal** - Loads and renders any portal by ID, as long as it exports a render function
79
+ 3. **Registers a Service Worker** - Enables authenticated API requests by automatically injecting auth tokens
80
+ 4. **Configures Development Environment** - Works with `plugin-api-service` to intercept and proxy authenticated requests during development
81
+
82
+ > [!TIP]
83
+ > The plugin will render the configured portal which can be sourced from:
84
+ > - A local npm package (like `@equinor/fusion-framework-dev-portal`, the default used by CLI)
85
+ > - The Fusion Portal Service (using a portal identifier)
86
+ > - Any custom portal implementation configured in your environment
87
+
88
+ ## How the Plugin Works
89
+
90
+ ```mermaid
91
+ flowchart
92
+ A[Vite HTML request] -->|index.html| B[Plugin serve SPA]
93
+ B -->|bootstrap.js| C[Initialize Fusion Framework]
94
+ C --> D[Register Service Worker]
95
+ C -->|"/portals/{portalId}@${portalTag}"| E[Fetch Portal Manifest]
96
+ E -->|manifest.build.config| F[Fetch Portal Configuration]
97
+ F -->|manifest.build.entrypoint| G[Import Portal Source]
98
+ G -->|"Fusion Instance"| H[Render Portal]
99
+
100
+ style A fill:#F00,stroke:#333,stroke-width:1px
101
+ style H fill:#00F,stroke:#333,stroke-width:1px
14
102
  ```
15
103
 
16
- ## Configure the plugin
104
+ **Flow Explanation:**
105
+ 1. **Vite HTML Request**: The plugin hooks into the Vite dev-server and intercepts SPA requests.
106
+ 2. **Serve SPA**: The plugin serves the SPA by returning the `index.html` file.
107
+ 3. **Initialize Fusion Framework**: `bootstrap.js` is loaded
108
+ - configures the framework (e.g. MSAL, service discovery)
109
+ - initializes the framework
110
+ - registers service worker (connects to fusion framework)
111
+ 4. **Fetch Portal Manifest**: The framework fetches the manifest describing the portal to load.
112
+ 5. **Fetch Portal Configuration**: Additional configuration for the portal is retrieved.
113
+ 6. **Load Portal Source File**: The main entry file for the portal is loaded based on the manifest.
114
+ 7. **Render Portal**: Renders the portal with the Fusion Framework.
115
+
116
+ ## Getting Started
117
+
118
+ > [!WARNING]
119
+ > This plugin is primarily designed to be used with the [Fusion Framework CLI](https://github.com/equinor/fusion-framework-cli). The CLI scaffolds all required configuration and wiring for you.
120
+
121
+ **Standalone usage is advanced:** If you use this plugin outside the CLI, you must provide detailed configuration for authentication, service discovery, portal loading, and more. There is no "one-line" quick start for custom setups. See the [Basic Configuration](#basic-configuration) and [Configuration Options](#configuration-options) sections below.
122
+
123
+ > [!NOTE]
124
+ > This plugin should cover most use cases for building SPAs with the Fusion Framework, but advanced configurations may require additional setup.
125
+ > If for some reason you are adventurous enough to write your custom implementation of developer utilities, this guide should help you get started.
126
+
127
+ ## Configuration Options
128
+
129
+ The plugin accepts a variety of configuration options to tailor the SPA to your project's needs. These options control authentication flows, service discovery, and routing behaviors.
130
+
131
+ ### Basic Configuration
132
+
133
+ Here's a comprehensive example showing all major configuration options:
17
134
 
18
135
  ```ts
19
136
  fusionSpaPlugin({
20
- generateTemplateEnv: () => {
21
- return {
22
- title: 'My App',
23
- portal: {
24
- id: 'my-portal',
25
- },
26
- serviceDiscovery: {
27
- url: 'https://my-server.com/service-discovery',
28
- scopes: ['api://my-app/scope'],
29
- },
30
- msal: {
31
- tenantId: 'my-tenant-id',
32
- clientId: 'my-client-id',
33
- redirectUri: 'https://my-app.com/auth-callback',
34
- requiresAuth: 'true',
35
- },
36
- serviceWorker: {
37
- resources: [
38
- {
39
- url: '/app-proxy',
40
- rewrite: '/@fusion-api/app',
41
- scopes: ['xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx/.default'],
42
- ],
43
- },
44
- }
45
- }
137
+ generateTemplateEnv: () => ({
138
+ // HTML page title
139
+ title: 'My App',
140
+
141
+ // Portal configuration: specify which portal to load
142
+ portal: {
143
+ id: 'my-portal', // Portal ID to load and render
144
+ // Can be:
145
+ // 1. A package name (e.g. '@equinor/fusion-framework-dev-portal', default for CLI)
146
+ // 2. An ID from the Fusion Portal Service
147
+ // 3. Any other configured portal ID
148
+ tag: 'latest', // (Optional) Version tag (defaults to 'latest')
149
+ },
150
+
151
+ // Service Discovery configuration
152
+ serviceDiscovery: {
153
+ url: 'https://my-server.com/service-discovery',
154
+ scopes: ['api://my-app/scope'],
155
+ },
156
+
157
+ // MSAL (Microsoft Authentication Library) configuration
158
+ msal: {
159
+ tenantId: 'my-tenant-id',
160
+ clientId: 'my-client-id',
161
+ redirectUri: 'https://my-app.com/auth-callback',
162
+ requiresAuth: 'true',
163
+ },
164
+
165
+ // Service Worker configuration for API proxying and authentication
166
+ serviceWorker: {
167
+ resources: [
168
+ {
169
+ url: '/app-proxy',
170
+ rewrite: '/@fusion-api/app',
171
+ scopes: ['xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx/.default'],
172
+ },
173
+ ],
174
+ },
175
+ })
46
176
  });
47
177
  ```
178
+ See [@equinor/fusion-framework-vite-plugin-api-service](https://github.com/equinor/fusion-framework/tree/main/packages/vite-plugins/api-service) for advanced API proxying.
48
179
 
49
180
  ### Service Discovery
50
181
 
51
- The service discovery URL is used to fetch the service discovery configuration from the server.
52
- The service discovery scopes are used to authenticate the service discovery request.
182
+ The Service Discovery component provides a dynamic way to discover and connect to backend services. It's a crucial part of the Fusion Framework architecture that helps applications locate and communicate with various microservices.
183
+
184
+ **Configuration Options:**
185
+
186
+ - `url`: The endpoint URL where the service discovery configuration can be fetched from
187
+ - `scopes`: Array of OAuth scopes required to authenticate service discovery requests
188
+
189
+ **Benefits:**
190
+ - Eliminates hardcoded service endpoints in your application
191
+ - Enables dynamic service routing based on environment
192
+ - Centralizes service endpoint management
53
193
 
54
194
  ### MSAL
55
195
 
56
- The MSAL configuration is used to authenticate the user with Azure AD.
57
- - `tenantId` is the ID of the Azure AD tenant.
58
- - `clientId` is the ID of the Azure AD application.
59
- - `redirectUri` is the URL to redirect the user to after authentication.
60
- - `requiresAuth` _(optional)_ property is used to specify if the framework requires authentication, will try to login user on initial load.
196
+ The Microsoft Authentication Library (MSAL) configuration handles user authentication with Azure Active Directory. This enables single sign-on capabilities and secure access to protected resources.
197
+
198
+ **Configuration Options:**
199
+
200
+ - `tenantId`: The Azure AD tenant ID associated with your organization
201
+ - `clientId`: The client/application ID registered in Azure AD
202
+ - `redirectUri`: The URL where users are redirected after authentication
203
+ - `requiresAuth` _(optional)_: When set to "true", the application will automatically prompt for login on initial load
61
204
 
62
205
  ### Service Worker
63
206
 
64
- The service worker configuration is used to configure routes for the service worker.
207
+ The Service Worker component provides powerful capabilities for handling network requests, adding authentication, and enabling offline capabilities in your SPA.
208
+
209
+ #### How It Works
210
+
211
+ ```mermaid
212
+ sequenceDiagram
213
+ participant App as Application
214
+ participant SW as Service Worker
215
+ participant Main as Fusion Framework
216
+ participant API as API Server
217
+
218
+ App->>SW: fetch('/app-proxy/assets/some-app/resource.json')
219
+ alt Route matches a registered resource
220
+ SW->>Main: Request auth tokens for scopes
221
+ Main-->>SW: Return tokens
222
+ SW->>SW: Rewrite URL to /@fusion-api/app/assets/some-app/resource.json
223
+ SW->>API: fetch with auth headers
224
+ API-->>SW: Response
225
+ SW-->>App: Return response
226
+ else Route does not match any resource
227
+ SW-->>App: Let request pass through (no interception)
228
+ end
229
+ ```
230
+
231
+ The Service Worker intercepts network requests made by your application and can modify them before they're sent. This is particularly useful for:
232
+
233
+ 1. Adding authentication tokens automatically to API calls
234
+ 2. Rewriting request URLs for proxying purposes
235
+ 3. Enabling offline functionality
236
+ 4. Improving performance through caching
237
+
238
+ #### Configuration Options
239
+
240
+ - `resources`: An array of resource configurations the service worker will manage
241
+ - `url`: Path pattern to match incoming requests against
242
+ - `rewrite` _(optional)_: Path to rewrite the matched URL to
243
+ - `scopes` _(optional)_: OAuth scopes to use for authenticating this resource
65
244
 
66
- When `fetch` calls are made to the `url` path, the service worker will intercept the request and apply authentication headers to the request for the specified scopes. The request url will be rewritten to the `rewrite` path, by replacing the `url` with the `rewrite` path.
245
+ #### Complete Example
67
246
 
68
- - `resources` is an array of resources which the service worker will handle.
69
- - `url` path to match requests to
70
- - `rewrite` _(optional)_ path to replace the `url` with
71
- - `scopes` _(optional)_ is an array of scopes to use for the resource.
247
+ Here's a detailed example of the Service Worker in action:
72
248
 
73
- Example:
74
249
  ```ts
250
+ // Service Worker configuration example
75
251
  const serviceWorker = {
76
252
  resources: [
77
253
  {
@@ -79,133 +255,300 @@ const serviceWorker = {
79
255
  rewrite: '/@fusion-api/app',
80
256
  scopes: [
81
257
  '2bed749c-843b-413d-8b17-e7841869730f/.default',
82
- '8c24cf81-de7a-435b-ab74-e90b1a7bda0a/.default'
258
+ '8c24cf81-de7a-435b-ab74-e90b1a7bda0a/.default',
83
259
  ],
84
260
  },
85
261
  ],
86
262
  };
87
263
 
264
+ // Example: Making a request in your application code
88
265
  fetch('/app-proxy/assets/some-app/resource-path/resource.json');
89
266
  ```
90
- 1. The request will be intercepted by the service worker.
91
- 2. The service worker will post message to the main thread for authentication.
92
- 3. The main thread will generate the authentication token for the specified scopes.
93
- 4. The service worker will rewrite the request to `/@fusion-api/app/assets/some-app/resource-path/resource.json`.
94
- 5. The service worker will execute the request to the rewritten path with the authentication token.
267
+
268
+ #### Request Processing Flow
269
+
270
+ When the above `fetch` request is made, the following happens:
271
+
272
+ 1. The Service Worker intercepts the request matching the `/app-proxy` pattern
273
+ 2. It sends a message to the main thread requesting authentication tokens for the specified scopes
274
+ 3. The main thread generates the necessary authentication tokens
275
+ 4. The Service Worker rewrites the URL from `/app-proxy/assets/some-app/resource-path/resource.json` to `/@fusion-api/app/assets/some-app/resource-path/resource.json`
276
+ 5. It adds the authentication headers to the request and executes it against the rewritten URL
277
+ 6. The response is returned to the application as if the original URL was called
95
278
 
96
279
  > [!TIP]
97
- > The `Url` does not have an endpoint, it is just a path to match the request to,
98
- > so that url can emulate a proxy service in the production environment.
99
- > The `rewrite` path is the actual endpoint to call.
280
+ > The `url` path doesn't need to correspond to an actual endpointit's simply a pattern used for matching requests. This allows you to emulate proxy services in production environments without changing your application code.
100
281
 
101
282
  > [!TIP]
102
- > Using the `@equinor/fusion-framework-vite-plugin-api-service` plugin,
103
- > you can create a dynamic proxy service that will handle the request.
104
- >
105
- > The `/@fusion-api/app` points to proxy route which can be intercepted in the dev-server.
106
- > This endpoint is generated from the service discovery configuration and will dynamically proxy the request to the service discovery endpoint.
283
+ > For enhanced development capabilities, consider using the `@equinor/fusion-framework-vite-plugin-api-service` plugin. This plugin creates a dynamic proxy service that can handle requests to the `/@fusion-api/app` path by intercepting them in the dev-server and routing them based on service discovery configuration.
284
+
285
+ ## Configuring through `.env` File
286
+
287
+ For greater flexibility—especially in CI/CD pipelines and deployment scenarios—the plugin supports configuration through environment variables in a `.env` file.
288
+
289
+ ### How Environment Variables Work
290
+
291
+ The plugin reads the `.env` file and overrides any properties defined in the `generateTemplateEnv` function with the corresponding environment variables. This approach allows you to:
107
292
 
108
- ## Configuring through `.env` file
293
+ - Maintain different configurations for development, testing, and production
294
+ - Keep sensitive information out of your source code
295
+ - Override configuration values during deployment pipelines
109
296
 
110
- The plugin can be configured through a `.env` file. The plugin will read the `.env` file and override the properties in the `generateTemplateEnv` function.
297
+ ### Naming Convention
111
298
 
112
- The property names are prefixed with `FUSION_SPA_` and snake cased.
299
+ Environment variables follow a specific naming pattern:
300
+ 1. All variables are prefixed with `FUSION_SPA_`
301
+ 2. Object paths are converted to snake case with underscores
302
+ 3. Arrays and objects are serialized as JSON strings
113
303
 
114
- example:
304
+ ### Example Conversion
305
+
306
+ Here's how JavaScript configuration objects map to environment variables:
115
307
 
116
308
  ```ts
309
+ // JavaScript configuration
117
310
  {
118
311
  serviceWorker: {
119
312
  resources: [...],
120
313
  },
121
314
  }
122
- // will be converted to
123
- FUSION_SPA_SERVICE_WORKER_RESOURCES: [...],
124
315
 
125
- // and accessible in the template as
316
+ // Becomes this environment variable
317
+ FUSION_SPA_SERVICE_WORKER_RESOURCES=[...]
318
+
319
+ // And can be accessed in your code as
126
320
  import.meta.env.FUSION_SPA_SERVICE_WORKER_RESOURCES
127
321
  ```
128
322
 
129
- > [!TIP]
130
- > Prefer to use the `generateTemplateEnv` function to provide the properties.
131
- > The `.env` file is used to override configurations, example in an CI/CD pipeline.
132
-
133
- > [!IMPORTANT]
134
- > The `.env` file should be placed in the root of the project.
135
- > Parameters from the `.env` file overrides the parameters from the `generateTemplateEnv` function.
323
+ ### Complete `.env` Example
136
324
 
137
- ```
325
+ ```sh
326
+ # Application basics
138
327
  FUSION_SPA_TITLE=My App
139
- FUSION_SPA_PORTAL_ID=my-portal
328
+ FUSION_SPA_PORTAL_ID=my-portal # Can be a package name, Fusion Portal Service ID, or any configured portal ID
329
+
330
+ # Service Discovery configuration
140
331
  FUSION_SPA_SERVICE_DISCOVERY_URL=https://my-server.com/service-discovery
141
332
  FUSION_SPA_SERVICE_DISCOVERY_SCOPES=[api://my-app/scope]
333
+
334
+ # MSAL Authentication configuration
142
335
  FUSION_SPA_MSAL_TENANT_ID=my-tenant-id
143
336
  FUSION_SPA_MSAL_CLIENT_ID=my-client-id
144
337
  FUSION_SPA_MSAL_REDIRECT_URI=https://my-app.com/auth-callback
145
338
  FUSION_SPA_MSAL_REQUIRES_AUTH=true
339
+
340
+ # Service Worker configuration (as JSON string)
146
341
  FUSION_SPA_SERVICE_WORKER_RESOURCES=[{"url":"/app-proxy","rewrite":"/@fusion-api/app","scopes":["xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx/.default"]}]
147
342
  ```
148
343
 
149
- ## Providing a custom template
344
+ > [!TIP]
345
+ > While environment variables are convenient for deployment scenarios, it's generally recommended to use the `generateTemplateEnv` function during development for better type checking and easier debugging.
346
+
347
+ > [!IMPORTANT]
348
+ > The `.env` file must be placed in the root of your project. Values defined in the `.env` file will override any corresponding values from the `generateTemplateEnv` function.
349
+
350
+ ## Advanced Customization
351
+
352
+ For advanced scenarios, the plugin provides options to customize both the HTML template and the application bootstrapping process. These customizations should be approached carefully as they may require deeper understanding of the framework.
353
+
354
+ ### Providing a Custom Template
355
+
356
+ You can provide a completely custom HTML template for your application. This gives you full control over the document structure while still leveraging the plugin's environment variable injection.
150
357
 
151
358
  > [!WARNING]
152
- > Your almost walking on the edge of the framework. Be careful.
153
- > At this point you might just define your own template and the plugin will inject the properties into the template.
359
+ > Custom templates move you closer to the edge of the framework's capabilities. Proceed with caution as you'll be responsible for ensuring proper structure and bootstrapping.
360
+
361
+ #### Example Custom Template
154
362
 
155
363
  ```ts
364
+ // Define your custom HTML template
156
365
  const template = `
157
366
  <!DOCTYPE html>
158
367
  <html lang="en">
159
- <head>
160
- <script type="module" src="./src/my-custom-bootloader"></script>
161
- </head>
162
- <body>
163
- <h1>%MY_CUSTOM_PROPERTY%</h1>
164
- <div id="app"></div>
165
- </body>
368
+ <head>
369
+ <meta charset="UTF-8">
370
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
371
+ <title>%MY_CUSTOM_TITLE%</title>
372
+ <script type="module" src="./src/my-custom-bootloader"></script>
373
+ </head>
374
+ <body>
375
+ <h1>%MY_CUSTOM_PROPERTY%</h1>
376
+ <div id="app"></div>
377
+ </body>
166
378
  </html>
167
379
  `;
168
380
 
381
+ // Configure Vite with your custom template
169
382
  import { defineConfig } from 'vite';
170
383
  import { createViteSPAPlugin } from '@equinor/fusion-framework-vite-plugin-spa';
171
384
 
385
+ // Custom prefix for environment variables
172
386
  const templateEnvPrefix = 'MY_CUSTOM_';
173
387
 
174
388
  export default defineConfig({
389
+ // Define environment variables to inject into the template
175
390
  define: {
176
- `import.meta.env.${templateEnvPrefix}PROPERTY`: 'my-custom-value',
391
+ [`import.meta.env.${templateEnvPrefix}PROPERTY`]: '"my-custom-value"',
392
+ [`import.meta.env.${templateEnvPrefix}TITLE`]: '"My Application"',
177
393
  },
178
- plugins: [createViteSPAPlugin({template, templateEnvPrefix})],
394
+ // Use the custom template with the plugin
395
+ plugins: [createViteSPAPlugin({ template, templateEnvPrefix })],
179
396
  });
180
397
  ```
181
398
 
182
399
  > [!TIP]
183
- > see [Vite documentation](https://vite.dev/guide/env-and-mode.html#html-constant-replacement) for more information about customizing the template.
400
+ > For more details on how HTML template variable replacement works in Vite, see the [Vite documentation](https://vite.dev/guide/env-and-mode.html#html-constant-replacement).
401
+
402
+ ### Providing Custom Bootstrap
184
403
 
185
- ### Providing custom bootstrap
404
+ For even more control, you can specify a custom bootstrap file that handles the initialization of your application:
186
405
 
187
406
  ```ts
188
407
  fusionSpaPlugin({
189
408
  generateTemplateEnv: () => {
190
409
  return {
410
+ // Points to your custom bootstrap file
191
411
  bootstrap: 'src/my-custom-bootloader.ts',
192
412
  }
193
413
  }
194
414
  });
195
415
  ```
196
416
 
197
- > [!WARNING]
198
- > The bootstrapping of the `ServiceWorker` is done in the the bootloader,
199
- > this functionality will no longer be available, but needs to be re-implemented in the custom bootloader.
417
+ #### Implementing Your Custom Bootloader
200
418
 
419
+ When implementing a custom bootloader, you'll need to handle all the initialization logic that would normally be managed by the default bootloader, including service worker registration:
201
420
 
202
421
  ```ts
203
422
  // custom-bootloader.ts
204
- import { registerServiceWorker} from '@equinor/fusion-framework-vite-plugin-spa/html';
205
-
423
+ import { registerServiceWorker } from '@equinor/fusion-framework-vite-plugin-spa/html';
206
424
  import initializeFramework from './my-custom-framework.js';
207
425
 
208
- registerServiceWorker(await initializeFramework());
426
+ // Initialize your framework and register the service worker
427
+ const app = await initializeFramework();
428
+ registerServiceWorker(app);
429
+
430
+ // Additional custom initialization code
431
+ document.addEventListener('DOMContentLoaded', () => {
432
+ console.log('Application fully loaded');
433
+ });
434
+ ```
435
+
436
+ > [!WARNING]
437
+ > When using a custom bootloader, the default ServiceWorker registration is bypassed. You must explicitly call `registerServiceWorker()` in your custom bootloader to maintain this functionality, as shown in the example above.
438
+
439
+
440
+ ## Examples
441
+
442
+ Here are some common implementation examples to help you get started quickly:
443
+
444
+ ### Basic SPA Configuration
445
+
446
+ ```ts
447
+ // vite.config.ts
448
+ import { defineConfig } from 'vite';
449
+ import { plugin as fusionSpaPlugin } from '@equinor/fusion-framework-vite-plugin-spa';
450
+ import react from '@vitejs/plugin-react';
451
+
452
+ export default defineConfig({
453
+ plugins: [
454
+ react(),
455
+ fusionSpaPlugin({
456
+ generateTemplateEnv: () => ({
457
+ title: 'My React App',
458
+ portal: { id: 'my-portal' },
459
+ serviceDiscovery: {
460
+ url: 'https://dev-server.com/service-discovery',
461
+ scopes: ['api://my-app/user.read'],
462
+ },
463
+ msal: {
464
+ tenantId: process.env.TENANT_ID,
465
+ clientId: process.env.CLIENT_ID,
466
+ redirectUri: 'http://localhost:3000/auth-callback',
467
+ requiresAuth: 'true',
468
+ },
469
+ }),
470
+ }),
471
+ ],
472
+ });
473
+ ```
474
+
475
+ ### Using with API Service Plugin
476
+
477
+ ```ts
478
+ // vite.config.ts
479
+ import { defineConfig } from 'vite';
480
+ import { plugin as fusionSpaPlugin } from '@equinor/fusion-framework-vite-plugin-spa';
481
+ import { plugin as apiServicePlugin } from '@equinor/fusion-framework-vite-plugin-api-service';
482
+ import react from '@vitejs/plugin-react';
483
+
484
+ export default defineConfig({
485
+ plugins: [
486
+ react(),
487
+ fusionSpaPlugin({
488
+ generateTemplateEnv: () => ({
489
+ title: 'API Service Example',
490
+ // Portal to load
491
+ portal: {
492
+ id: 'my-portal',
493
+ tag: 'latest',
494
+ },
495
+ serviceDiscovery: {
496
+ url: 'https://dev-server.com/service-discovery',
497
+ scopes: ['api://my-app/user.read'],
498
+ },
499
+ serviceWorker: {
500
+ resources: [
501
+ {
502
+ url: '/api-proxy',
503
+ rewrite: '/@fusion-api/service',
504
+ scopes: ['api://backend-service/.default'],
505
+ },
506
+ ],
507
+ },
508
+ }),
509
+ }),
510
+ // API Service plugin enables portal loading from service discovery
511
+ apiServicePlugin(),
512
+ ],
513
+ });
209
514
  ```
515
+ See [API Service Plugin docs](https://github.com/equinor/fusion-framework/tree/main/packages/vite-plugins/api-service) for more details.
516
+
517
+ ## Troubleshooting & FAQ
518
+
519
+ ### Common Issues
520
+
521
+ 1. **Authentication Failures**
522
+ - Ensure your `tenantId` and `clientId` values are correct
523
+ - Check that your application is properly registered in Azure AD
524
+ - Verify that the required scopes are configured correctly
525
+
526
+ 2. **Service Worker Not Working**
527
+ - Ensure the service worker is properly registered in your bootstrap process
528
+ - Check browser console for any registration errors
529
+ - Verify that the URL patterns in the resources configuration match your fetch calls
530
+
531
+ 3. **Environment Variables Not Applied**
532
+ - Confirm your `.env` file is in the project root
533
+ - Verify the naming convention follows `FUSION_SPA_*` with proper snake_casing
534
+ - Restart your development server after changing environment variables
535
+
536
+ ### Known Issues
537
+
538
+ | Issue | Impact | Description |
539
+ | ----- | ------ | ----------- |
540
+ | [#3266](https://github.com/equinor/fusion-framework/issues/3266) | **Missing bearer token on proxy assets** | When loading remote applications that use assets or code-splitting, the service worker may fail to attach the required Bearer token to requests for these resources. This occurs because the service worker rewrites `import.url`, which can interfere with proper token injection for asset requests. As a result, protected assets may not load correctly in some scenarios.|
541
+
542
+ ### Best Practices & FAQ
543
+
544
+ - **Keep secrets out of source code** — use environment variables for sensitive values.
545
+ - **Leverage the [API Service Plugin](https://github.com/equinor/fusion-framework/tree/main/packages/vite-plugins/api-service)** for advanced API proxying and local development.
546
+ - **When customizing templates or bootloaders,** always test service worker registration and authentication flows.
547
+ - **For local development:**
548
+ - Use [Vite's environment variable system](https://vitejs.dev/guide/env-and-mode.html)
549
+
550
+ ## Contributing
551
+
552
+ Contributions, bug reports, and feature requests are welcome! See [CONTRIBUTING.md](../../../CONTRIBUTING.md) for guidelines.
210
553
 
211
554
 
@@ -1,3 +1,3 @@
1
1
  // Generated by genversion.
2
- export const version = '1.0.0-next.9';
2
+ export const version = '1.0.0';
3
3
  //# sourceMappingURL=version.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"version.js","sourceRoot":"","sources":["../../src/version.ts"],"names":[],"mappings":"AAAA,2BAA2B;AAC3B,MAAM,CAAC,MAAM,OAAO,GAAG,cAAc,CAAC"}
1
+ {"version":3,"file":"version.js","sourceRoot":"","sources":["../../src/version.ts"],"names":[],"mappings":"AAAA,2BAA2B;AAC3B,MAAM,CAAC,MAAM,OAAO,GAAG,OAAO,CAAC"}