@bleedingdev/modern-js-plugin-bff 3.2.0-ultramodern.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 (88) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +26 -0
  3. package/cli.js +1 -0
  4. package/dist/cjs/cli.js +294 -0
  5. package/dist/cjs/constants.js +48 -0
  6. package/dist/cjs/index.js +58 -0
  7. package/dist/cjs/loader.js +106 -0
  8. package/dist/cjs/runtime/create-request/index.js +48 -0
  9. package/dist/cjs/runtime/data-platform/index.js +693 -0
  10. package/dist/cjs/runtime/effect/adapter.js +311 -0
  11. package/dist/cjs/runtime/effect/context.js +48 -0
  12. package/dist/cjs/runtime/effect/index.js +608 -0
  13. package/dist/cjs/runtime/effect-client/index.js +178 -0
  14. package/dist/cjs/runtime/hono/adapter.js +168 -0
  15. package/dist/cjs/runtime/hono/index.js +65 -0
  16. package/dist/cjs/runtime/hono/operators.js +68 -0
  17. package/dist/cjs/server.js +179 -0
  18. package/dist/cjs/utils/clientGenerator.js +342 -0
  19. package/dist/cjs/utils/createHonoRoutes.js +138 -0
  20. package/dist/cjs/utils/crossProjectApiPlugin.js +118 -0
  21. package/dist/cjs/utils/effectClientGenerator.js +673 -0
  22. package/dist/cjs/utils/pluginGenerator.js +73 -0
  23. package/dist/cjs/utils/runtimeGenerator.js +133 -0
  24. package/dist/esm/cli.mjs +245 -0
  25. package/dist/esm/constants.mjs +11 -0
  26. package/dist/esm/index.mjs +1 -0
  27. package/dist/esm/loader.mjs +62 -0
  28. package/dist/esm/runtime/create-request/index.mjs +1 -0
  29. package/dist/esm/runtime/data-platform/index.mjs +599 -0
  30. package/dist/esm/runtime/effect/adapter.mjs +267 -0
  31. package/dist/esm/runtime/effect/context.mjs +11 -0
  32. package/dist/esm/runtime/effect/index.mjs +438 -0
  33. package/dist/esm/runtime/effect-client/index.mjs +90 -0
  34. package/dist/esm/runtime/hono/adapter.mjs +124 -0
  35. package/dist/esm/runtime/hono/index.mjs +2 -0
  36. package/dist/esm/runtime/hono/operators.mjs +31 -0
  37. package/dist/esm/server.mjs +135 -0
  38. package/dist/esm/utils/clientGenerator.mjs +293 -0
  39. package/dist/esm/utils/createHonoRoutes.mjs +92 -0
  40. package/dist/esm/utils/crossProjectApiPlugin.mjs +54 -0
  41. package/dist/esm/utils/effectClientGenerator.mjs +623 -0
  42. package/dist/esm/utils/pluginGenerator.mjs +29 -0
  43. package/dist/esm/utils/runtimeGenerator.mjs +89 -0
  44. package/dist/esm-node/cli.mjs +249 -0
  45. package/dist/esm-node/constants.mjs +12 -0
  46. package/dist/esm-node/index.mjs +2 -0
  47. package/dist/esm-node/loader.mjs +64 -0
  48. package/dist/esm-node/runtime/create-request/index.mjs +2 -0
  49. package/dist/esm-node/runtime/data-platform/index.mjs +600 -0
  50. package/dist/esm-node/runtime/effect/adapter.mjs +269 -0
  51. package/dist/esm-node/runtime/effect/context.mjs +12 -0
  52. package/dist/esm-node/runtime/effect/index.mjs +439 -0
  53. package/dist/esm-node/runtime/effect-client/index.mjs +91 -0
  54. package/dist/esm-node/runtime/hono/adapter.mjs +125 -0
  55. package/dist/esm-node/runtime/hono/index.mjs +3 -0
  56. package/dist/esm-node/runtime/hono/operators.mjs +32 -0
  57. package/dist/esm-node/server.mjs +136 -0
  58. package/dist/esm-node/utils/clientGenerator.mjs +294 -0
  59. package/dist/esm-node/utils/createHonoRoutes.mjs +93 -0
  60. package/dist/esm-node/utils/crossProjectApiPlugin.mjs +55 -0
  61. package/dist/esm-node/utils/effectClientGenerator.mjs +625 -0
  62. package/dist/esm-node/utils/pluginGenerator.mjs +33 -0
  63. package/dist/esm-node/utils/runtimeGenerator.mjs +91 -0
  64. package/dist/types/cli.d.ts +3 -0
  65. package/dist/types/constants.d.ts +2 -0
  66. package/dist/types/index.d.ts +1 -0
  67. package/dist/types/loader.d.ts +27 -0
  68. package/dist/types/runtime/create-request/index.d.ts +2 -0
  69. package/dist/types/runtime/data-platform/index.d.ts +187 -0
  70. package/dist/types/runtime/effect/adapter.d.ts +22 -0
  71. package/dist/types/runtime/effect/context.d.ts +8 -0
  72. package/dist/types/runtime/effect/index.d.ts +171 -0
  73. package/dist/types/runtime/effect-client/index.d.ts +47 -0
  74. package/dist/types/runtime/hono/adapter.d.ts +19 -0
  75. package/dist/types/runtime/hono/index.d.ts +2 -0
  76. package/dist/types/runtime/hono/operators.d.ts +10 -0
  77. package/dist/types/server.d.ts +3 -0
  78. package/dist/types/utils/clientGenerator.d.ts +37 -0
  79. package/dist/types/utils/createHonoRoutes.d.ts +10 -0
  80. package/dist/types/utils/crossProjectApiPlugin.d.ts +9 -0
  81. package/dist/types/utils/effectClientGenerator.d.ts +27 -0
  82. package/dist/types/utils/pluginGenerator.d.ts +9 -0
  83. package/dist/types/utils/runtimeGenerator.d.ts +7 -0
  84. package/docs/data-platform-architecture.md +61 -0
  85. package/package.json +172 -0
  86. package/rslib.config.mts +4 -0
  87. package/rstest.config.mts +10 -0
  88. package/server.js +1 -0
@@ -0,0 +1,311 @@
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, definition)=>{
14
+ for(var key in definition)if (__webpack_require__.o(definition, key) && !__webpack_require__.o(exports1, key)) Object.defineProperty(exports1, key, {
15
+ enumerable: true,
16
+ get: definition[key]
17
+ });
18
+ };
19
+ })();
20
+ (()=>{
21
+ __webpack_require__.o = (obj, prop)=>Object.prototype.hasOwnProperty.call(obj, prop);
22
+ })();
23
+ (()=>{
24
+ __webpack_require__.r = (exports1)=>{
25
+ if ("u" > typeof Symbol && Symbol.toStringTag) Object.defineProperty(exports1, Symbol.toStringTag, {
26
+ value: 'Module'
27
+ });
28
+ Object.defineProperty(exports1, '__esModule', {
29
+ value: true
30
+ });
31
+ };
32
+ })();
33
+ var __webpack_exports__ = {};
34
+ __webpack_require__.r(__webpack_exports__);
35
+ __webpack_require__.d(__webpack_exports__, {
36
+ EffectAdapter: ()=>EffectAdapter
37
+ });
38
+ const utils_namespaceObject = require("@modern-js/utils");
39
+ const httpapi_namespaceObject = require("effect/unstable/httpapi");
40
+ const external_path_namespaceObject = require("path");
41
+ var external_path_default = /*#__PURE__*/ __webpack_require__.n(external_path_namespaceObject);
42
+ const external_context_js_namespaceObject = require("./context.js");
43
+ const external_index_js_namespaceObject = require("./index.js");
44
+ const before = [
45
+ 'custom-server-hook',
46
+ 'custom-server-middleware',
47
+ 'render'
48
+ ];
49
+ const JS_OR_TS_EXTS = [
50
+ '.js',
51
+ '.jsx',
52
+ '.ts',
53
+ '.tsx',
54
+ '.mjs',
55
+ '.mts',
56
+ '.cjs',
57
+ '.cts'
58
+ ];
59
+ function normalizePrefix(prefix) {
60
+ if ('/' === prefix) return '';
61
+ return prefix.endsWith('/') ? prefix.slice(0, -1) : prefix;
62
+ }
63
+ function removePrefixFromPath(pathname, prefix) {
64
+ const normalized = normalizePrefix(prefix);
65
+ if (!normalized || pathname !== normalized && !pathname.startsWith(`${normalized}/`)) return pathname;
66
+ const sliced = pathname.slice(normalized.length);
67
+ return sliced.startsWith('/') ? sliced : `/${sliced}`;
68
+ }
69
+ function createRequestForMountedPrefix(req, prefix) {
70
+ const url = new URL(req.url);
71
+ const nextPath = removePrefixFromPath(url.pathname, prefix);
72
+ if (nextPath === url.pathname) return req;
73
+ url.pathname = nextPath;
74
+ return new Request(url, req);
75
+ }
76
+ function isRequestHandler(value) {
77
+ return 'function' == typeof value;
78
+ }
79
+ function maybeResponse(value) {
80
+ return value instanceof Response;
81
+ }
82
+ function isRecord(value) {
83
+ return 'object' == typeof value && null !== value;
84
+ }
85
+ function includesRuntimeExports(value) {
86
+ return 'api' in value || 'layer' in value || 'createHandler' in value || 'handler' in value;
87
+ }
88
+ function isHttpApiWithProps(value) {
89
+ return httpapi_namespaceObject.HttpApi.isHttpApi(value) && isRecord(value) && 'string' == typeof value.identifier && isRecord(value.groups);
90
+ }
91
+ function isEffectApiDefinition(module) {
92
+ return isHttpApiWithProps(module.api) && void 0 !== module.layer;
93
+ }
94
+ class EffectAdapter {
95
+ resolveEntryFile() {
96
+ const { appDirectory, apiDirectory } = this.api.getServerContext();
97
+ const bffConfig = this.api.getServerConfig()?.bff;
98
+ const configuredEntry = bffConfig?.effect?.entry;
99
+ const defaultEntry = external_path_default().resolve(appDirectory || process.cwd(), apiDirectory || utils_namespaceObject.API_DIR, 'effect', 'index');
100
+ const entryWithoutExt = configuredEntry ? external_path_default().isAbsolute(configuredEntry) ? configuredEntry : external_path_default().resolve(appDirectory || process.cwd(), configuredEntry) : defaultEntry;
101
+ return (0, utils_namespaceObject.findExists)(JS_OR_TS_EXTS.map((ext)=>`${entryWithoutExt}${ext}`));
102
+ }
103
+ async loadEffectHandlerFromModule(mod) {
104
+ let normalizedModule = mod;
105
+ const mergeRuntimeExports = (value)=>{
106
+ if (!isRecord(value) || !includesRuntimeExports(value)) return;
107
+ normalizedModule = {
108
+ ...normalizedModule,
109
+ ...value
110
+ };
111
+ };
112
+ if (isRequestHandler(normalizedModule.handler)) return {
113
+ handler: normalizedModule.handler
114
+ };
115
+ const entry = normalizedModule.default;
116
+ if (isRequestHandler(entry)) return {
117
+ handler: entry
118
+ };
119
+ if ('function' == typeof entry && 0 === entry.length) {
120
+ const out = await entry();
121
+ if (isRequestHandler(out)) return {
122
+ handler: out
123
+ };
124
+ mergeRuntimeExports(out);
125
+ }
126
+ if (isRecord(entry)) normalizedModule = {
127
+ ...normalizedModule,
128
+ ...entry
129
+ };
130
+ if (isRecord(entry) && 'handler' in entry) {
131
+ const maybeHandler = entry.handler;
132
+ if (isRequestHandler(maybeHandler)) normalizedModule = {
133
+ ...normalizedModule,
134
+ handler: maybeHandler
135
+ };
136
+ }
137
+ if (isRequestHandler(normalizedModule.handler)) return {
138
+ handler: normalizedModule.handler
139
+ };
140
+ if ('function' == typeof normalizedModule.createHandler) {
141
+ const webHandler = normalizedModule.createHandler({
142
+ openapi: this.api.getServerConfig()?.bff?.effect?.openapi,
143
+ dataPlatform: this.api.getServerConfig()?.bff?.effect?.dataPlatform
144
+ });
145
+ return {
146
+ handler: async (request)=>webHandler.handler(request),
147
+ dispose: async ()=>{
148
+ await webHandler.dispose();
149
+ }
150
+ };
151
+ }
152
+ if (isEffectApiDefinition(normalizedModule)) {
153
+ utils_namespaceObject.logger.warn('[BFF][Effect] Detected { api, layer } export without createHandler. Prefer `defineEffectBff(...)` from @modern-js/plugin-bff/server to avoid module instance mismatch.');
154
+ const webHandler = (0, external_index_js_namespaceObject.createHttpApiHandler)({
155
+ api: normalizedModule.api,
156
+ layer: normalizedModule.layer,
157
+ openapi: this.api.getServerConfig()?.bff?.effect?.openapi,
158
+ dataPlatform: this.api.getServerConfig()?.bff?.effect?.dataPlatform
159
+ });
160
+ return {
161
+ handler: async (request)=>webHandler.handler(request),
162
+ dispose: async ()=>{
163
+ await webHandler.dispose();
164
+ }
165
+ };
166
+ }
167
+ return null;
168
+ }
169
+ async reloadHandler() {
170
+ if (!this.isEffect) return;
171
+ const entryFile = this.resolveEntryFile();
172
+ if (!entryFile) {
173
+ await this.disposeCurrentHandler();
174
+ this.handler = null;
175
+ return;
176
+ }
177
+ if (!await utils_namespaceObject.fs.pathExists(entryFile)) {
178
+ await this.disposeCurrentHandler();
179
+ this.handler = null;
180
+ return;
181
+ }
182
+ await this.disposeCurrentHandler();
183
+ const resolvedEntryFile = require.resolve(entryFile);
184
+ if (Object.hasOwn(require.cache, resolvedEntryFile)) delete require.cache[resolvedEntryFile];
185
+ let mod;
186
+ try {
187
+ mod = await (0, utils_namespaceObject.compatibleRequire)(entryFile, false);
188
+ } catch (error) {
189
+ utils_namespaceObject.logger.error(`[BFF][Effect] Failed to load Effect entry: ${entryFile}\n${String(error)}`);
190
+ this.handler = null;
191
+ return;
192
+ }
193
+ const loaded = await this.loadEffectHandlerFromModule(mod);
194
+ if (!loaded) {
195
+ utils_namespaceObject.logger.warn(`[BFF][Effect] Invalid Effect entry module: ${entryFile}. Export { api, layer } or handler.`);
196
+ this.handler = null;
197
+ return;
198
+ }
199
+ this.handler = loaded.handler;
200
+ this.dispose = loaded.dispose || null;
201
+ }
202
+ async disposeCurrentHandler() {
203
+ if (!this.dispose) return;
204
+ try {
205
+ await this.dispose();
206
+ } catch (error) {
207
+ utils_namespaceObject.logger.warn(`[BFF][Effect] Failed to dispose previous handler: ${String(error)}`);
208
+ } finally{
209
+ this.dispose = null;
210
+ }
211
+ }
212
+ async handleRuntimeError(error, c) {
213
+ try {
214
+ const serverConfig = this.api.getServerConfig();
215
+ const onErrorHandler = serverConfig?.onError;
216
+ if (onErrorHandler) {
217
+ const onErrorContext = this.ensureJsonContext(c);
218
+ const result = await onErrorHandler(error instanceof Error ? error : new Error(String(error)), onErrorContext);
219
+ if (result instanceof Response) return result;
220
+ } else utils_namespaceObject.logger.error(error);
221
+ } catch (configError) {
222
+ utils_namespaceObject.logger.error(`Error in serverConfig.onError handler: ${configError}`);
223
+ }
224
+ const status = 'object' == typeof error && null !== error && 'status' in error && 'number' == typeof error.status ? error.status : 500;
225
+ return new Response(JSON.stringify({
226
+ message: error instanceof Error ? error.message : '[BFF] Internal Server Error'
227
+ }), {
228
+ status,
229
+ headers: {
230
+ 'content-type': 'application/json; charset=utf-8'
231
+ }
232
+ });
233
+ }
234
+ ensureJsonContext(c) {
235
+ const maybeJsonContext = c;
236
+ if ('function' == typeof maybeJsonContext.json) return c;
237
+ const headers = {
238
+ 'content-type': 'application/json; charset=utf-8'
239
+ };
240
+ const withJson = Object.assign({}, c, {
241
+ json: (data, status = 200, extraHeaders)=>{
242
+ const responseHeaders = new Headers(headers);
243
+ if (extraHeaders) new Headers(extraHeaders).forEach((value, key)=>{
244
+ responseHeaders.set(key, value);
245
+ });
246
+ return new Response(JSON.stringify(data), {
247
+ status,
248
+ headers: responseHeaders
249
+ });
250
+ }
251
+ });
252
+ return withJson;
253
+ }
254
+ constructor(api){
255
+ this.isEffect = true;
256
+ this.effectMiddleware = null;
257
+ this.handler = null;
258
+ this.dispose = null;
259
+ this.registerMiddleware = async (options)=>{
260
+ const { prefix, enableHandleWeb } = options;
261
+ const { bffRuntimeFramework, middlewares: globalMiddlewares } = this.api.getServerContext();
262
+ if ('hono' === bffRuntimeFramework) {
263
+ this.isEffect = false;
264
+ return;
265
+ }
266
+ await this.reloadHandler();
267
+ this.effectMiddleware = {
268
+ name: 'effect-bff-handler',
269
+ path: enableHandleWeb ? '*' : `${prefix}/*`,
270
+ method: 'all',
271
+ order: 'post',
272
+ before,
273
+ handler: async (c, next)=>{
274
+ if (!this.handler) {
275
+ if (enableHandleWeb) return void await next();
276
+ return this.handleRuntimeError(new Error('[BFF][Effect] Missing Effect entry. Define api/effect/index or configure bff.effect.entry.'), c);
277
+ }
278
+ let response;
279
+ try {
280
+ const effectRequest = createRequestForMountedPrefix(c.req.raw, prefix);
281
+ const effectContext = {
282
+ request: effectRequest,
283
+ env: c.env,
284
+ path: c.req.path,
285
+ method: c.req.method
286
+ };
287
+ response = await (0, external_context_js_namespaceObject.runWithEffectContext)(effectContext, ()=>this.handler.length > 1 ? this.handler(effectRequest, effectContext) : this.handler(effectRequest));
288
+ } catch (error) {
289
+ return this.handleRuntimeError(error, c);
290
+ }
291
+ if (!maybeResponse(response)) return this.handleRuntimeError(new Error('[BFF][Effect] Effect handler must return a Response instance.'), c);
292
+ if (404 === response.status && enableHandleWeb) return void await next();
293
+ return new Response(response.body, response);
294
+ }
295
+ };
296
+ globalMiddlewares.push(this.effectMiddleware);
297
+ };
298
+ this.onApiHandlersUpdated = async ()=>{
299
+ if (!this.isEffect || (0, utils_namespaceObject.isProd)()) return;
300
+ await this.reloadHandler();
301
+ };
302
+ this.api = api;
303
+ }
304
+ }
305
+ exports.EffectAdapter = __webpack_exports__.EffectAdapter;
306
+ for(var __rspack_i in __webpack_exports__)if (-1 === [
307
+ "EffectAdapter"
308
+ ].indexOf(__rspack_i)) exports[__rspack_i] = __webpack_exports__[__rspack_i];
309
+ Object.defineProperty(exports, '__esModule', {
310
+ value: true
311
+ });
@@ -0,0 +1,48 @@
1
+ "use strict";
2
+ var __webpack_require__ = {};
3
+ (()=>{
4
+ __webpack_require__.d = (exports1, definition)=>{
5
+ for(var key in definition)if (__webpack_require__.o(definition, key) && !__webpack_require__.o(exports1, key)) Object.defineProperty(exports1, key, {
6
+ enumerable: true,
7
+ get: definition[key]
8
+ });
9
+ };
10
+ })();
11
+ (()=>{
12
+ __webpack_require__.o = (obj, prop)=>Object.prototype.hasOwnProperty.call(obj, prop);
13
+ })();
14
+ (()=>{
15
+ __webpack_require__.r = (exports1)=>{
16
+ if ("u" > typeof Symbol && Symbol.toStringTag) Object.defineProperty(exports1, Symbol.toStringTag, {
17
+ value: 'Module'
18
+ });
19
+ Object.defineProperty(exports1, '__esModule', {
20
+ value: true
21
+ });
22
+ };
23
+ })();
24
+ var __webpack_exports__ = {};
25
+ __webpack_require__.r(__webpack_exports__);
26
+ __webpack_require__.d(__webpack_exports__, {
27
+ runWithEffectContext: ()=>runWithEffectContext,
28
+ useEffectContext: ()=>useEffectContext
29
+ });
30
+ const external_node_async_hooks_namespaceObject = require("node:async_hooks");
31
+ const kEffectContextStorage = Symbol.for('modernjs.plugin-bff.effectContextStorage');
32
+ const globalStore = globalThis;
33
+ const effectContextStorage = globalStore[kEffectContextStorage] ?? (globalStore[kEffectContextStorage] = new external_node_async_hooks_namespaceObject.AsyncLocalStorage());
34
+ const runWithEffectContext = (context, cb)=>effectContextStorage.run(context, cb);
35
+ const useEffectContext = ()=>{
36
+ const context = effectContextStorage.getStore();
37
+ if (!context) throw new Error("Can't call useEffectContext out of Effect runtime scope");
38
+ return context;
39
+ };
40
+ exports.runWithEffectContext = __webpack_exports__.runWithEffectContext;
41
+ exports.useEffectContext = __webpack_exports__.useEffectContext;
42
+ for(var __rspack_i in __webpack_exports__)if (-1 === [
43
+ "runWithEffectContext",
44
+ "useEffectContext"
45
+ ].indexOf(__rspack_i)) exports[__rspack_i] = __webpack_exports__[__rspack_i];
46
+ Object.defineProperty(exports, '__esModule', {
47
+ value: true
48
+ });