@flusys/nestjs-entity-builder 9.1.0 → 9.1.1

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 (61) hide show
  1. package/README.md +52 -25
  2. package/config/entity-builder.constants.d.ts +2 -0
  3. package/config/message-keys.d.ts +93 -12
  4. package/controllers/definition-bundle.controller.d.ts +15 -0
  5. package/controllers/flow-definition.controller.d.ts +7 -1
  6. package/controllers/index.d.ts +1 -1
  7. package/dtos/definition-bundle.dto.d.ts +57 -0
  8. package/dtos/flow.dto.d.ts +12 -20
  9. package/dtos/index.d.ts +1 -0
  10. package/entities/flow-definition.entity.d.ts +3 -9
  11. package/entities/flow-execution.entity.d.ts +3 -0
  12. package/entities/flow-version.entity.d.ts +10 -0
  13. package/entities/index.d.ts +3 -3
  14. package/fesm/21.js +96 -14
  15. package/fesm/458.js +695 -0
  16. package/fesm/{489.js → 582.js} +22 -30
  17. package/fesm/{996.js → 677.js} +1453 -1078
  18. package/fesm/794.js +135 -240
  19. package/fesm/{719.js → 870.js} +1955 -220
  20. package/fesm/{362.js → 897.js} +566 -2
  21. package/fesm/{606.js → 946.js} +564 -290
  22. package/fesm/config/index.js +15 -2
  23. package/fesm/controllers/index.js +9 -9
  24. package/fesm/docs/index.js +7 -4
  25. package/fesm/dtos/index.js +451 -256
  26. package/fesm/entities/index.js +58 -3
  27. package/fesm/guards/index.js +101 -22
  28. package/fesm/index.js +429 -948
  29. package/fesm/interfaces/index.js +19 -2
  30. package/fesm/modules/index.js +10 -628
  31. package/fesm/rule-engine/index.js +12 -3
  32. package/fesm/services/index.js +97 -27
  33. package/flow-engine/flow-api-key.d.ts +3 -7
  34. package/flow-engine/flow-graph.validator.d.ts +4 -2
  35. package/flow-engine/flow-permission-logic.d.ts +2 -0
  36. package/flow-engine/flow-run.types.d.ts +17 -8
  37. package/flow-engine/flow.types.d.ts +101 -13
  38. package/guards/designer-writable.interceptor.d.ts +12 -0
  39. package/guards/index.d.ts +1 -0
  40. package/interfaces/definition-bundle.interface.d.ts +111 -0
  41. package/interfaces/entity-builder-module.interface.d.ts +6 -0
  42. package/interfaces/flow-definition.interface.d.ts +13 -9
  43. package/interfaces/index.d.ts +1 -0
  44. package/package.json +5 -5
  45. package/rule-engine/rule-engine.service.d.ts +1 -0
  46. package/rule-engine/rule-permission.d.ts +3 -7
  47. package/services/definition-bundle.diff.d.ts +62 -0
  48. package/services/definition-bundle.file.d.ts +7 -0
  49. package/services/definition-bundle.service.d.ts +32 -0
  50. package/services/entity-builder-config.service.d.ts +2 -0
  51. package/services/entity-flow-scaffold.d.ts +1 -1
  52. package/services/flow-definition.service.d.ts +25 -12
  53. package/services/flow-execution.service.d.ts +7 -0
  54. package/services/flow-executor.service.d.ts +8 -8
  55. package/services/generic-entity.service.d.ts +1 -1
  56. package/services/index.d.ts +3 -1
  57. package/services/schema-evolution.service.d.ts +3 -1
  58. package/services/schema-sync.service.d.ts +5 -1
  59. package/controllers/flow-api-key.controller.d.ts +0 -14
  60. package/entities/flow-api-key.entity.d.ts +0 -10
  61. package/services/flow-api-key.service.d.ts +0 -31
@@ -7,41 +7,11 @@ import * as __rspack_external_node_os_4f3c9d58 from "node:os";
7
7
  import * as __rspack_external_node_path_806ed179 from "node:path";
8
8
  import * as __rspack_external_node_url_3991086a from "node:url";
9
9
  import * as __rspack_external_node_worker_threads_28ca7740 from "node:worker_threads";
10
+ import * as __rspack_external__flusys_nestjs_shared_exceptions_3edf22e9 from "@flusys/nestjs-shared/exceptions";
10
11
  import * as __rspack_external_node_dns_c6249ebb from "node:dns";
11
- export const __rspack_esm_id = 996;
12
- export const __rspack_esm_ids = [996];
12
+ export const __rspack_esm_id = 677;
13
+ export const __rspack_esm_ids = [677];
13
14
  export const __webpack_modules__ = {
14
- 6576(__unused_rspack_module, __webpack_exports__, __webpack_require__) {
15
- const ENTITY_BUILDER_MODULE_OPTIONS = 'ENTITY_BUILDER_MODULE_OPTIONS';
16
- /**
17
- * Identifier whitelist enforced on every EntityDefinition/FieldDefinition `code`.
18
- * Also the exact contract SchemaSyncService's DDL engine validates against
19
- * before generating a table/column name.
20
- */ const ENTITY_CODE_PATTERN = /^[a-z][a-z0-9_]{2,59}$/;
21
- /** Matches the `code` column length on both EntityDefinition and FieldDefinition. */ const ENTITY_CODE_MAX_LENGTH = 63;
22
- /** Defense-in-depth prefix applied to every dynamically created table name. */ const ENTITY_TABLE_PREFIX = 'eb_';
23
- /** The generic API controller's record endpoints - each can be created as a ready-made flow with the entity. */ const ENTITY_FLOW_ENDPOINTS = [
24
- 'insert',
25
- 'insertMany',
26
- 'getById',
27
- 'getByIds',
28
- 'getAll',
29
- 'getByFilter',
30
- 'update',
31
- 'updateMany',
32
- 'bulkUpsert',
33
- 'delete'
34
- ];
35
-
36
- __webpack_require__.d(__webpack_exports__, {
37
- }, {
38
- CP: ENTITY_BUILDER_MODULE_OPTIONS,
39
- WT: ENTITY_FLOW_ENDPOINTS,
40
- vs: ENTITY_CODE_PATTERN
41
- });
42
-
43
-
44
- },
45
15
  982(__unused_rspack_module, __webpack_exports__, __webpack_require__) {
46
16
  /** Every entity the entity builder module publishes under */ const ENTITY_BUILDER_EVENT_ENTITIES = {
47
17
  ENTITY_DEFINITION: 'entity_definition',
@@ -59,6 +29,60 @@ __webpack_require__.d(__webpack_exports__, {
59
29
  });
60
30
 
61
31
 
32
+ },
33
+ 4816(__unused_rspack_module, __webpack_exports__, __webpack_require__) {
34
+ /* import */ var node_crypto__rspack_import_0 = __webpack_require__(2561);
35
+ /* import */ var _flow_types_js__rspack_import_1 = __webpack_require__(6256);
36
+
37
+
38
+ const API_KEY_PREFIX_LENGTH = 11; // "fk_" + 8 hex
39
+ const hashApiKey = (key)=>(0,node_crypto__rspack_import_0.createHash)('sha256').update(key).digest('hex');
40
+ const apiKeyPrefix = (key)=>key.slice(0, API_KEY_PREFIX_LENGTH);
41
+ /** A key as the designer makes it: `fk_<8 hex>_<32 hex>`. */ function looksLikeApiKey(key) {
42
+ return typeof key === 'string' && /^fk_[0-9a-f]{8}_[0-9a-f]{32}$/.test(key);
43
+ }
44
+ /** Constant-time comparison of a presented key against a stored hash. */ function apiKeyMatches(presented, storedHash) {
45
+ const a = Buffer.from(hashApiKey(presented), 'hex');
46
+ const b = Buffer.from(storedHash, 'hex');
47
+ return a.length === b.length && (0,node_crypto__rspack_import_0.timingSafeEqual)(a, b);
48
+ }
49
+ /** Whether `presented` is the key a Request step stores. */ const apiKeyOpens = (stored, presented)=>!!stored && looksLikeApiKey(presented) && apiKeyMatches(presented, stored.hash);
50
+ /**
51
+ * What is saved of the Request steps' API keys: a plain key the designer made becomes its prefix and
52
+ * hash, so the key itself is never stored; a step without `api_key` access keeps no key at all.
53
+ */ function sealApiKeys(nodes) {
54
+ return nodes.map((node)=>{
55
+ if (node.type !== 'trigger' || node.config?.['apiKey'] === undefined) return node;
56
+ const { apiKey, ...config } = node.config;
57
+ if (config['authMode'] !== 'api_key') return {
58
+ ...node,
59
+ config
60
+ };
61
+ if (looksLikeApiKey(apiKey)) return {
62
+ ...node,
63
+ config: {
64
+ ...config,
65
+ apiKey: {
66
+ prefix: apiKeyPrefix(apiKey),
67
+ hash: hashApiKey(apiKey)
68
+ }
69
+ }
70
+ };
71
+ return (0,_flow_types_js__rspack_import_1/* .isStoredApiKey */.xA)(apiKey) ? node : {
72
+ ...node,
73
+ config
74
+ };
75
+ });
76
+ }
77
+
78
+ __webpack_require__.d(__webpack_exports__, {
79
+ Jb: () => (looksLikeApiKey),
80
+ ue: () => (sealApiKeys)
81
+ }, {
82
+ NT: apiKeyOpens
83
+ });
84
+
85
+
62
86
  },
63
87
  8333(__unused_rspack_module, __webpack_exports__, __webpack_require__) {
64
88
 
@@ -80,8 +104,6 @@ var message_keys = __webpack_require__(5732);
80
104
  var flow_errors = __webpack_require__(8542);
81
105
  // EXTERNAL MODULE: ./projects/nestjs-entity-builder/src/flow-engine/flow.types.ts
82
106
  var flow_types = __webpack_require__(6256);
83
- // EXTERNAL MODULE: external "@flusys/nestjs-shared/constants"
84
- var constants_ = __webpack_require__(9140);
85
107
  // EXTERNAL MODULE: ./projects/nestjs-entity-builder/src/interfaces/generic-record.interface.ts
86
108
  var generic_record_interface = __webpack_require__(3372);
87
109
  ;// CONCATENATED MODULE: external "node:os"
@@ -302,7 +324,7 @@ const timeoutError = (job)=>new flow_errors/* .FlowCodeError */.yL(message_keys/
302
324
  const unavailableError = (node, detail)=>new flow_errors/* .FlowCodeError */.yL(message_keys/* .FLOW_ERROR_MESSAGES.CODE_UNAVAILABLE */.zr.CODE_UNAVAILABLE, {
303
325
  node
304
326
  }, `The code sandbox could not run the code: ${detail}`);
305
- let CodeSandboxPool = class CodeSandboxPool {
327
+ let flow_code_sandbox_CodeSandboxPool = class CodeSandboxPool {
306
328
  idle = [];
307
329
  running = new Map();
308
330
  queue = [];
@@ -432,14 +454,12 @@ let pool;
432
454
  detail: error.message
433
455
  }, error.message);
434
456
  }
435
- pool ??= new CodeSandboxPool();
457
+ pool ??= new flow_code_sandbox_CodeSandboxPool();
436
458
  return pool.run(code, input, timeoutMs, node);
437
459
  }
438
460
 
439
461
  // EXTERNAL MODULE: ./projects/nestjs-entity-builder/src/flow-engine/flow-graph.validator.ts
440
462
  var flow_graph_validator = __webpack_require__(3186);
441
- // EXTERNAL MODULE: ./projects/nestjs-entity-builder/src/rule-engine/rule-permission.ts
442
- var rule_permission = __webpack_require__(5191);
443
463
  // EXTERNAL MODULE: ./projects/nestjs-entity-builder/src/rule-engine/rule-values.ts
444
464
  var rule_values = __webpack_require__(6622);
445
465
  ;// CONCATENATED MODULE: ./projects/nestjs-entity-builder/src/flow-engine/flow-node-handlers.ts
@@ -452,15 +472,7 @@ var rule_values = __webpack_require__(6622);
452
472
 
453
473
 
454
474
 
455
-
456
-
457
- const PERMISSION_ACTION = {
458
- insert: 'create',
459
- update: 'update',
460
- delete: 'delete',
461
- read: 'read'
462
- };
463
- /** With `env`, `lookup` expressions read through the flow (permission check, transaction). */ function evaluationContext(state, env) {
475
+ /** With `env`, `lookup` expressions read through the flow (its transaction). */ function evaluationContext(state, env) {
464
476
  const base = {
465
477
  input: state.input,
466
478
  vars: state.vars,
@@ -478,7 +490,6 @@ const PERMISSION_ACTION = {
478
490
  function lookupResolver(env, state) {
479
491
  return {
480
492
  find: async (entityCode, query)=>{
481
- await authorize(env, state, entityCode, 'read');
482
493
  const service = await env.entityService(entityCode, state.qr);
483
494
  const { data, total } = await service.getAll(undefined, {
484
495
  filter: query.filter,
@@ -556,7 +567,7 @@ async function runValidate(cfg, env, state) {
556
567
  passed: true
557
568
  }
558
569
  };
559
- const status = cfg.statusCode ?? (failed.every((check)=>(0,flow_graph_validator/* .usesPermissionFunction */.u)(check.rule)) ? 403 : 400);
570
+ const status = cfg.statusCode ?? (failed.every((check)=>(0,flow_graph_validator/* .usesPermissionFunction */.u9)(check.rule)) ? 403 : 400);
560
571
  const errors = failed.map((check)=>({
561
572
  field: check.field || check.id,
562
573
  message: check.message
@@ -569,21 +580,17 @@ async function runValidate(cfg, env, state) {
569
580
  }
570
581
  });
571
582
  }
572
- /** `{ allowed, missing }` - `missing` lists the codes the caller does not hold. */ async function runPermissionCheck(cfg, env, state) {
573
- const codes = (0,rule_permission/* .parsePermissionCodes */.PS)(cfg.codes);
574
- let branch;
575
- if (cfg.branch === null) branch = null;
576
- else if (cfg.branch !== undefined) branch = (0,rule_permission/* .toBranchArgument */.lH)([
577
- await env.ruleEngine.resolveExpression(cfg.branch, evaluationContext(state, env))
578
- ], 0);
583
+ /** `{ allowed, missing }` - whether the caller passes the AND / OR rule; `missing` lists the codes it names that the caller does not hold. */ async function runPermissionCheck(cfg, env, state) {
579
584
  const permissions = state.permissions ?? env.ruleEngine.createPermissionResolver(state.user);
580
- const granted = state.user ? await permissions.granted(branch) : [];
581
- const missing = codes.filter((code)=>!(0,utils_.matchesPermission)(granted, code));
582
- const allowed = codes.length > 0 && (cfg.match === 'all' ? missing.length === 0 : missing.length < codes.length);
583
- const output = {
585
+ const granted = state.user ? await permissions.granted(undefined) : [];
586
+ const missing = (0,utils_.permissionLogicCodes)(cfg.permissions).filter((code)=>!(0,utils_.matchesPermission)(granted, code));
587
+ const allowed = (0,utils_.evaluatePermissionLogic)(cfg.permissions, granted).passed;
588
+ return deniedResult(cfg, {
584
589
  allowed,
585
590
  missing
586
- };
591
+ }, allowed);
592
+ }
593
+ /** A denied check: `onDenied: branch` follows the `denied` port, otherwise the run is answered `statusCode` (403). */ function deniedResult(cfg, output, allowed) {
587
594
  if (cfg.onDenied === 'branch') return {
588
595
  output,
589
596
  port: allowed ? 'allowed' : 'denied'
@@ -599,6 +606,68 @@ async function runValidate(cfg, env, state) {
599
606
  output
600
607
  };
601
608
  }
609
+ /** One id, a list of ids, or nothing (undefined / null / empty text), each id once. */ function idList(value) {
610
+ const items = Array.isArray(value) ? value : [
611
+ value
612
+ ];
613
+ return [
614
+ ...new Set(items.filter((item)=>item !== undefined && item !== null && item !== '').map(String))
615
+ ];
616
+ }
617
+ /** One id from an expression's value; anything but non-empty text is none. */ function oneId(value) {
618
+ return typeof value === 'string' && value ? value : null;
619
+ }
620
+ /**
621
+ * `{ userId, currentCompanyId, currentBranchId, companies, branches }` - what the user (the signed-in
622
+ * caller, or the one `userId` names) may work in; none without a user or the company feature. With
623
+ * `companyIds` / `branchIds` it also answers `allowed` and `missing`: every id asked for must be within
624
+ * reach (`branchMatch`, among the branches of `branchCompany`), and a target that resolves to nothing
625
+ * is a denial, never a pass.
626
+ */ async function runCompanyBranchCheck(cfg, env, state) {
627
+ const ctx = evaluationContext(state, env);
628
+ const resolve = async (expression)=>expression === undefined ? undefined : env.ruleEngine.resolveExpression(expression, ctx);
629
+ const asCaller = cfg.subject !== 'user';
630
+ const userId = asCaller ? state.user?.id ?? null : oneId(await resolve(cfg.userId));
631
+ const { companies, branches } = userId ? await env.companyAccess(userId) : {
632
+ companies: [],
633
+ branches: []
634
+ };
635
+ const currentCompanyId = asCaller ? state.user?.companyId ?? null : null;
636
+ const currentBranchId = asCaller ? state.user?.branchId ?? null : null;
637
+ const output = {
638
+ userId,
639
+ currentCompanyId,
640
+ currentBranchId,
641
+ companies,
642
+ branches
643
+ };
644
+ if (cfg.companyIds === undefined && cfg.branchIds === undefined) return {
645
+ output
646
+ };
647
+ const wanted = async (expression)=>expression === undefined ? null : idList(await resolve(expression));
648
+ const wantedCompanies = await wanted(cfg.companyIds);
649
+ const wantedBranches = await wanted(cfg.branchIds);
650
+ const granted = branches.filter((branch)=>cfg.branchCompany !== 'current' || branch.companyId === currentCompanyId).map((branch)=>branch.id);
651
+ const reachable = await reachableBranches(cfg.branchMatch ?? 'direct', granted, wantedBranches, env);
652
+ const missing = {
653
+ companyIds: (wantedCompanies ?? []).filter((id)=>!companies.some((company)=>company.id === id)),
654
+ branchIds: (wantedBranches ?? []).filter((id)=>!reachable.has(id))
655
+ };
656
+ const nothingToCheck = wantedCompanies?.length === 0 || wantedBranches?.length === 0;
657
+ const allowed = !nothingToCheck && !missing.companyIds.length && !missing.branchIds.length;
658
+ return deniedResult(cfg, {
659
+ ...output,
660
+ allowed,
661
+ missing
662
+ }, allowed);
663
+ }
664
+ /** The branches a check passes: the granted ones (direct), and with `within` every branch under them too. */ async function reachableBranches(match, granted, wanted, env) {
665
+ if (match !== 'within' || !wanted?.length || !granted.length || !env.branchTree || wanted.every((id)=>granted.includes(id))) return new Set(granted);
666
+ return new Set([
667
+ ...granted,
668
+ ...await env.branchTree(granted)
669
+ ]);
670
+ }
602
671
  const USES_PERMISSIONS = /\b(permissions|hasPermission|hasAnyPermission|hasAllPermissions)\b/;
603
672
  /** The caller as a code step sees it; permission codes (current branch) are fetched only for code that reads them. */ async function codeUser(code, state) {
604
673
  const user = state.user;
@@ -641,16 +710,6 @@ function asId(value) {
641
710
  });
642
711
  return value;
643
712
  }
644
- /** Fails closed: the caller needs the entity permission; the system only when that was granted for this run; anything else is refused. */ async function authorize(env, state, entityCode, action) {
645
- if (state.runAs === 'caller') return env.assertPermission((0,constants_.dynamicEntityPermission)(entityCode, PERMISSION_ACTION[action]), state.user);
646
- if (state.runAs === 'system' && state.systemAuthorized === true) return;
647
- throw new flow_errors/* .FlowHttpError */.hk(403, 'This flow is not allowed to read or write records as the system.', undefined, {
648
- messageKey: message_keys/* .FLOW_ERROR_MESSAGES.RUN_AS_NOT_ALLOWED */.zr.RUN_AS_NOT_ALLOWED,
649
- messageVariables: {
650
- runAs: String(state.runAs)
651
- }
652
- });
653
- }
654
713
  function writeOptions(state) {
655
714
  return state.qr ? {
656
715
  queryRunner: state.qr,
@@ -727,15 +786,16 @@ async function updateRecord(entityCode, service, values, state) {
727
786
  }
728
787
  /** @returns false when the record does not exist and `skip` allows that. */ async function deleteRecord(entityCode, service, id, skipMissing, state) {
729
788
  countWrite(state);
789
+ let action;
730
790
  try {
731
- await service.delete(id, state.user, writeOptions(state));
791
+ action = await service.delete(id, state.user, writeOptions(state));
732
792
  } catch (error) {
733
793
  if (skipMissing && error instanceof common_.NotFoundException) return false;
734
794
  throw error;
735
795
  }
736
796
  if (state.qr) state.pendingEvents.push({
737
797
  entityCode,
738
- action: 'deleted',
798
+ action,
739
799
  row: {
740
800
  id
741
801
  }
@@ -780,8 +840,6 @@ async function insertChildren(node, child, parentId, env, state) {
780
840
  * A write of several records (many, or any children) is all-or-nothing.
781
841
  */ async function runInsert(node, cfg, env, state) {
782
842
  const children = cfg.children ?? [];
783
- await authorize(env, state, cfg.entityCode, 'insert');
784
- for (const child of children)await authorize(env, state, child.entityCode, 'insert');
785
843
  const service = await env.entityService(cfg.entityCode, state.qr);
786
844
  const insertOne = async ()=>{
787
845
  const saved = await insertRecord(cfg.entityCode, service, await mapFieldValues(cfg.fields, env, state), state);
@@ -835,8 +893,6 @@ async function insertChildren(node, child, parentId, env, state) {
835
893
  }
836
894
  /** one: the saved record (null when skipped). many / filter: `{ items, count, updated, created, skipped }`, all-or-nothing. */ async function runUpdate(node, cfg, env, state) {
837
895
  const target = writeTarget(cfg);
838
- await authorize(env, state, cfg.entityCode, 'update');
839
- if (target !== 'filter' && cfg.onNotFound === 'insert') await authorize(env, state, cfg.entityCode, 'insert');
840
896
  const service = await env.entityService(cfg.entityCode, state.qr);
841
897
  if (target === 'one') return {
842
898
  output: (await updateOrCreate(cfg, service, undefined, env, state)).row
@@ -867,7 +923,6 @@ async function insertChildren(node, child, parentId, env, state) {
867
923
  /** one: `{ id, deleted }`. many / filter: `{ ids, count, skipped }`, all-or-nothing; an id listed twice is deleted once. */ async function runDelete(node, cfg, env, state) {
868
924
  const target = writeTarget(cfg);
869
925
  const skipMissing = cfg.onNotFound === 'skip';
870
- await authorize(env, state, cfg.entityCode, 'delete');
871
926
  const service = await env.entityService(cfg.entityCode, state.qr);
872
927
  const idOf = async (item)=>asId(cfg.id === undefined ? itemId(item) : await env.ruleEngine.resolveExpression(cfg.id, evaluationContext(state, env)));
873
928
  if (target === 'one') {
@@ -897,6 +952,377 @@ async function insertChildren(node, child, parentId, env, state) {
897
952
  })
898
953
  };
899
954
  }
955
+ /** As strict as the email module's own check, and without the characters that would break an address header. */ const EMAIL_PATTERN = /^[^\s@,;<>"]+@[^\s@,;<>"]+\.[^\s@,;<>"]+$/;
956
+ const MAX_EMAIL_LENGTH = 254;
957
+ const shown = (value)=>String(typeof value === 'object' && value !== null ? JSON.stringify(value) : value).slice(0, 100);
958
+ /** A value as message text: nothing for null / undefined, JSON for an object. */ function asText(value) {
959
+ if (value === null || value === undefined) return '';
960
+ return typeof value === 'object' ? JSON.stringify(value) : String(value);
961
+ }
962
+ /** Reserves `count` sends of the run's budget before any goes out, so a step never sends only some of its messages because of the limit. */ function countMessages(state, count) {
963
+ if (state.messages + count > flow_types/* .FLOW_LIMITS.MAX_MESSAGES_PER_RUN */.Or.MAX_MESSAGES_PER_RUN) throw new flow_errors/* .FlowLimitError */.IM(message_keys/* .FLOW_ERROR_MESSAGES.TOO_MANY_MESSAGES */.zr.TOO_MANY_MESSAGES, {
964
+ max: flow_types/* .FLOW_LIMITS.MAX_MESSAGES_PER_RUN */.Or.MAX_MESSAGES_PER_RUN
965
+ });
966
+ state.messages += count;
967
+ }
968
+ function assertRecipientCount(node, count, max) {
969
+ if (count > max) throw new flow_errors/* .FlowLimitError */.IM(message_keys/* .FLOW_ERROR_MESSAGES.TOO_MANY_RECIPIENTS */.zr.TOO_MANY_RECIPIENTS, {
970
+ node: (0,flow_types/* .nodeLabel */.dA)(node),
971
+ count,
972
+ max
973
+ });
974
+ }
975
+ /** Whatever the module threw, or its `success: false`, fails the step with the reason (a limit stays a limit). */ function sendFailure(key, node, error) {
976
+ if (error instanceof flow_errors/* .FlowKeyedError */.Hp || error instanceof flow_errors/* .FlowHttpError */.hk) return error;
977
+ const detail = error instanceof Error ? error.message : String(error);
978
+ return new flow_errors/* .FlowKeyedError */.Hp(key, {
979
+ node: (0,flow_types/* .nodeLabel */.dA)(node),
980
+ detail
981
+ }, `"${(0,flow_types/* .nodeLabel */.dA)(node)}" could not send: ${detail}`);
982
+ }
983
+ /** Inside a transaction the message waits for the commit, so a run that rolls back sends nothing. */ async function deliver(node, state, send) {
984
+ if (state.qr) {
985
+ state.pendingEvents.push({
986
+ delivery: (0,flow_types/* .nodeLabel */.dA)(node),
987
+ send
988
+ });
989
+ return 'queued';
990
+ }
991
+ await send();
992
+ return 'sent';
993
+ }
994
+ /** One id or a list of them, each once; null / undefined / empty text is none. Anything but an id rejects with `key`. */ function idsOf(node, value, key, text) {
995
+ const items = (Array.isArray(value) ? value : [
996
+ value
997
+ ]).filter((item)=>item !== undefined && item !== null && item !== '');
998
+ for (const item of items){
999
+ if (typeof item !== 'string' || !flow_types/* .FLOW_ID_PATTERN.test */.E2.test(item)) {
1000
+ throw new flow_errors/* .FlowHttpError */.hk(400, text(shown(item)), undefined, {
1001
+ messageKey: key,
1002
+ messageVariables: {
1003
+ node: (0,flow_types/* .nodeLabel */.dA)(node),
1004
+ value: shown(item)
1005
+ }
1006
+ });
1007
+ }
1008
+ }
1009
+ return [
1010
+ ...new Set(items)
1011
+ ];
1012
+ }
1013
+ const notificationRecipients = (node, value)=>idsOf(node, value, message_keys/* .FLOW_ERROR_MESSAGES.NOTIFICATION_RECIPIENT_INVALID */.zr.NOTIFICATION_RECIPIENT_INVALID, (shownValue)=>`"${(0,flow_types/* .nodeLabel */.dA)(node)}" cannot notify "${shownValue}": it is not a user id.`);
1014
+ /** Addresses: one, a list, or comma / semicolon separated text; each once (case aside), in the order given. */ function emailAddresses(node, value) {
1015
+ const items = (Array.isArray(value) ? value : [
1016
+ value
1017
+ ]).filter((item)=>item !== undefined && item !== null);
1018
+ const seen = new Map();
1019
+ for (const item of items){
1020
+ const parts = typeof item === 'string' ? item.split(/[,;]/) : [
1021
+ item
1022
+ ];
1023
+ for (const part of parts){
1024
+ const address = typeof part === 'string' ? part.trim() : part;
1025
+ if (address === '') continue;
1026
+ if (typeof address !== 'string' || address.length > MAX_EMAIL_LENGTH || !EMAIL_PATTERN.test(address)) {
1027
+ throw new flow_errors/* .FlowHttpError */.hk(400, `"${shown(address)}" is not a valid email address.`, undefined, {
1028
+ messageKey: message_keys/* .FLOW_ERROR_MESSAGES.EMAIL_ADDRESS_INVALID */.zr.EMAIL_ADDRESS_INVALID,
1029
+ messageVariables: {
1030
+ node: (0,flow_types/* .nodeLabel */.dA)(node),
1031
+ address: shown(address)
1032
+ }
1033
+ });
1034
+ }
1035
+ if (!seen.has(address.toLowerCase())) seen.set(address.toLowerCase(), address);
1036
+ }
1037
+ }
1038
+ return [
1039
+ ...seen.values()
1040
+ ];
1041
+ }
1042
+ /** The company a notification shows under: the one given, else the caller's current one. */ function notificationCompany(node, value, given, state) {
1043
+ if (!given) return state.user?.companyId ?? null;
1044
+ if (value === undefined || value === null || value === '') return null;
1045
+ if (typeof value === 'string' && flow_types/* .FLOW_ID_PATTERN.test */.E2.test(value)) return value;
1046
+ throw new flow_errors/* .FlowHttpError */.hk(400, `"${(0,flow_types/* .nodeLabel */.dA)(node)}": "${shown(value)}" is not a company id.`, undefined, {
1047
+ messageKey: message_keys/* .FLOW_ERROR_MESSAGES.NOTIFICATION_COMPANY_INVALID */.zr.NOTIFICATION_COMPANY_INVALID,
1048
+ messageVariables: {
1049
+ node: (0,flow_types/* .nodeLabel */.dA)(node),
1050
+ value: shown(value)
1051
+ }
1052
+ });
1053
+ }
1054
+ /** Sends `batches` (each already checked) as one step: the run's budget is taken for all of them first; a test run sends nothing. */ async function sendAll(node, state, batches, skipKey, recipients, send) {
1055
+ countMessages(state, batches.length);
1056
+ if (state.mode === 'dry_run') return {
1057
+ output: {
1058
+ skipped: true,
1059
+ reasonKey: skipKey,
1060
+ count: batches.length,
1061
+ recipients
1062
+ },
1063
+ skipped: true
1064
+ };
1065
+ let status = 'sent';
1066
+ for (const batch of batches)status = await deliver(node, state, ()=>send(batch));
1067
+ return status;
1068
+ }
1069
+ const nobody = (extra = {})=>({
1070
+ output: {
1071
+ skipped: true,
1072
+ reasonKey: message_keys/* .FLOW_SKIP_MESSAGES.NO_RECIPIENTS */.CB.NO_RECIPIENTS,
1073
+ recipients: 0,
1074
+ ...extra
1075
+ },
1076
+ skipped: true
1077
+ });
1078
+ /** Title, message, type, data and push of a notification, read in the current scope (the item's, in many mode). */ async function notificationContent(node, cfg, env, state) {
1079
+ const ctx = evaluationContext(state, env);
1080
+ const title = asText(await env.ruleEngine.resolveExpression(cfg.title, ctx)).trim().slice(0, flow_types/* .FLOW_LIMITS.MAX_NOTIFICATION_TITLE_LENGTH */.Or.MAX_NOTIFICATION_TITLE_LENGTH);
1081
+ if (!title) throw new flow_errors/* .FlowHttpError */.hk(400, `The title of "${(0,flow_types/* .nodeLabel */.dA)(node)}" is empty.`, undefined, {
1082
+ messageKey: message_keys/* .FLOW_ERROR_MESSAGES.NOTIFICATION_TITLE_EMPTY */.zr.NOTIFICATION_TITLE_EMPTY,
1083
+ messageVariables: {
1084
+ node: (0,flow_types/* .nodeLabel */.dA)(node)
1085
+ }
1086
+ });
1087
+ const message = cfg.message === undefined ? '' : asText(await env.ruleEngine.resolveExpression(cfg.message, ctx));
1088
+ const data = cfg.data && Object.keys(cfg.data).length ? await mapValues(cfg.data, env, state) : null;
1089
+ return {
1090
+ title,
1091
+ message: message || null,
1092
+ type: cfg.type ?? 'info',
1093
+ data,
1094
+ realtime: cfg.realtime !== false
1095
+ };
1096
+ }
1097
+ /** One notification to the users `userIds` names, or null when that is nobody. */ async function userNotification(node, cfg, env, state) {
1098
+ const ctx = evaluationContext(state, env);
1099
+ const userIds = notificationRecipients(node, cfg.userIds === undefined ? undefined : await env.ruleEngine.resolveExpression(cfg.userIds, ctx));
1100
+ if (!userIds.length) return null;
1101
+ assertRecipientCount(node, userIds.length, flow_types/* .FLOW_LIMITS.MAX_NOTIFICATION_RECIPIENTS */.Or.MAX_NOTIFICATION_RECIPIENTS);
1102
+ const content = await notificationContent(node, cfg, env, state);
1103
+ const companyId = notificationCompany(node, cfg.companyId === undefined ? undefined : await env.ruleEngine.resolveExpression(cfg.companyId, ctx), cfg.companyId !== undefined, state);
1104
+ return {
1105
+ ...content,
1106
+ userIds,
1107
+ companyId
1108
+ };
1109
+ }
1110
+ /** One notification per company that has members, each shown under its own company. */ async function companyNotifications(node, cfg, env, state) {
1111
+ const companyMembers = env.companyMembers;
1112
+ if (!companyMembers) {
1113
+ throw new flow_errors/* .FlowKeyedError */.Hp(message_keys/* .FLOW_ERROR_MESSAGES.NOTIFICATION_COMPANY_UNAVAILABLE */.zr.NOTIFICATION_COMPANY_UNAVAILABLE, {
1114
+ node: (0,flow_types/* .nodeLabel */.dA)(node)
1115
+ }, `"${(0,flow_types/* .nodeLabel */.dA)(node)}" notifies companies, which needs the company feature.`);
1116
+ }
1117
+ const ctx = evaluationContext(state, env);
1118
+ const resolve = async (expression)=>expression === undefined ? undefined : env.ruleEngine.resolveExpression(expression, ctx);
1119
+ const companyIds = idsOf(node, await resolve(cfg.companyIds), message_keys/* .FLOW_ERROR_MESSAGES.NOTIFICATION_COMPANY_INVALID */.zr.NOTIFICATION_COMPANY_INVALID, (value)=>`"${(0,flow_types/* .nodeLabel */.dA)(node)}": "${value}" is not a company id.`);
1120
+ const branchIds = idsOf(node, await resolve(cfg.branchIds), message_keys/* .FLOW_ERROR_MESSAGES.NOTIFICATION_BRANCH_INVALID */.zr.NOTIFICATION_BRANCH_INVALID, (value)=>`"${(0,flow_types/* .nodeLabel */.dA)(node)}": "${value}" is not a branch id.`);
1121
+ if (!companyIds.length) return [];
1122
+ const content = await notificationContent(node, cfg, env, state);
1123
+ const batches = [];
1124
+ let total = 0;
1125
+ for (const companyId of companyIds){
1126
+ const userIds = await companyMembers(companyId, branchIds.length ? branchIds : undefined);
1127
+ total += userIds.length;
1128
+ assertRecipientCount(node, total, flow_types/* .FLOW_LIMITS.MAX_COMPANY_NOTIFICATION_RECIPIENTS */.Or.MAX_COMPANY_NOTIFICATION_RECIPIENTS);
1129
+ if (userIds.length) batches.push({
1130
+ ...content,
1131
+ userIds,
1132
+ companyId
1133
+ });
1134
+ }
1135
+ return batches;
1136
+ }
1137
+ /**
1138
+ * one: `{ status, recipients }`. many / company: `{ status, count, recipients }` (+ `skipped` items
1139
+ * with nobody to notify, in many mode). `status` is `sent`, or `queued` inside a transaction (sent once
1140
+ * it commits). Every message is checked before the first goes out; nobody at all skips the step, and
1141
+ * a test run checks everything but sends nothing.
1142
+ */ async function runSendNotification(node, cfg, env, state) {
1143
+ const notifications = env.notifications;
1144
+ if (!notifications) throw new flow_errors/* .FlowKeyedError */.Hp(message_keys/* .FLOW_ERROR_MESSAGES.NOTIFICATION_UNAVAILABLE */.zr.NOTIFICATION_UNAVAILABLE, {
1145
+ node: (0,flow_types/* .nodeLabel */.dA)(node)
1146
+ }, `"${(0,flow_types/* .nodeLabel */.dA)(node)}" needs the notification module, which this app does not run.`);
1147
+ const target = cfg.target ?? 'one';
1148
+ let batches;
1149
+ let skippedItems = 0;
1150
+ if (target === 'company') {
1151
+ batches = await companyNotifications(node, cfg, env, state);
1152
+ } else if (target === 'many') {
1153
+ const items = await resolveList(node, cfg.items, flow_types/* .FLOW_LIMITS.MAX_MESSAGES_PER_RUN */.Or.MAX_MESSAGES_PER_RUN, env, state);
1154
+ const prepared = await eachItem(state, items, ()=>userNotification(node, cfg, env, state));
1155
+ batches = prepared.filter((batch)=>batch !== null);
1156
+ skippedItems = prepared.length - batches.length;
1157
+ } else {
1158
+ batches = [
1159
+ await userNotification(node, cfg, env, state)
1160
+ ].filter((batch)=>batch !== null);
1161
+ }
1162
+ const counts = target === 'one' ? {} : {
1163
+ count: batches.length,
1164
+ ...target === 'many' ? {
1165
+ skipped: skippedItems
1166
+ } : {}
1167
+ };
1168
+ if (!batches.length) return nobody(counts);
1169
+ const recipients = batches.reduce((sum, batch)=>sum + batch.userIds.length, 0);
1170
+ const sent = await sendAll(node, state, batches, message_keys/* .FLOW_SKIP_MESSAGES.NOTIFICATION */.CB.NOTIFICATION, recipients, async (batch)=>{
1171
+ try {
1172
+ await notifications.sendToMany(batch);
1173
+ } catch (error) {
1174
+ throw sendFailure(message_keys/* .FLOW_ERROR_MESSAGES.NOTIFICATION_FAILED */.zr.NOTIFICATION_FAILED, node, error);
1175
+ }
1176
+ });
1177
+ if (typeof sent !== 'string') return sent;
1178
+ return {
1179
+ output: {
1180
+ status: sent,
1181
+ recipients,
1182
+ ...counts
1183
+ }
1184
+ };
1185
+ }
1186
+ /** One email, read in the current scope (the item's, in many mode), or null when `to` has no address. */ async function prepareEmail(node, cfg, email, env, state) {
1187
+ const ctx = evaluationContext(state, env);
1188
+ const addresses = async (expression)=>expression === undefined ? [] : emailAddresses(node, await env.ruleEngine.resolveExpression(expression, ctx));
1189
+ const to = await addresses(cfg.to);
1190
+ if (!to.length) return null;
1191
+ // Someone already on `to` gets the email once.
1192
+ const listed = new Set(to.map((address)=>address.toLowerCase()));
1193
+ const notListed = (list)=>{
1194
+ const kept = list.filter((address)=>!listed.has(address.toLowerCase()));
1195
+ kept.forEach((address)=>listed.add(address.toLowerCase()));
1196
+ return kept;
1197
+ };
1198
+ const cc = notListed(await addresses(cfg.cc));
1199
+ const bcc = notListed(await addresses(cfg.bcc));
1200
+ const recipients = to.length + cc.length + bcc.length;
1201
+ assertRecipientCount(node, recipients, flow_types/* .FLOW_LIMITS.MAX_EMAIL_RECIPIENTS */.Or.MAX_EMAIL_RECIPIENTS);
1202
+ const replyTo = await addresses(cfg.replyTo);
1203
+ if (replyTo.length > 1) {
1204
+ throw new flow_errors/* .FlowHttpError */.hk(400, `"${replyTo.join(', ')}" is not a single email address.`, undefined, {
1205
+ messageKey: message_keys/* .FLOW_ERROR_MESSAGES.EMAIL_ADDRESS_INVALID */.zr.EMAIL_ADDRESS_INVALID,
1206
+ messageVariables: {
1207
+ node: (0,flow_types/* .nodeLabel */.dA)(node),
1208
+ address: shown(replyTo.join(', '))
1209
+ }
1210
+ });
1211
+ }
1212
+ const base = {
1213
+ to,
1214
+ ...cc.length ? {
1215
+ cc
1216
+ } : {},
1217
+ ...bcc.length ? {
1218
+ bcc
1219
+ } : {},
1220
+ ...replyTo.length ? {
1221
+ replyTo: replyTo[0]
1222
+ } : {},
1223
+ ...cfg.emailConfigId ? {
1224
+ emailConfigId: cfg.emailConfigId
1225
+ } : {}
1226
+ };
1227
+ const user = state.user;
1228
+ if (cfg.mode === 'template') {
1229
+ const variables = await mapValues(cfg.variables, env, state);
1230
+ const templateSlug = (cfg.templateSlug ?? '').trim();
1231
+ return {
1232
+ recipients,
1233
+ send: ()=>email.sendTemplate({
1234
+ ...base,
1235
+ templateSlug,
1236
+ variables
1237
+ }, user)
1238
+ };
1239
+ }
1240
+ // A line break in a subject would start a new mail header.
1241
+ const subject = asText(cfg.subject === undefined ? '' : await env.ruleEngine.resolveExpression(cfg.subject, ctx)).replace(/[\r\n]+/g, ' ').trim().slice(0, flow_types/* .FLOW_LIMITS.MAX_EMAIL_SUBJECT_LENGTH */.Or.MAX_EMAIL_SUBJECT_LENGTH);
1242
+ if (!subject) throw new flow_errors/* .FlowHttpError */.hk(400, `The subject of "${(0,flow_types/* .nodeLabel */.dA)(node)}" is empty.`, undefined, {
1243
+ messageKey: message_keys/* .FLOW_ERROR_MESSAGES.EMAIL_SUBJECT_EMPTY */.zr.EMAIL_SUBJECT_EMPTY,
1244
+ messageVariables: {
1245
+ node: (0,flow_types/* .nodeLabel */.dA)(node)
1246
+ }
1247
+ });
1248
+ const html = cfg.format === 'html';
1249
+ const body = cfg.body === undefined ? '' : asText(html ? await env.ruleEngine.resolveHtml(cfg.body, ctx) : await env.ruleEngine.resolveExpression(cfg.body, ctx));
1250
+ if (!body.trim()) throw new flow_errors/* .FlowHttpError */.hk(400, `The message of "${(0,flow_types/* .nodeLabel */.dA)(node)}" is empty.`, undefined, {
1251
+ messageKey: message_keys/* .FLOW_ERROR_MESSAGES.EMAIL_BODY_EMPTY */.zr.EMAIL_BODY_EMPTY,
1252
+ messageVariables: {
1253
+ node: (0,flow_types/* .nodeLabel */.dA)(node)
1254
+ }
1255
+ });
1256
+ return {
1257
+ recipients,
1258
+ send: ()=>email.send({
1259
+ ...base,
1260
+ subject,
1261
+ ...html ? {
1262
+ html: body
1263
+ } : {
1264
+ text: body
1265
+ }
1266
+ }, user)
1267
+ };
1268
+ }
1269
+ /**
1270
+ * one: `{ status, recipients, messageId }`. many: `{ status, count, recipients, skipped, messageIds }`
1271
+ * (one id per email sent, in order). `status` is `sent`, or `queued` inside a transaction (sent once it
1272
+ * commits, no ids yet). Every email is checked before the first goes out; no address at all skips the
1273
+ * step, and a test run checks everything but sends nothing.
1274
+ */ async function runSendEmail(node, cfg, env, state) {
1275
+ const email = env.email;
1276
+ if (!email) throw new flow_errors/* .FlowKeyedError */.Hp(message_keys/* .FLOW_ERROR_MESSAGES.EMAIL_UNAVAILABLE */.zr.EMAIL_UNAVAILABLE, {
1277
+ node: (0,flow_types/* .nodeLabel */.dA)(node)
1278
+ }, `"${(0,flow_types/* .nodeLabel */.dA)(node)}" needs the email module, which this app does not run.`);
1279
+ const many = cfg.target === 'many';
1280
+ let emails;
1281
+ let skippedItems = 0;
1282
+ if (many) {
1283
+ const items = await resolveList(node, cfg.items, flow_types/* .FLOW_LIMITS.MAX_MESSAGES_PER_RUN */.Or.MAX_MESSAGES_PER_RUN, env, state);
1284
+ const prepared = await eachItem(state, items, ()=>prepareEmail(node, cfg, email, env, state));
1285
+ emails = prepared.filter((item)=>item !== null);
1286
+ skippedItems = prepared.length - emails.length;
1287
+ } else {
1288
+ emails = [
1289
+ await prepareEmail(node, cfg, email, env, state)
1290
+ ].filter((prepared)=>prepared !== null);
1291
+ }
1292
+ if (!emails.length) return nobody(many ? {
1293
+ count: 0,
1294
+ skipped: skippedItems
1295
+ } : {});
1296
+ const recipients = emails.reduce((sum, prepared)=>sum + prepared.recipients, 0);
1297
+ const messageIds = [];
1298
+ const sent = await sendAll(node, state, emails, message_keys/* .FLOW_SKIP_MESSAGES.EMAIL */.CB.EMAIL, recipients, async (prepared)=>{
1299
+ let result;
1300
+ try {
1301
+ result = await prepared.send();
1302
+ } catch (error) {
1303
+ throw sendFailure(message_keys/* .FLOW_ERROR_MESSAGES.EMAIL_FAILED */.zr.EMAIL_FAILED, node, error);
1304
+ }
1305
+ if (!result?.success) throw sendFailure(message_keys/* .FLOW_ERROR_MESSAGES.EMAIL_FAILED */.zr.EMAIL_FAILED, node, result?.error || 'The email was not accepted.');
1306
+ messageIds.push(result.messageId ?? null);
1307
+ });
1308
+ if (typeof sent !== 'string') return sent;
1309
+ if (!many) return {
1310
+ output: {
1311
+ status: sent,
1312
+ recipients,
1313
+ messageId: messageIds[0] ?? null
1314
+ }
1315
+ };
1316
+ return {
1317
+ output: {
1318
+ status: sent,
1319
+ count: emails.length,
1320
+ recipients,
1321
+ skipped: skippedItems,
1322
+ messageIds
1323
+ }
1324
+ };
1325
+ }
900
1326
  /** Runs one node. Ports/branching/looping are the executor's job; this only does the node's own work. */ async function runNode(node, env, state) {
901
1327
  const ctx = evaluationContext(state, env);
902
1328
  const c = node.config;
@@ -911,6 +1337,8 @@ async function insertChildren(node, child, parentId, env, state) {
911
1337
  return runCode(node, c, state);
912
1338
  case 'permission_check':
913
1339
  return runPermissionCheck(c, env, state);
1340
+ case 'company_branch_check':
1341
+ return runCompanyBranchCheck(c, env, state);
914
1342
  case 'set':
915
1343
  {
916
1344
  const cfg = c;
@@ -967,7 +1395,6 @@ async function insertChildren(node, child, parentId, env, state) {
967
1395
  case 'entity_get':
968
1396
  {
969
1397
  const cfg = c;
970
- await authorize(env, state, cfg.entityCode, 'read');
971
1398
  const id = asId(await env.ruleEngine.resolveExpression(cfg.id, ctx));
972
1399
  const service = await env.entityService(cfg.entityCode, state.qr);
973
1400
  try {
@@ -984,7 +1411,6 @@ async function insertChildren(node, child, parentId, env, state) {
984
1411
  case 'entity_query':
985
1412
  {
986
1413
  const cfg = c;
987
- await authorize(env, state, cfg.entityCode, 'read');
988
1414
  const service = await env.entityService(cfg.entityCode, state.qr);
989
1415
  const filter = await mapFilter(node, cfg.filter, env, state, 'skip');
990
1416
  const search = cfg.search ? await env.ruleEngine.resolveExpression(cfg.search, ctx) : undefined;
@@ -1061,6 +1487,7 @@ async function insertChildren(node, child, parentId, env, state) {
1061
1487
  k,
1062
1488
  String(v ?? '')
1063
1489
  ]));
1490
+ if (cfg.forwardAuth === true && state.authorization && !Object.keys(headers).some((name)=>name.toLowerCase() === 'authorization')) headers['authorization'] = state.authorization;
1064
1491
  const body = cfg.body && Object.keys(cfg.body).length ? await mapValues(cfg.body, env, state) : undefined;
1065
1492
  const remaining = state.deadline - Date.now();
1066
1493
  if (remaining <= 0) throw new flow_errors/* .FlowLimitError */.IM(message_keys/* .FLOW_ERROR_MESSAGES.OUT_OF_TIME */.zr.OUT_OF_TIME);
@@ -1099,6 +1526,10 @@ async function insertChildren(node, child, parentId, env, state) {
1099
1526
  }
1100
1527
  };
1101
1528
  }
1529
+ case 'send_notification':
1530
+ return runSendNotification(node, c, env, state);
1531
+ case 'send_email':
1532
+ return runSendEmail(node, c, env, state);
1102
1533
  case 'respond':
1103
1534
  {
1104
1535
  const cfg = c;
@@ -1379,14 +1810,16 @@ function successResult(state, startedAt) {
1379
1810
  },
1380
1811
  3186(__unused_rspack_module, __webpack_exports__, __webpack_require__) {
1381
1812
  /* import */ var _flusys_nestjs_shared_utils__rspack_import_0 = __webpack_require__(9216);
1382
- /* import */ var _config_entity_builder_constants_js__rspack_import_8 = __webpack_require__(6576);
1383
- /* import */ var _config_message_keys_js__rspack_import_7 = __webpack_require__(5732);
1813
+ /* import */ var _config_entity_builder_constants_js__rspack_import_9 = __webpack_require__(6576);
1814
+ /* import */ var _config_message_keys_js__rspack_import_8 = __webpack_require__(5732);
1384
1815
  /* import */ var _interfaces_generic_record_interface_js__rspack_import_1 = __webpack_require__(3372);
1385
- /* import */ var _rule_engine_index_js__rspack_import_2 = __webpack_require__(5920);
1816
+ /* import */ var _rule_engine_index_js__rspack_import_2 = __webpack_require__(8429);
1386
1817
  /* import */ var _rule_engine_rule_ip_js__rspack_import_3 = __webpack_require__(809);
1387
1818
  /* import */ var _rule_engine_rule_permission_js__rspack_import_4 = __webpack_require__(5191);
1388
1819
  /* import */ var _rule_engine_rule_regex_js__rspack_import_5 = __webpack_require__(6308);
1389
1820
  /* import */ var _flow_types_js__rspack_import_6 = __webpack_require__(6256);
1821
+ /* import */ var _flow_api_key_js__rspack_import_7 = __webpack_require__(4816);
1822
+
1390
1823
 
1391
1824
 
1392
1825
 
@@ -1567,37 +2000,37 @@ function isExpression(value) {
1567
2000
  return !!value && typeof value === 'object' && typeof value.type === 'string';
1568
2001
  }
1569
2002
  /** Expression shape check with the engine's limits (no evaluation). */ function expressionProblem(value, depth = 0, options = {}) {
1570
- if (!isExpression(value)) return (0,_flusys_nestjs_shared_utils__rspack_import_0.messageRef)(_config_message_keys_js__rspack_import_7/* .FLOW_SHAPE_MESSAGES.NOT_AN_EXPRESSION */.IP.NOT_AN_EXPRESSION);
1571
- if (depth > _rule_engine_index_js__rspack_import_2/* .RULE_ENGINE_LIMITS.MAX_GROUP_DEPTH */.bw.MAX_GROUP_DEPTH) return (0,_flusys_nestjs_shared_utils__rspack_import_0.messageRef)(_config_message_keys_js__rspack_import_7/* .FLOW_SHAPE_MESSAGES.TOO_DEEP */.IP.TOO_DEEP);
2003
+ if (!isExpression(value)) return (0,_flusys_nestjs_shared_utils__rspack_import_0.messageRef)(_config_message_keys_js__rspack_import_8/* .FLOW_SHAPE_MESSAGES.NOT_AN_EXPRESSION */.IP.NOT_AN_EXPRESSION);
2004
+ if (depth > _rule_engine_index_js__rspack_import_2/* .RULE_ENGINE_LIMITS.MAX_GROUP_DEPTH */.bw.MAX_GROUP_DEPTH) return (0,_flusys_nestjs_shared_utils__rspack_import_0.messageRef)(_config_message_keys_js__rspack_import_8/* .FLOW_SHAPE_MESSAGES.TOO_DEEP */.IP.TOO_DEEP);
1572
2005
  switch(value.type){
1573
2006
  case 'literal':
1574
2007
  return null;
1575
2008
  case 'field_ref':
1576
- return typeof value.ref === 'string' && value.ref.length > 0 ? null : (0,_flusys_nestjs_shared_utils__rspack_import_0.messageRef)(_config_message_keys_js__rspack_import_7/* .FLOW_SHAPE_MESSAGES.EMPTY_REFERENCE */.IP.EMPTY_REFERENCE);
2009
+ return typeof value.ref === 'string' && value.ref.length > 0 ? null : (0,_flusys_nestjs_shared_utils__rspack_import_0.messageRef)(_config_message_keys_js__rspack_import_8/* .FLOW_SHAPE_MESSAGES.EMPTY_REFERENCE */.IP.EMPTY_REFERENCE);
1577
2010
  case 'template':
1578
- if (typeof value.template !== 'string') return (0,_flusys_nestjs_shared_utils__rspack_import_0.messageRef)(_config_message_keys_js__rspack_import_7/* .FLOW_SHAPE_MESSAGES.NO_TEMPLATE_TEXT */.IP.NO_TEMPLATE_TEXT);
1579
- return value.template.length <= _rule_engine_index_js__rspack_import_2/* .RULE_TEMPLATE_MAX_LENGTH */.ym ? null : (0,_flusys_nestjs_shared_utils__rspack_import_0.messageRef)(_config_message_keys_js__rspack_import_7/* .FLOW_SHAPE_MESSAGES.TEMPLATE_TOO_LONG */.IP.TEMPLATE_TOO_LONG);
2011
+ if (typeof value.template !== 'string') return (0,_flusys_nestjs_shared_utils__rspack_import_0.messageRef)(_config_message_keys_js__rspack_import_8/* .FLOW_SHAPE_MESSAGES.NO_TEMPLATE_TEXT */.IP.NO_TEMPLATE_TEXT);
2012
+ return value.template.length <= _rule_engine_index_js__rspack_import_2/* .RULE_TEMPLATE_MAX_LENGTH */.ym ? null : (0,_flusys_nestjs_shared_utils__rspack_import_0.messageRef)(_config_message_keys_js__rspack_import_8/* .FLOW_SHAPE_MESSAGES.TEMPLATE_TOO_LONG */.IP.TEMPLATE_TOO_LONG);
1580
2013
  case 'arithmetic':
1581
- return Array.isArray(value.operands) ? firstProblem(value.operands, depth, options) : (0,_flusys_nestjs_shared_utils__rspack_import_0.messageRef)(_config_message_keys_js__rspack_import_7/* .FLOW_SHAPE_MESSAGES.NO_OPERANDS */.IP.NO_OPERANDS);
2014
+ return Array.isArray(value.operands) ? firstProblem(value.operands, depth, options) : (0,_flusys_nestjs_shared_utils__rspack_import_0.messageRef)(_config_message_keys_js__rspack_import_8/* .FLOW_SHAPE_MESSAGES.NO_OPERANDS */.IP.NO_OPERANDS);
1582
2015
  case 'concat':
1583
- return Array.isArray(value.parts) ? firstProblem(value.parts, depth, options) : (0,_flusys_nestjs_shared_utils__rspack_import_0.messageRef)(_config_message_keys_js__rspack_import_7/* .FLOW_SHAPE_MESSAGES.NO_PARTS */.IP.NO_PARTS);
2016
+ return Array.isArray(value.parts) ? firstProblem(value.parts, depth, options) : (0,_flusys_nestjs_shared_utils__rspack_import_0.messageRef)(_config_message_keys_js__rspack_import_8/* .FLOW_SHAPE_MESSAGES.NO_PARTS */.IP.NO_PARTS);
1584
2017
  case 'function':
1585
2018
  return functionProblem(value, depth, options);
1586
2019
  case 'lookup':
1587
2020
  return lookupProblem(value, depth, options);
1588
2021
  default:
1589
- return (0,_flusys_nestjs_shared_utils__rspack_import_0.messageRef)(_config_message_keys_js__rspack_import_7/* .FLOW_SHAPE_MESSAGES.UNKNOWN_EXPRESSION_TYPE */.IP.UNKNOWN_EXPRESSION_TYPE, {
2022
+ return (0,_flusys_nestjs_shared_utils__rspack_import_0.messageRef)(_config_message_keys_js__rspack_import_8/* .FLOW_SHAPE_MESSAGES.UNKNOWN_EXPRESSION_TYPE */.IP.UNKNOWN_EXPRESSION_TYPE, {
1590
2023
  type: value.type
1591
2024
  });
1592
2025
  }
1593
2026
  }
1594
2027
  function functionProblem(value, depth, options) {
1595
- if (!Array.isArray(value.args)) return (0,_flusys_nestjs_shared_utils__rspack_import_0.messageRef)(_config_message_keys_js__rspack_import_7/* .FLOW_SHAPE_MESSAGES.NO_ARGUMENTS */.IP.NO_ARGUMENTS);
2028
+ if (!Array.isArray(value.args)) return (0,_flusys_nestjs_shared_utils__rspack_import_0.messageRef)(_config_message_keys_js__rspack_import_8/* .FLOW_SHAPE_MESSAGES.NO_ARGUMENTS */.IP.NO_ARGUMENTS);
1596
2029
  const arity = FUNCTION_ARITY[value.name];
1597
- if (!arity) return (0,_flusys_nestjs_shared_utils__rspack_import_0.messageRef)(_config_message_keys_js__rspack_import_7/* .FLOW_SHAPE_MESSAGES.UNKNOWN_FUNCTION */.IP.UNKNOWN_FUNCTION, {
2030
+ if (!arity) return (0,_flusys_nestjs_shared_utils__rspack_import_0.messageRef)(_config_message_keys_js__rspack_import_8/* .FLOW_SHAPE_MESSAGES.UNKNOWN_FUNCTION */.IP.UNKNOWN_FUNCTION, {
1598
2031
  name: String(value.name)
1599
2032
  });
1600
- if (value.args.length < arity[0] || value.args.length > arity[1]) return (0,_flusys_nestjs_shared_utils__rspack_import_0.messageRef)(_config_message_keys_js__rspack_import_7/* .FLOW_SHAPE_MESSAGES.WRONG_ARGUMENT_COUNT */.IP.WRONG_ARGUMENT_COUNT, {
2033
+ if (value.args.length < arity[0] || value.args.length > arity[1]) return (0,_flusys_nestjs_shared_utils__rspack_import_0.messageRef)(_config_message_keys_js__rspack_import_8/* .FLOW_SHAPE_MESSAGES.WRONG_ARGUMENT_COUNT */.IP.WRONG_ARGUMENT_COUNT, {
1601
2034
  name: value.name,
1602
2035
  min: arity[0],
1603
2036
  max: arity[1]
@@ -1605,7 +2038,7 @@ function functionProblem(value, depth, options) {
1605
2038
  const unitAt = DURATION_ARG[value.name];
1606
2039
  const unit = unitAt === undefined ? undefined : value.args[unitAt];
1607
2040
  if (unit?.type === 'literal' && !_rule_engine_index_js__rspack_import_2/* .RULE_DURATION_UNITS.includes */.qO.includes(unit.value)) {
1608
- return (0,_flusys_nestjs_shared_utils__rspack_import_0.messageRef)(_config_message_keys_js__rspack_import_7/* .FLOW_SHAPE_MESSAGES.DURATION_UNIT_INVALID */.IP.DURATION_UNIT_INVALID, {
2041
+ return (0,_flusys_nestjs_shared_utils__rspack_import_0.messageRef)(_config_message_keys_js__rspack_import_8/* .FLOW_SHAPE_MESSAGES.DURATION_UNIT_INVALID */.IP.DURATION_UNIT_INVALID, {
1609
2042
  units: _rule_engine_index_js__rspack_import_2/* .RULE_DURATION_UNITS.join */.qO.join(', ')
1610
2043
  });
1611
2044
  }
@@ -1628,18 +2061,18 @@ function functionProblem(value, depth, options) {
1628
2061
  try {
1629
2062
  parsed = (0,_rule_engine_rule_permission_js__rspack_import_4/* .parsePermissionCodes */.PS)(codes.value);
1630
2063
  } catch {
1631
- return (0,_flusys_nestjs_shared_utils__rspack_import_0.messageRef)(_config_message_keys_js__rspack_import_7/* .RULE_ENGINE_MESSAGES.TOO_MANY_PERMISSION_CODES */.x3.TOO_MANY_PERMISSION_CODES, {
2064
+ return (0,_flusys_nestjs_shared_utils__rspack_import_0.messageRef)(_config_message_keys_js__rspack_import_8/* .RULE_ENGINE_MESSAGES.TOO_MANY_PERMISSION_CODES */.x3.TOO_MANY_PERMISSION_CODES, {
1632
2065
  max: _rule_engine_index_js__rspack_import_2/* .RULE_ENGINE_LIMITS.MAX_PERMISSION_CODES */.bw.MAX_PERMISSION_CODES
1633
2066
  });
1634
2067
  }
1635
- return parsed.length ? null : (0,_flusys_nestjs_shared_utils__rspack_import_0.messageRef)(_config_message_keys_js__rspack_import_7/* .FLOW_SHAPE_MESSAGES.PERMISSION_CODE_REQUIRED */.IP.PERMISSION_CODE_REQUIRED, {
2068
+ return parsed.length ? null : (0,_flusys_nestjs_shared_utils__rspack_import_0.messageRef)(_config_message_keys_js__rspack_import_8/* .FLOW_SHAPE_MESSAGES.PERMISSION_CODE_REQUIRED */.IP.PERMISSION_CODE_REQUIRED, {
1636
2069
  name: value.name
1637
2070
  });
1638
2071
  }
1639
2072
  if (value.name === 'IP_IN_RANGE') {
1640
2073
  const ranges = literal(1);
1641
2074
  const invalid = ranges.ok ? (0,_rule_engine_rule_ip_js__rspack_import_3/* .invalidIpRange */.I8)(ranges.value) : null;
1642
- return invalid ? (0,_flusys_nestjs_shared_utils__rspack_import_0.messageRef)(_config_message_keys_js__rspack_import_7/* .FLOW_SHAPE_MESSAGES.IP_RANGE_INVALID */.IP.IP_RANGE_INVALID, {
2075
+ return invalid ? (0,_flusys_nestjs_shared_utils__rspack_import_0.messageRef)(_config_message_keys_js__rspack_import_8/* .FLOW_SHAPE_MESSAGES.IP_RANGE_INVALID */.IP.IP_RANGE_INVALID, {
1643
2076
  range: invalid
1644
2077
  }) : null;
1645
2078
  }
@@ -1653,13 +2086,13 @@ function functionProblem(value, depth, options) {
1653
2086
  function patternProblem(pattern) {
1654
2087
  switch((0,_rule_engine_rule_regex_js__rspack_import_5/* .rulePatternProblem */._V)(pattern)){
1655
2088
  case 'too_long':
1656
- return (0,_flusys_nestjs_shared_utils__rspack_import_0.messageRef)(_config_message_keys_js__rspack_import_7/* .FLOW_SHAPE_MESSAGES.REGEX_TOO_LONG */.IP.REGEX_TOO_LONG, {
2089
+ return (0,_flusys_nestjs_shared_utils__rspack_import_0.messageRef)(_config_message_keys_js__rspack_import_8/* .FLOW_SHAPE_MESSAGES.REGEX_TOO_LONG */.IP.REGEX_TOO_LONG, {
1657
2090
  max: _rule_engine_index_js__rspack_import_2/* .RULE_ENGINE_LIMITS.MAX_REGEX_PATTERN_LENGTH */.bw.MAX_REGEX_PATTERN_LENGTH
1658
2091
  });
1659
2092
  case 'invalid':
1660
- return (0,_flusys_nestjs_shared_utils__rspack_import_0.messageRef)(_config_message_keys_js__rspack_import_7/* .FLOW_SHAPE_MESSAGES.REGEX_INVALID */.IP.REGEX_INVALID);
2093
+ return (0,_flusys_nestjs_shared_utils__rspack_import_0.messageRef)(_config_message_keys_js__rspack_import_8/* .FLOW_SHAPE_MESSAGES.REGEX_INVALID */.IP.REGEX_INVALID);
1661
2094
  case 'too_complex':
1662
- return (0,_flusys_nestjs_shared_utils__rspack_import_0.messageRef)(_config_message_keys_js__rspack_import_7/* .FLOW_SHAPE_MESSAGES.REGEX_TOO_COMPLEX */.IP.REGEX_TOO_COMPLEX, {
2095
+ return (0,_flusys_nestjs_shared_utils__rspack_import_0.messageRef)(_config_message_keys_js__rspack_import_8/* .FLOW_SHAPE_MESSAGES.REGEX_TOO_COMPLEX */.IP.REGEX_TOO_COMPLEX, {
1663
2096
  max: _rule_engine_index_js__rspack_import_2/* .RULE_ENGINE_LIMITS.MAX_REGEX_REPEAT */.bw.MAX_REGEX_REPEAT
1664
2097
  });
1665
2098
  default:
@@ -1667,20 +2100,20 @@ function patternProblem(pattern) {
1667
2100
  }
1668
2101
  }
1669
2102
  function lookupProblem(value, depth, options) {
1670
- if (typeof value.entity !== 'string' || !_config_entity_builder_constants_js__rspack_import_8/* .ENTITY_CODE_PATTERN.test */.vs.test(value.entity)) return (0,_flusys_nestjs_shared_utils__rspack_import_0.messageRef)(_config_message_keys_js__rspack_import_7/* .FLOW_SHAPE_MESSAGES.LOOKUP_ENTITY_INVALID */.IP.LOOKUP_ENTITY_INVALID);
1671
- if (options.knownEntityCodes && !options.knownEntityCodes.has(value.entity)) return (0,_flusys_nestjs_shared_utils__rspack_import_0.messageRef)(_config_message_keys_js__rspack_import_7/* .FLOW_SHAPE_MESSAGES.LOOKUP_ENTITY_UNKNOWN */.IP.LOOKUP_ENTITY_UNKNOWN, {
2103
+ if (typeof value.entity !== 'string' || !_config_entity_builder_constants_js__rspack_import_9/* .ENTITY_CODE_PATTERN.test */.vs.test(value.entity)) return (0,_flusys_nestjs_shared_utils__rspack_import_0.messageRef)(_config_message_keys_js__rspack_import_8/* .FLOW_SHAPE_MESSAGES.LOOKUP_ENTITY_INVALID */.IP.LOOKUP_ENTITY_INVALID);
2104
+ if (options.knownEntityCodes && !options.knownEntityCodes.has(value.entity)) return (0,_flusys_nestjs_shared_utils__rspack_import_0.messageRef)(_config_message_keys_js__rspack_import_8/* .FLOW_SHAPE_MESSAGES.LOOKUP_ENTITY_UNKNOWN */.IP.LOOKUP_ENTITY_UNKNOWN, {
1672
2105
  entity: value.entity
1673
2106
  });
1674
2107
  if (![
1675
2108
  'exists',
1676
2109
  'count',
1677
2110
  'first'
1678
- ].includes(value.mode)) return (0,_flusys_nestjs_shared_utils__rspack_import_0.messageRef)(_config_message_keys_js__rspack_import_7/* .FLOW_SHAPE_MESSAGES.LOOKUP_MODE_INVALID */.IP.LOOKUP_MODE_INVALID);
1679
- if (value.field !== undefined && (typeof value.field !== 'string' || !COLUMN_CODE.test(value.field))) return (0,_flusys_nestjs_shared_utils__rspack_import_0.messageRef)(_config_message_keys_js__rspack_import_7/* .FLOW_SHAPE_MESSAGES.LOOKUP_FIELD_INVALID */.IP.LOOKUP_FIELD_INVALID);
1680
- if (value.sort !== undefined && (typeof value.sort?.field !== 'string' || !COLUMN_CODE.test(value.sort.field))) return (0,_flusys_nestjs_shared_utils__rspack_import_0.messageRef)(_config_message_keys_js__rspack_import_7/* .FLOW_SHAPE_MESSAGES.LOOKUP_FIELD_INVALID */.IP.LOOKUP_FIELD_INVALID);
1681
- if (!value.filter || typeof value.filter !== 'object' || Array.isArray(value.filter)) return (0,_flusys_nestjs_shared_utils__rspack_import_0.messageRef)(_config_message_keys_js__rspack_import_7/* .FLOW_SHAPE_MESSAGES.LOOKUP_FILTER_INVALID */.IP.LOOKUP_FILTER_INVALID);
2111
+ ].includes(value.mode)) return (0,_flusys_nestjs_shared_utils__rspack_import_0.messageRef)(_config_message_keys_js__rspack_import_8/* .FLOW_SHAPE_MESSAGES.LOOKUP_MODE_INVALID */.IP.LOOKUP_MODE_INVALID);
2112
+ if (value.field !== undefined && (typeof value.field !== 'string' || !COLUMN_CODE.test(value.field))) return (0,_flusys_nestjs_shared_utils__rspack_import_0.messageRef)(_config_message_keys_js__rspack_import_8/* .FLOW_SHAPE_MESSAGES.LOOKUP_FIELD_INVALID */.IP.LOOKUP_FIELD_INVALID);
2113
+ if (value.sort !== undefined && (typeof value.sort?.field !== 'string' || !COLUMN_CODE.test(value.sort.field))) return (0,_flusys_nestjs_shared_utils__rspack_import_0.messageRef)(_config_message_keys_js__rspack_import_8/* .FLOW_SHAPE_MESSAGES.LOOKUP_FIELD_INVALID */.IP.LOOKUP_FIELD_INVALID);
2114
+ if (!value.filter || typeof value.filter !== 'object' || Array.isArray(value.filter)) return (0,_flusys_nestjs_shared_utils__rspack_import_0.messageRef)(_config_message_keys_js__rspack_import_8/* .FLOW_SHAPE_MESSAGES.LOOKUP_FILTER_INVALID */.IP.LOOKUP_FILTER_INVALID);
1682
2115
  for (const [key, item] of Object.entries(value.filter)){
1683
- if (!COLUMN_CODE.test(key)) return (0,_flusys_nestjs_shared_utils__rspack_import_0.messageRef)(_config_message_keys_js__rspack_import_7/* .FLOW_SHAPE_MESSAGES.LOOKUP_FILTER_INVALID */.IP.LOOKUP_FILTER_INVALID);
2116
+ if (!COLUMN_CODE.test(key)) return (0,_flusys_nestjs_shared_utils__rspack_import_0.messageRef)(_config_message_keys_js__rspack_import_8/* .FLOW_SHAPE_MESSAGES.LOOKUP_FILTER_INVALID */.IP.LOOKUP_FILTER_INVALID);
1684
2117
  const problem = filterValueProblem(item, depth, options);
1685
2118
  if (problem) return problem;
1686
2119
  }
@@ -1689,13 +2122,13 @@ function lookupProblem(value, depth, options) {
1689
2122
  /** A filter entry: an expression (eq) or `{ op, value? }` with an expression value. */ function filterValueProblem(item, depth, options) {
1690
2123
  if (!(0,_interfaces_generic_record_interface_js__rspack_import_1/* .isRecordFilterCondition */.t1)(item)) {
1691
2124
  const op = item && typeof item === 'object' && !Array.isArray(item) && !('type' in item) ? item.op : undefined;
1692
- if (typeof op === 'string' && !_interfaces_generic_record_interface_js__rspack_import_1/* .RECORD_FILTER_OPS.includes */.ox.includes(op)) return (0,_flusys_nestjs_shared_utils__rspack_import_0.messageRef)(_config_message_keys_js__rspack_import_7/* .FLOW_SHAPE_MESSAGES.FILTER_OPERATOR_UNKNOWN */.IP.FILTER_OPERATOR_UNKNOWN, {
2125
+ if (typeof op === 'string' && !_interfaces_generic_record_interface_js__rspack_import_1/* .RECORD_FILTER_OPS.includes */.ox.includes(op)) return (0,_flusys_nestjs_shared_utils__rspack_import_0.messageRef)(_config_message_keys_js__rspack_import_8/* .FLOW_SHAPE_MESSAGES.FILTER_OPERATOR_UNKNOWN */.IP.FILTER_OPERATOR_UNKNOWN, {
1693
2126
  op
1694
2127
  });
1695
2128
  return expressionProblem(item, depth + 1, options);
1696
2129
  }
1697
2130
  if (item.op === 'is_null' || item.op === 'not_null') return null;
1698
- if (item.value === undefined) return (0,_flusys_nestjs_shared_utils__rspack_import_0.messageRef)(_config_message_keys_js__rspack_import_7/* .FLOW_SHAPE_MESSAGES.FILTER_VALUE_REQUIRED */.IP.FILTER_VALUE_REQUIRED, {
2131
+ if (item.value === undefined) return (0,_flusys_nestjs_shared_utils__rspack_import_0.messageRef)(_config_message_keys_js__rspack_import_8/* .FLOW_SHAPE_MESSAGES.FILTER_VALUE_REQUIRED */.IP.FILTER_VALUE_REQUIRED, {
1699
2132
  op: item.op
1700
2133
  });
1701
2134
  return expressionProblem(item.value, depth + 1, options);
@@ -1709,8 +2142,8 @@ function firstProblem(items, depth, options) {
1709
2142
  }
1710
2143
  function conditionProblem(node, options) {
1711
2144
  const hasLeft = node?.left !== undefined;
1712
- if (!node || !hasLeft && (typeof node.field !== 'string' || !node.field) || typeof node.comparison !== 'string') return (0,_flusys_nestjs_shared_utils__rspack_import_0.messageRef)(_config_message_keys_js__rspack_import_7/* .FLOW_SHAPE_MESSAGES.CONDITION_INCOMPLETE */.IP.CONDITION_INCOMPLETE);
1713
- if (!_rule_engine_index_js__rspack_import_2/* .RULE_COMPARISON_OPERATORS.includes */.dJ.includes(node.comparison)) return (0,_flusys_nestjs_shared_utils__rspack_import_0.messageRef)(_config_message_keys_js__rspack_import_7/* .FLOW_SHAPE_MESSAGES.UNKNOWN_COMPARISON */.IP.UNKNOWN_COMPARISON, {
2145
+ if (!node || !hasLeft && (typeof node.field !== 'string' || !node.field) || typeof node.comparison !== 'string') return (0,_flusys_nestjs_shared_utils__rspack_import_0.messageRef)(_config_message_keys_js__rspack_import_8/* .FLOW_SHAPE_MESSAGES.CONDITION_INCOMPLETE */.IP.CONDITION_INCOMPLETE);
2146
+ if (!_rule_engine_index_js__rspack_import_2/* .RULE_COMPARISON_OPERATORS.includes */.dJ.includes(node.comparison)) return (0,_flusys_nestjs_shared_utils__rspack_import_0.messageRef)(_config_message_keys_js__rspack_import_8/* .FLOW_SHAPE_MESSAGES.UNKNOWN_COMPARISON */.IP.UNKNOWN_COMPARISON, {
1714
2147
  comparison: node.comparison
1715
2148
  });
1716
2149
  if (hasLeft) {
@@ -1726,15 +2159,15 @@ function conditionProblem(node, options) {
1726
2159
  return null;
1727
2160
  }
1728
2161
  /** Rule group shape check with the engine's limits (no evaluation). */ function groupProblem(group, depth = 0, options = {}) {
1729
- if (!group || typeof group !== 'object' || !Array.isArray(group.conditions)) return (0,_flusys_nestjs_shared_utils__rspack_import_0.messageRef)(_config_message_keys_js__rspack_import_7/* .FLOW_SHAPE_MESSAGES.NOT_A_RULE */.IP.NOT_A_RULE);
2162
+ if (!group || typeof group !== 'object' || !Array.isArray(group.conditions)) return (0,_flusys_nestjs_shared_utils__rspack_import_0.messageRef)(_config_message_keys_js__rspack_import_8/* .FLOW_SHAPE_MESSAGES.NOT_A_RULE */.IP.NOT_A_RULE);
1730
2163
  const g = group;
1731
- if (depth > _rule_engine_index_js__rspack_import_2/* .RULE_ENGINE_LIMITS.MAX_GROUP_DEPTH */.bw.MAX_GROUP_DEPTH) return (0,_flusys_nestjs_shared_utils__rspack_import_0.messageRef)(_config_message_keys_js__rspack_import_7/* .FLOW_SHAPE_MESSAGES.TOO_DEEP */.IP.TOO_DEEP);
1732
- if (g.conditions.length > _rule_engine_index_js__rspack_import_2/* .RULE_ENGINE_LIMITS.MAX_CONDITIONS_PER_GROUP */.bw.MAX_CONDITIONS_PER_GROUP) return (0,_flusys_nestjs_shared_utils__rspack_import_0.messageRef)(_config_message_keys_js__rspack_import_7/* .FLOW_SHAPE_MESSAGES.TOO_MANY_CONDITIONS */.IP.TOO_MANY_CONDITIONS);
2164
+ if (depth > _rule_engine_index_js__rspack_import_2/* .RULE_ENGINE_LIMITS.MAX_GROUP_DEPTH */.bw.MAX_GROUP_DEPTH) return (0,_flusys_nestjs_shared_utils__rspack_import_0.messageRef)(_config_message_keys_js__rspack_import_8/* .FLOW_SHAPE_MESSAGES.TOO_DEEP */.IP.TOO_DEEP);
2165
+ if (g.conditions.length > _rule_engine_index_js__rspack_import_2/* .RULE_ENGINE_LIMITS.MAX_CONDITIONS_PER_GROUP */.bw.MAX_CONDITIONS_PER_GROUP) return (0,_flusys_nestjs_shared_utils__rspack_import_0.messageRef)(_config_message_keys_js__rspack_import_8/* .FLOW_SHAPE_MESSAGES.TOO_MANY_CONDITIONS */.IP.TOO_MANY_CONDITIONS);
1733
2166
  if (![
1734
2167
  'AND',
1735
2168
  'OR',
1736
2169
  'NOT'
1737
- ].includes(g.operator)) return (0,_flusys_nestjs_shared_utils__rspack_import_0.messageRef)(_config_message_keys_js__rspack_import_7/* .FLOW_SHAPE_MESSAGES.UNKNOWN_OPERATOR */.IP.UNKNOWN_OPERATOR);
2170
+ ].includes(g.operator)) return (0,_flusys_nestjs_shared_utils__rspack_import_0.messageRef)(_config_message_keys_js__rspack_import_8/* .FLOW_SHAPE_MESSAGES.UNKNOWN_OPERATOR */.IP.UNKNOWN_OPERATOR);
1738
2171
  for (const node of g.conditions){
1739
2172
  const problem = node && (0,_rule_engine_index_js__rspack_import_2/* .isRuleGroup */.GD)(node) ? groupProblem(node, depth + 1, options) : conditionProblem(node, options);
1740
2173
  if (problem) return problem;
@@ -1744,30 +2177,20 @@ function conditionProblem(node, options) {
1744
2177
  /**
1745
2178
  * Save-time check of a whole flow: structure (Request steps with distinct URLs, valid ids/ports, no cycles),
1746
2179
  * every node's configuration, every reference to another node, and the combination of
1747
- * settings (run-as / auth / transaction). Pure: no I/O, so the same result is what the
2180
+ * settings (auth / transaction). Pure: no I/O, so the same result is what the
1748
2181
  * designer shows and what the server enforces.
1749
2182
  */ function validateFlow(flow, context = {}) {
1750
2183
  const errors = [];
1751
2184
  const warnings = [];
1752
2185
  const err = (message)=>errors.push(message);
1753
- if (!_flow_types_js__rspack_import_6/* .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));
1754
- if (!_flow_types_js__rspack_import_6/* .FLOW_RUN_AS.includes */.gl.includes(flow.runAs)) err((0,_flusys_nestjs_shared_utils__rspack_import_0.messageRef)(_config_message_keys_js__rspack_import_7/* .FLOW_VALIDATION_MESSAGES.RUN_AS_INVALID */.qj.RUN_AS_INVALID, {
1755
- runAs: String(flow.runAs)
1756
- }));
1757
- if (!_flow_types_js__rspack_import_6/* .FLOW_AUTH_MODES.includes */.fO.includes(flow.authMode)) err((0,_flusys_nestjs_shared_utils__rspack_import_0.messageRef)(_config_message_keys_js__rspack_import_7/* .FLOW_VALIDATION_MESSAGES.AUTH_MODE_INVALID */.qj.AUTH_MODE_INVALID, {
1758
- authMode: String(flow.authMode)
1759
- }));
1760
- if (flow.runAs === 'caller' && flow.authMode !== 'jwt' && flow.authMode !== 'permission' && flow.authMode !== 'internal') {
1761
- err((0,_flusys_nestjs_shared_utils__rspack_import_0.messageRef)(_config_message_keys_js__rspack_import_7/* .FLOW_VALIDATION_MESSAGES.RUN_AS_CALLER_NEEDS_AUTH */.qj.RUN_AS_CALLER_NEEDS_AUTH));
1762
- }
1763
- validateInputSchema(flow, err);
2186
+ if (!_flow_types_js__rspack_import_6/* .FLOW_SLUG_PATTERN.test */.YD.test(flow.slug ?? '')) err((0,_flusys_nestjs_shared_utils__rspack_import_0.messageRef)(_config_message_keys_js__rspack_import_8/* .FLOW_VALIDATION_MESSAGES.SLUG_INVALID */.qj.SLUG_INVALID));
1764
2187
  const nodes = Array.isArray(flow.nodes) ? flow.nodes : [];
1765
2188
  const edges = Array.isArray(flow.edges) ? flow.edges : [];
1766
- 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));
1767
- if (nodes.length > _flow_types_js__rspack_import_6/* .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, {
2189
+ if (!nodes.length) err((0,_flusys_nestjs_shared_utils__rspack_import_0.messageRef)(_config_message_keys_js__rspack_import_8/* .FLOW_VALIDATION_MESSAGES.NO_NODES */.qj.NO_NODES));
2190
+ if (nodes.length > _flow_types_js__rspack_import_6/* .FLOW_LIMITS.MAX_NODES */.Or.MAX_NODES) err((0,_flusys_nestjs_shared_utils__rspack_import_0.messageRef)(_config_message_keys_js__rspack_import_8/* .FLOW_VALIDATION_MESSAGES.TOO_MANY_NODES */.qj.TOO_MANY_NODES, {
1768
2191
  max: _flow_types_js__rspack_import_6/* .FLOW_LIMITS.MAX_NODES */.Or.MAX_NODES
1769
2192
  }));
1770
- if (edges.length > _flow_types_js__rspack_import_6/* .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, {
2193
+ if (edges.length > _flow_types_js__rspack_import_6/* .FLOW_LIMITS.MAX_EDGES */.Or.MAX_EDGES) err((0,_flusys_nestjs_shared_utils__rspack_import_0.messageRef)(_config_message_keys_js__rspack_import_8/* .FLOW_VALIDATION_MESSAGES.TOO_MANY_EDGES */.qj.TOO_MANY_EDGES, {
1771
2194
  max: _flow_types_js__rspack_import_6/* .FLOW_LIMITS.MAX_EDGES */.Or.MAX_EDGES
1772
2195
  }));
1773
2196
  if (errors.length && (!nodes.length || nodes.length > _flow_types_js__rspack_import_6/* .FLOW_LIMITS.MAX_NODES */.Or.MAX_NODES || edges.length > _flow_types_js__rspack_import_6/* .FLOW_LIMITS.MAX_EDGES */.Or.MAX_EDGES)) return {
@@ -1777,16 +2200,16 @@ function conditionProblem(node, options) {
1777
2200
  const byId = new Map();
1778
2201
  for (const node of nodes){
1779
2202
  if (!_flow_types_js__rspack_import_6/* .FLOW_NODE_ID_PATTERN.test */.z6.test(node?.id ?? '')) {
1780
- 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, {
2203
+ err((0,_flusys_nestjs_shared_utils__rspack_import_0.messageRef)(_config_message_keys_js__rspack_import_8/* .FLOW_VALIDATION_MESSAGES.NODE_ID_INVALID */.qj.NODE_ID_INVALID, {
1781
2204
  id: node?.id
1782
2205
  }));
1783
2206
  continue;
1784
2207
  }
1785
- if (byId.has(node.id)) err((0,_flusys_nestjs_shared_utils__rspack_import_0.messageRef)(_config_message_keys_js__rspack_import_7/* .FLOW_VALIDATION_MESSAGES.NODE_ID_DUPLICATE */.qj.NODE_ID_DUPLICATE, {
2208
+ if (byId.has(node.id)) err((0,_flusys_nestjs_shared_utils__rspack_import_0.messageRef)(_config_message_keys_js__rspack_import_8/* .FLOW_VALIDATION_MESSAGES.NODE_ID_DUPLICATE */.qj.NODE_ID_DUPLICATE, {
1786
2209
  id: node.id
1787
2210
  }));
1788
2211
  byId.set(node.id, node);
1789
- if (!_flow_types_js__rspack_import_6/* .FLOW_NODE_TYPES.includes */.q2.includes(node.type)) err((0,_flusys_nestjs_shared_utils__rspack_import_0.messageRef)(_config_message_keys_js__rspack_import_7/* .FLOW_VALIDATION_MESSAGES.NODE_TYPE_UNKNOWN */.qj.NODE_TYPE_UNKNOWN, {
2212
+ if (!_flow_types_js__rspack_import_6/* .FLOW_NODE_TYPES.includes */.q2.includes(node.type)) err((0,_flusys_nestjs_shared_utils__rspack_import_0.messageRef)(_config_message_keys_js__rspack_import_8/* .FLOW_VALIDATION_MESSAGES.NODE_TYPE_UNKNOWN */.qj.NODE_TYPE_UNKNOWN, {
1790
2213
  id: node.id,
1791
2214
  type: node.type
1792
2215
  }));
@@ -1799,24 +2222,24 @@ function conditionProblem(node, options) {
1799
2222
  const outgoing = new Map();
1800
2223
  const edgeIds = new Set();
1801
2224
  for (const edge of edges){
1802
- if (edgeIds.has(edge.id)) err((0,_flusys_nestjs_shared_utils__rspack_import_0.messageRef)(_config_message_keys_js__rspack_import_7/* .FLOW_VALIDATION_MESSAGES.EDGE_ID_DUPLICATE */.qj.EDGE_ID_DUPLICATE, {
2225
+ if (edgeIds.has(edge.id)) err((0,_flusys_nestjs_shared_utils__rspack_import_0.messageRef)(_config_message_keys_js__rspack_import_8/* .FLOW_VALIDATION_MESSAGES.EDGE_ID_DUPLICATE */.qj.EDGE_ID_DUPLICATE, {
1803
2226
  id: edge.id
1804
2227
  }));
1805
2228
  edgeIds.add(edge.id);
1806
2229
  const from = byId.get(edge.from);
1807
2230
  const to = byId.get(edge.to);
1808
2231
  if (!from || !to) {
1809
- 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, {
2232
+ err((0,_flusys_nestjs_shared_utils__rspack_import_0.messageRef)(_config_message_keys_js__rspack_import_8/* .FLOW_VALIDATION_MESSAGES.EDGE_DANGLING */.qj.EDGE_DANGLING, {
1810
2233
  from: edge.from,
1811
2234
  to: edge.to
1812
2235
  }));
1813
2236
  continue;
1814
2237
  }
1815
- if (edge.from === edge.to) err((0,_flusys_nestjs_shared_utils__rspack_import_0.messageRef)(_config_message_keys_js__rspack_import_7/* .FLOW_VALIDATION_MESSAGES.EDGE_SELF */.qj.EDGE_SELF, {
2238
+ if (edge.from === edge.to) err((0,_flusys_nestjs_shared_utils__rspack_import_0.messageRef)(_config_message_keys_js__rspack_import_8/* .FLOW_VALIDATION_MESSAGES.EDGE_SELF */.qj.EDGE_SELF, {
1816
2239
  id: edge.from
1817
2240
  }));
1818
- if (to.type === 'trigger') err((0,_flusys_nestjs_shared_utils__rspack_import_0.messageRef)(_config_message_keys_js__rspack_import_7/* .FLOW_VALIDATION_MESSAGES.EDGE_INTO_TRIGGER */.qj.EDGE_INTO_TRIGGER));
1819
- if (!(0,_flow_types_js__rspack_import_6/* .portsOf */.a5)(from).includes(edge.port)) err((0,_flusys_nestjs_shared_utils__rspack_import_0.messageRef)(_config_message_keys_js__rspack_import_7/* .FLOW_VALIDATION_MESSAGES.EDGE_NO_PORT */.qj.EDGE_NO_PORT, {
2241
+ if (to.type === 'trigger') err((0,_flusys_nestjs_shared_utils__rspack_import_0.messageRef)(_config_message_keys_js__rspack_import_8/* .FLOW_VALIDATION_MESSAGES.EDGE_INTO_TRIGGER */.qj.EDGE_INTO_TRIGGER));
2242
+ if (!(0,_flow_types_js__rspack_import_6/* .portsOf */.a5)(from).includes(edge.port)) err((0,_flusys_nestjs_shared_utils__rspack_import_0.messageRef)(_config_message_keys_js__rspack_import_8/* .FLOW_VALIDATION_MESSAGES.EDGE_NO_PORT */.qj.EDGE_NO_PORT, {
1820
2243
  id: from.id,
1821
2244
  port: edge.port
1822
2245
  }));
@@ -1832,111 +2255,191 @@ function conditionProblem(node, options) {
1832
2255
  const seenPairs = new Set();
1833
2256
  for (const edge of edges){
1834
2257
  const key = `${edge.from}|${edge.port}|${edge.to}`;
1835
- if (seenPairs.has(key)) err((0,_flusys_nestjs_shared_utils__rspack_import_0.messageRef)(_config_message_keys_js__rspack_import_7/* .FLOW_VALIDATION_MESSAGES.EDGE_TWICE */.qj.EDGE_TWICE, {
2258
+ if (seenPairs.has(key)) err((0,_flusys_nestjs_shared_utils__rspack_import_0.messageRef)(_config_message_keys_js__rspack_import_8/* .FLOW_VALIDATION_MESSAGES.EDGE_TWICE */.qj.EDGE_TWICE, {
1836
2259
  from: edge.from,
1837
2260
  to: edge.to
1838
2261
  }));
1839
2262
  seenPairs.add(key);
1840
2263
  }
1841
2264
  const cyclic = hasCycle(nodes, outgoing);
1842
- if (cyclic) err((0,_flusys_nestjs_shared_utils__rspack_import_0.messageRef)(_config_message_keys_js__rspack_import_7/* .FLOW_VALIDATION_MESSAGES.CYCLE */.qj.CYCLE));
2265
+ if (cyclic) err((0,_flusys_nestjs_shared_utils__rspack_import_0.messageRef)(_config_message_keys_js__rspack_import_8/* .FLOW_VALIDATION_MESSAGES.CYCLE */.qj.CYCLE));
1843
2266
  const reachable = triggers.length ? new Set(triggers.flatMap((trigger)=>[
1844
2267
  ...reach(trigger.id, outgoing)
1845
2268
  ])) : null;
1846
2269
  for (const node of nodes){
1847
- if (reachable && !reachable.has(node.id)) warnings.push((0,_flusys_nestjs_shared_utils__rspack_import_0.messageRef)(_config_message_keys_js__rspack_import_7/* .FLOW_VALIDATION_MESSAGES.NODE_UNREACHABLE */.qj.NODE_UNREACHABLE, {
2270
+ if (reachable && !reachable.has(node.id)) warnings.push((0,_flusys_nestjs_shared_utils__rspack_import_0.messageRef)(_config_message_keys_js__rspack_import_8/* .FLOW_VALIDATION_MESSAGES.NODE_UNREACHABLE */.qj.NODE_UNREACHABLE, {
1848
2271
  node: (0,_flow_types_js__rspack_import_6/* .nodeLabel */.dA)(node)
1849
2272
  }));
1850
2273
  }
1851
2274
  checkNodeReferences(nodes, byId, flow, err, warnings);
1852
2275
  if (!cyclic && reachable) checkReferenceTiming(nodes, edges, byId, reachable, warnings);
1853
2276
  if (!cyclic) checkLoopDepth(nodes, outgoing, err);
1854
- if (flow.isTransactional && nodes.some((n)=>n.type === 'http_request')) {
1855
- warnings.push((0,_flusys_nestjs_shared_utils__rspack_import_0.messageRef)(_config_message_keys_js__rspack_import_7/* .FLOW_VALIDATION_MESSAGES.TRANSACTIONAL_HTTP */.qj.TRANSACTIONAL_HTTP));
1856
- }
1857
- checkTransactionUse(flow, nodes, outgoing, reachable, context, warnings);
1858
- if (flow.runAs === 'system' && flow.authMode === 'public' && nodes.some((n)=>n.type.startsWith('entity_') || collectLookupEntities(n.config).size > 0)) {
1859
- warnings.push((0,_flusys_nestjs_shared_utils__rspack_import_0.messageRef)(_config_message_keys_js__rspack_import_7/* .FLOW_VALIDATION_MESSAGES.PUBLIC_SYSTEM_ENTITY */.qj.PUBLIC_SYSTEM_ENTITY));
1860
- }
1861
- if ((flow.authMode === 'public' || flow.authMode === 'api_key') && nodes.some((n)=>n.type === 'permission_check' || usesPermissionFunction(n.config))) {
1862
- warnings.push((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));
1863
- }
2277
+ // Transaction and access are a Request step's own: each is checked against the steps its runs reach.
2278
+ const runs = (0,_flow_types_js__rspack_import_6/* .flowEndpoints */.Xr)(flow).map((endpoint)=>({
2279
+ endpoint,
2280
+ label: (0,_flow_types_js__rspack_import_6/* .nodeLabel */.dA)(byId.get(endpoint.nodeId) ?? {
2281
+ id: endpoint.nodeId,
2282
+ name: ''
2283
+ }),
2284
+ steps: [
2285
+ ...reach(endpoint.nodeId, outgoing)
2286
+ ].map((id)=>byId.get(id)).filter((node)=>!!node)
2287
+ }));
2288
+ checkEndpointRuns(runs, warnings);
2289
+ checkTransactionUse(flow, runs, outgoing, context, warnings);
1864
2290
  return {
1865
2291
  errors,
1866
2292
  warnings
1867
2293
  };
1868
2294
  }
2295
+ /** The entities a step reads or writes: its own, its child inserts' and those its lookups read. */ function entitiesOf(node) {
2296
+ const codes = collectLookupEntities(node.config);
2297
+ if (node.type.startsWith('entity_') && typeof node.config?.['entityCode'] === 'string') codes.add(node.config['entityCode']);
2298
+ const children = node.type === 'entity_insert' && Array.isArray(node.config?.['children']) ? node.config['children'] : [];
2299
+ for (const child of children)if (typeof child?.entityCode === 'string') codes.add(child.entityCode);
2300
+ return [
2301
+ ...codes
2302
+ ];
2303
+ }
2304
+ /** What depends on a Request step's access and transaction, for the steps its runs reach. A finding shared by several Request steps is reported once. */ function checkEndpointRuns(runs, warnings) {
2305
+ const reported = new Set();
2306
+ const warnOnce = (message)=>{
2307
+ const key = JSON.stringify(message);
2308
+ if (reported.has(key)) return;
2309
+ reported.add(key);
2310
+ warnings.push(message);
2311
+ };
2312
+ const keyless = {
2313
+ endpoints: [],
2314
+ entities: new Set()
2315
+ };
2316
+ for (const { endpoint, label, steps } of runs){
2317
+ if (endpoint.isTransactional) {
2318
+ if (steps.some((n)=>n.type === 'http_request')) warnings.push((0,_flusys_nestjs_shared_utils__rspack_import_0.messageRef)(_config_message_keys_js__rspack_import_8/* .FLOW_VALIDATION_MESSAGES.TRANSACTIONAL_HTTP */.qj.TRANSACTIONAL_HTTP, {
2319
+ node: label
2320
+ }));
2321
+ // Inside the run's transaction a message is only sent once it commits: a failed send is logged, it cannot take the error path.
2322
+ for (const n of steps)if ((0,_flow_types_js__rspack_import_6/* .isMessageNode */.SE)(n.type) && n.onError === 'continue') warnOnce((0,_flusys_nestjs_shared_utils__rspack_import_0.messageRef)(_config_message_keys_js__rspack_import_8/* .FLOW_VALIDATION_MESSAGES.MESSAGE_AFTER_COMMIT */.qj.MESSAGE_AFTER_COMMIT, {
2323
+ node: (0,_flow_types_js__rspack_import_6/* .nodeLabel */.dA)(n)
2324
+ }));
2325
+ }
2326
+ if (endpoint.authMode === 'public' && steps.some((n)=>(0,_flow_types_js__rspack_import_6/* .isMessageNode */.SE)(n.type))) warnOnce((0,_flusys_nestjs_shared_utils__rspack_import_0.messageRef)(_config_message_keys_js__rspack_import_8/* .FLOW_VALIDATION_MESSAGES.PUBLIC_MESSAGE */.qj.PUBLIC_MESSAGE));
2327
+ // 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.
2328
+ if (!_flow_types_js__rspack_import_6/* .KEYLESS_AUTH_MODES.has */.d7.has(endpoint.authMode)) continue;
2329
+ 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_8/* .FLOW_VALIDATION_MESSAGES.PERMISSION_WITHOUT_USER */.qj.PERMISSION_WITHOUT_USER));
2330
+ const entities = steps.flatMap(entitiesOf);
2331
+ if (!entities.length) continue;
2332
+ keyless.endpoints.push(endpoint.path ? `/${endpoint.path}` : '/');
2333
+ for (const code of entities)keyless.entities.add(code);
2334
+ }
2335
+ if (keyless.endpoints.length) warnings.push((0,_flusys_nestjs_shared_utils__rspack_import_0.messageRef)(_config_message_keys_js__rspack_import_8/* .FLOW_VALIDATION_MESSAGES.KEYLESS_ENTITY */.qj.KEYLESS_ENTITY, {
2336
+ endpoints: keyless.endpoints.join(', '),
2337
+ entities: [
2338
+ ...keyless.entities
2339
+ ].join(', ')
2340
+ }));
2341
+ }
1869
2342
  /** At least one Request step; at most one on the flow's own URL, the others on distinct paths. */ function checkTriggers(triggers, err) {
1870
- if (!triggers.length) return err((0,_flusys_nestjs_shared_utils__rspack_import_0.messageRef)(_config_message_keys_js__rspack_import_7/* .FLOW_VALIDATION_MESSAGES.TRIGGER_MISSING */.qj.TRIGGER_MISSING));
2343
+ if (!triggers.length) return err((0,_flusys_nestjs_shared_utils__rspack_import_0.messageRef)(_config_message_keys_js__rspack_import_8/* .FLOW_VALIDATION_MESSAGES.TRIGGER_MISSING */.qj.TRIGGER_MISSING));
1871
2344
  const main = triggers.filter((trigger)=>!(0,_flow_types_js__rspack_import_6/* .triggerPath */.um)(trigger));
1872
- if (main.length > 1) err((0,_flusys_nestjs_shared_utils__rspack_import_0.messageRef)(_config_message_keys_js__rspack_import_7/* .FLOW_VALIDATION_MESSAGES.TRIGGER_MAIN_DUPLICATE */.qj.TRIGGER_MAIN_DUPLICATE, {
2345
+ if (main.length > 1) err((0,_flusys_nestjs_shared_utils__rspack_import_0.messageRef)(_config_message_keys_js__rspack_import_8/* .FLOW_VALIDATION_MESSAGES.TRIGGER_MAIN_DUPLICATE */.qj.TRIGGER_MAIN_DUPLICATE, {
1873
2346
  count: main.length
1874
2347
  }));
1875
2348
  const paths = new Set();
1876
2349
  for (const trigger of triggers){
1877
2350
  const path = (0,_flow_types_js__rspack_import_6/* .triggerPath */.um)(trigger);
1878
2351
  if (!path) continue;
1879
- if (paths.has(path)) err((0,_flusys_nestjs_shared_utils__rspack_import_0.messageRef)(_config_message_keys_js__rspack_import_7/* .FLOW_VALIDATION_MESSAGES.TRIGGER_PATH_DUPLICATE */.qj.TRIGGER_PATH_DUPLICATE, {
2352
+ if (paths.has(path)) err((0,_flusys_nestjs_shared_utils__rspack_import_0.messageRef)(_config_message_keys_js__rspack_import_8/* .FLOW_VALIDATION_MESSAGES.TRIGGER_PATH_DUPLICATE */.qj.TRIGGER_PATH_DUPLICATE, {
1880
2353
  path
1881
2354
  }));
1882
2355
  paths.add(path);
1883
2356
  }
1884
2357
  }
1885
2358
  /**
1886
- * What the runtime would always refuse, caught on save: an API-key flow called by a flow running
1887
- * as the caller, a required input left out, a chain that comes back to this flow or nests deeper
2359
+ * What the runtime would always refuse, caught on save: an API-key URL (a flow has no key to send),
2360
+ * a required input left out, a chain that comes back to this flow or nests deeper
1888
2361
  * than the limit. An inactive flow and an input the called flow does not declare are warnings.
1889
- */ function checkCalledFlow(slug, endpoint, input, inputList, flow, known, at, err, warn) {
2362
+ */ function checkCalledFlow(node, flow, known, err, warn) {
2363
+ const { flowSlug: slug, endpoint, input, inputList } = node.config;
2364
+ const at = {
2365
+ node: (0,_flow_types_js__rspack_import_6/* .nodeLabel */.dA)(node)
2366
+ };
1890
2367
  const target = known.get(slug);
1891
2368
  const vars = {
1892
2369
  ...at,
1893
2370
  flow: slug
1894
2371
  };
1895
- if (!target.isActive) warn.push((0,_flusys_nestjs_shared_utils__rspack_import_0.messageRef)(_config_message_keys_js__rspack_import_7/* .FLOW_VALIDATION_MESSAGES.NODE_FLOW_INACTIVE */.qj.NODE_FLOW_INACTIVE, vars));
1896
- if (target.authMode === 'api_key') {
1897
- // A function runs as whoever calls it, so it fails only when a flow running as the caller does.
1898
- if (flow.authMode === 'internal') warn.push((0,_flusys_nestjs_shared_utils__rspack_import_0.messageRef)(_config_message_keys_js__rspack_import_7/* .FLOW_VALIDATION_MESSAGES.NODE_FLOW_API_KEY */.qj.NODE_FLOW_API_KEY, vars));
1899
- else if (flow.runAs === 'caller') err((0,_flusys_nestjs_shared_utils__rspack_import_0.messageRef)(_config_message_keys_js__rspack_import_7/* .FLOW_VALIDATION_MESSAGES.NODE_FLOW_API_KEY */.qj.NODE_FLOW_API_KEY, vars));
1900
- }
2372
+ if (!target.isActive) warn.push((0,_flusys_nestjs_shared_utils__rspack_import_0.messageRef)(_config_message_keys_js__rspack_import_8/* .FLOW_VALIDATION_MESSAGES.NODE_FLOW_INACTIVE */.qj.NODE_FLOW_INACTIVE, vars));
1901
2373
  const path = typeof endpoint === 'string' && endpoint ? endpoint : null;
1902
2374
  const entry = target.endpoints.find((e)=>e.path === path);
2375
+ // A flow has no key to send, so an API-key URL is never callable from one.
2376
+ if (entry?.authMode === 'api_key') err((0,_flusys_nestjs_shared_utils__rspack_import_0.messageRef)(_config_message_keys_js__rspack_import_8/* .FLOW_VALIDATION_MESSAGES.NODE_FLOW_API_KEY */.qj.NODE_FLOW_API_KEY, vars));
1903
2377
  if (!entry) {
1904
- err((0,_flusys_nestjs_shared_utils__rspack_import_0.messageRef)(path ? _config_message_keys_js__rspack_import_7/* .FLOW_VALIDATION_MESSAGES.NODE_FLOW_ENDPOINT_UNKNOWN */.qj.NODE_FLOW_ENDPOINT_UNKNOWN : _config_message_keys_js__rspack_import_7/* .FLOW_VALIDATION_MESSAGES.NODE_FLOW_ENDPOINT_REQUIRED */.qj.NODE_FLOW_ENDPOINT_REQUIRED, {
2378
+ err((0,_flusys_nestjs_shared_utils__rspack_import_0.messageRef)(path ? _config_message_keys_js__rspack_import_8/* .FLOW_VALIDATION_MESSAGES.NODE_FLOW_ENDPOINT_UNKNOWN */.qj.NODE_FLOW_ENDPOINT_UNKNOWN : _config_message_keys_js__rspack_import_8/* .FLOW_VALIDATION_MESSAGES.NODE_FLOW_ENDPOINT_REQUIRED */.qj.NODE_FLOW_ENDPOINT_REQUIRED, {
1905
2379
  ...vars,
1906
2380
  endpoint: path ?? ''
1907
2381
  }));
1908
2382
  } else if (entry.bodyType === 'list') {
1909
- if (inputList === undefined) err((0,_flusys_nestjs_shared_utils__rspack_import_0.messageRef)(_config_message_keys_js__rspack_import_7/* .FLOW_VALIDATION_MESSAGES.NODE_FLOW_INPUT_LIST_REQUIRED */.qj.NODE_FLOW_INPUT_LIST_REQUIRED, vars));
2383
+ if (inputList === undefined) err((0,_flusys_nestjs_shared_utils__rspack_import_0.messageRef)(_config_message_keys_js__rspack_import_8/* .FLOW_VALIDATION_MESSAGES.NODE_FLOW_INPUT_LIST_REQUIRED */.qj.NODE_FLOW_INPUT_LIST_REQUIRED, vars));
1910
2384
  } else if (inputList !== undefined) {
1911
- err((0,_flusys_nestjs_shared_utils__rspack_import_0.messageRef)(_config_message_keys_js__rspack_import_7/* .FLOW_VALIDATION_MESSAGES.NODE_FLOW_INPUT_LIST_UNEXPECTED */.qj.NODE_FLOW_INPUT_LIST_UNEXPECTED, vars));
2385
+ err((0,_flusys_nestjs_shared_utils__rspack_import_0.messageRef)(_config_message_keys_js__rspack_import_8/* .FLOW_VALIDATION_MESSAGES.NODE_FLOW_INPUT_LIST_UNEXPECTED */.qj.NODE_FLOW_INPUT_LIST_UNEXPECTED, vars));
1912
2386
  } else {
1913
2387
  checkCalledInputs(input, entry.inputSchema, vars, err, warn);
1914
2388
  }
1915
2389
  const loop = callPathTo(slug, flow.slug, known, new Set());
1916
- if (loop) return err((0,_flusys_nestjs_shared_utils__rspack_import_0.messageRef)(_config_message_keys_js__rspack_import_7/* .FLOW_VALIDATION_MESSAGES.NODE_FLOW_LOOP */.qj.NODE_FLOW_LOOP, {
2390
+ if (loop) return err((0,_flusys_nestjs_shared_utils__rspack_import_0.messageRef)(_config_message_keys_js__rspack_import_8/* .FLOW_VALIDATION_MESSAGES.NODE_FLOW_LOOP */.qj.NODE_FLOW_LOOP, {
1917
2391
  ...at,
1918
2392
  chain: [
1919
2393
  flow.slug,
1920
2394
  ...loop
1921
2395
  ].join(' > ')
1922
2396
  }));
1923
- if (1 + callDepth(slug, known, new Set()) > _flow_types_js__rspack_import_6/* .FLOW_LIMITS.MAX_CALL_DEPTH */.Or.MAX_CALL_DEPTH) err((0,_flusys_nestjs_shared_utils__rspack_import_0.messageRef)(_config_message_keys_js__rspack_import_7/* .FLOW_VALIDATION_MESSAGES.NODE_FLOW_TOO_DEEP */.qj.NODE_FLOW_TOO_DEEP, {
2397
+ if (1 + callDepth(slug, known, new Set()) > _flow_types_js__rspack_import_6/* .FLOW_LIMITS.MAX_CALL_DEPTH */.Or.MAX_CALL_DEPTH) err((0,_flusys_nestjs_shared_utils__rspack_import_0.messageRef)(_config_message_keys_js__rspack_import_8/* .FLOW_VALIDATION_MESSAGES.NODE_FLOW_TOO_DEEP */.qj.NODE_FLOW_TOO_DEEP, {
1924
2398
  ...at,
1925
2399
  max: _flow_types_js__rspack_import_6/* .FLOW_LIMITS.MAX_CALL_DEPTH */.Or.MAX_CALL_DEPTH
1926
2400
  }));
1927
2401
  }
2402
+ /**
2403
+ * What the Call flow steps of `caller` whose call chain reaches `slug` (that flow itself, or one
2404
+ * that calls it, however deep) would hit against the flows in `known`: the save errors of those
2405
+ * steps, and the flow they run being off (a warning on save, a failure at run).
2406
+ */ function callProblems(caller, slug, known) {
2407
+ const problems = [];
2408
+ for (const node of Array.isArray(caller.nodes) ? caller.nodes : []){
2409
+ const c = node?.config ?? {};
2410
+ if (node?.type !== 'call_flow' || typeof c.flowSlug !== 'string') continue;
2411
+ if (c.flowSlug !== slug && !callPathTo(c.flowSlug, slug, known, new Set())) continue;
2412
+ const at = {
2413
+ node: (0,_flow_types_js__rspack_import_6/* .nodeLabel */.dA)(node)
2414
+ };
2415
+ const target = known.get(c.flowSlug);
2416
+ if (!target) {
2417
+ problems.push((0,_flusys_nestjs_shared_utils__rspack_import_0.messageRef)(_config_message_keys_js__rspack_import_8/* .FLOW_VALIDATION_MESSAGES.NODE_FLOW_UNKNOWN */.qj.NODE_FLOW_UNKNOWN, {
2418
+ ...at,
2419
+ flow: c.flowSlug
2420
+ }));
2421
+ continue;
2422
+ }
2423
+ if (!target.isActive) problems.push((0,_flusys_nestjs_shared_utils__rspack_import_0.messageRef)(_config_message_keys_js__rspack_import_8/* .FLOW_VALIDATION_MESSAGES.NODE_FLOW_INACTIVE */.qj.NODE_FLOW_INACTIVE, {
2424
+ ...at,
2425
+ flow: c.flowSlug
2426
+ }));
2427
+ checkCalledFlow(node, caller, known, (m)=>problems.push(m), []);
2428
+ }
2429
+ return problems;
2430
+ }
1928
2431
  /** The named inputs sent to a flow whose body is an object: every required one given, the rest declared. */ function checkCalledInputs(input, schema, vars, err, warn) {
1929
2432
  const given = input && typeof input === 'object' && !Array.isArray(input) ? Object.keys(input) : [];
1930
2433
  if (schema.length) {
1931
2434
  const declared = new Set(schema.map((field)=>field.name));
1932
- for (const name of given)if (!declared.has(name)) warn.push((0,_flusys_nestjs_shared_utils__rspack_import_0.messageRef)(_config_message_keys_js__rspack_import_7/* .FLOW_VALIDATION_MESSAGES.NODE_FLOW_INPUT_UNDECLARED */.qj.NODE_FLOW_INPUT_UNDECLARED, {
2435
+ for (const name of given)if (!declared.has(name)) warn.push((0,_flusys_nestjs_shared_utils__rspack_import_0.messageRef)(_config_message_keys_js__rspack_import_8/* .FLOW_VALIDATION_MESSAGES.NODE_FLOW_INPUT_UNDECLARED */.qj.NODE_FLOW_INPUT_UNDECLARED, {
1933
2436
  ...vars,
1934
2437
  input: name
1935
2438
  }));
1936
2439
  }
1937
2440
  for (const field of schema){
1938
2441
  if (field.required && (field.default === undefined || field.default === null) && !given.includes(field.name)) {
1939
- err((0,_flusys_nestjs_shared_utils__rspack_import_0.messageRef)(_config_message_keys_js__rspack_import_7/* .FLOW_VALIDATION_MESSAGES.NODE_FLOW_INPUT_MISSING */.qj.NODE_FLOW_INPUT_MISSING, {
2442
+ err((0,_flusys_nestjs_shared_utils__rspack_import_0.messageRef)(_config_message_keys_js__rspack_import_8/* .FLOW_VALIDATION_MESSAGES.NODE_FLOW_INPUT_MISSING */.qj.NODE_FLOW_INPUT_MISSING, {
1940
2443
  ...vars,
1941
2444
  input: field.name
1942
2445
  }));
@@ -1966,42 +2469,36 @@ function conditionProblem(node, options) {
1966
2469
  running.delete(slug);
1967
2470
  return deepest;
1968
2471
  }
1969
- function validateInputSchema(flow, err) {
1970
- if (flow.bodyType !== undefined && !_flow_types_js__rspack_import_6/* .FLOW_BODY_TYPES.includes */.Y3.includes(flow.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, {
1971
- type: String(flow.bodyType)
1972
- }));
1973
- validateInputFields(flow.inputSchema ?? [], '', 1, err);
1974
- }
1975
2472
  /** One level of the request body: the top-level fields, or the fields of an object / list item at `prefix`. */ function validateInputFields(fields, prefix, depth, err) {
1976
- if (fields.length > _flow_types_js__rspack_import_6/* .FLOW_LIMITS.MAX_INPUT_FIELDS */.Or.MAX_INPUT_FIELDS) err((0,_flusys_nestjs_shared_utils__rspack_import_0.messageRef)(_config_message_keys_js__rspack_import_7/* .FLOW_VALIDATION_MESSAGES.INPUT_TOO_MANY */.qj.INPUT_TOO_MANY, {
2473
+ if (fields.length > _flow_types_js__rspack_import_6/* .FLOW_LIMITS.MAX_INPUT_FIELDS */.Or.MAX_INPUT_FIELDS) err((0,_flusys_nestjs_shared_utils__rspack_import_0.messageRef)(_config_message_keys_js__rspack_import_8/* .FLOW_VALIDATION_MESSAGES.INPUT_TOO_MANY */.qj.INPUT_TOO_MANY, {
1977
2474
  max: _flow_types_js__rspack_import_6/* .FLOW_LIMITS.MAX_INPUT_FIELDS */.Or.MAX_INPUT_FIELDS
1978
2475
  }));
1979
2476
  const seen = new Set();
1980
2477
  for (const field of fields){
1981
2478
  const name = `${prefix}${field?.name ?? ''}`;
1982
- if (!_flow_types_js__rspack_import_6/* .FLOW_INPUT_NAME_PATTERN.test */.fZ.test(field?.name ?? '')) err((0,_flusys_nestjs_shared_utils__rspack_import_0.messageRef)(_config_message_keys_js__rspack_import_7/* .FLOW_VALIDATION_MESSAGES.INPUT_NAME_INVALID */.qj.INPUT_NAME_INVALID, {
2479
+ if (!_flow_types_js__rspack_import_6/* .FLOW_INPUT_NAME_PATTERN.test */.fZ.test(field?.name ?? '')) err((0,_flusys_nestjs_shared_utils__rspack_import_0.messageRef)(_config_message_keys_js__rspack_import_8/* .FLOW_VALIDATION_MESSAGES.INPUT_NAME_INVALID */.qj.INPUT_NAME_INVALID, {
1983
2480
  name
1984
2481
  }));
1985
- else if (seen.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_DUPLICATE */.qj.INPUT_NAME_DUPLICATE, {
2482
+ else if (seen.has(field.name)) err((0,_flusys_nestjs_shared_utils__rspack_import_0.messageRef)(_config_message_keys_js__rspack_import_8/* .FLOW_VALIDATION_MESSAGES.INPUT_NAME_DUPLICATE */.qj.INPUT_NAME_DUPLICATE, {
1986
2483
  name
1987
2484
  }));
1988
2485
  seen.add(field?.name);
1989
- if (!_flow_types_js__rspack_import_6/* .FLOW_INPUT_TYPES.includes */.Q7.includes(field?.type)) err((0,_flusys_nestjs_shared_utils__rspack_import_0.messageRef)(_config_message_keys_js__rspack_import_7/* .FLOW_VALIDATION_MESSAGES.INPUT_TYPE_UNKNOWN */.qj.INPUT_TYPE_UNKNOWN, {
2486
+ if (!_flow_types_js__rspack_import_6/* .FLOW_INPUT_TYPES.includes */.Q7.includes(field?.type)) err((0,_flusys_nestjs_shared_utils__rspack_import_0.messageRef)(_config_message_keys_js__rspack_import_8/* .FLOW_VALIDATION_MESSAGES.INPUT_TYPE_UNKNOWN */.qj.INPUT_TYPE_UNKNOWN, {
1990
2487
  name,
1991
2488
  type: field?.type
1992
2489
  }));
1993
2490
  const itemType = field?.type === 'array' ? field.itemType : undefined;
1994
- if (itemType !== undefined && (itemType === 'array' || !_flow_types_js__rspack_import_6/* .FLOW_INPUT_TYPES.includes */.Q7.includes(itemType))) err((0,_flusys_nestjs_shared_utils__rspack_import_0.messageRef)(_config_message_keys_js__rspack_import_7/* .FLOW_VALIDATION_MESSAGES.INPUT_ITEM_TYPE_UNKNOWN */.qj.INPUT_ITEM_TYPE_UNKNOWN, {
2491
+ if (itemType !== undefined && (itemType === 'array' || !_flow_types_js__rspack_import_6/* .FLOW_INPUT_TYPES.includes */.Q7.includes(itemType))) err((0,_flusys_nestjs_shared_utils__rspack_import_0.messageRef)(_config_message_keys_js__rspack_import_8/* .FLOW_VALIDATION_MESSAGES.INPUT_ITEM_TYPE_UNKNOWN */.qj.INPUT_ITEM_TYPE_UNKNOWN, {
1995
2492
  name,
1996
2493
  type: itemType
1997
2494
  }));
1998
- if ((field?.type === 'choice' || itemType === 'choice') && !field.options?.length) err((0,_flusys_nestjs_shared_utils__rspack_import_0.messageRef)(_config_message_keys_js__rspack_import_7/* .FLOW_VALIDATION_MESSAGES.INPUT_CHOICE_NO_OPTIONS */.qj.INPUT_CHOICE_NO_OPTIONS, {
2495
+ if ((field?.type === 'choice' || itemType === 'choice') && !field.options?.length) err((0,_flusys_nestjs_shared_utils__rspack_import_0.messageRef)(_config_message_keys_js__rspack_import_8/* .FLOW_VALIDATION_MESSAGES.INPUT_CHOICE_NO_OPTIONS */.qj.INPUT_CHOICE_NO_OPTIONS, {
1999
2496
  name
2000
2497
  }));
2001
2498
  const holdsFields = field?.type === 'object' || itemType === 'object';
2002
2499
  if (!holdsFields || !Array.isArray(field.fields) || !field.fields.length) continue;
2003
2500
  if (depth >= _flow_types_js__rspack_import_6/* .FLOW_LIMITS.MAX_INPUT_DEPTH */.Or.MAX_INPUT_DEPTH) {
2004
- err((0,_flusys_nestjs_shared_utils__rspack_import_0.messageRef)(_config_message_keys_js__rspack_import_7/* .FLOW_VALIDATION_MESSAGES.INPUT_TOO_DEEP */.qj.INPUT_TOO_DEEP, {
2501
+ err((0,_flusys_nestjs_shared_utils__rspack_import_0.messageRef)(_config_message_keys_js__rspack_import_8/* .FLOW_VALIDATION_MESSAGES.INPUT_TOO_DEEP */.qj.INPUT_TOO_DEEP, {
2005
2502
  name,
2006
2503
  max: _flow_types_js__rspack_import_6/* .FLOW_LIMITS.MAX_INPUT_DEPTH */.Or.MAX_INPUT_DEPTH
2007
2504
  }));
@@ -2020,7 +2517,7 @@ function validateNodeConfig(node, flow, context, err, warn) {
2020
2517
  };
2021
2518
  const expr = (value, what)=>{
2022
2519
  const problem = expressionProblem(value, 0, shape);
2023
- if (problem) err((0,_flusys_nestjs_shared_utils__rspack_import_0.messageRef)(_config_message_keys_js__rspack_import_7/* .FLOW_VALIDATION_MESSAGES.NODE_EXPRESSION_PROBLEM */.qj.NODE_EXPRESSION_PROBLEM, {
2520
+ if (problem) err((0,_flusys_nestjs_shared_utils__rspack_import_0.messageRef)(_config_message_keys_js__rspack_import_8/* .FLOW_VALIDATION_MESSAGES.NODE_EXPRESSION_PROBLEM */.qj.NODE_EXPRESSION_PROBLEM, {
2024
2521
  ...at,
2025
2522
  what,
2026
2523
  problem
@@ -2028,7 +2525,7 @@ function validateNodeConfig(node, flow, context, err, warn) {
2028
2525
  };
2029
2526
  const exprMap = (value, kind)=>{
2030
2527
  if (value === undefined) return;
2031
- if (!value || typeof value !== 'object' || Array.isArray(value)) return err((0,_flusys_nestjs_shared_utils__rspack_import_0.messageRef)(_config_message_keys_js__rspack_import_7/* .FLOW_VALIDATION_MESSAGES.NODE_PAIRS_INVALID */.qj.NODE_PAIRS_INVALID, {
2528
+ if (!value || typeof value !== 'object' || Array.isArray(value)) return err((0,_flusys_nestjs_shared_utils__rspack_import_0.messageRef)(_config_message_keys_js__rspack_import_8/* .FLOW_VALIDATION_MESSAGES.NODE_PAIRS_INVALID */.qj.NODE_PAIRS_INVALID, {
2032
2529
  ...at,
2033
2530
  what: (0,_flusys_nestjs_shared_utils__rspack_import_0.messageRef)(kind.list)
2034
2531
  }));
@@ -2038,13 +2535,13 @@ function validateNodeConfig(node, flow, context, err, warn) {
2038
2535
  };
2039
2536
  const filterMap = (value, kind)=>{
2040
2537
  if (value === undefined) return;
2041
- if (!value || typeof value !== 'object' || Array.isArray(value)) return err((0,_flusys_nestjs_shared_utils__rspack_import_0.messageRef)(_config_message_keys_js__rspack_import_7/* .FLOW_VALIDATION_MESSAGES.NODE_PAIRS_INVALID */.qj.NODE_PAIRS_INVALID, {
2538
+ if (!value || typeof value !== 'object' || Array.isArray(value)) return err((0,_flusys_nestjs_shared_utils__rspack_import_0.messageRef)(_config_message_keys_js__rspack_import_8/* .FLOW_VALIDATION_MESSAGES.NODE_PAIRS_INVALID */.qj.NODE_PAIRS_INVALID, {
2042
2539
  ...at,
2043
2540
  what: (0,_flusys_nestjs_shared_utils__rspack_import_0.messageRef)(kind.list)
2044
2541
  }));
2045
2542
  for (const [key, item] of Object.entries(value)){
2046
2543
  const problem = filterValueProblem(item, -1, shape);
2047
- if (problem) err((0,_flusys_nestjs_shared_utils__rspack_import_0.messageRef)(_config_message_keys_js__rspack_import_7/* .FLOW_VALIDATION_MESSAGES.NODE_EXPRESSION_PROBLEM */.qj.NODE_EXPRESSION_PROBLEM, {
2544
+ if (problem) err((0,_flusys_nestjs_shared_utils__rspack_import_0.messageRef)(_config_message_keys_js__rspack_import_8/* .FLOW_VALIDATION_MESSAGES.NODE_EXPRESSION_PROBLEM */.qj.NODE_EXPRESSION_PROBLEM, {
2048
2545
  ...at,
2049
2546
  what: (0,_flusys_nestjs_shared_utils__rspack_import_0.messageRef)(kind.item, {
2050
2547
  name: key
@@ -2055,115 +2552,172 @@ function validateNodeConfig(node, flow, context, err, warn) {
2055
2552
  };
2056
2553
  const rule = (value, what)=>{
2057
2554
  const problem = groupProblem(value, 0, shape);
2058
- if (problem) err((0,_flusys_nestjs_shared_utils__rspack_import_0.messageRef)(_config_message_keys_js__rspack_import_7/* .FLOW_VALIDATION_MESSAGES.NODE_RULE_PROBLEM */.qj.NODE_RULE_PROBLEM, {
2555
+ if (problem) err((0,_flusys_nestjs_shared_utils__rspack_import_0.messageRef)(_config_message_keys_js__rspack_import_8/* .FLOW_VALIDATION_MESSAGES.NODE_RULE_PROBLEM */.qj.NODE_RULE_PROBLEM, {
2059
2556
  ...at,
2060
2557
  what,
2061
2558
  problem
2062
2559
  }));
2063
2560
  };
2064
2561
  const entity = (value)=>{
2065
- if (typeof value !== 'string' || !_config_entity_builder_constants_js__rspack_import_8/* .ENTITY_CODE_PATTERN.test */.vs.test(value)) return err((0,_flusys_nestjs_shared_utils__rspack_import_0.messageRef)(_config_message_keys_js__rspack_import_7/* .FLOW_VALIDATION_MESSAGES.NODE_CHOOSE_ENTITY */.qj.NODE_CHOOSE_ENTITY, at));
2066
- if (context.knownEntityCodes && !context.knownEntityCodes.has(value)) err((0,_flusys_nestjs_shared_utils__rspack_import_0.messageRef)(_config_message_keys_js__rspack_import_7/* .FLOW_VALIDATION_MESSAGES.NODE_ENTITY_UNKNOWN */.qj.NODE_ENTITY_UNKNOWN, {
2562
+ if (typeof value !== 'string' || !_config_entity_builder_constants_js__rspack_import_9/* .ENTITY_CODE_PATTERN.test */.vs.test(value)) return err((0,_flusys_nestjs_shared_utils__rspack_import_0.messageRef)(_config_message_keys_js__rspack_import_8/* .FLOW_VALIDATION_MESSAGES.NODE_CHOOSE_ENTITY */.qj.NODE_CHOOSE_ENTITY, at));
2563
+ if (context.knownEntityCodes && !context.knownEntityCodes.has(value)) err((0,_flusys_nestjs_shared_utils__rspack_import_0.messageRef)(_config_message_keys_js__rspack_import_8/* .FLOW_VALIDATION_MESSAGES.NODE_ENTITY_UNKNOWN */.qj.NODE_ENTITY_UNKNOWN, {
2067
2564
  ...at,
2068
2565
  entity: value
2069
2566
  }));
2070
2567
  };
2071
2568
  const KIND = {
2072
2569
  variable: {
2073
- list: _config_message_keys_js__rspack_import_7/* .FLOW_SUBJECT_MESSAGES.VARIABLES */.bc.VARIABLES,
2074
- item: _config_message_keys_js__rspack_import_7/* .FLOW_SUBJECT_MESSAGES.VARIABLE */.bc.VARIABLE
2570
+ list: _config_message_keys_js__rspack_import_8/* .FLOW_SUBJECT_MESSAGES.VARIABLES */.bc.VARIABLES,
2571
+ item: _config_message_keys_js__rspack_import_8/* .FLOW_SUBJECT_MESSAGES.VARIABLE */.bc.VARIABLE
2075
2572
  },
2076
2573
  field: {
2077
- list: _config_message_keys_js__rspack_import_7/* .FLOW_SUBJECT_MESSAGES.FIELDS */.bc.FIELDS,
2078
- item: _config_message_keys_js__rspack_import_7/* .FLOW_SUBJECT_MESSAGES.FIELD */.bc.FIELD
2574
+ list: _config_message_keys_js__rspack_import_8/* .FLOW_SUBJECT_MESSAGES.FIELDS */.bc.FIELDS,
2575
+ item: _config_message_keys_js__rspack_import_8/* .FLOW_SUBJECT_MESSAGES.FIELD */.bc.FIELD
2079
2576
  },
2080
2577
  filter: {
2081
- list: _config_message_keys_js__rspack_import_7/* .FLOW_SUBJECT_MESSAGES.FILTERS */.bc.FILTERS,
2082
- item: _config_message_keys_js__rspack_import_7/* .FLOW_SUBJECT_MESSAGES.FILTER */.bc.FILTER
2578
+ list: _config_message_keys_js__rspack_import_8/* .FLOW_SUBJECT_MESSAGES.FILTERS */.bc.FILTERS,
2579
+ item: _config_message_keys_js__rspack_import_8/* .FLOW_SUBJECT_MESSAGES.FILTER */.bc.FILTER
2083
2580
  },
2084
2581
  input: {
2085
- list: _config_message_keys_js__rspack_import_7/* .FLOW_SUBJECT_MESSAGES.INPUTS */.bc.INPUTS,
2086
- item: _config_message_keys_js__rspack_import_7/* .FLOW_SUBJECT_MESSAGES.INPUT */.bc.INPUT
2582
+ list: _config_message_keys_js__rspack_import_8/* .FLOW_SUBJECT_MESSAGES.INPUTS */.bc.INPUTS,
2583
+ item: _config_message_keys_js__rspack_import_8/* .FLOW_SUBJECT_MESSAGES.INPUT */.bc.INPUT
2087
2584
  },
2088
2585
  header: {
2089
- list: _config_message_keys_js__rspack_import_7/* .FLOW_SUBJECT_MESSAGES.HEADERS */.bc.HEADERS,
2090
- item: _config_message_keys_js__rspack_import_7/* .FLOW_SUBJECT_MESSAGES.HEADER */.bc.HEADER
2586
+ list: _config_message_keys_js__rspack_import_8/* .FLOW_SUBJECT_MESSAGES.HEADERS */.bc.HEADERS,
2587
+ item: _config_message_keys_js__rspack_import_8/* .FLOW_SUBJECT_MESSAGES.HEADER */.bc.HEADER
2091
2588
  },
2092
2589
  body: {
2093
- list: _config_message_keys_js__rspack_import_7/* .FLOW_SUBJECT_MESSAGES.BODY_VALUES */.bc.BODY_VALUES,
2094
- item: _config_message_keys_js__rspack_import_7/* .FLOW_SUBJECT_MESSAGES.BODY_VALUE */.bc.BODY_VALUE
2590
+ list: _config_message_keys_js__rspack_import_8/* .FLOW_SUBJECT_MESSAGES.BODY_VALUES */.bc.BODY_VALUES,
2591
+ item: _config_message_keys_js__rspack_import_8/* .FLOW_SUBJECT_MESSAGES.BODY_VALUE */.bc.BODY_VALUE
2095
2592
  },
2096
2593
  payload: {
2097
- list: _config_message_keys_js__rspack_import_7/* .FLOW_SUBJECT_MESSAGES.PAYLOAD_VALUES */.bc.PAYLOAD_VALUES,
2098
- item: _config_message_keys_js__rspack_import_7/* .FLOW_SUBJECT_MESSAGES.PAYLOAD_VALUE */.bc.PAYLOAD_VALUE
2594
+ list: _config_message_keys_js__rspack_import_8/* .FLOW_SUBJECT_MESSAGES.PAYLOAD_VALUES */.bc.PAYLOAD_VALUES,
2595
+ item: _config_message_keys_js__rspack_import_8/* .FLOW_SUBJECT_MESSAGES.PAYLOAD_VALUE */.bc.PAYLOAD_VALUE
2099
2596
  },
2100
2597
  response: {
2101
- list: _config_message_keys_js__rspack_import_7/* .FLOW_SUBJECT_MESSAGES.RESPONSE_VALUES */.bc.RESPONSE_VALUES,
2102
- item: _config_message_keys_js__rspack_import_7/* .FLOW_SUBJECT_MESSAGES.RESPONSE_VALUE */.bc.RESPONSE_VALUE
2598
+ list: _config_message_keys_js__rspack_import_8/* .FLOW_SUBJECT_MESSAGES.RESPONSE_VALUES */.bc.RESPONSE_VALUES,
2599
+ item: _config_message_keys_js__rspack_import_8/* .FLOW_SUBJECT_MESSAGES.RESPONSE_VALUE */.bc.RESPONSE_VALUE
2600
+ },
2601
+ data: {
2602
+ list: _config_message_keys_js__rspack_import_8/* .FLOW_SUBJECT_MESSAGES.DATA_VALUES */.bc.DATA_VALUES,
2603
+ item: _config_message_keys_js__rspack_import_8/* .FLOW_SUBJECT_MESSAGES.DATA_VALUE */.bc.DATA_VALUE
2604
+ },
2605
+ template: {
2606
+ list: _config_message_keys_js__rspack_import_8/* .FLOW_SUBJECT_MESSAGES.TEMPLATE_VALUES */.bc.TEMPLATE_VALUES,
2607
+ item: _config_message_keys_js__rspack_import_8/* .FLOW_SUBJECT_MESSAGES.TEMPLATE_VALUE */.bc.TEMPLATE_VALUE
2103
2608
  }
2104
2609
  };
2610
+ const optionalExpr = (value, what)=>{
2611
+ if (value !== undefined) expr(value, what);
2612
+ };
2613
+ /** An AND / OR rule of permission codes: one that names none needs a permission, a malformed one is `invalid`. */ const permissionRule = (value, invalid)=>{
2614
+ const problem = (0,_flusys_nestjs_shared_utils__rspack_import_0.permissionLogicProblem)(value);
2615
+ if (problem === 'empty') err((0,_flusys_nestjs_shared_utils__rspack_import_0.messageRef)(_config_message_keys_js__rspack_import_8/* .FLOW_VALIDATION_MESSAGES.NODE_PERMISSION_REQUIRED */.qj.NODE_PERMISSION_REQUIRED, at));
2616
+ else if (problem === 'too_many') err((0,_flusys_nestjs_shared_utils__rspack_import_0.messageRef)(_config_message_keys_js__rspack_import_8/* .RULE_ENGINE_MESSAGES.TOO_MANY_PERMISSION_CODES */.x3.TOO_MANY_PERMISSION_CODES, {
2617
+ max: _rule_engine_index_js__rspack_import_2/* .RULE_ENGINE_LIMITS.MAX_PERMISSION_CODES */.bw.MAX_PERMISSION_CODES
2618
+ }));
2619
+ else if (problem) err((0,_flusys_nestjs_shared_utils__rspack_import_0.messageRef)(invalid, at));
2620
+ };
2621
+ const serviceRunning = (service, unavailable)=>{
2622
+ if (context.messageServices && !context.messageServices[service]) err((0,_flusys_nestjs_shared_utils__rspack_import_0.messageRef)(unavailable, at));
2623
+ };
2105
2624
  switch(node.type){
2106
2625
  case 'trigger':
2107
2626
  {
2108
- if (c.path !== undefined && c.path !== '' && (typeof c.path !== 'string' || !_flow_types_js__rspack_import_6/* .FLOW_ENDPOINT_PATH_PATTERN.test */.YO.test(c.path))) err((0,_flusys_nestjs_shared_utils__rspack_import_0.messageRef)(_config_message_keys_js__rspack_import_7/* .FLOW_VALIDATION_MESSAGES.TRIGGER_PATH_INVALID */.qj.TRIGGER_PATH_INVALID, at));
2627
+ if (c.path !== undefined && c.path !== '' && (typeof c.path !== 'string' || !_flow_types_js__rspack_import_6/* .FLOW_ENDPOINT_PATH_PATTERN.test */.YO.test(c.path))) err((0,_flusys_nestjs_shared_utils__rspack_import_0.messageRef)(_config_message_keys_js__rspack_import_8/* .FLOW_VALIDATION_MESSAGES.TRIGGER_PATH_INVALID */.qj.TRIGGER_PATH_INVALID, at));
2628
+ if (c.authMode !== undefined && !_flow_types_js__rspack_import_6/* .FLOW_AUTH_MODES.includes */.fO.includes(c.authMode)) err((0,_flusys_nestjs_shared_utils__rspack_import_0.messageRef)(_config_message_keys_js__rspack_import_8/* .FLOW_VALIDATION_MESSAGES.TRIGGER_AUTH_INVALID */.qj.TRIGGER_AUTH_INVALID, at));
2629
+ if (c.permissions !== undefined) permissionRule(c.permissions, _config_message_keys_js__rspack_import_8/* .FLOW_VALIDATION_MESSAGES.TRIGGER_AUTH_INVALID */.qj.TRIGGER_AUTH_INVALID);
2630
+ if (c.isTransactional !== undefined && typeof c.isTransactional !== 'boolean') err((0,_flusys_nestjs_shared_utils__rspack_import_0.messageRef)(_config_message_keys_js__rspack_import_8/* .FLOW_VALIDATION_MESSAGES.TRIGGER_TRANSACTION_INVALID */.qj.TRIGGER_TRANSACTION_INVALID, at));
2631
+ if (c.timeoutMs !== undefined && !(Number.isInteger(c.timeoutMs) && c.timeoutMs >= _flow_types_js__rspack_import_6/* .FLOW_LIMITS.MIN_TIMEOUT_MS */.Or.MIN_TIMEOUT_MS && c.timeoutMs <= _flow_types_js__rspack_import_6/* .FLOW_LIMITS.MAX_TIMEOUT_MS */.Or.MAX_TIMEOUT_MS)) {
2632
+ err((0,_flusys_nestjs_shared_utils__rspack_import_0.messageRef)(_config_message_keys_js__rspack_import_8/* .FLOW_VALIDATION_MESSAGES.TRIGGER_TIMEOUT_RANGE */.qj.TRIGGER_TIMEOUT_RANGE, {
2633
+ ...at,
2634
+ min: _flow_types_js__rspack_import_6/* .FLOW_LIMITS.MIN_TIMEOUT_MS */.Or.MIN_TIMEOUT_MS / 1000,
2635
+ max: _flow_types_js__rspack_import_6/* .FLOW_LIMITS.MAX_TIMEOUT_MS */.Or.MAX_TIMEOUT_MS / 1000
2636
+ }));
2637
+ }
2638
+ if (c.rateLimitPerMinute !== undefined && !(Number.isInteger(c.rateLimitPerMinute) && c.rateLimitPerMinute >= 0 && c.rateLimitPerMinute <= _flow_types_js__rspack_import_6/* .FLOW_LIMITS.MAX_RATE_LIMIT_PER_MINUTE */.Or.MAX_RATE_LIMIT_PER_MINUTE)) {
2639
+ err((0,_flusys_nestjs_shared_utils__rspack_import_0.messageRef)(_config_message_keys_js__rspack_import_8/* .FLOW_VALIDATION_MESSAGES.TRIGGER_RATE_LIMIT_RANGE */.qj.TRIGGER_RATE_LIMIT_RANGE, {
2640
+ ...at,
2641
+ max: _flow_types_js__rspack_import_6/* .FLOW_LIMITS.MAX_RATE_LIMIT_PER_MINUTE */.Or.MAX_RATE_LIMIT_PER_MINUTE
2642
+ }));
2643
+ }
2644
+ // Without a key an API-key URL could never be called; any other access keeps none.
2645
+ if (c.authMode === 'api_key' && c.apiKey === undefined) err((0,_flusys_nestjs_shared_utils__rspack_import_0.messageRef)(_config_message_keys_js__rspack_import_8/* .FLOW_VALIDATION_MESSAGES.TRIGGER_API_KEY_REQUIRED */.qj.TRIGGER_API_KEY_REQUIRED, at));
2646
+ else if (c.apiKey !== undefined && !(0,_flow_api_key_js__rspack_import_7/* .looksLikeApiKey */.Jb)(c.apiKey) && !(0,_flow_types_js__rspack_import_6/* .isStoredApiKey */.xA)(c.apiKey)) err((0,_flusys_nestjs_shared_utils__rspack_import_0.messageRef)(_config_message_keys_js__rspack_import_8/* .FLOW_VALIDATION_MESSAGES.TRIGGER_API_KEY_INVALID */.qj.TRIGGER_API_KEY_INVALID, at));
2647
+ // Field errors are named after the URL (`<path>: <field>`) once a flow has several.
2109
2648
  const path = (0,_flow_types_js__rspack_import_6/* .triggerPath */.um)(node);
2110
- if (!path) break;
2111
- if (c.bodyType !== undefined && !_flow_types_js__rspack_import_6/* .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, {
2649
+ if (c.bodyType !== undefined && !_flow_types_js__rspack_import_6/* .FLOW_BODY_TYPES.includes */.Y3.includes(c.bodyType)) err((0,_flusys_nestjs_shared_utils__rspack_import_0.messageRef)(_config_message_keys_js__rspack_import_8/* .FLOW_VALIDATION_MESSAGES.BODY_TYPE_UNKNOWN */.qj.BODY_TYPE_UNKNOWN, {
2112
2650
  type: String(c.bodyType)
2113
2651
  }));
2114
- validateInputFields(Array.isArray(c.inputSchema) ? c.inputSchema : [], `${path}: `, 1, err);
2652
+ validateInputFields(Array.isArray(c.inputSchema) ? c.inputSchema : [], path ? `${path}: ` : '', 1, err);
2115
2653
  break;
2116
2654
  }
2117
2655
  case 'validate':
2118
2656
  validateChecks(c, at, rule, err);
2119
- if (c.mode !== undefined && c.mode !== 'all' && c.mode !== 'first') err((0,_flusys_nestjs_shared_utils__rspack_import_0.messageRef)(_config_message_keys_js__rspack_import_7/* .FLOW_VALIDATION_MESSAGES.NODE_MODE_INVALID */.qj.NODE_MODE_INVALID, at));
2657
+ if (c.mode !== undefined && c.mode !== 'all' && c.mode !== 'first') err((0,_flusys_nestjs_shared_utils__rspack_import_0.messageRef)(_config_message_keys_js__rspack_import_8/* .FLOW_VALIDATION_MESSAGES.NODE_MODE_INVALID */.qj.NODE_MODE_INVALID, at));
2120
2658
  if (c.statusCode !== undefined && !(Number.isInteger(c.statusCode) && c.statusCode >= 400 && c.statusCode <= 499)) {
2121
- err((0,_flusys_nestjs_shared_utils__rspack_import_0.messageRef)(_config_message_keys_js__rspack_import_7/* .FLOW_VALIDATION_MESSAGES.NODE_STATUS_4XX */.qj.NODE_STATUS_4XX, at));
2659
+ err((0,_flusys_nestjs_shared_utils__rspack_import_0.messageRef)(_config_message_keys_js__rspack_import_8/* .FLOW_VALIDATION_MESSAGES.NODE_STATUS_4XX */.qj.NODE_STATUS_4XX, at));
2122
2660
  }
2123
2661
  break;
2124
2662
  case 'permission_check':
2125
2663
  {
2126
- try {
2127
- if (!(0,_rule_engine_rule_permission_js__rspack_import_4/* .parsePermissionCodes */.PS)(c.codes).length) err((0,_flusys_nestjs_shared_utils__rspack_import_0.messageRef)(_config_message_keys_js__rspack_import_7/* .FLOW_VALIDATION_MESSAGES.NODE_PERMISSION_REQUIRED */.qj.NODE_PERMISSION_REQUIRED, at));
2128
- } catch {
2129
- err((0,_flusys_nestjs_shared_utils__rspack_import_0.messageRef)(_config_message_keys_js__rspack_import_7/* .RULE_ENGINE_MESSAGES.TOO_MANY_PERMISSION_CODES */.x3.TOO_MANY_PERMISSION_CODES, {
2130
- max: _rule_engine_index_js__rspack_import_2/* .RULE_ENGINE_LIMITS.MAX_PERMISSION_CODES */.bw.MAX_PERMISSION_CODES
2131
- }));
2664
+ if (c.permissions === undefined) err((0,_flusys_nestjs_shared_utils__rspack_import_0.messageRef)(_config_message_keys_js__rspack_import_8/* .FLOW_VALIDATION_MESSAGES.NODE_PERMISSION_REQUIRED */.qj.NODE_PERMISSION_REQUIRED, at));
2665
+ else permissionRule(c.permissions, _config_message_keys_js__rspack_import_8/* .FLOW_VALIDATION_MESSAGES.NODE_PERMISSION_OPTION_INVALID */.qj.NODE_PERMISSION_OPTION_INVALID);
2666
+ if (c.onDenied !== undefined && c.onDenied !== 'reject' && c.onDenied !== 'branch') err((0,_flusys_nestjs_shared_utils__rspack_import_0.messageRef)(_config_message_keys_js__rspack_import_8/* .FLOW_VALIDATION_MESSAGES.NODE_PERMISSION_OPTION_INVALID */.qj.NODE_PERMISSION_OPTION_INVALID, at));
2667
+ if (c.message !== undefined && typeof c.message !== 'string') err((0,_flusys_nestjs_shared_utils__rspack_import_0.messageRef)(_config_message_keys_js__rspack_import_8/* .FLOW_VALIDATION_MESSAGES.NODE_PERMISSION_OPTION_INVALID */.qj.NODE_PERMISSION_OPTION_INVALID, at));
2668
+ if (c.statusCode !== undefined && !(Number.isInteger(c.statusCode) && c.statusCode >= 400 && c.statusCode <= 499)) {
2669
+ err((0,_flusys_nestjs_shared_utils__rspack_import_0.messageRef)(_config_message_keys_js__rspack_import_8/* .FLOW_VALIDATION_MESSAGES.NODE_STATUS_4XX */.qj.NODE_STATUS_4XX, at));
2670
+ }
2671
+ break;
2672
+ }
2673
+ case 'company_branch_check':
2674
+ {
2675
+ if (c.subject !== undefined && !_flow_types_js__rspack_import_6/* .FLOW_ACCESS_SUBJECTS.includes */.iM.includes(c.subject)) err((0,_flusys_nestjs_shared_utils__rspack_import_0.messageRef)(_config_message_keys_js__rspack_import_8/* .FLOW_VALIDATION_MESSAGES.NODE_PERMISSION_OPTION_INVALID */.qj.NODE_PERMISSION_OPTION_INVALID, at));
2676
+ if (c.branchMatch !== undefined && !_flow_types_js__rspack_import_6/* .FLOW_BRANCH_MATCHES.includes */.Et.includes(c.branchMatch)) err((0,_flusys_nestjs_shared_utils__rspack_import_0.messageRef)(_config_message_keys_js__rspack_import_8/* .FLOW_VALIDATION_MESSAGES.NODE_PERMISSION_OPTION_INVALID */.qj.NODE_PERMISSION_OPTION_INVALID, at));
2677
+ if (c.subject === 'user') {
2678
+ if (c.userId === undefined) err((0,_flusys_nestjs_shared_utils__rspack_import_0.messageRef)(_config_message_keys_js__rspack_import_8/* .FLOW_VALIDATION_MESSAGES.NODE_ACCESS_USER_REQUIRED */.qj.NODE_ACCESS_USER_REQUIRED, at));
2679
+ else expr(c.userId, (0,_flusys_nestjs_shared_utils__rspack_import_0.messageRef)(_config_message_keys_js__rspack_import_8/* .FLOW_SUBJECT_MESSAGES.THE_USER */.bc.THE_USER));
2132
2680
  }
2133
- if (c.match !== undefined && c.match !== 'any' && c.match !== 'all') err((0,_flusys_nestjs_shared_utils__rspack_import_0.messageRef)(_config_message_keys_js__rspack_import_7/* .FLOW_VALIDATION_MESSAGES.NODE_PERMISSION_OPTION_INVALID */.qj.NODE_PERMISSION_OPTION_INVALID, at));
2134
- if (c.onDenied !== undefined && c.onDenied !== 'reject' && c.onDenied !== 'branch') err((0,_flusys_nestjs_shared_utils__rspack_import_0.messageRef)(_config_message_keys_js__rspack_import_7/* .FLOW_VALIDATION_MESSAGES.NODE_PERMISSION_OPTION_INVALID */.qj.NODE_PERMISSION_OPTION_INVALID, at));
2135
- if (c.branch !== undefined && c.branch !== null) expr(c.branch, (0,_flusys_nestjs_shared_utils__rspack_import_0.messageRef)(_config_message_keys_js__rspack_import_7/* .FLOW_SUBJECT_MESSAGES.THE_VALUE */.bc.THE_VALUE));
2136
- if (c.message !== undefined && typeof c.message !== 'string') err((0,_flusys_nestjs_shared_utils__rspack_import_0.messageRef)(_config_message_keys_js__rspack_import_7/* .FLOW_VALIDATION_MESSAGES.NODE_PERMISSION_OPTION_INVALID */.qj.NODE_PERMISSION_OPTION_INVALID, at));
2681
+ // Another user has no current company.
2682
+ if (c.branchCompany !== undefined && (!_flow_types_js__rspack_import_6/* .FLOW_BRANCH_COMPANIES.includes */.sk.includes(c.branchCompany) || c.branchCompany === 'current' && c.subject === 'user')) {
2683
+ err((0,_flusys_nestjs_shared_utils__rspack_import_0.messageRef)(_config_message_keys_js__rspack_import_8/* .FLOW_VALIDATION_MESSAGES.NODE_PERMISSION_OPTION_INVALID */.qj.NODE_PERMISSION_OPTION_INVALID, at));
2684
+ }
2685
+ if (c.companyIds !== undefined) expr(c.companyIds, (0,_flusys_nestjs_shared_utils__rspack_import_0.messageRef)(_config_message_keys_js__rspack_import_8/* .FLOW_SUBJECT_MESSAGES.THE_VALUE */.bc.THE_VALUE));
2686
+ if (c.branchIds !== undefined) expr(c.branchIds, (0,_flusys_nestjs_shared_utils__rspack_import_0.messageRef)(_config_message_keys_js__rspack_import_8/* .FLOW_SUBJECT_MESSAGES.THE_VALUE */.bc.THE_VALUE));
2687
+ if (c.onDenied !== undefined && c.onDenied !== 'reject' && c.onDenied !== 'branch') err((0,_flusys_nestjs_shared_utils__rspack_import_0.messageRef)(_config_message_keys_js__rspack_import_8/* .FLOW_VALIDATION_MESSAGES.NODE_PERMISSION_OPTION_INVALID */.qj.NODE_PERMISSION_OPTION_INVALID, at));
2688
+ // Without an id to check there is nothing to allow or deny.
2689
+ if (c.onDenied === 'branch' && c.companyIds === undefined && c.branchIds === undefined) err((0,_flusys_nestjs_shared_utils__rspack_import_0.messageRef)(_config_message_keys_js__rspack_import_8/* .FLOW_VALIDATION_MESSAGES.NODE_ACCESS_TARGET_REQUIRED */.qj.NODE_ACCESS_TARGET_REQUIRED, at));
2690
+ if (c.message !== undefined && typeof c.message !== 'string') err((0,_flusys_nestjs_shared_utils__rspack_import_0.messageRef)(_config_message_keys_js__rspack_import_8/* .FLOW_VALIDATION_MESSAGES.NODE_PERMISSION_OPTION_INVALID */.qj.NODE_PERMISSION_OPTION_INVALID, at));
2137
2691
  if (c.statusCode !== undefined && !(Number.isInteger(c.statusCode) && c.statusCode >= 400 && c.statusCode <= 499)) {
2138
- err((0,_flusys_nestjs_shared_utils__rspack_import_0.messageRef)(_config_message_keys_js__rspack_import_7/* .FLOW_VALIDATION_MESSAGES.NODE_STATUS_4XX */.qj.NODE_STATUS_4XX, at));
2692
+ err((0,_flusys_nestjs_shared_utils__rspack_import_0.messageRef)(_config_message_keys_js__rspack_import_8/* .FLOW_VALIDATION_MESSAGES.NODE_STATUS_4XX */.qj.NODE_STATUS_4XX, at));
2139
2693
  }
2140
2694
  break;
2141
2695
  }
2142
2696
  case 'set':
2143
2697
  exprMap(c.values, KIND.variable);
2144
- if (!c.values || !Object.keys(c.values).length) warn.push((0,_flusys_nestjs_shared_utils__rspack_import_0.messageRef)(_config_message_keys_js__rspack_import_7/* .FLOW_VALIDATION_MESSAGES.NODE_SETS_NOTHING */.qj.NODE_SETS_NOTHING, at));
2698
+ if (!c.values || !Object.keys(c.values).length) warn.push((0,_flusys_nestjs_shared_utils__rspack_import_0.messageRef)(_config_message_keys_js__rspack_import_8/* .FLOW_VALIDATION_MESSAGES.NODE_SETS_NOTHING */.qj.NODE_SETS_NOTHING, at));
2145
2699
  for (const key of Object.keys(c.values ?? {})){
2146
- if (!_flow_types_js__rspack_import_6/* .FLOW_INPUT_NAME_PATTERN.test */.fZ.test(key)) err((0,_flusys_nestjs_shared_utils__rspack_import_0.messageRef)(_config_message_keys_js__rspack_import_7/* .FLOW_VALIDATION_MESSAGES.NODE_VARIABLE_INVALID */.qj.NODE_VARIABLE_INVALID, {
2700
+ if (!_flow_types_js__rspack_import_6/* .FLOW_INPUT_NAME_PATTERN.test */.fZ.test(key)) err((0,_flusys_nestjs_shared_utils__rspack_import_0.messageRef)(_config_message_keys_js__rspack_import_8/* .FLOW_VALIDATION_MESSAGES.NODE_VARIABLE_INVALID */.qj.NODE_VARIABLE_INVALID, {
2147
2701
  ...at,
2148
2702
  name: key
2149
2703
  }));
2150
2704
  }
2151
2705
  break;
2152
2706
  case 'condition':
2153
- rule(c.rule, (0,_flusys_nestjs_shared_utils__rspack_import_0.messageRef)(_config_message_keys_js__rspack_import_7/* .FLOW_SUBJECT_MESSAGES.THE_RULE */.bc.THE_RULE));
2707
+ rule(c.rule, (0,_flusys_nestjs_shared_utils__rspack_import_0.messageRef)(_config_message_keys_js__rspack_import_8/* .FLOW_SUBJECT_MESSAGES.THE_RULE */.bc.THE_RULE));
2154
2708
  break;
2155
2709
  case 'switch':
2156
2710
  {
2157
- expr(c.value, (0,_flusys_nestjs_shared_utils__rspack_import_0.messageRef)(_config_message_keys_js__rspack_import_7/* .FLOW_SUBJECT_MESSAGES.THE_VALUE */.bc.THE_VALUE));
2711
+ expr(c.value, (0,_flusys_nestjs_shared_utils__rspack_import_0.messageRef)(_config_message_keys_js__rspack_import_8/* .FLOW_SUBJECT_MESSAGES.THE_VALUE */.bc.THE_VALUE));
2158
2712
  const cases = Array.isArray(c.cases) ? c.cases : [];
2159
- if (!cases.length) err((0,_flusys_nestjs_shared_utils__rspack_import_0.messageRef)(_config_message_keys_js__rspack_import_7/* .FLOW_VALIDATION_MESSAGES.NODE_CASE_REQUIRED */.qj.NODE_CASE_REQUIRED, at));
2713
+ if (!cases.length) err((0,_flusys_nestjs_shared_utils__rspack_import_0.messageRef)(_config_message_keys_js__rspack_import_8/* .FLOW_VALIDATION_MESSAGES.NODE_CASE_REQUIRED */.qj.NODE_CASE_REQUIRED, at));
2160
2714
  const ids = new Set();
2161
2715
  for (const item of cases){
2162
- if (typeof item.id !== 'string' || !/^[A-Za-z][A-Za-z0-9_]{0,29}$/.test(item.id) || item.id === 'default') err((0,_flusys_nestjs_shared_utils__rspack_import_0.messageRef)(_config_message_keys_js__rspack_import_7/* .FLOW_VALIDATION_MESSAGES.NODE_CASE_INVALID */.qj.NODE_CASE_INVALID, {
2716
+ if (typeof item.id !== 'string' || !/^[A-Za-z][A-Za-z0-9_]{0,29}$/.test(item.id) || item.id === 'default') err((0,_flusys_nestjs_shared_utils__rspack_import_0.messageRef)(_config_message_keys_js__rspack_import_8/* .FLOW_VALIDATION_MESSAGES.NODE_CASE_INVALID */.qj.NODE_CASE_INVALID, {
2163
2717
  ...at,
2164
2718
  name: String(item.id)
2165
2719
  }));
2166
- else if (ids.has(item.id)) err((0,_flusys_nestjs_shared_utils__rspack_import_0.messageRef)(_config_message_keys_js__rspack_import_7/* .FLOW_VALIDATION_MESSAGES.NODE_CASE_DUPLICATE */.qj.NODE_CASE_DUPLICATE, {
2720
+ else if (ids.has(item.id)) err((0,_flusys_nestjs_shared_utils__rspack_import_0.messageRef)(_config_message_keys_js__rspack_import_8/* .FLOW_VALIDATION_MESSAGES.NODE_CASE_DUPLICATE */.qj.NODE_CASE_DUPLICATE, {
2167
2721
  ...at,
2168
2722
  name: item.id
2169
2723
  }));
@@ -2172,10 +2726,10 @@ function validateNodeConfig(node, flow, context, err, warn) {
2172
2726
  break;
2173
2727
  }
2174
2728
  case 'foreach':
2175
- expr(c.items, (0,_flusys_nestjs_shared_utils__rspack_import_0.messageRef)(_config_message_keys_js__rspack_import_7/* .FLOW_SUBJECT_MESSAGES.THE_LIST */.bc.THE_LIST));
2176
- if (c.collect !== undefined) expr(c.collect, (0,_flusys_nestjs_shared_utils__rspack_import_0.messageRef)(_config_message_keys_js__rspack_import_7/* .FLOW_SUBJECT_MESSAGES.THE_COLLECTED_VALUE */.bc.THE_COLLECTED_VALUE));
2729
+ expr(c.items, (0,_flusys_nestjs_shared_utils__rspack_import_0.messageRef)(_config_message_keys_js__rspack_import_8/* .FLOW_SUBJECT_MESSAGES.THE_LIST */.bc.THE_LIST));
2730
+ if (c.collect !== undefined) expr(c.collect, (0,_flusys_nestjs_shared_utils__rspack_import_0.messageRef)(_config_message_keys_js__rspack_import_8/* .FLOW_SUBJECT_MESSAGES.THE_COLLECTED_VALUE */.bc.THE_COLLECTED_VALUE));
2177
2731
  if (c.maxItems !== undefined && !(Number.isInteger(c.maxItems) && c.maxItems >= 1 && c.maxItems <= _flow_types_js__rspack_import_6/* .FLOW_LIMITS.MAX_FOREACH_ITEMS */.Or.MAX_FOREACH_ITEMS)) {
2178
- err((0,_flusys_nestjs_shared_utils__rspack_import_0.messageRef)(_config_message_keys_js__rspack_import_7/* .FLOW_VALIDATION_MESSAGES.NODE_MAX_ITEMS_RANGE */.qj.NODE_MAX_ITEMS_RANGE, {
2732
+ err((0,_flusys_nestjs_shared_utils__rspack_import_0.messageRef)(_config_message_keys_js__rspack_import_8/* .FLOW_VALIDATION_MESSAGES.NODE_MAX_ITEMS_RANGE */.qj.NODE_MAX_ITEMS_RANGE, {
2179
2733
  ...at,
2180
2734
  max: _flow_types_js__rspack_import_6/* .FLOW_LIMITS.MAX_FOREACH_ITEMS */.Or.MAX_FOREACH_ITEMS
2181
2735
  }));
@@ -2228,30 +2782,34 @@ function validateNodeConfig(node, flow, context, err, warn) {
2228
2782
  break;
2229
2783
  case 'entity_get':
2230
2784
  entity(c.entityCode);
2231
- 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));
2785
+ expr(c.id, (0,_flusys_nestjs_shared_utils__rspack_import_0.messageRef)(_config_message_keys_js__rspack_import_8/* .FLOW_SUBJECT_MESSAGES.THE_RECORD_ID */.bc.THE_RECORD_ID));
2232
2786
  break;
2233
2787
  case 'entity_query':
2234
2788
  entity(c.entityCode);
2235
2789
  filterMap(c.filter, KIND.filter);
2236
- if (c.search !== undefined) expr(c.search, (0,_flusys_nestjs_shared_utils__rspack_import_0.messageRef)(_config_message_keys_js__rspack_import_7/* .FLOW_SUBJECT_MESSAGES.THE_SEARCH_TEXT */.bc.THE_SEARCH_TEXT));
2790
+ if (c.search !== undefined) expr(c.search, (0,_flusys_nestjs_shared_utils__rspack_import_0.messageRef)(_config_message_keys_js__rspack_import_8/* .FLOW_SUBJECT_MESSAGES.THE_SEARCH_TEXT */.bc.THE_SEARCH_TEXT));
2791
+ if (c.sort !== undefined) {
2792
+ const sort = c.sort;
2793
+ if (typeof sort?.field !== 'string' || !COLUMN_CODE.test(sort.field) || sort.direction !== undefined && sort.direction !== 'ASC' && sort.direction !== 'DESC') err((0,_flusys_nestjs_shared_utils__rspack_import_0.messageRef)(_config_message_keys_js__rspack_import_8/* .FLOW_VALIDATION_MESSAGES.NODE_SORT_INVALID */.qj.NODE_SORT_INVALID, at));
2794
+ }
2237
2795
  if (c.limit !== undefined && !(Number.isInteger(c.limit) && c.limit >= 1 && c.limit <= _flow_types_js__rspack_import_6/* .FLOW_LIMITS.MAX_QUERY_LIMIT */.Or.MAX_QUERY_LIMIT)) {
2238
- err((0,_flusys_nestjs_shared_utils__rspack_import_0.messageRef)(_config_message_keys_js__rspack_import_7/* .FLOW_VALIDATION_MESSAGES.NODE_LIMIT_RANGE */.qj.NODE_LIMIT_RANGE, {
2796
+ err((0,_flusys_nestjs_shared_utils__rspack_import_0.messageRef)(_config_message_keys_js__rspack_import_8/* .FLOW_VALIDATION_MESSAGES.NODE_LIMIT_RANGE */.qj.NODE_LIMIT_RANGE, {
2239
2797
  ...at,
2240
2798
  max: _flow_types_js__rspack_import_6/* .FLOW_LIMITS.MAX_QUERY_LIMIT */.Or.MAX_QUERY_LIMIT
2241
2799
  }));
2242
2800
  }
2243
2801
  break;
2244
2802
  case 'call_flow':
2245
- if (typeof c.flowSlug !== 'string' || !_flow_types_js__rspack_import_6/* .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));
2246
- else if (c.flowSlug === flow.slug) err((0,_flusys_nestjs_shared_utils__rspack_import_0.messageRef)(_config_message_keys_js__rspack_import_7/* .FLOW_VALIDATION_MESSAGES.NODE_CALLS_ITSELF */.qj.NODE_CALLS_ITSELF, at));
2247
- else if (context.knownFlows && !context.knownFlows.has(c.flowSlug)) err((0,_flusys_nestjs_shared_utils__rspack_import_0.messageRef)(_config_message_keys_js__rspack_import_7/* .FLOW_VALIDATION_MESSAGES.NODE_FLOW_UNKNOWN */.qj.NODE_FLOW_UNKNOWN, {
2803
+ if (typeof c.flowSlug !== 'string' || !_flow_types_js__rspack_import_6/* .FLOW_SLUG_PATTERN.test */.YD.test(c.flowSlug)) err((0,_flusys_nestjs_shared_utils__rspack_import_0.messageRef)(_config_message_keys_js__rspack_import_8/* .FLOW_VALIDATION_MESSAGES.NODE_CHOOSE_FLOW */.qj.NODE_CHOOSE_FLOW, at));
2804
+ else if (c.flowSlug === flow.slug) err((0,_flusys_nestjs_shared_utils__rspack_import_0.messageRef)(_config_message_keys_js__rspack_import_8/* .FLOW_VALIDATION_MESSAGES.NODE_CALLS_ITSELF */.qj.NODE_CALLS_ITSELF, at));
2805
+ else if (context.knownFlows && !context.knownFlows.has(c.flowSlug)) err((0,_flusys_nestjs_shared_utils__rspack_import_0.messageRef)(_config_message_keys_js__rspack_import_8/* .FLOW_VALIDATION_MESSAGES.NODE_FLOW_UNKNOWN */.qj.NODE_FLOW_UNKNOWN, {
2248
2806
  ...at,
2249
2807
  flow: c.flowSlug
2250
2808
  }));
2251
- else if (context.knownFlows) checkCalledFlow(c.flowSlug, c.endpoint, c.input, c.inputList, flow, context.knownFlows, at, err, warn);
2252
- if (c.endpoint !== undefined && c.endpoint !== '' && (typeof c.endpoint !== 'string' || !_flow_types_js__rspack_import_6/* .FLOW_ENDPOINT_PATH_PATTERN.test */.YO.test(c.endpoint))) err((0,_flusys_nestjs_shared_utils__rspack_import_0.messageRef)(_config_message_keys_js__rspack_import_7/* .FLOW_VALIDATION_MESSAGES.TRIGGER_PATH_INVALID */.qj.TRIGGER_PATH_INVALID, at));
2809
+ else if (context.knownFlows) checkCalledFlow(node, flow, context.knownFlows, err, warn);
2810
+ if (c.endpoint !== undefined && c.endpoint !== '' && (typeof c.endpoint !== 'string' || !_flow_types_js__rspack_import_6/* .FLOW_ENDPOINT_PATH_PATTERN.test */.YO.test(c.endpoint))) err((0,_flusys_nestjs_shared_utils__rspack_import_0.messageRef)(_config_message_keys_js__rspack_import_8/* .FLOW_VALIDATION_MESSAGES.TRIGGER_PATH_INVALID */.qj.TRIGGER_PATH_INVALID, at));
2253
2811
  exprMap(c.input, KIND.input);
2254
- if (c.inputList !== undefined) expr(c.inputList, (0,_flusys_nestjs_shared_utils__rspack_import_0.messageRef)(_config_message_keys_js__rspack_import_7/* .FLOW_SUBJECT_MESSAGES.THE_INPUT_LIST */.bc.THE_INPUT_LIST));
2812
+ if (c.inputList !== undefined) expr(c.inputList, (0,_flusys_nestjs_shared_utils__rspack_import_0.messageRef)(_config_message_keys_js__rspack_import_8/* .FLOW_SUBJECT_MESSAGES.THE_INPUT_LIST */.bc.THE_INPUT_LIST));
2255
2813
  break;
2256
2814
  case 'http_request':
2257
2815
  {
@@ -2261,12 +2819,13 @@ function validateNodeConfig(node, flow, context, err, warn) {
2261
2819
  'PUT',
2262
2820
  'PATCH',
2263
2821
  'DELETE'
2264
- ].includes(String(c.method))) err((0,_flusys_nestjs_shared_utils__rspack_import_0.messageRef)(_config_message_keys_js__rspack_import_7/* .FLOW_VALIDATION_MESSAGES.NODE_CHOOSE_METHOD */.qj.NODE_CHOOSE_METHOD, at));
2265
- expr(c.url, (0,_flusys_nestjs_shared_utils__rspack_import_0.messageRef)(_config_message_keys_js__rspack_import_7/* .FLOW_SUBJECT_MESSAGES.THE_URL */.bc.THE_URL));
2822
+ ].includes(String(c.method))) err((0,_flusys_nestjs_shared_utils__rspack_import_0.messageRef)(_config_message_keys_js__rspack_import_8/* .FLOW_VALIDATION_MESSAGES.NODE_CHOOSE_METHOD */.qj.NODE_CHOOSE_METHOD, at));
2823
+ expr(c.url, (0,_flusys_nestjs_shared_utils__rspack_import_0.messageRef)(_config_message_keys_js__rspack_import_8/* .FLOW_SUBJECT_MESSAGES.THE_URL */.bc.THE_URL));
2266
2824
  exprMap(c.headers, KIND.header);
2267
2825
  exprMap(c.body, KIND.body);
2826
+ if (c.forwardAuth === true) warn.push((0,_flusys_nestjs_shared_utils__rspack_import_0.messageRef)(_config_message_keys_js__rspack_import_8/* .FLOW_VALIDATION_MESSAGES.NODE_FORWARDS_TOKEN */.qj.NODE_FORWARDS_TOKEN, at));
2268
2827
  if (c.timeoutMs !== undefined && !(Number.isInteger(c.timeoutMs) && c.timeoutMs >= 100 && c.timeoutMs <= _flow_types_js__rspack_import_6/* .FLOW_LIMITS.MAX_HTTP_TIMEOUT_MS */.Or.MAX_HTTP_TIMEOUT_MS)) {
2269
- err((0,_flusys_nestjs_shared_utils__rspack_import_0.messageRef)(_config_message_keys_js__rspack_import_7/* .FLOW_VALIDATION_MESSAGES.NODE_TIMEOUT_RANGE */.qj.NODE_TIMEOUT_RANGE, {
2828
+ err((0,_flusys_nestjs_shared_utils__rspack_import_0.messageRef)(_config_message_keys_js__rspack_import_8/* .FLOW_VALIDATION_MESSAGES.NODE_TIMEOUT_RANGE */.qj.NODE_TIMEOUT_RANGE, {
2270
2829
  ...at,
2271
2830
  max: _flow_types_js__rspack_import_6/* .FLOW_LIMITS.MAX_HTTP_TIMEOUT_MS */.Or.MAX_HTTP_TIMEOUT_MS
2272
2831
  }));
@@ -2274,28 +2833,71 @@ function validateNodeConfig(node, flow, context, err, warn) {
2274
2833
  break;
2275
2834
  }
2276
2835
  case 'publish_event':
2277
- if (typeof c.eventName !== 'string' || !/^[a-z][a-z0-9_.-]{1,59}$/.test(c.eventName)) err((0,_flusys_nestjs_shared_utils__rspack_import_0.messageRef)(_config_message_keys_js__rspack_import_7/* .FLOW_VALIDATION_MESSAGES.NODE_EVENT_NAME_INVALID */.qj.NODE_EVENT_NAME_INVALID, at));
2836
+ if (typeof c.eventName !== 'string' || !/^[a-z][a-z0-9_.-]{1,59}$/.test(c.eventName)) err((0,_flusys_nestjs_shared_utils__rspack_import_0.messageRef)(_config_message_keys_js__rspack_import_8/* .FLOW_VALIDATION_MESSAGES.NODE_EVENT_NAME_INVALID */.qj.NODE_EVENT_NAME_INVALID, at));
2278
2837
  exprMap(c.payload, KIND.payload);
2279
2838
  break;
2839
+ case 'send_notification':
2840
+ {
2841
+ serviceRunning('notification', _config_message_keys_js__rspack_import_8/* .FLOW_VALIDATION_MESSAGES.NODE_NOTIFICATION_UNAVAILABLE */.qj.NODE_NOTIFICATION_UNAVAILABLE);
2842
+ const target = c.target ?? 'one';
2843
+ if (!_flow_types_js__rspack_import_6/* .FLOW_NOTIFICATION_TARGETS.includes */.as.includes(target)) err((0,_flusys_nestjs_shared_utils__rspack_import_0.messageRef)(_config_message_keys_js__rspack_import_8/* .FLOW_VALIDATION_MESSAGES.NODE_MESSAGE_TARGET_INVALID */.qj.NODE_MESSAGE_TARGET_INVALID, at));
2844
+ if (target === 'many') expr(c.items, (0,_flusys_nestjs_shared_utils__rspack_import_0.messageRef)(_config_message_keys_js__rspack_import_8/* .FLOW_SUBJECT_MESSAGES.THE_LIST */.bc.THE_LIST));
2845
+ if (target === 'company') {
2846
+ if (context.companyMembers === false) err((0,_flusys_nestjs_shared_utils__rspack_import_0.messageRef)(_config_message_keys_js__rspack_import_8/* .FLOW_VALIDATION_MESSAGES.NODE_NOTIFICATION_COMPANY_UNAVAILABLE */.qj.NODE_NOTIFICATION_COMPANY_UNAVAILABLE, at));
2847
+ expr(c.companyIds, (0,_flusys_nestjs_shared_utils__rspack_import_0.messageRef)(_config_message_keys_js__rspack_import_8/* .FLOW_SUBJECT_MESSAGES.THE_COMPANIES */.bc.THE_COMPANIES));
2848
+ optionalExpr(c.branchIds, (0,_flusys_nestjs_shared_utils__rspack_import_0.messageRef)(_config_message_keys_js__rspack_import_8/* .FLOW_SUBJECT_MESSAGES.THE_BRANCHES */.bc.THE_BRANCHES));
2849
+ } else {
2850
+ expr(c.userIds, (0,_flusys_nestjs_shared_utils__rspack_import_0.messageRef)(_config_message_keys_js__rspack_import_8/* .FLOW_SUBJECT_MESSAGES.THE_RECIPIENTS */.bc.THE_RECIPIENTS));
2851
+ optionalExpr(c.companyId, (0,_flusys_nestjs_shared_utils__rspack_import_0.messageRef)(_config_message_keys_js__rspack_import_8/* .FLOW_SUBJECT_MESSAGES.THE_COMPANY */.bc.THE_COMPANY));
2852
+ }
2853
+ expr(c.title, (0,_flusys_nestjs_shared_utils__rspack_import_0.messageRef)(_config_message_keys_js__rspack_import_8/* .FLOW_SUBJECT_MESSAGES.THE_TITLE */.bc.THE_TITLE));
2854
+ optionalExpr(c.message, (0,_flusys_nestjs_shared_utils__rspack_import_0.messageRef)(_config_message_keys_js__rspack_import_8/* .FLOW_SUBJECT_MESSAGES.THE_MESSAGE */.bc.THE_MESSAGE));
2855
+ exprMap(c.data, KIND.data);
2856
+ if (c.type !== undefined && !_flow_types_js__rspack_import_6/* .FLOW_NOTIFICATION_TYPES.includes */.LG.includes(c.type)) err((0,_flusys_nestjs_shared_utils__rspack_import_0.messageRef)(_config_message_keys_js__rspack_import_8/* .FLOW_VALIDATION_MESSAGES.NODE_NOTIFICATION_OPTION_INVALID */.qj.NODE_NOTIFICATION_OPTION_INVALID, at));
2857
+ if (c.realtime !== undefined && typeof c.realtime !== 'boolean') err((0,_flusys_nestjs_shared_utils__rspack_import_0.messageRef)(_config_message_keys_js__rspack_import_8/* .FLOW_VALIDATION_MESSAGES.NODE_NOTIFICATION_OPTION_INVALID */.qj.NODE_NOTIFICATION_OPTION_INVALID, at));
2858
+ break;
2859
+ }
2860
+ case 'send_email':
2861
+ {
2862
+ serviceRunning('email', _config_message_keys_js__rspack_import_8/* .FLOW_VALIDATION_MESSAGES.NODE_EMAIL_UNAVAILABLE */.qj.NODE_EMAIL_UNAVAILABLE);
2863
+ if (!_flow_types_js__rspack_import_6/* .FLOW_EMAIL_TARGETS.includes */.B9.includes(c.target ?? 'one')) err((0,_flusys_nestjs_shared_utils__rspack_import_0.messageRef)(_config_message_keys_js__rspack_import_8/* .FLOW_VALIDATION_MESSAGES.NODE_MESSAGE_TARGET_INVALID */.qj.NODE_MESSAGE_TARGET_INVALID, at));
2864
+ if (c.target === 'many') expr(c.items, (0,_flusys_nestjs_shared_utils__rspack_import_0.messageRef)(_config_message_keys_js__rspack_import_8/* .FLOW_SUBJECT_MESSAGES.THE_LIST */.bc.THE_LIST));
2865
+ expr(c.to, (0,_flusys_nestjs_shared_utils__rspack_import_0.messageRef)(_config_message_keys_js__rspack_import_8/* .FLOW_SUBJECT_MESSAGES.THE_RECIPIENTS */.bc.THE_RECIPIENTS));
2866
+ optionalExpr(c.cc, (0,_flusys_nestjs_shared_utils__rspack_import_0.messageRef)(_config_message_keys_js__rspack_import_8/* .FLOW_SUBJECT_MESSAGES.THE_CC */.bc.THE_CC));
2867
+ optionalExpr(c.bcc, (0,_flusys_nestjs_shared_utils__rspack_import_0.messageRef)(_config_message_keys_js__rspack_import_8/* .FLOW_SUBJECT_MESSAGES.THE_BCC */.bc.THE_BCC));
2868
+ optionalExpr(c.replyTo, (0,_flusys_nestjs_shared_utils__rspack_import_0.messageRef)(_config_message_keys_js__rspack_import_8/* .FLOW_SUBJECT_MESSAGES.THE_REPLY_TO */.bc.THE_REPLY_TO));
2869
+ if (c.mode !== undefined && !_flow_types_js__rspack_import_6/* .FLOW_EMAIL_MODES.includes */.v1.includes(c.mode)) err((0,_flusys_nestjs_shared_utils__rspack_import_0.messageRef)(_config_message_keys_js__rspack_import_8/* .FLOW_VALIDATION_MESSAGES.NODE_EMAIL_OPTION_INVALID */.qj.NODE_EMAIL_OPTION_INVALID, at));
2870
+ if (c.format !== undefined && !_flow_types_js__rspack_import_6/* .FLOW_EMAIL_FORMATS.includes */.FU.includes(c.format)) err((0,_flusys_nestjs_shared_utils__rspack_import_0.messageRef)(_config_message_keys_js__rspack_import_8/* .FLOW_VALIDATION_MESSAGES.NODE_EMAIL_OPTION_INVALID */.qj.NODE_EMAIL_OPTION_INVALID, at));
2871
+ if (c.emailConfigId !== undefined && c.emailConfigId !== '' && (typeof c.emailConfigId !== 'string' || !_flow_types_js__rspack_import_6/* .FLOW_ID_PATTERN.test */.E2.test(c.emailConfigId))) err((0,_flusys_nestjs_shared_utils__rspack_import_0.messageRef)(_config_message_keys_js__rspack_import_8/* .FLOW_VALIDATION_MESSAGES.NODE_EMAIL_OPTION_INVALID */.qj.NODE_EMAIL_OPTION_INVALID, at));
2872
+ if (c.mode === 'template') {
2873
+ const slug = typeof c.templateSlug === 'string' ? c.templateSlug.trim() : '';
2874
+ if (!slug || slug.length > _flow_types_js__rspack_import_6/* .FLOW_LIMITS.MAX_EMAIL_TEMPLATE_SLUG_LENGTH */.Or.MAX_EMAIL_TEMPLATE_SLUG_LENGTH) err((0,_flusys_nestjs_shared_utils__rspack_import_0.messageRef)(_config_message_keys_js__rspack_import_8/* .FLOW_VALIDATION_MESSAGES.NODE_EMAIL_TEMPLATE_REQUIRED */.qj.NODE_EMAIL_TEMPLATE_REQUIRED, at));
2875
+ exprMap(c.variables, KIND.template);
2876
+ } else {
2877
+ expr(c.subject, (0,_flusys_nestjs_shared_utils__rspack_import_0.messageRef)(_config_message_keys_js__rspack_import_8/* .FLOW_SUBJECT_MESSAGES.THE_SUBJECT */.bc.THE_SUBJECT));
2878
+ expr(c.body, (0,_flusys_nestjs_shared_utils__rspack_import_0.messageRef)(_config_message_keys_js__rspack_import_8/* .FLOW_SUBJECT_MESSAGES.THE_BODY */.bc.THE_BODY));
2879
+ }
2880
+ break;
2881
+ }
2280
2882
  case 'respond':
2281
2883
  {
2282
2884
  const status = c.statusCode;
2283
- 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));
2885
+ if (!(Number.isInteger(status) && status >= 200 && status <= 599)) err((0,_flusys_nestjs_shared_utils__rspack_import_0.messageRef)(_config_message_keys_js__rspack_import_8/* .FLOW_VALIDATION_MESSAGES.NODE_STATUS_2XX_5XX */.qj.NODE_STATUS_2XX_5XX, at));
2284
2886
  exprMap(c.body, KIND.response);
2285
- 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));
2887
+ if (c.bodyExpression !== undefined) expr(c.bodyExpression, (0,_flusys_nestjs_shared_utils__rspack_import_0.messageRef)(_config_message_keys_js__rspack_import_8/* .FLOW_SUBJECT_MESSAGES.THE_RESPONSE */.bc.THE_RESPONSE));
2286
2888
  break;
2287
2889
  }
2288
2890
  case 'commit':
2289
- if (node.onError === 'continue') err((0,_flusys_nestjs_shared_utils__rspack_import_0.messageRef)(_config_message_keys_js__rspack_import_7/* .FLOW_VALIDATION_MESSAGES.NODE_COMMIT_CONTINUE */.qj.NODE_COMMIT_CONTINUE, at));
2891
+ if (node.onError === 'continue') err((0,_flusys_nestjs_shared_utils__rspack_import_0.messageRef)(_config_message_keys_js__rspack_import_8/* .FLOW_VALIDATION_MESSAGES.NODE_COMMIT_CONTINUE */.qj.NODE_COMMIT_CONTINUE, at));
2290
2892
  break;
2291
2893
  case 'code':
2292
- if (typeof c.code !== 'string' || !c.code.trim()) err((0,_flusys_nestjs_shared_utils__rspack_import_0.messageRef)(_config_message_keys_js__rspack_import_7/* .FLOW_VALIDATION_MESSAGES.NODE_CODE_REQUIRED */.qj.NODE_CODE_REQUIRED, at));
2293
- else if (c.code.length > _flow_types_js__rspack_import_6/* .FLOW_LIMITS.MAX_CODE_LENGTH */.Or.MAX_CODE_LENGTH) err((0,_flusys_nestjs_shared_utils__rspack_import_0.messageRef)(_config_message_keys_js__rspack_import_7/* .FLOW_VALIDATION_MESSAGES.NODE_CODE_TOO_LONG */.qj.NODE_CODE_TOO_LONG, {
2894
+ if (typeof c.code !== 'string' || !c.code.trim()) err((0,_flusys_nestjs_shared_utils__rspack_import_0.messageRef)(_config_message_keys_js__rspack_import_8/* .FLOW_VALIDATION_MESSAGES.NODE_CODE_REQUIRED */.qj.NODE_CODE_REQUIRED, at));
2895
+ else if (c.code.length > _flow_types_js__rspack_import_6/* .FLOW_LIMITS.MAX_CODE_LENGTH */.Or.MAX_CODE_LENGTH) err((0,_flusys_nestjs_shared_utils__rspack_import_0.messageRef)(_config_message_keys_js__rspack_import_8/* .FLOW_VALIDATION_MESSAGES.NODE_CODE_TOO_LONG */.qj.NODE_CODE_TOO_LONG, {
2294
2896
  ...at,
2295
2897
  max: _flow_types_js__rspack_import_6/* .FLOW_LIMITS.MAX_CODE_LENGTH */.Or.MAX_CODE_LENGTH
2296
2898
  }));
2297
2899
  if (c.timeoutMs !== undefined && !(Number.isInteger(c.timeoutMs) && c.timeoutMs >= _flow_types_js__rspack_import_6/* .FLOW_LIMITS.MIN_CODE_TIMEOUT_MS */.Or.MIN_CODE_TIMEOUT_MS && c.timeoutMs <= _flow_types_js__rspack_import_6/* .FLOW_LIMITS.MAX_CODE_TIMEOUT_MS */.Or.MAX_CODE_TIMEOUT_MS)) {
2298
- err((0,_flusys_nestjs_shared_utils__rspack_import_0.messageRef)(_config_message_keys_js__rspack_import_7/* .FLOW_VALIDATION_MESSAGES.NODE_CODE_TIMEOUT_RANGE */.qj.NODE_CODE_TIMEOUT_RANGE, {
2900
+ err((0,_flusys_nestjs_shared_utils__rspack_import_0.messageRef)(_config_message_keys_js__rspack_import_8/* .FLOW_VALIDATION_MESSAGES.NODE_CODE_TIMEOUT_RANGE */.qj.NODE_CODE_TIMEOUT_RANGE, {
2299
2901
  ...at,
2300
2902
  min: _flow_types_js__rspack_import_6/* .FLOW_LIMITS.MIN_CODE_TIMEOUT_MS */.Or.MIN_CODE_TIMEOUT_MS,
2301
2903
  max: _flow_types_js__rspack_import_6/* .FLOW_LIMITS.MAX_CODE_TIMEOUT_MS */.Or.MAX_CODE_TIMEOUT_MS
@@ -2306,19 +2908,19 @@ function validateNodeConfig(node, flow, context, err, warn) {
2306
2908
  }
2307
2909
  /** `target` and what it needs: one - an id; many - a list (the id is optional, the item's own id by default); filter - a filter and a limit. */ function writeTargetConfig(c, targets, notFound, { at, expr, filterMap, err }) {
2308
2910
  const target = c.target ?? 'one';
2309
- if (typeof target !== 'string' || !targets.includes(target)) return err((0,_flusys_nestjs_shared_utils__rspack_import_0.messageRef)(_config_message_keys_js__rspack_import_7/* .FLOW_VALIDATION_MESSAGES.NODE_WRITE_TARGET_INVALID */.qj.NODE_WRITE_TARGET_INVALID, at));
2310
- if (c.onNotFound !== undefined && !notFound.includes(c.onNotFound)) err((0,_flusys_nestjs_shared_utils__rspack_import_0.messageRef)(_config_message_keys_js__rspack_import_7/* .FLOW_VALIDATION_MESSAGES.NODE_NOT_FOUND_INVALID */.qj.NODE_NOT_FOUND_INVALID, at));
2911
+ if (typeof target !== 'string' || !targets.includes(target)) return err((0,_flusys_nestjs_shared_utils__rspack_import_0.messageRef)(_config_message_keys_js__rspack_import_8/* .FLOW_VALIDATION_MESSAGES.NODE_WRITE_TARGET_INVALID */.qj.NODE_WRITE_TARGET_INVALID, at));
2912
+ if (c.onNotFound !== undefined && !notFound.includes(c.onNotFound)) err((0,_flusys_nestjs_shared_utils__rspack_import_0.messageRef)(_config_message_keys_js__rspack_import_8/* .FLOW_VALIDATION_MESSAGES.NODE_NOT_FOUND_INVALID */.qj.NODE_NOT_FOUND_INVALID, at));
2311
2913
  const needsId = notFound.length > 0;
2312
- if (target === 'one' && needsId) 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));
2914
+ if (target === 'one' && needsId) expr(c.id, (0,_flusys_nestjs_shared_utils__rspack_import_0.messageRef)(_config_message_keys_js__rspack_import_8/* .FLOW_SUBJECT_MESSAGES.THE_RECORD_ID */.bc.THE_RECORD_ID));
2313
2915
  if (target === 'many') {
2314
- expr(c.items, (0,_flusys_nestjs_shared_utils__rspack_import_0.messageRef)(_config_message_keys_js__rspack_import_7/* .FLOW_SUBJECT_MESSAGES.THE_LIST */.bc.THE_LIST));
2315
- if (needsId && c.id !== undefined) 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));
2916
+ expr(c.items, (0,_flusys_nestjs_shared_utils__rspack_import_0.messageRef)(_config_message_keys_js__rspack_import_8/* .FLOW_SUBJECT_MESSAGES.THE_LIST */.bc.THE_LIST));
2917
+ if (needsId && c.id !== undefined) expr(c.id, (0,_flusys_nestjs_shared_utils__rspack_import_0.messageRef)(_config_message_keys_js__rspack_import_8/* .FLOW_SUBJECT_MESSAGES.THE_RECORD_ID */.bc.THE_RECORD_ID));
2316
2918
  }
2317
2919
  if (target === 'filter') {
2318
2920
  filterMap(c.filter);
2319
- if (!c.filter || typeof c.filter !== 'object' || !Object.keys(c.filter).length) err((0,_flusys_nestjs_shared_utils__rspack_import_0.messageRef)(_config_message_keys_js__rspack_import_7/* .FLOW_VALIDATION_MESSAGES.NODE_FILTER_REQUIRED */.qj.NODE_FILTER_REQUIRED, at));
2921
+ if (!c.filter || typeof c.filter !== 'object' || !Object.keys(c.filter).length) err((0,_flusys_nestjs_shared_utils__rspack_import_0.messageRef)(_config_message_keys_js__rspack_import_8/* .FLOW_VALIDATION_MESSAGES.NODE_FILTER_REQUIRED */.qj.NODE_FILTER_REQUIRED, at));
2320
2922
  if (c.limit !== undefined && !(Number.isInteger(c.limit) && c.limit >= 1 && c.limit <= _flow_types_js__rspack_import_6/* .FLOW_LIMITS.MAX_WRITE_ITEMS */.Or.MAX_WRITE_ITEMS)) {
2321
- err((0,_flusys_nestjs_shared_utils__rspack_import_0.messageRef)(_config_message_keys_js__rspack_import_7/* .FLOW_VALIDATION_MESSAGES.NODE_LIMIT_RANGE */.qj.NODE_LIMIT_RANGE, {
2923
+ err((0,_flusys_nestjs_shared_utils__rspack_import_0.messageRef)(_config_message_keys_js__rspack_import_8/* .FLOW_VALIDATION_MESSAGES.NODE_LIMIT_RANGE */.qj.NODE_LIMIT_RANGE, {
2322
2924
  ...at,
2323
2925
  max: _flow_types_js__rspack_import_6/* .FLOW_LIMITS.MAX_WRITE_ITEMS */.Or.MAX_WRITE_ITEMS
2324
2926
  }));
@@ -2327,45 +2929,45 @@ function validateNodeConfig(node, flow, context, err, warn) {
2327
2929
  }
2328
2930
  function childInserts(value, { at, entity, expr, err }) {
2329
2931
  if (value === undefined) return;
2330
- if (!Array.isArray(value)) return err((0,_flusys_nestjs_shared_utils__rspack_import_0.messageRef)(_config_message_keys_js__rspack_import_7/* .FLOW_VALIDATION_MESSAGES.NODE_CHILDREN_INVALID */.qj.NODE_CHILDREN_INVALID, at));
2331
- if (value.length > _flow_types_js__rspack_import_6/* .FLOW_LIMITS.MAX_CHILD_INSERTS */.Or.MAX_CHILD_INSERTS) err((0,_flusys_nestjs_shared_utils__rspack_import_0.messageRef)(_config_message_keys_js__rspack_import_7/* .FLOW_VALIDATION_MESSAGES.NODE_CHILDREN_TOO_MANY */.qj.NODE_CHILDREN_TOO_MANY, {
2932
+ if (!Array.isArray(value)) return err((0,_flusys_nestjs_shared_utils__rspack_import_0.messageRef)(_config_message_keys_js__rspack_import_8/* .FLOW_VALIDATION_MESSAGES.NODE_CHILDREN_INVALID */.qj.NODE_CHILDREN_INVALID, at));
2933
+ if (value.length > _flow_types_js__rspack_import_6/* .FLOW_LIMITS.MAX_CHILD_INSERTS */.Or.MAX_CHILD_INSERTS) err((0,_flusys_nestjs_shared_utils__rspack_import_0.messageRef)(_config_message_keys_js__rspack_import_8/* .FLOW_VALIDATION_MESSAGES.NODE_CHILDREN_TOO_MANY */.qj.NODE_CHILDREN_TOO_MANY, {
2332
2934
  ...at,
2333
2935
  max: _flow_types_js__rspack_import_6/* .FLOW_LIMITS.MAX_CHILD_INSERTS */.Or.MAX_CHILD_INSERTS
2334
2936
  }));
2335
2937
  const names = new Set();
2336
2938
  for (const child of value){
2337
2939
  if (!child || typeof child !== 'object') {
2338
- err((0,_flusys_nestjs_shared_utils__rspack_import_0.messageRef)(_config_message_keys_js__rspack_import_7/* .FLOW_VALIDATION_MESSAGES.NODE_CHILDREN_INVALID */.qj.NODE_CHILDREN_INVALID, at));
2940
+ err((0,_flusys_nestjs_shared_utils__rspack_import_0.messageRef)(_config_message_keys_js__rspack_import_8/* .FLOW_VALIDATION_MESSAGES.NODE_CHILDREN_INVALID */.qj.NODE_CHILDREN_INVALID, at));
2339
2941
  continue;
2340
2942
  }
2341
2943
  const name = String(child.as ?? '');
2342
- if (!_flow_types_js__rspack_import_6/* .FLOW_INPUT_NAME_PATTERN.test */.fZ.test(name)) err((0,_flusys_nestjs_shared_utils__rspack_import_0.messageRef)(_config_message_keys_js__rspack_import_7/* .FLOW_VALIDATION_MESSAGES.NODE_CHILD_NAME_INVALID */.qj.NODE_CHILD_NAME_INVALID, {
2944
+ if (!_flow_types_js__rspack_import_6/* .FLOW_INPUT_NAME_PATTERN.test */.fZ.test(name)) err((0,_flusys_nestjs_shared_utils__rspack_import_0.messageRef)(_config_message_keys_js__rspack_import_8/* .FLOW_VALIDATION_MESSAGES.NODE_CHILD_NAME_INVALID */.qj.NODE_CHILD_NAME_INVALID, {
2343
2945
  ...at,
2344
2946
  name
2345
2947
  }));
2346
- else if (names.has(name)) err((0,_flusys_nestjs_shared_utils__rspack_import_0.messageRef)(_config_message_keys_js__rspack_import_7/* .FLOW_VALIDATION_MESSAGES.NODE_CHILD_NAME_DUPLICATE */.qj.NODE_CHILD_NAME_DUPLICATE, {
2948
+ else if (names.has(name)) err((0,_flusys_nestjs_shared_utils__rspack_import_0.messageRef)(_config_message_keys_js__rspack_import_8/* .FLOW_VALIDATION_MESSAGES.NODE_CHILD_NAME_DUPLICATE */.qj.NODE_CHILD_NAME_DUPLICATE, {
2347
2949
  ...at,
2348
2950
  name
2349
2951
  }));
2350
2952
  names.add(name);
2351
2953
  entity(child.entityCode);
2352
- if (typeof child.parentField !== 'string' || !COLUMN_CODE.test(child.parentField)) err((0,_flusys_nestjs_shared_utils__rspack_import_0.messageRef)(_config_message_keys_js__rspack_import_7/* .FLOW_VALIDATION_MESSAGES.NODE_CHILD_PARENT_FIELD_REQUIRED */.qj.NODE_CHILD_PARENT_FIELD_REQUIRED, {
2954
+ if (typeof child.parentField !== 'string' || !COLUMN_CODE.test(child.parentField)) err((0,_flusys_nestjs_shared_utils__rspack_import_0.messageRef)(_config_message_keys_js__rspack_import_8/* .FLOW_VALIDATION_MESSAGES.NODE_CHILD_PARENT_FIELD_REQUIRED */.qj.NODE_CHILD_PARENT_FIELD_REQUIRED, {
2353
2955
  ...at,
2354
2956
  name
2355
2957
  }));
2356
- expr(child.items, (0,_flusys_nestjs_shared_utils__rspack_import_0.messageRef)(_config_message_keys_js__rspack_import_7/* .FLOW_SUBJECT_MESSAGES.CHILD_LIST */.bc.CHILD_LIST, {
2958
+ expr(child.items, (0,_flusys_nestjs_shared_utils__rspack_import_0.messageRef)(_config_message_keys_js__rspack_import_8/* .FLOW_SUBJECT_MESSAGES.CHILD_LIST */.bc.CHILD_LIST, {
2357
2959
  name
2358
2960
  }));
2359
2961
  const fields = child.fields;
2360
2962
  if (fields === undefined) continue;
2361
2963
  if (!fields || typeof fields !== 'object' || Array.isArray(fields)) {
2362
- err((0,_flusys_nestjs_shared_utils__rspack_import_0.messageRef)(_config_message_keys_js__rspack_import_7/* .FLOW_VALIDATION_MESSAGES.NODE_PAIRS_INVALID */.qj.NODE_PAIRS_INVALID, {
2964
+ err((0,_flusys_nestjs_shared_utils__rspack_import_0.messageRef)(_config_message_keys_js__rspack_import_8/* .FLOW_VALIDATION_MESSAGES.NODE_PAIRS_INVALID */.qj.NODE_PAIRS_INVALID, {
2363
2965
  ...at,
2364
- what: (0,_flusys_nestjs_shared_utils__rspack_import_0.messageRef)(_config_message_keys_js__rspack_import_7/* .FLOW_SUBJECT_MESSAGES.FIELDS */.bc.FIELDS)
2966
+ what: (0,_flusys_nestjs_shared_utils__rspack_import_0.messageRef)(_config_message_keys_js__rspack_import_8/* .FLOW_SUBJECT_MESSAGES.FIELDS */.bc.FIELDS)
2365
2967
  }));
2366
2968
  continue;
2367
2969
  }
2368
- for (const [key, item] of Object.entries(fields))expr(item, (0,_flusys_nestjs_shared_utils__rspack_import_0.messageRef)(_config_message_keys_js__rspack_import_7/* .FLOW_SUBJECT_MESSAGES.CHILD_FIELD */.bc.CHILD_FIELD, {
2970
+ for (const [key, item] of Object.entries(fields))expr(item, (0,_flusys_nestjs_shared_utils__rspack_import_0.messageRef)(_config_message_keys_js__rspack_import_8/* .FLOW_SUBJECT_MESSAGES.CHILD_FIELD */.bc.CHILD_FIELD, {
2369
2971
  name: `${name}.${key}`
2370
2972
  }));
2371
2973
  }
@@ -2373,7 +2975,8 @@ function childInserts(value, { at, entity, expr, err }) {
2373
2975
  /**
2374
2976
  * How many loop levels each part of a node's config runs inside, beyond the loops around the node:
2375
2977
  * a foreach's `collect` runs in its own loop; a many / filter write's per-record expressions in one
2376
- * of the node's own; child records one deeper than their parent record.
2978
+ * of the node's own; child records one deeper than their parent record; a many message step's values
2979
+ * (all but its list) one per item.
2377
2980
  */ function configScopes(node) {
2378
2981
  const config = node.config ?? {};
2379
2982
  if (node.type === 'foreach') {
@@ -2391,14 +2994,29 @@ function childInserts(value, { at, entity, expr, err }) {
2391
2994
  }
2392
2995
  ];
2393
2996
  }
2394
- if (!WRITE_NODE_TYPES.has(node.type)) return [
2395
- {
2396
- value: config,
2397
- extra: 0,
2398
- ownLoop: false
2399
- }
2400
- ];
2401
- const { fields, id, children, ...rest } = config;
2997
+ if ((0,_flow_types_js__rspack_import_6/* .isMessageNode */.SE)(node.type) && config['target'] === 'many') {
2998
+ const { items, ...perItem } = config;
2999
+ return [
3000
+ {
3001
+ value: items,
3002
+ extra: 0,
3003
+ ownLoop: false
3004
+ },
3005
+ {
3006
+ value: perItem,
3007
+ extra: 1,
3008
+ ownLoop: false
3009
+ }
3010
+ ];
3011
+ }
3012
+ if (!WRITE_NODE_TYPES.has(node.type)) return [
3013
+ {
3014
+ value: config,
3015
+ extra: 0,
3016
+ ownLoop: false
3017
+ }
3018
+ ];
3019
+ const { fields, id, children, ...rest } = config;
2402
3020
  const perRecord = (rest.target ?? 'one') === 'one' ? 0 : 1;
2403
3021
  const scopes = [
2404
3022
  {
@@ -2429,32 +3047,32 @@ function childInserts(value, { at, entity, expr, err }) {
2429
3047
  return scopes;
2430
3048
  }
2431
3049
  function validateChecks(c, at, rule, err) {
2432
- if (!Array.isArray(c.checks) || !c.checks.length) return err((0,_flusys_nestjs_shared_utils__rspack_import_0.messageRef)(_config_message_keys_js__rspack_import_7/* .FLOW_VALIDATION_MESSAGES.NODE_CHECK_REQUIRED */.qj.NODE_CHECK_REQUIRED, at));
2433
- if (c.checks.length > _flow_types_js__rspack_import_6/* .FLOW_LIMITS.MAX_VALIDATE_CHECKS */.Or.MAX_VALIDATE_CHECKS) err((0,_flusys_nestjs_shared_utils__rspack_import_0.messageRef)(_config_message_keys_js__rspack_import_7/* .FLOW_VALIDATION_MESSAGES.NODE_TOO_MANY_CHECKS */.qj.NODE_TOO_MANY_CHECKS, {
3050
+ if (!Array.isArray(c.checks) || !c.checks.length) return err((0,_flusys_nestjs_shared_utils__rspack_import_0.messageRef)(_config_message_keys_js__rspack_import_8/* .FLOW_VALIDATION_MESSAGES.NODE_CHECK_REQUIRED */.qj.NODE_CHECK_REQUIRED, at));
3051
+ if (c.checks.length > _flow_types_js__rspack_import_6/* .FLOW_LIMITS.MAX_VALIDATE_CHECKS */.Or.MAX_VALIDATE_CHECKS) err((0,_flusys_nestjs_shared_utils__rspack_import_0.messageRef)(_config_message_keys_js__rspack_import_8/* .FLOW_VALIDATION_MESSAGES.NODE_TOO_MANY_CHECKS */.qj.NODE_TOO_MANY_CHECKS, {
2434
3052
  ...at,
2435
3053
  max: _flow_types_js__rspack_import_6/* .FLOW_LIMITS.MAX_VALIDATE_CHECKS */.Or.MAX_VALIDATE_CHECKS
2436
3054
  }));
2437
3055
  const ids = new Set();
2438
3056
  for (const check of c.checks){
2439
3057
  const id = check?.id;
2440
- if (typeof id !== 'string' || !CHECK_ID.test(id)) err((0,_flusys_nestjs_shared_utils__rspack_import_0.messageRef)(_config_message_keys_js__rspack_import_7/* .FLOW_VALIDATION_MESSAGES.NODE_CHECK_ID_INVALID */.qj.NODE_CHECK_ID_INVALID, {
3058
+ if (typeof id !== 'string' || !CHECK_ID.test(id)) err((0,_flusys_nestjs_shared_utils__rspack_import_0.messageRef)(_config_message_keys_js__rspack_import_8/* .FLOW_VALIDATION_MESSAGES.NODE_CHECK_ID_INVALID */.qj.NODE_CHECK_ID_INVALID, {
2441
3059
  ...at,
2442
3060
  name: String(id)
2443
3061
  }));
2444
- else if (ids.has(id)) err((0,_flusys_nestjs_shared_utils__rspack_import_0.messageRef)(_config_message_keys_js__rspack_import_7/* .FLOW_VALIDATION_MESSAGES.NODE_CHECK_ID_DUPLICATE */.qj.NODE_CHECK_ID_DUPLICATE, {
3062
+ else if (ids.has(id)) err((0,_flusys_nestjs_shared_utils__rspack_import_0.messageRef)(_config_message_keys_js__rspack_import_8/* .FLOW_VALIDATION_MESSAGES.NODE_CHECK_ID_DUPLICATE */.qj.NODE_CHECK_ID_DUPLICATE, {
2445
3063
  ...at,
2446
3064
  name: id
2447
3065
  }));
2448
3066
  else ids.add(id);
2449
3067
  const name = String(id);
2450
- rule(check?.rule, (0,_flusys_nestjs_shared_utils__rspack_import_0.messageRef)(_config_message_keys_js__rspack_import_7/* .FLOW_SUBJECT_MESSAGES.THE_CHECK */.bc.THE_CHECK, {
3068
+ rule(check?.rule, (0,_flusys_nestjs_shared_utils__rspack_import_0.messageRef)(_config_message_keys_js__rspack_import_8/* .FLOW_SUBJECT_MESSAGES.THE_CHECK */.bc.THE_CHECK, {
2451
3069
  name
2452
3070
  }));
2453
- if (typeof check?.message !== 'string' || !check.message.trim()) err((0,_flusys_nestjs_shared_utils__rspack_import_0.messageRef)(_config_message_keys_js__rspack_import_7/* .FLOW_VALIDATION_MESSAGES.NODE_CHECK_MESSAGE_REQUIRED */.qj.NODE_CHECK_MESSAGE_REQUIRED, {
3071
+ if (typeof check?.message !== 'string' || !check.message.trim()) err((0,_flusys_nestjs_shared_utils__rspack_import_0.messageRef)(_config_message_keys_js__rspack_import_8/* .FLOW_VALIDATION_MESSAGES.NODE_CHECK_MESSAGE_REQUIRED */.qj.NODE_CHECK_MESSAGE_REQUIRED, {
2454
3072
  ...at,
2455
3073
  name
2456
3074
  }));
2457
- if (check?.field !== undefined && (typeof check.field !== 'string' || !check.field.trim() || check.field.length > 100)) err((0,_flusys_nestjs_shared_utils__rspack_import_0.messageRef)(_config_message_keys_js__rspack_import_7/* .FLOW_VALIDATION_MESSAGES.NODE_CHECK_FIELD_INVALID */.qj.NODE_CHECK_FIELD_INVALID, {
3075
+ if (check?.field !== undefined && (typeof check.field !== 'string' || !check.field.trim() || check.field.length > 100)) err((0,_flusys_nestjs_shared_utils__rspack_import_0.messageRef)(_config_message_keys_js__rspack_import_8/* .FLOW_VALIDATION_MESSAGES.NODE_CHECK_FIELD_INVALID */.qj.NODE_CHECK_FIELD_INVALID, {
2458
3076
  ...at,
2459
3077
  name
2460
3078
  }));
@@ -2475,21 +3093,21 @@ function validateChecks(c, at, rule, err) {
2475
3093
  for (const ref of refs){
2476
3094
  const [scope, second] = ref.split('.');
2477
3095
  if (!REF_SCOPES.has(scope)) {
2478
- err((0,_flusys_nestjs_shared_utils__rspack_import_0.messageRef)(_config_message_keys_js__rspack_import_7/* .FLOW_VALIDATION_MESSAGES.REF_BAD_SCOPE */.qj.REF_BAD_SCOPE, {
3096
+ err((0,_flusys_nestjs_shared_utils__rspack_import_0.messageRef)(_config_message_keys_js__rspack_import_8/* .FLOW_VALIDATION_MESSAGES.REF_BAD_SCOPE */.qj.REF_BAD_SCOPE, {
2479
3097
  node: (0,_flow_types_js__rspack_import_6/* .nodeLabel */.dA)(node),
2480
3098
  ref
2481
3099
  }));
2482
3100
  } else if (scope === 'context' && second && !byId.has(second)) {
2483
- err((0,_flusys_nestjs_shared_utils__rspack_import_0.messageRef)(_config_message_keys_js__rspack_import_7/* .FLOW_VALIDATION_MESSAGES.REF_NODE_MISSING */.qj.REF_NODE_MISSING, {
3101
+ err((0,_flusys_nestjs_shared_utils__rspack_import_0.messageRef)(_config_message_keys_js__rspack_import_8/* .FLOW_VALIDATION_MESSAGES.REF_NODE_MISSING */.qj.REF_NODE_MISSING, {
2484
3102
  node: (0,_flow_types_js__rspack_import_6/* .nodeLabel */.dA)(node),
2485
3103
  target: second
2486
3104
  }));
2487
3105
  } else if (scope === 'context' && second === node.id) {
2488
- err((0,_flusys_nestjs_shared_utils__rspack_import_0.messageRef)(_config_message_keys_js__rspack_import_7/* .FLOW_VALIDATION_MESSAGES.REF_SELF */.qj.REF_SELF, {
3106
+ err((0,_flusys_nestjs_shared_utils__rspack_import_0.messageRef)(_config_message_keys_js__rspack_import_8/* .FLOW_VALIDATION_MESSAGES.REF_SELF */.qj.REF_SELF, {
2489
3107
  node: (0,_flow_types_js__rspack_import_6/* .nodeLabel */.dA)(node)
2490
3108
  }));
2491
3109
  } else if (scope === 'input' && second && namesKnown && !declaredInputs.has(second)) {
2492
- warnings.push((0,_flusys_nestjs_shared_utils__rspack_import_0.messageRef)(_config_message_keys_js__rspack_import_7/* .FLOW_VALIDATION_MESSAGES.REF_INPUT_UNDECLARED */.qj.REF_INPUT_UNDECLARED, {
3110
+ warnings.push((0,_flusys_nestjs_shared_utils__rspack_import_0.messageRef)(_config_message_keys_js__rspack_import_8/* .FLOW_VALIDATION_MESSAGES.REF_INPUT_UNDECLARED */.qj.REF_INPUT_UNDECLARED, {
2493
3111
  node: (0,_flow_types_js__rspack_import_6/* .nodeLabel */.dA)(node),
2494
3112
  input: second
2495
3113
  }));
@@ -2505,15 +3123,28 @@ function refsOf(value, refs = [], depth = 0) {
2505
3123
  return refs;
2506
3124
  }
2507
3125
  /**
2508
- * Writes that do not share a transaction, and Save progress steps that would not commit: in a flow
2509
- * without the transaction setting, inside a loop (once per item), or in a flow other flows call
2510
- * (inside its caller's transaction it is skipped).
2511
- */ function checkTransactionUse(flow, nodes, outgoing, reachable, context, warnings) {
2512
- const live = nodes.filter((n)=>!reachable || reachable.has(n.id));
2513
- const writes = live.filter((n)=>WRITE_NODE_TYPES.has(n.type)).length;
2514
- if (!flow.isTransactional && writes >= 2) warnings.push((0,_flusys_nestjs_shared_utils__rspack_import_0.messageRef)(_config_message_keys_js__rspack_import_7/* .FLOW_VALIDATION_MESSAGES.WRITES_NOT_TRANSACTIONAL */.qj.WRITES_NOT_TRANSACTIONAL, {
2515
- count: writes
2516
- }));
3126
+ * Writes that do not share a transaction, and Save progress steps that would not commit: reached
3127
+ * from a Request step without the transaction setting, inside a loop (once per item), or in a flow
3128
+ * other flows call (inside its caller's transaction it is skipped).
3129
+ */ function checkTransactionUse(flow, runs, outgoing, context, warnings) {
3130
+ for (const { endpoint, label, steps } of runs){
3131
+ if (endpoint.isTransactional) continue;
3132
+ const writes = steps.filter((n)=>WRITE_NODE_TYPES.has(n.type)).length;
3133
+ if (writes >= 2) warnings.push((0,_flusys_nestjs_shared_utils__rspack_import_0.messageRef)(_config_message_keys_js__rspack_import_8/* .FLOW_VALIDATION_MESSAGES.WRITES_NOT_TRANSACTIONAL */.qj.WRITES_NOT_TRANSACTIONAL, {
3134
+ count: writes,
3135
+ node: label
3136
+ }));
3137
+ for (const commit of steps)if (commit.type === 'commit') warnings.push((0,_flusys_nestjs_shared_utils__rspack_import_0.messageRef)(_config_message_keys_js__rspack_import_8/* .FLOW_VALIDATION_MESSAGES.COMMIT_NOT_TRANSACTIONAL */.qj.COMMIT_NOT_TRANSACTIONAL, {
3138
+ node: (0,_flow_types_js__rspack_import_6/* .nodeLabel */.dA)(commit),
3139
+ endpoint: label
3140
+ }));
3141
+ }
3142
+ const live = [
3143
+ ...new Map(runs.flatMap((run)=>run.steps).map((n)=>[
3144
+ n.id,
3145
+ n
3146
+ ])).values()
3147
+ ];
2517
3148
  const commits = live.filter((n)=>n.type === 'commit');
2518
3149
  if (!commits.length) return;
2519
3150
  const loops = live.filter((n)=>n.type === 'foreach').map((n)=>({
@@ -2527,13 +3158,12 @@ function refsOf(value, refs = [], depth = 0) {
2527
3158
  const at = {
2528
3159
  node: (0,_flow_types_js__rspack_import_6/* .nodeLabel */.dA)(commit)
2529
3160
  };
2530
- if (!flow.isTransactional) warnings.push((0,_flusys_nestjs_shared_utils__rspack_import_0.messageRef)(_config_message_keys_js__rspack_import_7/* .FLOW_VALIDATION_MESSAGES.COMMIT_NOT_TRANSACTIONAL */.qj.COMMIT_NOT_TRANSACTIONAL, at));
2531
3161
  const loop = loops.find((l)=>l.body.has(commit.id));
2532
- if (loop) warnings.push((0,_flusys_nestjs_shared_utils__rspack_import_0.messageRef)(_config_message_keys_js__rspack_import_7/* .FLOW_VALIDATION_MESSAGES.COMMIT_IN_LOOP */.qj.COMMIT_IN_LOOP, {
3162
+ if (loop) warnings.push((0,_flusys_nestjs_shared_utils__rspack_import_0.messageRef)(_config_message_keys_js__rspack_import_8/* .FLOW_VALIDATION_MESSAGES.COMMIT_IN_LOOP */.qj.COMMIT_IN_LOOP, {
2533
3163
  ...at,
2534
3164
  loop: (0,_flow_types_js__rspack_import_6/* .nodeLabel */.dA)(loop.node)
2535
3165
  }));
2536
- if (called) warnings.push((0,_flusys_nestjs_shared_utils__rspack_import_0.messageRef)(_config_message_keys_js__rspack_import_7/* .FLOW_VALIDATION_MESSAGES.COMMIT_IN_CALLED_FLOW */.qj.COMMIT_IN_CALLED_FLOW, at));
3166
+ if (called) warnings.push((0,_flusys_nestjs_shared_utils__rspack_import_0.messageRef)(_config_message_keys_js__rspack_import_8/* .FLOW_VALIDATION_MESSAGES.COMMIT_IN_CALLED_FLOW */.qj.COMMIT_IN_CALLED_FLOW, at));
2537
3167
  }
2538
3168
  }
2539
3169
  /** Every node a loop's `each` branch runs, nested loops included. */ function loopBody(loopId, outgoing) {
@@ -2636,14 +3266,14 @@ function refsOf(value, refs = [], depth = 0) {
2636
3266
  const [scope, target, field] = segs;
2637
3267
  if (scope === 'loop') {
2638
3268
  const parents = segs.slice(1).findIndex((s)=>s !== 'parent');
2639
- if (!depth) warn(_config_message_keys_js__rspack_import_7/* .FLOW_VALIDATION_MESSAGES.REF_LOOP_OUTSIDE */.qj.REF_LOOP_OUTSIDE, {});
2640
- else if ((parents < 0 ? segs.length - 1 : parents) >= depth) warn(_config_message_keys_js__rspack_import_7/* .FLOW_VALIDATION_MESSAGES.REF_LOOP_PARENT_MISSING */.qj.REF_LOOP_PARENT_MISSING, {});
3269
+ if (!depth) warn(_config_message_keys_js__rspack_import_8/* .FLOW_VALIDATION_MESSAGES.REF_LOOP_OUTSIDE */.qj.REF_LOOP_OUTSIDE, {});
3270
+ else if ((parents < 0 ? segs.length - 1 : parents) >= depth) warn(_config_message_keys_js__rspack_import_8/* .FLOW_VALIDATION_MESSAGES.REF_LOOP_PARENT_MISSING */.qj.REF_LOOP_PARENT_MISSING, {});
2641
3271
  }
2642
3272
  if (scope !== 'context' || !target || target === node.id || !byId.has(target)) continue;
2643
- if (!available.has(target)) warn(_config_message_keys_js__rspack_import_7/* .FLOW_VALIDATION_MESSAGES.REF_NODE_NOT_YET */.qj.REF_NODE_NOT_YET, {
3273
+ if (!available.has(target)) warn(_config_message_keys_js__rspack_import_8/* .FLOW_VALIDATION_MESSAGES.REF_NODE_NOT_YET */.qj.REF_NODE_NOT_YET, {
2644
3274
  target: nameOf(target)
2645
3275
  });
2646
- else if (field === 'results' && around.has(target)) warn(_config_message_keys_js__rspack_import_7/* .FLOW_VALIDATION_MESSAGES.REF_LOOP_RESULTS_EARLY */.qj.REF_LOOP_RESULTS_EARLY, {
3276
+ else if (field === 'results' && around.has(target)) warn(_config_message_keys_js__rspack_import_8/* .FLOW_VALIDATION_MESSAGES.REF_LOOP_RESULTS_EARLY */.qj.REF_LOOP_RESULTS_EARLY, {
2647
3277
  loop: nameOf(target)
2648
3278
  });
2649
3279
  }
@@ -2698,14 +3328,15 @@ function reach(start, outgoing) {
2698
3328
  nesting.set(id, max);
2699
3329
  return max;
2700
3330
  };
2701
- if (nodes.some((n)=>n.type === 'trigger' && nestingFrom(n.id) > _flow_types_js__rspack_import_6/* .FLOW_LIMITS.MAX_LOOP_DEPTH */.Or.MAX_LOOP_DEPTH)) err((0,_flusys_nestjs_shared_utils__rspack_import_0.messageRef)(_config_message_keys_js__rspack_import_7/* .FLOW_VALIDATION_MESSAGES.LOOP_TOO_DEEP */.qj.LOOP_TOO_DEEP, {
3331
+ if (nodes.some((n)=>n.type === 'trigger' && nestingFrom(n.id) > _flow_types_js__rspack_import_6/* .FLOW_LIMITS.MAX_LOOP_DEPTH */.Or.MAX_LOOP_DEPTH)) err((0,_flusys_nestjs_shared_utils__rspack_import_0.messageRef)(_config_message_keys_js__rspack_import_8/* .FLOW_VALIDATION_MESSAGES.LOOP_TOO_DEEP */.qj.LOOP_TOO_DEEP, {
2702
3332
  max: _flow_types_js__rspack_import_6/* .FLOW_LIMITS.MAX_LOOP_DEPTH */.Or.MAX_LOOP_DEPTH
2703
3333
  }));
2704
3334
  }
2705
3335
 
2706
3336
  __webpack_require__.d(__webpack_exports__, {
2707
- n: () => (validateFlow),
2708
- u: () => (usesPermissionFunction)
3337
+ nC: () => (validateFlow),
3338
+ r7: () => (callProblems),
3339
+ u9: () => (usesPermissionFunction)
2709
3340
  });
2710
3341
 
2711
3342
 
@@ -2908,6 +3539,19 @@ __webpack_require__.d(__webpack_exports__, {
2908
3539
  });
2909
3540
 
2910
3541
 
3542
+ },
3543
+ 8220(__unused_rspack_module, __webpack_exports__, __webpack_require__) {
3544
+ const permissionAction = (code)=>({
3545
+ type: 'action',
3546
+ actionId: code
3547
+ });
3548
+
3549
+ __webpack_require__.d(__webpack_exports__, {
3550
+ }, {
3551
+ s: permissionAction
3552
+ });
3553
+
3554
+
2911
3555
  },
2912
3556
  8542(__unused_rspack_module, __webpack_exports__, __webpack_require__) {
2913
3557
  /* import */ var _nestjs_common__rspack_import_0 = __webpack_require__(6157);
@@ -2989,203 +3633,14 @@ __webpack_require__.d(__webpack_exports__, {
2989
3633
  });
2990
3634
 
2991
3635
 
2992
- },
2993
- 6256(__unused_rspack_module, __webpack_exports__, __webpack_require__) {
2994
- const FLOW_NODE_TYPES = [
2995
- 'trigger',
2996
- 'validate',
2997
- 'permission_check',
2998
- 'set',
2999
- 'condition',
3000
- 'switch',
3001
- 'foreach',
3002
- 'entity_insert',
3003
- 'entity_update',
3004
- 'entity_delete',
3005
- 'entity_get',
3006
- 'entity_query',
3007
- 'commit',
3008
- 'call_flow',
3009
- 'http_request',
3010
- 'publish_event',
3011
- 'respond',
3012
- 'code'
3013
- ];
3014
- /** A failure of these always ends the run: checks can never be carried past, and Save progress cannot run in a savepoint. */ const NON_CONTINUABLE_NODE_TYPES = new Set([
3015
- 'validate',
3016
- 'permission_check',
3017
- 'commit'
3018
- ]);
3019
- const FLOW_AUTH_MODES = [
3020
- 'public',
3021
- 'api_key',
3022
- 'jwt',
3023
- 'permission',
3024
- 'internal'
3025
- ];
3026
- const FLOW_RUN_AS = [
3027
- 'caller',
3028
- 'system'
3029
- ];
3030
- /** object: the body is one object and the fields are its keys (`input.<name>`). list: the body is a list and the fields describe each item (`input.0.<name>`). */ const FLOW_BODY_TYPES = [
3031
- 'object',
3032
- 'list'
3033
- ];
3034
- const FLOW_INPUT_TYPES = [
3035
- 'string',
3036
- 'text',
3037
- 'number',
3038
- 'integer',
3039
- 'boolean',
3040
- 'date',
3041
- 'datetime',
3042
- 'email',
3043
- 'uuid',
3044
- 'object',
3045
- 'array',
3046
- 'choice'
3047
- ];
3048
- /** How a step is named in messages: its name, or its id when it was left unnamed. */ const nodeLabel = (node)=>node.name || node.id;
3049
- /**
3050
- * Which records a write node touches. one (default): a single record. many: one per item of `items`,
3051
- * whose per-record expressions read the item as `loop.item` (a loop around the node becomes
3052
- * `loop.parent`). filter: every record matching `filter`, read as `loop.item`.
3053
- */ const RECORD_WRITE_TARGETS = [
3054
- 'one',
3055
- 'many',
3056
- 'filter'
3057
- ];
3058
- const FLOW_LIMITS = {
3059
- MAX_NODES: 100,
3060
- MAX_EDGES: 300,
3061
- /** Room for a full foreach (MAX_FOREACH_ITEMS) with a few steps per item. */ MAX_STEPS_PER_RUN: 5000,
3062
- MAX_FOREACH_ITEMS: 1000,
3063
- MAX_LOOP_DEPTH: 3,
3064
- MAX_CALL_DEPTH: 5,
3065
- MAX_HTTP_CALLS: 10,
3066
- MAX_HTTP_TIMEOUT_MS: 15_000,
3067
- MAX_HTTP_RESPONSE_BYTES: 1_000_000,
3068
- MAX_QUERY_LIMIT: 200,
3069
- MAX_WRITE_ITEMS: 1000,
3070
- DEFAULT_WRITE_FILTER_LIMIT: 100,
3071
- MAX_CHILD_INSERTS: 10,
3072
- MAX_WRITES_PER_RUN: 5000,
3073
- DEFAULT_TIMEOUT_MS: 30_000,
3074
- MIN_TIMEOUT_MS: 1000,
3075
- MAX_TIMEOUT_MS: 120_000,
3076
- TRACE_PREVIEW_CHARS: 2000,
3077
- MAX_INPUT_FIELDS: 50,
3078
- /** How deep object/list-of-object fields may nest inside the request body. */ MAX_INPUT_DEPTH: 4,
3079
- MAX_VALIDATE_CHECKS: 50,
3080
- MAX_CODE_LENGTH: 20_000,
3081
- DEFAULT_CODE_TIMEOUT_MS: 1000,
3082
- MIN_CODE_TIMEOUT_MS: 10,
3083
- MAX_CODE_TIMEOUT_MS: 5000,
3084
- CODE_MEMORY_LIMIT_BYTES: 32 * 1024 * 1024,
3085
- CODE_STACK_SIZE_BYTES: 512 * 1024,
3086
- MAX_CODE_OUTPUT_BYTES: 1_000_000,
3087
- MAX_CODE_LOG_LINES: 50,
3088
- MAX_CODE_LOG_LINE_CHARS: 500
3089
- };
3090
- const FLOW_NODE_ID_PATTERN = /^[A-Za-z][A-Za-z0-9_]{0,39}$/;
3091
- const FLOW_SLUG_PATTERN = /^[a-z][a-z0-9-]{2,62}$/;
3092
- const FLOW_INPUT_NAME_PATTERN = /^[A-Za-z][A-Za-z0-9_]{0,59}$/;
3093
- const FLOW_ENDPOINT_PATH_PATTERN = /^[a-z][a-z0-9-]{0,62}$/;
3094
- /** A Request step's path, or null when it answers on the flow's own URL. */ function triggerPath(node) {
3095
- const path = node.config?.path;
3096
- return typeof path === 'string' && path.trim() ? path.trim() : null;
3097
- }
3098
- /** Every Request step of a flow as a URL. */ function flowEndpoints(flow) {
3099
- return (flow.nodes ?? []).filter((node)=>node?.type === 'trigger').map((node)=>{
3100
- const path = triggerPath(node);
3101
- if (!path) return {
3102
- nodeId: node.id,
3103
- path,
3104
- bodyType: flow.bodyType ?? 'object',
3105
- inputSchema: flow.inputSchema ?? []
3106
- };
3107
- const config = node.config;
3108
- return {
3109
- nodeId: node.id,
3110
- path,
3111
- bodyType: config.bodyType ?? 'object',
3112
- inputSchema: Array.isArray(config.inputSchema) ? config.inputSchema : []
3113
- };
3114
- });
3115
- }
3116
- /** The URL a call came in on: `path` of a Request step, or null / empty for the flow's own URL. */ function findEndpoint(flow, path) {
3117
- const wanted = path?.trim() || null;
3118
- return flowEndpoints(flow).find((endpoint)=>endpoint.path === wanted);
3119
- }
3120
- /** Output ports a node of this type can have (a `respond` node ends the flow: none). */ function portsOf(node) {
3121
- switch(node.type){
3122
- case 'condition':
3123
- return [
3124
- 'true',
3125
- 'false'
3126
- ];
3127
- case 'switch':
3128
- return [
3129
- ...(node.config.cases ?? []).map((c)=>c.id),
3130
- 'default'
3131
- ];
3132
- case 'foreach':
3133
- return [
3134
- 'each',
3135
- 'done'
3136
- ];
3137
- case 'permission_check':
3138
- return node.config.onDenied === 'branch' ? [
3139
- 'allowed',
3140
- 'denied'
3141
- ] : [
3142
- 'out'
3143
- ];
3144
- case 'respond':
3145
- return [];
3146
- case 'trigger':
3147
- case 'validate':
3148
- case 'set':
3149
- case 'commit':
3150
- return [
3151
- 'out'
3152
- ];
3153
- default:
3154
- return [
3155
- 'out',
3156
- 'error'
3157
- ];
3158
- }
3159
- }
3160
-
3161
- __webpack_require__.d(__webpack_exports__, {
3162
- Xr: () => (flowEndpoints),
3163
- a5: () => (portsOf),
3164
- um: () => (triggerPath),
3165
- vS: () => (findEndpoint)
3166
- }, {
3167
- At: NON_CONTINUABLE_NODE_TYPES,
3168
- Dn: RECORD_WRITE_TARGETS,
3169
- Or: FLOW_LIMITS,
3170
- Q7: FLOW_INPUT_TYPES,
3171
- Y3: FLOW_BODY_TYPES,
3172
- YD: FLOW_SLUG_PATTERN,
3173
- YO: FLOW_ENDPOINT_PATH_PATTERN,
3174
- dA: nodeLabel,
3175
- fO: FLOW_AUTH_MODES,
3176
- fZ: FLOW_INPUT_NAME_PATTERN,
3177
- gl: FLOW_RUN_AS,
3178
- q2: FLOW_NODE_TYPES,
3179
- z6: FLOW_NODE_ID_PATTERN
3180
- });
3181
-
3182
-
3183
3636
  },
3184
3637
  2678(__unused_rspack_module, __webpack_exports__, __webpack_require__) {
3185
3638
  const SENSITIVE_KEY = /pass(word)?|secret|token|authorization|api[-_]?key|cookie|credential|private[-_]?key/i;
3186
3639
  /** Copy of `value` with the content of sensitive-looking keys replaced, safe to store in a log. */ function redactSensitive(value, depth = 0) {
3187
3640
  if (depth > 8) return '[deep]';
3188
3641
  if (Array.isArray(value)) return value.slice(0, 100).map((v)=>redactSensitive(v, depth + 1));
3642
+ // Dates (and anything else with its own JSON form) have no own keys: walked as objects they would log as `{}`.
3643
+ if (value && typeof value.toJSON === 'function') return redactSensitive(value.toJSON(), depth + 1);
3189
3644
  if (value && typeof value === 'object') {
3190
3645
  const out = {};
3191
3646
  for (const [key, item] of Object.entries(value)){
@@ -3214,6 +3669,24 @@ __webpack_require__.d(__webpack_exports__, {
3214
3669
  });
3215
3670
 
3216
3671
 
3672
+ },
3673
+ 9609(__unused_rspack_module, __webpack_exports__, __webpack_require__) {
3674
+ /** What a version holds and a draft edits: everything about a flow but its identity. */ const FLOW_VERSIONED_FIELDS = [
3675
+ 'name',
3676
+ 'slug',
3677
+ 'description',
3678
+ 'isActive',
3679
+ 'retainExecutions',
3680
+ 'nodes',
3681
+ 'edges'
3682
+ ];
3683
+
3684
+ __webpack_require__.d(__webpack_exports__, {
3685
+ }, {
3686
+ v: FLOW_VERSIONED_FIELDS
3687
+ });
3688
+
3689
+
3217
3690
  },
3218
3691
  8323(__unused_rspack_module, __webpack_exports__, __webpack_require__) {
3219
3692
  /* import */ var _nestjs_common__rspack_import_0 = __webpack_require__(6157);
@@ -3415,6 +3888,7 @@ function _ts_param(paramIndex, decorator) {
3415
3888
  }
3416
3889
 
3417
3890
 
3891
+ const VARIABLE_ENV_PREFIX = 'ENTITY_BUILDER_VAR_';
3418
3892
  class EntityBuilderConfigService {
3419
3893
  options;
3420
3894
  constructor(options){
@@ -3435,6 +3909,20 @@ class EntityBuilderConfigService {
3435
3909
  getConfig() {
3436
3910
  return this.options.config;
3437
3911
  }
3912
+ /** `designer.readOnly`, else the `ENTITY_BUILDER_READ_ONLY=true` env variable (production). */ isDesignerReadOnly() {
3913
+ return this.getConfig()?.designer?.readOnly ?? process.env['ENTITY_BUILDER_READ_ONLY'] === 'true';
3914
+ }
3915
+ /** The values behind `${var:NAME}` in promoted flows: every non-empty `ENTITY_BUILDER_VAR_<NAME>` env variable, overridden by `bundles.variables`; names a placeholder cannot carry are left out. */ getBundleVariables() {
3916
+ const fromEnv = Object.entries(process.env).filter((entry)=>entry[0].startsWith(VARIABLE_ENV_PREFIX) && !!entry[1]).map(([key, value])=>[
3917
+ key.slice(VARIABLE_ENV_PREFIX.length),
3918
+ value
3919
+ ]);
3920
+ const variables = {
3921
+ ...Object.fromEntries(fromEnv),
3922
+ ...this.getConfig()?.bundles?.variables
3923
+ };
3924
+ return Object.fromEntries(Object.entries(variables).filter((entry)=>_config_entity_builder_constants_js__rspack_import_1/* .BUNDLE_VARIABLE_NAME_PATTERN.test */.JI.test(entry[0]) && typeof entry[1] === 'string'));
3925
+ }
3438
3926
  }
3439
3927
  EntityBuilderConfigService = _ts_decorate([
3440
3928
  (0,_nestjs_common__rspack_import_0.Injectable)(),
@@ -3840,54 +4328,24 @@ __webpack_require__.d(__webpack_exports__, {
3840
4328
 
3841
4329
 
3842
4330
  },
3843
- 8888(__unused_rspack_module, __webpack_exports__, __webpack_require__) {
3844
-
3845
- // EXPORTS
3846
- __webpack_require__.d(__webpack_exports__, {
3847
- T: () => (/* binding */ FlowApiKeyService)
3848
- });
3849
-
3850
- // EXTERNAL MODULE: external "@nestjs/common"
3851
- var common_ = __webpack_require__(6157);
3852
- // EXTERNAL MODULE: external "typeorm"
3853
- var external_typeorm_ = __webpack_require__(7158);
3854
- // EXTERNAL MODULE: ./projects/nestjs-entity-builder/src/config/message-keys.ts
3855
- var message_keys = __webpack_require__(5732);
3856
- // EXTERNAL MODULE: ./projects/nestjs-entity-builder/src/entities/flow-api-key.entity.ts
3857
- var flow_api_key_entity = __webpack_require__(201);
3858
- // EXTERNAL MODULE: external "node:crypto"
3859
- var external_node_crypto_ = __webpack_require__(2561);
3860
- ;// CONCATENATED MODULE: ./projects/nestjs-entity-builder/src/flow-engine/flow-api-key.ts
3861
-
3862
- const API_KEY_PREFIX_LENGTH = 11; // "fk_" + 8 hex
3863
- /** A new key: `fk_<8 hex>_<32 hex>`. Shown once; only its hash is stored. */ function generateApiKey() {
3864
- const key = `fk_${(0,external_node_crypto_.randomBytes)(4).toString('hex')}_${(0,external_node_crypto_.randomBytes)(16).toString('hex')}`;
3865
- return {
3866
- key,
3867
- prefix: apiKeyPrefix(key),
3868
- hash: hashApiKey(key)
3869
- };
3870
- }
3871
- const hashApiKey = (key)=>(0,external_node_crypto_.createHash)('sha256').update(key).digest('hex');
3872
- const apiKeyPrefix = (key)=>key.slice(0, API_KEY_PREFIX_LENGTH);
3873
- function looksLikeApiKey(key) {
3874
- return typeof key === 'string' && /^fk_[0-9a-f]{8}_[0-9a-f]{32}$/.test(key);
3875
- }
3876
- /** Constant-time comparison of a presented key against a stored hash. */ function apiKeyMatches(presented, storedHash) {
3877
- const a = Buffer.from(hashApiKey(presented), 'hex');
3878
- const b = Buffer.from(storedHash, 'hex');
3879
- return a.length === b.length && (0,external_node_crypto_.timingSafeEqual)(a, b);
3880
- }
3881
-
3882
- // EXTERNAL MODULE: ./projects/nestjs-entity-builder/src/entities/flow-definition.entity.ts
3883
- var flow_definition_entity = __webpack_require__(3770);
3884
- // EXTERNAL MODULE: ./projects/nestjs-entity-builder/src/rule-engine/rule-values.ts
3885
- var rule_values = __webpack_require__(6622);
3886
- // EXTERNAL MODULE: ./projects/nestjs-entity-builder/src/services/entity-builder-datasource.provider.ts
3887
- var entity_builder_datasource_provider = __webpack_require__(424);
3888
- // EXTERNAL MODULE: ./projects/nestjs-entity-builder/src/services/flow-definition.service.ts
3889
- var flow_definition_service = __webpack_require__(5229);
3890
- ;// CONCATENATED MODULE: ./projects/nestjs-entity-builder/src/services/flow-api-key.service.ts
4331
+ 5229(__unused_rspack_module, __webpack_exports__, __webpack_require__) {
4332
+ /* import */ var _flusys_nestjs_shared__rspack_import_0 = __webpack_require__(3422);
4333
+ /* import */ var _flusys_nestjs_shared_classes__rspack_import_1 = __webpack_require__(6937);
4334
+ /* import */ var _flusys_nestjs_shared_interfaces__rspack_import_2 = __webpack_require__(5103);
4335
+ /* import */ var _flusys_nestjs_shared_modules__rspack_import_3 = __webpack_require__(1236);
4336
+ /* import */ var _nestjs_common__rspack_import_4 = __webpack_require__(6157);
4337
+ /* import */ var typeorm__rspack_import_5 = __webpack_require__(7158);
4338
+ /* import */ var _config_event_actions_js__rspack_import_16 = __webpack_require__(982);
4339
+ /* import */ var _config_message_keys_js__rspack_import_14 = __webpack_require__(5732);
4340
+ /* import */ var _entities_entity_definition_entity_js__rspack_import_6 = __webpack_require__(3189);
4341
+ /* import */ var _entities_flow_definition_entity_js__rspack_import_7 = __webpack_require__(3770);
4342
+ /* import */ var _entities_flow_version_entity_js__rspack_import_8 = __webpack_require__(2399);
4343
+ /* import */ var _flow_engine_flow_api_key_js__rspack_import_9 = __webpack_require__(4816);
4344
+ /* import */ var _flow_engine_flow_permission_logic_js__rspack_import_13 = __webpack_require__(8220);
4345
+ /* import */ var _flow_engine_flow_graph_validator_js__rspack_import_10 = __webpack_require__(3186);
4346
+ /* import */ var _flow_engine_flow_types_js__rspack_import_11 = __webpack_require__(6256);
4347
+ /* import */ var _interfaces_flow_definition_interface_js__rspack_import_15 = __webpack_require__(9609);
4348
+ /* import */ var _entity_builder_datasource_provider_js__rspack_import_12 = __webpack_require__(424);
3891
4349
  function _ts_decorate(decorators, target, key, desc) {
3892
4350
  var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
3893
4351
  if (typeof Reflect === "object" && typeof Reflect.decorate === "function") {
@@ -3920,194 +4378,6 @@ function _ts_param(paramIndex, decorator) {
3920
4378
 
3921
4379
 
3922
4380
 
3923
- const TOUCH_INTERVAL_MS = 60_000;
3924
- class FlowApiKeyService {
3925
- provider;
3926
- constructor(provider){
3927
- this.provider = provider;
3928
- }
3929
- async repo() {
3930
- return this.provider.getRepository(flow_api_key_entity/* .FlowApiKey */.S);
3931
- }
3932
- async assertFlowExists(flowId) {
3933
- const flow = await (await this.provider.getRepository(flow_definition_entity/* .FlowDefinition */.G)).findOne({
3934
- where: {
3935
- id: flowId
3936
- },
3937
- select: {
3938
- id: true
3939
- }
3940
- });
3941
- if (!flow) throw (0,flow_definition_service/* .flowNotFound */.YW)();
3942
- }
3943
- async findKey(id) {
3944
- const key = await (await this.repo()).findOne({
3945
- where: {
3946
- id
3947
- }
3948
- });
3949
- if (!key) throw new common_.NotFoundException({
3950
- message: 'API key not found.',
3951
- messageKey: message_keys/* .FLOW_MESSAGES.API_KEY_NOT_FOUND */.wu.API_KEY_NOT_FOUND
3952
- });
3953
- await this.assertFlowExists(key.flowId);
3954
- return key;
3955
- }
3956
- /** The plain key is returned here and never again. */ async create(flowId, name, expiresAt, user) {
3957
- const expiry = expiresAt ? (0,rule_values/* .parseDateText */.hz)(expiresAt) : null;
3958
- if (expiry !== null && !(expiry > Date.now())) {
3959
- throw new common_.BadRequestException({
3960
- message: 'The expiry date of an API key must be in the future.',
3961
- messageKey: message_keys/* .FLOW_MESSAGES.API_KEY_EXPIRY_IN_PAST */.wu.API_KEY_EXPIRY_IN_PAST
3962
- });
3963
- }
3964
- await this.assertFlowExists(flowId);
3965
- const { key, prefix, hash } = generateApiKey();
3966
- const repo = await this.repo();
3967
- const saved = await repo.save(repo.create({
3968
- flowId,
3969
- name,
3970
- keyPrefix: prefix,
3971
- keyHash: hash,
3972
- isActive: true,
3973
- expiresAt: expiry === null ? null : new Date(expiry),
3974
- lastUsedAt: null,
3975
- createdById: user?.id ?? null
3976
- }));
3977
- return {
3978
- key,
3979
- info: this.info(saved)
3980
- };
3981
- }
3982
- async list(flowId) {
3983
- await this.assertFlowExists(flowId);
3984
- const rows = await (await this.repo()).find({
3985
- where: {
3986
- flowId
3987
- },
3988
- order: {
3989
- createdAt: 'DESC'
3990
- }
3991
- });
3992
- return rows.map((r)=>this.info(r));
3993
- }
3994
- async revoke(id) {
3995
- const key = await this.findKey(id);
3996
- await (await this.repo()).update({
3997
- id: key.id
3998
- }, {
3999
- isActive: false
4000
- });
4001
- }
4002
- async remove(id) {
4003
- const key = await this.findKey(id);
4004
- await (await this.repo()).softDelete({
4005
- id: key.id
4006
- });
4007
- }
4008
- /**
4009
- * Checks `presented` against the unrevoked keys of this flow. Only the flow access guard calls
4010
- * this, and only while the flow's authMode is `api_key`; revoking works in any authMode.
4011
- */ async verify(flowId, presented) {
4012
- if (!looksLikeApiKey(presented)) return 'invalid';
4013
- const repo = await this.repo();
4014
- const candidates = await repo.find({
4015
- where: {
4016
- flowId,
4017
- keyPrefix: apiKeyPrefix(presented),
4018
- isActive: true,
4019
- deletedAt: (0,external_typeorm_.IsNull)()
4020
- },
4021
- select: {
4022
- id: true,
4023
- keyHash: true,
4024
- expiresAt: true,
4025
- lastUsedAt: true
4026
- }
4027
- });
4028
- const match = candidates.find((c)=>apiKeyMatches(presented, c.keyHash));
4029
- if (!match) return 'invalid';
4030
- if (isExpired(match.expiresAt)) return 'expired';
4031
- if (!match.lastUsedAt || Date.now() - match.lastUsedAt.getTime() > TOUCH_INTERVAL_MS) {
4032
- await repo.update({
4033
- id: match.id
4034
- }, {
4035
- lastUsedAt: new Date()
4036
- });
4037
- }
4038
- return 'valid';
4039
- }
4040
- info(row) {
4041
- const status = !row.isActive ? 'revoked' : isExpired(row.expiresAt) ? 'expired' : 'active';
4042
- return {
4043
- id: row.id,
4044
- flowId: row.flowId,
4045
- name: row.name,
4046
- keyPrefix: row.keyPrefix,
4047
- isActive: row.isActive,
4048
- status,
4049
- expiresAt: row.expiresAt,
4050
- lastUsedAt: row.lastUsedAt,
4051
- createdAt: row.createdAt
4052
- };
4053
- }
4054
- }
4055
- FlowApiKeyService = _ts_decorate([
4056
- (0,common_.Injectable)({
4057
- scope: common_.Scope.REQUEST
4058
- }),
4059
- _ts_param(0, (0,common_.Inject)(entity_builder_datasource_provider/* .EntityBuilderDataSourceProvider */.D)),
4060
- _ts_metadata("design:type", Function),
4061
- _ts_metadata("design:paramtypes", [
4062
- typeof entity_builder_datasource_provider/* .EntityBuilderDataSourceProvider */.D === "undefined" ? Object : entity_builder_datasource_provider/* .EntityBuilderDataSourceProvider */.D
4063
- ])
4064
- ], FlowApiKeyService);
4065
- function isExpired(expiresAt) {
4066
- return !!expiresAt && expiresAt.getTime() <= Date.now();
4067
- }
4068
-
4069
-
4070
- },
4071
- 5229(__unused_rspack_module, __webpack_exports__, __webpack_require__) {
4072
- /* import */ var _flusys_nestjs_shared__rspack_import_0 = __webpack_require__(3422);
4073
- /* import */ var _flusys_nestjs_shared_classes__rspack_import_1 = __webpack_require__(6937);
4074
- /* import */ var _flusys_nestjs_shared_modules__rspack_import_2 = __webpack_require__(1236);
4075
- /* import */ var _nestjs_common__rspack_import_3 = __webpack_require__(6157);
4076
- /* import */ var typeorm__rspack_import_4 = __webpack_require__(7158);
4077
- /* import */ var _config_event_actions_js__rspack_import_11 = __webpack_require__(982);
4078
- /* import */ var _config_message_keys_js__rspack_import_10 = __webpack_require__(5732);
4079
- /* import */ var _entities_entity_definition_entity_js__rspack_import_5 = __webpack_require__(3189);
4080
- /* import */ var _entities_flow_definition_entity_js__rspack_import_6 = __webpack_require__(3770);
4081
- /* import */ var _flow_engine_flow_graph_validator_js__rspack_import_7 = __webpack_require__(3186);
4082
- /* import */ var _flow_engine_flow_types_js__rspack_import_8 = __webpack_require__(6256);
4083
- /* import */ var _entity_builder_datasource_provider_js__rspack_import_9 = __webpack_require__(424);
4084
- function _ts_decorate(decorators, target, key, desc) {
4085
- var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
4086
- if (typeof Reflect === "object" && typeof Reflect.decorate === "function") {
4087
- r = Reflect.decorate(decorators, target, key, desc);
4088
- } else {
4089
- for(var i = decorators.length - 1; i >= 0; i--){
4090
- if (d = decorators[i]) {
4091
- r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
4092
- }
4093
- }
4094
- }
4095
- return c > 3 && r && Object.defineProperty(target, key, r), r;
4096
- }
4097
- function _ts_metadata(metadataKey, metadataValue) {
4098
- if (typeof Reflect === "object" && typeof Reflect.metadata === "function") {
4099
- return Reflect.metadata(metadataKey, metadataValue);
4100
- }
4101
- }
4102
- function _ts_param(paramIndex, decorator) {
4103
- return function(target, key) {
4104
- decorator(target, key, paramIndex);
4105
- };
4106
- }
4107
-
4108
-
4109
-
4110
-
4111
4381
 
4112
4382
 
4113
4383
 
@@ -4117,21 +4387,28 @@ function _ts_param(paramIndex, decorator) {
4117
4387
 
4118
4388
 
4119
4389
  const defaultFlowPermissionCode = (slug)=>`entity_builder.flow.${slug}.execute`;
4120
- const flowNotFound = ()=>new _nestjs_common__rspack_import_3.NotFoundException({
4390
+ /** 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));
4391
+ const flowNotFound = ()=>new _nestjs_common__rspack_import_4.NotFoundException({
4121
4392
  message: 'Flow not found.',
4122
- messageKey: _config_message_keys_js__rspack_import_10/* .FLOW_MESSAGES.NOT_FOUND */.wu.NOT_FOUND
4393
+ messageKey: _config_message_keys_js__rspack_import_14/* .FLOW_MESSAGES.NOT_FOUND */.wu.NOT_FOUND
4394
+ });
4395
+ /** A flow that has been published at least once: its columns are what callers run. */ const isPublished = (flow)=>flow.version > 0;
4396
+ /** The versioned part of a flow (or of a draft), in a fixed key order so two snapshots compare as text. */ function snapshotOf(source) {
4397
+ return Object.fromEntries(_interfaces_flow_definition_interface_js__rspack_import_15/* .FLOW_VERSIONED_FIELDS.filter */.v.filter((key)=>source[key] !== undefined).map((key)=>[
4398
+ key,
4399
+ source[key]
4400
+ ]));
4401
+ }
4402
+ const sameSnapshot = (a, b)=>JSON.stringify(a) === JSON.stringify(b);
4403
+ /** What the designer edits: the saved draft, or the published flow when there is none. */ const workingCopy = (flow)=>({
4404
+ ...snapshotOf(flow),
4405
+ ...flow.draft
4123
4406
  });
4124
- /** Fills the defaults a flow definition gets when a field is left out. */ function toFlowSettings(dto) {
4407
+ /** What running a flow needs; everything about a run is on its Request steps. */ function toFlowSettings(dto) {
4125
4408
  return {
4126
4409
  slug: dto.slug,
4127
- authMode: dto.authMode ?? 'jwt',
4128
- runAs: dto.runAs ?? 'caller',
4129
- isTransactional: dto.isTransactional ?? false,
4130
- bodyType: dto.bodyType ?? 'object',
4131
- inputSchema: dto.inputSchema ?? [],
4132
4410
  nodes: dto.nodes ?? [],
4133
- edges: dto.edges ?? [],
4134
- timeoutMs: dto.timeoutMs ?? _flow_engine_flow_types_js__rspack_import_8/* .FLOW_LIMITS.DEFAULT_TIMEOUT_MS */.Or.DEFAULT_TIMEOUT_MS
4411
+ edges: dto.edges ?? []
4135
4412
  };
4136
4413
  }
4137
4414
  class FlowDefinitionService extends _flusys_nestjs_shared_classes__rspack_import_1.ApiService {
@@ -4140,11 +4417,14 @@ class FlowDefinitionService extends _flusys_nestjs_shared_classes__rspack_import
4140
4417
  provider;
4141
4418
  permissionCache;
4142
4419
  actionRegistry;
4143
- constructor(cacheManager, utilsService, provider, permissionCache, actionRegistry){
4144
- super(_config_event_actions_js__rspack_import_11/* .ENTITY_BUILDER_EVENT_ENTITIES.FLOW_DEFINITION */.P.FLOW_DEFINITION, cacheManager, utilsService, FlowDefinitionService.name, true, _config_event_actions_js__rspack_import_11/* .ENTITY_BUILDER_EVENT_MODULE */.S, provider, _entities_flow_definition_entity_js__rspack_import_6/* .FlowDefinition */.G), this.cacheManager = cacheManager, this.utilsService = utilsService, this.provider = provider, this.permissionCache = permissionCache, this.actionRegistry = actionRegistry;
4420
+ notifications;
4421
+ email;
4422
+ companyAccess;
4423
+ constructor(cacheManager, utilsService, provider, permissionCache, actionRegistry, notifications, email, companyAccess){
4424
+ super(_config_event_actions_js__rspack_import_16/* .ENTITY_BUILDER_EVENT_ENTITIES.FLOW_DEFINITION */.P.FLOW_DEFINITION, cacheManager, utilsService, FlowDefinitionService.name, true, _config_event_actions_js__rspack_import_16/* .ENTITY_BUILDER_EVENT_MODULE */.S, provider, _entities_flow_definition_entity_js__rspack_import_7/* .FlowDefinition */.G), this.cacheManager = cacheManager, this.utilsService = utilsService, this.provider = provider, this.permissionCache = permissionCache, this.actionRegistry = actionRegistry, this.notifications = notifications, this.email = email, this.companyAccess = companyAccess;
4145
4425
  }
4146
4426
  async findOrFail(id) {
4147
- const flow = await (await this.provider.getRepository(_entities_flow_definition_entity_js__rspack_import_6/* .FlowDefinition */.G)).findOne({
4427
+ const flow = await (await this.provider.getRepository(_entities_flow_definition_entity_js__rspack_import_7/* .FlowDefinition */.G)).findOne({
4148
4428
  where: {
4149
4429
  id
4150
4430
  }
@@ -4152,28 +4432,28 @@ class FlowDefinitionService extends _flusys_nestjs_shared_classes__rspack_import
4152
4432
  if (!flow) throw flowNotFound();
4153
4433
  return flow;
4154
4434
  }
4155
- /** Validation with the real entity and flow lists. Returns errors and warnings without throwing. */ async validate(settings) {
4156
- const entities = await (await this.provider.getRepository(_entities_entity_definition_entity_js__rspack_import_5/* .EntityDefinition */.f)).find({
4435
+ /** Validation with the real entity and flow lists and the modules this app runs. Returns errors and warnings without throwing. */ async validate(settings) {
4436
+ const entities = await (await this.provider.getRepository(_entities_entity_definition_entity_js__rspack_import_6/* .EntityDefinition */.f)).find({
4157
4437
  select: {
4158
4438
  code: true
4159
4439
  }
4160
4440
  });
4161
- const flows = await (await this.provider.getRepository(_entities_flow_definition_entity_js__rspack_import_6/* .FlowDefinition */.G)).find();
4162
- return (0,_flow_engine_flow_graph_validator_js__rspack_import_7/* .validateFlow */.n)(settings, {
4441
+ const flows = await (await this.provider.getRepository(_entities_flow_definition_entity_js__rspack_import_7/* .FlowDefinition */.G)).find();
4442
+ return (0,_flow_engine_flow_graph_validator_js__rspack_import_10/* .validateFlow */.nC)(settings, {
4163
4443
  knownEntityCodes: new Set(entities.map((e)=>e.code)),
4164
4444
  knownFlows: new Map(flows.map((flow)=>[
4165
4445
  flow.slug,
4166
- {
4167
- isActive: flow.isActive,
4168
- authMode: flow.authMode,
4169
- endpoints: (0,_flow_engine_flow_types_js__rspack_import_8/* .flowEndpoints */.Xr)(flow),
4170
- calls: calledSlugs(flow.nodes)
4171
- }
4172
- ]))
4446
+ callableOf(flow)
4447
+ ])),
4448
+ messageServices: {
4449
+ notification: !!this.notifications,
4450
+ email: !!this.email
4451
+ },
4452
+ companyMembers: !!this.companyAccess
4173
4453
  });
4174
4454
  }
4175
4455
  /** Names of the other flows whose `call_flow` nodes run `slug`. */ async callersOf(slug, excludeIds) {
4176
- const flows = await (await this.provider.getRepository(_entities_flow_definition_entity_js__rspack_import_6/* .FlowDefinition */.G)).find({
4456
+ const flows = await (await this.provider.getRepository(_entities_flow_definition_entity_js__rspack_import_7/* .FlowDefinition */.G)).find({
4177
4457
  select: {
4178
4458
  id: true,
4179
4459
  name: true,
@@ -4185,20 +4465,83 @@ class FlowDefinitionService extends _flusys_nestjs_shared_classes__rspack_import
4185
4465
  async assertNotCalled(slug, excludeIds) {
4186
4466
  const callers = await this.callersOf(slug, excludeIds);
4187
4467
  if (!callers.length) return;
4188
- throw new _nestjs_common__rspack_import_3.ConflictException({
4468
+ throw new _nestjs_common__rspack_import_4.ConflictException({
4189
4469
  message: `The flow '${slug}' is called by: ${callers.join(', ')}. Change those flows first.`,
4190
- messageKey: _config_message_keys_js__rspack_import_10/* .FLOW_MESSAGES.CALLED_BY_OTHER_FLOWS */.wu.CALLED_BY_OTHER_FLOWS,
4470
+ messageKey: _config_message_keys_js__rspack_import_14/* .FLOW_MESSAGES.CALLED_BY_OTHER_FLOWS */.wu.CALLED_BY_OTHER_FLOWS,
4191
4471
  messageVariables: {
4192
4472
  slug,
4193
4473
  flows: callers.join(', ')
4194
4474
  }
4195
4475
  });
4196
4476
  }
4477
+ /**
4478
+ * Other flows' Call flow steps keep working: the URL name they call is not renamed, the paths they
4479
+ * start at are not dropped, and nothing else they rely on changes under them - access, body,
4480
+ * required inputs, the flow staying on, the call chain staying loop-free and within depth.
4481
+ */ async assertCallersKept(before, after, live) {
4482
+ if (after.slug !== before.slug) return this.assertNotCalled(before.slug, [
4483
+ before.id
4484
+ ]);
4485
+ await this.assertEndpointsNotCalled(before, after);
4486
+ await this.assertCallsStillValid(before, {
4487
+ ...after,
4488
+ ...live
4489
+ });
4490
+ }
4491
+ /**
4492
+ * Checks each running flow that calls this one, directly or through other flows, against the flows
4493
+ * as they are and as this change leaves them, and refuses the change when it adds a problem. A
4494
+ * caller already broken for another reason does not block it; only what this change breaks does.
4495
+ */ async assertCallsStillValid(before, after) {
4496
+ const flows = await (await this.provider.getRepository(_entities_flow_definition_entity_js__rspack_import_7/* .FlowDefinition */.G)).find();
4497
+ const running = flows.filter((flow)=>flow.id !== before.id && flow.isActive && isPublished(flow) && calledSlugs(flow.nodes).length);
4498
+ if (!running.length) return;
4499
+ const now = new Map(flows.map((flow)=>[
4500
+ flow.slug,
4501
+ callableOf(flow)
4502
+ ]));
4503
+ const next = new Map(now).set(before.slug, callableOf(after));
4504
+ const broken = [];
4505
+ for (const caller of running){
4506
+ const settings = toFlowSettings(caller);
4507
+ // Counted, not a set: two steps with the same label can share a problem, and only the new one is added.
4508
+ const existing = new Map();
4509
+ for (const problem of (0,_flow_engine_flow_graph_validator_js__rspack_import_10/* .callProblems */.r7)(settings, before.slug, now)){
4510
+ const key = JSON.stringify(problem);
4511
+ existing.set(key, (existing.get(key) ?? 0) + 1);
4512
+ }
4513
+ const added = (0,_flow_engine_flow_graph_validator_js__rspack_import_10/* .callProblems */.r7)(settings, before.slug, next).filter((problem)=>{
4514
+ const key = JSON.stringify(problem);
4515
+ const left = existing.get(key) ?? 0;
4516
+ existing.set(key, left - 1);
4517
+ return left <= 0;
4518
+ });
4519
+ if (added.length) broken.push({
4520
+ flow: caller.name,
4521
+ problems: added
4522
+ });
4523
+ }
4524
+ if (!broken.length) return;
4525
+ const names = broken.map((b)=>b.flow).join(', ');
4526
+ throw new _nestjs_common__rspack_import_4.ConflictException({
4527
+ message: `This change breaks flows that call '${before.slug}': ${names}. Change those flows first.`,
4528
+ messageKey: _config_message_keys_js__rspack_import_14/* .FLOW_MESSAGES.CALLERS_BROKEN */.wu.CALLERS_BROKEN,
4529
+ messageVariables: {
4530
+ slug: before.slug,
4531
+ flows: names
4532
+ },
4533
+ errors: broken.flatMap((b)=>b.problems.map((problem)=>({
4534
+ field: 'flow',
4535
+ flow: b.flow,
4536
+ ...problem
4537
+ })))
4538
+ });
4539
+ }
4197
4540
  /** A URL other flows' Call flow steps start at cannot be removed or renamed from under them. */ async assertEndpointsNotCalled(before, after) {
4198
- const kept = new Set((0,_flow_engine_flow_types_js__rspack_import_8/* .flowEndpoints */.Xr)(after).map((e)=>e.path));
4199
- const removed = (0,_flow_engine_flow_types_js__rspack_import_8/* .flowEndpoints */.Xr)(before).map((e)=>e.path).filter((path)=>!kept.has(path));
4541
+ const kept = new Set((0,_flow_engine_flow_types_js__rspack_import_11/* .flowEndpoints */.Xr)(after).map((e)=>e.path));
4542
+ const removed = (0,_flow_engine_flow_types_js__rspack_import_11/* .flowEndpoints */.Xr)(before).map((e)=>e.path).filter((path)=>!kept.has(path));
4200
4543
  if (!removed.length) return;
4201
- const flows = await (await this.provider.getRepository(_entities_flow_definition_entity_js__rspack_import_6/* .FlowDefinition */.G)).find({
4544
+ const flows = await (await this.provider.getRepository(_entities_flow_definition_entity_js__rspack_import_7/* .FlowDefinition */.G)).find({
4202
4545
  select: {
4203
4546
  id: true,
4204
4547
  name: true,
@@ -4209,9 +4552,9 @@ class FlowDefinitionService extends _flusys_nestjs_shared_classes__rspack_import
4209
4552
  const callers = flows.filter((flow)=>flow.id !== before.id && calledTargets(flow.nodes).some((t)=>t.slug === before.slug && t.endpoint === path)).map((flow)=>flow.name);
4210
4553
  if (!callers.length) continue;
4211
4554
  const url = `api-flows/${before.slug}${path ? `/${path}` : ''}`;
4212
- throw new _nestjs_common__rspack_import_3.ConflictException({
4555
+ throw new _nestjs_common__rspack_import_4.ConflictException({
4213
4556
  message: `The URL ${url} is called by: ${callers.join(', ')}. Change those flows first.`,
4214
- messageKey: _config_message_keys_js__rspack_import_10/* .FLOW_MESSAGES.ENDPOINT_CALLED_BY_OTHER_FLOWS */.wu.ENDPOINT_CALLED_BY_OTHER_FLOWS,
4557
+ messageKey: _config_message_keys_js__rspack_import_14/* .FLOW_MESSAGES.ENDPOINT_CALLED_BY_OTHER_FLOWS */.wu.ENDPOINT_CALLED_BY_OTHER_FLOWS,
4215
4558
  messageVariables: {
4216
4559
  url,
4217
4560
  flows: callers.join(', ')
@@ -4222,9 +4565,9 @@ class FlowDefinitionService extends _flusys_nestjs_shared_classes__rspack_import
4222
4565
  async assertSaveable(settings, user, selfId) {
4223
4566
  const { errors, warnings } = await this.validate(settings);
4224
4567
  if (errors.length) {
4225
- throw new _nestjs_common__rspack_import_3.BadRequestException({
4568
+ throw new _nestjs_common__rspack_import_4.BadRequestException({
4226
4569
  message: 'The flow has problems.',
4227
- messageKey: _config_message_keys_js__rspack_import_10/* .FLOW_MESSAGES.INVALID */.wu.INVALID,
4570
+ messageKey: _config_message_keys_js__rspack_import_14/* .FLOW_MESSAGES.INVALID */.wu.INVALID,
4228
4571
  errors: errors.map((error)=>({
4229
4572
  field: 'flow',
4230
4573
  ...error
@@ -4232,16 +4575,16 @@ class FlowDefinitionService extends _flusys_nestjs_shared_classes__rspack_import
4232
4575
  warnings
4233
4576
  });
4234
4577
  }
4235
- const clash = await (await this.provider.getRepository(_entities_flow_definition_entity_js__rspack_import_6/* .FlowDefinition */.G)).findOne({
4578
+ const clash = await (await this.provider.getRepository(_entities_flow_definition_entity_js__rspack_import_7/* .FlowDefinition */.G)).findOne({
4236
4579
  where: {
4237
4580
  slug: settings.slug
4238
4581
  },
4239
4582
  withDeleted: true
4240
4583
  });
4241
4584
  if (clash && clash.id !== selfId) {
4242
- throw new _nestjs_common__rspack_import_3.ConflictException({
4585
+ throw new _nestjs_common__rspack_import_4.ConflictException({
4243
4586
  message: `The URL name '${settings.slug}' is already used by another flow.`,
4244
- messageKey: _config_message_keys_js__rspack_import_10/* .FLOW_MESSAGES.SLUG_TAKEN */.wu.SLUG_TAKEN,
4587
+ messageKey: _config_message_keys_js__rspack_import_14/* .FLOW_MESSAGES.SLUG_TAKEN */.wu.SLUG_TAKEN,
4245
4588
  messageVariables: {
4246
4589
  slug: settings.slug
4247
4590
  }
@@ -4250,7 +4593,6 @@ class FlowDefinitionService extends _flusys_nestjs_shared_classes__rspack_import
4250
4593
  await this.assertMayAuthor(settings, user);
4251
4594
  }
4252
4595
  /** The extra permissions a flow needs from whoever writes it: saving it, or test-running it as an unsaved draft. */ async assertMayAuthor(settings, user) {
4253
- if (settings.runAs === 'system') await this.assertHasPermission(user, _flusys_nestjs_shared__rspack_import_0.FLOW_DEFINITION_PERMISSIONS.SYSTEM);
4254
4596
  if (settings.nodes.some((node)=>node?.type === 'code')) await this.assertHasPermission(user, _flusys_nestjs_shared__rspack_import_0.FLOW_DEFINITION_PERMISSIONS.CODE);
4255
4597
  }
4256
4598
  async assertMayCreate(user) {
@@ -4258,13 +4600,13 @@ class FlowDefinitionService extends _flusys_nestjs_shared_classes__rspack_import
4258
4600
  }
4259
4601
  /**
4260
4602
  * A test run needs what running the flow for real would: the author permissions for its
4261
- * run-as and code nodes (a saved flow as much as a draft). A live run also needs what makes it
4262
- * real - a draft the permission to save it, a permission flow its own execute permission.
4263
- */ async assertMayTest(settings, user, run) {
4603
+ * code nodes (a saved flow as much as a draft). A live run also needs what makes it
4604
+ * real - a draft the permission to save it, a permission URL its own execute permission.
4605
+ */ async assertMayTest(settings, endpoint, user, run) {
4264
4606
  await this.assertMayAuthor(settings, user);
4265
4607
  if (run.mode !== 'live') return;
4266
4608
  if (run.draft) await this.assertHasPermission(user, run.saved ? _flusys_nestjs_shared__rspack_import_0.FLOW_DEFINITION_PERMISSIONS.UPDATE : _flusys_nestjs_shared__rspack_import_0.FLOW_DEFINITION_PERMISSIONS.CREATE);
4267
- if (settings.authMode === 'permission') await this.assertHasPermission(user, settings.permissionCode || defaultFlowPermissionCode(settings.slug));
4609
+ if (endpoint?.authMode === 'permission') await this.permissionCache.assertPermissionLogic(user, flowPermissions(endpoint, settings.slug));
4268
4610
  }
4269
4611
  assertHasPermission(user, permission) {
4270
4612
  return this.permissionCache.assertPermission(user, permission);
@@ -4274,37 +4616,171 @@ class FlowDefinitionService extends _flusys_nestjs_shared_classes__rspack_import
4274
4616
  dto
4275
4617
  ]){
4276
4618
  await this.assertSaveable(toFlowSettings(d), user);
4277
- // JSON columns have no database default, so leaving the optional settings out must not mean NULL.
4278
- d.inputSchema ??= [];
4279
- d.bodyType ??= 'object';
4280
- d.authMode ??= 'jwt';
4281
- d.runAs ??= 'caller';
4619
+ d.nodes = (0,_flow_engine_flow_api_key_js__rspack_import_9/* .sealApiKeys */.ue)(d.nodes);
4620
+ d.version = 0;
4282
4621
  }
4283
4622
  }
4284
- async beforeUpdateOperation(dto, user, _qr) {
4623
+ /**
4624
+ * Checked against what the save produces: the draft of a published flow, the flow itself before its
4625
+ * first publish. An unpublished flow changes in place, so its callers are protected now, not at publish.
4626
+ */ async beforeUpdateOperation(dto, user, _qr) {
4285
4627
  for (const d of Array.isArray(dto) ? dto : [
4286
4628
  dto
4287
4629
  ]){
4288
4630
  const existing = await this.findOrFail(d.id);
4289
- const merged = toFlowSettings({
4290
- ...existing,
4631
+ const settings = toFlowSettings({
4632
+ ...workingCopy(existing),
4291
4633
  ...stripUndefined(d)
4292
4634
  });
4293
- await this.assertSaveable(merged, user, existing.id);
4294
- if (merged.slug !== existing.slug) await this.assertNotCalled(existing.slug, [
4295
- existing.id
4296
- ]);
4297
- else await this.assertEndpointsNotCalled(existing, merged);
4298
- d.version = existing.version + 1;
4635
+ await this.assertSaveable(settings, user, existing.id);
4636
+ if (!isPublished(existing)) await this.assertCallersKept(existing, settings, {
4637
+ isActive: d.isActive ?? existing.isActive,
4638
+ version: existing.version
4639
+ });
4640
+ if (d.nodes) d.nodes = (0,_flow_engine_flow_api_key_js__rspack_import_9/* .sealApiKeys */.ue)(d.nodes);
4299
4641
  }
4300
4642
  }
4643
+ /** A published flow keeps running as published: the edits go to its draft (none when they match what is live). */ async convertSingleDtoToEntity(dto, user) {
4644
+ const id = dto.id;
4645
+ if (!id) return super.convertSingleDtoToEntity(dto, user);
4646
+ const existing = await this.findOrFail(id);
4647
+ if (!isPublished(existing)) return super.convertSingleDtoToEntity(dto, user);
4648
+ const draft = snapshotOf({
4649
+ ...workingCopy(existing),
4650
+ ...stripUndefined(dto)
4651
+ });
4652
+ existing.draft = sameSnapshot(draft, snapshotOf(existing)) ? null : draft;
4653
+ return existing;
4654
+ }
4655
+ /**
4656
+ * Makes the draft (or, before the first publish, the flow as saved) what callers run, as the next
4657
+ * version. It is checked like a save, plus what only going live can break: other flows calling
4658
+ * this one by a URL name or path it no longer has.
4659
+ */ async publish(id, note, user) {
4660
+ const existing = await this.findOrFail(id);
4661
+ const published = isPublished(existing);
4662
+ if (published && !existing.draft) throw new _nestjs_common__rspack_import_4.BadRequestException({
4663
+ message: 'There are no changes to publish.',
4664
+ messageKey: _config_message_keys_js__rspack_import_14/* .FLOW_MESSAGES.NOTHING_TO_PUBLISH */.wu.NOTHING_TO_PUBLISH
4665
+ });
4666
+ const next = workingCopy(existing);
4667
+ const settings = toFlowSettings(next);
4668
+ await this.assertSaveable(settings, user, existing.id);
4669
+ await this.assertCallersKept(existing, settings, {
4670
+ isActive: next.isActive ?? existing.isActive,
4671
+ version: existing.version + 1
4672
+ });
4673
+ const qr = await this.provider.getQueryRunner();
4674
+ try {
4675
+ await qr.connect();
4676
+ await qr.startTransaction();
4677
+ const versions = qr.manager.getRepository(_entities_flow_version_entity_js__rspack_import_8/* .FlowVersion */.p);
4678
+ Object.assign(existing, next, {
4679
+ draft: null,
4680
+ version: existing.version + 1,
4681
+ updatedById: user?.id ?? null
4682
+ });
4683
+ await qr.manager.getRepository(_entities_flow_definition_entity_js__rspack_import_7/* .FlowDefinition */.G).save(existing);
4684
+ await versions.save(versions.create({
4685
+ flowId: existing.id,
4686
+ version: existing.version,
4687
+ definition: next,
4688
+ note: note?.trim() || null,
4689
+ publishedAt: new Date(),
4690
+ publishedById: user?.id ?? null
4691
+ }));
4692
+ await qr.commitTransaction();
4693
+ } catch (error) {
4694
+ await qr.rollbackTransaction().catch(()=>undefined);
4695
+ throw error;
4696
+ } finally{
4697
+ await qr.release().catch(()=>undefined);
4698
+ }
4699
+ await this.clearCaches(existing);
4700
+ await this.provisionPermissions([
4701
+ existing
4702
+ ]);
4703
+ return existing;
4704
+ }
4705
+ /** Newest first, without the definitions (`getVersion` returns one in full). */ async listVersions(flowId) {
4706
+ await this.findOrFail(flowId);
4707
+ return (await this.provider.getRepository(_entities_flow_version_entity_js__rspack_import_8/* .FlowVersion */.p)).find({
4708
+ where: {
4709
+ flowId
4710
+ },
4711
+ order: {
4712
+ version: 'DESC'
4713
+ },
4714
+ select: {
4715
+ id: true,
4716
+ flowId: true,
4717
+ version: true,
4718
+ note: true,
4719
+ publishedAt: true,
4720
+ publishedById: true
4721
+ }
4722
+ });
4723
+ }
4724
+ async getVersion(flowId, version) {
4725
+ const row = await (await this.provider.getRepository(_entities_flow_version_entity_js__rspack_import_8/* .FlowVersion */.p)).findOne({
4726
+ where: {
4727
+ flowId,
4728
+ version
4729
+ }
4730
+ });
4731
+ if (!row) throw new _nestjs_common__rspack_import_4.NotFoundException({
4732
+ message: `Version ${version} of this flow was not found.`,
4733
+ messageKey: _config_message_keys_js__rspack_import_14/* .FLOW_MESSAGES.VERSION_NOT_FOUND */.wu.VERSION_NOT_FOUND,
4734
+ messageVariables: {
4735
+ version
4736
+ }
4737
+ });
4738
+ return row;
4739
+ }
4740
+ /**
4741
+ * Puts an old version back into the draft; callers keep the published flow until it is published
4742
+ * again. Not checked like a save (the entities it names may have changed since), but the author
4743
+ * still needs the permissions its code steps call for.
4744
+ */ async restoreVersion(flowId, version, user) {
4745
+ const flow = await this.findOrFail(flowId);
4746
+ const snapshot = snapshotOf((await this.getVersion(flowId, version)).definition);
4747
+ await this.assertMayAuthor(toFlowSettings(snapshot), user);
4748
+ flow.draft = sameSnapshot(snapshot, snapshotOf(flow)) ? null : snapshot;
4749
+ return this.saveDraft(flow, user);
4750
+ }
4751
+ async discardDraft(flowId, user) {
4752
+ const flow = await this.findOrFail(flowId);
4753
+ if (!flow.draft) throw new _nestjs_common__rspack_import_4.BadRequestException({
4754
+ message: 'This flow has no unpublished changes.',
4755
+ messageKey: _config_message_keys_js__rspack_import_14/* .FLOW_MESSAGES.NO_DRAFT */.wu.NO_DRAFT
4756
+ });
4757
+ flow.draft = null;
4758
+ return this.saveDraft(flow, user);
4759
+ }
4760
+ async saveDraft(flow, user) {
4761
+ await (await this.provider.getRepository(_entities_flow_definition_entity_js__rspack_import_7/* .FlowDefinition */.G)).update(flow.id, {
4762
+ draft: flow.draft,
4763
+ updatedById: user?.id ?? null
4764
+ });
4765
+ await this.clearCaches(flow);
4766
+ return flow;
4767
+ }
4768
+ async clearCaches(flow) {
4769
+ if (!this.isCacheable) return;
4770
+ await Promise.all([
4771
+ this.clearCacheForAll(),
4772
+ this.clearCacheForId([
4773
+ flow
4774
+ ])
4775
+ ]);
4776
+ }
4301
4777
  async beforeDeleteOperation(dto, user, _qr) {
4302
4778
  const ids = Array.isArray(dto.id) ? dto.id : [
4303
4779
  dto.id
4304
4780
  ];
4305
- const flows = await (await this.provider.getRepository(_entities_flow_definition_entity_js__rspack_import_6/* .FlowDefinition */.G)).find({
4781
+ const flows = await (await this.provider.getRepository(_entities_flow_definition_entity_js__rspack_import_7/* .FlowDefinition */.G)).find({
4306
4782
  where: {
4307
- id: (0,typeorm__rspack_import_4.In)(ids)
4783
+ id: (0,typeorm__rspack_import_5.In)(ids)
4308
4784
  },
4309
4785
  withDeleted: true
4310
4786
  });
@@ -4312,39 +4788,50 @@ class FlowDefinitionService extends _flusys_nestjs_shared_classes__rspack_import
4312
4788
  if (dto.type === 'restore') return;
4313
4789
  for (const flow of flows)await this.assertNotCalled(flow.slug, ids);
4314
4790
  }
4315
- async afterInsertOperation(entities) {
4316
- await this.provisionPermissions(entities);
4317
- }
4318
- async afterUpdateOperation(entities) {
4319
- await this.provisionPermissions(entities);
4320
- }
4321
- async provisionPermissions(flows) {
4322
- const needing = flows.filter((f)=>f.authMode === 'permission');
4323
- if (!needing.length) return;
4791
+ /** The flow's execute permission, registered so it can be granted, when a published URL asks for it (a URL with its own rule names existing permissions). */ async provisionPermissions(flows) {
4324
4792
  if (!this.actionRegistry) return;
4325
- await this.actionRegistry.registerActions(needing.map((f)=>({
4326
- code: f.permissionCode || defaultFlowPermissionCode(f.slug),
4327
- name: `Run ${f.name}`
4793
+ const actions = new Map();
4794
+ for (const flow of flows){
4795
+ for (const endpoint of (0,_flow_engine_flow_types_js__rspack_import_11/* .flowEndpoints */.Xr)(flow)){
4796
+ if (endpoint.authMode !== 'permission' || endpoint.permissions) continue;
4797
+ const code = defaultFlowPermissionCode(flow.slug);
4798
+ if (!actions.has(code)) actions.set(code, endpoint.path ? `Run ${flow.name} (${endpoint.path})` : `Run ${flow.name}`);
4799
+ }
4800
+ }
4801
+ if (actions.size) await this.actionRegistry.registerActions([
4802
+ ...actions
4803
+ ].map(([code, name])=>({
4804
+ code,
4805
+ name
4328
4806
  })));
4329
4807
  }
4330
4808
  }
4331
4809
  FlowDefinitionService = _ts_decorate([
4332
- (0,_nestjs_common__rspack_import_3.Injectable)({
4333
- scope: _nestjs_common__rspack_import_3.Scope.REQUEST
4810
+ (0,_nestjs_common__rspack_import_4.Injectable)({
4811
+ scope: _nestjs_common__rspack_import_4.Scope.REQUEST
4334
4812
  }),
4335
- _ts_param(0, (0,_nestjs_common__rspack_import_3.Inject)('CACHE_INSTANCE')),
4336
- _ts_param(1, (0,_nestjs_common__rspack_import_3.Inject)(_flusys_nestjs_shared_modules__rspack_import_2.UtilsService)),
4337
- _ts_param(2, (0,_nestjs_common__rspack_import_3.Inject)(_entity_builder_datasource_provider_js__rspack_import_9/* .EntityBuilderDataSourceProvider */.D)),
4338
- _ts_param(3, (0,_nestjs_common__rspack_import_3.Inject)(_flusys_nestjs_shared_modules__rspack_import_2.SharedPermissionCacheService)),
4339
- _ts_param(4, (0,_nestjs_common__rspack_import_3.Optional)()),
4340
- _ts_param(4, (0,_nestjs_common__rspack_import_3.Inject)(_flusys_nestjs_shared__rspack_import_0.PERMISSION_ACTION_REGISTRY)),
4813
+ _ts_param(0, (0,_nestjs_common__rspack_import_4.Inject)('CACHE_INSTANCE')),
4814
+ _ts_param(1, (0,_nestjs_common__rspack_import_4.Inject)(_flusys_nestjs_shared_modules__rspack_import_3.UtilsService)),
4815
+ _ts_param(2, (0,_nestjs_common__rspack_import_4.Inject)(_entity_builder_datasource_provider_js__rspack_import_12/* .EntityBuilderDataSourceProvider */.D)),
4816
+ _ts_param(3, (0,_nestjs_common__rspack_import_4.Inject)(_flusys_nestjs_shared_modules__rspack_import_3.SharedPermissionCacheService)),
4817
+ _ts_param(4, (0,_nestjs_common__rspack_import_4.Optional)()),
4818
+ _ts_param(4, (0,_nestjs_common__rspack_import_4.Inject)(_flusys_nestjs_shared__rspack_import_0.PERMISSION_ACTION_REGISTRY)),
4819
+ _ts_param(5, (0,_nestjs_common__rspack_import_4.Optional)()),
4820
+ _ts_param(5, (0,_nestjs_common__rspack_import_4.Inject)(_flusys_nestjs_shared_interfaces__rspack_import_2.NOTIFICATION_ADAPTER)),
4821
+ _ts_param(6, (0,_nestjs_common__rspack_import_4.Optional)()),
4822
+ _ts_param(6, (0,_nestjs_common__rspack_import_4.Inject)(_flusys_nestjs_shared_interfaces__rspack_import_2.EMAIL_ADAPTER)),
4823
+ _ts_param(7, (0,_nestjs_common__rspack_import_4.Optional)()),
4824
+ _ts_param(7, (0,_nestjs_common__rspack_import_4.Inject)(_flusys_nestjs_shared_interfaces__rspack_import_2.COMPANY_ACCESS_RESOLVER)),
4341
4825
  _ts_metadata("design:type", Function),
4342
4826
  _ts_metadata("design:paramtypes", [
4343
4827
  typeof _flusys_nestjs_shared_classes__rspack_import_1.HybridCache === "undefined" ? Object : _flusys_nestjs_shared_classes__rspack_import_1.HybridCache,
4344
- typeof _flusys_nestjs_shared_modules__rspack_import_2.UtilsService === "undefined" ? Object : _flusys_nestjs_shared_modules__rspack_import_2.UtilsService,
4345
- typeof _entity_builder_datasource_provider_js__rspack_import_9/* .EntityBuilderDataSourceProvider */.D === "undefined" ? Object : _entity_builder_datasource_provider_js__rspack_import_9/* .EntityBuilderDataSourceProvider */.D,
4346
- typeof _flusys_nestjs_shared_modules__rspack_import_2.SharedPermissionCacheService === "undefined" ? Object : _flusys_nestjs_shared_modules__rspack_import_2.SharedPermissionCacheService,
4347
- typeof IPermissionActionRegistry === "undefined" ? Object : IPermissionActionRegistry
4828
+ typeof _flusys_nestjs_shared_modules__rspack_import_3.UtilsService === "undefined" ? Object : _flusys_nestjs_shared_modules__rspack_import_3.UtilsService,
4829
+ typeof _entity_builder_datasource_provider_js__rspack_import_12/* .EntityBuilderDataSourceProvider */.D === "undefined" ? Object : _entity_builder_datasource_provider_js__rspack_import_12/* .EntityBuilderDataSourceProvider */.D,
4830
+ typeof _flusys_nestjs_shared_modules__rspack_import_3.SharedPermissionCacheService === "undefined" ? Object : _flusys_nestjs_shared_modules__rspack_import_3.SharedPermissionCacheService,
4831
+ typeof IPermissionActionRegistry === "undefined" ? Object : IPermissionActionRegistry,
4832
+ typeof INotificationAdapter === "undefined" ? Object : INotificationAdapter,
4833
+ typeof IEmailAdapter === "undefined" ? Object : IEmailAdapter,
4834
+ typeof ICompanyAccessResolver === "undefined" ? Object : ICompanyAccessResolver
4348
4835
  ])
4349
4836
  ], FlowDefinitionService);
4350
4837
  /** What the `call_flow` nodes of a flow run: a slug, and the path of the Request step they start at (null: the flow's own URL). */ function calledTargets(nodes) {
@@ -4356,6 +4843,13 @@ FlowDefinitionService = _ts_decorate([
4356
4843
  };
4357
4844
  });
4358
4845
  }
4846
+ /** What a Call flow step checks about a flow: it runs only once published and switched on. */ function callableOf(flow) {
4847
+ return {
4848
+ isActive: flow.isActive && isPublished(flow),
4849
+ endpoints: (0,_flow_engine_flow_types_js__rspack_import_11/* .flowEndpoints */.Xr)(flow),
4850
+ calls: calledSlugs(flow.nodes)
4851
+ };
4852
+ }
4359
4853
  /** Slugs the `call_flow` nodes of a flow run. */ function calledSlugs(nodes) {
4360
4854
  return calledTargets(nodes).map((target)=>target.slug);
4361
4855
  }
@@ -4365,10 +4859,14 @@ function stripUndefined(value) {
4365
4859
 
4366
4860
  __webpack_require__.d(__webpack_exports__, {
4367
4861
  RK: () => (toFlowSettings),
4368
- Tk: () => (FlowDefinitionService)
4862
+ Tk: () => (FlowDefinitionService),
4863
+ eY: () => (snapshotOf)
4369
4864
  }, {
4370
4865
  Er: defaultFlowPermissionCode,
4371
- YW: flowNotFound
4866
+ HF: isPublished,
4867
+ YW: flowNotFound,
4868
+ j4: flowPermissions,
4869
+ lw: workingCopy
4372
4870
  });
4373
4871
 
4374
4872
 
@@ -4428,8 +4926,11 @@ class FlowExecutionService {
4428
4926
  await repo.save(repo.create({
4429
4927
  flowId: entry.flowId,
4430
4928
  flowSlug: entry.flowSlug,
4929
+ flowVersion: entry.flowVersion,
4431
4930
  status: entry.result.status,
4432
4931
  trigger: entry.trigger,
4932
+ endpointNodeId: entry.endpoint?.nodeId ?? null,
4933
+ endpointPath: entry.endpoint?.path ?? null,
4433
4934
  mode: entry.mode,
4434
4935
  statusCode: entry.result.statusCode,
4435
4936
  input: inputText.length > INPUT_LIMIT_CHARS ? {
@@ -4480,6 +4981,8 @@ class FlowExecutionService {
4480
4981
  const where = {};
4481
4982
  if (query.flowId) where.flowId = query.flowId;
4482
4983
  if (query.status) where.status = query.status;
4984
+ if (query.trigger) where.trigger = query.trigger;
4985
+ if (query.endpointNodeId) where.endpointNodeId = query.endpointNodeId;
4483
4986
  const [rows, total] = await repo.findAndCount({
4484
4987
  where,
4485
4988
  order: {
@@ -4492,8 +4995,11 @@ class FlowExecutionService {
4492
4995
  id: true,
4493
4996
  flowId: true,
4494
4997
  flowSlug: true,
4998
+ flowVersion: true,
4495
4999
  status: true,
4496
5000
  trigger: true,
5001
+ endpointNodeId: true,
5002
+ endpointPath: true,
4497
5003
  mode: true,
4498
5004
  statusCode: true,
4499
5005
  durationMs: true,
@@ -4538,7 +5044,7 @@ __webpack_require__.d(__webpack_exports__, {
4538
5044
 
4539
5045
 
4540
5046
  },
4541
- 4698(__unused_rspack_module, __webpack_exports__, __webpack_require__) {
5047
+ 3908(__unused_rspack_module, __webpack_exports__, __webpack_require__) {
4542
5048
 
4543
5049
  // EXPORTS
4544
5050
  __webpack_require__.d(__webpack_exports__, {
@@ -4548,10 +5054,14 @@ __webpack_require__.d(__webpack_exports__, {
4548
5054
 
4549
5055
  // EXTERNAL MODULE: external "@flusys/nestjs-shared/classes"
4550
5056
  var classes_ = __webpack_require__(6937);
4551
- // EXTERNAL MODULE: external "@flusys/nestjs-shared/exceptions"
4552
- var exceptions_ = __webpack_require__(2417);
5057
+ ;// CONCATENATED MODULE: external "@flusys/nestjs-shared/exceptions"
5058
+
5059
+ // EXTERNAL MODULE: external "@flusys/nestjs-shared/interfaces"
5060
+ var interfaces_ = __webpack_require__(5103);
4553
5061
  // EXTERNAL MODULE: external "@flusys/nestjs-shared/modules"
4554
5062
  var modules_ = __webpack_require__(1236);
5063
+ // EXTERNAL MODULE: external "@flusys/nestjs-shared/utils"
5064
+ var utils_ = __webpack_require__(9216);
4555
5065
  // EXTERNAL MODULE: external "@nestjs/common"
4556
5066
  var common_ = __webpack_require__(6157);
4557
5067
  // EXTERNAL MODULE: external "@nestjs/core"
@@ -4789,8 +5299,8 @@ const BLOCKED_REQUEST_HEADERS = new Set([
4789
5299
  });
4790
5300
  }
4791
5301
 
4792
- // EXTERNAL MODULE: ./projects/nestjs-entity-builder/src/rule-engine/index.ts + 2 modules
4793
- var rule_engine = __webpack_require__(5920);
5302
+ // EXTERNAL MODULE: ./projects/nestjs-entity-builder/src/rule-engine/index.ts + 1 modules
5303
+ var rule_engine = __webpack_require__(8429);
4794
5304
  // EXTERNAL MODULE: ./projects/nestjs-entity-builder/src/services/entity-builder-config.service.ts
4795
5305
  var entity_builder_config_service = __webpack_require__(4281);
4796
5306
  // EXTERNAL MODULE: ./projects/nestjs-entity-builder/src/services/entity-builder-datasource.provider.ts
@@ -4843,6 +5353,8 @@ function _ts_param(paramIndex, decorator) {
4843
5353
 
4844
5354
 
4845
5355
 
5356
+
5357
+
4846
5358
 
4847
5359
 
4848
5360
 
@@ -4853,27 +5365,32 @@ const publishFlowEvent = (eventName, payload, user)=>(0,classes_.publishDomainEv
4853
5365
  metadata: payload,
4854
5366
  user
4855
5367
  });
4856
- /** The run's time budget, whatever a definition asks for. */ const flowTimeout = (timeoutMs)=>typeof timeoutMs === 'number' && Number.isFinite(timeoutMs) ? Math.min(Math.max(timeoutMs, flow_types/* .FLOW_LIMITS.MIN_TIMEOUT_MS */.Or.MIN_TIMEOUT_MS), flow_types/* .FLOW_LIMITS.MAX_TIMEOUT_MS */.Or.MAX_TIMEOUT_MS) : flow_types/* .FLOW_LIMITS.DEFAULT_TIMEOUT_MS */.Or.DEFAULT_TIMEOUT_MS;
5368
+ /** The run's time budget, whatever a Request step asks for. */ const flowTimeout = (timeoutMs)=>typeof timeoutMs === 'number' && Number.isFinite(timeoutMs) ? Math.min(Math.max(timeoutMs, flow_types/* .FLOW_LIMITS.MIN_TIMEOUT_MS */.Or.MIN_TIMEOUT_MS), flow_types/* .FLOW_LIMITS.MAX_TIMEOUT_MS */.Or.MAX_TIMEOUT_MS) : flow_types/* .FLOW_LIMITS.DEFAULT_TIMEOUT_MS */.Or.DEFAULT_TIMEOUT_MS;
4857
5369
  class FlowExecutorService {
4858
5370
  provider;
4859
5371
  ruleEngine;
4860
5372
  moduleRef;
4861
5373
  permissionCache;
4862
5374
  config;
5375
+ companyAccess;
5376
+ notifications;
5377
+ email;
5378
+ branchHierarchy;
4863
5379
  logger = new common_.Logger(FlowExecutorService.name);
4864
- constructor(provider, ruleEngine, moduleRef, permissionCache, config){
5380
+ constructor(provider, ruleEngine, moduleRef, permissionCache, config, companyAccess, notifications, email, branchHierarchy){
4865
5381
  this.provider = provider;
4866
5382
  this.ruleEngine = ruleEngine;
4867
5383
  this.moduleRef = moduleRef;
4868
5384
  this.permissionCache = permissionCache;
4869
5385
  this.config = config;
5386
+ this.companyAccess = companyAccess;
5387
+ this.notifications = notifications;
5388
+ this.email = email;
5389
+ this.branchHierarchy = branchHierarchy;
4870
5390
  }
4871
- async run(definition, options) {
5391
+ async run(flow, options) {
4872
5392
  const startedAt = Date.now();
4873
- const flow = {
4874
- ...definition,
4875
- timeoutMs: flowTimeout(definition.timeoutMs)
4876
- };
5393
+ const start = (0,flow_types/* .startEndpoint */.IE)(flow, options.startAt);
4877
5394
  const state = {
4878
5395
  startAt: options.startAt,
4879
5396
  input: options.input,
@@ -4881,14 +5398,14 @@ class FlowExecutorService {
4881
5398
  context: {},
4882
5399
  request: options.request,
4883
5400
  user: options.user,
5401
+ authorization: options.authorization,
4884
5402
  permissions: this.ruleEngine.createPermissionResolver(options.user),
4885
5403
  mode: options.mode,
4886
- runAs: flow.runAs,
4887
- systemAuthorized: flow.runAs === 'system' && options.systemAuthorized === true,
4888
5404
  steps: 0,
4889
5405
  httpCalls: 0,
5406
+ messages: 0,
4890
5407
  writes: 0,
4891
- deadline: startedAt + flow.timeoutMs,
5408
+ deadline: startedAt + flowTimeout(start?.timeoutMs),
4892
5409
  callStack: [
4893
5410
  flow.slug
4894
5411
  ],
@@ -4896,15 +5413,7 @@ class FlowExecutorService {
4896
5413
  trace: []
4897
5414
  };
4898
5415
  try {
4899
- if (!flow_types/* .FLOW_RUN_AS.includes */.gl.includes(flow.runAs)) {
4900
- throw new flow_errors/* .FlowHttpError */.hk(403, 'This flow is not allowed to run.', undefined, {
4901
- messageKey: message_keys/* .FLOW_ERROR_MESSAGES.RUN_AS_NOT_ALLOWED */.zr.RUN_AS_NOT_ALLOWED,
4902
- messageVariables: {
4903
- runAs: String(flow.runAs)
4904
- }
4905
- });
4906
- }
4907
- await this.execute(flow, state, this.createEnv());
5416
+ await this.execute(flow, start?.isTransactional === true, state, this.createEnv());
4908
5417
  return {
4909
5418
  ...(0,flow_engine/* .successResult */.JK)(state, startedAt),
4910
5419
  ...state.savedUpTo ? {
@@ -4934,10 +5443,10 @@ class FlowExecutorService {
4934
5443
  }
4935
5444
  }
4936
5445
  /**
4937
- * Walks `flow`. A flow called from inside a transaction joins it; otherwise a transactional
4938
- * flow (or any test run) gets its own, and its held-back events are published after the commit.
4939
- */ async execute(flow, state, env) {
4940
- if (state.qr || !(flow.isTransactional || state.mode === 'dry_run')) {
5446
+ * Walks `flow`. A flow called from inside a transaction joins it; otherwise a run whose Request step
5447
+ * is transactional (or any test run) gets its own, and its held-back events are published after the commit.
5448
+ */ async execute(flow, transactional, state, env) {
5449
+ if (state.qr || !(transactional || state.mode === 'dry_run')) {
4941
5450
  await new flow_engine/* .FlowEngine */.$I(flow, env).walk(state);
4942
5451
  return;
4943
5452
  }
@@ -5016,7 +5525,6 @@ class FlowExecutorService {
5016
5525
  max: flow_types/* .FLOW_LIMITS.MAX_CALL_DEPTH */.Or.MAX_CALL_DEPTH
5017
5526
  });
5018
5527
  const flow = await this.findCallable(slug, parent.qr);
5019
- await this.assertMayCall(flow, parent, env);
5020
5528
  const settings = (0,flow_definition_service/* .toFlowSettings */.RK)(flow);
5021
5529
  const start = (0,flow_types/* .findEndpoint */.vS)(settings, endpoint);
5022
5530
  if (!start) {
@@ -5025,6 +5533,7 @@ class FlowExecutorService {
5025
5533
  endpoint: endpoint ?? ''
5026
5534
  }, `Flow '${slug}' has no URL '${endpoint ?? ''}'.`);
5027
5535
  }
5536
+ await this.assertMayCall(flow, start, parent, env);
5028
5537
  const checked = (0,flow_input_validator/* .validateFlowInput */.v)(start.inputSchema, input, start.bodyType);
5029
5538
  if (checked.errors.length) {
5030
5539
  throw new flow_errors/* .FlowHttpError */.hk(400, 'Validation failed', checked.errors, {
@@ -5041,15 +5550,14 @@ class FlowExecutorService {
5041
5550
  context: {},
5042
5551
  request: parent.request,
5043
5552
  user: parent.user,
5553
+ authorization: parent.authorization,
5044
5554
  permissions: parent.permissions,
5045
5555
  mode: parent.mode,
5046
- runAs: flow.authMode === 'internal' ? parent.runAs : settings.runAs,
5047
- // A function inherits the caller's grant; any other called flow is a saved flow whose own run-as was checked when it was saved.
5048
- systemAuthorized: flow.authMode === 'internal' ? parent.systemAuthorized === true : settings.runAs === 'system',
5049
5556
  steps: parent.steps,
5050
5557
  httpCalls: parent.httpCalls,
5558
+ messages: parent.messages,
5051
5559
  writes: parent.writes,
5052
- deadline: Math.min(parent.deadline, Date.now() + flowTimeout(settings.timeoutMs)),
5560
+ deadline: Math.min(parent.deadline, Date.now() + flowTimeout(start.timeoutMs)),
5053
5561
  qr: parent.qr,
5054
5562
  callStack: [
5055
5563
  ...parent.callStack,
@@ -5064,13 +5572,14 @@ class FlowExecutorService {
5064
5572
  if (child.pendingEvents === parent.pendingEvents) parent.pendingEvents.length = queued;
5065
5573
  };
5066
5574
  try {
5067
- await this.execute(settings, child, env);
5575
+ await this.execute(settings, start.isTransactional, child, env);
5068
5576
  } catch (error) {
5069
5577
  dropQueued();
5070
5578
  throw calledFlowFailure(flow, error);
5071
5579
  } finally{
5072
5580
  parent.steps = child.steps;
5073
5581
  parent.httpCalls = child.httpCalls;
5582
+ parent.messages = child.messages;
5074
5583
  parent.writes = child.writes;
5075
5584
  }
5076
5585
  if (child.response) {
@@ -5089,7 +5598,8 @@ class FlowExecutorService {
5089
5598
  where: {
5090
5599
  slug,
5091
5600
  isActive: true,
5092
- deletedAt: (0,external_typeorm_.IsNull)()
5601
+ deletedAt: (0,external_typeorm_.IsNull)(),
5602
+ version: (0,external_typeorm_.MoreThan)(0)
5093
5603
  }
5094
5604
  });
5095
5605
  if (!flow) throw new flow_errors/* .FlowKeyedError */.Hp(message_keys/* .FLOW_ERROR_MESSAGES.CALLED_FLOW_NOT_FOUND */.zr.CALLED_FLOW_NOT_FOUND, {
@@ -5098,21 +5608,23 @@ class FlowExecutorService {
5098
5608
  return flow;
5099
5609
  }
5100
5610
  /**
5101
- * Any flow may call a function, and a flow running as the system may call any flow. One running
5102
- * as the caller may call only what that caller could call directly: public and signed-in flows,
5103
- * and permission flows when the caller holds the permission - never an API-key flow.
5104
- */ async assertMayCall(flow, parent, env) {
5105
- if (parent.runAs === 'system' && parent.systemAuthorized === true || flow.authMode === 'public' || flow.authMode === 'internal') return;
5106
- if (flow.authMode === 'api_key' || !parent.user) {
5611
+ * The called URL's own access, checked against the run's user: any flow may call a function or a
5612
+ * public URL; a signed-in one needs a user, a permission one the user holding it. An API-key URL
5613
+ * is never called from a flow, which has no key to send.
5614
+ */ async assertMayCall(flow, endpoint, parent, env) {
5615
+ const mode = endpoint.authMode;
5616
+ if (mode === 'public' || mode === 'internal') return;
5617
+ if (mode === 'api_key' || !parent.user) {
5107
5618
  throw new flow_errors/* .FlowKeyedError */.Hp(message_keys/* .FLOW_ERROR_MESSAGES.CALLED_FLOW_NOT_ALLOWED */.zr.CALLED_FLOW_NOT_ALLOWED, {
5108
5619
  flow: flow.slug,
5109
- authMode: flow.authMode
5110
- }, `Flow '${flow.slug}' (${flow.authMode}) cannot be called by this caller.`);
5620
+ authMode: mode
5621
+ }, `Flow '${flow.slug}' (${mode}) cannot be called by this caller.`);
5111
5622
  }
5112
- if (flow.authMode === 'permission') await env.assertPermission(flow.permissionCode || (0,flow_definition_service/* .defaultFlowPermissionCode */.Er)(flow.slug), parent.user);
5623
+ if (mode === 'permission') await env.assertPermissions((0,flow_definition_service/* .flowPermissions */.j4)(endpoint, flow.slug), parent.user);
5113
5624
  }
5114
5625
  createEnv() {
5115
5626
  const entityServices = new Map();
5627
+ /** One lookup per user for the whole run, however many steps ask. */ const access = new Map();
5116
5628
  let permissions;
5117
5629
  const env = {
5118
5630
  ruleEngine: this.ruleEngine,
@@ -5135,29 +5647,48 @@ class FlowExecutorService {
5135
5647
  atomic: (state, work)=>this.atomic(state, work),
5136
5648
  commit: (state)=>this.commitProgress(state),
5137
5649
  http: flowHttpRequest,
5650
+ notifications: this.notifications,
5651
+ email: this.email,
5652
+ ...this.companyAccess ? {
5653
+ companyMembers: (companyId, branchIds)=>this.companyAccess.getCompanyMembers(companyId, branchIds)
5654
+ } : {},
5655
+ ...this.branchHierarchy ? {
5656
+ branchTree: (branchIds)=>this.branchHierarchy.getDescendantIds(branchIds, {
5657
+ includeSelf: true
5658
+ })
5659
+ } : {},
5660
+ companyAccess: (userId)=>{
5661
+ if (!access.has(userId)) access.set(userId, this.companyAccess ? this.companyAccess.getUserAccess(userId) : Promise.resolve({
5662
+ companies: [],
5663
+ branches: []
5664
+ }));
5665
+ return access.get(userId);
5666
+ },
5138
5667
  publish: (eventName, payload, user)=>publishFlowEvent(eventName, payload, user),
5139
- assertPermission: async (code, user)=>{
5668
+ assertPermissions: async (logic, user)=>{
5140
5669
  if (!user) throw new common_.UnauthorizedException({
5141
5670
  message: 'Authentication required',
5142
5671
  messageKey: constants_.ERROR_MESSAGES.UNAUTHORIZED
5143
5672
  });
5144
- if (!this.permissionCache.isCacheConfigured()) throw new exceptions_.PermissionSystemUnavailableException();
5673
+ if (!this.permissionCache.isCacheConfigured()) throw new __rspack_external__flusys_nestjs_shared_exceptions_3edf22e9.PermissionSystemUnavailableException();
5145
5674
  permissions ??= this.permissionCache.getPermissions({
5146
5675
  userId: user.id,
5147
5676
  companyId: user.companyId,
5148
5677
  branchId: user.branchId
5149
5678
  });
5150
- const granted = await permissions;
5151
- if (!this.permissionCache.matchPermission(granted, code)) throw new exceptions_.InsufficientPermissionsException([
5152
- code
5153
- ], 'AND');
5679
+ const result = (0,utils_.evaluatePermissionLogic)(logic, await permissions);
5680
+ if (!result.passed) throw new __rspack_external__flusys_nestjs_shared_exceptions_3edf22e9.InsufficientPermissionsException(result.missingPermissions, result.operator);
5154
5681
  }
5155
5682
  };
5156
5683
  return env;
5157
5684
  }
5158
- /** After commit, so listeners see committed data. A failing publish never turns a completed run into an error. */ async publishPendingEvents(events, user) {
5685
+ /** After commit, so listeners see committed data and messages go out only for what was saved. A failing publish or send never turns a completed run into an error. */ async publishPendingEvents(events, user) {
5159
5686
  for (const event of events){
5160
5687
  try {
5688
+ if ('delivery' in event) {
5689
+ await event.send();
5690
+ continue;
5691
+ }
5161
5692
  if ('eventName' in event) {
5162
5693
  await publishFlowEvent(event.eventName, event.payload, user);
5163
5694
  continue;
@@ -5176,6 +5707,10 @@ class FlowExecutorService {
5176
5707
  user
5177
5708
  });
5178
5709
  } catch (error) {
5710
+ if ('delivery' in event) {
5711
+ this.logger.error(`Could not send the message of "${event.delivery}" after the commit: ${error.message}`);
5712
+ continue;
5713
+ }
5179
5714
  const name = 'eventName' in event ? event.eventName : `${event.entityCode}.${event.action}`;
5180
5715
  this.logger.error(`Could not publish ${name}: ${error.message}`);
5181
5716
  }
@@ -5189,13 +5724,25 @@ FlowExecutorService = _ts_decorate([
5189
5724
  _ts_param(2, (0,common_.Inject)(core_.ModuleRef)),
5190
5725
  _ts_param(3, (0,common_.Inject)(modules_.SharedPermissionCacheService)),
5191
5726
  _ts_param(4, (0,common_.Inject)(entity_builder_config_service/* .EntityBuilderConfigService */.E)),
5727
+ _ts_param(5, (0,common_.Optional)()),
5728
+ _ts_param(5, (0,common_.Inject)(interfaces_.COMPANY_ACCESS_RESOLVER)),
5729
+ _ts_param(6, (0,common_.Optional)()),
5730
+ _ts_param(6, (0,common_.Inject)(interfaces_.NOTIFICATION_ADAPTER)),
5731
+ _ts_param(7, (0,common_.Optional)()),
5732
+ _ts_param(7, (0,common_.Inject)(interfaces_.EMAIL_ADAPTER)),
5733
+ _ts_param(8, (0,common_.Optional)()),
5734
+ _ts_param(8, (0,common_.Inject)(interfaces_.BRANCH_HIERARCHY_RESOLVER)),
5192
5735
  _ts_metadata("design:type", Function),
5193
5736
  _ts_metadata("design:paramtypes", [
5194
5737
  typeof entity_builder_datasource_provider/* .EntityBuilderDataSourceProvider */.D === "undefined" ? Object : entity_builder_datasource_provider/* .EntityBuilderDataSourceProvider */.D,
5195
5738
  typeof rule_engine/* .RuleEngineService */.iI === "undefined" ? Object : rule_engine/* .RuleEngineService */.iI,
5196
5739
  typeof core_.ModuleRef === "undefined" ? Object : core_.ModuleRef,
5197
5740
  typeof modules_.SharedPermissionCacheService === "undefined" ? Object : modules_.SharedPermissionCacheService,
5198
- typeof entity_builder_config_service/* .EntityBuilderConfigService */.E === "undefined" ? Object : entity_builder_config_service/* .EntityBuilderConfigService */.E
5741
+ typeof entity_builder_config_service/* .EntityBuilderConfigService */.E === "undefined" ? Object : entity_builder_config_service/* .EntityBuilderConfigService */.E,
5742
+ typeof ICompanyAccessResolver === "undefined" ? Object : ICompanyAccessResolver,
5743
+ typeof INotificationAdapter === "undefined" ? Object : INotificationAdapter,
5744
+ typeof IEmailAdapter === "undefined" ? Object : IEmailAdapter,
5745
+ typeof IBranchHierarchyResolver === "undefined" ? Object : IBranchHierarchyResolver
5199
5746
  ])
5200
5747
  ], FlowExecutorService);
5201
5748
  /** A rejection or a limit inside the called flow is passed on as it is; anything else fails the calling node with the reason. */ function calledFlowFailure(flow, error) {
@@ -5238,7 +5785,7 @@ FlowExecutorService = _ts_decorate([
5238
5785
  /* import */ var _entity_builder_datasource_provider_js__rspack_import_5 = __webpack_require__(424);
5239
5786
  /* import */ var _flow_definition_service_js__rspack_import_6 = __webpack_require__(5229);
5240
5787
  /* import */ var _flow_execution_service_js__rspack_import_7 = __webpack_require__(7810);
5241
- /* import */ var _flow_executor_service_js__rspack_import_8 = __webpack_require__(4698);
5788
+ /* import */ var _flow_executor_service_js__rspack_import_8 = __webpack_require__(3908);
5242
5789
  function _ts_decorate(decorators, target, key, desc) {
5243
5790
  var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
5244
5791
  if (typeof Reflect === "object" && typeof Reflect.decorate === "function") {
@@ -5308,6 +5855,11 @@ const MAX_QUERY_VALUE_LENGTH = 2000;
5308
5855
  receivedAt: new Date().toISOString()
5309
5856
  };
5310
5857
  }
5858
+ /** The caller's own `Authorization` header, kept for Request steps that forward it: only for a signed-in (verified) caller. */ function callerAuthorization(caller, user) {
5859
+ if (!user) return undefined;
5860
+ const value = Object.entries(caller.headers).find(([key])=>key.toLowerCase() === 'authorization')?.[1];
5861
+ return typeof value === 'string' && value ? value : undefined;
5862
+ }
5311
5863
  /** A test run's request: the designer's own, with the headers / query / IP the author chose to simulate on top. */ function simulatedCaller(caller, simulated) {
5312
5864
  if (!simulated) return caller;
5313
5865
  const headers = {
@@ -5347,13 +5899,13 @@ class FlowRuntimeService {
5347
5899
  this.executions = executions;
5348
5900
  this.definitions = definitions;
5349
5901
  }
5350
- /** A function flow has no endpoint: it looks the same as an unknown one. */ async findActive(slug) {
5902
+ /** A flow never published is not live yet: it looks the same as an unknown one. */ async findActive(slug) {
5351
5903
  return (await this.provider.getRepository(_entities_flow_definition_entity_js__rspack_import_2/* .FlowDefinition */.G)).findOne({
5352
5904
  where: {
5353
5905
  slug,
5354
5906
  isActive: true,
5355
5907
  deletedAt: (0,typeorm__rspack_import_1.IsNull)(),
5356
- authMode: (0,typeorm__rspack_import_1.Not)('internal')
5908
+ version: (0,typeorm__rspack_import_1.MoreThan)(0)
5357
5909
  }
5358
5910
  });
5359
5911
  }
@@ -5362,25 +5914,23 @@ class FlowRuntimeService {
5362
5914
  const checked = (0,_flow_engine_flow_input_validator_js__rspack_import_3/* .validateFlowInput */.v)(endpoint.inputSchema, body, endpoint.bodyType);
5363
5915
  if (checked.errors.length) {
5364
5916
  const result = inputRejected(checked.errors);
5365
- await this.logWebhook(flow, body, result, startedAt, user, caller);
5917
+ await this.logWebhook(flow, endpoint, body, result, startedAt, user, caller);
5366
5918
  return result;
5367
5919
  }
5368
- // A saved flow: saving it as `runAs: system` needed the system permission.
5369
5920
  const result = await this.executor.run((0,_flow_definition_service_js__rspack_import_6/* .toFlowSettings */.RK)(flow), {
5370
5921
  mode: 'live',
5371
5922
  user,
5372
5923
  input: checked.values,
5373
5924
  startAt: endpoint.nodeId,
5374
5925
  request: requestInfo(caller),
5375
- exposeDetails: false,
5376
- systemAuthorized: true
5926
+ authorization: callerAuthorization(caller, user),
5927
+ exposeDetails: false
5377
5928
  });
5378
- await this.logWebhook(flow, checked.values, result, startedAt, user, caller);
5929
+ await this.logWebhook(flow, endpoint, checked.values, result, startedAt, user, caller);
5379
5930
  return result;
5380
5931
  }
5381
5932
  /** The designer's "Test run": a saved flow or an unsaved draft. */ async testRun(dto, user, caller) {
5382
5933
  let settings;
5383
- let permissionCode;
5384
5934
  let flow = null;
5385
5935
  if (dto.flowId) {
5386
5936
  flow = await this.definitions.findOrFail(dto.flowId);
@@ -5388,10 +5938,8 @@ class FlowRuntimeService {
5388
5938
  if (dto.definition) {
5389
5939
  // The designer's current draft, even for a saved flow: `flow` above is kept only to attribute the run.
5390
5940
  settings = (0,_flow_definition_service_js__rspack_import_6/* .toFlowSettings */.RK)(dto.definition);
5391
- permissionCode = dto.definition.permissionCode;
5392
5941
  } else if (flow) {
5393
5942
  settings = (0,_flow_definition_service_js__rspack_import_6/* .toFlowSettings */.RK)(flow);
5394
- permissionCode = flow.permissionCode;
5395
5943
  } else {
5396
5944
  throw new _nestjs_common__rspack_import_0.BadRequestException({
5397
5945
  message: 'Send a flowId or a definition to test.',
@@ -5399,15 +5947,22 @@ class FlowRuntimeService {
5399
5947
  });
5400
5948
  }
5401
5949
  const mode = dto.mode ?? 'dry_run';
5402
- // Checked before anything runs; passing it is what lets a `runAs: system` test touch entities.
5403
- await this.definitions.assertMayTest({
5404
- ...settings,
5405
- permissionCode
5406
- }, user, {
5950
+ const endpoint = (0,_flow_engine_flow_types_js__rspack_import_4/* .findEndpoint */.vS)(settings, dto.endpoint);
5951
+ // Checked before anything runs.
5952
+ await this.definitions.assertMayTest(settings, endpoint, user, {
5407
5953
  mode,
5408
5954
  draft: !!dto.definition,
5409
5955
  saved: !!flow
5410
5956
  });
5957
+ if (!endpoint) {
5958
+ throw new _nestjs_common__rspack_import_0.BadRequestException({
5959
+ message: `This flow has no URL '${dto.endpoint ?? ''}'.`,
5960
+ messageKey: _config_message_keys_js__rspack_import_9/* .FLOW_MESSAGES.ENDPOINT_NOT_FOUND */.wu.ENDPOINT_NOT_FOUND,
5961
+ messageVariables: {
5962
+ endpoint: dto.endpoint ?? ''
5963
+ }
5964
+ });
5965
+ }
5411
5966
  const validation = await this.definitions.validate(settings);
5412
5967
  if (validation.errors.length) {
5413
5968
  throw new _nestjs_common__rspack_import_0.BadRequestException({
@@ -5419,16 +5974,6 @@ class FlowRuntimeService {
5419
5974
  }))
5420
5975
  });
5421
5976
  }
5422
- const endpoint = (0,_flow_engine_flow_types_js__rspack_import_4/* .findEndpoint */.vS)(settings, dto.endpoint);
5423
- if (!endpoint) {
5424
- throw new _nestjs_common__rspack_import_0.BadRequestException({
5425
- message: `This flow has no URL '${dto.endpoint ?? ''}'.`,
5426
- messageKey: _config_message_keys_js__rspack_import_9/* .FLOW_MESSAGES.ENDPOINT_NOT_FOUND */.wu.ENDPOINT_NOT_FOUND,
5427
- messageVariables: {
5428
- endpoint: dto.endpoint ?? ''
5429
- }
5430
- });
5431
- }
5432
5977
  const startedAt = new Date();
5433
5978
  const simulated = simulatedCaller(caller, dto.request);
5434
5979
  const body = dto.input ?? (endpoint.bodyType === 'list' ? [] : {});
@@ -5439,13 +5984,15 @@ class FlowRuntimeService {
5439
5984
  input: checked.values,
5440
5985
  startAt: endpoint.nodeId,
5441
5986
  request: requestInfo(simulated),
5442
- exposeDetails: true,
5443
- systemAuthorized: true
5987
+ authorization: callerAuthorization(caller, user),
5988
+ exposeDetails: true
5444
5989
  });
5445
5990
  await this.executions.record({
5446
5991
  flowId: flow?.id ?? null,
5447
5992
  flowSlug: settings.slug,
5993
+ flowVersion: dto.definition ? null : flow?.version ?? null,
5448
5994
  trigger: 'test',
5995
+ endpoint,
5449
5996
  mode,
5450
5997
  input: body,
5451
5998
  result,
@@ -5459,11 +6006,13 @@ class FlowRuntimeService {
5459
6006
  warnings: validation.warnings
5460
6007
  };
5461
6008
  }
5462
- async logWebhook(flow, input, result, startedAt, user, caller) {
6009
+ async logWebhook(flow, endpoint, input, result, startedAt, user, caller) {
5463
6010
  await this.executions.record({
5464
6011
  flowId: flow.id,
5465
6012
  flowSlug: flow.slug,
6013
+ flowVersion: flow.version,
5466
6014
  trigger: 'webhook',
6015
+ endpoint,
5467
6016
  mode: 'live',
5468
6017
  input,
5469
6018
  result,
@@ -5865,6 +6414,7 @@ const FILTER_COMPARISON_SQL = {
5865
6414
  /** Soft-deletes the record, or deletes it for good when the entity has soft delete turned off. */ async delete(id, user, options = {}) {
5866
6415
  this.ensureActive();
5867
6416
  const repository = await this.ensureRepository();
6417
+ const soft = this.definition.isSoftDeleteEnabled;
5868
6418
  await this.withTransaction(options, async (queryRunner)=>{
5869
6419
  const existing = await queryRunner.manager.findOne(repository.target, {
5870
6420
  where: {
@@ -5872,7 +6422,6 @@ const FILTER_COMPARISON_SQL = {
5872
6422
  }
5873
6423
  });
5874
6424
  if (!existing) throw this.notFound(id);
5875
- const soft = this.definition.isSoftDeleteEnabled;
5876
6425
  if (soft) {
5877
6426
  await queryRunner.manager.softDelete(repository.target, [
5878
6427
  id
@@ -5898,6 +6447,7 @@ const FILTER_COMPARISON_SQL = {
5898
6447
  ]
5899
6448
  };
5900
6449
  }, user);
6450
+ return soft ? 'deleted' : 'purged';
5901
6451
  }
5902
6452
  async findById(id, options = {}) {
5903
6453
  const repository = await this.repositoryFor(options.queryRunner);
@@ -6100,181 +6650,6 @@ __webpack_require__.d(__webpack_exports__, {
6100
6650
  });
6101
6651
 
6102
6652
 
6103
- },
6104
- 5313(__unused_rspack_module, __webpack_exports__, __webpack_require__) {
6105
- /* import */ var node_crypto__rspack_import_0 = __webpack_require__(2561);
6106
- /* import */ var typeorm__rspack_import_1 = __webpack_require__(7158);
6107
- /* import */ var _enums_field_type_enum_js__rspack_import_2 = __webpack_require__(9614);
6108
-
6109
-
6110
-
6111
- /** Columns every dynamic table may carry (the `Identity` contract). Everything else is a designed field. */ const SYSTEM_COLUMN_NAMES = [
6112
- 'id',
6113
- 'created_at',
6114
- 'updated_at',
6115
- 'deleted_at',
6116
- 'created_by_id',
6117
- 'updated_by_id',
6118
- 'deleted_by_id'
6119
- ];
6120
- /** Field types stored as unbounded text or JSON: an index or unique constraint on them fails (MySQL) or breaks on large values (PostgreSQL). */ const NON_INDEXABLE_FIELD_TYPES = [
6121
- _enums_field_type_enum_js__rspack_import_2/* .FieldType.LONG_TEXT */.P.LONG_TEXT,
6122
- _enums_field_type_enum_js__rspack_import_2/* .FieldType.JSON */.P.JSON,
6123
- _enums_field_type_enum_js__rspack_import_2/* .FieldType.MULTI_SELECT */.P.MULTI_SELECT
6124
- ];
6125
- const isIndexableFieldType = (fieldType)=>!NON_INDEXABLE_FIELD_TYPES.includes(fieldType);
6126
- const uuidColumn = (name, driver, options = {})=>new typeorm__rspack_import_1.TableColumn({
6127
- name,
6128
- ...driver === 'postgres' ? {
6129
- type: 'uuid'
6130
- } : {
6131
- type: 'char',
6132
- length: '36'
6133
- },
6134
- ...options
6135
- });
6136
- function buildSystemColumns(flags, driver) {
6137
- const columns = [
6138
- uuidColumn('id', driver, {
6139
- isPrimary: true,
6140
- isGenerated: true,
6141
- generationStrategy: 'uuid'
6142
- }),
6143
- new typeorm__rspack_import_1.TableColumn({
6144
- name: 'created_at',
6145
- type: 'timestamp',
6146
- isNullable: false,
6147
- default: 'CURRENT_TIMESTAMP'
6148
- }),
6149
- new typeorm__rspack_import_1.TableColumn({
6150
- name: 'updated_at',
6151
- type: 'timestamp',
6152
- isNullable: false,
6153
- default: 'CURRENT_TIMESTAMP'
6154
- })
6155
- ];
6156
- if (flags.isSoftDeleteEnabled) columns.push(softDeleteColumn());
6157
- if (flags.isAuditEnabled) columns.push(...auditColumns(driver));
6158
- return columns;
6159
- }
6160
- const softDeleteColumn = ()=>new typeorm__rspack_import_1.TableColumn({
6161
- name: 'deleted_at',
6162
- type: 'timestamp',
6163
- isNullable: true
6164
- });
6165
- const auditColumns = (driver)=>[
6166
- uuidColumn('created_by_id', driver, {
6167
- isNullable: true
6168
- }),
6169
- uuidColumn('updated_by_id', driver, {
6170
- isNullable: true
6171
- }),
6172
- uuidColumn('deleted_by_id', driver, {
6173
- isNullable: true
6174
- })
6175
- ];
6176
- const sameColumns = (columnNames, expected)=>columnNames.length === expected.length && expected.every((c)=>columnNames.includes(c));
6177
- const NAME_HASH_LENGTH = 8;
6178
- /**
6179
- * Deterministic index/constraint name within the driver's identifier limit. The hash of
6180
- * `table.column` keeps names distinct when the readable part is cut short, and when two
6181
- * table/column pairs would read the same (`eb_a_b` + `c` vs `eb_a` + `b_c`).
6182
- */ function indexName(prefix, table, column, maxLength) {
6183
- const hash = (0,node_crypto__rspack_import_0.createHash)('sha256').update(`${table}.${column}`).digest('hex').slice(0, NAME_HASH_LENGTH);
6184
- const room = maxLength - prefix.length - hash.length - 2;
6185
- return `${prefix}_${`${table}_${column}`.slice(0, room)}_${hash}`;
6186
- }
6187
- const plainIndex = (table, column, driver, adapter)=>new typeorm__rspack_import_1.TableIndex({
6188
- name: indexName('idx', table, column, adapter.maxIdentifierLength(driver)),
6189
- columnNames: [
6190
- column
6191
- ]
6192
- });
6193
- const tableUnique = (table, column, driver, adapter)=>new typeorm__rspack_import_1.TableUnique({
6194
- name: indexName('uq', table, column, adapter.maxIdentifierLength(driver)),
6195
- columnNames: [
6196
- column
6197
- ]
6198
- });
6199
- /** MySQL has no unique constraints (TypeORM refuses them), only unique indexes. */ async function addUnique(qr, table, column, driver, adapter) {
6200
- const name = indexName('uq', table.name, column, adapter.maxIdentifierLength(driver));
6201
- if (driver === 'mysql') {
6202
- await qr.createIndex(table, new typeorm__rspack_import_1.TableIndex({
6203
- name,
6204
- columnNames: [
6205
- column
6206
- ],
6207
- isUnique: true
6208
- }));
6209
- return;
6210
- }
6211
- await qr.createUniqueConstraint(table, new typeorm__rspack_import_1.TableUnique({
6212
- name,
6213
- columnNames: [
6214
- column
6215
- ]
6216
- }));
6217
- }
6218
- /** The stored column type, with a `uuid` stored before MySQL support became char(36) resolved to what MySQL accepts. */ function columnType(field, driver, adapter) {
6219
- const type = field.dbType || adapter.mapFieldType(field.fieldType, driver);
6220
- return driver === 'mysql' && type === 'uuid' ? 'char' : type;
6221
- }
6222
- /**
6223
- * Column for a field. `enforceNotNull` controls whether the DB column is NOT NULL: true when
6224
- * the table is brand new (no rows can violate it); false when adding to a table that already
6225
- * has rows (the NOT NULL is applied afterwards, once the column has been backfilled).
6226
- */ function buildFieldColumn(field, driver, adapter, options) {
6227
- const sizing = adapter.getColumnSizing(field.fieldType, driver);
6228
- return new typeorm__rspack_import_1.TableColumn({
6229
- name: field.code,
6230
- type: columnType(field, driver, adapter),
6231
- length: sizing.length,
6232
- precision: sizing.precision ?? undefined,
6233
- scale: sizing.scale,
6234
- isNullable: options.enforceNotNull ? field.nullable : true,
6235
- isUnique: options.withUnique ? field.unique : false
6236
- });
6237
- }
6238
- /** The SQL type text for a field type on a driver, e.g. `varchar(255)`, `numeric(18,4)`, `char(36)`. */ function sqlTypeFor(fieldType, driver, adapter) {
6239
- const base = adapter.mapFieldType(fieldType, driver);
6240
- const sizing = adapter.getColumnSizing(fieldType, driver);
6241
- if (sizing.length) return `${base}(${sizing.length})`;
6242
- if (sizing.precision !== undefined) return `${base}(${sizing.precision},${sizing.scale ?? 0})`;
6243
- return base;
6244
- }
6245
- /** Coarse type family, so `character varying` (pg) and `varchar` (mysql) compare equal. */ function typeFamily(type) {
6246
- const t = type.toLowerCase();
6247
- if (t === 'uuid') return 'uuid';
6248
- if (t.includes('json')) return 'json';
6249
- if (t.includes('timestamp') || t === 'datetime') return 'timestamp';
6250
- if (t === 'date') return 'date';
6251
- if (t.includes('bool') || t === 'tinyint') return 'boolean';
6252
- if (t.includes('int') && !t.includes('point')) return 'integer';
6253
- if (t.includes('numeric') || t.includes('decimal')) return 'numeric';
6254
- if (t.includes('char') || t === 'varchar') return 'varchar';
6255
- if (t.includes('text')) return 'text';
6256
- return t;
6257
- }
6258
-
6259
- __webpack_require__.d(__webpack_exports__, {
6260
- $6: () => (addUnique),
6261
- Rm: () => (buildSystemColumns),
6262
- cg: () => (sqlTypeFor),
6263
- fu: () => (buildFieldColumn),
6264
- jp: () => (typeFamily),
6265
- mo: () => (indexName)
6266
- }, {
6267
- G7: sameColumns,
6268
- HK: auditColumns,
6269
- OG: softDeleteColumn,
6270
- d4: isIndexableFieldType,
6271
- gW: NON_INDEXABLE_FIELD_TYPES,
6272
- mq: SYSTEM_COLUMN_NAMES,
6273
- uy: tableUnique,
6274
- v7: plainIndex
6275
- });
6276
-
6277
-
6278
6653
  },
6279
6654
  7259(__unused_rspack_module, __webpack_exports__, __webpack_require__) {
6280
6655
  /* import */ var _nestjs_common__rspack_import_0 = __webpack_require__(6157);