@equinor/fusion-framework-cli-plugin-mock-server 0.1.0 → 0.1.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.
@@ -1 +1 @@
1
- export declare const version = "0.1.0";
1
+ export declare const version = "0.1.1";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@equinor/fusion-framework-cli-plugin-mock-server",
3
- "version": "0.1.0",
3
+ "version": "0.1.1",
4
4
  "description": "Fusion Framework CLI plugin adding `ffc mock-server`, serving @equinor/fusion-openapi-mock fakes over HTTP",
5
5
  "main": "dist/esm/index.js",
6
6
  "type": "module",
@@ -27,6 +27,9 @@
27
27
  ],
28
28
  "author": "",
29
29
  "license": "ISC",
30
+ "files": [
31
+ "dist"
32
+ ],
30
33
  "publishConfig": {
31
34
  "access": "public"
32
35
  },
@@ -37,9 +40,9 @@
37
40
  },
38
41
  "dependencies": {
39
42
  "commander": "^15.0.0",
40
- "@equinor/fusion-framework-dev-server": "^2.1.0",
41
- "@equinor/fusion-imports": "^2.0.3",
42
- "@equinor/fusion-openapi-mock-server": "^0.1.0"
43
+ "@equinor/fusion-framework-dev-server": "^2.1.1",
44
+ "@equinor/fusion-imports": "^2.0.4",
45
+ "@equinor/fusion-openapi-mock-server": "^0.1.1"
43
46
  },
44
47
  "devDependencies": {
45
48
  "typescript": "^7.0.2",
package/CHANGELOG.md DELETED
@@ -1,37 +0,0 @@
1
- # @equinor/fusion-framework-cli-plugin-mock-server
2
-
3
- ## 0.1.0
4
-
5
- ### Minor Changes
6
-
7
- - f663b46: Add a CLI plugin for running the standalone OpenAPI mock server through `ffc mock-server`.
8
-
9
- The command layers bundled presets and local executable mock modules, reads `mockServer` defaults
10
- from `dev-server.config.ts`, and accepts command-line host, port, and seed overrides. The standalone
11
- server resolves only predefined and local mocks; it never fetches remote service discovery.
12
-
13
- Installing the plugin augments `DevServerOptions` with typed `mockServer` settings for the module
14
- directory, host, port, and deterministic seed without coupling the base dev-server package to the
15
- optional plugin.
16
-
17
- ```ts
18
- // fusion-cli.config.ts
19
- import { defineFusionCli } from '@equinor/fusion-framework-cli';
20
- import mockServerPlugin from '@equinor/fusion-framework-cli-plugin-mock-server';
21
-
22
- export default defineFusionCli(() => ({
23
- plugins: [mockServerPlugin()],
24
- }));
25
- ```
26
-
27
- ```sh
28
- ffc mock-server ./mocks --port 4010
29
- ```
30
-
31
- ### Patch Changes
32
-
33
- - Updated dependencies [f663b46]
34
- - Updated dependencies [f663b46]
35
- - Updated dependencies [f663b46]
36
- - @equinor/fusion-framework-dev-server@2.1.0
37
- - @equinor/fusion-openapi-mock-server@0.1.0
@@ -1,113 +0,0 @@
1
- import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
2
-
3
- const mocks = vi.hoisted(() => ({
4
- close: vi.fn().mockResolvedValue(undefined),
5
- createMockServer: vi.fn(),
6
- discoverServices: vi.fn(),
7
- loadMockServerConfig: vi.fn(),
8
- start: vi.fn(),
9
- use: vi.fn(),
10
- }));
11
-
12
- vi.mock('@equinor/fusion-openapi-mock-server', () => ({
13
- createMockServer: mocks.createMockServer,
14
- }));
15
-
16
- vi.mock('@equinor/fusion-openapi-mock-server/discovery', () => ({
17
- discoverServices: mocks.discoverServices,
18
- }));
19
-
20
- vi.mock('../load-mock-server-config.js', () => ({
21
- loadMockServerConfig: mocks.loadMockServerConfig,
22
- }));
23
-
24
- import { createMockServerCommand } from '../create-mock-server-command.js';
25
-
26
- describe('createMockServerCommand', () => {
27
- beforeEach(() => {
28
- vi.clearAllMocks();
29
- vi.spyOn(process, 'on').mockReturnValue(process);
30
- mocks.createMockServer.mockReturnValue({
31
- close: mocks.close,
32
- start: mocks.start,
33
- use: mocks.use,
34
- });
35
- mocks.discoverServices.mockResolvedValue([]);
36
- mocks.start.mockResolvedValue({ url: 'http://localhost:4010' });
37
- });
38
-
39
- afterEach(() => {
40
- vi.restoreAllMocks();
41
- });
42
-
43
- it('uses project config before plugin defaults', async () => {
44
- mocks.loadMockServerConfig.mockResolvedValue({
45
- path: 'config-mocks',
46
- port: 4010,
47
- host: '127.0.0.1',
48
- seed: 42,
49
- });
50
-
51
- const command = createMockServerCommand({
52
- path: 'plugin-mocks',
53
- port: 4020,
54
- host: 'localhost',
55
- seed: 7,
56
- });
57
- await command.parseAsync(['node', 'test']);
58
-
59
- expect(mocks.discoverServices).toHaveBeenCalledWith('config-mocks');
60
- expect(mocks.createMockServer).toHaveBeenCalledWith({ seed: 42 });
61
- expect(mocks.start).toHaveBeenCalledWith({ port: 4010, host: '127.0.0.1' });
62
- });
63
-
64
- it('uses explicit arguments and flags before project config', async () => {
65
- mocks.loadMockServerConfig.mockResolvedValue({
66
- path: 'config-mocks',
67
- port: 4010,
68
- host: '127.0.0.1',
69
- seed: 42,
70
- });
71
-
72
- const command = createMockServerCommand();
73
- await command.parseAsync([
74
- 'node',
75
- 'test',
76
- 'cli-mocks',
77
- '--port=5000',
78
- '--host=0.0.0.0',
79
- '--seed=99',
80
- ]);
81
-
82
- expect(mocks.discoverServices).toHaveBeenCalledWith('cli-mocks');
83
- expect(mocks.createMockServer).toHaveBeenCalledWith({ seed: 99 });
84
- expect(mocks.start).toHaveBeenCalledWith({ port: 5000, host: '0.0.0.0' });
85
- });
86
-
87
- it('uses plugin defaults before built-in conventions', async () => {
88
- mocks.loadMockServerConfig.mockResolvedValue({});
89
-
90
- const command = createMockServerCommand({
91
- path: 'plugin-mocks',
92
- port: 4020,
93
- host: '127.0.0.1',
94
- seed: 7,
95
- });
96
- await command.parseAsync(['node', 'test']);
97
-
98
- expect(mocks.discoverServices).toHaveBeenCalledWith('plugin-mocks');
99
- expect(mocks.createMockServer).toHaveBeenCalledWith({ seed: 7 });
100
- expect(mocks.start).toHaveBeenCalledWith({ port: 4020, host: '127.0.0.1' });
101
- });
102
-
103
- it('uses built-in conventions when no other values are provided', async () => {
104
- mocks.loadMockServerConfig.mockResolvedValue({});
105
-
106
- const command = createMockServerCommand();
107
- await command.parseAsync(['node', 'test']);
108
-
109
- expect(mocks.discoverServices).toHaveBeenCalledWith('mocks');
110
- expect(mocks.createMockServer).toHaveBeenCalledWith({ seed: undefined });
111
- expect(mocks.start).toHaveBeenCalledWith({ port: 4010, host: 'localhost' });
112
- });
113
- });
@@ -1,88 +0,0 @@
1
- import { Command } from 'commander';
2
- import { describe, expect, it } from 'vitest';
3
-
4
- import type { DevServerOptions } from '@equinor/fusion-framework-dev-server';
5
-
6
- import mockServerPlugin from '../index.js';
7
-
8
- describe('mockServerPlugin', () => {
9
- it('augments DevServerOptions with mock-server configuration', () => {
10
- const config: DevServerOptions = {
11
- api: { serviceDiscoveryUrl: 'https://discovery.example.com' },
12
- mockServer: { path: 'api-mocks', port: 4010, seed: 42 },
13
- };
14
-
15
- expect(config.mockServer).toEqual({ path: 'api-mocks', port: 4010, seed: 42 });
16
- });
17
-
18
- it('registers a top-level "mock-server" command', () => {
19
- const program = new Command();
20
-
21
- mockServerPlugin()(program);
22
-
23
- // every registered command's name, to check "mock-server" is among them
24
- expect(program.commands.map((command) => command.name())).toContain('mock-server');
25
- });
26
-
27
- it('applies every --preset before any positional directory', () => {
28
- const program = new Command();
29
- mockServerPlugin()(program);
30
- // locate the command this plugin registered, by name
31
- const mockServerCommand = program.commands.find((command) => command.name() === 'mock-server');
32
-
33
- // parse() invokes the command's action; capture what it would pass to createMockServer via the parsed options
34
- const parsed = mockServerCommand?.parseOptions([
35
- '--preset=fusion',
36
- '--preset=extra',
37
- './mocks',
38
- './overrides',
39
- ]);
40
-
41
- expect(parsed?.operands).toEqual(['./mocks', './overrides']);
42
- expect(mockServerCommand?.opts().preset).toEqual(['fusion', 'extra']);
43
- });
44
-
45
- it('defaults to the "fusion" preset when --preset is omitted', () => {
46
- const program = new Command();
47
- mockServerPlugin()(program);
48
- // locate the command this plugin registered, by name
49
- const mockServerCommand = program.commands.find((command) => command.name() === 'mock-server');
50
-
51
- mockServerCommand?.parseOptions(['./mocks']);
52
-
53
- expect(mockServerCommand?.opts().preset).toEqual(['fusion']);
54
- });
55
-
56
- it('replaces the default preset instead of appending to it, on the first explicit --preset', () => {
57
- const program = new Command();
58
- mockServerPlugin()(program);
59
- // locate the command this plugin registered, by name
60
- const mockServerCommand = program.commands.find((command) => command.name() === 'mock-server');
61
-
62
- mockServerCommand?.parseOptions(['--preset=other', './mocks']);
63
-
64
- expect(mockServerCommand?.opts().preset).toEqual(['other']);
65
- });
66
-
67
- it('applies caller-provided preset defaults during option parsing', () => {
68
- const program = new Command();
69
- mockServerPlugin({ preset: ['other'], port: 4010, host: '0.0.0.0' })(program);
70
- // locate the command this plugin registered, by name
71
- const mockServerCommand = program.commands.find((command) => command.name() === 'mock-server');
72
-
73
- mockServerCommand?.parseOptions([]);
74
-
75
- expect(mockServerCommand?.opts()).toEqual({ preset: ['other'] });
76
- });
77
-
78
- it('lets an explicit flag override a caller-provided default', () => {
79
- const program = new Command();
80
- mockServerPlugin({ preset: ['other'], port: 4010 })(program);
81
- // locate the command this plugin registered, by name
82
- const mockServerCommand = program.commands.find((command) => command.name() === 'mock-server');
83
-
84
- mockServerCommand?.parseOptions(['--port=5000']);
85
-
86
- expect(mockServerCommand?.opts().port).toBe(5000);
87
- });
88
- });
@@ -1,126 +0,0 @@
1
- import { createCommand, createOption, type Command } from 'commander';
2
-
3
- import { createMockServer } from '@equinor/fusion-openapi-mock-server';
4
- import { discoverServices } from '@equinor/fusion-openapi-mock-server/discovery';
5
-
6
- import { loadMockServerConfig } from './load-mock-server-config.js';
7
-
8
- /** Option values for `ffc mock-server`. */
9
- interface MockServerCommandOptions {
10
- /** Bundled preset names to layer in, lowest precedence first (e.g. `['fusion']`). */
11
- preset: string[];
12
- /** Port to listen on; `undefined` lets the OS assign a free one. */
13
- port?: number;
14
- /** Hostname to bind to. */
15
- host?: string;
16
- /** Seeds every service's faked responses, if given. */
17
- seed?: number;
18
- }
19
-
20
- /** Overrides for `ffc mock-server`'s own built-in defaults, set by whoever registers the plugin. */
21
- export interface MockServerCommandDefaults {
22
- /** Mock-module directory used when no positional directory or config path is supplied. */
23
- path?: string;
24
- /** Bundled preset(s) to apply when `--preset` isn't given at all. Defaults to `['fusion']`. */
25
- preset?: string[];
26
- /** Port to listen on when `--port` isn't given. Defaults to `4010`. */
27
- port?: number;
28
- /** Hostname to bind to when `--host` isn't given. Defaults to `'localhost'`. */
29
- host?: string;
30
- /** Seed to apply when `--seed` isn't given. Defaults to unseeded (random) faked responses. */
31
- seed?: number;
32
- }
33
-
34
- /**
35
- * Builds the `ffc mock-server` command definition.
36
- *
37
- * Serves every service discovered from one or more directories of `<name>.mock.ts`
38
- * modules (plus any bundled presets) over HTTP, using
39
- * `@equinor/fusion-openapi-mock-server`'s `createMockServer`. Presets and
40
- * directories are both layered in ascending precedence — a later `--preset`
41
- * or directory replaces an earlier one's services by key — with every
42
- * `--preset` applied before every positional directory, regardless of their
43
- * order on the command line.
44
- *
45
- * Defaults to the bundled `fusion` preset when `--preset` isn't given at
46
- * all — a Fusion app's default framework modules resolve several
47
- * service-discovery keys eagerly at startup and fail hard without them. The
48
- * first explicit `--preset` fully replaces that default rather than
49
- * appending to it; repeat the flag (`--preset=fusion --preset=other`) to
50
- * combine it with something else. Pass {@link defaults} to change any of
51
- * these built-in defaults (e.g. a fixed port for a specific app).
52
- *
53
- * Keeps the server running in the foreground until `SIGINT`/`SIGTERM`, so it
54
- * dies with whatever started it (e.g. Playwright's `webServer`) rather than
55
- * lingering as an orphaned process.
56
- *
57
- * @param defaults - Overrides for the command's own built-in path, preset, port, host, and seed defaults.
58
- * @returns A fresh `Command` instance — a factory rather than a shared singleton, since
59
- * Commander stores parsed option values on the `Command` instance itself.
60
- *
61
- * @example
62
- * ```sh
63
- * ffc mock-server ./mocks --port 4010
64
- * ```
65
- */
66
- export function createMockServerCommand(defaults: MockServerCommandDefaults = {}): Command {
67
- // sentinel default for --preset, so the first explicit flag replaces it instead of appending to it
68
- const defaultPresets: string[] = defaults.preset ?? ['fusion'];
69
-
70
- return createCommand('mock-server')
71
- .description('Serve OpenAPI-fake responses over HTTP, from bundled presets and/or mock modules')
72
- .argument('[dirs...]', 'directories of <name>.mock.ts modules, in ascending precedence')
73
- .addOption(
74
- createOption(
75
- '--preset <name>',
76
- `bundled preset to layer in, in ascending precedence (repeatable; defaults to ${JSON.stringify(defaultPresets)}, replaced by the first explicit flag)`,
77
- )
78
- .default(defaultPresets)
79
- .argParser((value: string, previous: string[]) =>
80
- previous === defaultPresets ? [value] : [...previous, value],
81
- ),
82
- )
83
- .addOption(
84
- createOption(
85
- '--port <port>',
86
- `port to listen on (default: config or ${defaults.port ?? 4010})`,
87
- ).argParser(Number),
88
- )
89
- .addOption(createOption('--host <host>', 'hostname to bind to (default: config or localhost)'))
90
- .addOption(
91
- createOption(
92
- '--seed <seed>',
93
- `seeds every service's faked responses, for reproducible output (default: ${defaults.seed ?? 'unseeded/random'})`,
94
- ).argParser(Number),
95
- )
96
- .action(async (dirs: string[], options: MockServerCommandOptions) => {
97
- const config = await loadMockServerConfig(process.cwd());
98
- const sourceDirs = dirs.length ? dirs : [config.path ?? defaults.path ?? 'mocks'];
99
- const definitionGroups = await Promise.all(
100
- sourceDirs
101
- // Resolve every configured layer before startup so discovery requirements are known.
102
- .map((dir) => discoverServices(dir)),
103
- );
104
- const server = createMockServer({
105
- seed: options.seed ?? config.seed ?? defaults.seed,
106
- });
107
- // presets always apply before directories, regardless of flag position on the command line
108
- for (const preset of options.preset) server.use(preset);
109
- // Resolved directory groups are the highest-precedence layers, applied after every preset.
110
- for (const definitions of definitionGroups) server.use(definitions);
111
-
112
- const { url } = await server.start({
113
- port: options.port ?? config.port ?? defaults.port ?? 4010,
114
- host: options.host ?? config.host ?? defaults.host ?? 'localhost',
115
- });
116
- console.log(`mock server listening at ${url}`);
117
-
118
- const shutdown = (): void => {
119
- void server.close().finally(() => process.exit(0));
120
- };
121
- process.on('SIGINT', shutdown);
122
- process.on('SIGTERM', shutdown);
123
- });
124
- }
125
-
126
- export default createMockServerCommand;
@@ -1,26 +0,0 @@
1
- import type { DevServerOptions, FusionTemplateEnv } from '@equinor/fusion-framework-dev-server';
2
-
3
- /** Standalone OpenAPI mock-server settings added to development server configuration. */
4
- export interface DevServerMockOptions {
5
- /** Directory containing `<name>.mock.ts` modules, relative to the project root. Defaults to `mocks`. */
6
- path?: string;
7
- /** Port used by direct `<key>.localhost` endpoint URLs. */
8
- port?: number;
9
- /** Hostname the standalone mock server binds to. Defaults to `localhost`. */
10
- host?: string;
11
- /** Seed used for reproducible generated OpenAPI responses. */
12
- seed?: number;
13
- }
14
-
15
- declare module '@equinor/fusion-framework-dev-server' {
16
- /** Development server options contributed when the mock-server CLI plugin is installed. */
17
- interface DevServerOptions<TEnv extends Partial<FusionTemplateEnv> = Partial<FusionTemplateEnv>> {
18
- /** Settings consumed by local mock discovery and the standalone `ffc mock-server` process. */
19
- mockServer?: DevServerMockOptions;
20
- }
21
- }
22
-
23
- /** Compile-time assertion that the module augmentation is compatible with the base options. */
24
- export type MockServerDevServerOptions = DevServerOptions & {
25
- mockServer?: DevServerMockOptions;
26
- };
package/src/index.ts DELETED
@@ -1,36 +0,0 @@
1
- import type { Command } from 'commander';
2
-
3
- import {
4
- createMockServerCommand,
5
- type MockServerCommandDefaults,
6
- } from './create-mock-server-command.js';
7
-
8
- export type { MockServerCommandDefaults };
9
- export type {
10
- DevServerMockOptions,
11
- MockServerDevServerOptions,
12
- } from './dev-server-options.js';
13
-
14
- /**
15
- * Creates the `ffc mock-server` CLI plugin, for a `fusion-cli.config.ts`'s `plugins` array.
16
- *
17
- * @param defaults - Overrides for the command's own built-in `--preset`/`--port`/`--host` defaults.
18
- * @returns A plugin function that registers the `mock-server` command on the CLI program.
19
- *
20
- * @example
21
- * ```ts
22
- * import { defineFusionCli } from '@equinor/fusion-framework-cli';
23
- * import mockServerPlugin from '@equinor/fusion-framework-cli-plugin-mock-server';
24
- *
25
- * export default defineFusionCli(() => ({
26
- * plugins: [mockServerPlugin({ preset: ['fusion'], port: 4010 })],
27
- * }));
28
- * ```
29
- */
30
- export function mockServerPlugin(defaults?: MockServerCommandDefaults): (program: Command) => void {
31
- return (program: Command): void => {
32
- program.addCommand(createMockServerCommand(defaults));
33
- };
34
- }
35
-
36
- export default mockServerPlugin;
@@ -1,75 +0,0 @@
1
- import { FileNotFoundError, importConfig, type EsmModule } from '@equinor/fusion-imports';
2
-
3
- import type { DevServerOptions } from '@equinor/fusion-framework-dev-server';
4
-
5
- import type { DevServerMockOptions } from './dev-server-options.js';
6
-
7
- /** Mock-server settings resolved from `dev-server.config.ts`. */
8
- export interface ResolvedMockServerConfig extends DevServerMockOptions {}
9
-
10
- interface DevServerConfigOverrides {
11
- mockServer?: DevServerMockOptions;
12
- }
13
-
14
- interface DevServerConfigModule extends EsmModule {
15
- default?:
16
- | DevServerConfigOverrides
17
- | ((
18
- env: { command: 'serve'; environment: 'local'; mode: string; root: string },
19
- args: { base: DevServerOptions },
20
- ) => DevServerConfigOverrides | Promise<DevServerConfigOverrides | undefined> | undefined);
21
- }
22
-
23
- /**
24
- * Loads standalone mock-server settings from a project's `dev-server.config.ts`.
25
- *
26
- * @param root - Project root used to resolve the config file and relative mock path.
27
- * @returns Mock-server settings explicitly resolved from the project config.
28
- * @throws {Error} When an existing config cannot be imported or returns an invalid value.
29
- */
30
- export async function loadMockServerConfig(root: string): Promise<ResolvedMockServerConfig> {
31
- const base: DevServerOptions = {
32
- mockServer: { path: 'mocks' },
33
- api: { serviceDiscoveryUrl: '' },
34
- };
35
-
36
- try {
37
- const { config } = await importConfig<DevServerConfigOverrides, DevServerConfigModule>(
38
- 'dev-server.config',
39
- {
40
- baseDir: root,
41
- script: {
42
- // Config factories receive the same runtime shape as ordinary development serving.
43
- resolve: async (module) => {
44
- const exported = module.default;
45
- // Factory configs may derive settings from the supplied base configuration.
46
- if (typeof exported === 'function') {
47
- return (
48
- (await exported(
49
- { command: 'serve', environment: 'local', mode: 'development', root },
50
- { base },
51
- )) ?? {}
52
- );
53
- }
54
- return exported ?? {};
55
- },
56
- },
57
- },
58
- );
59
-
60
- return {
61
- path: config.mockServer?.path ?? base.mockServer?.path,
62
- port: config.mockServer?.port,
63
- host: config.mockServer?.host,
64
- seed: config.mockServer?.seed,
65
- };
66
- } catch (error) {
67
- // An absent config is a supported convention-only setup; import and evaluation failures are not.
68
- if (error instanceof FileNotFoundError) {
69
- return {};
70
- }
71
- throw error;
72
- }
73
- }
74
-
75
- export default loadMockServerConfig;
package/src/version.ts DELETED
@@ -1,2 +0,0 @@
1
- // Generated by genversion.
2
- export const version = '0.1.0';
package/tsconfig.json DELETED
@@ -1,26 +0,0 @@
1
- {
2
- "extends": "../../../tsconfig.base.json",
3
- "compilerOptions": {
4
- "module": "NodeNext",
5
- "moduleResolution": "NodeNext",
6
- "outDir": "dist/esm",
7
- "rootDir": "src",
8
- "declarationDir": "./dist/types",
9
- "paths": {
10
- "*": ["./*"]
11
- }
12
- },
13
- "references": [
14
- {
15
- "path": "../../dev-server"
16
- },
17
- {
18
- "path": "../../utils/imports"
19
- },
20
- {
21
- "path": "../../utils/openapi-mock-server"
22
- }
23
- ],
24
- "include": ["src/**/*"],
25
- "exclude": ["node_modules"]
26
- }
package/vitest.config.ts DELETED
@@ -1,9 +0,0 @@
1
- import { defineConfig } from 'vitest/config';
2
-
3
- export default defineConfig({
4
- test: {
5
- globals: true,
6
- environment: 'node',
7
- include: ['src/**/*.test.ts'],
8
- },
9
- });