@bleedingdev/modern-js-plugin-bff-extensions 3.8.3-ultramodern.2

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 (228) hide show
  1. package/LICENSE +22 -0
  2. package/README.md +29 -0
  3. package/dist/cjs/backend-federation/edge-runtime.js +230 -0
  4. package/dist/cjs/backend-federation/edge.js +80 -0
  5. package/dist/cjs/backend-federation/entry.js +94 -0
  6. package/dist/cjs/backend-federation/identity.js +82 -0
  7. package/dist/cjs/backend-federation/index.js +61 -0
  8. package/dist/cjs/backend-federation/legacy-warning.js +40 -0
  9. package/dist/cjs/backend-federation/load.js +72 -0
  10. package/dist/cjs/backend-federation/node.js +50 -0
  11. package/dist/cjs/backend-federation/plugin.js +63 -0
  12. package/dist/cjs/backend-federation/remotes.js +51 -0
  13. package/dist/cjs/backend-federation/runtime.js +128 -0
  14. package/dist/cjs/backend-federation/types.js +18 -0
  15. package/dist/cjs/backend-federation/utils.js +48 -0
  16. package/dist/cjs/backend-federation/validate-loaded.js +56 -0
  17. package/dist/cjs/backend-federation-manifest/errors.js +83 -0
  18. package/dist/cjs/backend-federation-manifest/index.js +48 -0
  19. package/dist/cjs/backend-federation-manifest/load.js +173 -0
  20. package/dist/cjs/backend-federation-manifest/metadata.js +101 -0
  21. package/dist/cjs/backend-federation-manifest/node.js +50 -0
  22. package/dist/cjs/backend-federation-manifest/reference.js +132 -0
  23. package/dist/cjs/backend-federation-manifest/remote.js +81 -0
  24. package/dist/cjs/backend-federation-manifest/types.js +18 -0
  25. package/dist/cjs/backend-federation-manifest/validation.js +110 -0
  26. package/dist/cjs/client-generator/built-entry.js +84 -0
  27. package/dist/cjs/client-generator/generator.js +73 -0
  28. package/dist/cjs/client-generator/index.js +54 -0
  29. package/dist/cjs/client-generator/paths.js +62 -0
  30. package/dist/cjs/client-generator/rendering.js +253 -0
  31. package/dist/cjs/client-generator/runtime.js +68 -0
  32. package/dist/cjs/client-generator/types.js +18 -0
  33. package/dist/cjs/cross-project-generation/index.js +40 -0
  34. package/dist/cjs/cross-project-generation/render.js +53 -0
  35. package/dist/cjs/cross-project-policy/evaluation.js +75 -0
  36. package/dist/cjs/cross-project-policy/index.js +40 -0
  37. package/dist/cjs/cross-project-policy/node.js +76 -0
  38. package/dist/cjs/effect-adapter/cross-project-policy.js +64 -0
  39. package/dist/cjs/effect-adapter/entry.js +98 -0
  40. package/dist/cjs/effect-adapter/error-response.js +75 -0
  41. package/dist/cjs/effect-adapter/index.js +187 -0
  42. package/dist/cjs/effect-source-loader/index.js +51 -0
  43. package/dist/cjs/effect-source-loader/loader.js +341 -0
  44. package/dist/cjs/effect-source-loader/worker-runtime-wrapper.js +97 -0
  45. package/dist/cjs/hono/bind-route-handlers.js +76 -0
  46. package/dist/cjs/hono/cross-project-policy.js +48 -0
  47. package/dist/cjs/hono/index.js +40 -0
  48. package/dist/esm/backend-federation/edge-runtime.mjs +186 -0
  49. package/dist/esm/backend-federation/edge.mjs +21 -0
  50. package/dist/esm/backend-federation/entry.mjs +56 -0
  51. package/dist/esm/backend-federation/identity.mjs +41 -0
  52. package/dist/esm/backend-federation/index.mjs +4 -0
  53. package/dist/esm/backend-federation/legacy-warning.mjs +2 -0
  54. package/dist/esm/backend-federation/load.mjs +34 -0
  55. package/dist/esm/backend-federation/node.mjs +12 -0
  56. package/dist/esm/backend-federation/plugin.mjs +25 -0
  57. package/dist/esm/backend-federation/remotes.mjs +13 -0
  58. package/dist/esm/backend-federation/runtime.mjs +90 -0
  59. package/dist/esm/backend-federation/types.mjs +0 -0
  60. package/dist/esm/backend-federation/utils.mjs +7 -0
  61. package/dist/esm/backend-federation/validate-loaded.mjs +18 -0
  62. package/dist/esm/backend-federation-manifest/errors.mjs +32 -0
  63. package/dist/esm/backend-federation-manifest/index.mjs +3 -0
  64. package/dist/esm/backend-federation-manifest/load.mjs +135 -0
  65. package/dist/esm/backend-federation-manifest/metadata.mjs +42 -0
  66. package/dist/esm/backend-federation-manifest/node.mjs +12 -0
  67. package/dist/esm/backend-federation-manifest/reference.mjs +88 -0
  68. package/dist/esm/backend-federation-manifest/remote.mjs +43 -0
  69. package/dist/esm/backend-federation-manifest/types.mjs +0 -0
  70. package/dist/esm/backend-federation-manifest/validation.mjs +66 -0
  71. package/dist/esm/client-generator/built-entry.mjs +40 -0
  72. package/dist/esm/client-generator/generator.mjs +32 -0
  73. package/dist/esm/client-generator/index.mjs +3 -0
  74. package/dist/esm/client-generator/paths.mjs +24 -0
  75. package/dist/esm/client-generator/rendering.mjs +209 -0
  76. package/dist/esm/client-generator/runtime.mjs +27 -0
  77. package/dist/esm/client-generator/types.mjs +0 -0
  78. package/dist/esm/cross-project-generation/index.mjs +1 -0
  79. package/dist/esm/cross-project-generation/render.mjs +15 -0
  80. package/dist/esm/cross-project-policy/evaluation.mjs +34 -0
  81. package/dist/esm/cross-project-policy/index.mjs +1 -0
  82. package/dist/esm/cross-project-policy/node.mjs +28 -0
  83. package/dist/esm/effect-adapter/cross-project-policy.mjs +26 -0
  84. package/dist/esm/effect-adapter/entry.mjs +50 -0
  85. package/dist/esm/effect-adapter/error-response.mjs +37 -0
  86. package/dist/esm/effect-adapter/index.mjs +139 -0
  87. package/dist/esm/effect-source-loader/index.mjs +3 -0
  88. package/dist/esm/effect-source-loader/loader.mjs +285 -0
  89. package/dist/esm/effect-source-loader/worker-runtime-wrapper.mjs +56 -0
  90. package/dist/esm/hono/bind-route-handlers.mjs +38 -0
  91. package/dist/esm/hono/cross-project-policy.mjs +10 -0
  92. package/dist/esm/hono/index.mjs +1 -0
  93. package/dist/esm-node/backend-federation/edge-runtime.mjs +187 -0
  94. package/dist/esm-node/backend-federation/edge.mjs +22 -0
  95. package/dist/esm-node/backend-federation/entry.mjs +57 -0
  96. package/dist/esm-node/backend-federation/identity.mjs +42 -0
  97. package/dist/esm-node/backend-federation/index.mjs +5 -0
  98. package/dist/esm-node/backend-federation/legacy-warning.mjs +3 -0
  99. package/dist/esm-node/backend-federation/load.mjs +35 -0
  100. package/dist/esm-node/backend-federation/node.mjs +13 -0
  101. package/dist/esm-node/backend-federation/plugin.mjs +26 -0
  102. package/dist/esm-node/backend-federation/remotes.mjs +14 -0
  103. package/dist/esm-node/backend-federation/runtime.mjs +91 -0
  104. package/dist/esm-node/backend-federation/types.mjs +1 -0
  105. package/dist/esm-node/backend-federation/utils.mjs +8 -0
  106. package/dist/esm-node/backend-federation/validate-loaded.mjs +19 -0
  107. package/dist/esm-node/backend-federation-manifest/errors.mjs +33 -0
  108. package/dist/esm-node/backend-federation-manifest/index.mjs +4 -0
  109. package/dist/esm-node/backend-federation-manifest/load.mjs +136 -0
  110. package/dist/esm-node/backend-federation-manifest/metadata.mjs +43 -0
  111. package/dist/esm-node/backend-federation-manifest/node.mjs +13 -0
  112. package/dist/esm-node/backend-federation-manifest/reference.mjs +89 -0
  113. package/dist/esm-node/backend-federation-manifest/remote.mjs +44 -0
  114. package/dist/esm-node/backend-federation-manifest/types.mjs +1 -0
  115. package/dist/esm-node/backend-federation-manifest/validation.mjs +67 -0
  116. package/dist/esm-node/client-generator/built-entry.mjs +41 -0
  117. package/dist/esm-node/client-generator/generator.mjs +33 -0
  118. package/dist/esm-node/client-generator/index.mjs +4 -0
  119. package/dist/esm-node/client-generator/paths.mjs +25 -0
  120. package/dist/esm-node/client-generator/rendering.mjs +210 -0
  121. package/dist/esm-node/client-generator/runtime.mjs +29 -0
  122. package/dist/esm-node/client-generator/types.mjs +1 -0
  123. package/dist/esm-node/cross-project-generation/index.mjs +2 -0
  124. package/dist/esm-node/cross-project-generation/render.mjs +16 -0
  125. package/dist/esm-node/cross-project-policy/evaluation.mjs +35 -0
  126. package/dist/esm-node/cross-project-policy/index.mjs +2 -0
  127. package/dist/esm-node/cross-project-policy/node.mjs +29 -0
  128. package/dist/esm-node/effect-adapter/cross-project-policy.mjs +27 -0
  129. package/dist/esm-node/effect-adapter/entry.mjs +51 -0
  130. package/dist/esm-node/effect-adapter/error-response.mjs +38 -0
  131. package/dist/esm-node/effect-adapter/index.mjs +140 -0
  132. package/dist/esm-node/effect-source-loader/index.mjs +4 -0
  133. package/dist/esm-node/effect-source-loader/loader.mjs +286 -0
  134. package/dist/esm-node/effect-source-loader/worker-runtime-wrapper.mjs +57 -0
  135. package/dist/esm-node/hono/bind-route-handlers.mjs +39 -0
  136. package/dist/esm-node/hono/cross-project-policy.mjs +11 -0
  137. package/dist/esm-node/hono/index.mjs +2 -0
  138. package/dist/types/backend-federation/edge-runtime.d.ts +46 -0
  139. package/dist/types/backend-federation/edge.d.ts +28 -0
  140. package/dist/types/backend-federation/entry.d.ts +2 -0
  141. package/dist/types/backend-federation/identity.d.ts +30 -0
  142. package/dist/types/backend-federation/index.d.ts +6 -0
  143. package/dist/types/backend-federation/legacy-warning.d.ts +1 -0
  144. package/dist/types/backend-federation/load.d.ts +13 -0
  145. package/dist/types/backend-federation/node.d.ts +4 -0
  146. package/dist/types/backend-federation/plugin.d.ts +3 -0
  147. package/dist/types/backend-federation/remotes.d.ts +2 -0
  148. package/dist/types/backend-federation/runtime.d.ts +3 -0
  149. package/dist/types/backend-federation/types.d.ts +76 -0
  150. package/dist/types/backend-federation/utils.d.ts +2 -0
  151. package/dist/types/backend-federation/validate-loaded.d.ts +8 -0
  152. package/dist/types/backend-federation-manifest/errors.d.ts +11 -0
  153. package/dist/types/backend-federation-manifest/index.d.ts +5 -0
  154. package/dist/types/backend-federation-manifest/load.d.ts +3 -0
  155. package/dist/types/backend-federation-manifest/metadata.d.ts +18 -0
  156. package/dist/types/backend-federation-manifest/node.d.ts +3 -0
  157. package/dist/types/backend-federation-manifest/reference.d.ts +4 -0
  158. package/dist/types/backend-federation-manifest/remote.d.ts +3 -0
  159. package/dist/types/backend-federation-manifest/types.d.ts +49 -0
  160. package/dist/types/backend-federation-manifest/validation.d.ts +5 -0
  161. package/dist/types/client-generator/built-entry.d.ts +16 -0
  162. package/dist/types/client-generator/generator.d.ts +3 -0
  163. package/dist/types/client-generator/index.d.ts +4 -0
  164. package/dist/types/client-generator/paths.d.ts +5 -0
  165. package/dist/types/client-generator/rendering.d.ts +24 -0
  166. package/dist/types/client-generator/runtime.d.ts +12 -0
  167. package/dist/types/client-generator/types.d.ts +33 -0
  168. package/dist/types/cross-project-generation/index.d.ts +1 -0
  169. package/dist/types/cross-project-generation/render.d.ts +1 -0
  170. package/dist/types/cross-project-policy/evaluation.d.ts +4 -0
  171. package/dist/types/cross-project-policy/index.d.ts +1 -0
  172. package/dist/types/cross-project-policy/node.d.ts +4 -0
  173. package/dist/types/effect-adapter/cross-project-policy.d.ts +4 -0
  174. package/dist/types/effect-adapter/entry.d.ts +2 -0
  175. package/dist/types/effect-adapter/error-response.d.ts +2 -0
  176. package/dist/types/effect-adapter/index.d.ts +19 -0
  177. package/dist/types/effect-source-loader/index.d.ts +3 -0
  178. package/dist/types/effect-source-loader/loader.d.ts +17 -0
  179. package/dist/types/effect-source-loader/worker-runtime-wrapper.d.ts +25 -0
  180. package/dist/types/hono/bind-route-handlers.d.ts +10 -0
  181. package/dist/types/hono/cross-project-policy.d.ts +3 -0
  182. package/dist/types/hono/index.d.ts +1 -0
  183. package/package.json +213 -0
  184. package/src/backend-federation/edge-runtime.ts +258 -0
  185. package/src/backend-federation/edge.ts +128 -0
  186. package/src/backend-federation/entry.ts +125 -0
  187. package/src/backend-federation/identity.ts +97 -0
  188. package/src/backend-federation/index.ts +30 -0
  189. package/src/backend-federation/legacy-warning.ts +2 -0
  190. package/src/backend-federation/load.ts +102 -0
  191. package/src/backend-federation/node.ts +33 -0
  192. package/src/backend-federation/plugin.ts +35 -0
  193. package/src/backend-federation/remotes.ts +17 -0
  194. package/src/backend-federation/runtime.ts +99 -0
  195. package/src/backend-federation/types.ts +103 -0
  196. package/src/backend-federation/utils.ts +7 -0
  197. package/src/backend-federation/validate-loaded.ts +72 -0
  198. package/src/backend-federation-manifest/errors.ts +85 -0
  199. package/src/backend-federation-manifest/index.ts +11 -0
  200. package/src/backend-federation-manifest/load.ts +266 -0
  201. package/src/backend-federation-manifest/metadata.ts +73 -0
  202. package/src/backend-federation-manifest/node.ts +19 -0
  203. package/src/backend-federation-manifest/reference.ts +240 -0
  204. package/src/backend-federation-manifest/remote.ts +102 -0
  205. package/src/backend-federation-manifest/types.ts +73 -0
  206. package/src/backend-federation-manifest/validation.ts +255 -0
  207. package/src/client-generator/built-entry.ts +70 -0
  208. package/src/client-generator/generator.ts +52 -0
  209. package/src/client-generator/index.ts +10 -0
  210. package/src/client-generator/paths.ts +40 -0
  211. package/src/client-generator/rendering.ts +307 -0
  212. package/src/client-generator/runtime.ts +69 -0
  213. package/src/client-generator/types.ts +35 -0
  214. package/src/cross-project-generation/index.ts +1 -0
  215. package/src/cross-project-generation/render.ts +14 -0
  216. package/src/cross-project-policy/evaluation.ts +70 -0
  217. package/src/cross-project-policy/index.ts +1 -0
  218. package/src/cross-project-policy/node.ts +63 -0
  219. package/src/effect-adapter/cross-project-policy.ts +63 -0
  220. package/src/effect-adapter/entry.ts +95 -0
  221. package/src/effect-adapter/error-response.ts +68 -0
  222. package/src/effect-adapter/index.ts +246 -0
  223. package/src/effect-source-loader/index.ts +6 -0
  224. package/src/effect-source-loader/loader.ts +397 -0
  225. package/src/effect-source-loader/worker-runtime-wrapper.ts +90 -0
  226. package/src/hono/bind-route-handlers.ts +62 -0
  227. package/src/hono/cross-project-policy.ts +26 -0
  228. package/src/hono/index.ts +4 -0
@@ -0,0 +1,397 @@
1
+ // Node-only source loading implementation; the public surface lives in index.ts.
2
+ // @effect-diagnostics anyUnknownInErrorContext:off asyncFunction:off nodeBuiltinImport:off strictBooleanExpressions:off
3
+ import crypto from 'node:crypto';
4
+ import fs from 'node:fs';
5
+ import { builtinModules, createRequire } from 'node:module';
6
+ import path from 'node:path';
7
+ import { pathToFileURL } from 'node:url';
8
+ import {
9
+ build,
10
+ type Loader,
11
+ type OnLoadArgs,
12
+ type Plugin,
13
+ transform,
14
+ } from 'esbuild';
15
+
16
+ const NODE_TARGET = 'node26.7';
17
+ const SOURCE_LOADERS = new Map<string, Loader>([
18
+ ['.js', 'js'],
19
+ ['.jsx', 'jsx'],
20
+ ['.mjs', 'js'],
21
+ ['.cjs', 'js'],
22
+ ['.ts', 'ts'],
23
+ ['.tsx', 'tsx'],
24
+ ['.mts', 'ts'],
25
+ ['.cts', 'ts'],
26
+ ]);
27
+
28
+ export type EffectSourceLoaderOptions = {
29
+ resourcePath: string;
30
+ appDir?: string;
31
+ onDependency?: (dependency: string) => void;
32
+ };
33
+
34
+ function sourceLoader(args: OnLoadArgs): Loader | undefined {
35
+ return SOURCE_LOADERS.get(path.extname(args.path).toLowerCase());
36
+ }
37
+
38
+ function preserveSourceModuleSemantics(): Plugin {
39
+ return {
40
+ name: 'modern-js-effect-source-semantics',
41
+ setup(buildApi) {
42
+ buildApi.onLoad({ filter: /\.[cm]?[jt]sx?$/ }, async args => {
43
+ const loader = sourceLoader(args);
44
+ if (!loader) {
45
+ return undefined;
46
+ }
47
+
48
+ const source = await fs.promises.readFile(args.path, 'utf8');
49
+ const compiled = await transform(source, {
50
+ define: {
51
+ 'import.meta.url': JSON.stringify(pathToFileURL(args.path).href),
52
+ },
53
+ jsx: 'automatic',
54
+ loader,
55
+ sourcefile: args.path,
56
+ target: NODE_TARGET,
57
+ });
58
+
59
+ return {
60
+ contents: compiled.code,
61
+ loader: 'js',
62
+ resolveDir: path.dirname(args.path),
63
+ };
64
+ });
65
+ },
66
+ };
67
+ }
68
+
69
+ const nodeBuiltins = new Set([
70
+ ...builtinModules,
71
+ ...builtinModules.map(name => `node:${name}`),
72
+ ]);
73
+ const dependencyResolutionMarker = 'modern-js-effect-dependency-resolution';
74
+
75
+ async function isEsmOnlyFile(filename: string) {
76
+ const extension = path.extname(filename).toLowerCase();
77
+ if (extension === '.mjs' || extension === '.mts') {
78
+ return true;
79
+ }
80
+ if (extension === '.cjs' || extension === '.cts') {
81
+ return false;
82
+ }
83
+
84
+ let directory = path.dirname(filename);
85
+ while (true) {
86
+ const packagePath = path.join(directory, 'package.json');
87
+ try {
88
+ const packageJson = JSON.parse(
89
+ await fs.promises.readFile(packagePath, 'utf8'),
90
+ ) as { type?: unknown };
91
+ return packageJson.type === 'module';
92
+ } catch (error) {
93
+ if (
94
+ !error ||
95
+ typeof error !== 'object' ||
96
+ !('code' in error) ||
97
+ error.code !== 'ENOENT'
98
+ ) {
99
+ throw error;
100
+ }
101
+ }
102
+
103
+ const parent = path.dirname(directory);
104
+ if (parent === directory) {
105
+ return false;
106
+ }
107
+ directory = parent;
108
+ }
109
+ }
110
+
111
+ function isBareSpecifier(specifier: string) {
112
+ return (
113
+ !specifier.startsWith('.') &&
114
+ !specifier.startsWith('/') &&
115
+ !path.isAbsolute(specifier)
116
+ );
117
+ }
118
+
119
+ /**
120
+ * Keep installed registry dependencies external while bundling symlinked
121
+ * workspace source packages. Registry packages remain executable from the
122
+ * deployment node_modules tree; workspace TypeScript is bundled so the
123
+ * generated entry is relocatable and directly executable by Node 26.7.
124
+ */
125
+ function externalizeInstalledDependencies(options: {
126
+ format: 'cjs' | 'esm';
127
+ runtimeResolveDir: string;
128
+ }): Plugin {
129
+ const runtimeRequire = createRequire(
130
+ path.join(options.runtimeResolveDir, '__modern_effect_entry.cjs'),
131
+ );
132
+ return {
133
+ name: 'modern-js-effect-installed-dependencies',
134
+ setup(buildApi) {
135
+ buildApi.onResolve({ filter: /.*/ }, async args => {
136
+ if (
137
+ !isBareSpecifier(args.path) ||
138
+ nodeBuiltins.has(args.path) ||
139
+ args.pluginData?.[dependencyResolutionMarker] === true
140
+ ) {
141
+ return nodeBuiltins.has(args.path)
142
+ ? { external: true, path: args.path }
143
+ : undefined;
144
+ }
145
+
146
+ const resolution = await buildApi.resolve(args.path, {
147
+ importer: args.importer,
148
+ kind: args.kind,
149
+ namespace: args.namespace,
150
+ pluginData: {
151
+ ...args.pluginData,
152
+ [dependencyResolutionMarker]: true,
153
+ },
154
+ resolveDir: args.resolveDir,
155
+ });
156
+ if (resolution.errors.length > 0) {
157
+ return { external: true, path: args.path };
158
+ }
159
+ if (resolution.external || !resolution.path) {
160
+ return { external: true, path: args.path };
161
+ }
162
+
163
+ const realPath = await fs.promises
164
+ .realpath(resolution.path)
165
+ .catch(() => resolution.path);
166
+ if (realPath.includes(`${path.sep}node_modules${path.sep}`)) {
167
+ const runtimeResolution = await buildApi.resolve(args.path, {
168
+ importer: '',
169
+ kind: args.kind,
170
+ namespace: 'file',
171
+ pluginData: {
172
+ [dependencyResolutionMarker]: true,
173
+ },
174
+ resolveDir: options.runtimeResolveDir,
175
+ });
176
+ if (runtimeResolution.errors.length === 0 && runtimeResolution.path) {
177
+ if (options.format === 'cjs') {
178
+ let requiredPath: string;
179
+ try {
180
+ requiredPath = runtimeRequire.resolve(args.path);
181
+ } catch {
182
+ requiredPath = runtimeResolution.path;
183
+ }
184
+ if (await isEsmOnlyFile(requiredPath)) {
185
+ return {
186
+ namespace: resolution.namespace,
187
+ path: resolution.path,
188
+ pluginData: resolution.pluginData,
189
+ sideEffects: resolution.sideEffects,
190
+ suffix: resolution.suffix,
191
+ warnings: resolution.warnings,
192
+ };
193
+ }
194
+ }
195
+ return { external: true, path: args.path };
196
+ }
197
+ }
198
+
199
+ return {
200
+ namespace: resolution.namespace,
201
+ path: resolution.path,
202
+ pluginData: resolution.pluginData,
203
+ sideEffects: resolution.sideEffects,
204
+ suffix: resolution.suffix,
205
+ warnings: resolution.warnings,
206
+ };
207
+ });
208
+ },
209
+ };
210
+ }
211
+
212
+ export async function bundleEffectEntryForNode(options: {
213
+ appDir: string;
214
+ entryPath: string;
215
+ format: 'cjs' | 'esm';
216
+ }) {
217
+ await build({
218
+ absWorkingDir: options.appDir,
219
+ allowOverwrite: true,
220
+ bundle: true,
221
+ entryPoints: [options.entryPath],
222
+ format: options.format,
223
+ logLevel: 'silent',
224
+ outfile: options.entryPath,
225
+ platform: 'node',
226
+ plugins: [
227
+ externalizeInstalledDependencies({
228
+ format: options.format,
229
+ runtimeResolveDir: options.appDir,
230
+ }),
231
+ preserveSourceModuleSemantics(),
232
+ ],
233
+ target: NODE_TARGET,
234
+ });
235
+ }
236
+
237
+ function isWatchableInput(filename: string) {
238
+ return (
239
+ !filename.includes(`${path.sep}node_modules${path.sep}`) &&
240
+ (SOURCE_LOADERS.has(path.extname(filename).toLowerCase()) ||
241
+ path.extname(filename).toLowerCase() === '.json')
242
+ );
243
+ }
244
+
245
+ function normalizeImportedModule(module: unknown): unknown {
246
+ if (!module || typeof module !== 'object') {
247
+ return module;
248
+ }
249
+ const namespace = module as Record<string, unknown>;
250
+ const commonJsModule = namespace.default;
251
+ if (
252
+ Object.keys(namespace).length === 1 &&
253
+ commonJsModule !== null &&
254
+ typeof commonJsModule === 'object' &&
255
+ (commonJsModule as Record<string, unknown>).__esModule === true
256
+ ) {
257
+ return commonJsModule;
258
+ }
259
+ return module;
260
+ }
261
+
262
+ function hasErrorCode(error: unknown, code: string): boolean {
263
+ return (
264
+ error !== null &&
265
+ typeof error === 'object' &&
266
+ 'code' in error &&
267
+ error.code === code
268
+ );
269
+ }
270
+
271
+ async function publishImmutableOutput(options: {
272
+ cacheDirectory: string;
273
+ contents: Uint8Array;
274
+ outputName: string;
275
+ }): Promise<string> {
276
+ const revision = crypto
277
+ .createHash('sha256')
278
+ .update(options.contents)
279
+ .digest('hex');
280
+ const outputPath = path.join(
281
+ options.cacheDirectory,
282
+ `${options.outputName}-${revision}.mjs`,
283
+ );
284
+ const temporaryPath = path.join(
285
+ options.cacheDirectory,
286
+ `.${options.outputName}-${crypto.randomUUID()}.tmp`,
287
+ );
288
+
289
+ try {
290
+ await fs.promises.writeFile(temporaryPath, options.contents, {
291
+ flag: 'wx',
292
+ });
293
+ try {
294
+ await fs.promises.link(temporaryPath, outputPath);
295
+ } catch (error) {
296
+ if (!hasErrorCode(error, 'EEXIST')) {
297
+ throw error;
298
+ }
299
+
300
+ const publishedContents = await fs.promises.readFile(outputPath);
301
+ if (!publishedContents.equals(Buffer.from(options.contents))) {
302
+ throw new Error(
303
+ `Effect source cache entry does not match its content address: ${outputPath}`,
304
+ );
305
+ }
306
+ }
307
+ } finally {
308
+ await fs.promises.rm(temporaryPath, { force: true });
309
+ }
310
+
311
+ return outputPath;
312
+ }
313
+
314
+ /** Loads a production artifact without changing its native Node boundary. */
315
+ export async function loadEffectBuiltModule(
316
+ resourcePath: string,
317
+ ): Promise<unknown> {
318
+ const canonicalPath = await fs.promises.realpath(path.resolve(resourcePath));
319
+ if (!(await isEsmOnlyFile(canonicalPath))) {
320
+ return createRequire(canonicalPath)(canonicalPath);
321
+ }
322
+ return normalizeImportedModule(
323
+ await import(pathToFileURL(canonicalPath).href),
324
+ );
325
+ }
326
+
327
+ /**
328
+ * Loads an Effect source entry as native ESM while reporting its complete
329
+ * local source graph to the framework watcher.
330
+ */
331
+ export async function loadEffectSourceModule(
332
+ options: EffectSourceLoaderOptions,
333
+ ): Promise<unknown> {
334
+ const resourcePath = path.resolve(options.resourcePath);
335
+ const appDir = path.resolve(options.appDir ?? path.dirname(resourcePath));
336
+ const tsconfigPath = path.join(appDir, 'tsconfig.json');
337
+ const cacheDirectory = path.join(
338
+ appDir,
339
+ 'node_modules',
340
+ '.cache',
341
+ 'modern-js',
342
+ 'effect-source-loader',
343
+ );
344
+ const outputName = crypto
345
+ .createHash('sha256')
346
+ .update(resourcePath)
347
+ .digest('hex')
348
+ .slice(0, 24);
349
+ await fs.promises.mkdir(cacheDirectory, { recursive: true });
350
+
351
+ const result = await build({
352
+ absWorkingDir: appDir,
353
+ banner: {
354
+ js: `import { createRequire as __modernCreateEffectRequire } from 'node:module'; const require = __modernCreateEffectRequire(${JSON.stringify(pathToFileURL(resourcePath).href)});`,
355
+ },
356
+ bundle: true,
357
+ entryPoints: [resourcePath],
358
+ format: 'esm',
359
+ jsx: 'automatic',
360
+ logLevel: 'silent',
361
+ metafile: true,
362
+ outfile: path.join(cacheDirectory, `${outputName}.mjs`),
363
+ platform: 'node',
364
+ plugins: [
365
+ externalizeInstalledDependencies({
366
+ format: 'esm',
367
+ runtimeResolveDir: appDir,
368
+ }),
369
+ preserveSourceModuleSemantics(),
370
+ ],
371
+ sourcemap: 'inline',
372
+ target: NODE_TARGET,
373
+ tsconfig: fs.existsSync(tsconfigPath) ? tsconfigPath : undefined,
374
+ write: false,
375
+ });
376
+
377
+ for (const input of Object.keys(result.metafile.inputs)) {
378
+ const dependency = path.isAbsolute(input)
379
+ ? path.normalize(input)
380
+ : path.resolve(appDir, input);
381
+ if (isWatchableInput(dependency)) {
382
+ options.onDependency?.(dependency);
383
+ }
384
+ }
385
+
386
+ if (result.outputFiles.length !== 1) {
387
+ throw new Error(
388
+ `Expected one Effect source output, received ${result.outputFiles.length}`,
389
+ );
390
+ }
391
+ const outputPath = await publishImmutableOutput({
392
+ cacheDirectory,
393
+ contents: result.outputFiles[0].contents,
394
+ outputName,
395
+ });
396
+ return normalizeImportedModule(await import(pathToFileURL(outputPath).href));
397
+ }
@@ -0,0 +1,90 @@
1
+ // @effect-diagnostics asyncFunction:off strictBooleanExpressions:off
2
+ import type { HttpMethodDecider } from '@modern-js/types';
3
+ import type { EffectClientCodegenOptions } from '../client-generator/types';
4
+
5
+ type EffectWorkerRuntimeGenerationOptions = {
6
+ apiDir: string;
7
+ appDir: string;
8
+ effectDataPlatformBatch?: {
9
+ allowedMethods?: string[];
10
+ enabled?: boolean;
11
+ endpoint?: string;
12
+ flushIntervalMs?: number;
13
+ maxBatchBytes?: number;
14
+ maxBatchSize?: number;
15
+ requestTimeoutMs?: number;
16
+ };
17
+ httpMethodDecider?: HttpMethodDecider;
18
+ port: number;
19
+ prefix: string;
20
+ requestCreator?: string;
21
+ requestId?: string;
22
+ };
23
+
24
+ export async function generateEffectWorkerRuntimeWrapper(
25
+ loader: { addDependency: (dependency: string) => void },
26
+ options: EffectWorkerRuntimeGenerationOptions,
27
+ resourcePath: string,
28
+ ) {
29
+ const { generateEffectClient } = await import(
30
+ '../client-generator/generator'
31
+ );
32
+ const artifacts = await generateEffectClient({
33
+ appDir: options.appDir,
34
+ apiDir: options.apiDir,
35
+ resourcePath,
36
+ prefix: options.prefix,
37
+ port: Number(options.port),
38
+ target: 'bundle',
39
+ requestId: options.requestId,
40
+ requestCreator: options.requestCreator,
41
+ httpMethodDecider: options.httpMethodDecider,
42
+ dataPlatformBatch: options.effectDataPlatformBatch,
43
+ onDependency: dependency => loader.addDependency(dependency),
44
+ });
45
+ const sourceRequest = `${resourcePath}?modern-bff-runtime-source`;
46
+ const operationContracts = artifacts?.operationContracts ?? {};
47
+
48
+ return `import * as effectBffModule from ${JSON.stringify(sourceRequest)};
49
+ import { createEffectBffEdgeDispatcher } from '@modern-js/plugin-bff/effect-edge/dispatcher';
50
+
51
+ const __generatedOperationContracts = ${JSON.stringify(operationContracts)};
52
+
53
+ const __mergeGeneratedOperationContracts = policy => {
54
+ if (
55
+ !policy ||
56
+ !policy.expectedOperationContracts ||
57
+ typeof policy.expectedOperationContracts !== 'object' ||
58
+ Array.isArray(policy.expectedOperationContracts)
59
+ ) {
60
+ return policy;
61
+ }
62
+ return {
63
+ ...policy,
64
+ expectedOperationContracts: {
65
+ ...policy.expectedOperationContracts,
66
+ ...__generatedOperationContracts,
67
+ },
68
+ };
69
+ };
70
+
71
+ export const __modern_create_effect_bff_dispatcher = options =>
72
+ createEffectBffEdgeDispatcher({
73
+ ...options,
74
+ crossProjectPolicy: __mergeGeneratedOperationContracts(
75
+ options?.crossProjectPolicy,
76
+ ),
77
+ module: effectBffModule,
78
+ });
79
+ `;
80
+ }
81
+
82
+ export async function generateEffectClientCode(
83
+ options: EffectClientCodegenOptions,
84
+ ) {
85
+ const { generateEffectClient } = await import(
86
+ '../client-generator/generator'
87
+ );
88
+ const artifacts = await generateEffectClient(options);
89
+ return artifacts === null ? null : artifacts.code;
90
+ }
@@ -0,0 +1,62 @@
1
+ import { createSafeFailureResponse } from '@modern-js/runtime-extensions/safe-failure';
2
+ import type { Context, MiddlewareHandler } from '@modern-js/server-core';
3
+
4
+ import type { ResolvedCrossProjectPolicy } from '../cross-project-policy/evaluation';
5
+ import { createHonoCrossProjectPolicyMiddleware } from './cross-project-policy';
6
+
7
+ export interface BindHonoRouteHandlersOptions {
8
+ handler: MiddlewareHandler | MiddlewareHandler[];
9
+ policy?: ResolvedCrossProjectPolicy;
10
+ routePath: string;
11
+ onError?: (error: Error, context: Context) => unknown | Promise<unknown>;
12
+ reportError?: (error: unknown) => void;
13
+ }
14
+
15
+ export const bindHonoRouteHandlers = ({
16
+ handler,
17
+ policy,
18
+ routePath,
19
+ onError,
20
+ reportError,
21
+ }: BindHonoRouteHandlersOptions): MiddlewareHandler | MiddlewareHandler[] => {
22
+ const handlers = Array.isArray(handler) ? handler : [handler];
23
+ const [firstHandler, ...restHandlers] = handlers;
24
+ let routeHandlers: MiddlewareHandler | MiddlewareHandler[] = handler;
25
+
26
+ if (firstHandler) {
27
+ const errorBoundary: MiddlewareHandler = async (context, next) => {
28
+ try {
29
+ return await firstHandler(context, next);
30
+ } catch (error) {
31
+ try {
32
+ if (onError) {
33
+ const result = await onError(error as Error, context);
34
+ if (result instanceof Response) {
35
+ return result;
36
+ }
37
+ } else {
38
+ reportError?.(error);
39
+ }
40
+ } catch (configError) {
41
+ reportError?.(
42
+ `Error in serverConfig.onError handler: ${configError}`,
43
+ );
44
+ }
45
+
46
+ return createSafeFailureResponse(error);
47
+ }
48
+ };
49
+ routeHandlers = Array.isArray(handler)
50
+ ? [errorBoundary, ...restHandlers]
51
+ : errorBoundary;
52
+ }
53
+
54
+ if (!policy) {
55
+ return routeHandlers;
56
+ }
57
+
58
+ return [
59
+ createHonoCrossProjectPolicyMiddleware(policy, routePath),
60
+ ...(Array.isArray(routeHandlers) ? routeHandlers : [routeHandlers]),
61
+ ];
62
+ };
@@ -0,0 +1,26 @@
1
+ import type { MiddlewareHandler } from '@modern-js/server-core';
2
+
3
+ import {
4
+ checkCrossProjectPolicyResponse,
5
+ type ResolvedCrossProjectPolicy,
6
+ } from '../cross-project-policy/evaluation';
7
+
8
+ export const createHonoCrossProjectPolicyMiddleware = (
9
+ policy: ResolvedCrossProjectPolicy,
10
+ routePath: string,
11
+ ): MiddlewareHandler =>
12
+ async function honoCrossProjectPolicyMiddleware(context, next) {
13
+ const denial = checkCrossProjectPolicyResponse(
14
+ context.req.header(),
15
+ policy,
16
+ {
17
+ method: context.req.method,
18
+ routePath,
19
+ },
20
+ );
21
+ if (denial) {
22
+ return denial;
23
+ }
24
+
25
+ return next();
26
+ };
@@ -0,0 +1,4 @@
1
+ export {
2
+ type BindHonoRouteHandlersOptions,
3
+ bindHonoRouteHandlers,
4
+ } from './bind-route-handlers';