@mfeflow/contracts 0.5.0 → 0.7.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 +1 -1
- package/dist/docs.d.ts +7 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.js +31 -5
- package/dist/mfe-manifest.d.ts +1 -1
- package/dist/project.d.ts +13 -1
- package/dist/remote-budget.d.ts +4 -4
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
Part of [mfeflow](https://github.com/mfeflow/mfeflow), the React micro-frontend boilerplate and platform kit.
|
|
4
4
|
|
|
5
|
-
Types, validators and JSON Schemas for `
|
|
5
|
+
Types, validators and JSON Schemas for `mfeflow.app.json`, `mfeflow.json` and the remote registry. Pre-1.0: minor versions may change the API.
|
|
6
6
|
|
|
7
7
|
```sh
|
|
8
8
|
pnpm add @mfeflow/contracts
|
package/dist/docs.d.ts
CHANGED
|
@@ -2,7 +2,8 @@
|
|
|
2
2
|
export declare const DOCS_URL = "https://mfeflow.github.io/mfeflow";
|
|
3
3
|
/** Where the JSON Schemas are published, for `$schema` in editors. */
|
|
4
4
|
export declare const SCHEMA_URLS: {
|
|
5
|
-
|
|
5
|
+
/** `mfeflow.app.json`, the file in each app's folder. */
|
|
6
|
+
readonly mfe: "https://mfeflow.github.io/mfeflow/schema/mfeflow-app.json";
|
|
6
7
|
readonly project: "https://mfeflow.github.io/mfeflow/schema/mfeflow.json";
|
|
7
8
|
readonly feature: "https://mfeflow.github.io/mfeflow/schema/feature.json";
|
|
8
9
|
readonly registryEntry: "https://mfeflow.github.io/mfeflow/schema/registry-entry.json";
|
|
@@ -10,5 +11,10 @@ export declare const SCHEMA_URLS: {
|
|
|
10
11
|
readonly featureRegistryIndex: "https://mfeflow.github.io/mfeflow/schema/feature-registry-index.json";
|
|
11
12
|
};
|
|
12
13
|
export type SchemaName = keyof typeof SCHEMA_URLS;
|
|
14
|
+
/**
|
|
15
|
+
* Old schema URLs that still serve a schema, so `$schema` lines written before a rename keep
|
|
16
|
+
* resolving. Each maps to the schema now published at its `SCHEMA_URLS` address.
|
|
17
|
+
*/
|
|
18
|
+
export declare const SCHEMA_ALIASES: Readonly<Record<string, SchemaName>>;
|
|
13
19
|
/** The docs page for an error code such as `E102`. */
|
|
14
20
|
export declare function errorDocsUrl(code: string): string;
|
package/dist/index.d.ts
CHANGED
|
@@ -7,8 +7,8 @@ export { judgeRemoteInitial, primaryExpose, type RemoteInitialVerdict } from './
|
|
|
7
7
|
export { injectIntoHead, registryInjection, type InjectionOptions, type RouteNeed, } from './html-injection.js';
|
|
8
8
|
export { isRegistryEntry, isRegistryIndex, isRegistryIndexRemote, registryRemoteIssues, ROUTE_OVERLAP, validateRegistryEntry, validateRegistryIndex, type RegistryEntry, type RegistryIndex, type RegistryIndexRemote, } from './registry.js';
|
|
9
9
|
export { isFeatureRegistryIndex, isLoopbackHttp, isPrivateHost, isRegistryProtocol, redactUrl, redactUrlsIn, registryIndexUrl, registryTemplateProblem, validateFeatureRegistryIndex, MAX_DESCRIPTION, MAX_INDEX_ITEMS, REGISTRY_ITEM_NAME, type FeatureRegistryIndex, type FeatureRegistryItem, } from './feature-registry.js';
|
|
10
|
-
export { FILE_HASH, PROJECT_REPOS, REGISTRY_HOST, REGISTRY_NAMESPACE, isMfeflowProject, validateMfeflowProject, type MfeflowProject, type ProjectBudget, type ProjectRegistry, } from './project.js';
|
|
10
|
+
export { ENV_KEY_NAME, FILE_HASH, PROJECT_REPOS, REGISTRY_HOST, REGISTRY_NAMESPACE, isMfeflowProject, validateMfeflowProject, type MfeflowProject, type ProjectBudget, type ProjectRegistry, } from './project.js';
|
|
11
11
|
export { COST_MODEL, judgeCost, round1, splitScripts, type CostLine, type CostModel, type CostSample, type CostVerdict, } from './cost-model.js';
|
|
12
12
|
export { FEATURE_CONDITION, FEATURE_NAME, isFeatureManifest, TRANSFORM_OPS, validateFeatureManifest, type AddBootStepOp, type AddEnvKeyOp, type AddImportOp, type AddRouteOp, type AddSharedOp, type AddWorkflowStepOp, type EnvValidator, type FeatureCondition, type FeatureDependency, type FeatureFile, type FeatureManifest, type FeatureMigration, type FeaturePrompt, type FeatureScript, type FeatureTarget, type Literal, type PromptValue, type TransformOp, type TransformOpName, type WorkflowStep, type WrapProviderOp, } from './feature.js';
|
|
13
|
-
export { DOCS_URL, SCHEMA_URLS, errorDocsUrl, type SchemaName } from './docs.js';
|
|
13
|
+
export { DOCS_URL, SCHEMA_ALIASES, SCHEMA_URLS, errorDocsUrl, type SchemaName } from './docs.js';
|
|
14
14
|
export { JSON_SCHEMAS } from './json-schema.js';
|
package/dist/index.js
CHANGED
|
@@ -349,7 +349,7 @@ function judgeRemoteInitial(bytes, budgetKb, model = COST_MODEL) {
|
|
|
349
349
|
totalKb,
|
|
350
350
|
limitKb: budgetKb,
|
|
351
351
|
pass: totalKb <= budgetKb,
|
|
352
|
-
source: '
|
|
352
|
+
source: 'mfeflow.app.json'
|
|
353
353
|
};
|
|
354
354
|
const kb = round1(bytes.entryBytes / remote_budget_KB);
|
|
355
355
|
return {
|
|
@@ -715,6 +715,7 @@ function redactUrlsIn(text) {
|
|
|
715
715
|
}
|
|
716
716
|
const project_SEGMENT = String.raw`(?!\.\.?(?:/|$))[^/\\\u0000]+`;
|
|
717
717
|
const CHECKOUT_PATH = new RegExp(`^(?:(?![A-Za-z]:)(?:\\.\\.(?:/\\.\\.)*(?:/${project_SEGMENT})*|${project_SEGMENT}(?:/${project_SEGMENT})*)|(?:/|[A-Za-z]:/)${project_SEGMENT}(?:/${project_SEGMENT})*)$`);
|
|
718
|
+
const ENV_KEY_NAME = /^[A-Za-z_][A-Za-z0-9_]*$/;
|
|
718
719
|
const FILE_HASH = /^sha256-[0-9a-f]{64}$/;
|
|
719
720
|
const REGISTRY_NAMESPACE = /^@[a-z0-9][a-z0-9-]*$/;
|
|
720
721
|
function isCheckoutPath(value) {
|
|
@@ -794,7 +795,7 @@ function validateMfeflowProject(value) {
|
|
|
794
795
|
});
|
|
795
796
|
if (void 0 !== port && !Number.isInteger(port)) c.add(`${at}.port`, 'must be a whole number');
|
|
796
797
|
}
|
|
797
|
-
if (0 === shells && 'remote' !== repo) c.add('apps', 'needs at least one shell');
|
|
798
|
+
if (0 === shells && 'remote' !== repo && 'platform' !== repo) c.add('apps', 'needs at least one shell');
|
|
798
799
|
}
|
|
799
800
|
const features = c.object(value, 'features', '');
|
|
800
801
|
if (features) for (const [name, feature] of Object.entries(features)){
|
|
@@ -815,6 +816,18 @@ function validateMfeflowProject(value) {
|
|
|
815
816
|
if (!isSafeRelativePath(file)) c.add(at, 'must be a relative path with forward slashes');
|
|
816
817
|
if ('string' != typeof hash || !FILE_HASH.test(hash)) c.add(at, 'must be a hash like "sha256-<64 hex characters>"');
|
|
817
818
|
}
|
|
819
|
+
const kept = c.object(value, 'kept', '', false);
|
|
820
|
+
if (kept) for (const [file, hash] of Object.entries(kept)){
|
|
821
|
+
const at = `kept.${file}`;
|
|
822
|
+
if (!isSafeRelativePath(file)) c.add(at, 'must be a relative path with forward slashes');
|
|
823
|
+
if ('string' != typeof hash || !FILE_HASH.test(hash)) c.add(at, 'must be a hash like "sha256-<64 hex characters>"');
|
|
824
|
+
}
|
|
825
|
+
const envKeys = c.object(value, 'envKeys', '', false);
|
|
826
|
+
if (envKeys) for (const [file, keys] of Object.entries(envKeys)){
|
|
827
|
+
const at = `envKeys.${file}`;
|
|
828
|
+
if (!isSafeRelativePath(file)) c.add(at, 'must be a relative path with forward slashes');
|
|
829
|
+
if (!Array.isArray(keys) || keys.some((key)=>'string' != typeof key || !ENV_KEY_NAME.test(key))) c.add(at, 'must be a list of key names like "API_ORIGIN"');
|
|
830
|
+
}
|
|
818
831
|
const registries = c.object(value, 'registries', '', false);
|
|
819
832
|
if (registries) for (const [ns, url] of Object.entries(registries)){
|
|
820
833
|
const at = `registries.${ns}`;
|
|
@@ -1148,13 +1161,16 @@ function isFeatureManifest(value) {
|
|
|
1148
1161
|
}
|
|
1149
1162
|
const DOCS_URL = 'https://mfeflow.github.io/mfeflow';
|
|
1150
1163
|
const SCHEMA_URLS = {
|
|
1151
|
-
mfe: `${DOCS_URL}/schema/
|
|
1164
|
+
mfe: `${DOCS_URL}/schema/mfeflow-app.json`,
|
|
1152
1165
|
project: `${DOCS_URL}/schema/mfeflow.json`,
|
|
1153
1166
|
feature: `${DOCS_URL}/schema/feature.json`,
|
|
1154
1167
|
registryEntry: `${DOCS_URL}/schema/registry-entry.json`,
|
|
1155
1168
|
registryIndex: `${DOCS_URL}/schema/registry-index.json`,
|
|
1156
1169
|
featureRegistryIndex: `${DOCS_URL}/schema/feature-registry-index.json`
|
|
1157
1170
|
};
|
|
1171
|
+
const SCHEMA_ALIASES = {
|
|
1172
|
+
[`${DOCS_URL}/schema/mfe.json`]: 'mfe'
|
|
1173
|
+
};
|
|
1158
1174
|
function errorDocsUrl(code) {
|
|
1159
1175
|
return `${DOCS_URL}/errors/${code}`;
|
|
1160
1176
|
}
|
|
@@ -1394,7 +1410,7 @@ const json_schema_item = (properties, required)=>({
|
|
|
1394
1410
|
required
|
|
1395
1411
|
});
|
|
1396
1412
|
const JSON_SCHEMAS = {
|
|
1397
|
-
mfe: root('mfe', '
|
|
1413
|
+
mfe: root('mfe', 'mfeflow.app.json', {
|
|
1398
1414
|
properties: {
|
|
1399
1415
|
name: str(MFE_NAME),
|
|
1400
1416
|
owner: str(),
|
|
@@ -1510,6 +1526,16 @@ const JSON_SCHEMAS = {
|
|
|
1510
1526
|
propertyNames: relativePath,
|
|
1511
1527
|
additionalProperties: str(FILE_HASH)
|
|
1512
1528
|
},
|
|
1529
|
+
kept: {
|
|
1530
|
+
type: 'object',
|
|
1531
|
+
propertyNames: relativePath,
|
|
1532
|
+
additionalProperties: str(FILE_HASH)
|
|
1533
|
+
},
|
|
1534
|
+
envKeys: {
|
|
1535
|
+
type: 'object',
|
|
1536
|
+
propertyNames: relativePath,
|
|
1537
|
+
additionalProperties: json_schema_strings(ENV_KEY_NAME)
|
|
1538
|
+
},
|
|
1513
1539
|
registries: {
|
|
1514
1540
|
type: 'object',
|
|
1515
1541
|
propertyNames: {
|
|
@@ -1759,4 +1785,4 @@ const JSON_SCHEMAS = {
|
|
|
1759
1785
|
})
|
|
1760
1786
|
};
|
|
1761
1787
|
export { EXPOSE, ROUTE, isRegistryIndexRemote, registryRemoteIssues } from "./entry.js";
|
|
1762
|
-
export { ANALYTICS_EVENT, COST_MODEL, DOCS_URL, EVENT_NAME, FEATURE_CONDITION, FEATURE_NAME, FILE_HASH, JSON_SCHEMAS, MAX_DESCRIPTION, MAX_INDEX_ITEMS, MFE_NAME, PROJECT_REPOS, REGISTRY_HOST, REGISTRY_ITEM_NAME, REGISTRY_NAMESPACE, ROUTE_OVERLAP, SCHEMA_URLS, TRANSFORM_OPS, assetsFromManifest, errorDocsUrl, injectIntoHead, isFeatureManifest, isFeatureRegistryIndex, isLoopbackHttp, isMfeManifest, isMfeflowProject, isPortableRelativePath, isPrivateHost, isRegistryEntry, isRegistryIndex, isRegistryProtocol, isSafeRelativePath, judgeCost, judgeRemoteInitial, primaryExpose, redactUrl, redactUrlsIn, registryIndexUrl, registryInjection, registryTemplateProblem, remoteDistFiles, round1, routesOverlap, splitScripts, validateFeatureManifest, validateFeatureRegistryIndex, validateMfeManifest, validateMfeflowProject, validateRegistryEntry, validateRegistryIndex };
|
|
1788
|
+
export { ANALYTICS_EVENT, COST_MODEL, DOCS_URL, ENV_KEY_NAME, EVENT_NAME, FEATURE_CONDITION, FEATURE_NAME, FILE_HASH, JSON_SCHEMAS, MAX_DESCRIPTION, MAX_INDEX_ITEMS, MFE_NAME, PROJECT_REPOS, REGISTRY_HOST, REGISTRY_ITEM_NAME, REGISTRY_NAMESPACE, ROUTE_OVERLAP, SCHEMA_ALIASES, SCHEMA_URLS, TRANSFORM_OPS, assetsFromManifest, errorDocsUrl, injectIntoHead, isFeatureManifest, isFeatureRegistryIndex, isLoopbackHttp, isMfeManifest, isMfeflowProject, isPortableRelativePath, isPrivateHost, isRegistryEntry, isRegistryIndex, isRegistryProtocol, isSafeRelativePath, judgeCost, judgeRemoteInitial, primaryExpose, redactUrl, redactUrlsIn, registryIndexUrl, registryInjection, registryTemplateProblem, remoteDistFiles, round1, routesOverlap, splitScripts, validateFeatureManifest, validateFeatureRegistryIndex, validateMfeManifest, validateMfeflowProject, validateRegistryEntry, validateRegistryIndex };
|
package/dist/mfe-manifest.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { EXPOSE, ROUTE } from './entry.js';
|
|
2
2
|
import { type Issue } from './validate.js';
|
|
3
3
|
export { EXPOSE, ROUTE };
|
|
4
|
-
/** `mfe.json
|
|
4
|
+
/** `mfeflow.app.json` (formerly `mfe.json`): who owns a micro-frontend and what it promises. */
|
|
5
5
|
export interface MfeManifest {
|
|
6
6
|
$schema?: string;
|
|
7
7
|
/** Module Federation name: lowercase letters, digits, `_`. */
|
package/dist/project.d.ts
CHANGED
|
@@ -5,7 +5,7 @@ export interface MfeflowProject {
|
|
|
5
5
|
version: 1;
|
|
6
6
|
/** Version of the CLI that last wrote this file. */
|
|
7
7
|
cli?: string;
|
|
8
|
-
/** Which repo this is in a multi-repo setup. Absent means a platform
|
|
8
|
+
/** Which repo this is in a multi-repo setup. Absent means a monorepo. A `platform` or `remote` repo may have no shell. */
|
|
9
9
|
repo?: 'platform' | 'shell' | 'remote';
|
|
10
10
|
topology: 'monorepo' | 'polyrepo' | 'hybrid';
|
|
11
11
|
packageManager: 'pnpm' | 'npm';
|
|
@@ -24,6 +24,16 @@ export interface MfeflowProject {
|
|
|
24
24
|
}>;
|
|
25
25
|
/** Hashes of copied files, so `upgrade` can tell when a file was edited. */
|
|
26
26
|
files?: Record<string, string>;
|
|
27
|
+
/**
|
|
28
|
+
* Files `upgrade --keep` left alone: the hash of the new version each one was kept against.
|
|
29
|
+
* While the new version is that one, the file stays quiet; when it changes, the file is raised again.
|
|
30
|
+
*/
|
|
31
|
+
kept?: Record<string, string>;
|
|
32
|
+
/**
|
|
33
|
+
* Keys `mfeflow add env-key` wrote, by the file it wrote them in. `upgrade` asks before a new
|
|
34
|
+
* template changes the value of one of them. Absent in projects made before this was recorded.
|
|
35
|
+
*/
|
|
36
|
+
envKeys?: Record<string, string[]>;
|
|
27
37
|
/** Third-party feature registries: `{ "@someone": "https://…/{name}.json" }`, https only (plain http for loopback hosts). `@mfeflow` is reserved. */
|
|
28
38
|
registries?: Record<string, string>;
|
|
29
39
|
/** Built mfeflow checkout, relative to the project root, that `@mfeflow/*` dependencies link to. Set by `--local` only. */
|
|
@@ -51,6 +61,8 @@ export interface ProjectBudget {
|
|
|
51
61
|
}
|
|
52
62
|
/** The regex form of `isCheckoutPath`, for JSON Schema. */
|
|
53
63
|
export declare const CHECKOUT_PATH: RegExp;
|
|
64
|
+
/** An env key name, as `mfeflow add env-key` accepts it. */
|
|
65
|
+
export declare const ENV_KEY_NAME: RegExp;
|
|
54
66
|
export declare const FILE_HASH: RegExp;
|
|
55
67
|
export declare const REGISTRY_NAMESPACE: RegExp;
|
|
56
68
|
/** A relative path with forward slashes whose only `..` segments come first, so it may leave the project. */
|
package/dist/remote-budget.d.ts
CHANGED
|
@@ -2,17 +2,17 @@ import { type CostModel } from './cost-model.js';
|
|
|
2
2
|
/** The expose a remote's route loads first: `./App`, else the first one. */
|
|
3
3
|
export declare function primaryExpose(names: readonly string[]): string | undefined;
|
|
4
4
|
export interface RemoteInitialVerdict {
|
|
5
|
-
/** Gzip KB that were judged, one decimal: the entry plus the primary expose, or the entry alone without an
|
|
5
|
+
/** Gzip KB that were judged, one decimal: the entry plus the primary expose, or the entry alone without an app file budget. */
|
|
6
6
|
kb: number;
|
|
7
7
|
/** Gzip KB of the entry plus the primary expose, whichever was judged. */
|
|
8
8
|
totalKb: number;
|
|
9
9
|
limitKb: number;
|
|
10
10
|
pass: boolean;
|
|
11
|
-
/** Where the limit came from: the remote's `
|
|
12
|
-
source: '
|
|
11
|
+
/** Where the limit came from: the remote's `mfeflow.app.json` budget or the cost model. */
|
|
12
|
+
source: 'mfeflow.app.json' | 'cost-model';
|
|
13
13
|
}
|
|
14
14
|
/**
|
|
15
|
-
* Judges the JS a remote loads first. With
|
|
15
|
+
* Judges the JS a remote loads first. With a `mfeflow.app.json` `budgets.initialJsKb` the entry plus the
|
|
16
16
|
* primary expose must fit it; without one the entry alone must fit the cost model's `remoteKb`.
|
|
17
17
|
*/
|
|
18
18
|
export declare function judgeRemoteInitial(bytes: {
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mfeflow/contracts",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "Types, validators and JSON Schemas for mfeflow:
|
|
3
|
+
"version": "0.7.0",
|
|
4
|
+
"description": "Types, validators and JSON Schemas for mfeflow: mfeflow.app.json ownership manifests, mfeflow.json project state, and the remote registry (entries and index).",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"micro-frontend",
|
|
7
7
|
"mfe",
|