@flusys/nestjs-form-builder 9.1.0 → 9.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/fesm/432.js CHANGED
@@ -3,6 +3,7 @@ import { Identity as entities_Identity } from "@flusys/nestjs-shared/entities";
3
3
  import * as __rspack_external__flusys_nestjs_shared_classes_6076a329 from "@flusys/nestjs-shared/classes";
4
4
  import * as __rspack_external__flusys_nestjs_shared_decorators_c129b601 from "@flusys/nestjs-shared/decorators";
5
5
  import * as __rspack_external__flusys_nestjs_shared_dtos_c4585026 from "@flusys/nestjs-shared/dtos";
6
+ import * as __rspack_external__flusys_nestjs_shared_interfaces_413f2706 from "@flusys/nestjs-shared/interfaces";
6
7
  import * as __rspack_external__flusys_nestjs_shared_modules_24702bee from "@flusys/nestjs-shared/modules";
7
8
  import * as __rspack_external__flusys_nestjs_shared_utils_9125c1f1 from "@flusys/nestjs-shared/utils";
8
9
  import * as __rspack_external__nestjs_common_a04939da from "@nestjs/common";
@@ -67,7 +68,8 @@ const FORM_MESSAGES = {
67
68
  OTP_INVALID: 'form.email.otp.invalid',
68
69
  OTP_EXPIRED: 'form.email.otp.expired',
69
70
  OTP_TOO_MANY_ATTEMPTS: 'form.email.otp.too.many.attempts',
70
- OTP_VERIFIED: 'form.email.otp.verified'
71
+ OTP_VERIFIED: 'form.email.otp.verified',
72
+ INVALID_COMPUTED_FIELD: 'form.invalid.computed.field'
71
73
  };
72
74
  const FORM_RESULT_MESSAGES = {
73
75
  GET_ALL_SUCCESS: 'form.result.get.all.success',
@@ -379,7 +381,7 @@ class Form extends entities_Identity {
379
381
  schema;
380
382
  schemaVersion;
381
383
  accessType;
382
- actionGroups;
384
+ permissionLogic;
383
385
  tags;
384
386
  isActive;
385
387
  }
@@ -436,10 +438,10 @@ _ts_decorate([
436
438
  (0,external_typeorm_.Column)({
437
439
  type: getJsonColumnType(),
438
440
  nullable: true,
439
- name: 'action_groups'
441
+ name: 'permission_logic'
440
442
  }),
441
443
  _ts_metadata("design:type", Object)
442
- ], Form.prototype, "actionGroups", void 0);
444
+ ], Form.prototype, "permissionLogic", void 0);
443
445
  _ts_decorate([
444
446
  (0,external_typeorm_.Column)({
445
447
  type: getJsonColumnType(),
@@ -1168,19 +1170,20 @@ FormEmailAuthService = _ts_decorate([
1168
1170
  160(__unused_rspack_module, __webpack_exports__, __webpack_require__) {
1169
1171
  /* import */ var _flusys_nestjs_shared_classes__rspack_import_0 = __webpack_require__(937);
1170
1172
  /* import */ var _flusys_nestjs_shared_decorators__rspack_import_1 = __webpack_require__(265);
1171
- /* import */ var _flusys_nestjs_shared_modules__rspack_import_2 = __webpack_require__(236);
1172
- /* import */ var _nestjs_common__rspack_import_3 = __webpack_require__(157);
1173
- /* import */ var typeorm__rspack_import_4 = __webpack_require__(158);
1174
- /* import */ var _config_index_js__rspack_import_11 = __webpack_require__(769);
1175
- /* import */ var _config_index_js__rspack_import_12 = __webpack_require__(137);
1176
- /* import */ var _dtos_form_result_dto_js__rspack_import_5 = __webpack_require__(788);
1177
- /* import */ var _entities_index_js__rspack_import_6 = __webpack_require__(814);
1178
- /* import */ var _enums_form_access_type_enum_js__rspack_import_13 = __webpack_require__(376);
1179
- /* import */ var _utils_computed_field_utils_js__rspack_import_14 = __webpack_require__(801);
1180
- /* import */ var _utils_permission_utils_js__rspack_import_7 = __webpack_require__(988);
1181
- /* import */ var _form_builder_config_service_js__rspack_import_8 = __webpack_require__(781);
1182
- /* import */ var _form_builder_datasource_provider_js__rspack_import_9 = __webpack_require__(593);
1183
- /* import */ var _form_email_auth_service_js__rspack_import_10 = __webpack_require__(474);
1173
+ /* import */ var _flusys_nestjs_shared_interfaces__rspack_import_2 = __webpack_require__(103);
1174
+ /* import */ var _flusys_nestjs_shared_modules__rspack_import_3 = __webpack_require__(236);
1175
+ /* import */ var _nestjs_common__rspack_import_4 = __webpack_require__(157);
1176
+ /* import */ var typeorm__rspack_import_5 = __webpack_require__(158);
1177
+ /* import */ var _config_index_js__rspack_import_13 = __webpack_require__(769);
1178
+ /* import */ var _config_index_js__rspack_import_14 = __webpack_require__(137);
1179
+ /* import */ var _dtos_form_result_dto_js__rspack_import_6 = __webpack_require__(788);
1180
+ /* import */ var _entities_index_js__rspack_import_7 = __webpack_require__(814);
1181
+ /* import */ var _enums_form_access_type_enum_js__rspack_import_15 = __webpack_require__(376);
1182
+ /* import */ var _utils_computed_field_utils_js__rspack_import_8 = __webpack_require__(801);
1183
+ /* import */ var _utils_permission_utils_js__rspack_import_9 = __webpack_require__(988);
1184
+ /* import */ var _form_builder_config_service_js__rspack_import_10 = __webpack_require__(781);
1185
+ /* import */ var _form_builder_datasource_provider_js__rspack_import_11 = __webpack_require__(593);
1186
+ /* import */ var _form_email_auth_service_js__rspack_import_12 = __webpack_require__(474);
1184
1187
  function _ts_decorate(decorators, target, key, desc) {
1185
1188
  var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
1186
1189
  if (typeof Reflect === "object" && typeof Reflect.decorate === "function") {
@@ -1218,12 +1221,15 @@ function _ts_param(paramIndex, decorator) {
1218
1221
 
1219
1222
 
1220
1223
 
1224
+
1221
1225
  class FormResultService extends _flusys_nestjs_shared_classes__rspack_import_0.ApiService {
1222
1226
  cacheManager;
1223
1227
  utilsService;
1224
1228
  formBuilderConfig;
1225
1229
  dataSourceProvider;
1226
1230
  formEmailAuthService;
1231
+ permissionResolver;
1232
+ permissionCache;
1227
1233
  formRepository = null;
1228
1234
  // Serializes the single-response check-then-insert per (formId, email) within
1229
1235
  // this process, so a double-click/two-tab submission can't both pass the check
@@ -1231,13 +1237,13 @@ class FormResultService extends _flusys_nestjs_shared_classes__rspack_import_0.A
1231
1237
  // per-instance lock would never be shared between the two racing requests.
1232
1238
  // Does not span multiple app instances; see withSubmissionLock().
1233
1239
  static submissionLocks = new Map();
1234
- constructor(cacheManager, utilsService, formBuilderConfig, dataSourceProvider, formEmailAuthService){
1235
- super(_config_index_js__rspack_import_11/* .FORM_BUILDER_EVENT_ENTITIES.FORM_RESULT */.eF.FORM_RESULT, cacheManager, utilsService, FormResultService.name, true, _config_index_js__rspack_import_11/* .FORM_BUILDER_EVENT_MODULE */.T$, dataSourceProvider, _entities_index_js__rspack_import_6/* .FormResult */.ez), this.cacheManager = cacheManager, this.utilsService = utilsService, this.formBuilderConfig = formBuilderConfig, this.dataSourceProvider = dataSourceProvider, this.formEmailAuthService = formEmailAuthService;
1240
+ constructor(cacheManager, utilsService, formBuilderConfig, dataSourceProvider, formEmailAuthService, permissionResolver, permissionCache){
1241
+ super(_config_index_js__rspack_import_13/* .FORM_BUILDER_EVENT_ENTITIES.FORM_RESULT */.eF.FORM_RESULT, cacheManager, utilsService, FormResultService.name, true, _config_index_js__rspack_import_13/* .FORM_BUILDER_EVENT_MODULE */.T$, dataSourceProvider, _entities_index_js__rspack_import_7/* .FormResult */.ez), this.cacheManager = cacheManager, this.utilsService = utilsService, this.formBuilderConfig = formBuilderConfig, this.dataSourceProvider = dataSourceProvider, this.formEmailAuthService = formEmailAuthService, this.permissionResolver = permissionResolver, this.permissionCache = permissionCache;
1236
1242
  }
1237
1243
  async ensureFormRepositoryInitialized() {
1238
1244
  if (!this.formRepository) {
1239
1245
  const enableCompanyFeature = this.formBuilderConfig.isCompanyFeatureEnabled();
1240
- const formEntity = enableCompanyFeature ? _entities_index_js__rspack_import_6/* .FormWithCompany */.Wl : _entities_index_js__rspack_import_6/* .Form */.lV;
1246
+ const formEntity = enableCompanyFeature ? _entities_index_js__rspack_import_7/* .FormWithCompany */.Wl : _entities_index_js__rspack_import_7/* .Form */.lV;
1241
1247
  this.formRepository = await this.dataSourceProvider.getRepository(formEntity);
1242
1248
  }
1243
1249
  }
@@ -1264,13 +1270,13 @@ class FormResultService extends _flusys_nestjs_shared_classes__rspack_import_0.A
1264
1270
  where: {
1265
1271
  id: formId,
1266
1272
  isActive: true,
1267
- deletedAt: (0,typeorm__rspack_import_4.IsNull)()
1273
+ deletedAt: (0,typeorm__rspack_import_5.IsNull)()
1268
1274
  }
1269
1275
  });
1270
1276
  if (!form) {
1271
- throw new _nestjs_common__rspack_import_3.NotFoundException({
1277
+ throw new _nestjs_common__rspack_import_4.NotFoundException({
1272
1278
  message: 'Form not found or inactive',
1273
- messageKey: _config_index_js__rspack_import_12/* .FORM_MESSAGES.NOT_FOUND */.Q.NOT_FOUND
1279
+ messageKey: _config_index_js__rspack_import_14/* .FORM_MESSAGES.NOT_FOUND */.Q.NOT_FOUND
1274
1280
  });
1275
1281
  }
1276
1282
  return form;
@@ -1283,64 +1289,67 @@ class FormResultService extends _flusys_nestjs_shared_classes__rspack_import_0.A
1283
1289
  }
1284
1290
  }
1285
1291
  static PUBLICLY_SUBMITTABLE_ACCESS_TYPES = new Set([
1286
- _enums_form_access_type_enum_js__rspack_import_13/* .FormAccessType.PUBLIC */.h.PUBLIC,
1287
- _enums_form_access_type_enum_js__rspack_import_13/* .FormAccessType.EMAIL_VERIFIED */.h.EMAIL_VERIFIED
1292
+ _enums_form_access_type_enum_js__rspack_import_15/* .FormAccessType.PUBLIC */.h.PUBLIC,
1293
+ _enums_form_access_type_enum_js__rspack_import_15/* .FormAccessType.EMAIL_VERIFIED */.h.EMAIL_VERIFIED
1288
1294
  ]);
1289
1295
  async validateSubmissionAccess(form, user, isPublic) {
1290
1296
  if (isPublic) {
1291
1297
  if (!FormResultService.PUBLICLY_SUBMITTABLE_ACCESS_TYPES.has(form.accessType)) {
1292
- throw new _nestjs_common__rspack_import_3.UnauthorizedException({
1298
+ throw new _nestjs_common__rspack_import_4.UnauthorizedException({
1293
1299
  message: 'This form is not available for public submission',
1294
- messageKey: _config_index_js__rspack_import_12/* .FORM_MESSAGES.NOT_PUBLIC */.Q.NOT_PUBLIC
1300
+ messageKey: _config_index_js__rspack_import_14/* .FORM_MESSAGES.NOT_PUBLIC */.Q.NOT_PUBLIC
1295
1301
  });
1296
1302
  }
1297
1303
  return;
1298
1304
  }
1299
1305
  switch(form.accessType){
1300
- case _enums_form_access_type_enum_js__rspack_import_13/* .FormAccessType.PUBLIC */.h.PUBLIC:
1306
+ case _enums_form_access_type_enum_js__rspack_import_15/* .FormAccessType.PUBLIC */.h.PUBLIC:
1301
1307
  return;
1302
- case _enums_form_access_type_enum_js__rspack_import_13/* .FormAccessType.EMAIL_VERIFIED */.h.EMAIL_VERIFIED:
1303
- throw new _nestjs_common__rspack_import_3.BadRequestException({
1308
+ case _enums_form_access_type_enum_js__rspack_import_15/* .FormAccessType.EMAIL_VERIFIED */.h.EMAIL_VERIFIED:
1309
+ throw new _nestjs_common__rspack_import_4.BadRequestException({
1304
1310
  message: 'Email-verified forms must be submitted through the public endpoint',
1305
- messageKey: _config_index_js__rspack_import_12/* .FORM_MESSAGES.INVALID_ACCESS_TYPE */.Q.INVALID_ACCESS_TYPE
1311
+ messageKey: _config_index_js__rspack_import_14/* .FORM_MESSAGES.INVALID_ACCESS_TYPE */.Q.INVALID_ACCESS_TYPE
1306
1312
  });
1307
- case _enums_form_access_type_enum_js__rspack_import_13/* .FormAccessType.ACTION_GROUP */.h.ACTION_GROUP:
1308
- case _enums_form_access_type_enum_js__rspack_import_13/* .FormAccessType.AUTHENTICATED */.h.AUTHENTICATED:
1313
+ case _enums_form_access_type_enum_js__rspack_import_15/* .FormAccessType.ACTION_GROUP */.h.ACTION_GROUP:
1314
+ case _enums_form_access_type_enum_js__rspack_import_15/* .FormAccessType.AUTHENTICATED */.h.AUTHENTICATED:
1309
1315
  break;
1310
1316
  default:
1311
- throw new _nestjs_common__rspack_import_3.BadRequestException({
1317
+ throw new _nestjs_common__rspack_import_4.BadRequestException({
1312
1318
  message: 'Invalid form access type',
1313
- messageKey: _config_index_js__rspack_import_12/* .FORM_MESSAGES.INVALID_ACCESS_TYPE */.Q.INVALID_ACCESS_TYPE
1319
+ messageKey: _config_index_js__rspack_import_14/* .FORM_MESSAGES.INVALID_ACCESS_TYPE */.Q.INVALID_ACCESS_TYPE
1314
1320
  });
1315
1321
  }
1316
1322
  if (!user) {
1317
- throw new _nestjs_common__rspack_import_3.UnauthorizedException({
1323
+ throw new _nestjs_common__rspack_import_4.UnauthorizedException({
1318
1324
  message: 'Authentication required to submit this form',
1319
- messageKey: _config_index_js__rspack_import_12/* .FORM_MESSAGES.AUTH_REQUIRED */.Q.AUTH_REQUIRED
1325
+ messageKey: _config_index_js__rspack_import_14/* .FORM_MESSAGES.AUTH_REQUIRED */.Q.AUTH_REQUIRED
1320
1326
  });
1321
1327
  }
1322
- if (form.accessType === _enums_form_access_type_enum_js__rspack_import_13/* .FormAccessType.ACTION_GROUP */.h.ACTION_GROUP && form.actionGroups?.length) {
1323
- const hasPermission = await (0,_utils_permission_utils_js__rspack_import_7/* .validateUserPermissions */.i)(user, form.actionGroups, this.cacheManager, this.formBuilderConfig.isCompanyFeatureEnabled());
1324
- if (!hasPermission) {
1325
- throw new _nestjs_common__rspack_import_3.ForbiddenException({
1328
+ if (form.accessType === _enums_form_access_type_enum_js__rspack_import_15/* .FormAccessType.ACTION_GROUP */.h.ACTION_GROUP && form.permissionLogic) {
1329
+ const permitted = await (0,_utils_permission_utils_js__rspack_import_9/* .hasFormPermission */.E)(user, form.permissionLogic, {
1330
+ resolver: this.permissionResolver,
1331
+ cache: this.permissionCache
1332
+ });
1333
+ if (!permitted) {
1334
+ throw new _nestjs_common__rspack_import_4.ForbiddenException({
1326
1335
  message: 'You do not have permission to submit this form',
1327
- messageKey: _config_index_js__rspack_import_12/* .FORM_MESSAGES.ACCESS_DENIED */.Q.ACCESS_DENIED
1336
+ messageKey: _config_index_js__rspack_import_14/* .FORM_MESSAGES.ACCESS_DENIED */.Q.ACCESS_DENIED
1328
1337
  });
1329
1338
  }
1330
- } else if (form.accessType !== _enums_form_access_type_enum_js__rspack_import_13/* .FormAccessType.AUTHENTICATED */.h.AUTHENTICATED) {
1331
- throw new _nestjs_common__rspack_import_3.BadRequestException({
1339
+ } else if (form.accessType !== _enums_form_access_type_enum_js__rspack_import_15/* .FormAccessType.AUTHENTICATED */.h.AUTHENTICATED) {
1340
+ throw new _nestjs_common__rspack_import_4.BadRequestException({
1332
1341
  message: 'Invalid form access type',
1333
- messageKey: _config_index_js__rspack_import_12/* .FORM_MESSAGES.INVALID_ACCESS_TYPE */.Q.INVALID_ACCESS_TYPE
1342
+ messageKey: _config_index_js__rspack_import_14/* .FORM_MESSAGES.INVALID_ACCESS_TYPE */.Q.INVALID_ACCESS_TYPE
1334
1343
  });
1335
1344
  }
1336
1345
  }
1337
1346
  async resolveEmailVerifiedSubmitter(form, user, accessToken) {
1338
- if (form.accessType !== _enums_form_access_type_enum_js__rspack_import_13/* .FormAccessType.EMAIL_VERIFIED */.h.EMAIL_VERIFIED) return null;
1347
+ if (form.accessType !== _enums_form_access_type_enum_js__rspack_import_15/* .FormAccessType.EMAIL_VERIFIED */.h.EMAIL_VERIFIED) return null;
1339
1348
  const email = await this.formEmailAuthService.resolveSubmitterEmail(form.id, user ?? undefined, accessToken);
1340
1349
  if (!email) {
1341
- throw new _nestjs_common__rspack_import_3.UnauthorizedException({
1350
+ throw new _nestjs_common__rspack_import_4.UnauthorizedException({
1342
1351
  message: 'Email verification required to submit this form',
1343
- messageKey: _config_index_js__rspack_import_12/* .FORM_MESSAGES.EMAIL_VERIFICATION_REQUIRED */.Q.EMAIL_VERIFICATION_REQUIRED
1352
+ messageKey: _config_index_js__rspack_import_14/* .FORM_MESSAGES.EMAIL_VERIFICATION_REQUIRED */.Q.EMAIL_VERIFICATION_REQUIRED
1344
1353
  });
1345
1354
  }
1346
1355
  return email;
@@ -1354,13 +1363,13 @@ class FormResultService extends _flusys_nestjs_shared_classes__rspack_import_0.A
1354
1363
  formId: form.id,
1355
1364
  submitterEmail,
1356
1365
  isDraft: false,
1357
- deletedAt: (0,typeorm__rspack_import_4.IsNull)()
1366
+ deletedAt: (0,typeorm__rspack_import_5.IsNull)()
1358
1367
  }
1359
1368
  });
1360
1369
  if (count > 0) {
1361
- throw new _nestjs_common__rspack_import_3.ConflictException({
1370
+ throw new _nestjs_common__rspack_import_4.ConflictException({
1362
1371
  message: 'You have already submitted a response to this form',
1363
- messageKey: _config_index_js__rspack_import_12/* .FORM_RESULT_MESSAGES.ALREADY_SUBMITTED */.y.ALREADY_SUBMITTED
1372
+ messageKey: _config_index_js__rspack_import_14/* .FORM_RESULT_MESSAGES.ALREADY_SUBMITTED */.y.ALREADY_SUBMITTED
1364
1373
  });
1365
1374
  }
1366
1375
  }
@@ -1369,7 +1378,7 @@ class FormResultService extends _flusys_nestjs_shared_classes__rspack_import_0.A
1369
1378
  formId,
1370
1379
  submittedById: userId,
1371
1380
  isDraft: true,
1372
- deletedAt: (0,typeorm__rspack_import_4.IsNull)()
1381
+ deletedAt: (0,typeorm__rspack_import_5.IsNull)()
1373
1382
  };
1374
1383
  }
1375
1384
  // Query Customization
@@ -1405,13 +1414,10 @@ class FormResultService extends _flusys_nestjs_shared_classes__rspack_import_0.A
1405
1414
  if (isDraft) {
1406
1415
  return data;
1407
1416
  }
1408
- const schema = form.schema;
1409
- const settings = schema?.settings;
1410
- const computedFields = settings?.computedFields;
1411
- if (!computedFields || computedFields.length === 0) {
1417
+ const computedValues = (0,_utils_computed_field_utils_js__rspack_import_8/* .calculateComputedFields */.XZ)(data, form.schema);
1418
+ if (Object.keys(computedValues).length === 0) {
1412
1419
  return data;
1413
1420
  }
1414
- const computedValues = (0,_utils_computed_field_utils_js__rspack_import_14/* .calculateComputedFields */.X)(data, computedFields);
1415
1421
  return {
1416
1422
  ...data,
1417
1423
  _computed: computedValues
@@ -1514,13 +1520,13 @@ class FormResultService extends _flusys_nestjs_shared_classes__rspack_import_0.A
1514
1520
  where: {
1515
1521
  id: draftId,
1516
1522
  submittedById: user.id,
1517
- deletedAt: (0,typeorm__rspack_import_4.IsNull)()
1523
+ deletedAt: (0,typeorm__rspack_import_5.IsNull)()
1518
1524
  }
1519
1525
  });
1520
1526
  if (!draft) {
1521
- throw new _nestjs_common__rspack_import_3.NotFoundException({
1527
+ throw new _nestjs_common__rspack_import_4.NotFoundException({
1522
1528
  message: 'Draft not found',
1523
- messageKey: _config_index_js__rspack_import_12/* .FORM_RESULT_MESSAGES.NOT_FOUND */.y.NOT_FOUND
1529
+ messageKey: _config_index_js__rspack_import_14/* .FORM_RESULT_MESSAGES.NOT_FOUND */.y.NOT_FOUND
1524
1530
  });
1525
1531
  }
1526
1532
  const form = await this.getActiveForm(dto.formId);
@@ -1540,7 +1546,7 @@ class FormResultService extends _flusys_nestjs_shared_classes__rspack_import_0.A
1540
1546
  return this.convertEntityToResponseDto(saved, false);
1541
1547
  }
1542
1548
  async publishSubmissionEvent(result, formId, isDraft, user) {
1543
- await this.publishDomainAction(isDraft ? _config_index_js__rspack_import_11/* .FORM_BUILDER_EVENT_ACTIONS.DRAFT_SAVED */.ov.DRAFT_SAVED : _config_index_js__rspack_import_11/* .FORM_BUILDER_EVENT_ACTIONS.SUBMITTED */.ov.SUBMITTED, {
1549
+ await this.publishDomainAction(isDraft ? _config_index_js__rspack_import_13/* .FORM_BUILDER_EVENT_ACTIONS.DRAFT_SAVED */.ov.DRAFT_SAVED : _config_index_js__rspack_import_13/* .FORM_BUILDER_EVENT_ACTIONS.SUBMITTED */.ov.SUBMITTED, {
1544
1550
  ids: [
1545
1551
  result.id
1546
1552
  ],
@@ -1581,7 +1587,7 @@ class FormResultService extends _flusys_nestjs_shared_classes__rspack_import_0.A
1581
1587
  formId,
1582
1588
  submittedById: user.id,
1583
1589
  isDraft: false,
1584
- deletedAt: (0,typeorm__rspack_import_4.IsNull)()
1590
+ deletedAt: (0,typeorm__rspack_import_5.IsNull)()
1585
1591
  }
1586
1592
  });
1587
1593
  return count > 0;
@@ -1595,7 +1601,7 @@ class FormResultService extends _flusys_nestjs_shared_classes__rspack_import_0.A
1595
1601
  formId,
1596
1602
  submitterEmail: email,
1597
1603
  isDraft: false,
1598
- deletedAt: (0,typeorm__rspack_import_4.IsNull)()
1604
+ deletedAt: (0,typeorm__rspack_import_5.IsNull)()
1599
1605
  }
1600
1606
  });
1601
1607
  return count > 0;
@@ -1608,7 +1614,7 @@ _ts_decorate([
1608
1614
  }),
1609
1615
  _ts_metadata("design:type", Function),
1610
1616
  _ts_metadata("design:paramtypes", [
1611
- typeof _dtos_form_result_dto_js__rspack_import_5/* .SubmitFormDto */.sH === "undefined" ? Object : _dtos_form_result_dto_js__rspack_import_5/* .SubmitFormDto */.sH,
1617
+ typeof _dtos_form_result_dto_js__rspack_import_6/* .SubmitFormDto */.sH === "undefined" ? Object : _dtos_form_result_dto_js__rspack_import_6/* .SubmitFormDto */.sH,
1612
1618
  Object,
1613
1619
  void 0
1614
1620
  ]),
@@ -1622,27 +1628,33 @@ _ts_decorate([
1622
1628
  _ts_metadata("design:type", Function),
1623
1629
  _ts_metadata("design:paramtypes", [
1624
1630
  String,
1625
- typeof _dtos_form_result_dto_js__rspack_import_5/* .SubmitFormDto */.sH === "undefined" ? Object : _dtos_form_result_dto_js__rspack_import_5/* .SubmitFormDto */.sH,
1631
+ typeof _dtos_form_result_dto_js__rspack_import_6/* .SubmitFormDto */.sH === "undefined" ? Object : _dtos_form_result_dto_js__rspack_import_6/* .SubmitFormDto */.sH,
1626
1632
  typeof ILoggedUserInfo === "undefined" ? Object : ILoggedUserInfo
1627
1633
  ]),
1628
1634
  _ts_metadata("design:returntype", Promise)
1629
1635
  ], FormResultService.prototype, "updateDraft", null);
1630
1636
  FormResultService = _ts_decorate([
1631
- (0,_nestjs_common__rspack_import_3.Injectable)({
1632
- scope: _nestjs_common__rspack_import_3.Scope.REQUEST
1637
+ (0,_nestjs_common__rspack_import_4.Injectable)({
1638
+ scope: _nestjs_common__rspack_import_4.Scope.REQUEST
1633
1639
  }),
1634
- _ts_param(0, (0,_nestjs_common__rspack_import_3.Inject)('CACHE_INSTANCE')),
1635
- _ts_param(1, (0,_nestjs_common__rspack_import_3.Inject)(_flusys_nestjs_shared_modules__rspack_import_2.UtilsService)),
1636
- _ts_param(2, (0,_nestjs_common__rspack_import_3.Inject)(_form_builder_config_service_js__rspack_import_8/* .FormBuilderConfigService */.j)),
1637
- _ts_param(3, (0,_nestjs_common__rspack_import_3.Inject)(_form_builder_datasource_provider_js__rspack_import_9/* .FormBuilderDataSourceProvider */._)),
1638
- _ts_param(4, (0,_nestjs_common__rspack_import_3.Inject)(_form_email_auth_service_js__rspack_import_10/* .FormEmailAuthService */.w)),
1640
+ _ts_param(0, (0,_nestjs_common__rspack_import_4.Inject)('CACHE_INSTANCE')),
1641
+ _ts_param(1, (0,_nestjs_common__rspack_import_4.Inject)(_flusys_nestjs_shared_modules__rspack_import_3.UtilsService)),
1642
+ _ts_param(2, (0,_nestjs_common__rspack_import_4.Inject)(_form_builder_config_service_js__rspack_import_10/* .FormBuilderConfigService */.j)),
1643
+ _ts_param(3, (0,_nestjs_common__rspack_import_4.Inject)(_form_builder_datasource_provider_js__rspack_import_11/* .FormBuilderDataSourceProvider */._)),
1644
+ _ts_param(4, (0,_nestjs_common__rspack_import_4.Inject)(_form_email_auth_service_js__rspack_import_12/* .FormEmailAuthService */.w)),
1645
+ _ts_param(5, (0,_nestjs_common__rspack_import_4.Optional)()),
1646
+ _ts_param(5, (0,_nestjs_common__rspack_import_4.Inject)(_flusys_nestjs_shared_interfaces__rspack_import_2.PERMISSION_RESOLVER)),
1647
+ _ts_param(6, (0,_nestjs_common__rspack_import_4.Optional)()),
1648
+ _ts_param(6, (0,_nestjs_common__rspack_import_4.Inject)(_flusys_nestjs_shared_modules__rspack_import_3.SharedPermissionCacheService)),
1639
1649
  _ts_metadata("design:type", Function),
1640
1650
  _ts_metadata("design:paramtypes", [
1641
1651
  typeof _flusys_nestjs_shared_classes__rspack_import_0.HybridCache === "undefined" ? Object : _flusys_nestjs_shared_classes__rspack_import_0.HybridCache,
1642
- typeof _flusys_nestjs_shared_modules__rspack_import_2.UtilsService === "undefined" ? Object : _flusys_nestjs_shared_modules__rspack_import_2.UtilsService,
1643
- typeof _form_builder_config_service_js__rspack_import_8/* .FormBuilderConfigService */.j === "undefined" ? Object : _form_builder_config_service_js__rspack_import_8/* .FormBuilderConfigService */.j,
1644
- typeof _form_builder_datasource_provider_js__rspack_import_9/* .FormBuilderDataSourceProvider */._ === "undefined" ? Object : _form_builder_datasource_provider_js__rspack_import_9/* .FormBuilderDataSourceProvider */._,
1645
- typeof _form_email_auth_service_js__rspack_import_10/* .FormEmailAuthService */.w === "undefined" ? Object : _form_email_auth_service_js__rspack_import_10/* .FormEmailAuthService */.w
1652
+ typeof _flusys_nestjs_shared_modules__rspack_import_3.UtilsService === "undefined" ? Object : _flusys_nestjs_shared_modules__rspack_import_3.UtilsService,
1653
+ typeof _form_builder_config_service_js__rspack_import_10/* .FormBuilderConfigService */.j === "undefined" ? Object : _form_builder_config_service_js__rspack_import_10/* .FormBuilderConfigService */.j,
1654
+ typeof _form_builder_datasource_provider_js__rspack_import_11/* .FormBuilderDataSourceProvider */._ === "undefined" ? Object : _form_builder_datasource_provider_js__rspack_import_11/* .FormBuilderDataSourceProvider */._,
1655
+ typeof _form_email_auth_service_js__rspack_import_12/* .FormEmailAuthService */.w === "undefined" ? Object : _form_email_auth_service_js__rspack_import_12/* .FormEmailAuthService */.w,
1656
+ typeof IPermissionResolver === "undefined" ? Object : IPermissionResolver,
1657
+ typeof _flusys_nestjs_shared_modules__rspack_import_3.SharedPermissionCacheService === "undefined" ? Object : _flusys_nestjs_shared_modules__rspack_import_3.SharedPermissionCacheService
1646
1658
  ])
1647
1659
  ], FormResultService);
1648
1660
 
@@ -1663,6 +1675,8 @@ __webpack_require__.d(__webpack_exports__, {
1663
1675
 
1664
1676
  // EXTERNAL MODULE: external "@flusys/nestjs-shared/classes"
1665
1677
  var classes_ = __webpack_require__(937);
1678
+ // EXTERNAL MODULE: external "@flusys/nestjs-shared/interfaces"
1679
+ var interfaces_ = __webpack_require__(103);
1666
1680
  // EXTERNAL MODULE: external "@flusys/nestjs-shared/modules"
1667
1681
  var modules_ = __webpack_require__(236);
1668
1682
  // EXTERNAL MODULE: external "@flusys/nestjs-shared/utils"
@@ -1679,6 +1693,8 @@ var message_keys = __webpack_require__(137);
1679
1693
  var entities = __webpack_require__(814);
1680
1694
  // EXTERNAL MODULE: ./projects/nestjs-form-builder/src/enums/form-access-type.enum.ts
1681
1695
  var form_access_type_enum = __webpack_require__(376);
1696
+ // EXTERNAL MODULE: ./projects/nestjs-form-builder/src/utils/computed-field.utils.ts
1697
+ var computed_field_utils = __webpack_require__(801);
1682
1698
  // EXTERNAL MODULE: ./projects/nestjs-form-builder/src/utils/permission.utils.ts
1683
1699
  var permission_utils = __webpack_require__(988);
1684
1700
  // EXTERNAL MODULE: ./projects/nestjs-form-builder/src/services/form-builder-config.service.ts
@@ -1721,17 +1737,24 @@ function _ts_param(paramIndex, decorator) {
1721
1737
 
1722
1738
 
1723
1739
 
1740
+
1741
+
1724
1742
  class FormService extends classes_.ApiService {
1725
1743
  cacheManager;
1726
1744
  utilsService;
1727
1745
  formBuilderConfig;
1728
- constructor(cacheManager, utilsService, formBuilderConfig, dataSourceProvider){
1729
- super(event_actions/* .FORM_BUILDER_EVENT_ENTITIES.FORM */.eF.FORM, cacheManager, utilsService, FormService.name, true, event_actions/* .FORM_BUILDER_EVENT_MODULE */.T$, dataSourceProvider, formBuilderConfig.isCompanyFeatureEnabled() ? entities/* .FormWithCompany */.Wl : entities/* .Form */.lV), this.cacheManager = cacheManager, this.utilsService = utilsService, this.formBuilderConfig = formBuilderConfig;
1746
+ permissionResolver;
1747
+ permissionCache;
1748
+ constructor(cacheManager, utilsService, formBuilderConfig, dataSourceProvider, permissionResolver, permissionCache){
1749
+ super(event_actions/* .FORM_BUILDER_EVENT_ENTITIES.FORM */.eF.FORM, cacheManager, utilsService, FormService.name, true, event_actions/* .FORM_BUILDER_EVENT_MODULE */.T$, dataSourceProvider, formBuilderConfig.isCompanyFeatureEnabled() ? entities/* .FormWithCompany */.Wl : entities/* .Form */.lV), this.cacheManager = cacheManager, this.utilsService = utilsService, this.formBuilderConfig = formBuilderConfig, this.permissionResolver = permissionResolver, this.permissionCache = permissionCache;
1730
1750
  }
1731
1751
  // Entity Conversion
1732
1752
  async convertSingleDtoToEntity(dto, user) {
1733
1753
  let form = {};
1734
1754
  let isUpdate = false;
1755
+ if (dto.schema) {
1756
+ this.assertValidComputedFields(dto.schema);
1757
+ }
1735
1758
  // NOTE: Using 'id' in dto check instead of instanceof - instanceof may not work after esbuild bundling
1736
1759
  if ('id' in dto && dto.id && typeof dto.id === 'string') {
1737
1760
  const dbData = await this.repository.findOne({
@@ -1776,6 +1799,19 @@ class FormService extends classes_.ApiService {
1776
1799
  }
1777
1800
  return form;
1778
1801
  }
1802
+ assertValidComputedFields(schema) {
1803
+ const issue = (0,computed_field_utils/* .findComputedFieldIssue */.Qc)(schema);
1804
+ if (!issue) return;
1805
+ throw new common_.BadRequestException({
1806
+ message: `Computed field "${issue.field}" is invalid (${issue.code}).`,
1807
+ messageKey: message_keys/* .FORM_MESSAGES.INVALID_COMPUTED_FIELD */.Q.INVALID_COMPUTED_FIELD,
1808
+ messageVariables: {
1809
+ field: issue.field,
1810
+ code: issue.code,
1811
+ ...issue.variables
1812
+ }
1813
+ });
1814
+ }
1779
1815
  async getSelectQuery(query, _user, select) {
1780
1816
  if (!select || !select.length) {
1781
1817
  select = [
@@ -1786,7 +1822,7 @@ class FormService extends classes_.ApiService {
1786
1822
  'schema',
1787
1823
  'schemaVersion',
1788
1824
  'accessType',
1789
- 'actionGroups',
1825
+ 'permissionLogic',
1790
1826
  'tags',
1791
1827
  'isActive',
1792
1828
  'createdAt',
@@ -1856,7 +1892,7 @@ class FormService extends classes_.ApiService {
1856
1892
  schema: entity.schema,
1857
1893
  schemaVersion: entity.schemaVersion,
1858
1894
  accessType: entity.accessType,
1859
- actionGroups: entity.actionGroups,
1895
+ permissionLogic: entity.permissionLogic,
1860
1896
  tags: entity.tags,
1861
1897
  isActive: entity.isActive,
1862
1898
  companyId: entityWithCompany.companyId ?? null,
@@ -1966,7 +2002,7 @@ class FormService extends classes_.ApiService {
1966
2002
  'name',
1967
2003
  'description',
1968
2004
  'accessType',
1969
- 'actionGroups',
2005
+ 'permissionLogic',
1970
2006
  'isActive'
1971
2007
  ]
1972
2008
  });
@@ -1981,7 +2017,7 @@ class FormService extends classes_.ApiService {
1981
2017
  name: form.name,
1982
2018
  description: form.description,
1983
2019
  accessType: form.accessType,
1984
- actionGroups: form.actionGroups,
2020
+ permissionLogic: form.permissionLogic,
1985
2021
  isActive: form.isActive
1986
2022
  };
1987
2023
  }
@@ -2014,10 +2050,12 @@ class FormService extends classes_.ApiService {
2014
2050
  }
2015
2051
  async getAuthenticatedForm(formId, user) {
2016
2052
  const form = await this.getFormForSubmission(formId, user);
2017
- // For action_group access, check permissions from cache
2018
- if (form.accessType === form_access_type_enum/* .FormAccessType.ACTION_GROUP */.h.ACTION_GROUP && form.actionGroups?.length) {
2019
- const hasPermission = await (0,permission_utils/* .validateUserPermissions */.i)(user, form.actionGroups, this.cacheManager, this.formBuilderConfig.isCompanyFeatureEnabled());
2020
- if (!hasPermission) {
2053
+ if (form.accessType === form_access_type_enum/* .FormAccessType.ACTION_GROUP */.h.ACTION_GROUP && form.permissionLogic) {
2054
+ const permitted = await (0,permission_utils/* .hasFormPermission */.E)(user, form.permissionLogic, {
2055
+ resolver: this.permissionResolver,
2056
+ cache: this.permissionCache
2057
+ });
2058
+ if (!permitted) {
2021
2059
  throw new common_.ForbiddenException({
2022
2060
  message: 'You do not have permission to access this form',
2023
2061
  messageKey: message_keys/* .FORM_MESSAGES.ACCESS_DENIED */.Q.ACCESS_DENIED
@@ -2035,268 +2073,192 @@ FormService = _ts_decorate([
2035
2073
  _ts_param(1, (0,common_.Inject)(modules_.UtilsService)),
2036
2074
  _ts_param(2, (0,common_.Inject)(form_builder_config_service/* .FormBuilderConfigService */.j)),
2037
2075
  _ts_param(3, (0,common_.Inject)(form_builder_datasource_provider/* .FormBuilderDataSourceProvider */._)),
2076
+ _ts_param(4, (0,common_.Optional)()),
2077
+ _ts_param(4, (0,common_.Inject)(interfaces_.PERMISSION_RESOLVER)),
2078
+ _ts_param(5, (0,common_.Optional)()),
2079
+ _ts_param(5, (0,common_.Inject)(modules_.SharedPermissionCacheService)),
2038
2080
  _ts_metadata("design:type", Function),
2039
2081
  _ts_metadata("design:paramtypes", [
2040
2082
  typeof classes_.HybridCache === "undefined" ? Object : classes_.HybridCache,
2041
2083
  typeof modules_.UtilsService === "undefined" ? Object : modules_.UtilsService,
2042
2084
  typeof form_builder_config_service/* .FormBuilderConfigService */.j === "undefined" ? Object : form_builder_config_service/* .FormBuilderConfigService */.j,
2043
- typeof form_builder_datasource_provider/* .FormBuilderDataSourceProvider */._ === "undefined" ? Object : form_builder_datasource_provider/* .FormBuilderDataSourceProvider */._
2085
+ typeof form_builder_datasource_provider/* .FormBuilderDataSourceProvider */._ === "undefined" ? Object : form_builder_datasource_provider/* .FormBuilderDataSourceProvider */._,
2086
+ typeof IPermissionResolver === "undefined" ? Object : IPermissionResolver,
2087
+ typeof modules_.SharedPermissionCacheService === "undefined" ? Object : modules_.SharedPermissionCacheService
2044
2088
  ])
2045
2089
  ], FormService);
2046
2090
 
2047
2091
 
2048
2092
  },
2049
2093
  801(__unused_rspack_module, __webpack_exports__, __webpack_require__) {
2050
- /**
2051
- * Computed Field Calculation Utilities
2052
- *
2053
- * This module provides functions to calculate computed field values
2054
- * based on form submission data and computed field definitions.
2055
- *
2056
- * NOTE: Interface definitions here mirror @flusys/ng-form-builder interfaces
2057
- * but are defined separately to avoid cross-package dependencies.
2058
- * Both use compatible JSON structures for form schema serialization.
2059
- *
2060
- * @see FLUSYS_NG/projects/ng-form-builder/interfaces/computed-field.interface.ts
2061
- */ // ============================================================================
2062
- // Type Definitions
2063
- // ============================================================================
2064
- // ============================================================================
2065
- // Type Guards & Helpers
2066
- // ============================================================================
2067
- function isObject(value) {
2068
- return typeof value === 'object' && value !== null;
2069
- }
2070
- function isDirectValueConfig(config) {
2071
- return isObject(config) && config.type === 'direct' && 'value' in config;
2094
+ /* import */ var _flusys_nestjs_shared_utils__rspack_import_0 = __webpack_require__(216);
2095
+
2096
+ /** A field reference to an earlier computed field: `computed.<key>`. */ const COMPUTED_REF_PREFIX = 'computed.';
2097
+ const COMPUTED_KEY_PATTERN = /^[A-Za-z_]\w*$/;
2098
+ function asSchema(schema) {
2099
+ return typeof schema === 'object' && schema !== null ? schema : {};
2072
2100
  }
2073
- function isFieldReferenceConfig(config) {
2074
- return isObject(config) && config.type === 'field_reference' && 'fieldId' in config;
2101
+ function formFields(schema) {
2102
+ return (schema.sections ?? []).flatMap((section)=>section.fields ?? []).filter((field)=>typeof field?.id === 'string');
2075
2103
  }
2076
- function isArithmeticConfig(config) {
2077
- return isObject(config) && config.type === 'arithmetic' && 'operation' in config && 'operands' in config && Array.isArray(config.operands);
2104
+ /** References resolve by field id, by field name (what templates use), or `computed.<key>`. */ function createResolver(data, fields, computed) {
2105
+ const idByName = new Map(fields.filter((f)=>f.name).map((f)=>[
2106
+ f.name,
2107
+ f.id
2108
+ ]));
2109
+ return (ref)=>{
2110
+ if (ref.startsWith(COMPUTED_REF_PREFIX)) {
2111
+ const key = ref.slice(COMPUTED_REF_PREFIX.length);
2112
+ return Object.hasOwn(computed, key) ? computed[key] : undefined;
2113
+ }
2114
+ if (Object.hasOwn(data, ref)) return data[ref];
2115
+ const id = idByName.get(ref);
2116
+ return id !== undefined && Object.hasOwn(data, id) ? data[id] : undefined;
2117
+ };
2078
2118
  }
2079
- function isEmptyValue(value) {
2080
- return value === null || value === undefined || value === '' || Array.isArray(value) && value.length === 0;
2119
+ function castValue(value, valueType) {
2120
+ if (value === null || value === undefined) return null;
2121
+ switch(valueType){
2122
+ case 'number':
2123
+ try {
2124
+ return (0,_flusys_nestjs_shared_utils__rspack_import_0.expressionToNumber)(value);
2125
+ } catch {
2126
+ return null;
2127
+ }
2128
+ case 'boolean':
2129
+ if (typeof value === 'boolean') return value;
2130
+ return [
2131
+ 'true',
2132
+ '1',
2133
+ 'yes'
2134
+ ].includes(((0,_flusys_nestjs_shared_utils__rspack_import_0.expressionToText)(value) ?? '').trim().toLowerCase());
2135
+ default:
2136
+ return (0,_flusys_nestjs_shared_utils__rspack_import_0.expressionToText)(value);
2137
+ }
2081
2138
  }
2082
- /**
2083
- * Normalize operator to uppercase string (handles both enum and string values)
2084
- */ function normalizeOperator(op) {
2085
- const strOp = String(op).toUpperCase();
2086
- return strOp === 'OR' ? 'OR' : 'AND';
2139
+ function computeField(field, ctx) {
2140
+ for (const rule of field.rules ?? []){
2141
+ if (!rule.condition || (0,_flusys_nestjs_shared_utils__rspack_import_0.evaluateConditionGroup)(rule.condition, ctx)) return (0,_flusys_nestjs_shared_utils__rspack_import_0.evaluateExpression)(rule.value, ctx);
2142
+ }
2143
+ return field.defaultValue ?? null;
2087
2144
  }
2088
2145
  /**
2089
- * Calculate all computed fields for a form submission
2090
- *
2091
- * @param formData - The submitted form data (field values by field ID)
2092
- * @param computedFields - Array of computed field definitions from form settings
2093
- * @returns Object containing computed field values keyed by their key property
2094
- */ function calculateComputedFields(formData, computedFields) {
2095
- if (!computedFields || computedFields.length === 0) {
2096
- return {};
2097
- }
2146
+ * Computed values for a submission, keyed by each computed field's `key`. Fields run in order,
2147
+ * so a field may use earlier ones (`computed.<key>`); a field whose rules fail at run time gets
2148
+ * its default value instead of failing the submission.
2149
+ */ function calculateComputedFields(data, schema) {
2150
+ const parsed = asSchema(schema);
2151
+ const computedFields = parsed.settings?.computedFields ?? [];
2098
2152
  const result = {};
2153
+ if (computedFields.length === 0) return result;
2154
+ const ctx = {
2155
+ resolveRef: createResolver(data, formFields(parsed), result)
2156
+ };
2099
2157
  for (const field of computedFields){
2158
+ let value;
2100
2159
  try {
2101
- const value = calculateComputedField(formData, field);
2102
- result[field.key] = value;
2103
- } catch (error) {
2104
- // If calculation fails, use default value or null
2105
- result[field.key] = field.defaultValue ?? null;
2160
+ value = computeField(field, ctx);
2161
+ } catch {
2162
+ value = field.defaultValue ?? null;
2106
2163
  }
2164
+ result[field.key] = castValue(value, field.valueType);
2107
2165
  }
2108
2166
  return result;
2109
2167
  }
2110
- /**
2111
- * Calculate a single computed field value
2112
- */ function calculateComputedField(formData, field) {
2113
- // Evaluate rules in order, first matching rule wins
2114
- for (const rule of field.rules){
2115
- if (evaluateCondition(formData, rule.condition)) {
2116
- return executeComputation(formData, rule.computation, field.valueType);
2168
+ /** First problem in the schema's computed fields, or null. Run on save so authors see it there, not at submit. */ function findComputedFieldIssue(schema) {
2169
+ const parsed = asSchema(schema);
2170
+ const computedFields = parsed.settings?.computedFields ?? [];
2171
+ const fields = formFields(parsed);
2172
+ const knownRefs = new Set(fields.flatMap((f)=>f.name ? [
2173
+ f.id,
2174
+ f.name
2175
+ ] : [
2176
+ f.id
2177
+ ]));
2178
+ const earlierKeys = new Set();
2179
+ for (const field of computedFields){
2180
+ const label = String(field?.name || field?.key || '');
2181
+ if (typeof field?.key !== 'string' || !COMPUTED_KEY_PATTERN.test(field.key)) {
2182
+ return {
2183
+ field: label,
2184
+ code: 'invalid_key',
2185
+ variables: {
2186
+ key: String(field?.key ?? '')
2187
+ }
2188
+ };
2117
2189
  }
2118
- }
2119
- // No rule matched, return default
2120
- return field.defaultValue ?? null;
2121
- }
2122
- /**
2123
- * Evaluate a condition group
2124
- */ function evaluateCondition(formData, conditionGroup) {
2125
- // No condition = always true
2126
- if (!conditionGroup || !conditionGroup.conditions || conditionGroup.conditions.length === 0) {
2127
- return true;
2128
- }
2129
- const results = conditionGroup.conditions.map((condition)=>evaluateSingleCondition(formData, condition));
2130
- // Normalize operator to handle both string and enum values
2131
- const operator = normalizeOperator(conditionGroup.operator);
2132
- if (operator === 'AND') {
2133
- return results.every((r)=>r);
2134
- } else {
2135
- return results.some((r)=>r);
2136
- }
2137
- }
2138
- /**
2139
- * Evaluate a single condition
2140
- */ function evaluateSingleCondition(formData, condition) {
2141
- const fieldValue = formData[condition.fieldId];
2142
- const compareValue = condition.value;
2143
- switch(condition.comparison){
2144
- case 'equals':
2145
- return fieldValue === compareValue || String(fieldValue) === String(compareValue);
2146
- case 'not_equals':
2147
- return fieldValue !== compareValue && String(fieldValue) !== String(compareValue);
2148
- case 'is_empty':
2149
- return isEmptyValue(fieldValue);
2150
- case 'is_not_empty':
2151
- return !isEmptyValue(fieldValue);
2152
- case 'contains':
2153
- return String(fieldValue).includes(String(compareValue));
2154
- case 'not_contains':
2155
- return !String(fieldValue).includes(String(compareValue));
2156
- case 'starts_with':
2157
- return String(fieldValue).startsWith(String(compareValue));
2158
- case 'ends_with':
2159
- return String(fieldValue).endsWith(String(compareValue));
2160
- case 'greater_than':
2161
- return Number(fieldValue) > Number(compareValue);
2162
- case 'less_than':
2163
- return Number(fieldValue) < Number(compareValue);
2164
- case 'greater_or_equal':
2165
- return Number(fieldValue) >= Number(compareValue);
2166
- case 'less_or_equal':
2167
- return Number(fieldValue) <= Number(compareValue);
2168
- case 'is_before':
2169
- return new Date(String(fieldValue)) < new Date(String(compareValue));
2170
- case 'is_after':
2171
- return new Date(String(fieldValue)) > new Date(String(compareValue));
2172
- case 'is_checked':
2173
- return fieldValue === true || fieldValue === 'true' || fieldValue === 1;
2174
- case 'is_not_checked':
2175
- return fieldValue === false || fieldValue === 'false' || fieldValue === 0 || !fieldValue;
2176
- case 'is_any_of':
2177
- case 'in':
2178
- return Array.isArray(compareValue) && compareValue.includes(fieldValue);
2179
- case 'is_none_of':
2180
- case 'not_in':
2181
- return !Array.isArray(compareValue) || !compareValue.includes(fieldValue);
2182
- default:
2183
- return false;
2184
- }
2185
- }
2186
- /**
2187
- * Execute a computation and return the result
2188
- */ function executeComputation(formData, computation, valueType) {
2189
- const { config } = computation;
2190
- // Use type guards for safe access
2191
- if (computation.type === 'direct' && isDirectValueConfig(config)) {
2192
- return valueType === 'number' ? Number(config.value) : String(config.value);
2193
- }
2194
- if (computation.type === 'field_reference' && isFieldReferenceConfig(config)) {
2195
- const value = formData[config.fieldId];
2196
- if (value === null || value === undefined) {
2197
- return null;
2190
+ if (earlierKeys.has(field.key)) return {
2191
+ field: label,
2192
+ code: 'duplicate_key',
2193
+ variables: {
2194
+ key: field.key
2195
+ }
2196
+ };
2197
+ if (!Array.isArray(field.rules)) return {
2198
+ field: label,
2199
+ code: 'invalid_node',
2200
+ variables: {
2201
+ type: 'rules'
2202
+ }
2203
+ };
2204
+ const isKnownRef = (ref)=>knownRefs.has(ref) || ref.startsWith(COMPUTED_REF_PREFIX) && earlierKeys.has(ref.slice(COMPUTED_REF_PREFIX.length));
2205
+ try {
2206
+ for (const rule of field.rules){
2207
+ if (rule.condition) (0,_flusys_nestjs_shared_utils__rspack_import_0.assertValidConditionGroup)(rule.condition, {
2208
+ isKnownRef
2209
+ });
2210
+ (0,_flusys_nestjs_shared_utils__rspack_import_0.assertValidExpression)(rule.value, {
2211
+ isKnownRef
2212
+ });
2213
+ }
2214
+ } catch (error) {
2215
+ if ((0,_flusys_nestjs_shared_utils__rspack_import_0.isExpressionError)(error)) return {
2216
+ field: label,
2217
+ code: error.code,
2218
+ variables: error.variables
2219
+ };
2220
+ throw error;
2198
2221
  }
2199
- return valueType === 'number' ? Number(value) : String(value);
2222
+ earlierKeys.add(field.key);
2200
2223
  }
2201
- if (computation.type === 'arithmetic' && isArithmeticConfig(config)) {
2202
- return calculateArithmetic(formData, config);
2203
- }
2204
- // Unknown computation type
2205
2224
  return null;
2206
2225
  }
2207
- /**
2208
- * Perform arithmetic calculation
2209
- */ function calculateArithmetic(formData, config) {
2210
- const values = config.operands.map((operand)=>{
2211
- if (operand.type === 'constant') {
2212
- return operand.value ?? 0;
2213
- } else {
2214
- const fieldValue = formData[operand.fieldId ?? ''];
2215
- return fieldValue !== null && fieldValue !== undefined ? Number(fieldValue) : 0;
2216
- }
2217
- });
2218
- if (values.length === 0) {
2219
- return null;
2220
- }
2221
- switch(config.operation){
2222
- case 'sum':
2223
- case 'increment':
2224
- return values.reduce((acc, val)=>acc + val, 0);
2225
- case 'subtract':
2226
- case 'decrement':
2227
- return values.reduce((acc, val, idx)=>idx === 0 ? val : acc - val, 0);
2228
- case 'multiply':
2229
- return values.reduce((acc, val)=>acc * val, 1);
2230
- case 'divide':
2231
- {
2232
- const [first, ...rest] = values;
2233
- return rest.reduce((acc, val)=>val !== 0 ? acc / val : acc, first);
2234
- }
2235
- case 'average':
2236
- return values.reduce((acc, val)=>acc + val, 0) / values.length;
2237
- case 'min':
2238
- return Math.min(...values);
2239
- case 'max':
2240
- return Math.max(...values);
2241
- default:
2242
- return null;
2243
- }
2244
- }
2245
2226
 
2246
2227
  __webpack_require__.d(__webpack_exports__, {
2247
- X: () => (calculateComputedFields)
2228
+ Qc: () => (findComputedFieldIssue),
2229
+ XZ: () => (calculateComputedFields)
2230
+ }, {
2231
+ xI: COMPUTED_REF_PREFIX
2248
2232
  });
2249
2233
 
2250
2234
 
2251
2235
  },
2252
2236
  988(__unused_rspack_module, __webpack_exports__, __webpack_require__) {
2253
- /* import */ var _config_index_js__rspack_import_1 = __webpack_require__(137);
2254
- /* import */ var _nestjs_common__rspack_import_0 = __webpack_require__(157);
2237
+ /* import */ var _flusys_nestjs_shared_utils__rspack_import_0 = __webpack_require__(216);
2238
+ /* import */ var _nestjs_common__rspack_import_1 = __webpack_require__(157);
2239
+ /* import */ var _config_index_js__rspack_import_2 = __webpack_require__(137);
2255
2240
 
2256
2241
 
2257
- const CACHE_PREFIX = 'permissions';
2258
- /**
2259
- * Build permission cache key for a user
2260
- * Uses same cache key format as PermissionGuard in nestjs-shared
2261
- */ function buildPermissionCacheKey(user, enableCompanyFeature) {
2262
- if (enableCompanyFeature && user.companyId) {
2263
- return `${CACHE_PREFIX}:company:${user.companyId}:branch:${user.branchId || 'null'}:user:${user.id}`;
2264
- }
2265
- return `${CACHE_PREFIX}:user:${user.id}`;
2266
- }
2267
- /**
2268
- * Get user permissions from cache
2269
- * Fail-closed: if cache fails, deny access rather than allowing potentially unauthorized access
2270
- */ async function getUserPermissionsFromCache(user, cacheManager, enableCompanyFeature) {
2271
- const cacheKey = buildPermissionCacheKey(user, enableCompanyFeature);
2242
+
2243
+ /** Whether `user` passes a form's AND / OR permission rule; fails closed when permissions cannot be read. */ async function hasFormPermission(user, logic, sources) {
2244
+ let granted;
2272
2245
  try {
2273
- const permissions = await cacheManager.get(cacheKey);
2274
- return permissions ?? [];
2246
+ granted = await (0,_flusys_nestjs_shared_utils__rspack_import_0.loadPermissionCodes)({
2247
+ userId: user.id,
2248
+ companyId: user.companyId,
2249
+ branchId: user.branchId
2250
+ }, sources);
2275
2251
  } catch {
2276
- throw new _nestjs_common__rspack_import_0.ForbiddenException({
2252
+ throw new _nestjs_common__rspack_import_1.ForbiddenException({
2277
2253
  message: 'Unable to verify permissions. Please try again.',
2278
- messageKey: _config_index_js__rspack_import_1/* .FORM_MESSAGES.PERMISSION_CHECK_FAILED */.Q.PERMISSION_CHECK_FAILED
2254
+ messageKey: _config_index_js__rspack_import_2/* .FORM_MESSAGES.PERMISSION_CHECK_FAILED */.Q.PERMISSION_CHECK_FAILED
2279
2255
  });
2280
2256
  }
2281
- }
2282
- /**
2283
- * Validate user has at least one of the required permissions
2284
- *
2285
- * @param user - The logged-in user
2286
- * @param requiredPermissions - Array of required permission strings (user needs at least one)
2287
- * @param cacheManager - Cache manager instance
2288
- * @param enableCompanyFeature - Whether company feature is enabled
2289
- * @returns true if user has permission, false otherwise
2290
- */ async function validateUserPermissions(user, requiredPermissions, cacheManager, enableCompanyFeature) {
2291
- if (!requiredPermissions.length) {
2292
- return true;
2293
- }
2294
- const userPermissions = await getUserPermissionsFromCache(user, cacheManager, enableCompanyFeature);
2295
- return requiredPermissions.some((perm)=>userPermissions.includes(perm));
2257
+ return (0,_flusys_nestjs_shared_utils__rspack_import_0.evaluatePermissionLogic)(logic, granted).passed;
2296
2258
  }
2297
2259
 
2298
2260
  __webpack_require__.d(__webpack_exports__, {
2299
- i: () => (validateUserPermissions)
2261
+ E: () => (hasFormPermission)
2300
2262
  });
2301
2263
 
2302
2264
 
@@ -2318,6 +2280,12 @@ module.exports = __rspack_external__flusys_nestjs_shared_decorators_c129b601;
2318
2280
  module.exports = __rspack_external__flusys_nestjs_shared_dtos_c4585026;
2319
2281
 
2320
2282
 
2283
+ },
2284
+ 103(module) {
2285
+
2286
+ module.exports = __rspack_external__flusys_nestjs_shared_interfaces_413f2706;
2287
+
2288
+
2321
2289
  },
2322
2290
  236(module) {
2323
2291