@mfeflow/contracts 0.1.0 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/feature.d.ts CHANGED
@@ -21,9 +21,17 @@ export interface FeaturePrompt {
21
21
  choices?: string[];
22
22
  }
23
23
  export interface FeatureFile {
24
- /** Destination, relative to the target. */
24
+ /**
25
+ * Destination, relative to the target. `{{app.name}}` and the other template values are filled
26
+ * in per app, so a feature can write one file for each remote.
27
+ */
25
28
  to: string;
26
29
  target?: FeatureTarget;
30
+ /**
31
+ * Resolve `to` from the project root instead of the target app, for a file about the app that
32
+ * lives elsewhere, like `packages/contracts/src/routes/{{app.name}}.ts`. First-party only.
33
+ */
34
+ atRoot?: boolean;
27
35
  when?: FeatureCondition;
28
36
  /** Source file inside a local feature folder. Remote features cannot use it. */
29
37
  from?: string;
@@ -33,6 +41,7 @@ export interface FeatureFile {
33
41
  template?: boolean;
34
42
  }
35
43
  export interface FeatureDependency {
44
+ /** An npm package name. `{{scope}}/contracts` is the project's own package of that name. */
36
45
  name: string;
37
46
  range: string;
38
47
  dev?: boolean;
@@ -168,6 +177,10 @@ export declare const ENV_KEY: RegExp;
168
177
  export declare const JOB_ID: RegExp;
169
178
  export declare const SCRIPT_NAME: RegExp;
170
179
  export declare const PACKAGE: RegExp;
180
+ /** A package in the project's own scope: `{{scope}}/contracts`. */
181
+ export declare const SCOPED_PACKAGE: RegExp;
182
+ /** What a first-party dependency name may be, for the JSON schema. */
183
+ export declare const DEPENDENCY_NAME: RegExp;
171
184
  export declare function validateFeatureManifest(value: unknown): Issue[];
172
185
  export declare function isFeatureManifest(value: unknown): value is FeatureManifest;
173
186
  export {};
package/dist/index.js CHANGED
@@ -81,7 +81,7 @@ function isSafeRelativePath(value) {
81
81
  const MFE_NAME = /^[a-z][a-z0-9]*(?:_[a-z][a-z0-9]*)*$/;
82
82
  const ROUTE = /^\/[A-Za-z0-9/_:*.-]*$/;
83
83
  const EXPOSE = /^\.\/[A-Za-z0-9_/-]+$/;
84
- const EVENT_NAME = /^[a-z][a-z0-9-]*\.[a-z][a-z0-9_]*@[0-9]+$/;
84
+ const EVENT_NAME = /^[a-z][a-z0-9-]*(\.[a-z][a-z0-9-]*)+\.v[1-9][0-9]*$/;
85
85
  const ANALYTICS_EVENT = /^[a-z][a-z0-9]*(_[a-z0-9]+)+$/;
86
86
  function validateMfeManifest(value) {
87
87
  const c = new Checker();
@@ -114,11 +114,11 @@ function validateMfeManifest(value) {
114
114
  if (events) {
115
115
  c.stringArray(events, 'publishes', 'events', {
116
116
  pattern: EVENT_NAME,
117
- hint: 'must look like "cart.item_added@1"'
117
+ hint: 'must look like "cart.item-added.v1"'
118
118
  });
119
119
  c.stringArray(events, 'consumes', 'events', {
120
120
  pattern: EVENT_NAME,
121
- hint: 'must look like "cart.item_added@1"'
121
+ hint: 'must look like "cart.item-added.v1"'
122
122
  });
123
123
  }
124
124
  c.stringArray(value, 'analyticsEvents', '', {
@@ -597,6 +597,8 @@ const ENV_KEY = /^[A-Za-z_][A-Za-z0-9_]*$/;
597
597
  const JOB_ID = /^[A-Za-z_][A-Za-z0-9_-]*$/;
598
598
  const SCRIPT_NAME = /^[A-Za-z0-9:_.-]+$/;
599
599
  const PACKAGE = /^(@[a-z0-9-~][a-z0-9-._~]*\/)?[a-z0-9-~][a-z0-9-._~]*$/;
600
+ const SCOPED_PACKAGE = /^\{\{scope\}\}\/[a-z0-9-~][a-z0-9-._~]*$/;
601
+ const DEPENDENCY_NAME = new RegExp(`${PACKAGE.source}|${SCOPED_PACKAGE.source}`);
600
602
  function isTransformOp(op) {
601
603
  return TRANSFORM_OPS.some((name)=>name === op);
602
604
  }
@@ -825,11 +827,12 @@ function validateFeatureManifest(value) {
825
827
  c.string(f, 'content', at, {
826
828
  required: false
827
829
  });
830
+ if (void 0 !== f.atRoot && 'boolean' != typeof f.atRoot) c.add(`${at}.atRoot`, 'must be true or false');
828
831
  if ('string' == typeof f.from && !isSafeRelativePath(f.from)) c.add(`${at}.from`, 'must be a relative path with forward slashes and no ".."');
829
832
  if (void 0 === f.from === (void 0 === f.content)) c.add(at, 'needs exactly one of from or content');
830
833
  });
831
834
  eachObject(c, value, 'dependencies', (d, at)=>{
832
- c.string(d, 'name', at, {
835
+ if ('string' != typeof d.name || !SCOPED_PACKAGE.test(d.name)) c.string(d, 'name', at, {
833
836
  pattern: PACKAGE,
834
837
  hint: 'must be an npm package name'
835
838
  });
@@ -1296,6 +1299,9 @@ const JSON_SCHEMAS = {
1296
1299
  content: str(),
1297
1300
  template: {
1298
1301
  type: 'boolean'
1302
+ },
1303
+ atRoot: {
1304
+ type: 'boolean'
1299
1305
  }
1300
1306
  }, [
1301
1307
  'to'
@@ -1317,7 +1323,7 @@ const JSON_SCHEMAS = {
1317
1323
  dependencies: {
1318
1324
  type: 'array',
1319
1325
  items: json_schema_item({
1320
- name: str(PACKAGE),
1326
+ name: str(DEPENDENCY_NAME),
1321
1327
  range: str(),
1322
1328
  dev: {
1323
1329
  type: 'boolean'
@@ -34,7 +34,7 @@ export interface MfeManifest {
34
34
  export declare const MFE_NAME: RegExp;
35
35
  export declare const ROUTE: RegExp;
36
36
  export declare const EXPOSE: RegExp;
37
- /** `<domain>.<event>@<major>`, e.g. `cart.item_added@1`. */
37
+ /** `<domain>.<event>.v<major>`, e.g. `cart.item-added.v1`. The same pattern `@mfeflow/event-bus` checks names against. */
38
38
  export declare const EVENT_NAME: RegExp;
39
39
  /** `<domain>_<object>_<action>`, e.g. `checkout_order_completed`. */
40
40
  export declare const ANALYTICS_EVENT: RegExp;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mfeflow/contracts",
3
- "version": "0.1.0",
3
+ "version": "0.2.0",
4
4
  "description": "Types, validators and JSON Schemas for mfeflow: mfe.json ownership manifests, mfeflow.json project state, and the remote registry (entries and index).",
5
5
  "keywords": [
6
6
  "micro-frontend",