@flusys/nestjs-entity-builder 9.1.2 → 9.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/fesm/677.js CHANGED
@@ -725,6 +725,128 @@ function writeOptions(state) {
725
725
  publishEvents: false
726
726
  } : {};
727
727
  }
728
+ /** A get_all request's own query: what a `fromRequest` query reads. A list body has none. */ function requestQuery(state) {
729
+ return Array.isArray(state.input) ? {} : state.input;
730
+ }
731
+ /** A request's filter the way the API controller reads it: a value matches as equal, a list as one of its items - never an operator the caller sent. */ function requestFilter(filter) {
732
+ return Object.fromEntries(Object.entries(filter ?? {}).map(([key, value])=>[
733
+ key,
734
+ {
735
+ op: Array.isArray(value) ? 'in' : 'eq',
736
+ value
737
+ }
738
+ ]));
739
+ }
740
+ /** Only the fields a request's `select` names, `id` always kept; every field when it names none. */ function selectFields(rows, select) {
741
+ if (!select?.length) return rows;
742
+ const kept = new Set([
743
+ 'id',
744
+ ...select
745
+ ]);
746
+ return rows.map((row)=>Object.fromEntries(Object.entries(row).filter(([key])=>kept.has(key))));
747
+ }
748
+ async function runQuery(node, cfg, env, state) {
749
+ const ctx = evaluationContext(state, env);
750
+ const service = await env.entityService(cfg.entityCode, state.qr);
751
+ const request = cfg.fromRequest ? requestQuery(state) : {};
752
+ const filter = {
753
+ ...requestFilter(request.filter),
754
+ ...await mapFilter(node, cfg.filter, env, state, 'skip')
755
+ };
756
+ const searched = cfg.search ? await env.ruleEngine.resolveExpression(cfg.search, ctx) : cfg.fromRequest ? state.request['query']?.['q'] : undefined;
757
+ const search = typeof searched === 'string' && searched ? searched : undefined;
758
+ const sort = cfg.sort ? {
759
+ [cfg.sort.field]: cfg.sort.direction === 'DESC' ? 'DESC' : 'ASC'
760
+ } : request.sort;
761
+ const page = request.pagination?.currentPage ?? 0;
762
+ const pageSize = Math.min(request.pagination?.pageSize ?? cfg.limit ?? 50, flow_types/* .FLOW_LIMITS.MAX_QUERY_LIMIT */.Or.MAX_QUERY_LIMIT);
763
+ const withDeleted = request.withDeleted === true ? {
764
+ withDeleted: true
765
+ } : {};
766
+ const { data, total } = await service.getAll(search, {
767
+ filter,
768
+ sort,
769
+ pagination: {
770
+ currentPage: page,
771
+ pageSize
772
+ },
773
+ ...withDeleted
774
+ }, readOptions(state));
775
+ return {
776
+ output: {
777
+ items: selectFields(data, request.select),
778
+ total,
779
+ page,
780
+ pageSize
781
+ }
782
+ };
783
+ }
784
+ /** A Respond step's body: as built (raw), or the API controller envelope around it. */ async function respondBody(node, cfg, env, state) {
785
+ const ctx = evaluationContext(state, env);
786
+ const format = cfg.format ?? 'raw';
787
+ const built = async ()=>cfg.bodyExpression ? env.ruleEngine.resolveExpression(cfg.bodyExpression, ctx) : mapValues(cfg.body, env, state);
788
+ if (format === 'raw') return built();
789
+ const variables = await mapValues(cfg.messageVariables, env, state);
790
+ const envelope = {
791
+ success: cfg.statusCode < 400,
792
+ message: cfg.message ?? '',
793
+ messageKey: cfg.messageKey ?? '',
794
+ ...Object.keys(variables).length ? {
795
+ messageVariables: variables
796
+ } : {}
797
+ };
798
+ if (format === 'message') return envelope;
799
+ if (format === 'single') return {
800
+ ...envelope,
801
+ data: await built()
802
+ };
803
+ const value = await built();
804
+ if (value !== null && value !== undefined && !Array.isArray(value)) throw new flow_errors/* .FlowKeyedError */.Hp(message_keys/* .FLOW_ERROR_MESSAGES.RESPOND_DATA_NOT_LIST */.zr.RESPOND_DATA_NOT_LIST, {
805
+ node: (0,flow_types/* .nodeLabel */.dA)(node)
806
+ });
807
+ const data = value ?? [];
808
+ const count = async (name, fallback)=>{
809
+ const expression = cfg[name];
810
+ if (expression === undefined) return fallback;
811
+ const resolved = await env.ruleEngine.resolveExpression(expression, ctx);
812
+ if (resolved === null || resolved === undefined || resolved === '') return fallback;
813
+ const n = Number(resolved);
814
+ if (!Number.isInteger(n) || n < 0) throw new flow_errors/* .FlowKeyedError */.Hp(message_keys/* .FLOW_ERROR_MESSAGES.RESPOND_META_INVALID */.zr.RESPOND_META_INVALID, {
815
+ node: (0,flow_types/* .nodeLabel */.dA)(node),
816
+ name
817
+ });
818
+ return n;
819
+ };
820
+ if (format === 'bulk') {
821
+ const total = await count('total', Array.isArray(state.input) ? state.input.length : data.length);
822
+ return {
823
+ ...envelope,
824
+ data,
825
+ meta: {
826
+ count: data.length,
827
+ total,
828
+ failed: Math.max(total - data.length, 0)
829
+ }
830
+ };
831
+ }
832
+ const pagination = requestQuery(state).pagination;
833
+ const total = await count('total', data.length);
834
+ const page = await count('page', pagination?.currentPage ?? 0);
835
+ const pageSize = await count('pageSize', pagination?.pageSize ?? data.length);
836
+ const totalPages = pageSize > 0 ? Math.ceil(total / pageSize) : 1;
837
+ return {
838
+ ...envelope,
839
+ data,
840
+ meta: {
841
+ total,
842
+ page,
843
+ pageSize,
844
+ count: data.length,
845
+ hasMore: page < totalPages - 1,
846
+ totalPages
847
+ }
848
+ };
849
+ }
728
850
  function readOptions(state) {
729
851
  return {
730
852
  queryRunner: state.qr
@@ -1407,8 +1529,11 @@ const nobody = (extra = {})=>({
1407
1529
  const id = asId(await env.ruleEngine.resolveExpression(cfg.id, ctx));
1408
1530
  const service = await env.entityService(cfg.entityCode, state.qr);
1409
1531
  try {
1532
+ const record = await service.findById(id, readOptions(state));
1410
1533
  return {
1411
- output: await service.findById(id, readOptions(state))
1534
+ output: cfg.fromRequest ? selectFields([
1535
+ record
1536
+ ], requestQuery(state).select)[0] : record
1412
1537
  };
1413
1538
  } catch (error) {
1414
1539
  if (error instanceof common_.NotFoundException && cfg.onNotFound === 'null') return {
@@ -1418,29 +1543,7 @@ const nobody = (extra = {})=>({
1418
1543
  }
1419
1544
  }
1420
1545
  case 'entity_query':
1421
- {
1422
- const cfg = c;
1423
- const service = await env.entityService(cfg.entityCode, state.qr);
1424
- const filter = await mapFilter(node, cfg.filter, env, state, 'skip');
1425
- const search = cfg.search ? await env.ruleEngine.resolveExpression(cfg.search, ctx) : undefined;
1426
- const limit = Math.min(cfg.limit ?? 50, flow_types/* .FLOW_LIMITS.MAX_QUERY_LIMIT */.Or.MAX_QUERY_LIMIT);
1427
- const { data, total } = await service.getAll(typeof search === 'string' && search ? search : undefined, {
1428
- filter,
1429
- sort: cfg.sort ? {
1430
- [cfg.sort.field]: cfg.sort.direction === 'DESC' ? 'DESC' : 'ASC'
1431
- } : undefined,
1432
- pagination: {
1433
- currentPage: 0,
1434
- pageSize: limit
1435
- }
1436
- }, readOptions(state));
1437
- return {
1438
- output: {
1439
- items: data,
1440
- total
1441
- }
1442
- };
1443
- }
1546
+ return runQuery(node, c, env, state);
1444
1547
  case 'commit':
1445
1548
  {
1446
1549
  const reasonKey = state.mode === 'dry_run' ? message_keys/* .FLOW_SKIP_MESSAGES.COMMIT_DRY_RUN */.CB.COMMIT_DRY_RUN : !state.qr ? message_keys/* .FLOW_SKIP_MESSAGES.COMMIT_NO_TRANSACTION */.CB.COMMIT_NO_TRANSACTION : !state.ownsTransaction ? message_keys/* .FLOW_SKIP_MESSAGES.COMMIT_JOINED */.CB.COMMIT_JOINED : null;
@@ -1542,7 +1645,7 @@ const nobody = (extra = {})=>({
1542
1645
  case 'respond':
1543
1646
  {
1544
1647
  const cfg = c;
1545
- const body = cfg.bodyExpression ? await env.ruleEngine.resolveExpression(cfg.bodyExpression, ctx) : await mapValues(cfg.body, env, state);
1648
+ const body = await respondBody(node, cfg, env, state);
1546
1649
  return {
1547
1650
  output: body,
1548
1651
  respond: {
@@ -2203,19 +2306,29 @@ function conditionProblem(node, options) {
2203
2306
  const warnings = [];
2204
2307
  const err = (message)=>errors.push(message);
2205
2308
  if (!_flow_types_js__rspack_import_5/* .FLOW_SLUG_PATTERN.test */.YD.test(flow.slug ?? '')) err((0,_flusys_nestjs_shared_utils__rspack_import_0.messageRef)(_config_message_keys_js__rspack_import_7/* .FLOW_VALIDATION_MESSAGES.SLUG_INVALID */.qj.SLUG_INVALID));
2206
- const nodes = Array.isArray(flow.nodes) ? flow.nodes : [];
2207
- const edges = Array.isArray(flow.edges) ? flow.edges : [];
2208
- if (!nodes.length) err((0,_flusys_nestjs_shared_utils__rspack_import_0.messageRef)(_config_message_keys_js__rspack_import_7/* .FLOW_VALIDATION_MESSAGES.NO_NODES */.qj.NO_NODES));
2209
- if (nodes.length > _flow_types_js__rspack_import_5/* .FLOW_LIMITS.MAX_NODES */.Or.MAX_NODES) err((0,_flusys_nestjs_shared_utils__rspack_import_0.messageRef)(_config_message_keys_js__rspack_import_7/* .FLOW_VALIDATION_MESSAGES.TOO_MANY_NODES */.qj.TOO_MANY_NODES, {
2309
+ const allNodes = Array.isArray(flow.nodes) ? flow.nodes : [];
2310
+ const allEdges = Array.isArray(flow.edges) ? flow.edges : [];
2311
+ if (!allNodes.length) err((0,_flusys_nestjs_shared_utils__rspack_import_0.messageRef)(_config_message_keys_js__rspack_import_7/* .FLOW_VALIDATION_MESSAGES.NO_NODES */.qj.NO_NODES));
2312
+ if (allNodes.length > _flow_types_js__rspack_import_5/* .FLOW_LIMITS.MAX_NODES */.Or.MAX_NODES) err((0,_flusys_nestjs_shared_utils__rspack_import_0.messageRef)(_config_message_keys_js__rspack_import_7/* .FLOW_VALIDATION_MESSAGES.TOO_MANY_NODES */.qj.TOO_MANY_NODES, {
2210
2313
  max: _flow_types_js__rspack_import_5/* .FLOW_LIMITS.MAX_NODES */.Or.MAX_NODES
2211
2314
  }));
2212
- if (edges.length > _flow_types_js__rspack_import_5/* .FLOW_LIMITS.MAX_EDGES */.Or.MAX_EDGES) err((0,_flusys_nestjs_shared_utils__rspack_import_0.messageRef)(_config_message_keys_js__rspack_import_7/* .FLOW_VALIDATION_MESSAGES.TOO_MANY_EDGES */.qj.TOO_MANY_EDGES, {
2315
+ if (allEdges.length > _flow_types_js__rspack_import_5/* .FLOW_LIMITS.MAX_EDGES */.Or.MAX_EDGES) err((0,_flusys_nestjs_shared_utils__rspack_import_0.messageRef)(_config_message_keys_js__rspack_import_7/* .FLOW_VALIDATION_MESSAGES.TOO_MANY_EDGES */.qj.TOO_MANY_EDGES, {
2213
2316
  max: _flow_types_js__rspack_import_5/* .FLOW_LIMITS.MAX_EDGES */.Or.MAX_EDGES
2214
2317
  }));
2215
- if (errors.length && (!nodes.length || nodes.length > _flow_types_js__rspack_import_5/* .FLOW_LIMITS.MAX_NODES */.Or.MAX_NODES || edges.length > _flow_types_js__rspack_import_5/* .FLOW_LIMITS.MAX_EDGES */.Or.MAX_EDGES)) return {
2318
+ if (errors.length && (!allNodes.length || allNodes.length > _flow_types_js__rspack_import_5/* .FLOW_LIMITS.MAX_NODES */.Or.MAX_NODES || allEdges.length > _flow_types_js__rspack_import_5/* .FLOW_LIMITS.MAX_EDGES */.Or.MAX_EDGES)) return {
2216
2319
  errors,
2217
2320
  warnings
2218
2321
  };
2322
+ const isEntry = (item)=>!!item && typeof item === 'object';
2323
+ const nodes = allNodes.filter(isEntry);
2324
+ const edges = allEdges.filter(isEntry);
2325
+ if (nodes.length < allNodes.length) err((0,_flusys_nestjs_shared_utils__rspack_import_0.messageRef)(_config_message_keys_js__rspack_import_7/* .FLOW_VALIDATION_MESSAGES.NODE_ID_INVALID */.qj.NODE_ID_INVALID, {
2326
+ id: undefined
2327
+ }));
2328
+ if (edges.length < allEdges.length) err((0,_flusys_nestjs_shared_utils__rspack_import_0.messageRef)(_config_message_keys_js__rspack_import_7/* .FLOW_VALIDATION_MESSAGES.EDGE_DANGLING */.qj.EDGE_DANGLING, {
2329
+ from: undefined,
2330
+ to: undefined
2331
+ }));
2219
2332
  const byId = new Map();
2220
2333
  for (const node of nodes){
2221
2334
  if (!_flow_types_js__rspack_import_5/* .FLOW_NODE_ID_PATTERN.test */.z6.test(node?.id ?? '')) {
@@ -2313,7 +2426,7 @@ function conditionProblem(node, options) {
2313
2426
  }
2314
2427
  /** The entities a step reads or writes: its own, its child inserts' and those its lookups read. */ function entitiesOf(node) {
2315
2428
  const codes = collectLookupEntities(node.config);
2316
- if (node.type.startsWith('entity_') && typeof node.config?.['entityCode'] === 'string') codes.add(node.config['entityCode']);
2429
+ if (String(node.type).startsWith('entity_') && typeof node.config?.['entityCode'] === 'string') codes.add(node.config['entityCode']);
2317
2430
  const children = node.type === 'entity_insert' && Array.isArray(node.config?.['children']) ? node.config['children'] : [];
2318
2431
  for (const child of children)if (typeof child?.entityCode === 'string') codes.add(child.entityCode);
2319
2432
  return [
@@ -2343,6 +2456,17 @@ function conditionProblem(node, options) {
2343
2456
  }));
2344
2457
  }
2345
2458
  if (endpoint.authMode === 'public' && steps.some((n)=>(0,_flow_types_js__rspack_import_5/* .isMessageNode */.SE)(n.type))) warnOnce((0,_flusys_nestjs_shared_utils__rspack_import_0.messageRef)(_config_message_keys_js__rspack_import_7/* .FLOW_VALIDATION_MESSAGES.PUBLIC_MESSAGE */.qj.PUBLIC_MESSAGE));
2459
+ for (const n of steps){
2460
+ if (n.config?.['fromRequest'] !== true) continue;
2461
+ if (n.type === 'entity_query' && endpoint.contract !== 'get_all' && endpoint.contract !== 'get_by_ids') warnOnce((0,_flusys_nestjs_shared_utils__rspack_import_0.messageRef)(_config_message_keys_js__rspack_import_7/* .FLOW_VALIDATION_MESSAGES.NODE_QUERY_NOT_GET_ALL */.qj.NODE_QUERY_NOT_GET_ALL, {
2462
+ node: (0,_flow_types_js__rspack_import_5/* .nodeLabel */.dA)(n),
2463
+ endpoint: label
2464
+ }));
2465
+ if (n.type === 'entity_get' && endpoint.contract !== 'get_by_id') warnOnce((0,_flusys_nestjs_shared_utils__rspack_import_0.messageRef)(_config_message_keys_js__rspack_import_7/* .FLOW_VALIDATION_MESSAGES.NODE_GET_NOT_GET_BY_ID */.qj.NODE_GET_NOT_GET_BY_ID, {
2466
+ node: (0,_flow_types_js__rspack_import_5/* .nodeLabel */.dA)(n),
2467
+ endpoint: label
2468
+ }));
2469
+ }
2346
2470
  // A URL callers reach without signing in has no user: user checks deny them, and its record steps are open to anyone who can call it.
2347
2471
  if (!_flow_types_js__rspack_import_5/* .KEYLESS_AUTH_MODES.has */.d7.has(endpoint.authMode)) continue;
2348
2472
  if (steps.some((n)=>(n.type === 'permission_check' || n.type === 'company_branch_check') && n.config?.['subject'] !== 'user' || usesPermissionFunction(n.config))) warnOnce((0,_flusys_nestjs_shared_utils__rspack_import_0.messageRef)(_config_message_keys_js__rspack_import_7/* .FLOW_VALIDATION_MESSAGES.PERMISSION_WITHOUT_USER */.qj.PERMISSION_WITHOUT_USER));
@@ -2624,6 +2748,10 @@ function validateNodeConfig(node, flow, context, err, warn) {
2624
2748
  template: {
2625
2749
  list: _config_message_keys_js__rspack_import_7/* .FLOW_SUBJECT_MESSAGES.TEMPLATE_VALUES */.bc.TEMPLATE_VALUES,
2626
2750
  item: _config_message_keys_js__rspack_import_7/* .FLOW_SUBJECT_MESSAGES.TEMPLATE_VALUE */.bc.TEMPLATE_VALUE
2751
+ },
2752
+ messageVariable: {
2753
+ list: _config_message_keys_js__rspack_import_7/* .FLOW_SUBJECT_MESSAGES.MESSAGE_VARIABLES */.bc.MESSAGE_VARIABLES,
2754
+ item: _config_message_keys_js__rspack_import_7/* .FLOW_SUBJECT_MESSAGES.MESSAGE_VARIABLE */.bc.MESSAGE_VARIABLE
2627
2755
  }
2628
2756
  };
2629
2757
  const optionalExpr = (value, what)=>{
@@ -2665,10 +2793,24 @@ function validateNodeConfig(node, flow, context, err, warn) {
2665
2793
  else if (c.apiKey !== undefined && !(0,_flow_api_key_js__rspack_import_6/* .looksLikeApiKey */.Jb)(c.apiKey) && !(0,_flow_types_js__rspack_import_5/* .isStoredApiKey */.xA)(c.apiKey)) err((0,_flusys_nestjs_shared_utils__rspack_import_0.messageRef)(_config_message_keys_js__rspack_import_7/* .FLOW_VALIDATION_MESSAGES.TRIGGER_API_KEY_INVALID */.qj.TRIGGER_API_KEY_INVALID, at));
2666
2794
  // Field errors are named after the URL (`<path>: <field>`) once a flow has several.
2667
2795
  const path = (0,_flow_types_js__rspack_import_5/* .triggerPath */.um)(node);
2796
+ const prefix = path ? `${path}: ` : '';
2668
2797
  if (c.bodyType !== undefined && !_flow_types_js__rspack_import_5/* .FLOW_BODY_TYPES.includes */.Y3.includes(c.bodyType)) err((0,_flusys_nestjs_shared_utils__rspack_import_0.messageRef)(_config_message_keys_js__rspack_import_7/* .FLOW_VALIDATION_MESSAGES.BODY_TYPE_UNKNOWN */.qj.BODY_TYPE_UNKNOWN, {
2669
2798
  type: String(c.bodyType)
2670
2799
  }));
2671
- validateInputFields(Array.isArray(c.inputSchema) ? c.inputSchema : [], path ? `${path}: ` : '', 1, err);
2800
+ const declared = Array.isArray(c.inputSchema) ? c.inputSchema : [];
2801
+ if (c.contract !== undefined && !_flow_types_js__rspack_import_5/* .FLOW_REQUEST_CONTRACTS.includes */.j_.includes(c.contract)) {
2802
+ err((0,_flusys_nestjs_shared_utils__rspack_import_0.messageRef)(_config_message_keys_js__rspack_import_7/* .FLOW_VALIDATION_MESSAGES.TRIGGER_CONTRACT_INVALID */.qj.TRIGGER_CONTRACT_INVALID, {
2803
+ ...at,
2804
+ contract: String(c.contract)
2805
+ }));
2806
+ } else {
2807
+ // A declared field may not take the name of one the request type already brings.
2808
+ const reserved = new Set((0,_flow_types_js__rspack_import_5/* .contractFields */._h)(c.contract ?? 'custom').map((f)=>f.name));
2809
+ for (const field of declared)if (reserved.has(field?.name)) err((0,_flusys_nestjs_shared_utils__rspack_import_0.messageRef)(_config_message_keys_js__rspack_import_7/* .FLOW_VALIDATION_MESSAGES.INPUT_NAME_RESERVED */.qj.INPUT_NAME_RESERVED, {
2810
+ name: `${prefix}${field.name}`
2811
+ }));
2812
+ }
2813
+ validateInputFields(declared, prefix, 1, err);
2672
2814
  break;
2673
2815
  }
2674
2816
  case 'validate':
@@ -2809,6 +2951,7 @@ function validateNodeConfig(node, flow, context, err, warn) {
2809
2951
  case 'entity_get':
2810
2952
  entity(c.entityCode);
2811
2953
  expr(c.id, (0,_flusys_nestjs_shared_utils__rspack_import_0.messageRef)(_config_message_keys_js__rspack_import_7/* .FLOW_SUBJECT_MESSAGES.THE_RECORD_ID */.bc.THE_RECORD_ID));
2954
+ if (c.fromRequest !== undefined && typeof c.fromRequest !== 'boolean') err((0,_flusys_nestjs_shared_utils__rspack_import_0.messageRef)(_config_message_keys_js__rspack_import_7/* .FLOW_VALIDATION_MESSAGES.NODE_QUERY_FROM_REQUEST_INVALID */.qj.NODE_QUERY_FROM_REQUEST_INVALID, at));
2812
2955
  break;
2813
2956
  case 'entity_query':
2814
2957
  entity(c.entityCode);
@@ -2824,6 +2967,7 @@ function validateNodeConfig(node, flow, context, err, warn) {
2824
2967
  max: _flow_types_js__rspack_import_5/* .FLOW_LIMITS.MAX_QUERY_LIMIT */.Or.MAX_QUERY_LIMIT
2825
2968
  }));
2826
2969
  }
2970
+ if (c.fromRequest !== undefined && typeof c.fromRequest !== 'boolean') err((0,_flusys_nestjs_shared_utils__rspack_import_0.messageRef)(_config_message_keys_js__rspack_import_7/* .FLOW_VALIDATION_MESSAGES.NODE_QUERY_FROM_REQUEST_INVALID */.qj.NODE_QUERY_FROM_REQUEST_INVALID, at));
2827
2971
  break;
2828
2972
  case 'call_flow':
2829
2973
  if (typeof c.flowSlug !== 'string' || !_flow_types_js__rspack_import_5/* .FLOW_SLUG_PATTERN.test */.YD.test(c.flowSlug)) err((0,_flusys_nestjs_shared_utils__rspack_import_0.messageRef)(_config_message_keys_js__rspack_import_7/* .FLOW_VALIDATION_MESSAGES.NODE_CHOOSE_FLOW */.qj.NODE_CHOOSE_FLOW, at));
@@ -2909,8 +3053,28 @@ function validateNodeConfig(node, flow, context, err, warn) {
2909
3053
  {
2910
3054
  const status = c.statusCode;
2911
3055
  if (!(Number.isInteger(status) && status >= 200 && status <= 599)) err((0,_flusys_nestjs_shared_utils__rspack_import_0.messageRef)(_config_message_keys_js__rspack_import_7/* .FLOW_VALIDATION_MESSAGES.NODE_STATUS_2XX_5XX */.qj.NODE_STATUS_2XX_5XX, at));
2912
- exprMap(c.body, KIND.response);
2913
- if (c.bodyExpression !== undefined) expr(c.bodyExpression, (0,_flusys_nestjs_shared_utils__rspack_import_0.messageRef)(_config_message_keys_js__rspack_import_7/* .FLOW_SUBJECT_MESSAGES.THE_RESPONSE */.bc.THE_RESPONSE));
3056
+ const format = c.format ?? 'raw';
3057
+ if (!_flow_types_js__rspack_import_5/* .FLOW_RESPONSE_FORMATS.includes */.A4.includes(format)) {
3058
+ err((0,_flusys_nestjs_shared_utils__rspack_import_0.messageRef)(_config_message_keys_js__rspack_import_7/* .FLOW_VALIDATION_MESSAGES.NODE_RESPOND_FORMAT_INVALID */.qj.NODE_RESPOND_FORMAT_INVALID, {
3059
+ ...at,
3060
+ format: String(format)
3061
+ }));
3062
+ break;
3063
+ }
3064
+ const what = (0,_flusys_nestjs_shared_utils__rspack_import_0.messageRef)(format === 'raw' ? _config_message_keys_js__rspack_import_7/* .FLOW_SUBJECT_MESSAGES.THE_RESPONSE */.bc.THE_RESPONSE : _config_message_keys_js__rspack_import_7/* .FLOW_SUBJECT_MESSAGES.THE_DATA */.bc.THE_DATA);
3065
+ if (format !== 'message') {
3066
+ exprMap(c.body, KIND.response);
3067
+ if (c.bodyExpression !== undefined) expr(c.bodyExpression, what);
3068
+ }
3069
+ if (format === 'raw') break;
3070
+ if (typeof c.message !== 'string' || !c.message.trim() || typeof c.messageKey !== 'string' || !c.messageKey.trim()) err((0,_flusys_nestjs_shared_utils__rspack_import_0.messageRef)(_config_message_keys_js__rspack_import_7/* .FLOW_VALIDATION_MESSAGES.NODE_RESPOND_MESSAGE_REQUIRED */.qj.NODE_RESPOND_MESSAGE_REQUIRED, at));
3071
+ exprMap(c.messageVariables, KIND.messageVariable);
3072
+ if ((format === 'list' || format === 'bulk') && c.bodyExpression === undefined) err((0,_flusys_nestjs_shared_utils__rspack_import_0.messageRef)(_config_message_keys_js__rspack_import_7/* .FLOW_VALIDATION_MESSAGES.NODE_RESPOND_LIST_VALUE_REQUIRED */.qj.NODE_RESPOND_LIST_VALUE_REQUIRED, at));
3073
+ if (format === 'list' || format === 'bulk') optionalExpr(c.total, (0,_flusys_nestjs_shared_utils__rspack_import_0.messageRef)(_config_message_keys_js__rspack_import_7/* .FLOW_SUBJECT_MESSAGES.THE_TOTAL */.bc.THE_TOTAL));
3074
+ if (format === 'list') {
3075
+ optionalExpr(c.page, (0,_flusys_nestjs_shared_utils__rspack_import_0.messageRef)(_config_message_keys_js__rspack_import_7/* .FLOW_SUBJECT_MESSAGES.THE_PAGE */.bc.THE_PAGE));
3076
+ optionalExpr(c.pageSize, (0,_flusys_nestjs_shared_utils__rspack_import_0.messageRef)(_config_message_keys_js__rspack_import_7/* .FLOW_SUBJECT_MESSAGES.THE_PAGE_SIZE */.bc.THE_PAGE_SIZE));
3077
+ }
2914
3078
  break;
2915
3079
  }
2916
3080
  case 'commit':
@@ -3369,26 +3533,28 @@ __webpack_require__.d(__webpack_exports__, {
3369
3533
  },
3370
3534
  6390(__unused_rspack_module, __webpack_exports__, __webpack_require__) {
3371
3535
  /* import */ var _flusys_nestjs_shared_utils__rspack_import_0 = __webpack_require__(9216);
3372
- /* import */ var _config_message_keys_js__rspack_import_3 = __webpack_require__(5732);
3373
- /* import */ var _enums_field_type_enum_js__rspack_import_2 = __webpack_require__(9614);
3536
+ /* import */ var _config_message_keys_js__rspack_import_4 = __webpack_require__(5732);
3537
+ /* import */ var _enums_field_type_enum_js__rspack_import_3 = __webpack_require__(9614);
3374
3538
  /* import */ var _services_field_value_validator_js__rspack_import_1 = __webpack_require__(3525);
3539
+ /* import */ var _flow_types_js__rspack_import_2 = __webpack_require__(6256);
3540
+
3375
3541
 
3376
3542
 
3377
3543
 
3378
3544
 
3379
3545
  const TYPE_MAP = {
3380
- string: _enums_field_type_enum_js__rspack_import_2/* .FieldType.TEXT */.P.TEXT,
3381
- text: _enums_field_type_enum_js__rspack_import_2/* .FieldType.LONG_TEXT */.P.LONG_TEXT,
3382
- number: _enums_field_type_enum_js__rspack_import_2/* .FieldType.DECIMAL */.P.DECIMAL,
3383
- integer: _enums_field_type_enum_js__rspack_import_2/* .FieldType.INTEGER */.P.INTEGER,
3384
- boolean: _enums_field_type_enum_js__rspack_import_2/* .FieldType.BOOLEAN */.P.BOOLEAN,
3385
- date: _enums_field_type_enum_js__rspack_import_2/* .FieldType.DATE */.P.DATE,
3386
- datetime: _enums_field_type_enum_js__rspack_import_2/* .FieldType.DATETIME */.P.DATETIME,
3387
- email: _enums_field_type_enum_js__rspack_import_2/* .FieldType.EMAIL */.P.EMAIL,
3388
- uuid: _enums_field_type_enum_js__rspack_import_2/* .FieldType.RELATION */.P.RELATION,
3389
- object: _enums_field_type_enum_js__rspack_import_2/* .FieldType.JSON */.P.JSON,
3390
- array: _enums_field_type_enum_js__rspack_import_2/* .FieldType.JSON */.P.JSON,
3391
- choice: _enums_field_type_enum_js__rspack_import_2/* .FieldType.SINGLE_SELECT */.P.SINGLE_SELECT
3546
+ string: _enums_field_type_enum_js__rspack_import_3/* .FieldType.TEXT */.P.TEXT,
3547
+ text: _enums_field_type_enum_js__rspack_import_3/* .FieldType.LONG_TEXT */.P.LONG_TEXT,
3548
+ number: _enums_field_type_enum_js__rspack_import_3/* .FieldType.DECIMAL */.P.DECIMAL,
3549
+ integer: _enums_field_type_enum_js__rspack_import_3/* .FieldType.INTEGER */.P.INTEGER,
3550
+ boolean: _enums_field_type_enum_js__rspack_import_3/* .FieldType.BOOLEAN */.P.BOOLEAN,
3551
+ date: _enums_field_type_enum_js__rspack_import_3/* .FieldType.DATE */.P.DATE,
3552
+ datetime: _enums_field_type_enum_js__rspack_import_3/* .FieldType.DATETIME */.P.DATETIME,
3553
+ email: _enums_field_type_enum_js__rspack_import_3/* .FieldType.EMAIL */.P.EMAIL,
3554
+ uuid: _enums_field_type_enum_js__rspack_import_3/* .FieldType.RELATION */.P.RELATION,
3555
+ object: _enums_field_type_enum_js__rspack_import_3/* .FieldType.JSON */.P.JSON,
3556
+ array: _enums_field_type_enum_js__rspack_import_3/* .FieldType.JSON */.P.JSON,
3557
+ choice: _enums_field_type_enum_js__rspack_import_3/* .FieldType.SINGLE_SELECT */.P.SINGLE_SELECT
3392
3558
  };
3393
3559
  const isPlainObject = (v)=>typeof v === 'object' && v !== null && !Array.isArray(v);
3394
3560
  /**
@@ -3407,13 +3573,89 @@ const isPlainObject = (v)=>typeof v === 'object' && v !== null && !Array.isArray
3407
3573
  };
3408
3574
  return checkFields(schema, source, '');
3409
3575
  }
3576
+ const FIELD_NAME = /^[a-zA-Z_][a-zA-Z0-9_]*$/;
3577
+ const OPEN_CONTRACTS = new Set([
3578
+ 'update',
3579
+ 'update_many'
3580
+ ]);
3581
+ const QUERY_CONTRACTS = new Set([
3582
+ 'get_all',
3583
+ 'get_by_id',
3584
+ 'get_by_ids'
3585
+ ]);
3586
+ /**
3587
+ * Checks a request body against a Request step, the way its API controller endpoint's DTO would: a
3588
+ * delete `id` sent as one id is read as a list and a get_by_ids `select` may be comma-separated; `sort`
3589
+ * values must be ASC / DESC and `select` names plain field names. An update / update_many with no
3590
+ * declared fields keeps the rest of the body, with only its `id` checked.
3591
+ */ function validateEndpointInput(endpoint, body) {
3592
+ const { contract } = endpoint;
3593
+ const source = normalizeBody(contract, body);
3594
+ const result = validateFlowInput(endpoint.inputSchema, source, endpoint.bodyType);
3595
+ if (OPEN_CONTRACTS.has(contract) && endpoint.inputSchema.length === (0,_flow_types_js__rspack_import_2/* .contractFields */._h)(contract).length) result.values = keepRest(source, result.values);
3596
+ if (QUERY_CONTRACTS.has(contract) && !Array.isArray(result.values)) result.errors.push(...contractErrors(result.values));
3597
+ return result;
3598
+ }
3599
+ function normalizeBody(contract, body) {
3600
+ if (!isPlainObject(body)) return body;
3601
+ const source = body;
3602
+ if (contract === 'delete' && typeof source['id'] === 'string') return {
3603
+ ...source,
3604
+ id: [
3605
+ source['id']
3606
+ ]
3607
+ };
3608
+ if (contract === 'get_by_ids' && typeof source['select'] === 'string') {
3609
+ return {
3610
+ ...source,
3611
+ select: source['select'].split(',').map((name)=>name.trim()).filter(Boolean)
3612
+ };
3613
+ }
3614
+ return body;
3615
+ }
3616
+ function keepRest(source, values) {
3617
+ if (Array.isArray(values)) return values.map((item, i)=>isPlainObject(item) && Array.isArray(source) && isPlainObject(source[i]) ? {
3618
+ ...source[i],
3619
+ ...item
3620
+ } : item);
3621
+ return isPlainObject(source) ? {
3622
+ ...source,
3623
+ ...values
3624
+ } : values;
3625
+ }
3626
+ function contractErrors(values) {
3627
+ const errors = [];
3628
+ const sort = values['sort'];
3629
+ if (isPlainObject(sort)) {
3630
+ for (const [field, direction] of Object.entries(sort)){
3631
+ if (!FIELD_NAME.test(field) || direction !== 'ASC' && direction !== 'DESC') errors.push({
3632
+ field: `sort.${field}`,
3633
+ ...(0,_flusys_nestjs_shared_utils__rspack_import_0.messageRef)(_config_message_keys_js__rspack_import_4/* .FLOW_MESSAGES.INPUT_SORT_INVALID */.wu.INPUT_SORT_INVALID, {
3634
+ field
3635
+ })
3636
+ });
3637
+ }
3638
+ }
3639
+ const select = values['select'];
3640
+ if (Array.isArray(select)) {
3641
+ select.forEach((field, i)=>{
3642
+ if (typeof field !== 'string' || !FIELD_NAME.test(field)) errors.push({
3643
+ field: `select[${i}]`,
3644
+ ...(0,_flusys_nestjs_shared_utils__rspack_import_0.messageRef)(_config_message_keys_js__rspack_import_4/* .FLOW_MESSAGES.INPUT_SELECT_INVALID */.wu.INPUT_SELECT_INVALID, {
3645
+ field: String(field)
3646
+ })
3647
+ });
3648
+ });
3649
+ }
3650
+ return errors;
3651
+ }
3410
3652
  function checkList(schema, body) {
3411
3653
  if (!Array.isArray(body)) return {
3412
3654
  values: [],
3413
3655
  errors: [
3414
3656
  {
3415
3657
  field: 'body',
3416
- ...(0,_flusys_nestjs_shared_utils__rspack_import_0.messageRef)(_config_message_keys_js__rspack_import_3/* .FLOW_MESSAGES.INPUT_BODY_NOT_LIST */.wu.INPUT_BODY_NOT_LIST)
3658
+ ...(0,_flusys_nestjs_shared_utils__rspack_import_0.messageRef)(_config_message_keys_js__rspack_import_4/* .FLOW_MESSAGES.INPUT_BODY_NOT_LIST */.wu.INPUT_BODY_NOT_LIST)
3417
3659
  }
3418
3660
  ]
3419
3661
  };
@@ -3428,7 +3670,7 @@ function checkList(schema, body) {
3428
3670
  values.push(item);
3429
3671
  errors.push({
3430
3672
  field: `[${i}]`,
3431
- ...(0,_flusys_nestjs_shared_utils__rspack_import_0.messageRef)(_config_message_keys_js__rspack_import_3/* .FLOW_MESSAGES.INPUT_BODY_ITEM_NOT_OBJECT */.wu.INPUT_BODY_ITEM_NOT_OBJECT, {
3673
+ ...(0,_flusys_nestjs_shared_utils__rspack_import_0.messageRef)(_config_message_keys_js__rspack_import_4/* .FLOW_MESSAGES.INPUT_BODY_ITEM_NOT_OBJECT */.wu.INPUT_BODY_ITEM_NOT_OBJECT, {
3432
3674
  index: i + 1
3433
3675
  })
3434
3676
  });
@@ -3448,7 +3690,7 @@ function toValidatable(field, code, label) {
3448
3690
  return {
3449
3691
  code,
3450
3692
  label,
3451
- fieldType: TYPE_MAP[field.type] ?? _enums_field_type_enum_js__rspack_import_2/* .FieldType.TEXT */.P.TEXT,
3693
+ fieldType: TYPE_MAP[field.type] ?? _enums_field_type_enum_js__rspack_import_3/* .FieldType.TEXT */.P.TEXT,
3452
3694
  nullable: !field.required,
3453
3695
  defaultValue: field.default ?? null,
3454
3696
  selectOptions: field.type === 'choice' ? field.options ?? [] : null,
@@ -3503,7 +3745,7 @@ function checkValue(field, supplied, raw, path, label) {
3503
3745
  errors: [
3504
3746
  {
3505
3747
  field: path,
3506
- ...(0,_flusys_nestjs_shared_utils__rspack_import_0.messageRef)(_config_message_keys_js__rspack_import_3/* .FIELD_VALIDATION_MESSAGES.MUST_BE_OBJECT */.KE.MUST_BE_OBJECT, {
3748
+ ...(0,_flusys_nestjs_shared_utils__rspack_import_0.messageRef)(_config_message_keys_js__rspack_import_4/* .FIELD_VALIDATION_MESSAGES.MUST_BE_OBJECT */.KE.MUST_BE_OBJECT, {
3507
3749
  label
3508
3750
  })
3509
3751
  }
@@ -3528,7 +3770,7 @@ function checkValue(field, supplied, raw, path, label) {
3528
3770
  errors: [
3529
3771
  {
3530
3772
  field: path,
3531
- ...(0,_flusys_nestjs_shared_utils__rspack_import_0.messageRef)(_config_message_keys_js__rspack_import_3/* .FIELD_VALIDATION_MESSAGES.MUST_BE_LIST */.KE.MUST_BE_LIST, {
3773
+ ...(0,_flusys_nestjs_shared_utils__rspack_import_0.messageRef)(_config_message_keys_js__rspack_import_4/* .FIELD_VALIDATION_MESSAGES.MUST_BE_LIST */.KE.MUST_BE_LIST, {
3532
3774
  label
3533
3775
  })
3534
3776
  }
@@ -3561,7 +3803,7 @@ function checkValue(field, supplied, raw, path, label) {
3561
3803
  }
3562
3804
 
3563
3805
  __webpack_require__.d(__webpack_exports__, {
3564
- v: () => (validateFlowInput)
3806
+ G: () => (validateEndpointInput)
3565
3807
  });
3566
3808
 
3567
3809
 
@@ -4070,7 +4312,12 @@ class EntityBuilderDataSourceProvider extends _flusys_nestjs_shared_modules__rsp
4070
4312
  // buildMetadatas() is `protected` in TypeORM's public .d.ts (a compile-time-only
4071
4313
  // restriction, not enforced by the JS runtime) but is the only way to rebuild
4072
4314
  // entityMetadatas from options.entities after DataSource.initialize() has run.
4073
- await dataSource.buildMetadatas();
4315
+ try {
4316
+ await dataSource.buildMetadatas();
4317
+ } catch (error) {
4318
+ if (perDsMap.get(schema.options.name) === schema) perDsMap.delete(schema.options.name);
4319
+ throw error;
4320
+ }
4074
4321
  });
4075
4322
  this.statics.rebuildLocks.set(dataSource, next.catch(()=>undefined));
4076
4323
  await next;
@@ -4414,6 +4661,7 @@ function _ts_param(paramIndex, decorator) {
4414
4661
 
4415
4662
  const defaultFlowPermissionCode = (slug)=>`entity_builder.flow.${slug}.execute`;
4416
4663
  /** The rule a `permission` URL asks callers to pass: its own, or the flow's execute permission. */ const flowPermissions = (endpoint, slug)=>endpoint.permissions ?? (0,_flow_engine_flow_permission_logic_js__rspack_import_13/* .permissionAction */.s)(defaultFlowPermissionCode(slug));
4664
+ /** A `permission` URL without a rule of its own: it asks for the flow's execute permission, which publishing registers. */ const asksExecutePermission = (endpoint)=>endpoint.authMode === 'permission' && !endpoint.permissions;
4417
4665
  const flowNotFound = ()=>new _nestjs_common__rspack_import_4.NotFoundException({
4418
4666
  message: 'Flow not found.',
4419
4667
  messageKey: _config_message_keys_js__rspack_import_14/* .FLOW_MESSAGES.NOT_FOUND */.wu.NOT_FOUND
@@ -4722,7 +4970,7 @@ class FlowDefinitionService extends _flusys_nestjs_shared_classes__rspack_import
4722
4970
  } finally{
4723
4971
  await qr.release().catch(()=>undefined);
4724
4972
  }
4725
- await this.clearCaches(existing);
4973
+ await this.invalidateCache();
4726
4974
  await this.provisionPermissions([
4727
4975
  existing
4728
4976
  ]);
@@ -4788,18 +5036,9 @@ class FlowDefinitionService extends _flusys_nestjs_shared_classes__rspack_import
4788
5036
  draft: flow.draft,
4789
5037
  updatedById: user?.id ?? null
4790
5038
  });
4791
- await this.clearCaches(flow);
5039
+ await this.invalidateCache();
4792
5040
  return flow;
4793
5041
  }
4794
- async clearCaches(flow) {
4795
- if (!this.isCacheable) return;
4796
- await Promise.all([
4797
- this.clearCacheForAll(),
4798
- this.clearCacheForId([
4799
- flow
4800
- ])
4801
- ]);
4802
- }
4803
5042
  async beforeDeleteOperation(dto, user, _qr) {
4804
5043
  const ids = Array.isArray(dto.id) ? dto.id : [
4805
5044
  dto.id
@@ -4819,7 +5058,7 @@ class FlowDefinitionService extends _flusys_nestjs_shared_classes__rspack_import
4819
5058
  const actions = new Map();
4820
5059
  for (const flow of flows){
4821
5060
  for (const endpoint of (0,_flow_engine_flow_types_js__rspack_import_11/* .flowEndpoints */.Xr)(flow)){
4822
- if (endpoint.authMode !== 'permission' || endpoint.permissions) continue;
5061
+ if (!asksExecutePermission(endpoint)) continue;
4823
5062
  const code = defaultFlowPermissionCode(flow.slug);
4824
5063
  if (!actions.has(code)) actions.set(code, endpoint.path ? `Run ${flow.name} (${endpoint.path})` : `Run ${flow.name}`);
4825
5064
  }
@@ -4891,6 +5130,7 @@ __webpack_require__.d(__webpack_exports__, {
4891
5130
  Er: defaultFlowPermissionCode,
4892
5131
  HF: isPublished,
4893
5132
  YW: flowNotFound,
5133
+ b$: asksExecutePermission,
4894
5134
  j4: flowPermissions,
4895
5135
  lw: workingCopy
4896
5136
  });
@@ -5187,7 +5427,7 @@ class FlowUrlError extends flow_errors/* .FlowKeyedError */.Hp {
5187
5427
  }
5188
5428
  if (url.protocol !== 'http:' && url.protocol !== 'https:') throw new FlowUrlError(message_keys/* .FLOW_ERROR_MESSAGES.URL_PROTOCOL */.zr.URL_PROTOCOL);
5189
5429
  if (url.username || url.password) throw new FlowUrlError(message_keys/* .FLOW_ERROR_MESSAGES.URL_CREDENTIALS */.zr.URL_CREDENTIALS);
5190
- const host = url.hostname.replace(/^\[|\]$/g, '');
5430
+ const host = url.hostname.replace(/^\[|\]$/g, '').replace(/\.+$/, '');
5191
5431
  if (!allowPrivate) {
5192
5432
  if (host === 'localhost' || host.endsWith('.localhost') || host.endsWith('.internal') || host.endsWith('.local')) {
5193
5433
  throw new FlowUrlError(message_keys/* .FLOW_ERROR_MESSAGES.URL_PRIVATE */.zr.URL_PRIVATE);
@@ -5560,7 +5800,7 @@ class FlowExecutorService {
5560
5800
  }, `Flow '${slug}' has no URL '${endpoint ?? ''}'.`);
5561
5801
  }
5562
5802
  await this.assertMayCall(flow, start, parent, env);
5563
- const checked = (0,flow_input_validator/* .validateFlowInput */.v)(start.inputSchema, input, start.bodyType);
5803
+ const checked = (0,flow_input_validator/* .validateEndpointInput */.G)(start, input);
5564
5804
  if (checked.errors.length) {
5565
5805
  throw new flow_errors/* .FlowHttpError */.hk(400, 'Validation failed', checked.errors, {
5566
5806
  messageKey: message_keys/* .FLOW_MESSAGES.INPUT_INVALID */.wu.INPUT_INVALID,
@@ -5802,16 +6042,17 @@ FlowExecutorService = _ts_decorate([
5802
6042
 
5803
6043
  },
5804
6044
  2300(__unused_rspack_module, __webpack_exports__, __webpack_require__) {
5805
- /* import */ var _nestjs_common__rspack_import_0 = __webpack_require__(6157);
5806
- /* import */ var typeorm__rspack_import_1 = __webpack_require__(7158);
5807
- /* import */ var _config_message_keys_js__rspack_import_9 = __webpack_require__(5732);
5808
- /* import */ var _entities_flow_definition_entity_js__rspack_import_2 = __webpack_require__(3770);
5809
- /* import */ var _flow_engine_flow_input_validator_js__rspack_import_3 = __webpack_require__(6390);
5810
- /* import */ var _flow_engine_flow_types_js__rspack_import_4 = __webpack_require__(6256);
5811
- /* import */ var _entity_builder_datasource_provider_js__rspack_import_5 = __webpack_require__(424);
5812
- /* import */ var _flow_definition_service_js__rspack_import_6 = __webpack_require__(5229);
5813
- /* import */ var _flow_execution_service_js__rspack_import_7 = __webpack_require__(7810);
5814
- /* import */ var _flow_executor_service_js__rspack_import_8 = __webpack_require__(3908);
6045
+ /* import */ var _flusys_nestjs_shared_utils__rspack_import_0 = __webpack_require__(9216);
6046
+ /* import */ var _nestjs_common__rspack_import_1 = __webpack_require__(6157);
6047
+ /* import */ var typeorm__rspack_import_2 = __webpack_require__(7158);
6048
+ /* import */ var _config_message_keys_js__rspack_import_10 = __webpack_require__(5732);
6049
+ /* import */ var _entities_flow_definition_entity_js__rspack_import_3 = __webpack_require__(3770);
6050
+ /* import */ var _flow_engine_flow_input_validator_js__rspack_import_4 = __webpack_require__(6390);
6051
+ /* import */ var _flow_engine_flow_types_js__rspack_import_5 = __webpack_require__(6256);
6052
+ /* import */ var _entity_builder_datasource_provider_js__rspack_import_6 = __webpack_require__(424);
6053
+ /* import */ var _flow_definition_service_js__rspack_import_7 = __webpack_require__(5229);
6054
+ /* import */ var _flow_execution_service_js__rspack_import_8 = __webpack_require__(7810);
6055
+ /* import */ var _flow_executor_service_js__rspack_import_9 = __webpack_require__(3908);
5815
6056
  function _ts_decorate(decorators, target, key, desc) {
5816
6057
  var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
5817
6058
  if (typeof Reflect === "object" && typeof Reflect.decorate === "function") {
@@ -5845,6 +6086,7 @@ function _ts_param(paramIndex, decorator) {
5845
6086
 
5846
6087
 
5847
6088
 
6089
+
5848
6090
  const HIDDEN_HEADERS = new Set([
5849
6091
  'authorization',
5850
6092
  'cookie',
@@ -5908,7 +6150,7 @@ const inputRejected = (errors)=>({
5908
6150
  body: {
5909
6151
  success: false,
5910
6152
  message: 'Validation failed',
5911
- messageKey: _config_message_keys_js__rspack_import_9/* .FLOW_MESSAGES.INPUT_INVALID */.wu.INPUT_INVALID,
6153
+ messageKey: _config_message_keys_js__rspack_import_10/* .FLOW_MESSAGES.INPUT_INVALID */.wu.INPUT_INVALID,
5912
6154
  errors
5913
6155
  },
5914
6156
  trace: [],
@@ -5926,24 +6168,27 @@ class FlowRuntimeService {
5926
6168
  this.definitions = definitions;
5927
6169
  }
5928
6170
  /** A flow never published is not live yet: it looks the same as an unknown one. */ async findActive(slug) {
5929
- return (await this.provider.getRepository(_entities_flow_definition_entity_js__rspack_import_2/* .FlowDefinition */.G)).findOne({
6171
+ return (await this.provider.getRepository(_entities_flow_definition_entity_js__rspack_import_3/* .FlowDefinition */.G)).findOne({
5930
6172
  where: {
5931
6173
  slug,
5932
6174
  isActive: true,
5933
- deletedAt: (0,typeorm__rspack_import_1.IsNull)(),
5934
- version: (0,typeorm__rspack_import_1.MoreThan)(0)
6175
+ deletedAt: (0,typeorm__rspack_import_2.IsNull)(),
6176
+ version: (0,typeorm__rspack_import_2.MoreThan)(0)
5935
6177
  }
5936
6178
  });
5937
6179
  }
6180
+ /** The tenant of this request ('' for a single database): tenant databases cloned from one template share flow ids. */ tenantScope() {
6181
+ return (0,_flusys_nestjs_shared_utils__rspack_import_0.cacheTenantId)(this.provider) ?? '';
6182
+ }
5938
6183
  /** The public entry: a caller hit `POST api-flows/:slug` or `POST api-flows/:slug/:endpoint`. */ async handle(flow, endpoint, body, caller, user) {
5939
6184
  const startedAt = new Date();
5940
- const checked = (0,_flow_engine_flow_input_validator_js__rspack_import_3/* .validateFlowInput */.v)(endpoint.inputSchema, body, endpoint.bodyType);
6185
+ const checked = (0,_flow_engine_flow_input_validator_js__rspack_import_4/* .validateEndpointInput */.G)(endpoint, body);
5941
6186
  if (checked.errors.length) {
5942
6187
  const result = inputRejected(checked.errors);
5943
6188
  await this.logWebhook(flow, endpoint, body, result, startedAt, user, caller);
5944
6189
  return result;
5945
6190
  }
5946
- const result = await this.executor.run((0,_flow_definition_service_js__rspack_import_6/* .toFlowSettings */.RK)(flow), {
6191
+ const result = await this.executor.run((0,_flow_definition_service_js__rspack_import_7/* .toFlowSettings */.RK)(flow), {
5947
6192
  mode: 'live',
5948
6193
  user,
5949
6194
  input: checked.values,
@@ -5963,17 +6208,17 @@ class FlowRuntimeService {
5963
6208
  }
5964
6209
  if (dto.definition) {
5965
6210
  // The designer's current draft, even for a saved flow: `flow` above is kept only to attribute the run.
5966
- settings = (0,_flow_definition_service_js__rspack_import_6/* .toFlowSettings */.RK)(dto.definition);
6211
+ settings = (0,_flow_definition_service_js__rspack_import_7/* .toFlowSettings */.RK)(dto.definition);
5967
6212
  } else if (flow) {
5968
- settings = (0,_flow_definition_service_js__rspack_import_6/* .toFlowSettings */.RK)(flow);
6213
+ settings = (0,_flow_definition_service_js__rspack_import_7/* .toFlowSettings */.RK)(flow);
5969
6214
  } else {
5970
- throw new _nestjs_common__rspack_import_0.BadRequestException({
6215
+ throw new _nestjs_common__rspack_import_1.BadRequestException({
5971
6216
  message: 'Send a flowId or a definition to test.',
5972
- messageKey: _config_message_keys_js__rspack_import_9/* .FLOW_MESSAGES.NOTHING_TO_TEST */.wu.NOTHING_TO_TEST
6217
+ messageKey: _config_message_keys_js__rspack_import_10/* .FLOW_MESSAGES.NOTHING_TO_TEST */.wu.NOTHING_TO_TEST
5973
6218
  });
5974
6219
  }
5975
6220
  const mode = dto.mode ?? 'dry_run';
5976
- const endpoint = (0,_flow_engine_flow_types_js__rspack_import_4/* .findEndpoint */.vS)(settings, dto.endpoint);
6221
+ const endpoint = (0,_flow_engine_flow_types_js__rspack_import_5/* .findEndpoint */.vS)(settings, dto.endpoint);
5977
6222
  // Checked before anything runs.
5978
6223
  await this.definitions.assertMayTest(settings, endpoint, user, {
5979
6224
  mode,
@@ -5981,9 +6226,9 @@ class FlowRuntimeService {
5981
6226
  saved: !!flow
5982
6227
  });
5983
6228
  if (!endpoint) {
5984
- throw new _nestjs_common__rspack_import_0.BadRequestException({
6229
+ throw new _nestjs_common__rspack_import_1.BadRequestException({
5985
6230
  message: `This flow has no URL '${dto.endpoint ?? ''}'.`,
5986
- messageKey: _config_message_keys_js__rspack_import_9/* .FLOW_MESSAGES.ENDPOINT_NOT_FOUND */.wu.ENDPOINT_NOT_FOUND,
6231
+ messageKey: _config_message_keys_js__rspack_import_10/* .FLOW_MESSAGES.ENDPOINT_NOT_FOUND */.wu.ENDPOINT_NOT_FOUND,
5987
6232
  messageVariables: {
5988
6233
  endpoint: dto.endpoint ?? ''
5989
6234
  }
@@ -5991,9 +6236,9 @@ class FlowRuntimeService {
5991
6236
  }
5992
6237
  const validation = await this.definitions.validate(settings);
5993
6238
  if (validation.errors.length) {
5994
- throw new _nestjs_common__rspack_import_0.BadRequestException({
6239
+ throw new _nestjs_common__rspack_import_1.BadRequestException({
5995
6240
  message: 'The flow has problems.',
5996
- messageKey: _config_message_keys_js__rspack_import_9/* .FLOW_MESSAGES.INVALID */.wu.INVALID,
6241
+ messageKey: _config_message_keys_js__rspack_import_10/* .FLOW_MESSAGES.INVALID */.wu.INVALID,
5997
6242
  errors: validation.errors.map((error)=>({
5998
6243
  field: 'flow',
5999
6244
  ...error
@@ -6003,7 +6248,7 @@ class FlowRuntimeService {
6003
6248
  const startedAt = new Date();
6004
6249
  const simulated = simulatedCaller(caller, dto.request);
6005
6250
  const body = dto.input ?? (endpoint.bodyType === 'list' ? [] : {});
6006
- const checked = (0,_flow_engine_flow_input_validator_js__rspack_import_3/* .validateFlowInput */.v)(endpoint.inputSchema, body, endpoint.bodyType);
6251
+ const checked = (0,_flow_engine_flow_input_validator_js__rspack_import_4/* .validateEndpointInput */.G)(endpoint, body);
6007
6252
  const result = checked.errors.length ? inputRejected(checked.errors) : await this.executor.run(settings, {
6008
6253
  mode,
6009
6254
  user,
@@ -6050,19 +6295,19 @@ class FlowRuntimeService {
6050
6295
  }
6051
6296
  }
6052
6297
  FlowRuntimeService = _ts_decorate([
6053
- (0,_nestjs_common__rspack_import_0.Injectable)({
6054
- scope: _nestjs_common__rspack_import_0.Scope.REQUEST
6298
+ (0,_nestjs_common__rspack_import_1.Injectable)({
6299
+ scope: _nestjs_common__rspack_import_1.Scope.REQUEST
6055
6300
  }),
6056
- _ts_param(0, (0,_nestjs_common__rspack_import_0.Inject)(_entity_builder_datasource_provider_js__rspack_import_5/* .EntityBuilderDataSourceProvider */.D)),
6057
- _ts_param(1, (0,_nestjs_common__rspack_import_0.Inject)(_flow_executor_service_js__rspack_import_8/* .FlowExecutorService */.F)),
6058
- _ts_param(2, (0,_nestjs_common__rspack_import_0.Inject)(_flow_execution_service_js__rspack_import_7/* .FlowExecutionService */.A)),
6059
- _ts_param(3, (0,_nestjs_common__rspack_import_0.Inject)(_flow_definition_service_js__rspack_import_6/* .FlowDefinitionService */.Tk)),
6301
+ _ts_param(0, (0,_nestjs_common__rspack_import_1.Inject)(_entity_builder_datasource_provider_js__rspack_import_6/* .EntityBuilderDataSourceProvider */.D)),
6302
+ _ts_param(1, (0,_nestjs_common__rspack_import_1.Inject)(_flow_executor_service_js__rspack_import_9/* .FlowExecutorService */.F)),
6303
+ _ts_param(2, (0,_nestjs_common__rspack_import_1.Inject)(_flow_execution_service_js__rspack_import_8/* .FlowExecutionService */.A)),
6304
+ _ts_param(3, (0,_nestjs_common__rspack_import_1.Inject)(_flow_definition_service_js__rspack_import_7/* .FlowDefinitionService */.Tk)),
6060
6305
  _ts_metadata("design:type", Function),
6061
6306
  _ts_metadata("design:paramtypes", [
6062
- typeof _entity_builder_datasource_provider_js__rspack_import_5/* .EntityBuilderDataSourceProvider */.D === "undefined" ? Object : _entity_builder_datasource_provider_js__rspack_import_5/* .EntityBuilderDataSourceProvider */.D,
6063
- typeof _flow_executor_service_js__rspack_import_8/* .FlowExecutorService */.F === "undefined" ? Object : _flow_executor_service_js__rspack_import_8/* .FlowExecutorService */.F,
6064
- typeof _flow_execution_service_js__rspack_import_7/* .FlowExecutionService */.A === "undefined" ? Object : _flow_execution_service_js__rspack_import_7/* .FlowExecutionService */.A,
6065
- typeof _flow_definition_service_js__rspack_import_6/* .FlowDefinitionService */.Tk === "undefined" ? Object : _flow_definition_service_js__rspack_import_6/* .FlowDefinitionService */.Tk
6307
+ typeof _entity_builder_datasource_provider_js__rspack_import_6/* .EntityBuilderDataSourceProvider */.D === "undefined" ? Object : _entity_builder_datasource_provider_js__rspack_import_6/* .EntityBuilderDataSourceProvider */.D,
6308
+ typeof _flow_executor_service_js__rspack_import_9/* .FlowExecutorService */.F === "undefined" ? Object : _flow_executor_service_js__rspack_import_9/* .FlowExecutorService */.F,
6309
+ typeof _flow_execution_service_js__rspack_import_8/* .FlowExecutionService */.A === "undefined" ? Object : _flow_execution_service_js__rspack_import_8/* .FlowExecutionService */.A,
6310
+ typeof _flow_definition_service_js__rspack_import_7/* .FlowDefinitionService */.Tk === "undefined" ? Object : _flow_definition_service_js__rspack_import_7/* .FlowDefinitionService */.Tk
6066
6311
  ])
6067
6312
  ], FlowRuntimeService);
6068
6313
 
@@ -6508,6 +6753,7 @@ const TEXT_FIELD_TYPES = new Set([
6508
6753
  async getAll(search, query, options = {}) {
6509
6754
  const repository = await this.repositoryFor(options.queryRunner);
6510
6755
  let builder = repository.createQueryBuilder(this.entityCode);
6756
+ if (query.withDeleted) builder.withDeleted();
6511
6757
  if (query.filter) {
6512
6758
  builder = this.applyFilter(builder, query.filter);
6513
6759
  }
@@ -6950,6 +7196,23 @@ class SchemaDialectAdapterService {
6950
7196
  });
6951
7197
  }
6952
7198
  }
7199
+ /** Takes `lockKey` for the connection's session (it outlives any transaction) without waiting: false when another session holds it. */ async tryAcquireSessionLock(queryRunner, lockKey, driver) {
7200
+ const rows = driver === 'postgres' ? await queryRunner.query('SELECT pg_try_advisory_lock(hashtext($1)) AS acquired', [
7201
+ lockKey
7202
+ ]) : await queryRunner.query('SELECT GET_LOCK(?, 0) AS acquired', [
7203
+ this.lockName(lockKey)
7204
+ ]);
7205
+ const acquired = rows?.[0]?.acquired;
7206
+ return acquired === true || Number(acquired) === 1;
7207
+ }
7208
+ /** Must run before the connection goes back to the pool, or the lock stays with it. */ async releaseSessionLock(queryRunner, lockKey, driver) {
7209
+ if (driver === 'postgres') await queryRunner.query('SELECT pg_advisory_unlock(hashtext($1))', [
7210
+ lockKey
7211
+ ]);
7212
+ else await queryRunner.query('SELECT RELEASE_LOCK(?)', [
7213
+ this.lockName(lockKey)
7214
+ ]);
7215
+ }
6953
7216
  async releaseEntityLock(queryRunner, lockKey, driver) {
6954
7217
  if (driver === 'postgres') {
6955
7218
  // pg_advisory_xact_lock is transaction-scoped - released automatically on commit/rollback.
@@ -7030,7 +7293,10 @@ class SchemaRegistryService {
7030
7293
  id: entityDefinitionId
7031
7294
  }
7032
7295
  });
7296
+ const cacheKey = this.cacheKey(entityDefinitionId);
7033
7297
  if (!definition) {
7298
+ // Dropped, possibly on another instance: its schema is never asked for again.
7299
+ SchemaRegistryService.cache.delete(cacheKey);
7034
7300
  throw new _nestjs_common__rspack_import_1.NotFoundException({
7035
7301
  message: `Entity definition '${entityDefinitionId}' not found.`,
7036
7302
  messageKey: _config_message_keys_js__rspack_import_8/* .ENTITY_DEFINITION_MESSAGES.NOT_FOUND */.pL.NOT_FOUND,
@@ -7039,7 +7305,6 @@ class SchemaRegistryService {
7039
7305
  }
7040
7306
  });
7041
7307
  }
7042
- const cacheKey = this.cacheKey(entityDefinitionId);
7043
7308
  const cached = SchemaRegistryService.cache.get(cacheKey);
7044
7309
  if (cached && cached.version === definition.schemaVersion) {
7045
7310
  return cached.schema;