mfeflow 0.0.0-stage → 0.2.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 (137) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +126 -2
  3. package/assets/features/analytics-gtm/feature.json +117 -0
  4. package/assets/features/analytics-gtm/files/Analytics.tsx +13 -0
  5. package/assets/features/analytics-gtm/files/ConsentBanner.tsx +47 -0
  6. package/assets/features/analytics-gtm/files/consent-banner-test.tsx +36 -0
  7. package/assets/features/analytics-gtm/files/init.ts +26 -0
  8. package/assets/features/analytics-gtm/files/remote-analytics-test.ts.eta +18 -0
  9. package/assets/features/analytics-gtm/files/remote-analytics.ts.eta +13 -0
  10. package/assets/features/analytics-gtm/files/shell-init-test.ts +42 -0
  11. package/assets/features/ci-github/feature.json +26 -0
  12. package/assets/features/ci-github/files/ci.md +41 -0
  13. package/assets/features/ci-github/files/ci.yml +63 -0
  14. package/assets/features/env-t3/feature.json +399 -0
  15. package/assets/features/env-t3/files/check-runtime-config.ts +105 -0
  16. package/assets/features/env-t3/files/env.arktype.ts +12 -0
  17. package/assets/features/env-t3/files/env.example +1 -0
  18. package/assets/features/env-t3/files/env.valibot.ts +12 -0
  19. package/assets/features/env-t3/files/env.zod.ts +12 -0
  20. package/assets/features/env-t3/files/runtime.ts.eta +30 -0
  21. package/assets/features/env-t3/files/schema.arktype.ts +8 -0
  22. package/assets/features/env-t3/files/schema.valibot.ts +8 -0
  23. package/assets/features/env-t3/files/schema.zod.ts +8 -0
  24. package/assets/features/event-bus/feature.json +100 -0
  25. package/assets/features/event-bus/files/announce.tsx.eta +10 -0
  26. package/assets/features/event-bus/files/contracts-events.ts +11 -0
  27. package/assets/features/event-bus/files/contracts-package.json.eta +10 -0
  28. package/assets/features/event-bus/files/contracts-readme.md +10 -0
  29. package/assets/features/event-bus/files/events-test.ts.eta +18 -0
  30. package/assets/features/event-bus/files/events.ts.eta +20 -0
  31. package/assets/features/event-bus/files/shell-events-test.ts.eta +22 -0
  32. package/assets/features/event-bus/files/shell-events.ts.eta +19 -0
  33. package/assets/features/git-hooks/feature.json +42 -0
  34. package/assets/features/git-hooks/files/commitlint.config.js +1 -0
  35. package/assets/features/git-hooks/files/lefthook.yml +9 -0
  36. package/assets/features/http/feature.json +81 -0
  37. package/assets/features/http/files/client-test.ts +19 -0
  38. package/assets/features/http/files/client.ts +20 -0
  39. package/assets/features/lint-oxlint/feature.json +60 -0
  40. package/assets/features/lint-oxlint/files/oxlintrc.json +5 -0
  41. package/assets/features/lint-oxlint/files/prettierignore +5 -0
  42. package/assets/features/lint-oxlint/files/prettierrc.json +5 -0
  43. package/assets/features/route-factory/feature.json +60 -0
  44. package/assets/features/route-factory/files/contract.ts.eta +7 -0
  45. package/assets/features/route-factory/files/routes-contract.ts.eta +11 -0
  46. package/assets/features/route-factory/files/routes-test.ts.eta +9 -0
  47. package/assets/features/route-factory/files/routes.ts.eta +11 -0
  48. package/assets/features/telemetry-otel/feature.json +159 -0
  49. package/assets/features/telemetry-otel/files/init.ts.eta +53 -0
  50. package/assets/features/telemetry-otel/files/remote-telemetry-test.ts.eta +13 -0
  51. package/assets/features/telemetry-otel/files/remote-telemetry.ts.eta +11 -0
  52. package/assets/features/telemetry-otel/files/shell-init-test.ts.eta +36 -0
  53. package/assets/features/testing/feature.json +202 -0
  54. package/assets/features/testing/files/handlers.ts +5 -0
  55. package/assets/features/testing/files/playwright.config.ts +30 -0
  56. package/assets/features/testing/files/root-vitest.config.ts +6 -0
  57. package/assets/features/testing/files/sample-handlers-test.ts +16 -0
  58. package/assets/features/testing/files/sample-remote-test.tsx +12 -0
  59. package/assets/features/testing/files/sample-shell-test.tsx +11 -0
  60. package/assets/features/testing/files/sample-smoke-e2e.ts +53 -0
  61. package/assets/features/testing/files/server.ts +4 -0
  62. package/assets/features/testing/files/setup.ts +18 -0
  63. package/assets/features/testing/files/vitest.config.ts +11 -0
  64. package/assets/features/tsconfig/feature.json +78 -0
  65. package/assets/features/tsconfig/files/root.tsconfig.json +4 -0
  66. package/assets/features/tsconfig/files/tsconfig.json +5 -0
  67. package/assets/features/url-factory/feature.json +30 -0
  68. package/assets/features/url-factory/files/endpoints.ts +23 -0
  69. package/assets/templates/monorepo/feature.json +321 -0
  70. package/assets/templates/monorepo/files/remote/mfe.json.eta +7 -0
  71. package/assets/templates/monorepo/files/remote/package.json.eta +6 -0
  72. package/assets/templates/monorepo/files/remote/rsbuild.config.ts.eta +7 -0
  73. package/assets/templates/monorepo/files/remote/src/bootstrap.tsx.eta +6 -0
  74. package/assets/templates/monorepo/files/remote/src/env.d.ts.eta +1 -0
  75. package/assets/templates/monorepo/files/remote/src/exposed/App.tsx.eta +18 -0
  76. package/assets/templates/monorepo/files/remote/src/index.ts.eta +2 -0
  77. package/assets/templates/monorepo/files/root/AGENTS.md.eta +17 -0
  78. package/assets/templates/monorepo/files/root/README.md.eta +52 -0
  79. package/assets/templates/monorepo/files/root/gitignore.eta +11 -0
  80. package/assets/templates/monorepo/files/root/nvmrc.eta +1 -0
  81. package/assets/templates/monorepo/files/root/package.json.eta +9 -0
  82. package/assets/templates/monorepo/files/root/pnpm-workspace.yaml.eta +7 -0
  83. package/assets/templates/monorepo/files/root/turbo.json.eta +16 -0
  84. package/assets/templates/monorepo/files/shell/mfe.json.eta +7 -0
  85. package/assets/templates/monorepo/files/shell/package.json.eta +6 -0
  86. package/assets/templates/monorepo/files/shell/rsbuild.config.ts.eta +74 -0
  87. package/assets/templates/monorepo/files/shell/src/App.tsx.eta +46 -0
  88. package/assets/templates/monorepo/files/shell/src/bootstrap.tsx.eta +24 -0
  89. package/assets/templates/monorepo/files/shell/src/env.d.ts.eta +1 -0
  90. package/assets/templates/monorepo/files/shell/src/index.ts.eta +4 -0
  91. package/assets/templates/monorepo/files/shell/src/pages/Home.tsx.eta +14 -0
  92. package/assets/templates/monorepo/files/shell/src/remotes/route-table.tsx.eta +12 -0
  93. package/assets/templates/monorepo/glue/registry-entry.json.eta +5 -0
  94. package/assets/templates/monorepo/glue/route.tsx.eta +5 -0
  95. package/bin/mfeflow.js +4 -0
  96. package/dist/args.d.ts +68 -0
  97. package/dist/assets.d.ts +13 -0
  98. package/dist/catalog.d.ts +24 -0
  99. package/dist/checks/compat.d.ts +2 -0
  100. package/dist/checks/config.d.ts +8 -0
  101. package/dist/checks/env.d.ts +6 -0
  102. package/dist/checks/index.d.ts +6 -0
  103. package/dist/checks/plugins.d.ts +4 -0
  104. package/dist/checks/singletons.d.ts +9 -0
  105. package/dist/checks/workspace.d.ts +2 -0
  106. package/dist/cli.d.ts +11 -0
  107. package/dist/commands/add.d.ts +84 -0
  108. package/dist/commands/budget.d.ts +56 -0
  109. package/dist/commands/dev.d.ts +62 -0
  110. package/dist/commands/doctor.d.ts +141 -0
  111. package/dist/commands/env-key.d.ts +4 -0
  112. package/dist/commands/features.d.ts +53 -0
  113. package/dist/commands/init.d.ts +102 -0
  114. package/dist/devregistry.d.ts +19 -0
  115. package/dist/exit.d.ts +31 -0
  116. package/dist/findings.d.ts +35 -0
  117. package/dist/glue.d.ts +41 -0
  118. package/dist/help.d.ts +12 -0
  119. package/dist/index.d.ts +8 -0
  120. package/dist/index.js +4171 -0
  121. package/dist/inspect.d.ts +57 -0
  122. package/dist/io.d.ts +10 -0
  123. package/dist/local.d.ts +28 -0
  124. package/dist/options.d.ts +7 -0
  125. package/dist/output.d.ts +157 -0
  126. package/dist/pipeline.d.ts +7 -0
  127. package/dist/preset.d.ts +19 -0
  128. package/dist/proc.d.ts +79 -0
  129. package/dist/prompter.d.ts +28 -0
  130. package/dist/report.d.ts +26 -0
  131. package/dist/runtime.d.ts +17 -0
  132. package/dist/singletons.d.ts +11 -0
  133. package/dist/sizes.d.ts +17 -0
  134. package/dist/system.d.ts +20 -0
  135. package/dist/version.d.ts +8 -0
  136. package/dist/write.d.ts +26 -0
  137. package/package.json +66 -3
@@ -0,0 +1,57 @@
1
+ import { type Issue, type MfeManifest, type RegistryEntry } from '@mfeflow/contracts';
2
+ import { type MfeflowProject } from '@mfeflow/core';
3
+ export declare const ENVS: readonly ['dev', 'staging', 'prod'];
4
+ export type Read<T> = {
5
+ state: 'missing';
6
+ } | {
7
+ state: 'invalid';
8
+ issues: Issue[];
9
+ } | {
10
+ state: 'ok';
11
+ value: T;
12
+ };
13
+ export interface PackageView {
14
+ name?: string;
15
+ version?: string;
16
+ /** dependencies, devDependencies and peerDependencies, `catalog:` already resolved. */
17
+ deps: Record<string, string>;
18
+ }
19
+ export interface AppView {
20
+ name: string;
21
+ kind: 'shell' | 'remote';
22
+ /** Path from `mfeflow.json`. */
23
+ rel: string;
24
+ dir: string;
25
+ port: number | undefined;
26
+ /** The folder has a package.json. */
27
+ present: boolean;
28
+ pkg: PackageView | undefined;
29
+ mfe: Read<MfeManifest>;
30
+ }
31
+ export interface WorkspacePackage {
32
+ dir: string;
33
+ rel: string;
34
+ pkg: PackageView;
35
+ hasVersion: boolean;
36
+ }
37
+ export interface ProjectView {
38
+ root: string;
39
+ project: MfeflowProject;
40
+ apps: AppView[];
41
+ workspace: WorkspacePackage[];
42
+ /** env → remote name → entry. Only remotes with a file in that env are listed. */
43
+ registry: Record<string, Record<string, Read<RegistryEntry>>>;
44
+ /** Folders under registry/envs that exist. */
45
+ envDirs: string[];
46
+ }
47
+ export declare function readText(file: string): Promise<string | undefined>;
48
+ /** Workspace folders for the simple globs pnpm projects use: `dir/*`, plain paths, and `!` exclusions. */
49
+ export declare function expandGlobs(root: string, globs: readonly string[]): Promise<string[]>;
50
+ /** Everything the checks read, loaded once. Throws the E102-E104 errors of `loadProject`. */
51
+ export declare function inspectProject(root: string): Promise<ProjectView>;
52
+ /**
53
+ * The version of `name` an app uses: what is installed under node_modules (walking up from
54
+ * the app to the project root, as Node does, never above it), else the version its package.json
55
+ * declares. Only packages the app declares or the shared list names are looked up.
56
+ */
57
+ export declare function versionIn(app: Pick<AppView, 'dir' | 'pkg'>, name: string, root: string): Promise<string | undefined>;
package/dist/io.d.ts ADDED
@@ -0,0 +1,10 @@
1
+ /** Everything the commands read from or write to the process, so tests can stand in for it. */
2
+ export interface Io {
3
+ stdout(text: string): void;
4
+ stderr(text: string): void;
5
+ cwd: string;
6
+ env: Readonly<Record<string, string | undefined>>;
7
+ /** True when both stdin and stdout are terminals, so prompting makes sense. */
8
+ interactive: boolean;
9
+ }
10
+ export declare function processIo(): Io;
@@ -0,0 +1,28 @@
1
+ import { type MfeflowProject } from '@mfeflow/core';
2
+ /** Range first-party features declare for `@mfeflow/*` packages; the CLI swaps in the real one. */
3
+ export declare const PLACEHOLDER_RANGE = "latest";
4
+ interface LocalPackage {
5
+ dir: string;
6
+ built: boolean;
7
+ }
8
+ /** The `@mfeflow/*` packages of a checkout, and the `mfeflow` CLI, by name. */
9
+ export declare function scanCheckout(checkout: string): Map<string, LocalPackage>;
10
+ /**
11
+ * Turns the placeholder range of an `@mfeflow/*` (or `mfeflow`) dependency into the range the
12
+ * project should install: `link:<checkout>/packages/<name>` with `--local`, else `^<version>`.
13
+ */
14
+ export declare function createRangeRewriter(version: string, local?: string): (name: string, range: string) => string | undefined;
15
+ /** `--local none`: stop linking and go back to the published packages. */
16
+ export declare const LOCAL_NONE = "none";
17
+ /**
18
+ * The checkout a command links `@mfeflow/*` from, and the project recording it. An explicit
19
+ * `flag` (a path, or `none`) changes the project's mode; without one the project's own mode
20
+ * stays, so a project is never half linked and half published. Both sides are compared by real
21
+ * path, so a root and a checkout reached through different symlinks (`/var` -> `/private/var`)
22
+ * still get the right relative path.
23
+ */
24
+ export declare function chooseLocal(project: MfeflowProject, root: string, cwd: string, flag: string | undefined): {
25
+ checkout: string | undefined;
26
+ project: MfeflowProject;
27
+ };
28
+ export {};
@@ -0,0 +1,7 @@
1
+ export type FeatureOptions = Record<string, Record<string, unknown>>;
2
+ /** `--option feature.key=value` flags as `{ feature: { key: value } }`. `true` and `false` become booleans. */
3
+ export declare function parseFeatureOptions(flags: readonly string[]): FeatureOptions;
4
+ /** `later` wins over `earlier`, key by key. */
5
+ export declare function mergeOptions(earlier: FeatureOptions, later: FeatureOptions): FeatureOptions;
6
+ /** Options for a feature that is not part of this run are a typo or a stale preset, not something to ignore. */
7
+ export declare function assertOptionsAreForFeatures(options: FeatureOptions, features: readonly string[]): void;
@@ -0,0 +1,157 @@
1
+ import type { Issue } from '@mfeflow/contracts';
2
+ import { type Plan } from '@mfeflow/core';
3
+ import { type ErrorReport } from './exit.js';
4
+ import type { Finding } from './findings.js';
5
+ import { type CheckReport, type Summary } from './report.js';
6
+ export interface PlanReport {
7
+ features: {
8
+ name: string;
9
+ version: string;
10
+ }[];
11
+ changes: {
12
+ path: string;
13
+ action: 'create' | 'patch' | 'delete';
14
+ }[];
15
+ dependencies: {
16
+ file: string;
17
+ name: string;
18
+ range: string;
19
+ dev: boolean;
20
+ }[];
21
+ scripts: {
22
+ file: string;
23
+ name: string;
24
+ command: string;
25
+ }[];
26
+ warnings: string[];
27
+ }
28
+ export interface AppReport {
29
+ name: string;
30
+ kind: 'shell' | 'remote';
31
+ path: string;
32
+ port: number | null;
33
+ }
34
+ interface WriteReport {
35
+ ok: true;
36
+ dryRun: boolean;
37
+ root: string;
38
+ plan: PlanReport;
39
+ /** Files written, in plan order. Empty for a dry run. */
40
+ written: string[];
41
+ installed: boolean;
42
+ /** Unified diff of every change. Only present for a dry run. */
43
+ diff?: string;
44
+ }
45
+ export interface InitResult extends WriteReport {
46
+ command: 'init';
47
+ scope: string;
48
+ apps: AppReport[];
49
+ next: string[];
50
+ }
51
+ export interface AddResult extends WriteReport {
52
+ command: 'add';
53
+ kind: 'remote' | 'shell' | 'feature' | 'env-key';
54
+ /** What was added: an app name, or feature names. */
55
+ added: string[];
56
+ /** Features that were already part of the project. */
57
+ skipped: string[];
58
+ }
59
+ export interface FeatureInfo {
60
+ name: string;
61
+ version: string;
62
+ description: string;
63
+ always: boolean;
64
+ default: boolean;
65
+ installed: boolean;
66
+ }
67
+ export interface FeaturesResult {
68
+ ok: true;
69
+ command: 'features';
70
+ search: string | null;
71
+ features: FeatureInfo[];
72
+ }
73
+ /** What `doctor`, `validate` and `check-compat` print with `--json`. `ok` is false when any finding is an error. */
74
+ export interface ReportResult {
75
+ ok: boolean;
76
+ command: 'doctor' | 'validate' | 'check-compat';
77
+ root: string;
78
+ summary: Summary;
79
+ checks: CheckReport[];
80
+ findings: Finding[];
81
+ }
82
+ export interface ShellBudget {
83
+ name: string;
84
+ /** Gzip KB of the scripts index.html loads. */
85
+ initialKb: number;
86
+ scripts: number;
87
+ /** Your pages built as one app (budget.baselineKb), or null when not set. */
88
+ baselineKb: number | null;
89
+ /** baselineKb plus what the shell may add; null without a baseline. */
90
+ budgetKb: number | null;
91
+ /** Null when there is nothing to compare with. */
92
+ pass: boolean | null;
93
+ }
94
+ export interface RouteBudget {
95
+ route: string;
96
+ expose: string | null;
97
+ /** Entry plus the expose's synchronous JS, gzip KB. */
98
+ kb: number;
99
+ /** mfe.json budgets.initialJsKb, or null. */
100
+ budgetKb: number | null;
101
+ pass: boolean | null;
102
+ }
103
+ export interface RemoteBudget {
104
+ name: string;
105
+ entryKb: number;
106
+ /** What a remote may add (cost model, 10 KB). */
107
+ budgetKb: number;
108
+ pass: boolean;
109
+ routes: RouteBudget[];
110
+ }
111
+ export interface BudgetResult {
112
+ ok: boolean;
113
+ command: 'budget';
114
+ root: string;
115
+ model: {
116
+ lcpDeltaMs: number;
117
+ shellKb: number;
118
+ remoteKb: number;
119
+ };
120
+ shells: ShellBudget[];
121
+ remotes: RemoteBudget[];
122
+ findings: Finding[];
123
+ }
124
+ export interface DevApp {
125
+ name: string;
126
+ kind: 'shell' | 'remote';
127
+ port: number | null;
128
+ url: string | null;
129
+ command: string;
130
+ /** `overridden`: a server you run yourself, given with --remote. */
131
+ status: 'started' | 'overridden';
132
+ }
133
+ export interface DevResult {
134
+ ok: boolean;
135
+ command: 'dev';
136
+ root: string;
137
+ /** The generated dev registry the shell reads. */
138
+ registry: string;
139
+ apps: DevApp[];
140
+ overrides: Record<string, string>;
141
+ dryRun: boolean;
142
+ /** Stopped with Ctrl+C or a termination signal. */
143
+ interrupted: boolean;
144
+ }
145
+ export interface FailureResult {
146
+ ok: false;
147
+ error: ErrorReport;
148
+ exitCode: number;
149
+ }
150
+ export type CliResult = InitResult | AddResult | FeaturesResult | ReportResult | BudgetResult | DevResult | FailureResult;
151
+ /** The plan as plain data for `--json`, without file contents. Features are listed once. */
152
+ export declare function planReport(plan: Plan): PlanReport;
153
+ /** Checks a document the CLI printed with `--json`. Returns the problems; empty means valid. */
154
+ export declare function validateResult(value: unknown): Issue[];
155
+ /** Exit code for a result that printed normally: findings decide for the checks, Ctrl+C is 130. */
156
+ export declare function exitOf(result: CliResult): number;
157
+ export {};
@@ -0,0 +1,7 @@
1
+ import { PlanBuilder, type MfeflowProject, type Plan } from '@mfeflow/core';
2
+ /** Reads a project file by its `/`-separated path; undefined when it does not exist. */
3
+ export declare function projectReader(root: string): (file: string) => Promise<string | undefined>;
4
+ /** A plan builder that reads `root`, keeps dependency versions in the catalog and resolves `@mfeflow/*` ranges. */
5
+ export declare function createBuilder(root: string, checkout: string | undefined): PlanBuilder;
6
+ /** `plan` with the `mfeflow.json` change added, for the summary and the diff. */
7
+ export declare function withProjectChange(plan: Plan, project: MfeflowProject): Plan;
@@ -0,0 +1,19 @@
1
+ import { type FetchLike } from '@mfeflow/core';
2
+ /** The options a preset may carry: the same names as the flags of `init`. */
3
+ export interface Preset {
4
+ scope?: string;
5
+ remotes?: string[];
6
+ features?: string[];
7
+ packageManager?: string;
8
+ topology?: string;
9
+ options?: Record<string, Record<string, unknown>>;
10
+ }
11
+ /** Checks a parsed preset. Unknown keys are an error, so a typo does not silently do nothing. */
12
+ export declare function parsePreset(source: string, value: unknown): Preset;
13
+ export interface PresetSources {
14
+ cwd: string;
15
+ fetch: FetchLike;
16
+ readFile?: (file: string) => Promise<string>;
17
+ }
18
+ /** Loads a preset from a file path or an https URL. Plain http is refused. */
19
+ export declare function loadPreset(source: string, sources: PresetSources): Promise<Preset>;
package/dist/proc.d.ts ADDED
@@ -0,0 +1,79 @@
1
+ import { type ChildProcess } from 'node:child_process';
2
+ export interface DevProcess {
3
+ readonly pid: number | undefined;
4
+ onOutput(listener: (chunk: string) => void): void;
5
+ onExit(listener: (code: number | null) => void): void;
6
+ /** Ends the process and everything it started. Resolves once it is gone. */
7
+ stop(): Promise<void>;
8
+ }
9
+ export interface DevSpec {
10
+ command: string;
11
+ args: readonly string[];
12
+ cwd: string;
13
+ env: Record<string, string | undefined>;
14
+ }
15
+ export type SpawnDev = (spec: DevSpec) => DevProcess;
16
+ export interface DevHost {
17
+ spawn: SpawnDev;
18
+ /** Calls back on Ctrl+C or a termination signal. Returns a function that stops listening. */
19
+ onInterrupt(listener: () => void): () => void;
20
+ /** Calls back, debounced, when any of `paths` changes. Returns a function that stops watching. */
21
+ watch(paths: readonly string[], listener: () => void): () => void;
22
+ portFree(port: number): Promise<boolean>;
23
+ /** How often `dev` looks for a new remote's install to finish, and when it gives up. */
24
+ waitPolicy?: WaitPolicy;
25
+ }
26
+ export interface WaitPolicy {
27
+ intervalMs: number;
28
+ timeoutMs: number;
29
+ }
30
+ export declare const DEFAULT_WAIT: WaitPolicy;
31
+ type Kill = (pid: number, signal: NodeJS.Signals | 0) => void;
32
+ export interface TreeKiller {
33
+ platform: NodeJS.Platform;
34
+ kill: Kill;
35
+ /** Runs `taskkill` on Windows. */
36
+ taskkill(pid: number): Promise<void>;
37
+ graceMs: number;
38
+ }
39
+ export interface ResolveEnv {
40
+ /** The `PATH` value; undefined when the variable is not set. */
41
+ path: string | undefined;
42
+ /** Windows: the `PATHEXT` value. */
43
+ pathext?: string | undefined;
44
+ platform: NodeJS.Platform;
45
+ /** Is `file` a runnable file? */
46
+ isFile(file: string): boolean;
47
+ }
48
+ /**
49
+ * The absolute path of the first `command` found on the `PATH` entries, in order (on Windows with
50
+ * each `PATHEXT` extension). A command that has a folder in it is returned as given. Undefined when none is found.
51
+ */
52
+ export declare function resolveCommand(command: string, env: ResolveEnv): string | undefined;
53
+ /** Stops a process and its children: the process group on POSIX, `taskkill /T` on Windows. */
54
+ export declare function killTree(pid: number, how: TreeKiller): Promise<void>;
55
+ export declare function wrap(child: ChildProcess, killer: TreeKiller): DevProcess;
56
+ export interface ExitSource {
57
+ on(event: 'exit', listener: () => void): unknown;
58
+ }
59
+ export interface Reaper {
60
+ track(pid: number): void;
61
+ untrack(pid: number): void;
62
+ }
63
+ /**
64
+ * Last resort for dev servers still running when this process exits (a closed terminal, a
65
+ * crash): kills what is left, synchronously, because nothing async runs inside an `exit` handler.
66
+ */
67
+ export declare function createReaper(proc: ExitSource, killNow: (pid: number) => void): Reaper;
68
+ export declare const spawnDev: SpawnDev;
69
+ export declare function portFree(port: number): Promise<boolean>;
70
+ /** Watches files (through their folder, since an atomic save replaces the file) and folders. */
71
+ export declare function watchPaths(paths: readonly string[], listener: () => void): () => void;
72
+ export interface SignalSource {
73
+ on(signal: NodeJS.Signals, listener: () => void): unknown;
74
+ off(signal: NodeJS.Signals, listener: () => void): unknown;
75
+ }
76
+ /** Ctrl+C, `kill`, and a closed terminal (SIGHUP) all stop the dev servers. */
77
+ export declare function onInterrupt(listener: () => void, source?: SignalSource): () => void;
78
+ export declare function realDevHost(): DevHost;
79
+ export {};
@@ -0,0 +1,28 @@
1
+ export interface Choice {
2
+ value: string;
3
+ label: string;
4
+ hint?: string;
5
+ }
6
+ /** The prompts the commands use. The real one is clack; tests pass a scripted one. */
7
+ export interface Prompter {
8
+ text(options: {
9
+ message: string;
10
+ initialValue?: string;
11
+ validate?: (value: string) => string | undefined;
12
+ }): Promise<string>;
13
+ multiselect(options: {
14
+ message: string;
15
+ choices: readonly Choice[];
16
+ initialValues: readonly string[];
17
+ }): Promise<string[]>;
18
+ select(options: {
19
+ message: string;
20
+ choices: readonly Choice[];
21
+ initialValue?: string;
22
+ }): Promise<string>;
23
+ confirm(options: {
24
+ message: string;
25
+ initialValue?: boolean;
26
+ }): Promise<boolean>;
27
+ }
28
+ export declare const clackPrompter: Prompter;
@@ -0,0 +1,26 @@
1
+ import type { Check, Finding } from './findings.js';
2
+ import type { ProjectView } from './inspect.js';
3
+ export interface CheckReport {
4
+ id: string;
5
+ title: string;
6
+ status: 'pass' | 'warn' | 'fail' | 'skipped';
7
+ /** Why a skipped check did not run. */
8
+ skipped?: string;
9
+ findings: number;
10
+ }
11
+ export interface Summary {
12
+ errors: number;
13
+ warnings: number;
14
+ skipped: number;
15
+ fixed: number;
16
+ }
17
+ export interface Report {
18
+ checks: CheckReport[];
19
+ findings: Finding[];
20
+ }
21
+ export declare function runChecks(view: ProjectView, checks: readonly Check[]): Promise<Report>;
22
+ export declare function summarizeReport(report: Report, fixed: number): Summary;
23
+ /** Exit code of a report: 0 without errors, else the lowest code among the errors' ranges (project before build). */
24
+ export declare function exitForFindings(findings: readonly Finding[]): number;
25
+ /** The human form: one line per check, then each finding with its fix and docs link. */
26
+ export declare function formatReport(report: Report, summary: Summary): string;
@@ -0,0 +1,17 @@
1
+ import type { FetchLike } from '@mfeflow/core';
2
+ import { type AssetRoots } from './assets.js';
3
+ import { type Io } from './io.js';
4
+ import { type DevHost } from './proc.js';
5
+ import { type Prompter } from './prompter.js';
6
+ import { type System } from './system.js';
7
+ /** Everything a command touches outside its own arguments. Tests replace the parts they watch. */
8
+ export interface Runtime {
9
+ io: Io;
10
+ prompter: Prompter;
11
+ system: System;
12
+ fetch: FetchLike;
13
+ roots: AssetRoots;
14
+ /** Processes, signals and file watching for `mfeflow dev`. Defaults to the real ones. */
15
+ dev?: DevHost;
16
+ }
17
+ export declare function realRuntime(io?: Io): Runtime;
@@ -0,0 +1,11 @@
1
+ /**
2
+ * The packages `@mfeflow/rsbuild` shares as singletons, by specifier. A test keeps this equal
3
+ * to its `DEFAULT_SINGLETONS`; the CLI does not depend on the preset package at run time.
4
+ */
5
+ export declare const SINGLETON_SPECIFIERS: readonly string[];
6
+ /** `react-dom/client` → `react-dom`, `@mfeflow/theme/react` → `@mfeflow/theme`. */
7
+ export declare function packageNameOf(specifier: string): string;
8
+ /** The singleton packages, each once. */
9
+ export declare const SINGLETON_PACKAGES: readonly string[];
10
+ /** The specifiers of a singleton package, for messages: `react` → react, react/jsx-runtime, … */
11
+ export declare function specifiersOf(name: string): string[];
@@ -0,0 +1,17 @@
1
+ export declare const KB = 1024;
2
+ export declare const kb: (bytes: number) => number;
3
+ /** Gzip size at level 6, what a CDN sends. Undefined when the file is not there. */
4
+ export declare function gzipBytes(file: string): Promise<number | undefined>;
5
+ export interface ShellSize {
6
+ files: string[];
7
+ bytes: number;
8
+ }
9
+ /** Scripts `dist/index.html` loads, as paths inside dist, and their gzip size. */
10
+ export declare function shellInitial(dist: string): Promise<ShellSize | undefined>;
11
+ export interface RemoteSize {
12
+ entryBytes: number;
13
+ /** expose (`./App`) → gzip bytes of the files it loads before it renders, without the entry. */
14
+ exposes: Record<string, number>;
15
+ }
16
+ /** Entry script and each expose's synchronous JS, read from the remote's `mf-manifest.json`. */
17
+ export declare function remoteSizes(dist: string): Promise<RemoteSize | undefined>;
@@ -0,0 +1,20 @@
1
+ import type { Io } from './io.js';
2
+ export interface RunResult {
3
+ code: number;
4
+ output: string;
5
+ }
6
+ /** Runs a program and collects what it prints. Rejects only when it cannot be started. */
7
+ export type Run = (command: string, args: readonly string[], options: {
8
+ cwd: string;
9
+ onOutput?: (chunk: string) => void;
10
+ }) => Promise<RunResult>;
11
+ /** `env` without the variables that would bind a child's git to another repository. */
12
+ export declare function isolatedEnv(env?: NodeJS.ProcessEnv): NodeJS.ProcessEnv;
13
+ export declare const run: Run;
14
+ export interface System {
15
+ /** Runs `<packageManager> install` in `root`. Throws with the tail of the output when it fails. */
16
+ install(root: string, packageManager: string, verbose: boolean): Promise<void>;
17
+ /** `git init` unless `root` is already inside a work tree. Returns whether it ran. */
18
+ gitInit(root: string): Promise<boolean>;
19
+ }
20
+ export declare function createSystem(io: Pick<Io, 'stderr'>, runner?: Run): System;
@@ -0,0 +1,8 @@
1
+ export interface PackageJson {
2
+ name: string;
3
+ version: string;
4
+ }
5
+ /** True when `value` has a string `name` and `version`. */
6
+ export declare function isPackageJson(value: unknown): value is PackageJson;
7
+ /** Name and version from this package's package.json (works from src/ and dist/). */
8
+ export declare function readPackageJson(): PackageJson;
@@ -0,0 +1,26 @@
1
+ import { type MfeflowProject, type Plan } from '@mfeflow/core';
2
+ import type { Globals } from './args.js';
3
+ import type { Runtime } from './runtime.js';
4
+ export interface WriteRequest {
5
+ rt: Runtime;
6
+ globals: Globals;
7
+ root: string;
8
+ plan: Plan;
9
+ /** The project as it will be after the plan, saved to `mfeflow.json` in the same transaction. */
10
+ project: MfeflowProject;
11
+ /** Runs once the files are written, before the install (`git init` for hooks that need a repo). */
12
+ afterWrite?: () => Promise<void>;
13
+ }
14
+ export interface WriteOutcome {
15
+ dryRun: boolean;
16
+ /** The plan including the `mfeflow.json` change. */
17
+ shown: Plan;
18
+ written: string[];
19
+ installed: boolean;
20
+ diff?: string;
21
+ }
22
+ /**
23
+ * Shows the plan and, unless this is a dry run, writes it: all or nothing, with
24
+ * the install inside the transaction. Backing out at the prompt writes nothing.
25
+ */
26
+ export declare function writePlan(request: WriteRequest): Promise<WriteOutcome>;
package/package.json CHANGED
@@ -1,6 +1,69 @@
1
1
  {
2
2
  "name": "mfeflow",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
3
+ "version": "0.2.0",
4
+ "description": "Create and grow a React micro-frontend platform: mfeflow init, add remote, add <feature>.",
5
+ "keywords": [
6
+ "micro-frontend",
7
+ "micro-frontends",
8
+ "mfe",
9
+ "module-federation",
10
+ "module-federation-2",
11
+ "react",
12
+ "rsbuild",
13
+ "rspack",
14
+ "cli",
15
+ "scaffolding",
16
+ "boilerplate"
17
+ ],
18
+ "homepage": "https://mfeflow.github.io/mfeflow/",
19
+ "bugs": "https://github.com/mfeflow/mfeflow/issues",
20
+ "repository": {
21
+ "type": "git",
22
+ "url": "git+https://github.com/mfeflow/mfeflow.git",
23
+ "directory": "packages/cli"
24
+ },
25
+ "license": "MIT",
26
+ "author": "Sanjeev Sharma <speedsharma.ai@gmail.com>",
27
+ "type": "module",
28
+ "bin": {
29
+ "mfeflow": "./bin/mfeflow.js"
30
+ },
31
+ "exports": {
32
+ ".": {
33
+ "types": "./dist/index.d.ts",
34
+ "import": "./dist/index.js",
35
+ "default": "./dist/index.js"
36
+ }
37
+ },
38
+ "files": [
39
+ "assets",
40
+ "bin",
41
+ "dist"
42
+ ],
43
+ "engines": {
44
+ "node": ">=22.12"
45
+ },
46
+ "publishConfig": {
47
+ "access": "public",
48
+ "provenance": true
49
+ },
50
+ "dependencies": {
51
+ "@clack/prompts": "1.8.1",
52
+ "@mfeflow/contracts": "0.2.0",
53
+ "@mfeflow/core": "0.2.0",
54
+ "citty": "0.2.2",
55
+ "semver": "7.8.5",
56
+ "yaml": "2.9.1"
57
+ },
58
+ "devDependencies": {
59
+ "@mfeflow/rsbuild": "0.2.0",
60
+ "@rslib/core": "1.0.5",
61
+ "@types/node": "26.6.6",
62
+ "@types/semver": "7.8.0",
63
+ "typescript": "7.0.2"
64
+ },
65
+ "scripts": {
66
+ "build": "node scripts/copy-assets.mjs && rslib build",
67
+ "typecheck": "tsc --noEmit"
68
+ }
6
69
  }