mfeflow 0.4.0 → 0.6.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.
- package/README.md +14 -5
- package/assets/features/analytics-gtm/feature.json +1 -1
- package/assets/features/ci-github/feature.json +1 -1
- package/assets/features/env-t3/feature.json +1 -1
- package/assets/features/lint-oxlint/feature.json +1 -1
- package/assets/features/registry-server/feature.json +128 -0
- package/assets/features/registry-server/files/deploy-server.yml +69 -0
- package/assets/features/registry-server/files/registry-server.md.eta +52 -0
- package/assets/features/registry-server/files/registry-sources.ts +12 -0
- package/assets/features/registry-static/feature.json +61 -0
- package/assets/features/registry-static/files/deploy.yml +61 -0
- package/assets/features/registry-static/files/registry.md.eta +49 -0
- package/assets/features/release/feature.json +43 -0
- package/assets/features/release/files/changeset-readme.md +7 -0
- package/assets/features/release/files/config.json +10 -0
- package/assets/features/release/files/releasing.md +21 -0
- package/assets/features/testing/feature.json +1 -1
- package/assets/features/tsconfig/feature.json +1 -1
- package/assets/features/url-factory/feature.json +1 -1
- package/assets/templates/monorepo/feature.json +13 -6
- package/assets/templates/monorepo/files/remote/{mfe.json.eta → mfeflow.app.json.eta} +1 -0
- package/assets/templates/monorepo/files/root/README.md.eta +16 -1
- package/assets/templates/monorepo/files/shell/{mfe.json.eta → mfeflow.app.json.eta} +1 -0
- package/assets/templates/monorepo/files/shell/rsbuild.config.ts.eta +42 -6
- package/assets/templates/monorepo/files/shell/src/assert.ts.eta +37 -0
- package/assets/templates/monorepo/files/shell/src/bootstrap.tsx.eta +12 -2
- package/assets/templates/monorepo/migrations/0.4.0-app-file.mjs +58 -0
- package/assets/templates/polyrepo/feature.json +305 -0
- package/assets/templates/polyrepo/files/AGENTS.md.eta +15 -0
- package/assets/templates/polyrepo/files/changeset/README.md +5 -0
- package/assets/templates/polyrepo/files/changeset/config.json +9 -0
- package/assets/templates/polyrepo/files/packages/contracts/package.json.eta +23 -0
- package/assets/templates/polyrepo/files/packages/contracts/src/index.ts +11 -0
- package/assets/templates/polyrepo/files/packages/contracts/tsconfig.json.eta +5 -0
- package/assets/templates/polyrepo/files/packages/tokens/package.json.eta +23 -0
- package/assets/templates/polyrepo/files/packages/tokens/src/index.ts +13 -0
- package/assets/templates/polyrepo/files/packages/tokens/tsconfig.json.eta +5 -0
- package/assets/templates/polyrepo/files/platform/README.md.eta +43 -0
- package/assets/templates/polyrepo/files/platform/default.json.eta +27 -0
- package/assets/templates/polyrepo/files/remote/README.md.eta +23 -0
- package/assets/templates/polyrepo/files/renovate.json.eta +4 -0
- package/assets/templates/polyrepo/files/shell/README.md.eta +32 -0
- package/assets/templates/polyrepo/files/tsconfig.base.json.eta +13 -0
- package/assets/templates/polyrepo/files/workflows/platform-ci.yml +29 -0
- package/assets/templates/polyrepo/files/workflows/release.yml +40 -0
- package/assets/templates/polyrepo/files/workflows/reusable-ci.yml.eta +50 -0
- package/assets/templates/polyrepo/files/workflows/team-ci.yml.eta +12 -0
- package/dist/against.d.ts +16 -0
- package/dist/catalog.d.ts +8 -0
- package/dist/checks/compat.d.ts +12 -0
- package/dist/checks/index.d.ts +0 -1
- package/dist/checks/singletons.d.ts +2 -5
- package/dist/commands/add.d.ts +4 -2
- package/dist/commands/adopt.d.ts +54 -0
- package/dist/commands/deploy.d.ts +262 -0
- package/dist/commands/doctor.d.ts +5 -0
- package/dist/commands/init.d.ts +9 -1
- package/dist/commands/server-mode.d.ts +210 -0
- package/dist/commands/status.d.ts +63 -0
- package/dist/commands/team-repo.d.ts +9 -0
- package/dist/commands/upgrade.d.ts +66 -0
- package/dist/deploy-testing.d.ts +41 -0
- package/dist/devregistry.d.ts +4 -5
- package/dist/devremotes.d.ts +15 -0
- package/dist/exit.d.ts +2 -1
- package/dist/glue.d.ts +6 -0
- package/dist/hosts.d.ts +25 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.js +5477 -1643
- package/dist/inspect.d.ts +14 -2
- package/dist/output.d.ts +214 -7
- package/dist/pipeline.d.ts +13 -2
- package/dist/polyrepo.d.ts +69 -0
- package/dist/publish.d.ts +98 -0
- package/dist/runtime.d.ts +10 -1
- package/dist/server-client.d.ts +61 -0
- package/dist/system.d.ts +2 -0
- package/dist/testing.d.ts +82 -0
- package/dist/trust.d.ts +4 -0
- package/dist/upgrade.d.ts +25 -0
- package/dist/write.d.ts +8 -0
- package/package.json +5 -4
package/dist/inspect.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { type Issue, type MfeManifest, type RegistryEntry } from '@mfeflow/contracts';
|
|
2
2
|
import { type MfeflowProject } from '@mfeflow/core';
|
|
3
|
+
import type { Io } from './io.js';
|
|
3
4
|
export declare const ENVS: readonly ['dev', 'staging', 'prod'];
|
|
4
5
|
export type Read<T> = {
|
|
5
6
|
state: 'missing';
|
|
@@ -27,6 +28,8 @@ export interface AppView {
|
|
|
27
28
|
present: boolean;
|
|
28
29
|
pkg: PackageView | undefined;
|
|
29
30
|
mfe: Read<MfeManifest>;
|
|
31
|
+
/** Path from the root of the app file that was read: `mfeflow.app.json`, or the old `mfe.json`. */
|
|
32
|
+
appFile: string;
|
|
30
33
|
}
|
|
31
34
|
export interface WorkspacePackage {
|
|
32
35
|
dir: string;
|
|
@@ -39,16 +42,25 @@ export interface ProjectView {
|
|
|
39
42
|
project: MfeflowProject;
|
|
40
43
|
apps: AppView[];
|
|
41
44
|
workspace: WorkspacePackage[];
|
|
45
|
+
/** A polyrepo shell only: remote name → manifest URL of its dev server, from the shell's dev config. */
|
|
46
|
+
devRemotes?: Record<string, string>;
|
|
42
47
|
/** env → remote name → entry. Only remotes with a file in that env are listed. */
|
|
43
48
|
registry: Record<string, Record<string, Read<RegistryEntry>>>;
|
|
44
49
|
/** Folders under registry/envs that exist. */
|
|
45
50
|
envDirs: string[];
|
|
51
|
+
/** Folders of the apps that still use the old `mfe.json` name. */
|
|
52
|
+
legacyAppFiles: string[];
|
|
46
53
|
}
|
|
47
54
|
export declare function readText(file: string): Promise<string | undefined>;
|
|
48
55
|
/** Workspace folders for the simple globs pnpm projects use: `dir/*`, plain paths, and `!` exclusions. */
|
|
49
56
|
export declare function expandGlobs(root: string, globs: readonly string[]): Promise<string[]>;
|
|
50
|
-
/**
|
|
51
|
-
export declare function
|
|
57
|
+
/** Prints the old app file name notice to stderr, at most once for each `io`, which is once per command. */
|
|
58
|
+
export declare function noticeLegacyAppFiles(io: Pick<Io, 'stderr'>, legacy: readonly string[]): void;
|
|
59
|
+
/**
|
|
60
|
+
* Everything the checks read, loaded once. Throws the E102-E104 errors of `loadProject`, and E122
|
|
61
|
+
* when an app has both app file names. With `io`, apps on the old name get one notice per command.
|
|
62
|
+
*/
|
|
63
|
+
export declare function inspectProject(root: string, io?: Pick<Io, 'stderr'>): Promise<ProjectView>;
|
|
52
64
|
/**
|
|
53
65
|
* The version of `name` an app uses: what is installed under node_modules (walking up from
|
|
54
66
|
* the app to the project root, as Node does, never above it), else the version its package.json
|
package/dist/output.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { Issue } from '@mfeflow/contracts';
|
|
1
|
+
import type { Issue, RegistryIndex } from '@mfeflow/contracts';
|
|
2
2
|
import { type Plan } from '@mfeflow/core';
|
|
3
3
|
import { type ErrorReport } from './exit.js';
|
|
4
4
|
import type { Finding } from './findings.js';
|
|
@@ -12,11 +12,13 @@ export interface PlanReport {
|
|
|
12
12
|
path: string;
|
|
13
13
|
action: 'create' | 'patch' | 'delete';
|
|
14
14
|
}[];
|
|
15
|
+
/** `thirdParty` (only present when true): a feature from a registry asked for it. */
|
|
15
16
|
dependencies: {
|
|
16
17
|
file: string;
|
|
17
18
|
name: string;
|
|
18
19
|
range: string;
|
|
19
20
|
dev: boolean;
|
|
21
|
+
thirdParty?: boolean;
|
|
20
22
|
}[];
|
|
21
23
|
scripts: {
|
|
22
24
|
file: string;
|
|
@@ -42,19 +44,88 @@ interface WriteReport {
|
|
|
42
44
|
/** Unified diff of every change. Only present for a dry run. */
|
|
43
45
|
diff?: string;
|
|
44
46
|
}
|
|
47
|
+
/** One repo of a polyrepo `init`. */
|
|
48
|
+
export interface RepoReport {
|
|
49
|
+
/** The folder inside `root`. */
|
|
50
|
+
name: string;
|
|
51
|
+
repo: 'platform' | 'shell' | 'remote';
|
|
52
|
+
root: string;
|
|
53
|
+
/** Files written in this repo, relative to it. */
|
|
54
|
+
written: string[];
|
|
55
|
+
installed: boolean;
|
|
56
|
+
}
|
|
45
57
|
export interface InitResult extends WriteReport {
|
|
46
58
|
command: 'init';
|
|
47
59
|
scope: string;
|
|
48
60
|
apps: AppReport[];
|
|
61
|
+
/** Only for `--topology polyrepo`: the repos made, in the order they were written. */
|
|
62
|
+
repos?: RepoReport[];
|
|
63
|
+
next: string[];
|
|
64
|
+
}
|
|
65
|
+
export interface AdoptResult extends WriteReport {
|
|
66
|
+
command: 'adopt';
|
|
67
|
+
scope: string;
|
|
68
|
+
apps: AppReport[];
|
|
69
|
+
/** Parts of the existing config adopt read but did not carry over, each with the reason. */
|
|
70
|
+
notAdopted: {
|
|
71
|
+
dir: string;
|
|
72
|
+
file: string;
|
|
73
|
+
field: string;
|
|
74
|
+
reason: string;
|
|
75
|
+
}[];
|
|
76
|
+
/** The build config files adopt read and left exactly as they were. */
|
|
77
|
+
buildConfig: string[];
|
|
78
|
+
/** Lines to paste into the shared list of a build config. */
|
|
79
|
+
snippet: string;
|
|
49
80
|
next: string[];
|
|
50
81
|
}
|
|
51
82
|
export interface AddResult extends WriteReport {
|
|
52
83
|
command: 'add';
|
|
53
|
-
kind: 'remote' | 'shell' | 'feature' | 'env-key';
|
|
84
|
+
kind: 'remote' | 'shell' | 'feature' | 'env-key' | 'team-repo';
|
|
54
85
|
/** What was added: an app name, or feature names. */
|
|
55
86
|
added: string[];
|
|
56
87
|
/** Features that were already part of the project. */
|
|
57
88
|
skipped: string[];
|
|
89
|
+
/** `add team-repo` only: the new repo's folder, next to this one. */
|
|
90
|
+
created?: string;
|
|
91
|
+
}
|
|
92
|
+
export interface UpgradeFileReport {
|
|
93
|
+
path: string;
|
|
94
|
+
reason: 'edited' | 'untracked' | 'edited-by-cli' | 'skipped-feature' | 'symlink';
|
|
95
|
+
/** `accept`: written with the new version. `keep`: left, and remembered. `undecided`: left, and the run fails without a person to ask. */
|
|
96
|
+
decision: 'accept' | 'keep' | 'undecided';
|
|
97
|
+
/** Keys in your file that the new version lacks or has with another value; accepting drops them. */
|
|
98
|
+
lostKeys: string[];
|
|
99
|
+
/** The part of `lostKeys` that the new version has with another value. */
|
|
100
|
+
changedValues: string[];
|
|
101
|
+
/** Unified diff from your file to the new version. */
|
|
102
|
+
diff: string;
|
|
103
|
+
}
|
|
104
|
+
export interface UpgradeResult extends WriteReport {
|
|
105
|
+
command: 'upgrade';
|
|
106
|
+
from: string;
|
|
107
|
+
/** Where `from` was read: the `cli` field of mfeflow.json, or the shell's `@mfeflow/runtime` range. */
|
|
108
|
+
fromSource: 'mfeflow.json' | 'package.json';
|
|
109
|
+
to: string;
|
|
110
|
+
/** Nothing to write: the project already matches this version. */
|
|
111
|
+
upToDate: boolean;
|
|
112
|
+
/** Generated files you had not changed, now at the new version. */
|
|
113
|
+
replaced: string[];
|
|
114
|
+
created: string[];
|
|
115
|
+
/** package.json, pnpm-workspace.yaml and turbo.json, edited in place. */
|
|
116
|
+
edited: string[];
|
|
117
|
+
diverged: UpgradeFileReport[];
|
|
118
|
+
/** Registry features by name: `verified` against the recorded hash, `first-seen` when none was recorded, or `accepted-changed`. */
|
|
119
|
+
integrity: Record<string, 'verified' | 'first-seen' | 'accepted-changed'>;
|
|
120
|
+
/** Generated files you deleted. Not brought back. */
|
|
121
|
+
missing: string[];
|
|
122
|
+
/** Files the new version no longer generates. Left in place. */
|
|
123
|
+
removed: string[];
|
|
124
|
+
migrations: {
|
|
125
|
+
feature: string;
|
|
126
|
+
from: string;
|
|
127
|
+
to: string;
|
|
128
|
+
}[];
|
|
58
129
|
}
|
|
59
130
|
export interface FeatureInfo {
|
|
60
131
|
/** `mfeflow` for the features that ship with the CLI, otherwise the registry's namespace, like `@test`. */
|
|
@@ -69,8 +140,10 @@ export interface FeatureInfo {
|
|
|
69
140
|
}
|
|
70
141
|
export interface RegistryStatus {
|
|
71
142
|
namespace: string;
|
|
72
|
-
/** The index.json asked for. */
|
|
143
|
+
/** The index.json asked for, with token-like query values hidden. */
|
|
73
144
|
index: string;
|
|
145
|
+
/** False: the user has not trusted this registry yet, so nothing was requested from it. */
|
|
146
|
+
trusted: boolean;
|
|
74
147
|
/** How many items it lists. */
|
|
75
148
|
items: number;
|
|
76
149
|
/** Why it could not be read; the other registries are listed regardless. */
|
|
@@ -85,15 +158,17 @@ export interface FeaturesResult {
|
|
|
85
158
|
}
|
|
86
159
|
export interface RegistryEntryInfo {
|
|
87
160
|
namespace: string;
|
|
88
|
-
/** The URL template from mfeflow.json. */
|
|
161
|
+
/** The URL template from mfeflow.json, with token-like query values hidden. */
|
|
89
162
|
url: string;
|
|
90
163
|
/** Where its index.json is. */
|
|
91
164
|
index: string;
|
|
165
|
+
/** Whether the user has trusted this namespace at this URL. */
|
|
166
|
+
trusted: boolean;
|
|
92
167
|
}
|
|
93
168
|
export interface RegistryResult {
|
|
94
169
|
ok: true;
|
|
95
170
|
command: 'registry';
|
|
96
|
-
action: 'add' | 'remove' | 'list' | 'build';
|
|
171
|
+
action: 'add' | 'remove' | 'list' | 'build' | 'trust' | 'untrust';
|
|
97
172
|
dryRun: boolean;
|
|
98
173
|
/** The project folder; null for `build`, which needs none. */
|
|
99
174
|
root: string | null;
|
|
@@ -120,6 +195,8 @@ export interface ReportResult {
|
|
|
120
195
|
summary: Summary;
|
|
121
196
|
checks: CheckReport[];
|
|
122
197
|
findings: Finding[];
|
|
198
|
+
/** `check-compat --against`: where the shell's versions came from. */
|
|
199
|
+
against?: string;
|
|
123
200
|
}
|
|
124
201
|
export interface ShellBudget {
|
|
125
202
|
name: string;
|
|
@@ -138,7 +215,7 @@ export interface RouteBudget {
|
|
|
138
215
|
expose: string | null;
|
|
139
216
|
/** Entry plus the expose's synchronous JS, gzip KB. */
|
|
140
217
|
kb: number;
|
|
141
|
-
/**
|
|
218
|
+
/** mfeflow.app.json budgets.initialJsKb, or null. */
|
|
142
219
|
budgetKb: number | null;
|
|
143
220
|
pass: boolean | null;
|
|
144
221
|
}
|
|
@@ -184,12 +261,142 @@ export interface DevResult {
|
|
|
184
261
|
/** Stopped with Ctrl+C or a termination signal. */
|
|
185
262
|
interrupted: boolean;
|
|
186
263
|
}
|
|
264
|
+
export interface PlannedUpload {
|
|
265
|
+
key: string;
|
|
266
|
+
bytes: number;
|
|
267
|
+
contentType: string;
|
|
268
|
+
cacheControl: string;
|
|
269
|
+
/** `create-only`: refuses to overwrite. `if-match`: only if the index is the one that was read. */
|
|
270
|
+
guard: 'create-only' | 'if-match' | 'none';
|
|
271
|
+
}
|
|
272
|
+
/** What `deploy`, `promote` and `rollback` print with `--json`. `ok` is false when a gate failed. */
|
|
273
|
+
export interface DeployResult {
|
|
274
|
+
ok: boolean;
|
|
275
|
+
command: 'deploy' | 'promote' | 'rollback';
|
|
276
|
+
dryRun: boolean;
|
|
277
|
+
root: string;
|
|
278
|
+
env: string;
|
|
279
|
+
/** The env a promote copied from; null otherwise. */
|
|
280
|
+
fromEnv: string | null;
|
|
281
|
+
/** The host as a person wrote it, never an endpoint or a credential. */
|
|
282
|
+
host: string;
|
|
283
|
+
remote: string;
|
|
284
|
+
version: string;
|
|
285
|
+
/** The version the env served before; null when the remote was not deployed there. */
|
|
286
|
+
servedBefore: string | null;
|
|
287
|
+
/** The index now points at `version` (false for a dry run, `--no-publish`, a failed gate, or when it already did). */
|
|
288
|
+
published: boolean;
|
|
289
|
+
previous: string | null;
|
|
290
|
+
rolledBackFrom: string | null;
|
|
291
|
+
/** Everything the run uploads, in order, the index last. */
|
|
292
|
+
uploads: PlannedUpload[];
|
|
293
|
+
uploaded: string[];
|
|
294
|
+
skipped: string[];
|
|
295
|
+
/** The registry/envs file written to match the host; null when none was. */
|
|
296
|
+
localEntry: string | null;
|
|
297
|
+
/** The index the run would write. Only for a dry run. */
|
|
298
|
+
index?: RegistryIndex;
|
|
299
|
+
warnings: string[];
|
|
300
|
+
findings: Finding[];
|
|
301
|
+
/** Server mode only: set when the deploy started a percentage rollout. `version` is then the candidate. */
|
|
302
|
+
rollout?: {
|
|
303
|
+
id: string;
|
|
304
|
+
stable: string;
|
|
305
|
+
candidate: string;
|
|
306
|
+
percent: number;
|
|
307
|
+
steps: number[];
|
|
308
|
+
};
|
|
309
|
+
}
|
|
310
|
+
export interface StatusRemote {
|
|
311
|
+
name: string;
|
|
312
|
+
version: string;
|
|
313
|
+
previous: string | null;
|
|
314
|
+
history: string[];
|
|
315
|
+
rolledBackFrom: string | null;
|
|
316
|
+
/** The version in `registry/envs/<env>/<name>.json`; null when there is no such file. */
|
|
317
|
+
local: string | null;
|
|
318
|
+
/** Versions on the host newer than `version` that no environment points at; null when the host cannot list. */
|
|
319
|
+
unreferenced: string[] | null;
|
|
320
|
+
drift: string[];
|
|
321
|
+
/** Server mode with a token: the remote was killed and is not in the served index. */
|
|
322
|
+
killed?: boolean;
|
|
323
|
+
/** Server mode with a token: a percentage rollout is running. */
|
|
324
|
+
rollout?: {
|
|
325
|
+
candidate: string;
|
|
326
|
+
percent: number;
|
|
327
|
+
} | null;
|
|
328
|
+
}
|
|
329
|
+
export interface StatusEnv {
|
|
330
|
+
env: string;
|
|
331
|
+
/** The host has an index for this env. */
|
|
332
|
+
published: boolean;
|
|
333
|
+
/** When that index was written. The index records no per-remote time. */
|
|
334
|
+
generatedAt: string | null;
|
|
335
|
+
remotes: StatusRemote[];
|
|
336
|
+
}
|
|
337
|
+
export interface StatusResult {
|
|
338
|
+
ok: boolean;
|
|
339
|
+
command: 'status';
|
|
340
|
+
root: string;
|
|
341
|
+
host: string;
|
|
342
|
+
envs: StatusEnv[];
|
|
343
|
+
findings: Finding[];
|
|
344
|
+
}
|
|
345
|
+
/** `mfeflow rollout step|complete|abort`. Fields are null for a dry run, which sends nothing. */
|
|
346
|
+
export interface RolloutCommandResult {
|
|
347
|
+
ok: boolean;
|
|
348
|
+
command: 'rollout';
|
|
349
|
+
action: 'step' | 'complete' | 'abort' | 'status';
|
|
350
|
+
dryRun: boolean;
|
|
351
|
+
root: string;
|
|
352
|
+
host: string;
|
|
353
|
+
env: string;
|
|
354
|
+
remote: string;
|
|
355
|
+
stable: string | null;
|
|
356
|
+
candidate: string | null;
|
|
357
|
+
percent: number | null;
|
|
358
|
+
/** What the index serves now. */
|
|
359
|
+
serves: string | null;
|
|
360
|
+
/** `rollout status` only: the browser reports behind the gate. */
|
|
361
|
+
report?: {
|
|
362
|
+
startedAt: string;
|
|
363
|
+
steps: number[];
|
|
364
|
+
candidateCounts: {
|
|
365
|
+
loads: number;
|
|
366
|
+
errors: number;
|
|
367
|
+
clients: number;
|
|
368
|
+
};
|
|
369
|
+
stableCounts: {
|
|
370
|
+
loads: number;
|
|
371
|
+
errors: number;
|
|
372
|
+
clients: number;
|
|
373
|
+
};
|
|
374
|
+
gate: {
|
|
375
|
+
verdict: 'wait' | 'ok' | 'halt';
|
|
376
|
+
reason: string | null;
|
|
377
|
+
};
|
|
378
|
+
reporterCapFull: boolean;
|
|
379
|
+
};
|
|
380
|
+
}
|
|
381
|
+
/** `mfeflow kill` and `mfeflow restore`. */
|
|
382
|
+
export interface KillCommandResult {
|
|
383
|
+
ok: boolean;
|
|
384
|
+
command: 'kill' | 'restore';
|
|
385
|
+
dryRun: boolean;
|
|
386
|
+
root: string;
|
|
387
|
+
host: string;
|
|
388
|
+
env: string;
|
|
389
|
+
remote: string;
|
|
390
|
+
/** False when the remote was already in that state. Null for a dry run. */
|
|
391
|
+
changed: boolean | null;
|
|
392
|
+
killed: boolean | null;
|
|
393
|
+
}
|
|
187
394
|
export interface FailureResult {
|
|
188
395
|
ok: false;
|
|
189
396
|
error: ErrorReport;
|
|
190
397
|
exitCode: number;
|
|
191
398
|
}
|
|
192
|
-
export type CliResult = InitResult | AddResult | FeaturesResult | RegistryResult | ReportResult | BudgetResult | DevResult | FailureResult;
|
|
399
|
+
export type CliResult = InitResult | AdoptResult | AddResult | UpgradeResult | FeaturesResult | RegistryResult | ReportResult | BudgetResult | DevResult | DeployResult | StatusResult | RolloutCommandResult | KillCommandResult | FailureResult;
|
|
193
400
|
/** The plan as plain data for `--json`, without file contents. Features are listed once. */
|
|
194
401
|
export declare function planReport(plan: Plan): PlanReport;
|
|
195
402
|
/** Checks a document the CLI printed with `--json`. Returns the problems; empty means valid. */
|
package/dist/pipeline.d.ts
CHANGED
|
@@ -1,7 +1,18 @@
|
|
|
1
1
|
import { PlanBuilder, type MfeflowProject, type Plan } from '@mfeflow/core';
|
|
2
2
|
/** Reads a project file by its `/`-separated path; undefined when it does not exist. */
|
|
3
3
|
export declare function projectReader(root: string): (file: string) => Promise<string | undefined>;
|
|
4
|
-
/**
|
|
5
|
-
|
|
4
|
+
/**
|
|
5
|
+
* A plan builder that reads `root`, keeps dependency versions in the catalog and resolves `@mfeflow/*` ranges.
|
|
6
|
+
* `links` maps a package name to the `link:` path it is installed from instead of a registry range.
|
|
7
|
+
*/
|
|
8
|
+
export declare function createBuilder(root: string, checkout: string | undefined, links?: Readonly<Record<string, string>>): PlanBuilder;
|
|
6
9
|
/** `plan` with the `mfeflow.json` change added, for the summary and the diff. */
|
|
7
10
|
export declare function withProjectChange(plan: Plan, project: MfeflowProject): Plan;
|
|
11
|
+
/**
|
|
12
|
+
* `project` with `cli` set to the running version, but only when the plan already changes
|
|
13
|
+
* something and only upward: an older CLI never lowers it, and a run with nothing to do
|
|
14
|
+
* leaves `mfeflow.json` alone.
|
|
15
|
+
*/
|
|
16
|
+
export declare function stampCli(project: MfeflowProject, plan: Plan): MfeflowProject;
|
|
17
|
+
/** `project` with `cli` raised to the running version; never lowered. */
|
|
18
|
+
export declare function raiseCli(project: MfeflowProject): MfeflowProject;
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { type LoadedFeature, type MfeflowProject, type Plan } from '@mfeflow/core';
|
|
2
|
+
import type { Globals } from './args.js';
|
|
3
|
+
import { type RemoteSpec } from './devremotes.js';
|
|
4
|
+
import { type FeatureOptions } from './options.js';
|
|
5
|
+
import type { PlanReport } from './output.js';
|
|
6
|
+
import type { Runtime } from './runtime.js';
|
|
7
|
+
import { type WriteOutcome } from './write.js';
|
|
8
|
+
export declare const PLATFORM_DIR = "platform";
|
|
9
|
+
export declare const SHELL_DIR = "shell";
|
|
10
|
+
export type RepoKind = 'platform' | 'shell' | 'remote';
|
|
11
|
+
export declare function assertFeaturesFit(features: readonly string[]): void;
|
|
12
|
+
export interface RepoRequest {
|
|
13
|
+
kind: RepoKind;
|
|
14
|
+
/** The folder inside the org folder. */
|
|
15
|
+
dir: string;
|
|
16
|
+
root: string;
|
|
17
|
+
scope: string;
|
|
18
|
+
/** The GitHub owner the CI and Renovate files point at; the scope without `@` when empty. */
|
|
19
|
+
org?: string | undefined;
|
|
20
|
+
/** The remote this repo holds (kind remote only). */
|
|
21
|
+
remote?: RemoteSpec;
|
|
22
|
+
/** The remotes the shell routes to and lists in its dev config (kind shell only). */
|
|
23
|
+
shellRemotes?: readonly RemoteSpec[];
|
|
24
|
+
features: readonly string[];
|
|
25
|
+
featureOptions: FeatureOptions;
|
|
26
|
+
local: string | undefined;
|
|
27
|
+
cwd: string;
|
|
28
|
+
}
|
|
29
|
+
/** Where the shell and each remote install `<scope>/*` from until the platform packages are published. */
|
|
30
|
+
export declare function platformLinks(scope: string): Record<string, string>;
|
|
31
|
+
export declare function remotePorts(names: readonly string[]): RemoteSpec[];
|
|
32
|
+
export declare function repoProject(kind: RepoKind, scope: string, remote?: RemoteSpec): MfeflowProject;
|
|
33
|
+
/**
|
|
34
|
+
* `core` for one repo of a polyrepo: the same files and dependencies, minus what belongs to the
|
|
35
|
+
* monorepo as a whole. The root package.json is named after the repo, the README and AGENTS.md come
|
|
36
|
+
* from the polyrepo template, and a platform repo keeps only the workspace files.
|
|
37
|
+
*/
|
|
38
|
+
export declare function polyrepoCore(core: LoadedFeature, request: Pick<RepoRequest, 'kind' | 'scope' | 'remote'>): Promise<LoadedFeature>;
|
|
39
|
+
export interface RepoPlan {
|
|
40
|
+
request: RepoRequest;
|
|
41
|
+
project: MfeflowProject;
|
|
42
|
+
plan: Plan;
|
|
43
|
+
}
|
|
44
|
+
/** What one repo of the polyrepo would get; nothing is written. */
|
|
45
|
+
export declare function planRepo(rt: Runtime, request: RepoRequest): Promise<RepoPlan>;
|
|
46
|
+
/** `report` with every path under `dir/`, so reports of several repos can share one list. */
|
|
47
|
+
export declare function prefixReport(report: PlanReport, dir: string): PlanReport;
|
|
48
|
+
export declare function mergeReports(reports: readonly PlanReport[]): PlanReport;
|
|
49
|
+
export interface RepoWrite {
|
|
50
|
+
repo: RepoPlan;
|
|
51
|
+
outcome: WriteOutcome;
|
|
52
|
+
}
|
|
53
|
+
/** The folders of `dir`'s path that do not exist yet, outermost first. */
|
|
54
|
+
export declare function missingParents(dir: string): Promise<string[]>;
|
|
55
|
+
/** The warning for repos `git init` did not make, or nothing when all of them are. */
|
|
56
|
+
export declare function notReposWarning(dirs: readonly string[]): string[];
|
|
57
|
+
export interface RepoWrites {
|
|
58
|
+
writes: RepoWrite[];
|
|
59
|
+
/** Folders where `git init` did not run. */
|
|
60
|
+
notRepos: string[];
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Writes the repos one after another, each in its own transaction with its own `git init`. If one
|
|
64
|
+
* fails, the repos this run already wrote are removed again, and so are the folders above them that
|
|
65
|
+
* this run made (while empty), so nothing half-built is left behind.
|
|
66
|
+
*/
|
|
67
|
+
export declare function writeRepos(rt: Runtime, globals: Globals, org: string, repos: readonly RepoPlan[]): Promise<RepoWrites>;
|
|
68
|
+
/** The GitHub owner from `--org`, checked; undefined when not given. */
|
|
69
|
+
export declare function checkOrg(org: string | undefined): string | undefined;
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
import type { MfeManifest, RegistryEntry, RegistryIndex, RegistryIndexRemote } from '@mfeflow/contracts';
|
|
2
|
+
import { type ErrorCode } from '@mfeflow/core';
|
|
3
|
+
import { type ApplyResult, type DeployPlan, type GateCode, type RegistryHost } from '@mfeflow/registry/static';
|
|
4
|
+
import { type Globals } from './args.js';
|
|
5
|
+
import { type Finding } from './findings.js';
|
|
6
|
+
import { type AppView, type ProjectView } from './inspect.js';
|
|
7
|
+
import type { PlannedUpload } from './output.js';
|
|
8
|
+
import type { Runtime } from './runtime.js';
|
|
9
|
+
export declare const GATE_CODES: Record<GateCode, ErrorCode>;
|
|
10
|
+
export interface Session {
|
|
11
|
+
rt: Runtime;
|
|
12
|
+
globals: Globals;
|
|
13
|
+
view: ProjectView;
|
|
14
|
+
host: RegistryHost;
|
|
15
|
+
hostLabel: string;
|
|
16
|
+
/** Secret values to scrub from anything printed. */
|
|
17
|
+
secrets: string[];
|
|
18
|
+
/** `--base-url`, else `registry.baseUrl` from mfeflow.json. */
|
|
19
|
+
baseUrl: string | undefined;
|
|
20
|
+
/** Server mode with no host: there is nowhere to upload artifacts to. */
|
|
21
|
+
hostless: boolean;
|
|
22
|
+
}
|
|
23
|
+
export declare function envOf(args: Record<string, unknown>, key?: string): string;
|
|
24
|
+
export declare function positionals(args: Record<string, unknown>, max: number, usage: string): string[];
|
|
25
|
+
export declare function openSession(rt: Runtime, args: Record<string, unknown>, raw?: Globals, options?: {
|
|
26
|
+
hostOptional?: boolean;
|
|
27
|
+
}): Promise<Session>;
|
|
28
|
+
/**
|
|
29
|
+
* A base URL ends up in a public index, so it must be an https URL (http only on localhost) with no
|
|
30
|
+
* credentials, query or fragment. Returns the normalised form without a trailing slash.
|
|
31
|
+
*/
|
|
32
|
+
export declare function normalizeBaseUrl(value: string, label: string): string;
|
|
33
|
+
/** A failure from the host or the files around it as the catalogue error that names it. Secrets never reach the message. */
|
|
34
|
+
export declare function hostFailure(error: unknown, secrets: readonly string[]): Error;
|
|
35
|
+
/** Runs host work, turning host errors into catalogue errors. */
|
|
36
|
+
export declare function onHost<T>(s: Session, work: () => Promise<T>): Promise<T>;
|
|
37
|
+
export declare function remoteApp(view: ProjectView, name: string | undefined): AppView & {
|
|
38
|
+
mfe: MfeOk;
|
|
39
|
+
};
|
|
40
|
+
type MfeOk = {
|
|
41
|
+
state: 'ok';
|
|
42
|
+
value: MfeManifest;
|
|
43
|
+
};
|
|
44
|
+
/** The versions the shell has, for the packages a remote's compatibility is judged on. */
|
|
45
|
+
export declare function shellVersions(view: ProjectView, mfe: MfeManifest): Promise<Record<string, string>>;
|
|
46
|
+
export declare const shellManifestKey: (env: string) => string;
|
|
47
|
+
/** The shell's deployed `mf-manifest.json`: the file given, else the one on the host under the env. */
|
|
48
|
+
export declare function readShellManifest(s: Session, env: string, file: string | undefined): Promise<{
|
|
49
|
+
manifest?: unknown;
|
|
50
|
+
warnings: string[];
|
|
51
|
+
}>;
|
|
52
|
+
/** Environments that have an index on the host. Empty when the host cannot list. */
|
|
53
|
+
export declare function hostEnvs(s: Session): Promise<string[]>;
|
|
54
|
+
export declare function currentIndex(s: Session, env: string): Promise<{
|
|
55
|
+
index: RegistryIndex | undefined;
|
|
56
|
+
etag?: string;
|
|
57
|
+
}>;
|
|
58
|
+
/** Where artifacts are served from: the setting, else read back from an entry already in the index. */
|
|
59
|
+
export declare function baseUrlFor(s: Session, index: RegistryIndex | undefined): string;
|
|
60
|
+
export declare function describeUploads(plan: DeployPlan): PlannedUpload[];
|
|
61
|
+
export declare function gateFindings(plan: DeployPlan, app: string): Finding[];
|
|
62
|
+
export interface Planned {
|
|
63
|
+
plan: DeployPlan;
|
|
64
|
+
/** The remote's entry in the index before this run. */
|
|
65
|
+
before: RegistryIndexRemote | undefined;
|
|
66
|
+
warnings: string[];
|
|
67
|
+
/** The pointer already is where the command would move it. */
|
|
68
|
+
noop?: boolean;
|
|
69
|
+
}
|
|
70
|
+
export interface Committed {
|
|
71
|
+
planned: Planned;
|
|
72
|
+
applied: ApplyResult | undefined;
|
|
73
|
+
/** The index was written. */
|
|
74
|
+
published: boolean;
|
|
75
|
+
}
|
|
76
|
+
export interface CommitOptions {
|
|
77
|
+
env: string;
|
|
78
|
+
name: string;
|
|
79
|
+
dryRun: boolean;
|
|
80
|
+
/** Upload the artifacts and leave the index where it is. */
|
|
81
|
+
noPublish?: boolean;
|
|
82
|
+
/** Reads the host and plans. Runs again after another writer got in first. */
|
|
83
|
+
plan: () => Promise<Planned>;
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* Plans and applies. When another writer moves the index between the read and the write, reads it
|
|
87
|
+
* again, plans again and tries once more; a second collision stops with E510 naming what changed.
|
|
88
|
+
*/
|
|
89
|
+
export declare function commit(s: Session, options: CommitOptions): Promise<Committed>;
|
|
90
|
+
/** The entry `registry/envs/<env>/<name>.json` records for what the index serves. */
|
|
91
|
+
export declare function localEntryOf(remote: RegistryIndexRemote): RegistryEntry;
|
|
92
|
+
export declare function writeLocalEntry(root: string, env: string, remote: RegistryIndexRemote): Promise<string>;
|
|
93
|
+
export interface Build {
|
|
94
|
+
manifest: unknown;
|
|
95
|
+
files: Record<string, Uint8Array>;
|
|
96
|
+
}
|
|
97
|
+
export declare function readBuild(dir: string, shown: string): Promise<Build>;
|
|
98
|
+
export {};
|
package/dist/runtime.d.ts
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
|
-
import type
|
|
1
|
+
import { type FetchLike } from '@mfeflow/core';
|
|
2
2
|
import { type AssetRoots } from './assets.js';
|
|
3
3
|
import { type Io } from './io.js';
|
|
4
4
|
import { type DevHost } from './proc.js';
|
|
5
5
|
import { type Prompter } from './prompter.js';
|
|
6
|
+
import type { RegistryHost } from '@mfeflow/registry/static';
|
|
6
7
|
import { type System } from './system.js';
|
|
7
8
|
/** Everything a command touches outside its own arguments. Tests replace the parts they watch. */
|
|
8
9
|
export interface Runtime {
|
|
@@ -11,7 +12,15 @@ export interface Runtime {
|
|
|
11
12
|
system: System;
|
|
12
13
|
fetch: FetchLike;
|
|
13
14
|
roots: AssetRoots;
|
|
15
|
+
/** Where per-user settings live, like the registries the user trusts. */
|
|
16
|
+
configDir: string;
|
|
14
17
|
/** Processes, signals and file watching for `mfeflow dev`. Defaults to the real ones. */
|
|
15
18
|
dev?: DevHost;
|
|
19
|
+
/** `fetch` for S3 hosts. Tests stand in for the network. */
|
|
20
|
+
hostFetch?: typeof fetch;
|
|
21
|
+
/** `fetch` for the registry server in server mode. Tests point it at a server on 127.0.0.1. */
|
|
22
|
+
serverFetch?: typeof fetch;
|
|
23
|
+
/** Wraps the registry host `deploy`, `promote`, `rollback` and `status` open. Tests inject a second writer. */
|
|
24
|
+
wrapHost?: (host: RegistryHost) => RegistryHost;
|
|
16
25
|
}
|
|
17
26
|
export declare function realRuntime(io?: Io): Runtime;
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { type RegistryIndex } from '@mfeflow/contracts';
|
|
2
|
+
import { MfeflowError, type ErrorCode } from '@mfeflow/core';
|
|
3
|
+
import type { Runtime } from './runtime.js';
|
|
4
|
+
export declare const URL_ENV = "MFEFLOW_REGISTRY_URL";
|
|
5
|
+
export declare const TOKEN_ENV = "MFEFLOW_REGISTRY_TOKEN";
|
|
6
|
+
type Env = Readonly<Record<string, string | undefined>>;
|
|
7
|
+
/** The server address: `--registry-url`, else `MFEFLOW_REGISTRY_URL`. Undefined means static mode. */
|
|
8
|
+
export declare function serverUrlOf(args: Record<string, unknown>, env: Env): string | undefined;
|
|
9
|
+
/**
|
|
10
|
+
* The address as the client uses it: https, or http on a loopback host, with no login, query or
|
|
11
|
+
* fragment, and no trailing slash. A token is sent to this address, so anything else is refused.
|
|
12
|
+
*/
|
|
13
|
+
export declare function normalizeServerUrl(raw: string): string;
|
|
14
|
+
/** The catalogue code for a server error: its own code first, then the HTTP status. Always E5xx. */
|
|
15
|
+
export declare function errorCodeFor(status: number, code: string | undefined): ErrorCode;
|
|
16
|
+
export interface ServerFailure {
|
|
17
|
+
code: string;
|
|
18
|
+
message: string;
|
|
19
|
+
failures?: {
|
|
20
|
+
code: string;
|
|
21
|
+
message?: string;
|
|
22
|
+
route?: string;
|
|
23
|
+
}[];
|
|
24
|
+
}
|
|
25
|
+
export declare class ServerError extends Error {
|
|
26
|
+
readonly status: number;
|
|
27
|
+
readonly failure: ServerFailure;
|
|
28
|
+
constructor(status: number, failure: ServerFailure);
|
|
29
|
+
}
|
|
30
|
+
export interface Reply {
|
|
31
|
+
status: number;
|
|
32
|
+
body: unknown;
|
|
33
|
+
}
|
|
34
|
+
export declare class ServerClient {
|
|
35
|
+
readonly url: string;
|
|
36
|
+
private readonly token;
|
|
37
|
+
private readonly doFetch;
|
|
38
|
+
private readonly secrets;
|
|
39
|
+
constructor(url: string, token: string | undefined, doFetch: typeof fetch, secrets: readonly string[]);
|
|
40
|
+
/** How the server is named in output: never a credential. */
|
|
41
|
+
get label(): string;
|
|
42
|
+
/** Text the server sent, with the token taken out, for anything that is printed. */
|
|
43
|
+
scrub(words: string): string;
|
|
44
|
+
/** A result document with every string scrubbed. The server's words reach the output through it. */
|
|
45
|
+
scrubAll<T>(value: T): T;
|
|
46
|
+
get hasToken(): boolean;
|
|
47
|
+
/** Throws the catalogue error for anything that is not a 2xx reply; `gates_failed` is thrown as a ServerError. */
|
|
48
|
+
get(route: string, needsToken?: boolean): Promise<unknown>;
|
|
49
|
+
post(route: string, body: unknown): Promise<unknown>;
|
|
50
|
+
/** Like `get` but a 404 is `undefined`. */
|
|
51
|
+
getOptional(route: string): Promise<unknown>;
|
|
52
|
+
index(env: string): Promise<RegistryIndex | undefined>;
|
|
53
|
+
private send;
|
|
54
|
+
/** A server error as the catalogue error that names it, with secrets scrubbed. */
|
|
55
|
+
wrap(error: ServerError): MfeflowError;
|
|
56
|
+
/** Runs a call and turns every failure into a catalogue error, except `gates_failed`, which `onGates` handles. */
|
|
57
|
+
run<T>(work: () => Promise<T>, onGates?: (failure: ServerFailure) => T): Promise<T>;
|
|
58
|
+
}
|
|
59
|
+
/** The client, or undefined when this run is in static mode. */
|
|
60
|
+
export declare function openServer(rt: Runtime, args: Record<string, unknown>): ServerClient | undefined;
|
|
61
|
+
export {};
|
package/dist/system.d.ts
CHANGED
|
@@ -16,5 +16,7 @@ export interface System {
|
|
|
16
16
|
install(root: string, packageManager: string, verbose: boolean): Promise<void>;
|
|
17
17
|
/** `git init` unless `root` is already inside a work tree. Returns whether it ran. */
|
|
18
18
|
gitInit(root: string): Promise<boolean>;
|
|
19
|
+
/** `git init` for a repo of its own, even when `root` sits inside another work tree. Returns whether it ran. */
|
|
20
|
+
initRepo(root: string): Promise<boolean>;
|
|
19
21
|
}
|
|
20
22
|
export declare function createSystem(io: Pick<Io, 'stderr'>, runner?: Run): System;
|