mfeflow 0.0.0-stage → 0.1.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 (120) 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 +378 -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/git-hooks/feature.json +42 -0
  25. package/assets/features/git-hooks/files/commitlint.config.js +1 -0
  26. package/assets/features/git-hooks/files/lefthook.yml +9 -0
  27. package/assets/features/lint-oxlint/feature.json +60 -0
  28. package/assets/features/lint-oxlint/files/oxlintrc.json +5 -0
  29. package/assets/features/lint-oxlint/files/prettierignore +5 -0
  30. package/assets/features/lint-oxlint/files/prettierrc.json +5 -0
  31. package/assets/features/telemetry-otel/feature.json +159 -0
  32. package/assets/features/telemetry-otel/files/init.ts.eta +53 -0
  33. package/assets/features/telemetry-otel/files/remote-telemetry-test.ts.eta +13 -0
  34. package/assets/features/telemetry-otel/files/remote-telemetry.ts.eta +11 -0
  35. package/assets/features/telemetry-otel/files/shell-init-test.ts.eta +36 -0
  36. package/assets/features/testing/feature.json +202 -0
  37. package/assets/features/testing/files/handlers.ts +5 -0
  38. package/assets/features/testing/files/playwright.config.ts +30 -0
  39. package/assets/features/testing/files/root-vitest.config.ts +6 -0
  40. package/assets/features/testing/files/sample-handlers-test.ts +16 -0
  41. package/assets/features/testing/files/sample-remote-test.tsx +12 -0
  42. package/assets/features/testing/files/sample-shell-test.tsx +11 -0
  43. package/assets/features/testing/files/sample-smoke-e2e.ts +53 -0
  44. package/assets/features/testing/files/server.ts +4 -0
  45. package/assets/features/testing/files/setup.ts +18 -0
  46. package/assets/features/testing/files/vitest.config.ts +11 -0
  47. package/assets/features/tsconfig/feature.json +78 -0
  48. package/assets/features/tsconfig/files/root.tsconfig.json +4 -0
  49. package/assets/features/tsconfig/files/tsconfig.json +5 -0
  50. package/assets/features/url-factory/feature.json +30 -0
  51. package/assets/features/url-factory/files/endpoints.ts +12 -0
  52. package/assets/templates/monorepo/feature.json +295 -0
  53. package/assets/templates/monorepo/files/remote/mfe.json.eta +7 -0
  54. package/assets/templates/monorepo/files/remote/package.json.eta +6 -0
  55. package/assets/templates/monorepo/files/remote/rsbuild.config.ts.eta +7 -0
  56. package/assets/templates/monorepo/files/remote/src/bootstrap.tsx.eta +6 -0
  57. package/assets/templates/monorepo/files/remote/src/env.d.ts.eta +1 -0
  58. package/assets/templates/monorepo/files/remote/src/exposed/App.tsx.eta +18 -0
  59. package/assets/templates/monorepo/files/remote/src/index.ts.eta +2 -0
  60. package/assets/templates/monorepo/files/root/AGENTS.md.eta +17 -0
  61. package/assets/templates/monorepo/files/root/README.md.eta +52 -0
  62. package/assets/templates/monorepo/files/root/gitignore.eta +11 -0
  63. package/assets/templates/monorepo/files/root/nvmrc.eta +1 -0
  64. package/assets/templates/monorepo/files/root/package.json.eta +9 -0
  65. package/assets/templates/monorepo/files/root/pnpm-workspace.yaml.eta +7 -0
  66. package/assets/templates/monorepo/files/root/turbo.json.eta +16 -0
  67. package/assets/templates/monorepo/files/shell/mfe.json.eta +7 -0
  68. package/assets/templates/monorepo/files/shell/package.json.eta +6 -0
  69. package/assets/templates/monorepo/files/shell/rsbuild.config.ts.eta +74 -0
  70. package/assets/templates/monorepo/files/shell/src/App.tsx.eta +46 -0
  71. package/assets/templates/monorepo/files/shell/src/bootstrap.tsx.eta +24 -0
  72. package/assets/templates/monorepo/files/shell/src/env.d.ts.eta +1 -0
  73. package/assets/templates/monorepo/files/shell/src/index.ts.eta +4 -0
  74. package/assets/templates/monorepo/files/shell/src/pages/Home.tsx.eta +12 -0
  75. package/assets/templates/monorepo/files/shell/src/remotes/route-table.tsx.eta +12 -0
  76. package/assets/templates/monorepo/glue/registry-entry.json.eta +5 -0
  77. package/assets/templates/monorepo/glue/route.tsx.eta +5 -0
  78. package/bin/mfeflow.js +4 -0
  79. package/dist/args.d.ts +68 -0
  80. package/dist/assets.d.ts +13 -0
  81. package/dist/catalog.d.ts +24 -0
  82. package/dist/checks/compat.d.ts +2 -0
  83. package/dist/checks/config.d.ts +8 -0
  84. package/dist/checks/env.d.ts +6 -0
  85. package/dist/checks/index.d.ts +6 -0
  86. package/dist/checks/plugins.d.ts +4 -0
  87. package/dist/checks/singletons.d.ts +9 -0
  88. package/dist/checks/workspace.d.ts +2 -0
  89. package/dist/cli.d.ts +11 -0
  90. package/dist/commands/add.d.ts +84 -0
  91. package/dist/commands/budget.d.ts +56 -0
  92. package/dist/commands/dev.d.ts +62 -0
  93. package/dist/commands/doctor.d.ts +141 -0
  94. package/dist/commands/env-key.d.ts +4 -0
  95. package/dist/commands/features.d.ts +53 -0
  96. package/dist/commands/init.d.ts +102 -0
  97. package/dist/devregistry.d.ts +19 -0
  98. package/dist/exit.d.ts +31 -0
  99. package/dist/findings.d.ts +35 -0
  100. package/dist/glue.d.ts +41 -0
  101. package/dist/help.d.ts +12 -0
  102. package/dist/index.d.ts +8 -0
  103. package/dist/index.js +4154 -0
  104. package/dist/inspect.d.ts +57 -0
  105. package/dist/io.d.ts +10 -0
  106. package/dist/local.d.ts +28 -0
  107. package/dist/options.d.ts +7 -0
  108. package/dist/output.d.ts +157 -0
  109. package/dist/pipeline.d.ts +7 -0
  110. package/dist/preset.d.ts +19 -0
  111. package/dist/proc.d.ts +79 -0
  112. package/dist/prompter.d.ts +28 -0
  113. package/dist/report.d.ts +26 -0
  114. package/dist/runtime.d.ts +17 -0
  115. package/dist/singletons.d.ts +11 -0
  116. package/dist/sizes.d.ts +17 -0
  117. package/dist/system.d.ts +20 -0
  118. package/dist/version.d.ts +8 -0
  119. package/dist/write.d.ts +26 -0
  120. package/package.json +66 -3
@@ -0,0 +1,7 @@
1
+ packages:
2
+ - apps/*
3
+ - packages/*
4
+
5
+ # Dependencies allowed to run install scripts.
6
+ allowBuilds:
7
+ lefthook: true
@@ -0,0 +1,16 @@
1
+ {
2
+ "$schema": "https://turborepo.com/schema.json",
3
+ "tasks": {
4
+ "build": {
5
+ "dependsOn": ["^build"],
6
+ "outputs": ["dist/**"]
7
+ },
8
+ "typecheck": {
9
+ "dependsOn": ["^build"]
10
+ },
11
+ "dev": {
12
+ "cache": false,
13
+ "persistent": true
14
+ }
15
+ }
16
+ }
@@ -0,0 +1,7 @@
1
+ {
2
+ "name": "<%= it.app.name %>",
3
+ "owner": "team-<%= it.app.name %>",
4
+ "shells": [],
5
+ "routes": [],
6
+ "exposes": []
7
+ }
@@ -0,0 +1,6 @@
1
+ {
2
+ "name": "<%= it.scope %>/<%= it.app.name %>",
3
+ "version": "0.0.0",
4
+ "private": true,
5
+ "type": "module"
6
+ }
@@ -0,0 +1,74 @@
1
+ import fs from 'node:fs';
2
+ import path from 'node:path';
3
+ import { defineConfig, mergeRsbuildConfig } from '@rsbuild/core';
4
+ import { createShellConfig } from '@mfeflow/rsbuild';
5
+
6
+ interface DevEntry {
7
+ manifestUrl: string;
8
+ version: string;
9
+ }
10
+
11
+ function readEntry(file: string): DevEntry {
12
+ const value: unknown = JSON.parse(fs.readFileSync(file, 'utf8'));
13
+ if (typeof value !== 'object' || value === null || !('manifestUrl' in value)) {
14
+ throw new Error(`${file} has no manifestUrl`);
15
+ }
16
+ const { manifestUrl } = value;
17
+ if (typeof manifestUrl !== 'string') throw new Error(`${file}: manifestUrl must be text`);
18
+ const version = 'version' in value && typeof value.version === 'string' ? value.version : 'dev';
19
+ return { manifestUrl, version };
20
+ }
21
+
22
+ function readNames(dir: string): string[] {
23
+ try {
24
+ return fs.readdirSync(dir);
25
+ } catch (error) {
26
+ // A project with no remotes yet has nothing to list.
27
+ if (error instanceof Error && 'code' in error && error.code === 'ENOENT') return [];
28
+ throw error;
29
+ }
30
+ }
31
+
32
+ // The shell finds its remotes through the registry. In dev that is registry/envs/dev, which
33
+ // points at each remote's dev server, inlined so the shell needs no registry server.
34
+ // `mfeflow dev` writes the merged registry (apps from mfeflow.json, --remote overrides) to
35
+ // MFEFLOW_DEV_REGISTRY and rewrites it when the project changes; plain `pnpm dev` reads the folder.
36
+ const registryDir = path.resolve(process.cwd(), '../../registry/envs/dev');
37
+ const generated = process.env.MFEFLOW_DEV_REGISTRY;
38
+
39
+ function readGenerated(file: string): unknown {
40
+ return JSON.parse(fs.readFileSync(file, 'utf8'));
41
+ }
42
+
43
+ function devRegistry(): string {
44
+ const remotes = generated
45
+ ? readGenerated(generated)
46
+ : {
47
+ remotes: readNames(registryDir)
48
+ .filter((file) => file.endsWith('.json'))
49
+ .toSorted()
50
+ .map((file) => {
51
+ const { manifestUrl, version } = readEntry(path.join(registryDir, file));
52
+ return { name: file.slice(0, -'.json'.length), entry: manifestUrl, version };
53
+ }),
54
+ };
55
+ return `window.__MFE_REGISTRY__=${JSON.stringify(remotes).replaceAll('<', '\\u003c')}`;
56
+ }
57
+
58
+ export default defineConfig(({ command }) =>
59
+ mergeRsbuildConfig(
60
+ // snapshot: registry entries carry a manifest URL until deploys also publish remoteEntry.
61
+ createShellConfig({ name: '<%= it.app.name %>', port: <%= it.app.port %>, snapshot: true }),
62
+ defineConfig({
63
+ // A new or changed remote restarts the dev server, which reads the registry again.
64
+ dev: { watchFiles: { paths: generated ?? registryDir, type: 'restart' } },
65
+ html: {
66
+ title: '<%= it.app.name %>',
67
+ tags:
68
+ command === 'dev'
69
+ ? [{ tag: 'script', children: devRegistry(), head: true, append: false }]
70
+ : [],
71
+ },
72
+ }),
73
+ ),
74
+ );
@@ -0,0 +1,46 @@
1
+ import { useEffect, useState, type MouseEvent } from 'react';
2
+ import { DevOverlay } from '@mfeflow/runtime';
3
+ import { routes, type RouteEntry } from './remotes/route-table';
4
+
5
+ function usePath(): [string, (event: MouseEvent<HTMLAnchorElement>) => void] {
6
+ const [path, setPath] = useState(location.pathname);
7
+ useEffect(() => {
8
+ const onPop = () => {
9
+ setPath(location.pathname);
10
+ };
11
+ addEventListener('popstate', onPop);
12
+ return () => {
13
+ removeEventListener('popstate', onPop);
14
+ };
15
+ }, []);
16
+ const go = (event: MouseEvent<HTMLAnchorElement>) => {
17
+ event.preventDefault();
18
+ history.pushState(null, '', event.currentTarget.href);
19
+ setPath(location.pathname);
20
+ };
21
+ return [path, go];
22
+ }
23
+
24
+ const matches = (route: RouteEntry, path: string) =>
25
+ route.path === '/' ? path === '/' : path === route.path || path.startsWith(`${route.path}/`);
26
+
27
+ export function App() {
28
+ const [path, go] = usePath();
29
+ const current = routes.find((route) => matches(route, path));
30
+ return (
31
+ <>
32
+ <header>
33
+ <strong><%= it.app.name %></strong>
34
+ <nav>
35
+ {routes.map((route) => (
36
+ <a key={route.path} href={route.path} onClick={go}>
37
+ {route.path === '/' ? 'home' : route.path.slice(1)}
38
+ </a>
39
+ ))}
40
+ </nav>
41
+ </header>
42
+ <main>{current ? <current.Component /> : <p>Nothing at {path}.</p>}</main>
43
+ <DevOverlay enabled={process.env.NODE_ENV !== 'production'} />
44
+ </>
45
+ );
46
+ }
@@ -0,0 +1,24 @@
1
+ import { createRoot } from 'react-dom/client';
2
+ import {
3
+ applyDevOverrides,
4
+ loadRegistryIndex,
5
+ registerRemotesFromIndex,
6
+ reportRemoteError,
7
+ } from '@mfeflow/runtime';
8
+ import { App } from './App';
9
+
10
+ const DEV = process.env.NODE_ENV !== 'production';
11
+
12
+ export async function main(): Promise<void> {
13
+ try {
14
+ // Dev only: ?mfe-override=cart@http://localhost:4312/mf-manifest.json points a remote at your build.
15
+ const index = applyDevOverrides(await loadRegistryIndex(), { enabled: DEV });
16
+ // In dev the registry points at the remotes' dev servers; elsewhere remotes share the shell's origin.
17
+ const devOrigins = DEV ? index.remotes.map((remote) => new URL(remote.entry).origin) : [];
18
+ registerRemotesFromIndex(index, { allowedOrigins: [location.origin, ...devOrigins] });
19
+ } catch (error) {
20
+ reportRemoteError('<%= it.app.name %>', 'registry', error);
21
+ }
22
+ const el = document.getElementById('root');
23
+ if (el) createRoot(el).render(<App />);
24
+ }
@@ -0,0 +1 @@
1
+ /// <reference types="@rsbuild/core/types" />
@@ -0,0 +1,4 @@
1
+ import { main } from './bootstrap';
2
+
3
+ // Shared singletons are eager in the shell, so there is no async boundary to wait behind.
4
+ void main();
@@ -0,0 +1,12 @@
1
+ import { RemoteBoundary, registeredRemotes } from '@mfeflow/runtime';
2
+
3
+ // Shows every remote the registry listed, so a new remote appears here without a code change.
4
+ export function Home() {
5
+ const remotes = registeredRemotes();
6
+ if (remotes.length === 0) return <p>No remotes yet. Run `mfeflow add remote &lt;name&gt;`.</p>;
7
+ return remotes.map((remote) => (
8
+ <section key={remote.name}>
9
+ <RemoteBoundary id={`${remote.name}/App`} loading={<p>Loading {remote.name}…</p>} />
10
+ </section>
11
+ ));
12
+ }
@@ -0,0 +1,12 @@
1
+ import type { ComponentType } from 'react';
2
+ import { Home } from '../pages/Home';
3
+
4
+ export interface RouteEntry {
5
+ path: string;
6
+ Component: ComponentType;
7
+ }
8
+
9
+ // `mfeflow add remote <name>` adds a route here.
10
+ export const routes: RouteEntry[] = [
11
+ { path: '/', Component: Home },
12
+ ];
@@ -0,0 +1,5 @@
1
+ {
2
+ "version": "<%= it.version %>",
3
+ "manifestUrl": "<%= it.manifestUrl %>",
4
+ "owner": "team-<%= it.name %>"
5
+ }
@@ -0,0 +1,5 @@
1
+ import { RemoteBoundary } from '@mfeflow/runtime';
2
+
3
+ export function <%= it.component %>() {
4
+ return <RemoteBoundary id="<%= it.name %>/App" loading={<p>Loading <%= it.name %>…</p>} />;
5
+ }
package/bin/mfeflow.js ADDED
@@ -0,0 +1,4 @@
1
+ #!/usr/bin/env node
2
+ import { main } from '../dist/index.js';
3
+
4
+ process.exitCode = await main(process.argv.slice(2));
package/dist/args.d.ts ADDED
@@ -0,0 +1,68 @@
1
+ import type { ArgsDef } from 'citty';
2
+ /** Flags every command takes. */
3
+ export declare const GLOBAL_ARGS: {
4
+ readonly yes: {
5
+ readonly type: 'boolean';
6
+ readonly alias: 'y';
7
+ readonly description: 'Take the defaults and never prompt';
8
+ };
9
+ readonly preset: {
10
+ readonly type: 'string';
11
+ readonly description: 'Replay options from a JSON file or an https URL';
12
+ readonly valueHint: 'file|url';
13
+ };
14
+ readonly json: {
15
+ readonly type: 'boolean';
16
+ readonly description: 'Print one JSON document on stdout, nothing else';
17
+ };
18
+ readonly 'dry-run': {
19
+ readonly type: 'boolean';
20
+ readonly description: 'Print the summary and diff, write nothing';
21
+ };
22
+ readonly cwd: {
23
+ readonly type: 'string';
24
+ readonly description: 'Run as if started in this folder';
25
+ readonly valueHint: 'dir';
26
+ };
27
+ readonly install: {
28
+ readonly type: 'boolean';
29
+ readonly default: true;
30
+ readonly description: 'Run the package manager after writing (--no-install to skip)';
31
+ };
32
+ readonly verbose: {
33
+ readonly type: 'boolean';
34
+ readonly description: 'Show package manager output and stack traces';
35
+ };
36
+ readonly local: {
37
+ readonly type: 'string';
38
+ readonly description: 'Link @mfeflow/* from a built mfeflow checkout (env MFEFLOW_LOCAL)';
39
+ readonly valueHint: 'path';
40
+ };
41
+ readonly help: {
42
+ readonly type: 'boolean';
43
+ readonly alias: 'h';
44
+ readonly description: 'Show this help';
45
+ };
46
+ };
47
+ export interface Globals {
48
+ yes: boolean;
49
+ preset: string | undefined;
50
+ json: boolean;
51
+ dryRun: boolean;
52
+ cwd: string;
53
+ install: boolean;
54
+ verbose: boolean;
55
+ local: string | undefined;
56
+ /** A person is at a terminal and has not opted out with --yes or --json. */
57
+ canPrompt: boolean;
58
+ }
59
+ type Parsed = Record<string, unknown>;
60
+ export declare function text(args: Parsed, key: string): string | undefined;
61
+ export declare function readGlobals(args: Parsed, env: Readonly<Record<string, string | undefined>>, cwd: string, interactive?: boolean): Globals;
62
+ /** Throws E107 for a flag the command does not declare; citty would quietly treat it as a boolean. */
63
+ export declare function assertKnownFlags(raw: readonly string[], defs: ArgsDef): void;
64
+ /** Every value of a repeatable flag (`--option a=1 --option b=2`); citty keeps only the last. */
65
+ export declare function repeated(raw: readonly string[], name: string): string[];
66
+ /** A comma-separated flag or preset value as a list, trimmed, without empties. */
67
+ export declare function splitList(value: string | readonly string[] | undefined): string[] | undefined;
68
+ export {};
@@ -0,0 +1,13 @@
1
+ export interface AssetRoots {
2
+ /** Folder with one sub-folder per optional first-party feature. */
3
+ features: string;
4
+ /** Folder with the project templates; `monorepo` holds the `core` feature. */
5
+ templates: string;
6
+ }
7
+ /**
8
+ * Published, the features and templates are copied next to `dist` (`assets/`).
9
+ * In a checkout they sit at the repository root, two levels above this package.
10
+ * Code running from `src` (the tests) always reads the checkout, so an edit to a
11
+ * template is seen without a build, and a stale `assets/` left by one never is.
12
+ */
13
+ export declare function assetRoots(from?: string, exists?: (dir: string) => boolean): AssetRoots;
@@ -0,0 +1,24 @@
1
+ import { type FetchLike, type LoadedFeature } from '@mfeflow/core';
2
+ import type { AssetRoots } from './assets.js';
3
+ export interface FirstPartyFeature {
4
+ name: string;
5
+ /** Part of every project; not offered as a choice. */
6
+ always: boolean;
7
+ /** Selected by default in `init`. */
8
+ default: boolean;
9
+ }
10
+ /** The features that ship with mfeflow, in the order they are applied. */
11
+ export declare const FIRST_PARTY: readonly FirstPartyFeature[];
12
+ export declare function isFirstParty(name: string): boolean;
13
+ /** The first-party features a user may pick, in catalogue order. */
14
+ export declare const OPTIONAL_FEATURES: readonly string[];
15
+ export declare const DEFAULT_FEATURES: readonly string[];
16
+ export interface FeatureSource {
17
+ roots: AssetRoots;
18
+ registries?: Record<string, string> | undefined;
19
+ fetch?: FetchLike | undefined;
20
+ /** The version to ask a registry for, when its URL template can pin one. */
21
+ version?: string | undefined;
22
+ }
23
+ /** A first-party feature by name, or `@namespace/name` from a configured registry. */
24
+ export declare function loadAny(name: string, source: FeatureSource): Promise<LoadedFeature>;
@@ -0,0 +1,2 @@
1
+ import { type Check } from '../findings.js';
2
+ export declare const compat: Check;
@@ -0,0 +1,8 @@
1
+ import { type Check } from '../findings.js';
2
+ export declare const appsOnDisk: Check;
3
+ export declare const mfeJson: Check;
4
+ export declare const ports: Check;
5
+ export declare const routes: Check;
6
+ export declare const registry: Check;
7
+ export declare const localCheckout: Check;
8
+ export declare const drift: Check;
@@ -0,0 +1,6 @@
1
+ import { type Check } from '../findings.js';
2
+ export declare const envExample: Check;
3
+ export declare const runtimeConfig: Check;
4
+ export declare const publicPrefix: Check;
5
+ export declare const turboEnv: Check;
6
+ export declare const envChecks: readonly Check[];
@@ -0,0 +1,6 @@
1
+ import type { Check } from '../findings.js';
2
+ /** What `mfeflow validate` runs: the files against their schemas and each other. */
3
+ export declare const VALIDATE_CHECKS: readonly Check[];
4
+ export declare const COMPAT_CHECKS: readonly Check[];
5
+ /** Every check `mfeflow doctor` runs, in order. */
6
+ export declare const DOCTOR_CHECKS: readonly Check[];
@@ -0,0 +1,4 @@
1
+ import { type Check } from '../findings.js';
2
+ /** Module specifiers a source file imports, requires or dynamically imports at run time: type-only imports are left out. */
3
+ export declare function importsOf(code: string): string[];
4
+ export declare const runtimePlugins: Check;
@@ -0,0 +1,9 @@
1
+ import { type Check, type Finding } from '../findings.js';
2
+ /**
3
+ * The comparable version behind an installed version or a declared range. Undefined for a spec
4
+ * that names no version (`link:`, `workspace:*`, `catalog:`, `latest`): it is only known after install.
5
+ */
6
+ export declare function comparable(version: string): string | undefined;
7
+ /** The warning for specs that cannot be compared before install. */
8
+ export declare function unresolved(check: string, code: 'E301' | 'E302', name: string, specs: readonly string[]): Finding;
9
+ export declare const singletonVersions: Check;
@@ -0,0 +1,2 @@
1
+ import { type Check } from '../findings.js';
2
+ export declare const workspaceVersions: Check;
package/dist/cli.d.ts ADDED
@@ -0,0 +1,11 @@
1
+ import { type CommandHelp } from './help.js';
2
+ import { type CliResult } from './output.js';
3
+ import { type Runtime } from './runtime.js';
4
+ type Handler = (rt: Runtime, args: Record<string, unknown>, raw: readonly string[]) => Promise<CliResult>;
5
+ interface Command extends CommandHelp {
6
+ run: Handler;
7
+ }
8
+ export declare const COMMANDS: readonly Command[];
9
+ /** Runs the CLI with `argv` (without node and the script) and returns the exit code. */
10
+ export declare function main(argv: readonly string[], rt?: Runtime): Promise<number>;
11
+ export {};
@@ -0,0 +1,84 @@
1
+ import { type AddResult } from '../output.js';
2
+ import type { Runtime } from '../runtime.js';
3
+ export declare const ADD_ARGS: {
4
+ readonly yes: {
5
+ readonly type: 'boolean';
6
+ readonly alias: 'y';
7
+ readonly description: 'Take the defaults and never prompt';
8
+ };
9
+ readonly preset: {
10
+ readonly type: 'string';
11
+ readonly description: 'Replay options from a JSON file or an https URL';
12
+ readonly valueHint: 'file|url';
13
+ };
14
+ readonly json: {
15
+ readonly type: 'boolean';
16
+ readonly description: 'Print one JSON document on stdout, nothing else';
17
+ };
18
+ readonly 'dry-run': {
19
+ readonly type: 'boolean';
20
+ readonly description: 'Print the summary and diff, write nothing';
21
+ };
22
+ readonly cwd: {
23
+ readonly type: 'string';
24
+ readonly description: 'Run as if started in this folder';
25
+ readonly valueHint: 'dir';
26
+ };
27
+ readonly install: {
28
+ readonly type: 'boolean';
29
+ readonly default: true;
30
+ readonly description: 'Run the package manager after writing (--no-install to skip)';
31
+ };
32
+ readonly verbose: {
33
+ readonly type: 'boolean';
34
+ readonly description: 'Show package manager output and stack traces';
35
+ };
36
+ readonly local: {
37
+ readonly type: 'string';
38
+ readonly description: 'Link @mfeflow/* from a built mfeflow checkout (env MFEFLOW_LOCAL)';
39
+ readonly valueHint: 'path';
40
+ };
41
+ readonly help: {
42
+ readonly type: 'boolean';
43
+ readonly alias: 'h';
44
+ readonly description: 'Show this help';
45
+ };
46
+ readonly port: {
47
+ readonly type: 'string';
48
+ readonly description: 'Dev server port for a new app (default: the next free one)';
49
+ readonly valueHint: 'n';
50
+ };
51
+ readonly option: {
52
+ readonly type: 'string';
53
+ readonly description: 'A feature option, repeatable: --option feature.key=value';
54
+ readonly valueHint: 'f.k=v';
55
+ };
56
+ readonly scope: {
57
+ readonly type: 'string';
58
+ readonly description: 'add env-key: server (env.ts, .env.example) or runtime (runtime-config files)';
59
+ readonly valueHint: 'server|runtime';
60
+ };
61
+ readonly schema: {
62
+ readonly type: 'string';
63
+ readonly description: 'add env-key: the validator expression, like "z.url()"';
64
+ readonly valueHint: 'expr';
65
+ };
66
+ readonly value: {
67
+ readonly type: 'string';
68
+ readonly description: 'add env-key: the placeholder written to .env.example or the config files';
69
+ readonly valueHint: 'text';
70
+ };
71
+ readonly app: {
72
+ readonly type: 'string';
73
+ readonly description: 'add env-key: exactly this app (default: every app; runtime: every shell)';
74
+ readonly valueHint: 'name';
75
+ };
76
+ readonly group: {
77
+ readonly type: 'string';
78
+ readonly description: 'add env-key: root, every shell or every remote, instead of --app';
79
+ readonly valueHint: 'root|shell|remotes';
80
+ };
81
+ };
82
+ /** First-party features in the order `init` applies them, then the rest as typed: a feature may edit another's files. */
83
+ export declare function inCatalogueOrder(names: readonly string[]): string[];
84
+ export declare function runAdd(rt: Runtime, args: Record<string, unknown>, raw: readonly string[]): Promise<AddResult>;
@@ -0,0 +1,56 @@
1
+ import { type CostModel } from '@mfeflow/contracts';
2
+ import { type ProjectView } from '../inspect.js';
3
+ import type { BudgetResult } from '../output.js';
4
+ import type { Runtime } from '../runtime.js';
5
+ export declare const BUDGET_ARGS: {
6
+ readonly yes: {
7
+ readonly type: 'boolean';
8
+ readonly alias: 'y';
9
+ readonly description: 'Take the defaults and never prompt';
10
+ };
11
+ readonly preset: {
12
+ readonly type: 'string';
13
+ readonly description: 'Replay options from a JSON file or an https URL';
14
+ readonly valueHint: 'file|url';
15
+ };
16
+ readonly json: {
17
+ readonly type: 'boolean';
18
+ readonly description: 'Print one JSON document on stdout, nothing else';
19
+ };
20
+ readonly 'dry-run': {
21
+ readonly type: 'boolean';
22
+ readonly description: 'Print the summary and diff, write nothing';
23
+ };
24
+ readonly cwd: {
25
+ readonly type: 'string';
26
+ readonly description: 'Run as if started in this folder';
27
+ readonly valueHint: 'dir';
28
+ };
29
+ readonly install: {
30
+ readonly type: 'boolean';
31
+ readonly default: true;
32
+ readonly description: 'Run the package manager after writing (--no-install to skip)';
33
+ };
34
+ readonly verbose: {
35
+ readonly type: 'boolean';
36
+ readonly description: 'Show package manager output and stack traces';
37
+ };
38
+ readonly local: {
39
+ readonly type: 'string';
40
+ readonly description: 'Link @mfeflow/* from a built mfeflow checkout (env MFEFLOW_LOCAL)';
41
+ readonly valueHint: 'path';
42
+ };
43
+ readonly help: {
44
+ readonly type: 'boolean';
45
+ readonly alias: 'h';
46
+ readonly description: 'Show this help';
47
+ };
48
+ readonly dist: {
49
+ readonly type: 'string';
50
+ readonly description: 'Build output folder inside each app (default dist)';
51
+ readonly valueHint: 'dir';
52
+ };
53
+ };
54
+ export declare function modelOf(view: ProjectView): CostModel;
55
+ export declare function runBudget(rt: Runtime, args: Record<string, unknown>, raw: readonly string[]): Promise<BudgetResult>;
56
+ export declare function formatBudget(result: BudgetResult): string;
@@ -0,0 +1,62 @@
1
+ import type { DevResult } from '../output.js';
2
+ import type { Runtime } from '../runtime.js';
3
+ export declare const DEV_ARGS: {
4
+ readonly yes: {
5
+ readonly type: 'boolean';
6
+ readonly alias: 'y';
7
+ readonly description: 'Take the defaults and never prompt';
8
+ };
9
+ readonly preset: {
10
+ readonly type: 'string';
11
+ readonly description: 'Replay options from a JSON file or an https URL';
12
+ readonly valueHint: 'file|url';
13
+ };
14
+ readonly json: {
15
+ readonly type: 'boolean';
16
+ readonly description: 'Print one JSON document on stdout, nothing else';
17
+ };
18
+ readonly 'dry-run': {
19
+ readonly type: 'boolean';
20
+ readonly description: 'Print the summary and diff, write nothing';
21
+ };
22
+ readonly cwd: {
23
+ readonly type: 'string';
24
+ readonly description: 'Run as if started in this folder';
25
+ readonly valueHint: 'dir';
26
+ };
27
+ readonly install: {
28
+ readonly type: 'boolean';
29
+ readonly default: true;
30
+ readonly description: 'Run the package manager after writing (--no-install to skip)';
31
+ };
32
+ readonly verbose: {
33
+ readonly type: 'boolean';
34
+ readonly description: 'Show package manager output and stack traces';
35
+ };
36
+ readonly local: {
37
+ readonly type: 'string';
38
+ readonly description: 'Link @mfeflow/* from a built mfeflow checkout (env MFEFLOW_LOCAL)';
39
+ readonly valueHint: 'path';
40
+ };
41
+ readonly help: {
42
+ readonly type: 'boolean';
43
+ readonly alias: 'h';
44
+ readonly description: 'Show this help';
45
+ };
46
+ readonly only: {
47
+ readonly type: 'string';
48
+ readonly description: 'Run only these apps, comma separated (default: the shell and every remote)';
49
+ readonly valueHint: 'a,b';
50
+ };
51
+ readonly remote: {
52
+ readonly type: 'string';
53
+ readonly description: 'Use a server you already run for a remote, repeatable: --remote checkout=http://localhost:3001';
54
+ readonly valueHint: 'name=url';
55
+ };
56
+ readonly 'allow-origin': {
57
+ readonly type: 'string';
58
+ readonly description: 'Let --remote point at this origin; localhost is always allowed. Repeatable';
59
+ readonly valueHint: 'origin';
60
+ };
61
+ };
62
+ export declare function runDev(rt: Runtime, args: Record<string, unknown>, raw: readonly string[]): Promise<DevResult>;