mfeflow 0.0.0-stage → 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.
Files changed (137) 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 +399 -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/event-bus/feature.json +100 -0
  25. package/assets/features/event-bus/files/announce.tsx.eta +10 -0
  26. package/assets/features/event-bus/files/contracts-events.ts +11 -0
  27. package/assets/features/event-bus/files/contracts-package.json.eta +10 -0
  28. package/assets/features/event-bus/files/contracts-readme.md +10 -0
  29. package/assets/features/event-bus/files/events-test.ts.eta +18 -0
  30. package/assets/features/event-bus/files/events.ts.eta +20 -0
  31. package/assets/features/event-bus/files/shell-events-test.ts.eta +22 -0
  32. package/assets/features/event-bus/files/shell-events.ts.eta +19 -0
  33. package/assets/features/git-hooks/feature.json +42 -0
  34. package/assets/features/git-hooks/files/commitlint.config.js +1 -0
  35. package/assets/features/git-hooks/files/lefthook.yml +9 -0
  36. package/assets/features/http/feature.json +81 -0
  37. package/assets/features/http/files/client-test.ts +19 -0
  38. package/assets/features/http/files/client.ts +20 -0
  39. package/assets/features/lint-oxlint/feature.json +60 -0
  40. package/assets/features/lint-oxlint/files/oxlintrc.json +5 -0
  41. package/assets/features/lint-oxlint/files/prettierignore +5 -0
  42. package/assets/features/lint-oxlint/files/prettierrc.json +5 -0
  43. package/assets/features/route-factory/feature.json +60 -0
  44. package/assets/features/route-factory/files/contract.ts.eta +7 -0
  45. package/assets/features/route-factory/files/routes-contract.ts.eta +11 -0
  46. package/assets/features/route-factory/files/routes-test.ts.eta +9 -0
  47. package/assets/features/route-factory/files/routes.ts.eta +11 -0
  48. package/assets/features/telemetry-otel/feature.json +159 -0
  49. package/assets/features/telemetry-otel/files/init.ts.eta +53 -0
  50. package/assets/features/telemetry-otel/files/remote-telemetry-test.ts.eta +13 -0
  51. package/assets/features/telemetry-otel/files/remote-telemetry.ts.eta +11 -0
  52. package/assets/features/telemetry-otel/files/shell-init-test.ts.eta +36 -0
  53. package/assets/features/testing/feature.json +202 -0
  54. package/assets/features/testing/files/handlers.ts +5 -0
  55. package/assets/features/testing/files/playwright.config.ts +30 -0
  56. package/assets/features/testing/files/root-vitest.config.ts +6 -0
  57. package/assets/features/testing/files/sample-handlers-test.ts +16 -0
  58. package/assets/features/testing/files/sample-remote-test.tsx +12 -0
  59. package/assets/features/testing/files/sample-shell-test.tsx +11 -0
  60. package/assets/features/testing/files/sample-smoke-e2e.ts +53 -0
  61. package/assets/features/testing/files/server.ts +4 -0
  62. package/assets/features/testing/files/setup.ts +18 -0
  63. package/assets/features/testing/files/vitest.config.ts +11 -0
  64. package/assets/features/tsconfig/feature.json +78 -0
  65. package/assets/features/tsconfig/files/root.tsconfig.json +4 -0
  66. package/assets/features/tsconfig/files/tsconfig.json +5 -0
  67. package/assets/features/url-factory/feature.json +30 -0
  68. package/assets/features/url-factory/files/endpoints.ts +23 -0
  69. package/assets/templates/monorepo/feature.json +321 -0
  70. package/assets/templates/monorepo/files/remote/mfe.json.eta +7 -0
  71. package/assets/templates/monorepo/files/remote/package.json.eta +6 -0
  72. package/assets/templates/monorepo/files/remote/rsbuild.config.ts.eta +7 -0
  73. package/assets/templates/monorepo/files/remote/src/bootstrap.tsx.eta +6 -0
  74. package/assets/templates/monorepo/files/remote/src/env.d.ts.eta +1 -0
  75. package/assets/templates/monorepo/files/remote/src/exposed/App.tsx.eta +18 -0
  76. package/assets/templates/monorepo/files/remote/src/index.ts.eta +2 -0
  77. package/assets/templates/monorepo/files/root/AGENTS.md.eta +17 -0
  78. package/assets/templates/monorepo/files/root/README.md.eta +52 -0
  79. package/assets/templates/monorepo/files/root/gitignore.eta +11 -0
  80. package/assets/templates/monorepo/files/root/nvmrc.eta +1 -0
  81. package/assets/templates/monorepo/files/root/package.json.eta +9 -0
  82. package/assets/templates/monorepo/files/root/pnpm-workspace.yaml.eta +7 -0
  83. package/assets/templates/monorepo/files/root/turbo.json.eta +16 -0
  84. package/assets/templates/monorepo/files/shell/mfe.json.eta +7 -0
  85. package/assets/templates/monorepo/files/shell/package.json.eta +6 -0
  86. package/assets/templates/monorepo/files/shell/rsbuild.config.ts.eta +74 -0
  87. package/assets/templates/monorepo/files/shell/src/App.tsx.eta +46 -0
  88. package/assets/templates/monorepo/files/shell/src/bootstrap.tsx.eta +24 -0
  89. package/assets/templates/monorepo/files/shell/src/env.d.ts.eta +1 -0
  90. package/assets/templates/monorepo/files/shell/src/index.ts.eta +4 -0
  91. package/assets/templates/monorepo/files/shell/src/pages/Home.tsx.eta +14 -0
  92. package/assets/templates/monorepo/files/shell/src/remotes/route-table.tsx.eta +12 -0
  93. package/assets/templates/monorepo/glue/registry-entry.json.eta +5 -0
  94. package/assets/templates/monorepo/glue/route.tsx.eta +5 -0
  95. package/bin/mfeflow.js +4 -0
  96. package/dist/args.d.ts +68 -0
  97. package/dist/assets.d.ts +13 -0
  98. package/dist/catalog.d.ts +24 -0
  99. package/dist/checks/compat.d.ts +2 -0
  100. package/dist/checks/config.d.ts +8 -0
  101. package/dist/checks/env.d.ts +6 -0
  102. package/dist/checks/index.d.ts +6 -0
  103. package/dist/checks/plugins.d.ts +4 -0
  104. package/dist/checks/singletons.d.ts +9 -0
  105. package/dist/checks/workspace.d.ts +2 -0
  106. package/dist/cli.d.ts +11 -0
  107. package/dist/commands/add.d.ts +84 -0
  108. package/dist/commands/budget.d.ts +56 -0
  109. package/dist/commands/dev.d.ts +62 -0
  110. package/dist/commands/doctor.d.ts +141 -0
  111. package/dist/commands/env-key.d.ts +4 -0
  112. package/dist/commands/features.d.ts +53 -0
  113. package/dist/commands/init.d.ts +102 -0
  114. package/dist/devregistry.d.ts +19 -0
  115. package/dist/exit.d.ts +31 -0
  116. package/dist/findings.d.ts +35 -0
  117. package/dist/glue.d.ts +41 -0
  118. package/dist/help.d.ts +12 -0
  119. package/dist/index.d.ts +8 -0
  120. package/dist/index.js +4171 -0
  121. package/dist/inspect.d.ts +57 -0
  122. package/dist/io.d.ts +10 -0
  123. package/dist/local.d.ts +28 -0
  124. package/dist/options.d.ts +7 -0
  125. package/dist/output.d.ts +157 -0
  126. package/dist/pipeline.d.ts +7 -0
  127. package/dist/preset.d.ts +19 -0
  128. package/dist/proc.d.ts +79 -0
  129. package/dist/prompter.d.ts +28 -0
  130. package/dist/report.d.ts +26 -0
  131. package/dist/runtime.d.ts +17 -0
  132. package/dist/singletons.d.ts +11 -0
  133. package/dist/sizes.d.ts +17 -0
  134. package/dist/system.d.ts +20 -0
  135. package/dist/version.d.ts +8 -0
  136. package/dist/write.d.ts +26 -0
  137. package/package.json +66 -3
@@ -0,0 +1,100 @@
1
+ {
2
+ "name": "event-bus",
3
+ "version": "0.1.0",
4
+ "description": "Typed events between remotes (@mfeflow/event-bus) and the contracts package that holds their types: packages/contracts/src/events, merged into the bus in every app, with a sample event.",
5
+ "requires": ["core", "tsconfig"],
6
+ "files": [
7
+ {
8
+ "to": "packages/contracts/package.json",
9
+ "target": "root",
10
+ "from": "files/contracts-package.json.eta",
11
+ "template": true
12
+ },
13
+ {
14
+ "to": "packages/contracts/src/events/index.ts",
15
+ "target": "root",
16
+ "from": "files/contracts-events.ts"
17
+ },
18
+ {
19
+ "to": "packages/contracts/README.md",
20
+ "target": "root",
21
+ "from": "files/contracts-readme.md"
22
+ },
23
+ {
24
+ "to": "src/events.ts",
25
+ "target": "shell",
26
+ "from": "files/shell-events.ts.eta",
27
+ "template": true
28
+ },
29
+ {
30
+ "to": "src/events.test.ts",
31
+ "target": "shell",
32
+ "from": "files/shell-events-test.ts.eta",
33
+ "template": true,
34
+ "when": {
35
+ "feature:testing": true
36
+ }
37
+ },
38
+ {
39
+ "to": "src/events.ts",
40
+ "target": "remotes",
41
+ "from": "files/events.ts.eta",
42
+ "template": true
43
+ },
44
+ {
45
+ "to": "src/events.test.ts",
46
+ "target": "remotes",
47
+ "from": "files/events-test.ts.eta",
48
+ "template": true,
49
+ "when": {
50
+ "feature:testing": true
51
+ }
52
+ },
53
+ {
54
+ "to": "src/Announce.tsx",
55
+ "target": "remotes",
56
+ "from": "files/announce.tsx.eta",
57
+ "template": true
58
+ }
59
+ ],
60
+ "dependencies": [
61
+ {
62
+ "name": "@mfeflow/event-bus",
63
+ "range": "latest",
64
+ "target": "shell"
65
+ },
66
+ {
67
+ "name": "@mfeflow/event-bus",
68
+ "range": "latest",
69
+ "target": "remotes"
70
+ },
71
+ {
72
+ "name": "{{scope}}/contracts",
73
+ "range": "workspace:*",
74
+ "target": "shell"
75
+ },
76
+ {
77
+ "name": "{{scope}}/contracts",
78
+ "range": "workspace:*",
79
+ "target": "remotes"
80
+ }
81
+ ],
82
+ "transforms": [
83
+ {
84
+ "op": "add-boot-step",
85
+ "file": "src/bootstrap.tsx",
86
+ "target": "shell",
87
+ "call": "configureShellEvents",
88
+ "from": "./events",
89
+ "await": false
90
+ },
91
+ {
92
+ "op": "wrap-provider",
93
+ "file": "src/exposed/App.tsx",
94
+ "target": "remotes",
95
+ "component": "Announce",
96
+ "from": "../Announce",
97
+ "function": "App"
98
+ }
99
+ ]
100
+ }
@@ -0,0 +1,10 @@
1
+ import { useEffect, type ReactNode } from 'react';
2
+ import { announceReady } from './events';
3
+
4
+ /** Tells the other remotes that <%= it.app.name %> is on the page, each time it mounts. */
5
+ export function Announce({ children }: { children: ReactNode }) {
6
+ useEffect(() => {
7
+ announceReady();
8
+ }, []);
9
+ return <>{children}</>;
10
+ }
@@ -0,0 +1,11 @@
1
+ /**
2
+ * Every event the platform can carry, by versioned name: <domain>.<event>.v<major>, like
3
+ * cart.item-added.v1. Types only: nothing here runs, so any remote can read it.
4
+ *
5
+ * Add an event by adding a line. Change a payload in a way old listeners cannot read by adding
6
+ * a new version (cart.item-added.v2) and publishing both until every listener has moved.
7
+ */
8
+ export interface Events {
9
+ /** A remote is on the page. The sample event: every remote announces itself, every other one can listen. */
10
+ 'mfe.ready.v1': { mfe: string };
11
+ }
@@ -0,0 +1,10 @@
1
+ {
2
+ "name": "<%= it.scope %>/contracts",
3
+ "version": "0.0.0",
4
+ "private": true,
5
+ "type": "module",
6
+ "exports": {
7
+ "./events": "./src/events/index.ts",
8
+ "./routes/*": "./src/routes/*.ts"
9
+ }
10
+ }
@@ -0,0 +1,10 @@
1
+ # contracts
2
+
3
+ What the micro-frontends agree on, and nothing else. A remote never imports another remote; it
4
+ imports this.
5
+
6
+ - `src/events`: the events remotes publish and listen to, as types.
7
+ - `src/routes`: each remote's public paths, as plain data (added by `route-factory`).
8
+
9
+ Keep it to types and plain data with no imports, so reading it costs a remote nothing at run time
10
+ and it never pulls in another remote's code.
@@ -0,0 +1,18 @@
1
+ import { publish, resetEventBusForTests } from '@mfeflow/event-bus';
2
+ import { afterEach, describe, expect, it, vi } from 'vitest';
3
+ import { announceReady, onOtherRemoteReady } from './events';
4
+
5
+ afterEach(resetEventBusForTests);
6
+
7
+ describe('the <%= it.app.name %> events', () => {
8
+ it('hears another remote and not itself', () => {
9
+ const heard = vi.fn();
10
+ const stop = onOtherRemoteReady(heard);
11
+ announceReady();
12
+ publish('mfe.ready.v1', { mfe: 'somewhere-else' });
13
+ expect(heard).toHaveBeenCalledExactlyOnceWith('somewhere-else');
14
+ stop();
15
+ publish('mfe.ready.v1', { mfe: 'later' });
16
+ expect(heard).toHaveBeenCalledOnce();
17
+ });
18
+ });
@@ -0,0 +1,20 @@
1
+ import { publish, subscribe } from '@mfeflow/event-bus';
2
+ import type { Events } from '<%= it.scope %>/contracts/events';
3
+
4
+ // The platform's event map is the contracts package's. This one declaration merges it in.
5
+ declare module '@mfeflow/event-bus' {
6
+ // oxlint-disable-next-line typescript/no-empty-interface -- merges the contracts into the platform map
7
+ interface EventMap extends Events {}
8
+ }
9
+
10
+ /** Tells the other remotes that <%= it.app.name %> is on the page. */
11
+ export function announceReady(): void {
12
+ publish('mfe.ready.v1', { mfe: '<%= it.app.name %>' });
13
+ }
14
+
15
+ /** Calls `handler` with the name of each other remote that announces itself. Returns the function that stops it. */
16
+ export function onOtherRemoteReady(handler: (mfe: string) => void): () => void {
17
+ return subscribe('mfe.ready.v1', ({ mfe }) => {
18
+ if (mfe !== '<%= it.app.name %>') handler(mfe);
19
+ });
20
+ }
@@ -0,0 +1,22 @@
1
+ import { publish, resetEventBusForTests, subscribe } from '@mfeflow/event-bus';
2
+ import { afterEach, describe, expect, it, vi } from 'vitest';
3
+ import { configureShellEvents } from './events';
4
+
5
+ afterEach(resetEventBusForTests);
6
+
7
+ describe('the shell event bus', () => {
8
+ it('carries the contracts events between remotes', () => {
9
+ const heard = vi.fn();
10
+ subscribe('mfe.ready.v1', heard);
11
+ publish('mfe.ready.v1', { mfe: 'cart' });
12
+ expect(heard).toHaveBeenCalledExactlyOnceWith({ mfe: 'cart' });
13
+ });
14
+
15
+ it('replays the latest announcement to a remote that mounts late', () => {
16
+ configureShellEvents();
17
+ publish('mfe.ready.v1', { mfe: 'cart' });
18
+ const late = vi.fn();
19
+ subscribe('mfe.ready.v1', late);
20
+ expect(late).toHaveBeenCalledExactlyOnceWith({ mfe: 'cart' });
21
+ });
22
+ });
@@ -0,0 +1,19 @@
1
+ import { configureEvent } from '@mfeflow/event-bus';
2
+ import type { Events } from '<%= it.scope %>/contracts/events';
3
+
4
+ // The platform's event map is the contracts package's. This one declaration merges it in.
5
+ declare module '@mfeflow/event-bus' {
6
+ // oxlint-disable-next-line typescript/no-empty-interface -- merges the contracts into the platform map
7
+ interface EventMap extends Events {}
8
+ }
9
+
10
+ /**
11
+ * How the platform's events behave, set once at boot. The shell imports the bus here, which makes
12
+ * it the one that provides the copy every remote shares.
13
+ */
14
+ export function configureShellEvents(): void {
15
+ // "This remote is up" is a fact about the page, not a moment: a remote that mounts later still
16
+ // hears the latest announcement. Only the last one is kept. Leave replay off for events that
17
+ // are things that happened, like cart.item-added.v1.
18
+ configureEvent('mfe.ready.v1', { replay: true });
19
+ }
@@ -0,0 +1,42 @@
1
+ {
2
+ "name": "git-hooks",
3
+ "version": "0.1.0",
4
+ "description": "lefthook with commitlint: Conventional Commit messages, typecheck before push. Opt-in.",
5
+ "requires": ["core"],
6
+ "files": [
7
+ {
8
+ "to": "lefthook.yml",
9
+ "from": "files/lefthook.yml"
10
+ },
11
+ {
12
+ "to": "commitlint.config.js",
13
+ "from": "files/commitlint.config.js"
14
+ }
15
+ ],
16
+ "dependencies": [
17
+ {
18
+ "name": "lefthook",
19
+ "range": "2.2.2",
20
+ "target": "root",
21
+ "dev": true
22
+ },
23
+ {
24
+ "name": "@commitlint/cli",
25
+ "range": "21.2.4",
26
+ "target": "root",
27
+ "dev": true
28
+ },
29
+ {
30
+ "name": "@commitlint/config-conventional",
31
+ "range": "21.2.4",
32
+ "target": "root",
33
+ "dev": true
34
+ }
35
+ ],
36
+ "scripts": [
37
+ {
38
+ "name": "prepare",
39
+ "command": "lefthook install"
40
+ }
41
+ ]
42
+ }
@@ -0,0 +1 @@
1
+ export default { extends: ['@commitlint/config-conventional'] };
@@ -0,0 +1,9 @@
1
+ commit-msg:
2
+ commands:
3
+ commitlint:
4
+ run: pnpm commitlint --edit {1}
5
+
6
+ pre-push:
7
+ commands:
8
+ typecheck:
9
+ run: pnpm typecheck
@@ -0,0 +1,81 @@
1
+ {
2
+ "name": "http",
3
+ "version": "0.1.0",
4
+ "description": "A fetch client over your url-factory endpoints (@mfeflow/http): src/api/client.ts in every app, with retries for idempotent requests, timeouts and typed errors. The API base URL is the runtime config's apiUrl.",
5
+ "requires": ["core", "tsconfig", "url-factory"],
6
+ "files": [
7
+ {
8
+ "to": "src/api/client.ts",
9
+ "target": "shell",
10
+ "from": "files/client.ts"
11
+ },
12
+ {
13
+ "to": "src/api/client.ts",
14
+ "target": "remotes",
15
+ "from": "files/client.ts"
16
+ },
17
+ {
18
+ "to": "src/api/client.test.ts",
19
+ "target": "shell",
20
+ "from": "files/client-test.ts",
21
+ "when": {
22
+ "feature:testing": true
23
+ }
24
+ },
25
+ {
26
+ "to": "src/api/client.test.ts",
27
+ "target": "remotes",
28
+ "from": "files/client-test.ts",
29
+ "when": {
30
+ "feature:testing": true
31
+ }
32
+ }
33
+ ],
34
+ "dependencies": [
35
+ {
36
+ "name": "@mfeflow/http",
37
+ "range": "latest",
38
+ "target": "shell"
39
+ },
40
+ {
41
+ "name": "@mfeflow/http",
42
+ "range": "latest",
43
+ "target": "remotes"
44
+ }
45
+ ],
46
+ "transforms": [
47
+ {
48
+ "op": "add-env-key",
49
+ "file": "src/config/schema.ts",
50
+ "target": "shell",
51
+ "key": "apiUrl",
52
+ "scope": "runtime",
53
+ "schema": {
54
+ "zod": "z.string().min(1)",
55
+ "valibot": "v.pipe(v.string(), v.minLength(1))",
56
+ "arktype": "'string > 0'"
57
+ },
58
+ "value": "/api",
59
+ "configFiles": [
60
+ "public/config/runtime-config.dev.json",
61
+ "public/config/runtime-config.staging.json",
62
+ "public/config/runtime-config.prod.json"
63
+ ],
64
+ "when": {
65
+ "feature:env-t3": true
66
+ }
67
+ },
68
+ {
69
+ "op": "add-env-key",
70
+ "file": "public/config/runtime-config.json",
71
+ "target": "shell",
72
+ "key": "apiUrl",
73
+ "scope": "runtime",
74
+ "schema": "z.string().min(1)",
75
+ "value": "/api",
76
+ "when": {
77
+ "feature:env-t3": false
78
+ }
79
+ }
80
+ ]
81
+ }
@@ -0,0 +1,19 @@
1
+ import { isHttpError } from '@mfeflow/http';
2
+ import { http, HttpResponse } from 'msw';
3
+ import { describe, expect, it } from 'vitest';
4
+ import { server } from '../mocks/server';
5
+ import { api } from './client';
6
+
7
+ describe('the api client', () => {
8
+ it('reads an endpoint through the mock server', async () => {
9
+ expect(await api.get('health')).toEqual({ ok: true });
10
+ });
11
+
12
+ it('turns a failure into an HttpError with the status', async () => {
13
+ server.use(
14
+ http.get('*/api/health', () => HttpResponse.json({ error: 'down' }, { status: 404 })),
15
+ );
16
+ const error = await api.get('health').catch((e: unknown) => e);
17
+ expect(isHttpError(error) && error.status).toBe(404);
18
+ });
19
+ });
@@ -0,0 +1,20 @@
1
+ import { createHttpClient } from '@mfeflow/http';
2
+ import { apiUrl, type endpoints } from './endpoints';
3
+
4
+ // What each endpoint answers with, by the name in ./endpoints. Add a line per endpoint you call.
5
+ interface Responses {
6
+ health: { ok: boolean };
7
+ }
8
+
9
+ /**
10
+ * One client per app over the endpoints in ./endpoints. The base URL is the runtime config's
11
+ * `apiUrl`. Idempotent requests retry on a network error, a timeout and a 502, 503 or 504; a
12
+ * failure is an HttpError with the status and body, and no URL in its message.
13
+ *
14
+ * const { ok } = await api.get('health');
15
+ */
16
+ export const api = createHttpClient<typeof endpoints, Responses>({
17
+ url: apiUrl,
18
+ // Return the signed-in user's token; no header is sent while this returns undefined.
19
+ // getToken: () => session.accessToken(),
20
+ });
@@ -0,0 +1,60 @@
1
+ {
2
+ "name": "lint-oxlint",
3
+ "version": "0.1.0",
4
+ "description": "oxlint with type-aware rules on TypeScript 7 (@mfeflow/oxlint-config), and Prettier.",
5
+ "requires": ["core", "tsconfig"],
6
+ "files": [
7
+ {
8
+ "to": ".oxlintrc.json",
9
+ "from": "files/oxlintrc.json"
10
+ },
11
+ {
12
+ "to": ".prettierrc.json",
13
+ "from": "files/prettierrc.json"
14
+ },
15
+ {
16
+ "to": ".prettierignore",
17
+ "from": "files/prettierignore"
18
+ }
19
+ ],
20
+ "dependencies": [
21
+ {
22
+ "name": "@mfeflow/oxlint-config",
23
+ "range": "latest",
24
+ "target": "root",
25
+ "dev": true
26
+ },
27
+ {
28
+ "name": "oxlint",
29
+ "range": "1.90.0",
30
+ "target": "root",
31
+ "dev": true
32
+ },
33
+ {
34
+ "name": "oxlint-tsgolint",
35
+ "range": "7.0.2005",
36
+ "target": "root",
37
+ "dev": true
38
+ },
39
+ {
40
+ "name": "prettier",
41
+ "range": "3.9.10",
42
+ "target": "root",
43
+ "dev": true
44
+ }
45
+ ],
46
+ "scripts": [
47
+ {
48
+ "name": "lint",
49
+ "command": "oxlint --type-aware --deny-warnings"
50
+ },
51
+ {
52
+ "name": "format",
53
+ "command": "prettier --write ."
54
+ },
55
+ {
56
+ "name": "format:check",
57
+ "command": "prettier --check ."
58
+ }
59
+ ]
60
+ }
@@ -0,0 +1,5 @@
1
+ {
2
+ "$schema": "./node_modules/oxlint/configuration_schema.json",
3
+ "extends": ["./node_modules/@mfeflow/oxlint-config/oxlintrc.json"],
4
+ "ignorePatterns": ["**/dist/**", "**/.turbo/**"]
5
+ }
@@ -0,0 +1,5 @@
1
+ dist
2
+ .turbo
3
+ coverage
4
+ pnpm-lock.yaml
5
+ CHANGELOG.md
@@ -0,0 +1,5 @@
1
+ {
2
+ "singleQuote": true,
3
+ "printWidth": 100,
4
+ "overrides": [{ "files": "*.yaml", "options": { "singleQuote": false } }]
5
+ }
@@ -0,0 +1,60 @@
1
+ {
2
+ "name": "route-factory",
3
+ "version": "0.1.0",
4
+ "description": "Typed links between remotes (@mfeflow/route-factory): src/routes.ts in every remote. With event-bus the paths live in the contracts package, so remotes link to each other without importing each other.",
5
+ "requires": ["core", "tsconfig"],
6
+ "files": [
7
+ {
8
+ "to": "src/routes.ts",
9
+ "target": "remotes",
10
+ "from": "files/routes.ts.eta",
11
+ "template": true,
12
+ "when": {
13
+ "feature:event-bus": false
14
+ }
15
+ },
16
+ {
17
+ "to": "src/routes.ts",
18
+ "target": "remotes",
19
+ "from": "files/routes-contract.ts.eta",
20
+ "template": true,
21
+ "when": {
22
+ "feature:event-bus": true
23
+ }
24
+ },
25
+ {
26
+ "to": "packages/contracts/src/routes/{{app.name}}.ts",
27
+ "target": "remotes",
28
+ "atRoot": true,
29
+ "from": "files/contract.ts.eta",
30
+ "template": true,
31
+ "when": {
32
+ "feature:event-bus": true
33
+ }
34
+ },
35
+ {
36
+ "to": "src/routes.test.ts",
37
+ "target": "remotes",
38
+ "from": "files/routes-test.ts.eta",
39
+ "template": true,
40
+ "when": {
41
+ "feature:testing": true
42
+ }
43
+ }
44
+ ],
45
+ "dependencies": [
46
+ {
47
+ "name": "@mfeflow/route-factory",
48
+ "range": "latest",
49
+ "target": "remotes"
50
+ },
51
+ {
52
+ "name": "{{scope}}/contracts",
53
+ "range": "workspace:*",
54
+ "target": "remotes",
55
+ "when": {
56
+ "feature:event-bus": true
57
+ }
58
+ }
59
+ ]
60
+ }
@@ -0,0 +1,7 @@
1
+ /**
2
+ * <%= it.app.name %>'s public paths, as plain data: no imports, so any remote can read them.
3
+ * `:id` is a path param; `?q&page?` after the path names search params (q is required).
4
+ */
5
+ export const routes = {
6
+ home: '/',
7
+ } as const;
@@ -0,0 +1,11 @@
1
+ import { createLinks, defineRoutes } from '@mfeflow/route-factory';
2
+ import { routes as contract } from '<%= it.scope %>/contracts/routes/<%= it.app.name %>';
3
+
4
+ // The paths are in the contracts package, so another remote can link here without importing this
5
+ // one. `/<%= it.app.name %>` is where the shell serves this remote.
6
+ export const routes = defineRoutes('/<%= it.app.name %>', contract);
7
+
8
+ // Links to this remote's routes. Another remote links here with
9
+ // import { routes as <%= it.app.name %> } from '<%= it.scope %>/contracts/routes/<%= it.app.name %>'
10
+ // and createLinks({ <%= it.app.name %>: defineRoutes('/<%= it.app.name %>', <%= it.app.name %>) }).
11
+ export const links = createLinks({ <%= it.app.name %>: routes });
@@ -0,0 +1,9 @@
1
+ import { describe, expect, it } from 'vitest';
2
+ import { links, routes } from './routes';
3
+
4
+ describe('the <%= it.app.name %> routes', () => {
5
+ it('are served under /<%= it.app.name %>', () => {
6
+ expect(routes.mount).toBe('/<%= it.app.name %>');
7
+ expect(links.href('<%= it.app.name %>.home')).toBe('/<%= it.app.name %>');
8
+ });
9
+ });
@@ -0,0 +1,11 @@
1
+ import { createLinks, defineRoutes } from '@mfeflow/route-factory';
2
+
3
+ // <%= it.app.name %>'s public paths. `/<%= it.app.name %>` is where the shell serves this remote.
4
+ // A route is a path template: `:id` is a path param, and `?q&page?` after the path names search
5
+ // params (q is required, page is optional). Links to a route are checked against this table.
6
+ export const routes = defineRoutes('/<%= it.app.name %>', {
7
+ home: '/',
8
+ });
9
+
10
+ // Links to this remote's routes. Another remote links here by adding `{ <%= it.app.name %>: routes }` to its own createLinks.
11
+ export const links = createLinks({ <%= it.app.name %>: routes });