@mfeflow/contracts 0.5.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 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 `mfe.json`, `mfeflow.json` and the remote registry. Pre-1.0: minor versions may change the API.
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
- readonly mfe: "https://mfeflow.github.io/mfeflow/schema/mfe.json";
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
@@ -10,5 +10,5 @@ export { isFeatureRegistryIndex, isLoopbackHttp, isPrivateHost, isRegistryProtoc
10
10
  export { 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: 'mfe.json'
352
+ source: 'mfeflow.app.json'
353
353
  };
354
354
  const kb = round1(bytes.entryBytes / remote_budget_KB);
355
355
  return {
@@ -794,7 +794,7 @@ function validateMfeflowProject(value) {
794
794
  });
795
795
  if (void 0 !== port && !Number.isInteger(port)) c.add(`${at}.port`, 'must be a whole number');
796
796
  }
797
- if (0 === shells && 'remote' !== repo) c.add('apps', 'needs at least one shell');
797
+ if (0 === shells && 'remote' !== repo && 'platform' !== repo) c.add('apps', 'needs at least one shell');
798
798
  }
799
799
  const features = c.object(value, 'features', '');
800
800
  if (features) for (const [name, feature] of Object.entries(features)){
@@ -815,6 +815,12 @@ function validateMfeflowProject(value) {
815
815
  if (!isSafeRelativePath(file)) c.add(at, 'must be a relative path with forward slashes');
816
816
  if ('string' != typeof hash || !FILE_HASH.test(hash)) c.add(at, 'must be a hash like "sha256-<64 hex characters>"');
817
817
  }
818
+ const kept = c.object(value, 'kept', '', false);
819
+ if (kept) for (const [file, hash] of Object.entries(kept)){
820
+ const at = `kept.${file}`;
821
+ if (!isSafeRelativePath(file)) c.add(at, 'must be a relative path with forward slashes');
822
+ if ('string' != typeof hash || !FILE_HASH.test(hash)) c.add(at, 'must be a hash like "sha256-<64 hex characters>"');
823
+ }
818
824
  const registries = c.object(value, 'registries', '', false);
819
825
  if (registries) for (const [ns, url] of Object.entries(registries)){
820
826
  const at = `registries.${ns}`;
@@ -1148,13 +1154,16 @@ function isFeatureManifest(value) {
1148
1154
  }
1149
1155
  const DOCS_URL = 'https://mfeflow.github.io/mfeflow';
1150
1156
  const SCHEMA_URLS = {
1151
- mfe: `${DOCS_URL}/schema/mfe.json`,
1157
+ mfe: `${DOCS_URL}/schema/mfeflow-app.json`,
1152
1158
  project: `${DOCS_URL}/schema/mfeflow.json`,
1153
1159
  feature: `${DOCS_URL}/schema/feature.json`,
1154
1160
  registryEntry: `${DOCS_URL}/schema/registry-entry.json`,
1155
1161
  registryIndex: `${DOCS_URL}/schema/registry-index.json`,
1156
1162
  featureRegistryIndex: `${DOCS_URL}/schema/feature-registry-index.json`
1157
1163
  };
1164
+ const SCHEMA_ALIASES = {
1165
+ [`${DOCS_URL}/schema/mfe.json`]: 'mfe'
1166
+ };
1158
1167
  function errorDocsUrl(code) {
1159
1168
  return `${DOCS_URL}/errors/${code}`;
1160
1169
  }
@@ -1394,7 +1403,7 @@ const json_schema_item = (properties, required)=>({
1394
1403
  required
1395
1404
  });
1396
1405
  const JSON_SCHEMAS = {
1397
- mfe: root('mfe', 'mfe.json', {
1406
+ mfe: root('mfe', 'mfeflow.app.json', {
1398
1407
  properties: {
1399
1408
  name: str(MFE_NAME),
1400
1409
  owner: str(),
@@ -1510,6 +1519,11 @@ const JSON_SCHEMAS = {
1510
1519
  propertyNames: relativePath,
1511
1520
  additionalProperties: str(FILE_HASH)
1512
1521
  },
1522
+ kept: {
1523
+ type: 'object',
1524
+ propertyNames: relativePath,
1525
+ additionalProperties: str(FILE_HASH)
1526
+ },
1513
1527
  registries: {
1514
1528
  type: 'object',
1515
1529
  propertyNames: {
@@ -1759,4 +1773,4 @@ const JSON_SCHEMAS = {
1759
1773
  })
1760
1774
  };
1761
1775
  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 };
1776
+ 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_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 };
@@ -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`: who owns a micro-frontend and what it promises. */
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 monorepo. */
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,11 @@ 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>;
27
32
  /** Third-party feature registries: `{ "@someone": "https://…/{name}.json" }`, https only (plain http for loopback hosts). `@mfeflow` is reserved. */
28
33
  registries?: Record<string, string>;
29
34
  /** Built mfeflow checkout, relative to the project root, that `@mfeflow/*` dependencies link to. Set by `--local` only. */
@@ -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 `mfe.json` budget. */
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 `mfe.json` budget or the cost model. */
12
- source: 'mfe.json' | 'cost-model';
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 an `mfe.json` `budgets.initialJsKb` the entry plus the
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.5.0",
4
- "description": "Types, validators and JSON Schemas for mfeflow: mfe.json ownership manifests, mfeflow.json project state, and the remote registry (entries and index).",
3
+ "version": "0.6.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",