@equinor/fusion-framework-cli 15.2.11 → 15.3.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.
Files changed (34) hide show
  1. package/CHANGELOG.md +44 -0
  2. package/README.md +9 -0
  3. package/bin/build/bin.mjs +1 -1
  4. package/bin/build/cli.mjs +3 -3
  5. package/dist/esm/lib/utils/assert-object.js +2 -1
  6. package/dist/esm/lib/utils/assert-object.js.map +1 -1
  7. package/dist/esm/version.js +1 -1
  8. package/dist/types/bin/serve-application.d.ts +4 -0
  9. package/dist/types/bin/start-app-dev-server.d.ts +6 -1
  10. package/dist/types/bin/utils/__tests__/fixtures/dev-mocks/foo.mock.d.ts +6 -0
  11. package/dist/types/bin/utils/__tests__/fixtures/dev-mocks/service.mock.d.ts +6 -0
  12. package/dist/types/bin/utils/apply-dev-server-mocks.d.ts +12 -0
  13. package/dist/types/bin/utils/apply-dev-server-mocks.test.d.ts +1 -0
  14. package/dist/types/bin/utils/create-dev-server-config.d.ts +3 -1
  15. package/dist/types/bin/utils/create-dev-server-config.test.d.ts +1 -0
  16. package/dist/types/bin/utils/discover-dev-server-mocks.d.ts +20 -0
  17. package/dist/types/bin/utils/discover-dev-server-mocks.test.d.ts +1 -0
  18. package/dist/types/bin/utils/normalize-dev-server-config.d.ts +2 -0
  19. package/dist/types/cli/commands/app/dev.command.d.ts +4 -1
  20. package/dist/types/cli/commands/app/dev.command.test.d.ts +1 -0
  21. package/dist/types/cli/commands/app/serve.command.d.ts +1 -0
  22. package/dist/types/cli/commands/index.d.ts +5 -3
  23. package/dist/types/cli/commands/portal/dev.command.d.ts +1 -1
  24. package/dist/types/cli/commands/register-optional-mock-server-command.d.ts +13 -0
  25. package/dist/types/cli/commands/register-optional-mock-server-command.test.d.ts +1 -0
  26. package/dist/types/cli/plugins/is-module-not-found-error.d.ts +8 -0
  27. package/dist/types/cli/plugins/is-module-not-found-error.test.d.ts +1 -0
  28. package/dist/types/cli/plugins/resolve-optional-plugin.d.ts +14 -0
  29. package/dist/types/cli/plugins/resolve-optional-plugin.test.d.ts +1 -0
  30. package/dist/types/version.d.ts +1 -1
  31. package/docs/application.md +17 -1
  32. package/docs/dev-server-config.md +132 -112
  33. package/docs/dev-server.md +39 -50
  34. package/package.json +14 -15
@@ -20,6 +20,7 @@ import { assert } from './assert.js';
20
20
  */
21
21
  export function assertObject(value, message) {
22
22
  // typeof null is 'object', so this does not exclude null values
23
- assert(typeof value === 'object', message);
23
+ // normalize to `Error | undefined` to match the `node:assert` overload
24
+ assert(typeof value === 'object', typeof message === 'string' ? new Error(message) : message);
24
25
  }
25
26
  //# sourceMappingURL=assert-object.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"assert-object.js","sourceRoot":"","sources":["../../../../src/lib/utils/assert-object.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAErC;;;;;;;;;;;;;;;;;;GAkBG;AACH,MAAM,UAAU,YAAY,CAAC,KAAa,EAAE,OAAwB;IAClE,gEAAgE;IAChE,MAAM,CAAC,OAAO,KAAK,KAAK,QAAQ,EAAE,OAAO,CAAC,CAAC;AAC7C,CAAC"}
1
+ {"version":3,"file":"assert-object.js","sourceRoot":"","sources":["../../../../src/lib/utils/assert-object.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAErC;;;;;;;;;;;;;;;;;;GAkBG;AACH,MAAM,UAAU,YAAY,CAAC,KAAa,EAAE,OAAwB;IAClE,gEAAgE;IAChE,uEAAuE;IACvE,MAAM,CAAC,OAAO,KAAK,KAAK,QAAQ,EAAE,OAAO,OAAO,KAAK,QAAQ,CAAC,CAAC,CAAC,IAAI,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC;AAChG,CAAC"}
@@ -1,3 +1,3 @@
1
1
  // Generated by genversion.
2
- export const version = '15.2.11';
2
+ export const version = '15.3.0';
3
3
  //# sourceMappingURL=version.js.map
@@ -32,6 +32,10 @@ export interface ServeApplicationOptions {
32
32
  * Enable debug mode for verbose logging.
33
33
  */
34
34
  debug?: boolean;
35
+ /**
36
+ * Origin of a local mock server to use for isolated service discovery and mock authentication.
37
+ */
38
+ mock?: string;
35
39
  }
36
40
  /**
37
41
  * Serves a built application using the dev-portal.
@@ -13,7 +13,7 @@ export interface StartAppDevServerOptions {
13
13
  /**
14
14
  * Runtime environment settings for the dev server (optional).
15
15
  */
16
- env?: RuntimeEnv;
16
+ env?: Partial<RuntimeEnv>;
17
17
  /**
18
18
  * Path to the application manifest file (optional).
19
19
  */
@@ -34,6 +34,11 @@ export interface StartAppDevServerOptions {
34
34
  * Host for the development server (optional, defaults to 'localhost').
35
35
  */
36
36
  host?: string;
37
+ /**
38
+ * Origin of a local mock server (e.g. `http://localhost:4010`) to use for API service
39
+ * discovery instead of the default CI environment (optional).
40
+ */
41
+ mock?: string;
37
42
  }
38
43
  /**
39
44
  * Starts the application development server for local development.
@@ -0,0 +1,6 @@
1
+ declare const _default: {
2
+ key: string;
3
+ serviceDiscovery: string;
4
+ document: {};
5
+ };
6
+ export default _default;
@@ -0,0 +1,6 @@
1
+ declare const _default: {
2
+ key: string;
3
+ serviceDiscovery: boolean;
4
+ document: {};
5
+ };
6
+ export default _default;
@@ -0,0 +1,12 @@
1
+ import { type DevServerOptions } from '@equinor/fusion-framework-dev-server';
2
+ import type { DevServerMockService } from './discover-dev-server-mocks.js';
3
+ /**
4
+ * Adds discovered local mock services to a dev-server configuration.
5
+ *
6
+ * @param config - Loaded dev-server configuration, including any user-defined processor and routes.
7
+ * @param mocks - Local services derived from visible `defineService` modules.
8
+ * @returns A configuration where local services replace real entries by key.
9
+ * @throws {Error} When a mock marked as new collides with upstream service discovery.
10
+ */
11
+ export declare function applyDevServerMocks(config: DevServerOptions, mocks: DevServerMockService[]): DevServerOptions;
12
+ export default applyDevServerMocks;
@@ -35,6 +35,8 @@ export type CreateDevServerOptions = {
35
35
  manifest: AppManifest;
36
36
  config?: ApiAppConfig;
37
37
  };
38
+ /** Origin of a local mock server (e.g. `http://localhost:4010`) to use for service discovery. */
39
+ mock?: string;
38
40
  };
39
41
  /**
40
42
  * Creates the full dev server configuration for Fusion CLI.
@@ -43,5 +45,5 @@ export type CreateDevServerOptions = {
43
45
  * @param options - Options for dev server config, including app/portal manifests and overrides.
44
46
  * @returns The complete dev server options object.
45
47
  */
46
- export declare const createDevServerConfig: (options: CreateDevServerOptions) => DevServerOptions;
48
+ export declare const createDevServerConfig: (options: CreateDevServerOptions) => DevServerOptions<Partial<FusionTemplateEnv>>;
47
49
  export {};
@@ -0,0 +1,20 @@
1
+ import type { FusionService } from '@equinor/fusion-framework-dev-server';
2
+ interface LocalMockService {
3
+ key: string;
4
+ serviceDiscovery: false | 'merge' | 'new' | 'replace';
5
+ }
6
+ /** Discovery-visible local mock service used by normal app development. */
7
+ export interface DevServerMockService extends FusionService {
8
+ /** Controls collision behavior when overlaying upstream service discovery. */
9
+ serviceDiscovery: Exclude<LocalMockService['serviceDiscovery'], false>;
10
+ }
11
+ /**
12
+ * Discovers visible `defineService` modules for normal-development proxying.
13
+ *
14
+ * @param directory - Directory scanned non-recursively for dev-server mock definitions.
15
+ * @param port - Manually started standalone mock-server port.
16
+ * @returns Local service-discovery entries in deterministic filename order.
17
+ * @throws {Error} When the directory cannot be read or a discovered module has an invalid export.
18
+ */
19
+ export declare function discoverDevServerMocks(directory: string, port: number): Promise<DevServerMockService[]>;
20
+ export default discoverDevServerMocks;
@@ -17,6 +17,8 @@ export declare const normalizeDevServerConfig: (config: DevServerOptions) => {
17
17
  clientId: string;
18
18
  redirectUri: string;
19
19
  requiresAuth: string;
20
+ mock?: string | boolean;
21
+ mockToken?: string;
20
22
  } | undefined;
21
23
  proxy: import("@equinor/fusion-framework-vite-plugin-spa").ResourceConfiguration[] | undefined;
22
24
  serviceDiscoveryUrl: string;
@@ -19,6 +19,7 @@
19
19
  * --env <environment> Runtime environment for the dev server (default: local)
20
20
  * --port <port> Port for the development server (default: 3000)
21
21
  * --host <host> Host for the development server (default: localhost)
22
+ * --mock [endpoint] Point API service discovery at a local mock server (default: http://localhost:4010)
22
23
  *
23
24
  * Configuration:
24
25
  * dev-server.config.ts Optional configuration file for API mocking, service discovery,
@@ -29,9 +30,11 @@
29
30
  * $ ffc app dev --port 4000
30
31
  * $ ffc app dev --manifest ./app.manifest.local.ts --config ./app.config.ts
31
32
  * $ ffc app dev --host 0.0.0.0
33
+ * $ ffc app dev --mock
34
+ * $ ffc app dev --mock http://localhost:5010
32
35
  *
33
36
  * @see startAppDevServer for implementation details
34
- * @see dev-server-config.md for configuration options
37
+ * @see {@link https://equinor.github.io/fusion-framework/cli/docs/dev-server/configuration.html | Dev server configuration guide}
35
38
  */
36
39
  export declare const command: import("node_modules/commander/typings/index.js").Command;
37
40
  export default command;
@@ -0,0 +1 @@
1
+ export {};
@@ -13,6 +13,7 @@
13
13
  * $ ffc app serve
14
14
  * $ ffc app serve --port 5000
15
15
  * $ ffc app serve --dir ./dist --host 0.0.0.0
16
+ * $ ffc app serve --mock
16
17
  *
17
18
  * Options:
18
19
  * --port <port> Port for the preview server (default: 4173)
@@ -3,9 +3,11 @@ export default _default;
3
3
  /**
4
4
  * Registers all built-in CLI commands and optional plugins on the Commander program.
5
5
  *
6
- * Built-in command groups: `app`, `auth`, `create`, `disco`, `portal`.
7
- * After registering built-in commands, loads any plugins declared in the
8
- * project's `fusion-cli.config` file.
6
+ * Built-in command groups: `app`, `auth`, `create`, `disco`, `portal`, `mock-server`.
7
+ * Configured plugins load before the optional `mock-server` fallback so an explicit
8
+ * `mockServerPlugin()` registration can retain its app-specific defaults. When no configured
9
+ * plugin owns that command, it delegates to the installed plugin package or registers an
10
+ * install-hint stub.
9
11
  *
10
12
  * @param program - The Commander program instance to register commands on.
11
13
  */
@@ -27,7 +27,7 @@
27
27
  * $ ffc portal dev --debug
28
28
  *
29
29
  * @see startPortalDevServer for implementation details
30
- * @see dev-server-config.md for configuration options
30
+ * @see {@link https://equinor.github.io/fusion-framework/cli/docs/dev-server/configuration.html | Dev server configuration guide}
31
31
  */
32
32
  export declare const command: import("node_modules/commander/typings/index.js").Command;
33
33
  export default command;
@@ -0,0 +1,13 @@
1
+ import type { Command } from 'commander';
2
+ /**
3
+ * Registers `ffc mock-server` without requiring it to be declared in `fusion-cli.config.ts`.
4
+ *
5
+ * Delegates to `@equinor/fusion-framework-cli-plugin-mock-server` when it's installed;
6
+ * otherwise registers a stub command that tells the user how to install it. Either way,
7
+ * `ffc mock-server --help` (and the top-level command list) show the command, whether or
8
+ * not the optional plugin package is present.
9
+ *
10
+ * @param program - The Commander program instance to register the command on.
11
+ * @param packageName - Overridable for testing; defaults to the real plugin package name.
12
+ */
13
+ export declare function registerOptionalMockServerCommand(program: Command, packageName?: string): Promise<void>;
@@ -0,0 +1,8 @@
1
+ /**
2
+ * True if `error` is Node's "module/package not found" error, for either CJS or ESM resolution.
3
+ *
4
+ * @param error - The value caught from a failed `import()` or `require()`.
5
+ * @param moduleName - Optional package name that must be identified as missing by the error.
6
+ * @returns Whether `error` represents the requested missing module/package.
7
+ */
8
+ export declare function isModuleNotFoundError(error: unknown, moduleName?: string): boolean;
@@ -0,0 +1,14 @@
1
+ import type { Command } from 'commander';
2
+ /**
3
+ * Dynamically imports an optional CLI plugin package's default export factory.
4
+ *
5
+ * Used to wire a specific, known plugin package into a built-in command without declaring
6
+ * it as a hard dependency of `@equinor/fusion-framework-cli` — the caller decides what to do
7
+ * (e.g. register a stub) when the package isn't installed.
8
+ *
9
+ * @param packageName - The npm package name to dynamically import.
10
+ * @returns The package's default export, called with no arguments; `null` if the package
11
+ * isn't installed.
12
+ * @throws The original error, for any failure other than the package not being found.
13
+ */
14
+ export declare function resolveOptionalPlugin(packageName: string): Promise<((program: Command) => void) | null>;
@@ -1 +1 @@
1
- export declare const version = "15.2.11";
1
+ export declare const version = "15.3.0";
@@ -215,6 +215,13 @@ export default defineAppConfig((env, args) => {
215
215
  > [!IMPORTANT]
216
216
  > Endpoints defined here are **automatically registered as named HTTP clients** when the application initializes. You do not need to call `configureHttpClient(name, ...)` in `config.ts` for these endpoints — they are ready to use via `framework.modules.http.createClient('my-end-point')`. See the [HTTP client configuration docs](../../modules/http/docs/client-configuration.md) for details.
217
217
 
218
+ > [!TIP]
219
+ > To mock an app-owned `endpoints` client locally, use a `serviceDiscovery: false` executable
220
+ > service and point `app.config.local.ts` at `http://<key>.localhost:4010`. See
221
+ > [Mock an application-config endpoint locally](../../app/docs/http-clients.md#mock-an-application-config-endpoint-locally).
222
+ > This direct workflow needs no `/@fusion-api` route or handwritten mock middleware. Restart
223
+ > `ffc app dev` after changing `app.config.<env>.ts`.
224
+
218
225
  > [!TIP]
219
226
  > In `endpoints` you can also override Service Discovery urls. This might be useful when you are
220
227
  > testing in a PR-environment.
@@ -414,7 +421,6 @@ Start your application in development mode with hot reloading and environment-sp
414
421
  | `--debug` | Enable debug mode for verbose logging. | `false` |
415
422
  | `--manifest <path>` | Path to the app manifest file (`app.manifest[.env]?.[ts,js,json]`). | `app.manifest.ts` |
416
423
  | `--config <path>` | Path to the app config file (`app.config[.env]?.[ts,js,json]`). | `app.config.ts` |
417
- | `--env <env>` | Runtime environment for the dev server. | `local` |
418
424
  | `--port <port>` | Port for the development server. | `3000` |
419
425
 
420
426
  **Usage:**
@@ -446,6 +452,7 @@ The `serve` command serves your built application through the dev-portal, simila
446
452
  | `--dir <directory>` | Directory to serve (default: detected from build config). | |
447
453
  | `--manifest <path>` | Path to the app manifest file (`app.manifest[.env]?.[ts,js,json]`). | `app.manifest.ts` |
448
454
  | `--config <path>` | Path to the app config file (`app.config[.env]?.[ts,js,json]`). | `app.config.ts` |
455
+ | `--mock [endpoint]` | Use isolated service discovery and mock authentication from a manually started mock server. | `http://localhost:4010` |
449
456
  | `-d`, `--debug` | Enable debug mode for verbose logging. | `false` |
450
457
 
451
458
  **Usage:**
@@ -459,6 +466,7 @@ pnpm fusion-framework-cli app serve
459
466
  pnpm fusion-framework-cli app serve --port 5000
460
467
  pnpm fusion-framework-cli app serve --dir ./dist --host 0.0.0.0
461
468
  pnpm fusion-framework-cli app serve --manifest app.manifest.prod.ts --config app.config.prod.ts
469
+ ffc app serve --mock
462
470
  ```
463
471
 
464
472
  > [!IMPORTANT]
@@ -468,6 +476,14 @@ pnpm fusion-framework-cli app serve --manifest app.manifest.prod.ts --config app
468
476
  >
469
477
  > **Preview Environment**: The serve command uses production mode and marks the environment as preview, so the manifest uses the compiled entry point from your build output.
470
478
 
479
+ > [!TIP]
480
+ > Start `ffc mock-server` in another foreground terminal before using `--mock`. The `serve`
481
+ > command does not own or start that process. Both `app dev` and `app serve` resolve the `local`
482
+ > environment; pass explicit `--manifest` or `--config` paths when another file is needed. Mock
483
+ > authentication signs in as `Test User` by default; see
484
+ > [Generate a mock user and update `.env`](../../vite-plugins/spa/README.md#generate-a-mock-user-and-update-env)
485
+ > to customize identity claims and token scopes.
486
+
471
487
  ### Publish
472
488
 
473
489
  Publish your application to the Fusion app store (registry) for deployment.
@@ -1,4 +1,15 @@
1
- The dev-server supports optional configuration through a `dev-server.config.ts` file in your project root. This allows you to customize how your application interacts with the Fusion Framework during development.
1
+ # Configure `ffc app dev`
2
+
3
+ The dev server supports optional configuration through a `dev-server.config.ts` file in your
4
+ project root. Use it for SPA environment values, service-discovery endpoints, logging, and shared
5
+ mock-server defaults.
6
+
7
+ > [!IMPORTANT]
8
+ > Define mocked service behavior in `mocks/<service>.mock.ts` with `defineService`. Do not handwrite
9
+ > service mocks as `api.routes` or inject them with `api.processServices`; those are low-level
10
+ > extension points for server-owned behavior and discovery transformations. Follow
11
+ > [Migrate existing dev-server configuration](../../dev-server/docs/mocking.md#migrate-existing-dev-server-configuration)
12
+ > to move existing mocks without discarding unrelated host configuration.
2
13
 
3
14
  > [!NOTE]
4
15
  > Basic server options like `port`, `host`, and `open` are configured via CLI flags or Vite configuration, not through `dev-server.config.ts`.
@@ -7,24 +18,24 @@ The dev-server supports optional configuration through a `dev-server.config.ts`
7
18
 
8
19
  The default dev-server configuration works for most applications, but you may want to customize it when:
9
20
 
10
- - **Testing API integrations**: Mock services or override API responses during development
21
+ - **Configuring mock discovery**: Set shared mock directory, host, port, or seed defaults
11
22
  - **Debugging service discovery**: Filter or modify discovered services for testing
12
23
  - **Customizing the development environment**: Adjust template variables, CLI logging, or browser console logging
13
24
  - **Isolating development scenarios**: Configure different behaviors for different development stages
14
25
 
15
26
  ## Getting Started
16
27
 
17
- Create a `dev-server.config.ts` file in your project root. Start simple with object configuration:
28
+ Most applications need no `dev-server.config.ts`. To mock a backend, start with the
29
+ [mock-service guide](../../dev-server/docs/mocking.md) and create `mocks/<service>.mock.ts`. Existing
30
+ applications can follow the guide's
31
+ [migration steps](../../dev-server/docs/mocking.md#migrate-existing-dev-server-configuration).
32
+
33
+ Create `dev-server.config.ts` only when the application needs shared server settings. Start with
34
+ object configuration:
18
35
 
19
36
  ```typescript
20
- // Simple object configuration
21
37
  export default {
22
- api: {
23
- routes: [{
24
- match: '/my-api/test',
25
- middleware: (req, res) => res.end('OK')
26
- }]
27
- }
38
+ log: { level: 4 },
28
39
  };
29
40
  ```
30
41
 
@@ -35,15 +46,10 @@ import { defineDevServerConfig } from '@equinor/fusion-framework-cli';
35
46
 
36
47
  export default defineDevServerConfig(({ base }) => {
37
48
  // Access to base config and environment for advanced logic
38
- const isLocalDev = process.env.USER === 'your-username'; // Example condition
49
+ const isDebug = process.env.DEBUG === 'true';
39
50
 
40
51
  return {
41
- api: {
42
- routes: [
43
- // Different routes based on conditions
44
- isLocalDev && { match: '/api/local-dev', middleware: localHandler }
45
- ].filter(Boolean) // Remove falsy values
46
- }
52
+ log: { level: isDebug ? 4 : base.log?.level },
47
53
  };
48
54
  });
49
55
  ```
@@ -58,22 +64,9 @@ For full TypeScript support and intellisense, import the configuration types:
58
64
  ```typescript
59
65
  import { defineDevServerConfig, type DevServerConfig } from '@equinor/fusion-framework-cli';
60
66
 
61
- // Fully typed configuration
62
67
  export default defineDevServerConfig(({ base }): DevServerConfig => ({
63
68
  ...base,
64
- api: {
65
- ...base.api,
66
- routes: [
67
- {
68
- match: '/api/users',
69
- middleware: (req, res) => {
70
- // req and res are properly typed
71
- res.setHeader('Content-Type', 'application/json');
72
- res.end(JSON.stringify([]));
73
- }
74
- }
75
- ]
76
- }
69
+ log: { level: 4 },
77
70
  }));
78
71
  ```
79
72
 
@@ -88,8 +81,9 @@ The dev-server configuration supports these main areas:
88
81
 
89
82
  | Area | Purpose | Common Use Cases |
90
83
  |------|---------|------------------|
91
- | `api.routes` | Mock API endpoints | Testing UI without backend, error scenarios |
92
- | `api.processServices` | Modify service discovery | Add mock services, override endpoints |
84
+ | `mockServer` | Shared mock-server defaults | Mock directory, host, port, deterministic seed |
85
+ | `api.routes` | Add server-owned endpoints | Health checks or infrastructure callbacks |
86
+ | `api.processServices` | Modify processed discovery | Advanced filtering or URI transformation |
93
87
  | `api.serviceDiscoveryUrl` | Change discovery endpoint | Custom/dev environments |
94
88
  | `spa.templateEnv` | Override Fusion config | Portal settings, MSAL config, telemetry |
95
89
  | `log` | Control CLI logging verbosity | Debug dev-server issues, reduce terminal noise |
@@ -103,13 +97,6 @@ Just export the properties you want to override:
103
97
 
104
98
  ```typescript
105
99
  export default {
106
- // Only override what you need to change
107
- api: {
108
- routes: [{
109
- match: '/api/users',
110
- middleware: (req, res) => res.end(JSON.stringify([]))
111
- }]
112
- },
113
100
  spa: {
114
101
  templateEnv: {
115
102
  telemetry: { consoleLevel: 0 } // Only override telemetry
@@ -127,12 +114,7 @@ Use functions when you need conditional logic or access to runtime values:
127
114
  export default defineDevServerConfig(({ base }) => {
128
115
  // You have access to base config and runtime environment
129
116
  return {
130
- api: {
131
- routes: [
132
- // Your routes automatically merge with any existing ones
133
- { match: '/api/test', middleware: testHandler }
134
- ]
135
- }
117
+ log: { level: process.env.DEBUG === 'true' ? 4 : base.log?.level },
136
118
  };
137
119
  });
138
120
  ```
@@ -163,85 +145,51 @@ routes: [{ match: '/api/users', middleware: yourHandler }]
163
145
  ### I Need To...
164
146
  | I want to... | Configuration | Example |
165
147
  |--------------|---------------|---------|
166
- | Mock an API endpoint | `api.routes` | `routes: [{ match: '/api/users', middleware: (req, res) => res.end('[]') }]` |
167
- | Add a mock service | `api.processServices` | Add services to the service discovery response |
148
+ | Mock a service | `mocks/<service>.mock.ts` | Use `defineService` and run `ffc mock-server` |
149
+ | Configure mock defaults | `mockServer` | Set `path`, `host`, `port`, or `seed` |
150
+ | Transform real discovery | `api.processServices` | Advanced: filter or rewrite processed services |
168
151
  | Override MSAL config | `spa.templateEnv.msal` | `msal: { clientId: 'dev-client-id' }` |
169
152
  | Change telemetry logging | `spa.templateEnv.telemetry` | `telemetry: { consoleLevel: 0 }` |
170
153
  | Reduce CLI noise | `log.level` | `log: { level: 2 }` |
171
154
 
172
155
  ## Essential Configurations
173
156
 
174
- ### API Mocking
157
+ ### Mock services with executable modules
175
158
 
176
- **When you need it**: Your application depends on backend services that aren't available during development, or you want to test specific API responses without hitting real services.
159
+ When a backend is unavailable or needs deterministic responses, install the optional mock-server
160
+ plugin and create one executable module per service:
177
161
 
178
- **How it works**: Add custom routes that intercept API calls and return mock data.
162
+ ```sh
163
+ pnpm add -D @equinor/fusion-framework-cli-plugin-mock-server
164
+ ```
179
165
 
180
166
  ```typescript
181
- export default defineDevServerConfig(() => ({
182
- api: {
183
- routes: [
184
- {
185
- match: '/api/users',
186
- middleware: (req, res) => {
187
- res.setHeader('Content-Type', 'application/json');
188
- res.end(JSON.stringify([
189
- { id: 1, name: 'John Doe', role: 'developer' },
190
- { id: 2, name: 'Jane Smith', role: 'designer' }
191
- ]));
192
- }
193
- },
194
- // Mock error responses
195
- {
196
- match: '/api/users/404',
197
- middleware: (req, res) => {
198
- res.statusCode = 404;
199
- res.end(JSON.stringify({ error: 'User not found' }));
200
- }
201
- }
202
- ]
203
- }
204
- }));
167
+ // mocks/inventory.mock.ts
168
+ import schema from './inventory.openapi.json' with { type: 'json' };
169
+ import { defineService } from '@equinor/fusion-openapi-mock-server/discovery';
170
+
171
+ export default defineService({
172
+ key: 'inventory',
173
+ serviceDiscovery: 'new',
174
+ schema,
175
+ components: {
176
+ InventoryItem: { name: () => 'Local item' },
177
+ },
178
+ });
205
179
  ```
206
180
 
207
- **Benefits**: Develop UI components and user flows without backend dependencies. Test error handling scenarios easily.
208
-
209
- ### Service Discovery Customization
181
+ Run the standalone mock server in one terminal and the app in another:
210
182
 
211
- **When you need it**: You're developing against a service that doesn't exist yet in the remote service discovery, or you want to override service endpoints for local development.
212
-
213
- **How it works**: Add mock services to the service discovery response that your application can use during development.
214
-
215
- ```typescript
216
- export default defineDevServerConfig(() => ({
217
- api: {
218
- processServices: (dataResponse) => {
219
- const { data, routes } = dataResponse;
220
-
221
- // Add mock services for development
222
- const mockServices = [
223
- {
224
- key: 'my-new-service',
225
- name: 'My New Service (Mock)',
226
- uri: '/api/mock-service' // This will be proxied by the dev server
227
- },
228
- {
229
- key: 'beta-feature-api',
230
- name: 'Beta Feature API (Mock)',
231
- uri: 'https://beta-api.example.com'
232
- }
233
- ];
234
-
235
- return {
236
- data: [...data, ...mockServices],
237
- routes
238
- };
239
- }
240
- }
241
- }));
183
+ ```sh
184
+ ffc mock-server
185
+ ffc app dev
242
186
  ```
243
187
 
244
- **Benefits**: Develop against planned services before they're deployed. Test integration scenarios with mock endpoints.
188
+ Normal development keeps real discovery and overlays discovery-visible local modules by key. Use
189
+ `ffc app dev --mock` when the app should resolve only bundled presets and local mock modules.
190
+
191
+ See [Develop with mock services](../../dev-server/docs/mocking.md) for discovery modes and complete
192
+ examples.
245
193
 
246
194
  ### Template Environment Variables
247
195
 
@@ -391,8 +339,10 @@ export default {
391
339
  - Check for TypeScript errors in config file
392
340
 
393
341
  ### Services Not Appearing
394
- - Ensure `processServices` returns `{ data: Service[], routes: Route[] }`
395
- - Verify you're not accidentally filtering out needed services
342
+ - Confirm the mock server is running at `http://localhost:4010`.
343
+ - Confirm the file matches `mocks/<service>.mock.ts` and default-exports `defineService(...)`.
344
+ - Confirm `serviceDiscovery` is not `false` when the framework must resolve the service by key.
345
+ - In `--mock` mode, include every required service through a bundled preset or local module.
396
346
 
397
347
  ### Template Variables Not Available
398
348
  - Variables are injected as `import.meta.env.FUSION_SPA_*`
@@ -415,3 +365,73 @@ export default {
415
365
  > [!WARNING]
416
366
  > Only use when working with non-standard environments. The default Fusion service discovery endpoint is usually correct.
417
367
 
368
+ ### Transform service discovery and add custom routes
369
+
370
+ Use `api.processServices` when the dev server must filter or transform the processed service
371
+ discovery response. Call the default `processServices` helper first to preserve local URI rewriting
372
+ and generated proxy routes. Use `api.routes` for server-owned endpoints that do not represent a
373
+ mocked service:
374
+
375
+ ```typescript
376
+ import { defineDevServerConfig } from '@equinor/fusion-framework-cli';
377
+ import { processServices } from '@equinor/fusion-framework-dev-server';
378
+
379
+ export default defineDevServerConfig(() => ({
380
+ api: {
381
+ processServices: (data, args) => {
382
+ const processed = processServices(data, args);
383
+ return {
384
+ ...processed,
385
+ data: processed.data.filter((service) => service.key !== 'deprecated-service'),
386
+ };
387
+ },
388
+ routes: [
389
+ {
390
+ match: '/health',
391
+ middleware: (_request, response) => {
392
+ response.setHeader('content-type', 'application/json');
393
+ response.end(JSON.stringify({ status: 'ready' }));
394
+ },
395
+ },
396
+ ],
397
+ },
398
+ }));
399
+ ```
400
+
401
+ Defining `api.processServices` replaces the default processing entry point. Omit the helper call
402
+ only when the host intentionally owns all service URI rewriting and proxy-route generation. Route
403
+ entries are merged by `match`, and a route from `dev-server.config.ts` replaces a base route with
404
+ the same path.
405
+
406
+ These extension points remain available for custom hosts and infrastructure. Define application
407
+ service behavior in `mocks/<service>.mock.ts` so discovery metadata, OpenAPI operations, and mock
408
+ responses stay in one executable module.
409
+
410
+ ### Local mock server
411
+
412
+ The recommended workflow needs no `api.routes` or `api.processServices` configuration. Start the
413
+ foreground mock server, then choose normal overlay or isolated development:
414
+
415
+ ```sh
416
+ ffc mock-server
417
+ ffc app dev # real discovery plus visible local modules
418
+ ffc app dev --mock # bundled presets plus local modules only
419
+ ```
420
+
421
+ `--mock` sets `api.serviceDiscoveryUrl` to `<endpoint>/@fusion-mock/discovery`. A
422
+ `dev-server.config.ts` override for `api.serviceDiscoveryUrl` still takes precedence over it.
423
+ The mode also enables mock authentication with `Test User`; see
424
+ [Generate a mock user and update `.env`](../../vite-plugins/spa/README.md#generate-a-mock-user-and-update-env)
425
+ to set identity claims and space-separated `scp` token scopes.
426
+
427
+ Use `dev-server.config.ts` only to share mock-server defaults such as a nonstandard path or port:
428
+
429
+ ```typescript
430
+ import type {} from '@equinor/fusion-framework-cli-plugin-mock-server';
431
+ import { defineDevServerConfig } from '@equinor/fusion-framework-cli';
432
+
433
+ export default defineDevServerConfig(() => ({
434
+ mockServer: { path: 'mocks', port: 4010, seed: 42 },
435
+ }));
436
+ ```
437
+