@bleedingdev/modern-js-bff-core 3.2.0-ultramodern.12 → 3.2.0-ultramodern.121

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 (46) hide show
  1. package/dist/cjs/adapter-kit/index.js +140 -0
  2. package/dist/cjs/adapter-kit/parity.js +546 -0
  3. package/dist/cjs/api.js +9 -5
  4. package/dist/cjs/client/generateClient.js +74 -17
  5. package/dist/cjs/client/index.js +9 -5
  6. package/dist/cjs/client/result.js +13 -9
  7. package/dist/cjs/contracts/eventContracts.js +14 -10
  8. package/dist/cjs/errors/http.js +13 -9
  9. package/dist/cjs/index.js +83 -41
  10. package/dist/cjs/operators/http.js +9 -5
  11. package/dist/cjs/router/constants.js +9 -5
  12. package/dist/cjs/router/index.js +12 -8
  13. package/dist/cjs/router/utils.js +9 -5
  14. package/dist/cjs/security/crossProjectPolicy.js +25 -13
  15. package/dist/cjs/security/operationContracts.js +155 -59
  16. package/dist/cjs/security/resolveCrossProjectPolicy.js +65 -0
  17. package/dist/cjs/types.js +18 -13
  18. package/dist/cjs/utils/alias.js +9 -5
  19. package/dist/cjs/utils/debug.js +9 -5
  20. package/dist/cjs/utils/index.js +12 -8
  21. package/dist/cjs/utils/meta.js +15 -11
  22. package/dist/cjs/utils/storage.js +9 -5
  23. package/dist/cjs/utils/validate.js +9 -5
  24. package/dist/esm/adapter-kit/index.mjs +75 -0
  25. package/dist/esm/adapter-kit/parity.mjs +490 -0
  26. package/dist/esm/client/generateClient.mjs +66 -13
  27. package/dist/esm/index.mjs +2 -0
  28. package/dist/esm/rslib-runtime.mjs +18 -0
  29. package/dist/esm/security/crossProjectPolicy.mjs +10 -2
  30. package/dist/esm/security/operationContracts.mjs +111 -37
  31. package/dist/esm/security/resolveCrossProjectPolicy.mjs +27 -0
  32. package/dist/esm-node/adapter-kit/index.mjs +76 -0
  33. package/dist/esm-node/adapter-kit/parity.mjs +491 -0
  34. package/dist/esm-node/client/generateClient.mjs +66 -13
  35. package/dist/esm-node/index.mjs +2 -0
  36. package/dist/esm-node/rslib-runtime.mjs +19 -0
  37. package/dist/esm-node/security/crossProjectPolicy.mjs +10 -2
  38. package/dist/esm-node/security/operationContracts.mjs +111 -37
  39. package/dist/esm-node/security/resolveCrossProjectPolicy.mjs +28 -0
  40. package/dist/types/adapter-kit/index.d.ts +90 -0
  41. package/dist/types/adapter-kit/parity.d.ts +102 -0
  42. package/dist/types/index.d.ts +2 -0
  43. package/dist/types/security/crossProjectPolicy.d.ts +40 -1
  44. package/dist/types/security/operationContracts.d.ts +60 -4
  45. package/dist/types/security/resolveCrossProjectPolicy.d.ts +48 -0
  46. package/package.json +12 -10
@@ -0,0 +1,18 @@
1
+ var __webpack_modules__ = {};
2
+ var __webpack_module_cache__ = {};
3
+ function __webpack_require__(moduleId) {
4
+ var cachedModule = __webpack_module_cache__[moduleId];
5
+ if (void 0 !== cachedModule) return cachedModule.exports;
6
+ var module = __webpack_module_cache__[moduleId] = {
7
+ exports: {}
8
+ };
9
+ __webpack_modules__[moduleId](module, module.exports, __webpack_require__);
10
+ return module.exports;
11
+ }
12
+ __webpack_require__.m = __webpack_modules__;
13
+ (()=>{
14
+ __webpack_require__.add = function(modules) {
15
+ Object.assign(__webpack_require__.m, modules);
16
+ };
17
+ })();
18
+ export { __webpack_require__ };
@@ -49,10 +49,18 @@ const evaluateCrossProjectPolicy = (headers, policy)=>{
49
49
  }
50
50
  const requestId = String(envelope.requestId || '').trim();
51
51
  if (!requestId) return createViolation('missing_request_id', 'Cross-project envelope does not include a valid requestId', status);
52
+ const claimedNamespace = extractNamespace(requestId);
53
+ let effectiveNamespace = claimedNamespace;
54
+ if ('function' == typeof policy.verifyProducerIdentity) {
55
+ const verifiedNamespaceRaw = policy.verifyProducerIdentity(headers);
56
+ const verifiedNamespace = 'string' == typeof verifiedNamespaceRaw ? verifiedNamespaceRaw.trim().toLowerCase() : void 0;
57
+ if (!verifiedNamespace) return createViolation('producer_identity_mismatch', 'Producer identity could not be verified for this request', status);
58
+ if (verifiedNamespace !== claimedNamespace) return createViolation('producer_identity_mismatch', `Envelope namespace "${claimedNamespace || 'unknown'}" does not match verified producer identity "${verifiedNamespace}"`, status);
59
+ effectiveNamespace = verifiedNamespace;
60
+ }
52
61
  const namespaces = (policy.allowedNamespaces || []).map((item)=>item.trim().toLowerCase()).filter(Boolean);
53
62
  if (namespaces.length > 0) {
54
- const namespace = extractNamespace(requestId);
55
- if (!namespace || !namespaces.includes(namespace)) return createViolation('namespace_not_allowed', `Producer namespace "${namespace || 'unknown'}" is not allowed`, status);
63
+ if (!effectiveNamespace || !namespaces.includes(effectiveNamespace)) return createViolation('namespace_not_allowed', `Producer namespace "${effectiveNamespace || 'unknown'}" is not allowed`, status);
56
64
  }
57
65
  if (requireOperationContext) {
58
66
  const operationContext = readHeader(headers, operationContextHeader);
@@ -1,5 +1,84 @@
1
1
  import { createHash } from "crypto";
2
+ import "reflect-metadata";
3
+ import { HttpMetadata } from "../types.mjs";
4
+ import { __webpack_require__ } from "../rslib-runtime.mjs";
5
+ import * as __rspack_external_zod from "zod";
6
+ __webpack_require__.add({
7
+ zod (module) {
8
+ module.exports = __rspack_external_zod;
9
+ }
10
+ });
2
11
  const DEFAULT_OPERATION_VERSION = 1;
12
+ const deriveOperationVersion = (packageVersion)=>{
13
+ if ('string' != typeof packageVersion) return DEFAULT_OPERATION_VERSION;
14
+ const match = packageVersion.trim().match(/^v?(\d+)\./);
15
+ if (!match) return DEFAULT_OPERATION_VERSION;
16
+ const major = Number.parseInt(match[1], 10);
17
+ return Number.isInteger(major) && major >= 0 ? major : DEFAULT_OPERATION_VERSION;
18
+ };
19
+ const stableStringify = (value)=>{
20
+ if (Array.isArray(value)) return `[${value.map((item)=>stableStringify(item)).join(',')}]`;
21
+ if (value && 'object' == typeof value) {
22
+ const entries = Object.entries(value).filter(([, entryValue])=>void 0 !== entryValue).sort(([a], [b])=>a.localeCompare(b)).map(([key, entryValue])=>`${JSON.stringify(key)}:${stableStringify(entryValue)}`);
23
+ return `{${entries.join(',')}}`;
24
+ }
25
+ return JSON.stringify(value) ?? 'null';
26
+ };
27
+ const sha256 = (text)=>createHash('sha256').update(text).digest('hex');
28
+ let cachedZodToJSONSchema;
29
+ const resolveZodToJSONSchema = ()=>{
30
+ if (void 0 !== cachedZodToJSONSchema) return cachedZodToJSONSchema;
31
+ try {
32
+ const zod = __webpack_require__("zod");
33
+ const candidate = zod?.toJSONSchema ?? zod?.z?.toJSONSchema;
34
+ cachedZodToJSONSchema = 'function' == typeof candidate ? candidate : null;
35
+ } catch {
36
+ cachedZodToJSONSchema = null;
37
+ }
38
+ return cachedZodToJSONSchema;
39
+ };
40
+ const INPUT_SCHEMA_METADATA_KEYS = [
41
+ HttpMetadata.Data,
42
+ HttpMetadata.Query,
43
+ HttpMetadata.Params,
44
+ HttpMetadata.Headers,
45
+ HttpMetadata.Files
46
+ ];
47
+ const serializeSchema = (schema)=>{
48
+ const toJSONSchema = resolveZodToJSONSchema();
49
+ if (toJSONSchema) try {
50
+ return toJSONSchema(schema, {
51
+ io: 'input',
52
+ unrepresentable: 'any'
53
+ });
54
+ } catch {}
55
+ return {
56
+ __unserializableSchema: true
57
+ };
58
+ };
59
+ const serializeOperationSchemas = (handler)=>{
60
+ if ('function' != typeof handler) return;
61
+ const serialized = {};
62
+ for (const metadataKey of INPUT_SCHEMA_METADATA_KEYS){
63
+ let schema;
64
+ try {
65
+ schema = Reflect.getMetadata(metadataKey, handler);
66
+ } catch {
67
+ schema = void 0;
68
+ }
69
+ if (null != schema) serialized[metadataKey] = serializeSchema(schema);
70
+ }
71
+ return Object.keys(serialized).length > 0 ? serialized : void 0;
72
+ };
73
+ const createOperationContractHash = (operation, requestId)=>sha256(stableStringify({
74
+ httpMethod: String(operation.httpMethod || '').toUpperCase(),
75
+ name: operation.name,
76
+ requestId,
77
+ routePath: operation.routePath,
78
+ ...operation.schemas ? {
79
+ schemas: operation.schemas
80
+ } : {}
81
+ }));
3
82
  const createOperationEntries = (handlers)=>handlers.map((item)=>({
4
83
  name: item.name,
5
84
  httpMethod: String(item.httpMethod || '').toUpperCase(),
@@ -9,49 +88,44 @@ const createOperationEntries = (handlers)=>handlers.map((item)=>({
9
88
  const keyB = `${b.routePath}:${b.httpMethod}:${b.name}`;
10
89
  return keyA.localeCompare(keyB);
11
90
  });
12
- const createOperationSchemaHash = (operationEntries, requestId)=>createHash('sha256').update(JSON.stringify({
13
- operations: operationEntries.map((item)=>({
14
- name: item.name,
15
- httpMethod: item.httpMethod,
16
- routePath: item.routePath
17
- })),
91
+ const createOperationSchemaHash = (operationEntries, requestId)=>sha256(stableStringify({
92
+ operations: [
93
+ ...operationEntries
94
+ ].map((item)=>({
95
+ hash: item.schemaHash ?? createOperationContractHash({
96
+ name: item.name,
97
+ httpMethod: item.httpMethod,
98
+ routePath: item.routePath
99
+ }, requestId)
100
+ })).sort((a, b)=>a.hash.localeCompare(b.hash)),
18
101
  requestId
19
- })).digest('hex');
20
- const buildOperationContractMap = ({ handlers, requestId })=>{
102
+ }));
103
+ const buildOperationContractMap = ({ handlers, requestId, operationVersion })=>{
21
104
  const normalizedRequestId = 'string' == typeof requestId && requestId.trim().length > 0 ? requestId.trim() : 'default';
22
- const byModule = new Map();
105
+ const normalizedOperationVersion = 'number' == typeof operationVersion && Number.isInteger(operationVersion) ? operationVersion : DEFAULT_OPERATION_VERSION;
106
+ const contracts = {};
23
107
  handlers.forEach((item)=>{
24
- const moduleId = 'string' == typeof item.filename && item.filename.length > 0 ? item.filename : '__anonymous__';
25
- const group = byModule.get(moduleId) || [];
26
- group.push({
108
+ const httpMethod = String(item.httpMethod || '').toUpperCase();
109
+ const schemaHash = createOperationContractHash({
27
110
  name: item.name,
28
- httpMethod: item.httpMethod.toUpperCase(),
111
+ httpMethod,
112
+ routePath: item.routePath,
113
+ schemas: serializeOperationSchemas(item.handler)
114
+ }, normalizedRequestId);
115
+ const operationId = `${normalizedRequestId}:${item.name}`;
116
+ const contract = {
117
+ requestId: normalizedRequestId,
118
+ operationVersion: normalizedOperationVersion,
119
+ schemaHash,
120
+ method: httpMethod,
29
121
  routePath: item.routePath,
122
+ operationId,
123
+ handlerName: item.name,
30
124
  filename: item.filename
31
- });
32
- byModule.set(moduleId, group);
33
- });
34
- const contracts = {};
35
- byModule.forEach((moduleEntries)=>{
36
- const entries = createOperationEntries(moduleEntries);
37
- const schemaHash = createOperationSchemaHash(entries, normalizedRequestId);
38
- const filename = moduleEntries[0]?.filename;
39
- entries.forEach((entry)=>{
40
- const operationId = `${normalizedRequestId}:${entry.name}`;
41
- const contract = {
42
- requestId: normalizedRequestId,
43
- operationVersion: DEFAULT_OPERATION_VERSION,
44
- schemaHash,
45
- method: entry.httpMethod,
46
- routePath: entry.routePath,
47
- operationId,
48
- handlerName: entry.name,
49
- filename
50
- };
51
- contracts[`${entry.httpMethod}:${entry.routePath}`] = contract;
52
- contracts[`operation:${operationId}`] = contract;
53
- });
125
+ };
126
+ contracts[`${httpMethod}:${item.routePath}`] = contract;
127
+ contracts[`operation:${operationId}`] = contract;
54
128
  });
55
129
  return contracts;
56
130
  };
57
- export { DEFAULT_OPERATION_VERSION, buildOperationContractMap, createOperationEntries, createOperationSchemaHash };
131
+ export { DEFAULT_OPERATION_VERSION, buildOperationContractMap, createOperationContractHash, createOperationEntries, createOperationSchemaHash, deriveOperationVersion, serializeOperationSchemas };
@@ -0,0 +1,27 @@
1
+ import { buildOperationContractMap } from "./operationContracts.mjs";
2
+ const resolveCrossProjectPolicy = (input)=>{
3
+ const { crossProjectPolicy, handlers, requestId, isCrossProjectServer, operationVersion } = input;
4
+ if (!crossProjectPolicy && !isCrossProjectServer) return;
5
+ const policy = crossProjectPolicy ?? {};
6
+ const effectiveRequestId = 'string' == typeof requestId && requestId.trim().length > 0 ? requestId : 'default';
7
+ const generatedContracts = buildOperationContractMap({
8
+ handlers,
9
+ requestId: effectiveRequestId,
10
+ operationVersion
11
+ });
12
+ return {
13
+ ...policy,
14
+ enabled: policy.enabled ?? Boolean(isCrossProjectServer),
15
+ requireEnvelope: policy.requireEnvelope ?? true,
16
+ requireOperationContext: policy.requireOperationContext ?? true,
17
+ requireOperationContextDetails: policy.requireOperationContextDetails ?? true,
18
+ requireOperationSchemaHash: policy.requireOperationSchemaHash ?? true,
19
+ requireOperationVersion: policy.requireOperationVersion ?? true,
20
+ allowUnknownOperations: policy.allowUnknownOperations ?? false,
21
+ expectedOperationContracts: {
22
+ ...policy.expectedOperationContracts ?? {},
23
+ ...generatedContracts
24
+ }
25
+ };
26
+ };
27
+ export { resolveCrossProjectPolicy };
@@ -0,0 +1,76 @@
1
+ import "node:module";
2
+ import "reflect-metadata";
3
+ import { evaluateCrossProjectPolicy } from "../security/crossProjectPolicy.mjs";
4
+ import { HttpMetadata, HttpMethod } from "../types.mjs";
5
+ import { isInputParamsDeciderHandler, isWithMetaHandler } from "../utils/index.mjs";
6
+ const API_ROUTE_METHODS = {
7
+ [HttpMethod.Get]: 'get',
8
+ [HttpMethod.Post]: 'post',
9
+ [HttpMethod.Put]: 'put',
10
+ [HttpMethod.Delete]: 'delete',
11
+ [HttpMethod.Connect]: 'connect',
12
+ [HttpMethod.Trace]: 'trace',
13
+ [HttpMethod.Patch]: 'patch',
14
+ [HttpMethod.Options]: 'options',
15
+ [HttpMethod.Head]: 'head'
16
+ };
17
+ const toApiRouteMethod = (httpMethod)=>{
18
+ const method = API_ROUTE_METHODS[httpMethod];
19
+ if (!method) throw new Error(`[bff-core] Unsupported HTTP method "${String(httpMethod)}" in API handler info`);
20
+ return method;
21
+ };
22
+ const getRouteMiddlewares = (handler)=>{
23
+ const middlewares = Reflect.getMetadata('middleware', handler);
24
+ return Array.isArray(middlewares) ? middlewares : [];
25
+ };
26
+ const planApiRoutes = (handlerInfos)=>handlerInfos.map(({ routePath, handler, httpMethod })=>({
27
+ method: toApiRouteMethod(httpMethod),
28
+ routePath,
29
+ handler,
30
+ middlewares: getRouteMiddlewares(handler)
31
+ }));
32
+ const HANDLER_WITH_SCHEMA = 'HANDLER_WITH_SCHEMA';
33
+ const isSchemaApiHandler = (handler)=>{
34
+ if ('function' != typeof handler) return false;
35
+ const marked = handler;
36
+ return true === marked[HANDLER_WITH_SCHEMA];
37
+ };
38
+ const getApiHandlerMode = (handler)=>{
39
+ if (isWithMetaHandler(handler)) return 'meta';
40
+ if (isSchemaApiHandler(handler)) return 'schema';
41
+ if (isInputParamsDeciderHandler(handler)) return 'inputParamsDecider';
42
+ return 'plain';
43
+ };
44
+ const mapSchemaHandlerResult = (result)=>{
45
+ if ('HandleSuccess' === result.type) return {
46
+ success: true,
47
+ status: 200,
48
+ body: result.value
49
+ };
50
+ return {
51
+ success: false,
52
+ status: 'InputValidationError' === result.type ? 400 : 500,
53
+ body: result.message
54
+ };
55
+ };
56
+ const getResponseMetaList = (handler)=>{
57
+ const responseMeta = Reflect.getMetadata(HttpMetadata.Response, handler);
58
+ return Array.isArray(responseMeta) ? responseMeta : [];
59
+ };
60
+ const buildPositionalHandlerArgs = (input)=>[
61
+ ...Object.values(input.params),
62
+ input
63
+ ];
64
+ const checkCrossProjectPolicy = (headers, policy)=>{
65
+ const violation = evaluateCrossProjectPolicy(headers, policy);
66
+ if (!violation) return null;
67
+ return {
68
+ status: violation.status,
69
+ body: {
70
+ code: violation.code,
71
+ reason: violation.reason,
72
+ message: violation.message
73
+ }
74
+ };
75
+ };
76
+ export { HANDLER_WITH_SCHEMA, buildPositionalHandlerArgs, checkCrossProjectPolicy, getApiHandlerMode, getResponseMetaList, getRouteMiddlewares, isSchemaApiHandler, mapSchemaHandlerResult, planApiRoutes, toApiRouteMethod };