@bleedingdev/modern-js-app-tools 3.9.0-ultramodern.2 → 3.9.0-ultramodern.20

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 (138) hide show
  1. package/dist/cjs/builder/generator/createCopyPattern.js +2 -1
  2. package/dist/cjs/builder/generator/index.js +0 -1
  3. package/dist/cjs/builder/shared/builderPlugins/adapterHtml.js +2 -1
  4. package/dist/cjs/builder/shared/builderPlugins/index.js +4 -11
  5. package/dist/cjs/builder/shared/lazyCompilation.js +26 -0
  6. package/dist/cjs/commands/index.js +6 -2
  7. package/dist/cjs/compat/hooks.js +1 -1
  8. package/dist/cjs/config/index.js +4 -11
  9. package/dist/cjs/index.js +70 -87
  10. package/dist/cjs/plugins/analyze/getFileSystemEntry.js +1 -1
  11. package/dist/cjs/plugins/analyze/getServerRoutes.js +1 -1
  12. package/dist/cjs/plugins/analyze/utils.js +2 -2
  13. package/dist/cjs/plugins/deploy/index.js +7 -28
  14. package/dist/cjs/plugins/deploy/platforms/netlify.js +2 -1
  15. package/dist/cjs/plugins/deploy/platforms/node.js +12 -16
  16. package/dist/cjs/plugins/deploy/platforms/vercel.js +2 -1
  17. package/dist/cjs/plugins/deploy/utils/generator.js +1 -1
  18. package/dist/cjs/plugins/deploy/utils/index.js +15 -12
  19. package/dist/cjs/plugins/initialize/index.js +3 -1
  20. package/dist/cjs/plugins/serverBuild.js +2 -3
  21. package/dist/cjs/{plugins/backendFederationBuild.js → server-plugin.js} +4 -7
  22. package/dist/cjs/utils/loadPlugins.js +1 -6
  23. package/dist/esm/builder/generator/createCopyPattern.mjs +2 -1
  24. package/dist/esm/builder/generator/index.mjs +1 -2
  25. package/dist/esm/builder/shared/builderPlugins/adapterHtml.mjs +2 -1
  26. package/dist/esm/builder/shared/builderPlugins/index.mjs +0 -1
  27. package/dist/esm/builder/shared/lazyCompilation.mjs +14 -1
  28. package/dist/esm/commands/index.mjs +6 -2
  29. package/dist/esm/compat/hooks.mjs +1 -1
  30. package/dist/esm/config/index.mjs +0 -1
  31. package/dist/esm/index.mjs +9 -6
  32. package/dist/esm/plugins/analyze/getFileSystemEntry.mjs +1 -1
  33. package/dist/esm/plugins/analyze/getServerRoutes.mjs +1 -1
  34. package/dist/esm/plugins/analyze/utils.mjs +2 -2
  35. package/dist/esm/plugins/deploy/index.mjs +5 -21
  36. package/dist/esm/plugins/deploy/platforms/netlify.mjs +2 -1
  37. package/dist/esm/plugins/deploy/platforms/node.mjs +12 -16
  38. package/dist/esm/plugins/deploy/platforms/vercel.mjs +2 -1
  39. package/dist/esm/plugins/deploy/utils/generator.mjs +1 -1
  40. package/dist/esm/plugins/deploy/utils/index.mjs +17 -14
  41. package/dist/esm/plugins/initialize/index.mjs +3 -1
  42. package/dist/esm/plugins/serverBuild.mjs +2 -3
  43. package/dist/esm/server-plugin.mjs +1 -0
  44. package/dist/esm/utils/loadPlugins.mjs +2 -7
  45. package/dist/esm-node/builder/generator/createCopyPattern.mjs +2 -1
  46. package/dist/esm-node/builder/generator/index.mjs +1 -2
  47. package/dist/esm-node/builder/shared/builderPlugins/adapterHtml.mjs +2 -1
  48. package/dist/esm-node/builder/shared/builderPlugins/index.mjs +0 -1
  49. package/dist/esm-node/builder/shared/lazyCompilation.mjs +14 -1
  50. package/dist/esm-node/commands/index.mjs +6 -2
  51. package/dist/esm-node/compat/hooks.mjs +1 -1
  52. package/dist/esm-node/config/index.mjs +0 -1
  53. package/dist/esm-node/index.mjs +9 -6
  54. package/dist/esm-node/plugins/analyze/getFileSystemEntry.mjs +1 -1
  55. package/dist/esm-node/plugins/analyze/getServerRoutes.mjs +1 -1
  56. package/dist/esm-node/plugins/analyze/utils.mjs +2 -2
  57. package/dist/esm-node/plugins/deploy/index.mjs +5 -21
  58. package/dist/esm-node/plugins/deploy/platforms/netlify.mjs +2 -1
  59. package/dist/esm-node/plugins/deploy/platforms/node.mjs +12 -16
  60. package/dist/esm-node/plugins/deploy/platforms/vercel.mjs +2 -1
  61. package/dist/esm-node/plugins/deploy/utils/generator.mjs +1 -1
  62. package/dist/esm-node/plugins/deploy/utils/index.mjs +17 -15
  63. package/dist/esm-node/plugins/initialize/index.mjs +3 -1
  64. package/dist/esm-node/plugins/serverBuild.mjs +2 -3
  65. package/dist/esm-node/server-plugin.mjs +2 -0
  66. package/dist/esm-node/utils/loadPlugins.mjs +2 -7
  67. package/dist/types/builder/shared/builderPlugins/index.d.ts +0 -1
  68. package/dist/types/builder/shared/lazyCompilation.d.ts +15 -0
  69. package/dist/types/config/index.d.ts +0 -1
  70. package/dist/types/index.d.ts +2 -2
  71. package/dist/types/plugins/deploy/index.d.ts +1 -4
  72. package/dist/types/plugins/deploy/utils/generator.d.ts +1 -1
  73. package/dist/types/plugins/deploy/utils/index.d.ts +1 -1
  74. package/dist/types/server-plugin.d.ts +10 -0
  75. package/dist/types/types/config/output.d.ts +0 -9
  76. package/dist/types/types/index.d.ts +1 -1
  77. package/dist/types/types/plugin.d.ts +58 -2
  78. package/dist/types/utils/initAppContext.d.ts +3 -2
  79. package/docs/apis/app/commands.md +1 -0
  80. package/docs/configure/app/bff/effect.md +34 -36
  81. package/docs/configure/app/source/react-compiler.md +2 -0
  82. package/docs/guides/advanced-features/bff/data-platform.md +2 -2
  83. package/docs/guides/advanced-features/bff/frameworks.md +36 -24
  84. package/docs/guides/advanced-features/bff/function.md +2 -2
  85. package/docs/guides/advanced-features/bff/operators.md +17 -17
  86. package/docs/guides/basic-features/render/ssr-cache.md +14 -1
  87. package/docs/guides/get-started/quick-start.md +2 -6
  88. package/docs/guides/get-started/ultramodern.md +14 -97
  89. package/docs/llms.txt +3 -3
  90. package/docs/plugin/server-plugins/api.md +1 -0
  91. package/package.json +38 -36
  92. package/dist/cjs/builder/shared/builderPlugins/adapterPrecompress.js +0 -125
  93. package/dist/cjs/config/build-environment.js +0 -268
  94. package/dist/cjs/config/public.js +0 -46
  95. package/dist/cjs/plugins/backend-federation/codegen.js +0 -256
  96. package/dist/cjs/plugins/backend-federation/config.js +0 -204
  97. package/dist/cjs/plugins/backend-federation/emit.js +0 -150
  98. package/dist/cjs/plugins/deploy/platforms/cloudflare-output-verifier/index.js +0 -62
  99. package/dist/cjs/plugins/deploy/utils/npmAliases.js +0 -226
  100. package/dist/cjs/plugins/ultramodernReleaseEnvelope.js +0 -44
  101. package/dist/cjs/presetUltramodern.js +0 -168
  102. package/dist/cjs/types/config/precompress.js +0 -18
  103. package/dist/esm/builder/shared/builderPlugins/adapterPrecompress.mjs +0 -77
  104. package/dist/esm/config/build-environment.mjs +0 -224
  105. package/dist/esm/config/public.mjs +0 -1
  106. package/dist/esm/plugins/backend-federation/codegen.mjs +0 -205
  107. package/dist/esm/plugins/backend-federation/config.mjs +0 -128
  108. package/dist/esm/plugins/backend-federation/emit.mjs +0 -99
  109. package/dist/esm/plugins/backendFederationBuild.mjs +0 -1
  110. package/dist/esm/plugins/deploy/platforms/cloudflare-output-verifier/index.mjs +0 -1
  111. package/dist/esm/plugins/deploy/utils/npmAliases.mjs +0 -174
  112. package/dist/esm/plugins/ultramodernReleaseEnvelope.mjs +0 -6
  113. package/dist/esm/presetUltramodern.mjs +0 -112
  114. package/dist/esm/types/config/precompress.mjs +0 -0
  115. package/dist/esm-node/builder/shared/builderPlugins/adapterPrecompress.mjs +0 -78
  116. package/dist/esm-node/config/build-environment.mjs +0 -225
  117. package/dist/esm-node/config/public.mjs +0 -2
  118. package/dist/esm-node/plugins/backend-federation/codegen.mjs +0 -206
  119. package/dist/esm-node/plugins/backend-federation/config.mjs +0 -129
  120. package/dist/esm-node/plugins/backend-federation/emit.mjs +0 -100
  121. package/dist/esm-node/plugins/backendFederationBuild.mjs +0 -2
  122. package/dist/esm-node/plugins/deploy/platforms/cloudflare-output-verifier/index.mjs +0 -2
  123. package/dist/esm-node/plugins/deploy/utils/npmAliases.mjs +0 -175
  124. package/dist/esm-node/plugins/ultramodernReleaseEnvelope.mjs +0 -7
  125. package/dist/esm-node/presetUltramodern.mjs +0 -113
  126. package/dist/esm-node/types/config/precompress.mjs +0 -1
  127. package/dist/types/builder/shared/builderPlugins/adapterPrecompress.d.ts +0 -3
  128. package/dist/types/config/build-environment.d.ts +0 -22
  129. package/dist/types/config/public.d.ts +0 -2
  130. package/dist/types/plugins/backend-federation/codegen.d.ts +0 -86
  131. package/dist/types/plugins/backend-federation/config.d.ts +0 -103
  132. package/dist/types/plugins/backend-federation/emit.d.ts +0 -13
  133. package/dist/types/plugins/backendFederationBuild.d.ts +0 -2
  134. package/dist/types/plugins/deploy/platforms/cloudflare-output-verifier/index.d.ts +0 -1
  135. package/dist/types/plugins/deploy/utils/npmAliases.d.ts +0 -14
  136. package/dist/types/plugins/ultramodernReleaseEnvelope.d.ts +0 -3
  137. package/dist/types/presetUltramodern.d.ts +0 -87
  138. package/dist/types/types/config/precompress.d.ts +0 -20
@@ -37,7 +37,7 @@ export interface CacheControl {
37
37
  }
38
38
  ```
39
39
 
40
- Here, `customKey` is the custom cache key. By default, Modern.js uses the request `pathname` as the cache key, but developers can define it when necessary.
40
+ Here, `customKey` is the custom cache key. By default, the cache key is derived from the request's origin, normalized pathname (trailing slash removed), and query string. When `customKey` is set, it fully replaces the default key: the `customKey` callback receives the normalized pathname (not the `CacheOptionProvider`'s `req`) and is responsible for the whole partition (e.g. by tenant, host, or query) — it must not embed raw cookies or other credentials into the returned key.
41
41
 
42
42
  **Function Type**
43
43
 
@@ -108,6 +108,19 @@ export const cacheOption: CacheOption = {
108
108
 
109
109
  The above `/home` and `/about` are patterns, meaning `/home/abc` will also match. You can use regex in these patterns, such as `/home/.+`.
110
110
 
111
+ ### Cache Policy
112
+
113
+ The following policy is enforced automatically and cannot be overridden by the returned `CacheControl`:
114
+
115
+ - Only `GET` requests are cached.
116
+ - A request carrying a `Cookie` or `Authorization` header skips the cache unless `customKey` is set, since a custom key is treated as an explicit partitioning contract.
117
+ - A request with `Cache-Control: private`, `no-cache`, or `no-store` skips the cache.
118
+ - A response is only stored when it is `200`, has no `private`/`no-cache`/`no-store` in `Cache-Control`, has no `Set-Cookie`, and has no nonempty `Vary` header; otherwise any existing cache entry for that key is deleted instead of being written.
119
+ - If a `stale` revalidation produces a response that is no longer cacheable (for example, it became per-user), that already-served stale response is unaffected, but the cache entry is evicted so subsequent requests no longer hit it.
120
+ - A `CacheOptionProvider` returning `false` still disables caching entirely for that request, independent of the policy above.
121
+
122
+ Cache keys are namespaced (`__ssr__cache:v2:...`). Older un-namespaced entries — including ones already stored in a custom `Container` — are never read back; they simply age out under the container's own eviction/TTL. There is no global cache flush.
123
+
111
124
  ### Cache Container
112
125
 
113
126
  By default, the server uses memory for caching. Typically, services are deployed in a Serverless container, creating a new process for each access, making it impossible to use the previous cache.
@@ -40,7 +40,7 @@ Additionally, after installing nvm or fnm, when there is a `.nvmrc` file contain
40
40
  It is recommended to use [pnpm](https://pnpm.io/installation) to manage dependencies:
41
41
 
42
42
  ```bash
43
- mise use pnpm@11.24.0
43
+ mise use pnpm@11.27.1
44
44
  ```
45
45
 
46
46
  :::note
@@ -125,11 +125,7 @@ Now, the project structure is as follows:
125
125
  ```
126
126
 
127
127
  The default workspace starts shell-only and installs the published BleedingDev
128
- package aliases:
129
-
130
- ```bash
131
- pnpm dlx @bleedingdev/modern-js-ultramodern-create my-super-app
132
- ```
128
+ package aliases.
133
129
 
134
130
  From a generated SuperApp workspace, add a business MicroVertical in place:
135
131
 
@@ -15,17 +15,9 @@ UltraModern.js 3.0 is our SuperApp framework forked from Modern.js. It keeps the
15
15
  - Add platform-level contracts only where they improve cross-team reliability.
16
16
  - Keep escape hatches explicit and outside the generated HTTP API path.
17
17
 
18
- ## Compatibility Contract
18
+ ## Current Workspace Contract
19
19
 
20
- UltraModern.js 3.0 keeps:
21
-
22
- - Modern.js app/config/plugin mental model.
23
- - Existing project structure and command flow.
24
- - Progressive adoption path (apps can stay mostly unchanged).
25
-
26
- UltraModern.js additions are designed as the default product surface for new SuperApps. The framework direction is Effect + TanStack + SSR + Micro Verticals, and generated HTTP API work uses that direction by default instead of preserving parallel raw-handler layouts.
27
- Existing Modern.js apps can migrate gradually; once an API surface is generated
28
- or migrated as UltraModern HTTP API, it must use the strict Effect HttpApi path.
20
+ UltraModern.js uses Effect HttpApi, TanStack Router, SSR, and independently deployable Micro Verticals. Generated workspaces use `api/index.ts`, `shared/api.ts`, and `src/api/*` with concrete Effect schemas. The CLI creates, adds to, and validates workspaces against the current contract.
29
21
 
30
22
  ## Intentional Differences (v3 line)
31
23
 
@@ -53,21 +45,9 @@ or migrated as UltraModern HTTP API, it must use the strict Effect HttpApi path.
53
45
  - Generated UltraModern API work uses the Effect runtime only; raw Hono/function handlers are not part of the generated API architecture.
54
46
  - We make incompatible scaffold changes when they remove architecture drift.
55
47
 
56
- ## Migration Guide
57
-
58
- For teams already on Modern.js 3.0 or an older BleedingDev UltraModern scaffold, the adoption path is to move API work onto the strict Effect HttpApi surface instead of preserving older raw handler layouts.
59
-
60
- 1. Keep existing Modern.js apps running as-is while they are outside the generated UltraModern surface. TanStack Router is the preferred path for new scaffolds and incremental route adoption, but route migration can happen on the team's schedule.
61
- 2. Use `bff.runtimeFramework: 'effect'` with `bff.effect.strictEffectApproach: true` for API work. Entries live at `api/index.ts`, contracts live at `shared/api.ts`, clients live under `src/api/*`, and request/response/error shapes come from Effect `Schema` plus `HttpApi`.
62
- 3. Treat raw handlers, `api/lambda/**`, manual `Response` construction, and manual request parsing as migration defects in generated or migrated UltraModern workspaces.
63
- 4. The public preset now ships with explicit release and certification gates. Generated workspaces include `.github/workflows/ultramodern-workspace-gates.yml`, so `pnpm check` and `pnpm build` stay part of the local adoption contract from day one while CI runs the primitive gates as parallel matrix jobs.
64
-
65
- For an older generated workspace, migrate by treating the published cohort as
66
- the source of truth:
48
+ ## Create and Validate a Workspace
67
49
 
68
50
  ```bash
69
- pnpm dlx @bleedingdev/modern-js-ultramodern-create@latest --help
70
- pnpm dlx @bleedingdev/modern-js-ultramodern-create@latest catalog --vertical --dry-run
71
51
  pnpm dlx @bleedingdev/modern-js-ultramodern-create@latest catalog --vertical
72
52
  mise install
73
53
  mise exec -- pnpm install
@@ -75,94 +55,31 @@ mise exec -- pnpm check
75
55
  mise exec -- pnpm build
76
56
  ```
77
57
 
78
- ### Migrating localized Cloudflare SSR workspaces
79
-
80
- Current cohorts localize Cloudflare SSR workspaces.
81
- The framework moves bare locale-root redirects into the framework-owned
82
- Cloudflare Worker entry and the i18n server runtime. A request for `/` is
83
- redirected server-side with `302` to the negotiated locale path, for example
84
- `/cs` for `Accept-Language: cs-CZ` or `/en` for English and fallback traffic.
85
-
86
- Existing generated workspaces should upgrade the whole BleedingDev Modern
87
- package cohort together. Resolve the current cohort version first, then run
88
- the matching migration:
89
-
90
- ```bash
91
- COHORT="$(npm view @bleedingdev/modern-js-ultramodern-create version)"
92
- pnpm dlx "@bleedingdev/modern-js-ultramodern-create@$COHORT" ultramodern \
93
- migrate-strict-effect --version "$COHORT"
94
- pnpm install
95
- pnpm check
96
- pnpm build
97
- pnpm cloudflare:build
98
- ```
99
-
100
- For deployed Cloudflare Workers, redeploy after the cohort update and verify
101
- the root response before accepting the migration:
58
+ Localized Cloudflare SSR workspaces redirect `/` on the server to the negotiated locale. For example, `Accept-Language: cs-CZ` returns `302` with `Location: /cs`, `Cache-Control: private, no-store`, and `Vary` covering locale detection headers. Validate the deployed Worker with:
102
59
 
103
60
  ```bash
104
61
  curl -I -H 'Accept-Language: cs-CZ,cs;q=0.9,en;q=0.1' https://<worker-host>/
105
62
  ```
106
63
 
107
- The expected response is `302` with `Location: /cs` or the matching locale,
108
- `Cache-Control: private, no-store`, and `Vary` covering the locale detection
109
- headers. Following the redirect should return the SSR locale page with the
110
- matching document language and i18n SSR data.
111
-
112
- Do not fix older root `404` responses by adding app-owned root route files,
113
- client-side redirects, custom navigation wrappers, Cloudflare Worker
114
- postprocessing, generated output edits, or local redirect shims. If `/` still
115
- returns `404` after upgrading, confirm the production build log shows
116
- `Modern.js Framework v<cohort-version>` for the cohort you migrated to and
117
- redeploy the Worker.
118
-
119
- Strict generated API migration is part of every current cohort: the direct
120
- `api/index.ts` generator, generated `.mts` checks, the strict Oxlint boundary
121
- rule set, Effect cohort overrides, and the strict Effect migration command.
122
- Agents that cannot install the BleedingDev cohort yet should use the local
123
- Modern.js workspace for migration validation; otherwise pin the target cohort
124
- with `--ultramodern-package-version`.
125
-
126
- Before hand-editing package aliases or generated metadata, run the framework
127
- migration command from the target workspace:
128
-
129
- ```bash
130
- COHORT="$(npm view @bleedingdev/modern-js-ultramodern-create version)"
131
- pnpm dlx "@bleedingdev/modern-js-ultramodern-create@$COHORT" ultramodern \
132
- migrate-strict-effect --version "$COHORT"
133
- pnpm api:check
134
- pnpm contract:check
135
- ```
136
-
137
- The command updates `.modernjs/ultramodern.json`, root
138
- `modernjs.packageSource`, generated Modern package aliases, framework-owned
139
- toolchain pins, direct topology API metadata, strict Effect pnpm
140
- overrides/trust policy, and the pnpm lockfile. Remaining failures are source
141
- migration work: move code to `shared/api.ts`, `api/index.ts`, and
142
- `src/api/*-client.ts`, then delete `api/effect`, `api/lambda`, `shared/effect`,
143
- and `src/effect`.
144
-
145
64
  Generated strict Effect workspaces pin the compatible Effect cohort with pnpm
146
- overrides: `effect@4.0.0-rc.112`, `@effect/opentelemetry@4.0.0-rc.112`,
147
- and `@effect/vitest@4.0.0-rc.112`. Do not add app-local direct Effect
65
+ overrides: `effect@4.0.0-rc.117`, `@effect/opentelemetry@4.0.0-rc.117`,
66
+ and `@effect/vitest@4.0.0-rc.117`. Do not add app-local direct Effect
148
67
  versions that disagree with those overrides. The strict 24-hour release-age
149
68
  gate applies to installed packages; this cohort carries no Effect age
150
69
  exemption, and override-only `@effect/vitest` is not an installed approval
151
- target. Independently, exact `trustPolicyExclude` entries for `effect`
152
- and `@effect/opentelemetry` cover their trusted-publisher to provenance
153
- metadata transition; they are not release-age approvals.
70
+ target.
154
71
 
155
72
  ### Current generated dependency baseline
156
73
 
157
74
  | Surface | Pin |
158
75
  | ------------------------------ | --------------------------------------------------------------------------------------------------- |
159
- | Effect runtime and test cohort | `effect`, `@effect/opentelemetry`, and `@effect/vitest`: `4.0.0-rc.112` |
160
- | Effect compiler | `@effect/tsgo@0.41.0` |
161
- | Oxc and Ultracite | `oxlint@1.81.0`, `oxfmt@0.66.0`, `ultracite@7.11.0` |
162
- | TanStack Router | `@tanstack/react-router@1.170.33`, `@tanstack/router-core@1.171.28`, `@tanstack/history@1.162.2` |
163
- | Module Federation | `bridge-react`, `manifest`, `modern-js-v3`, and `rspack`: `2.9.0`; `@module-federation/node@2.7.50` |
76
+ | Effect runtime and test cohort | `effect`, `@effect/opentelemetry`, and `@effect/vitest`: `4.0.0-rc.117` |
77
+ | Effect compiler | `@effect/tsgo@0.45.0` |
78
+ | Oxc and Ultracite | `oxlint@1.85.0`, `oxfmt@0.70.0`, `ultracite@7.12.0` |
79
+ | TanStack Router | `@tanstack/react-router@1.170.39`, `@tanstack/router-core@1.171.32`, `@tanstack/history@1.162.4` |
80
+ | Module Federation | `bridge-react`, `manifest`, `modern-js-v3`, and `rspack`: `2.9.2`; `@module-federation/node@2.7.52` |
164
81
  | Tailwind CSS | `tailwindcss@4.3.3` |
165
- | Node and package tooling | Node `26.7.0`, `@types/node@^26.4.1`, pnpm `11.24.0` |
82
+ | Node and package tooling | Node `26.7.0`, `@types/node@^26.6.2`, pnpm `11.27.1` |
166
83
 
167
84
  Generated workspaces default to Module Federation bridge-react's router-free
168
85
  base entry (`bridge.enableBridgeRouter: false`), with TanStack Router as the
@@ -210,7 +127,7 @@ transport surfaces when needed. They do not make raw request handlers valid
210
127
  inside generated HTTP API modules.
211
128
 
212
129
  Strict API tests should exercise the `HttpApi` contract. Use
213
- `createEffectBffTestHandler` from `@modern-js/plugin-bff/effect-edge` for
130
+ `createEffectBffTestHandler` from `@modern-js/bff-effect/effect-edge` for
214
131
  edge-compatible proof tests; if you manually compose a web handler, provide
215
132
  `HttpServer.layerServices` beside your API group layer before calling
216
133
  `HttpRouter.toWebHandler`.
package/docs/llms.txt CHANGED
@@ -47,7 +47,7 @@
47
47
  - [Deploy Application](/guides/basic-features/deploy.md): Currently, Modern.js offers two deployment way: You can host your application in a container that includes a Node.js environment on your own, which provides flexibility for the deployment of the application.You can also deploy your application through a platform. Currently, Modern.js officially supports deployment on Netlify, Vercel, and Github pages.
48
48
  - [BFF](/guides/advanced-features/bff.md): BFF (Backends for Frontends) is an architectural pattern primarily used to address issues of data aggregation in front-end and back-end collaboration. Under the BFF architecture, front-end applications do not communicate directly with backend services. Instead, they interact with backend services through a dedicated BFF middleware layer, custom-made for the front end. The main problems it tries to solve include: Aggregation, mapping, clipping, and proxying of lower-level APIs according to their own business needs.Cache data for some specific scenarios to improve performance and thus improve user experience.Quickly development of new products based on existing interfaces.Interface with third-party systems, such as login authentication. Modern.js officially supported the BFF and provided the Integrated BFF to further strengthen the BFF's capabilities, mainly including the following capabilities: Quick development and debugging go live, running, building, and deploying BFF code in the same project.Minimal pure function call, directly import BFF function on the front end, and can be automatically converted into HTTP request when called.No private protocol, follow RESTful API specification, all BFF interfaces are standardized.Full TypeScript support.Meet user preferences and support multi-frame extension writing.
49
49
  - [Basic Usage](/guides/advanced-features/bff/function.md): In a Modern.js application, developers can define API files under the api/lambda directory and export API functions from these files. In the frontend code, these API functions can be directly invoked by importing the file, which initiates the API requests. This invocation method is called unified invocation, where developers do not need to write glue code for the frontend and backend separately, thereby ensuring type safety across both.
50
- - [Runtime Framework](/guides/advanced-features/bff/frameworks.md): Modern.js supports two BFF runtime frameworks: effect (default): use Effect HttpApi runtime from api/index.hono: use file-convention BFF handlers from api/lambda/**. effect and hono are strict runtime modes. There is no automatic fallback between them. ::::info Generated UltraModern workspaces use strict Effect APIs: author HTTP APIs in shared/api.ts and api/index.ts, not Hono/file-convention handlers. ::::
50
+ - [Runtime Framework](/guides/advanced-features/bff/frameworks.md): Modern.js and the UltraModern BFF extension provide two runtime frameworks: hono is the native plugin default and uses file-convention handlers from api/lambda/**.effect is the UltraModern extension default and uses Effect HttpApi from api/index. effect and hono are strict runtime modes. There is no automatic fallback between them. ::::info Generated UltraModern workspaces use strict Effect APIs: author HTTP APIs in shared/api.ts and api/index.ts, not Hono/file-convention handlers. ::::
51
51
  - [Data Platform](/guides/advanced-features/bff/data-platform.md): Modern.js Effect API runtime now supports a request-envelope based data platform contract for safer and more predictable data orchestration in complex applications such as Module Federation and micro frontends.
52
52
  - [Creating Extensible BFF Functions](/guides/advanced-features/bff/operators.md): The previous section showed how to export a simple BFF function in a file. In more complex scenarios, each BFF function may need to do independent type validation, pre-logic, etc. Therefore, Modern.js exposes Api, which supports creating BFF functions through this API. BFF functions created in this way can be easily extended with functionality.
53
53
  - [Extend BFF Server](/guides/advanced-features/bff/extend-server.md): In some applications, developers may want to handle all BFF functions uniformly, such as authentication, logging, data processing, etc. Modern.js allows users to freely extend the BFF Server through Middleware method.
@@ -111,7 +111,7 @@
111
111
  - [watchFiles](/configure/app/dev/watch-files.md): Type: Default: undefined Watch specified files and directories for changes. When a file change is detected, it can trigger a page reload or restart the dev server.
112
112
  - [writeToDisk](/configure/app/dev/write-to-disk.md): Type: boolean | ((filename: string) => boolean)Default: (file) => !file.includes('.hot-update.') Controls whether the build output from development mode is written to disk.
113
113
  - [crossProject](/configure/app/bff/cross-project.md): Type: booleanDefault: false This configuration is used to enable BFF cross-project invocation functionality. When enabled, the current project can be used as a BFF producer, generating an SDK that can be directly called by other projects. For detailed configuration and usage of BFF cross-project invocation, please refer to the BFF Cross-Project Invocation Guide.
114
- - [effect](/configure/app/bff/effect.md): Type: Default: {} bff.effect is only effective when bff.runtimeFramework is set to 'effect'. Generated UltraModern workspaces use this runtime as the only generated HTTP API path. The API contract lives at shared/api.ts, the server runtime lives at api/index.ts, clients live under src/api/*-client.ts, and generated checks reject api/effect, api/lambda, shared/effect, src/effect, Hono server imports, raw request handlers, manual request parsing, and manual Response construction in API modules. For Effect v4, TypeScript should use export-map aware module resolution.
114
+ - [effect](/configure/app/bff/effect.md): Type: Default: {} bff.effect is only effective when bff.runtimeFramework is set to 'effect'. Use bffPlugin from @modern-js/plugin-bff-build-extensions to register the Effect runtime. It composes the native BFF plugin. Install @modern-js/bff-effect and @modern-js/plugin-bff-extensions from the same framework cohort as application production dependencies, so the adapter remains available after development dependencies are removed. The build plugin can be a development dependency. See the runtime configuration example. Native @modern-js/plugin-bff/server exports Hono APIs. For Node Effect APIs, import framework helpers such as defineEffectBff from @modern-js/bff-effect/effect and namespaces from the corresponding effect/* modules. Worker handlers and worker request context use @modern-js/bff-effect/effect-edge. Generated UltraModern workspaces use this runtime as the only generated HTTP API path. The API contract lives at shared/api.ts, the server runtime lives at api/index.ts, clients live under src/api/*-client.ts, and generated checks reject api/effect, api/lambda, shared/effect, src/effect, Hono server imports, raw request handlers, manual request parsing, and manual Response construction in API modules. For Effect v4, TypeScript should use export-map aware module resolution.
115
115
  - [prefix](/configure/app/bff/prefix.md): Type: stringDefault: /api By default, the prefix for accessing routes in the BFF API directory is /api, as shown in the following directory structure: The route corresponding to api/hello.ts when accessed is localhost:8080/api/hello. This configuration option can modify the default route prefix: The corresponding route for api/hello.ts when accessed is localhost:8080/api-demo/hello.
116
116
  - [runtimeFramework](/configure/app/bff/runtime-framework.md): Type: 'hono' | 'effect'Default: 'effect' Controls which runtime framework Modern.js uses for BFF routing. Set to 'effect' to run BFF only from api/index (Effect HttpApi runtime).Set to 'hono' to run BFF only from file-convention handlers under api/lambda/**. There is no implicit fallback between runtimes. Choose one runtime mode per app. Generated UltraModern apps use strict Effect APIs by default and set bff.effect.strictEffectApproach: true; hono and api/lambda/** are not valid generated UltraModern HTTP API paths.
117
117
  - [appIcon](/configure/app/html/app-icon.md): Type: Default: undefined Set the web application icons to display when added to the home screen of a mobile device: Generate the web app manifest file and its icons field.Generate the apple-touch-icon and manifest tags in the HTML file.
@@ -156,7 +156,7 @@
156
156
  - [include](/configure/app/source/include.md): Type: RuleSetCondition[]Default value: source.include is used to specify additional JavaScript files that need to be compiled. By default, the following files will be compiled: TypeScript and JSX files in any directory, with file extensions matching .ts, .tsx, .jsx, .mts, .cts.JavaScript files not in the node_modules directory, with file extensions matching .js, .mjs, .cjs.
157
157
  - [mainEntryName](/configure/app/source/main-entry-name.md): Type: stringDefault: 'index' This option is used to configure the main entry name of Modern.js. By default, the default main entry name of Modern.js is index. When using a single entry or the entry name is the same as the name field in package.json, the corresponding entry will be used as the main entry. This configuration can be used to modify the name of the main entry. For example, change the main entry name to main: After building, the corresponding HTML output path will be changed to dist/html/index/index.html.
158
158
  - [preEntry](/configure/app/source/pre-entry.md): Type: string | string[]Default: [] Add a script before the entry file of each page. This script will be executed before the page code. It can be used to execute global logics, such as injecting polyfills, setting global styles, etc.
159
- - [reactCompiler](/configure/app/source/react-compiler.md): Type: boolean | ReactCompilerOptionsDefault: undefined (disabled) Whether to enable React Compiler. React Compiler is a build-time tool that optimizes re-rendering performance of React applications through automatic memoization. Modern.js implements this capability based on the Rust-based React Compiler built into Rspack's builtin:swc-loader (equivalent to setting SWC's jsc.transform.reactCompiler), reusing Rspack's built-in SWC transform chain without introducing Babel.
159
+ - [reactCompiler](/configure/app/source/react-compiler.md): Type: boolean | ReactCompilerOptionsDefault: undefined (disabled) Whether to enable React Compiler. React Compiler is a build-time tool that optimizes re-rendering performance of React applications through automatic memoization. Modern.js implements this capability based on the Rust-based React Compiler built into Rspack's builtin:swc-loader (equivalent to setting SWC's jsc.transform.reactCompiler), reusing Rspack's built-in SWC transform chain without introducing Babel. The compiler only runs for browser environments (output.target: 'web'). Server environments (node, the web-worker SSR worker and BFF code) render once per request and gain nothing from memoization, so they are never compiled.
160
160
  - [transformImport](/configure/app/source/transform-import.md): Transform the import path, which can be used to modularly import the subpath of third-party packages. The functionality is similar to babel-plugin-import。 Type: Default: undefined
161
161
  - [aliasStrategy](/configure/app/resolve/alias-strategy.md): Type: 'prefer-tsconfig' | 'prefer-alias'Default: 'prefer-tsconfig' Set the strategy for path alias resolution, to control the priority relationship between the paths option in tsconfig.json and the resolve.alias option of Rsbuild.
162
162
  - [alias](/configure/app/resolve/alias.md): Type: string[] Create aliases for module paths to simplify import paths or redirect module references.
@@ -191,6 +191,7 @@ Adds additional logic when the server resets.
191
191
  - `'file-change'`: File change event
192
192
  - `event.payload`: When `type` is `'file-change'`, contains an array of file change information.
193
193
  - **Execution Phase:** When files change or repack is needed.
194
+ - **Repack Order:** On `'repack'` the dev server awaits the handlers in registration order before it purges the previous server bundle from the require cache. A rejecting handler skips the handlers after it. New requests wait until the handlers settle; requests already running keep the modules they loaded. A repack handler must not await a request to the dev server, because that request waits for the handler.
194
195
  - **Example:**
195
196
 
196
197
  ```typescript
package/package.json CHANGED
@@ -17,7 +17,7 @@
17
17
  "modern",
18
18
  "modern.js"
19
19
  ],
20
- "version": "3.9.0-ultramodern.2",
20
+ "version": "3.9.0-ultramodern.20",
21
21
  "types": "./dist/types/index.d.ts",
22
22
  "main": "./dist/cjs/index.js",
23
23
  "exports": {
@@ -37,12 +37,6 @@
37
37
  },
38
38
  "default": "./dist/cjs/builder/index.js"
39
39
  },
40
- "./config": {
41
- "types": "./dist/types/config/public.d.ts",
42
- "import": "./dist/esm-node/config/public.mjs",
43
- "require": "./dist/cjs/config/public.js",
44
- "default": "./dist/cjs/config/public.js"
45
- },
46
40
  "./cli": {
47
41
  "types": "./dist/types/index.d.ts",
48
42
  "node": {
@@ -59,11 +53,10 @@
59
53
  },
60
54
  "default": "./dist/cjs/run/index.js"
61
55
  },
62
- "./cloudflare-output-verifier": {
63
- "types": "./dist/types/plugins/deploy/platforms/cloudflare-output-verifier/index.d.ts",
64
- "import": "./dist/esm-node/plugins/deploy/platforms/cloudflare-output-verifier/index.mjs",
65
- "require": "./dist/cjs/plugins/deploy/platforms/cloudflare-output-verifier/index.js",
66
- "default": "./dist/cjs/plugins/deploy/platforms/cloudflare-output-verifier/index.js"
56
+ "./server-plugin": {
57
+ "types": "./dist/types/server-plugin.d.ts",
58
+ "import": "./dist/esm-node/server-plugin.mjs",
59
+ "require": "./dist/cjs/server-plugin.js"
67
60
  },
68
61
  "./types": {
69
62
  "types": "./lib/types.d.ts",
@@ -85,15 +78,15 @@
85
78
  "cli/run": [
86
79
  "./dist/types/run/index.d.ts"
87
80
  ],
81
+ "server-plugin": [
82
+ "./dist/types/server-plugin.d.ts"
83
+ ],
88
84
  "types": [
89
85
  "./lib/types.d.ts"
90
86
  ],
91
87
  "builder": [
92
88
  "./dist/types/builder/index.d.ts"
93
89
  ],
94
- "config": [
95
- "./dist/types/config/public.d.ts"
96
- ],
97
90
  "bundle-docs": [
98
91
  "./dist/types/bundleDocs.d.ts"
99
92
  ]
@@ -130,23 +123,23 @@
130
123
  }
131
124
  },
132
125
  "dependencies": {
133
- "@babel/parser": "^8.0.4",
134
- "@babel/traverse": "^8.0.4",
135
- "@babel/types": "^8.0.4",
126
+ "@babel/parser": "^8.0.6",
127
+ "@babel/traverse": "^8.0.6",
128
+ "@babel/types": "^8.0.6",
136
129
  "@loadable/component": "5.16.7",
137
- "@modern-js/app-tools-extensions": "npm:@bleedingdev/modern-js-app-tools-extensions@3.9.0-ultramodern.2",
138
- "@modern-js/builder": "npm:@bleedingdev/modern-js-builder@3.9.0-ultramodern.2",
139
- "@modern-js/i18n-utils": "npm:@bleedingdev/modern-js-i18n-utils@3.9.0-ultramodern.2",
140
- "@modern-js/plugin": "npm:@bleedingdev/modern-js-plugin@3.9.0-ultramodern.2",
141
- "@modern-js/plugin-data-loader": "npm:@bleedingdev/modern-js-plugin-data-loader@3.9.0-ultramodern.2",
142
- "@modern-js/prod-server": "npm:@bleedingdev/modern-js-prod-server@3.9.0-ultramodern.2",
143
- "@modern-js/server": "npm:@bleedingdev/modern-js-server@3.9.0-ultramodern.2",
144
- "@modern-js/server-core": "npm:@bleedingdev/modern-js-server-core@3.9.0-ultramodern.2",
145
- "@modern-js/server-runtime-extensions": "npm:@bleedingdev/modern-js-server-runtime-extensions@3.9.0-ultramodern.2",
146
- "@modern-js/server-utils": "npm:@bleedingdev/modern-js-server-utils@3.9.0-ultramodern.2",
147
- "@modern-js/types": "npm:@bleedingdev/modern-js-types@3.9.0-ultramodern.2",
148
- "@modern-js/utils": "npm:@bleedingdev/modern-js-utils@3.9.0-ultramodern.2",
149
- "@rsbuild/core": "2.2.3",
130
+ "@modern-js/app-tools-extensions": "npm:@bleedingdev/modern-js-app-tools-extensions@3.9.0-ultramodern.20",
131
+ "@modern-js/builder": "npm:@bleedingdev/modern-js-builder@3.9.0-ultramodern.20",
132
+ "@modern-js/i18n-utils": "npm:@bleedingdev/modern-js-i18n-utils@3.9.0-ultramodern.20",
133
+ "@modern-js/plugin": "npm:@bleedingdev/modern-js-plugin@3.9.0-ultramodern.20",
134
+ "@modern-js/plugin-data-loader": "npm:@bleedingdev/modern-js-plugin-data-loader@3.9.0-ultramodern.20",
135
+ "@modern-js/prod-server": "npm:@bleedingdev/modern-js-prod-server@3.9.0-ultramodern.20",
136
+ "@modern-js/server": "npm:@bleedingdev/modern-js-server@3.9.0-ultramodern.20",
137
+ "@modern-js/server-core": "npm:@bleedingdev/modern-js-server-core@3.9.0-ultramodern.20",
138
+ "@modern-js/server-runtime-extensions": "npm:@bleedingdev/modern-js-server-runtime-extensions@3.9.0-ultramodern.20",
139
+ "@modern-js/server-utils": "npm:@bleedingdev/modern-js-server-utils@3.9.0-ultramodern.20",
140
+ "@modern-js/types": "npm:@bleedingdev/modern-js-types@3.9.0-ultramodern.20",
141
+ "@modern-js/utils": "npm:@bleedingdev/modern-js-utils@3.9.0-ultramodern.20",
142
+ "@rsbuild/core": "2.2.9",
150
143
  "@swc/core": "1.16.2",
151
144
  "@swc/helpers": "^0.5.23",
152
145
  "compression-webpack-plugin": "^12.0.0",
@@ -156,25 +149,34 @@
156
149
  "import-meta-resolve": "^4.2.0",
157
150
  "mlly": "^1.8.2",
158
151
  "ndepe": "^0.1.13",
159
- "pkg-types": "^2.3.2",
152
+ "pkg-types": "^2.3.3",
160
153
  "std-env": "4.2.0"
161
154
  },
162
155
  "devDependencies": {
163
- "@rslib/core": "1.0.0",
156
+ "@rslib/core": "1.0.2",
164
157
  "@scripts/rstest-config": "2.66.0",
165
158
  "@types/babel__traverse": "7.28.0",
166
- "@types/node": "^26.4.1",
159
+ "@types/node": "^26.6.2",
167
160
  "@typescript/native-preview": "7.0.0-dev.20260707.2",
161
+ "react": "^19.3.0",
162
+ "react-dom": "^19.3.0",
168
163
  "typescript": "^7.0.2"
169
164
  },
165
+ "peerDependencies": {
166
+ "react": "^19.2.8"
167
+ },
168
+ "peerDependenciesMeta": {
169
+ "react": {
170
+ "optional": true
171
+ }
172
+ },
170
173
  "sideEffects": false,
171
174
  "publishConfig": {
172
175
  "access": "public"
173
176
  },
174
177
  "scripts": {
175
178
  "dev": "rslib build --watch",
176
- "build": "rslib build && pnpm run verify:public-surface && node ./bin/modern-bundle-docs.js",
177
- "verify:public-surface": "node ./tests/config/verify-public-surface.mjs",
179
+ "build": "rslib build && node ./bin/modern-bundle-docs.js",
178
180
  "test": "rstest"
179
181
  }
180
182
  }
@@ -1,125 +0,0 @@
1
- "use strict";
2
- var __webpack_require__ = {};
3
- (()=>{
4
- __webpack_require__.n = (module)=>{
5
- var getter = module && module.__esModule ? ()=>module['default'] : ()=>module;
6
- __webpack_require__.d(getter, {
7
- a: getter
8
- });
9
- return getter;
10
- };
11
- })();
12
- (()=>{
13
- __webpack_require__.d = (exports1, getters, values)=>{
14
- var define = (defs, kind)=>{
15
- for(var key in defs)if (__webpack_require__.o(defs, key) && !__webpack_require__.o(exports1, key)) Object.defineProperty(exports1, key, {
16
- enumerable: true,
17
- [kind]: defs[key]
18
- });
19
- };
20
- define(getters, "get");
21
- define(values, "value");
22
- };
23
- })();
24
- (()=>{
25
- __webpack_require__.o = (obj, prop)=>Object.prototype.hasOwnProperty.call(obj, prop);
26
- })();
27
- (()=>{
28
- __webpack_require__.r = (exports1)=>{
29
- if ("u" > typeof Symbol && Symbol.toStringTag) Object.defineProperty(exports1, Symbol.toStringTag, {
30
- value: 'Module'
31
- });
32
- Object.defineProperty(exports1, '__esModule', {
33
- value: true
34
- });
35
- };
36
- })();
37
- var __webpack_exports__ = {};
38
- __webpack_require__.r(__webpack_exports__);
39
- __webpack_require__.d(__webpack_exports__, {
40
- builderPluginAdapterPrecompress: ()=>builderPluginAdapterPrecompress
41
- });
42
- const external_node_zlib_namespaceObject = require("node:zlib");
43
- const external_compression_webpack_plugin_namespaceObject = require("compression-webpack-plugin");
44
- var external_compression_webpack_plugin_default = /*#__PURE__*/ __webpack_require__.n(external_compression_webpack_plugin_namespaceObject);
45
- const DEFAULT_PRECOMPRESS_TEST = /\.(js|mjs|cjs|css|html|svg|json|map)$/i;
46
- const DEFAULT_PRECOMPRESS_THRESHOLD = 10240;
47
- const DEFAULT_PRECOMPRESS_MIN_RATIO = 0.8;
48
- const createDefaultGzipOptions = ()=>({
49
- algorithm: 'gzip',
50
- filename: '[path][base].gz',
51
- test: DEFAULT_PRECOMPRESS_TEST,
52
- threshold: DEFAULT_PRECOMPRESS_THRESHOLD,
53
- minRatio: DEFAULT_PRECOMPRESS_MIN_RATIO,
54
- compressionOptions: {
55
- level: 6
56
- }
57
- });
58
- const createDefaultBrotliOptions = ()=>({
59
- algorithm: 'brotliCompress',
60
- filename: '[path][base].br',
61
- test: DEFAULT_PRECOMPRESS_TEST,
62
- threshold: DEFAULT_PRECOMPRESS_THRESHOLD,
63
- minRatio: DEFAULT_PRECOMPRESS_MIN_RATIO,
64
- compressionOptions: {
65
- params: {
66
- [external_node_zlib_namespaceObject.constants.BROTLI_PARAM_QUALITY]: 9
67
- }
68
- }
69
- });
70
- const mergeCompressionOptions = (base, next)=>{
71
- const merged = {
72
- ...base,
73
- ...next
74
- };
75
- if (base.compressionOptions || next.compressionOptions) {
76
- const baseCompressionOptions = base.compressionOptions;
77
- const nextCompressionOptions = next.compressionOptions;
78
- const mergedCompressionOptions = {
79
- ...baseCompressionOptions,
80
- ...nextCompressionOptions
81
- };
82
- if (baseCompressionOptions?.params || nextCompressionOptions?.params) mergedCompressionOptions.params = {
83
- ...baseCompressionOptions?.params,
84
- ...nextCompressionOptions?.params
85
- };
86
- merged.compressionOptions = mergedCompressionOptions;
87
- }
88
- return merged;
89
- };
90
- const resolveCodecOptions = (codecConfig, createDefaultOptions)=>{
91
- if (false === codecConfig) return null;
92
- if (void 0 === codecConfig || true === codecConfig) return createDefaultOptions();
93
- return mergeCompressionOptions(createDefaultOptions(), codecConfig);
94
- };
95
- const resolvePrecompressConfig = (normalizedConfig)=>{
96
- const precompress = normalizedConfig.output.precompress;
97
- if (false === precompress || void 0 === precompress) return false;
98
- if (true === precompress) return {};
99
- return precompress;
100
- };
101
- const builderPluginAdapterPrecompress = (options)=>({
102
- name: 'builder-plugin-adapter-modern-precompress',
103
- setup (api) {
104
- api.modifyBundlerChain((chain, { isProd, target })=>{
105
- if (!isProd || 'web' !== target) return;
106
- const precompressConfig = resolvePrecompressConfig(options.normalizedConfig);
107
- if (false === precompressConfig) return;
108
- const gzipOptions = resolveCodecOptions(precompressConfig.gzip, createDefaultGzipOptions);
109
- if (gzipOptions) chain.plugin('modern-precompress-gzip').use(external_compression_webpack_plugin_default(), [
110
- gzipOptions
111
- ]);
112
- const brotliOptions = resolveCodecOptions(precompressConfig.brotli, createDefaultBrotliOptions);
113
- if (brotliOptions) chain.plugin('modern-precompress-brotli').use(external_compression_webpack_plugin_default(), [
114
- brotliOptions
115
- ]);
116
- });
117
- }
118
- });
119
- exports.builderPluginAdapterPrecompress = __webpack_exports__.builderPluginAdapterPrecompress;
120
- for(var __rspack_i in __webpack_exports__)if (-1 === [
121
- "builderPluginAdapterPrecompress"
122
- ].indexOf(__rspack_i)) exports[__rspack_i] = __webpack_exports__[__rspack_i];
123
- Object.defineProperty(exports, '__esModule', {
124
- value: true
125
- });