@seed-fe/mfa
v1.0.4
Published
MFA (Micro Frontend Assistant) is the core library and the `mfa` CLI for multi-repository micro frontend workspaces.
Readme
@seed-fe/mfa
MFA (Micro Frontend Assistant) is the core library and the mfa CLI for multi-repository micro frontend workspaces: generate workspace metadata and runtime configuration, fetch application sources, run the local development services and proxy, generate Sitemaps, and build and publish project artifacts.
If you work in VS Code, install the MFA extension instead — it ships the same core and does not require this package.
Install
Requires Node.js >= 22.12.0.
# Global install, so the mfa command is available everywhere
npm install -g @seed-fe/mfa
mfa --helpWorkspace layout
<workspace-root>/
├── project/
│ ├── meta.json
│ ├── project.config.json
│ ├── menu.config.json
│ ├── menu.config.local.json
│ ├── dev.local.jsonc
│ └── sitemap.json
├── container/
│ └── <container-name>/
└── microapps/
└── <microapp-name>/
└── public/
└── sitemap.jsonCommands walk up from the current directory to the nearest one that contains project/, container/ and microapps/; -w, --workspace selects it explicitly. MFA generates project.config.json and project/sitemap.json. menu.config.local.json is an optional local menu override you maintain by hand.
Quick start
# Create a workspace and clone the project configuration repository
mfa init <configuration-repository> -w <workspace-directory>
cd <workspace-directory>
# Fetch only the applications you need; existing sources are left untouched
mfa pull --apps <container-name> <microapp-name>
# Run the applications and the shared entry point in the foreground; Ctrl+C stops the session
mfa dev --apps <container-name> <microapp-name>Commands
Global options: -w, --workspace <path> (defaults to the current directory), --json, -V, --version. Output and error messages are in English.
| Command | Description |
| -------------------------------------------- | ------------------------------------------------------------------------------------------- |
| mfa init <repository> | Create the conventional directories and clone the configuration repository into project/ |
| mfa metadata | Generate project/meta.json from the conventional directories, replacing the existing file |
| mfa generate | Generate project.config.json from meta.json (alias g) |
| mfa pull | Clone missing applications, with --all or --apps <names...> |
| mfa status | Inspect configured applications and their local source state |
| mfa refresh | Fast-forward the configuration repository's release branch and regenerate runtime config |
| mfa dev | Run applications and the shared entry point in the foreground (alias start) |
| mfa server | Run only the shared entry point |
| mfa build | Fetch missing applications, build them and assemble the artifact |
| mfa assemble | Assemble existing application dist directories only |
| mfa release | Assemble a release package at the exact release.version |
| mfa artifact pack / mfa artifact publish | Package and upload versioned artifacts |
| mfa sitemap generate / show / save | Generate, inspect and save Sitemap data |
Results go to stdout, logs go to stderr, and failures exit non-zero. Use --json when a script reads the result. Development services run in the foreground and never fork a background daemon. Press q + enter to stop: that stops every application the session started and releases its ports. When stdin is not a terminal (CI, pipes), press Ctrl+C instead.
mfa metadata accepts --name, --version and --branch. mfa dev accepts --apps <names...>, -n, --name <name>, --no-server and --mock (use the dev:mock script, see Start scripts and package managers); without a selection it starts every application whose source is already local.
Metadata and runtime configuration
metadata generates meta.json from the directories; generate generates project.config.json from meta.json. They are separate operations.
Metadata generation requires the three conventional directories and exactly one container directory under container/. The application list comes only from the current directories — it does not audit applications you have not fetched. Each application name is its directory name; description and version come from package.json; repository URL and branch come from Git, and an application without a remote keeps an empty URL. The project name defaults to the workspace directory name, the version to project/package.json or 1.0.0, and the branch to the current Git branch or main. pathname drops the project suffix from the microapplication directory name: the project identifier is the project name without a trailing -web, and a directory name ending in -<project-identifier> loses that suffix. Adjust it afterwards to match your routes.
The container loads runtime configuration through /project/, and project menu locales live at /project/locales/<locale>/project-menu.json. The container public/ directory never holds these files.
Local build
# Full build: fetch missing applications, build them, output to dist/ in the workspace root
mfa build
# Choose the output directory; relative paths resolve from the workspace root, absolute paths are used as given.
# Without it, output always goes to dist/
mfa build -o artifacts
# Replace an existing artifact for the same project name and version
mfa build --force
# Build only the selected applications; project configuration is still included
mfa build --apps <microapp-name>
# Assemble project configuration only; no application source required
mfa build --project-config-only
# Ship only the selected application assets, without project configuration
mfa build --apps <microapp-name> --exclude-project-config
# Skip dependency installation when it is already prepared
mfa build --no-install
# Assemble existing dist directories without fetching or building
mfa assemble--apps, --exclude-project-config, --project-config-only, -o, --output-dir and -f, --force mean the same thing for build, assemble and release. --project-config-only conflicts with --apps and --exclude-project-config.
Output layout:
dist/
├── <name>-<release.version>/
│ ├── container/
│ ├── microapps/
│ │ └── <microapp-name>/
│ └── project/
│ ├── project.config.json
│ └── menu.config.json
└── <name>-<release.version>.tar.gz- The output directory defaults to
dist/in the workspace root, and only-o, --output-diroverrides it. - The artifact also carries an existing
project/menu.config.json,project/sitemap.json,project/version.jsandproject/locales/. It never carries metadata, local overrides or tool configuration. - The menu and sitemap are presets. A container may use them as they are, adapt them after loading, or provide its own (for example a menu served by the backend), so a build succeeds without them.
- The archive contains one top-level directory of the same name. Asset directories are assembled by application
name;pathnameonly expresses the route prefix. - A partial build still writes the complete application list from
meta.jsonintoproject.config.json. A configuration-only package contains justproject/, with no emptycontainer/ormicroapps/. - Existing applications build from their current source and branch. The package manager follows the same rule as development services, see Start scripts and package managers.
- An existing output is reported as a conflict.
--forcereplaces the artifact directory and.tar.gzfor this project name and version, and keeps every other version and file in the output directory.
This release does not produce Docker images, Nginx, certificates, gateway or deployment configuration.
Versioned release
mfa release --provider github
mfa release --provider gitlab --apps <microapp-name>
mfa release --provider github --project-config-only
mfa release --provider github --input-mode sourcerelease assembles the exact versions recorded in meta.json and uses published artifacts by default. --input-mode source checks the exact Tags out into a temporary directory and builds them, leaving your local branches alone. Use --host <origin> for self-hosted platforms and --project-repository <url> to override the configuration repository. A missing artifact or Tag is an error; nothing falls back automatically.
Versioned artifacts come from mfa artifact pack and mfa artifact publish. GitHub uses Release assets and GitLab uses the Generic Package Registry. For naming, credentials and pipeline presets, see the CI/CD project build guide; the presets ship in templates/ci/.
Development services and proxy
MFA has exactly one configuration file, project/dev.local.jsonc, holding development services, ports, proxies and package manager overrides. The package ships templates/dev.local.jsonc.example and schemas/dev.schema.json; copy them to project/dev.local.jsonc and project/dev.schema.json, then add project/dev.local.jsonc to the configuration repository's .gitignore.
The file allows comments and trailing commas:
{
"$schema": "./dev.schema.json",
"server.port": 7000,
"server.host": "127.0.0.1",
"applications.port.start": 6060,
"project.source": "local",
"server.proxy.api": {
"/api/orders": { "target": "http://127.0.0.1:8081", "ws": true },
"/api/": { "target": "https://gateway.example.invalid" },
},
}| Key | Default | Description |
| --------------------------------------------------- | -------------------- | ------------------------------------------------------------------------- |
| server.port / server.host | 7000 / 127.0.0.1 | Shared workspace entry point |
| applications.port.start | 6060 | First port assigned to applications |
| project.source | local | remote loads project configuration from server.proxy.project |
| server.proxy.project | none | Full-package environment used when an application is not running locally |
| server.proxy.container / server.proxy.microapps | none | Attach separately started local or remote applications |
| server.proxy.api | {} | API proxy rules |
| server.proxy.token | none | Bearer token shared by API rules |
| git.remotes | [] | Self-hosted Git platforms, see below |
| tag.project.commitMetadata | true | Bump the version and commit changes before tagging the project, see below |
| packageManager.* | auto-detect | Package manager detection and overrides, see below |
Application targets are resolved in this order: an application MFA is running, then an explicit server.proxy.container / server.proxy.microapps target, then the full-package environment.
API paths are entirely yours to define. A plain key matches by prefix and a key starting with ^ is a regular expression; longer keys win, and HTTP and WS share the same rule order. The request path is forwarded as-is unless you declare rewrite:
"/api/orders": {
"target": "http://127.0.0.1:8081",
"ws": true,
"changeOrigin": true,
"rewrite": { "pattern": "^/api", "replacement": "" },
"headers": { "X-Tenant": "<tenant>" }
}Rules also accept secure, enabled and token. Tokens reach API rules only, in the order explicit Authorization header > rule token > server.proxy.token; an empty rule token stops the shared token from being inherited. The startup summary names the source and never prints the token.
Saving the file reloads the shared entry point. A changed application port takes effect the next time that application starts. Each workspace uses its own ports, so you can work on several workspaces at once. The service exposes only project.config.json, menu.config.json, sitemap.json and version.js under /project/ — never metadata, proxy configuration or hidden files.
git.remotes declares self-hosted Git platforms in the same shape as GitLens gitlens.remotes; the VS Code extension uses it to build web links to repositories, branches, tags and commits. Public platforms such as GitHub, GitLab, Gitea, Bitbucket, Gitee, CNB and Codeup need no entry:
"git.remotes": [{ "domain": "git.example.com", "type": "GitLab" }]tag.project.commitMetadata is on by default: when the VS Code extension tags the project, it first bumps the project release.version in meta.json, commits meta.json (including the versions written when tagging applications) together with the other changes of the project configuration repository (menus, locales and so on; ignored files left out) to the release branch, then tags that commit and optionally pushes the branch and the tag. Turned off, the latest commit of the remote release branch is tagged with the version already committed there.
Start scripts and package managers
Applications start from conventional package.json scripts: dev works against online services, while dev:mock serves part of the requests from the application's own mock and sends the rest online. MFA only picks the script and passes --port; mock and proxy behavior belong to the application's scaffold. mfa dev --mock, or "Start Application with Mock" in VS Code, uses dev:mock; an application without that script falls back to dev (then serve, start) and the log says so.
Development services and local builds share one package manager rule, resolved in this order:
- The application's entry in
packageManager.applications; - The global
packageManager.use; - The application's package.json
packageManagerfield (Corepack format, such as[email protected]); - The lockfile mapping: entries in
packageManager.lockfilesfirst, then the built-inpnpm-lock.yaml,yarn.lock,package-lock.json,npm-shrinkwrap.json,bun.lockandbun.lockb; - npm when nothing matches.
Forcing a package manager skips steps 3 and 4. Setting packageManager.autoDetect to false skips them too and uses npm unless one is forced. The built-in pnpm, yarn and bun receive --port right after the script name; npm and any other package manager use run <script> -- --port=<port>.
"packageManager.lockfiles": { "<lockfile>": "<manager>" },
"packageManager.use": "pnpm",
"packageManager.applications": { "<app-name>": "yarn" }On every start or reload, MFA merges your local configuration with what it derives from the workspace into one effective configuration at mfa-<date>/<project-name>-<path-hash>/project-server.json in the system temporary directory. The "Effective config" line in the startup summary is its path. The project service reads only this file:
applications: where each application goes —running(started by MFA),attached(explicit target),remote(full-package environment) orunavailable(no target).routes: every proxy rule in actual match order;origintells whether it comes fromdev.local.jsonc(user) or was derived by MFA (mfa).server:requestedPortis the configured port,portthe one actually used.
The file is cleaned up with the system temporary directory.
Sitemap
# Merge existing application Sitemaps only
mfa sitemap generate
# Regenerate local microapplication Sitemaps first, then merge
mfa sitemap generate --all
mfa sitemap generate --apps <microapp-name>
# Show the project Sitemap; --app <name> shows one application
mfa sitemap show
# Save an edited application Sitemap from a JSON array of nodes
mfa sitemap save <microapp-name> ./edited-sitemap.json- Application level: scans the conventional page directories
src/views/pagesandsrc/pages— whichever exists, and it is an error only when neither does — derives relative routes, prefixes keys with the package name, and writesmicroapps/<app>/public/sitemap.json. Valid manual grouping and ordering survive; new pages are mounted by route and deleted pages are removed. - Project level: reads each application's existing
public/sitemap.jsonand only prefixespathwith the application mount path — no key prefix, no page scanning — then writesproject/sitemap.json. A missing application keeps its previous project nodes or is skipped. - Output keeps the JSON properties you declared and never writes ownership fields, scan metadata or empty
children. Regenerating does not prefix a path twice.
Declare a Vue page with a <sitemap lang="json"> custom block:
<sitemap lang="json">
{
"key": "resourceDetail",
"title": "Resource detail",
"extensions": {
"permission": "<microapp-name>.resource.detail.view"
}
}
</sitemap>Declare a React page with @Sitemap in a header comment, followed by YAML frontmatter or JSON:
/**
* @Sitemap
* ---
* key: resourceDetail
* title: Resource detail
* extensions:
* permission: <microapp-name>.resource.detail.view
* ---
*/