@flusys/nestjs-shared 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.
@@ -276,7 +276,6 @@ const FLOW_DEFINITION_PERMISSIONS = {
276
276
  UPDATE: 'entity_builder.flow_definition.update',
277
277
  DELETE: 'entity_builder.flow_definition.delete',
278
278
  TEST: 'entity_builder.flow_definition.test',
279
- /** Needed to save a flow that runs as the system (bypassing per-entity permissions). */ SYSTEM: 'entity_builder.flow_definition.system',
280
279
  /** Needed to save a flow containing a code node (sandboxed JavaScript). */ CODE: 'entity_builder.flow_definition.code'
281
280
  };
282
281
  const ENTITY_BUILDER_DYNAMIC_PREFIX = 'entity_builder.entity';
@@ -443,6 +442,53 @@ __webpack_require__.d(__webpack_exports__, {
443
442
  });
444
443
 
445
444
 
445
+ },
446
+ 374(__unused_rspack_module, __webpack_exports__, __webpack_require__) {
447
+ /* import */ var _nestjs_common__rspack_import_0 = __webpack_require__(157);
448
+ /* import */ var _constants_message_keys_js__rspack_import_1 = __webpack_require__(389);
449
+
450
+
451
+ class PermissionSystemUnavailableException extends _nestjs_common__rspack_import_0.InternalServerErrorException {
452
+ constructor(){
453
+ super({
454
+ success: false,
455
+ message: 'Permission system temporarily unavailable',
456
+ messageKey: _constants_message_keys_js__rspack_import_1/* .ERROR_MESSAGES.PERMISSION_SYSTEM_UNAVAILABLE */.UU.PERMISSION_SYSTEM_UNAVAILABLE
457
+ });
458
+ }
459
+ }
460
+ class InsufficientPermissionsException extends _nestjs_common__rspack_import_0.ForbiddenException {
461
+ constructor(missingPermissions, operator = 'AND'){
462
+ const messageKey = operator === 'OR' ? _constants_message_keys_js__rspack_import_1/* .ERROR_MESSAGES.INSUFFICIENT_PERMISSIONS_OR */.UU.INSUFFICIENT_PERMISSIONS_OR : _constants_message_keys_js__rspack_import_1/* .ERROR_MESSAGES.INSUFFICIENT_PERMISSIONS */.UU.INSUFFICIENT_PERMISSIONS;
463
+ super({
464
+ success: false,
465
+ message: 'Insufficient permissions',
466
+ messageKey,
467
+ messageVariables: {
468
+ permissions: missingPermissions.join(', ')
469
+ },
470
+ missingPermissions,
471
+ operator
472
+ });
473
+ }
474
+ }
475
+ class NoPermissionsFoundException extends _nestjs_common__rspack_import_0.ForbiddenException {
476
+ constructor(){
477
+ super({
478
+ success: false,
479
+ message: 'No permissions found',
480
+ messageKey: _constants_message_keys_js__rspack_import_1/* .ERROR_MESSAGES.NO_PERMISSIONS_FOUND */.UU.NO_PERMISSIONS_FOUND
481
+ });
482
+ }
483
+ }
484
+
485
+ __webpack_require__.d(__webpack_exports__, {
486
+ GS: () => (PermissionSystemUnavailableException),
487
+ d$: () => (NoPermissionsFoundException),
488
+ z8: () => (InsufficientPermissionsException)
489
+ });
490
+
491
+
446
492
  },
447
493
  154(__unused_rspack_module, __webpack_exports__, __webpack_require__) {
448
494
  /**
@@ -559,538 +605,6 @@ __webpack_require__.d(__webpack_exports__, {
559
605
  });
560
606
 
561
607
 
562
- },
563
- 528(__unused_rspack_module, __webpack_exports__, __webpack_require__) {
564
-
565
- // EXPORTS
566
- __webpack_require__.d(__webpack_exports__, {
567
- s8: () => (/* reexport */ permission_cache_key_util/* .DEFAULT_COMPANY_PERMISSION_KEY_FORMAT */.s8),
568
- q6: () => (/* reexport */ permission_cache_key_util/* .DEFAULT_USER_PERMISSION_KEY_FORMAT */.q6),
569
- wf: () => (/* reexport */ applyCompanyFilter),
570
- kQ: () => (/* reexport */ buildCompanyWhereCondition),
571
- fJ: () => (/* reexport */ buildCookieOptions),
572
- oV: () => (/* reexport */ buildFrontendUrl),
573
- nt: () => (/* reexport */ permission_cache_key_util/* .buildPermissionCacheKey */.nt),
574
- qW: () => (/* reexport */ cache_tenant_util/* .cacheTenantId */.q),
575
- ZD: () => (/* reexport */ escapeHtml),
576
- qo: () => (/* reexport */ escapeHtmlVariables),
577
- hX: () => (/* reexport */ extractFrontendUrl),
578
- Sd: () => (/* reexport */ formatInTimezone),
579
- sN: () => (/* reexport */ generateSecureToken),
580
- z9: () => (/* reexport */ generateSlug),
581
- oo: () => (/* reexport */ generateUniqueSlug),
582
- oB: () => (/* reexport */ getTodayInTimezone),
583
- f: () => (/* reexport */ hasCompanyId),
584
- QK: () => (/* reexport */ hashToken),
585
- wh: () => (/* reexport */ isBrowserRequest),
586
- _d: () => (/* reexport */ isExpired),
587
- _c: () => (/* reexport */ event_pattern_util/* .isMultiWildcard */._c),
588
- lk: () => (/* reexport */ optional_module_util/* .loadOptionalModule */.l),
589
- vf: () => (/* reexport */ localToUtc),
590
- qe: () => (/* reexport */ event_pattern_util/* .matchEventPattern */.qe),
591
- xY: () => (/* reexport */ permission_match_util/* .matchesPermission */.x),
592
- RG: () => (/* reexport */ messageRef),
593
- Ju: () => (/* reexport */ parseDurationToMs),
594
- IN: () => (/* reexport */ event_pattern_util/* .sanitizeEventPayload */.IN),
595
- v4: () => (/* reexport */ utcToLocal),
596
- bE: () => (/* reexport */ validateCompanyOwnership),
597
- o0: () => (/* reexport */ withSavepoint)
598
- });
599
-
600
- // EXTERNAL MODULE: ./projects/nestjs-shared/src/utils/cache-tenant.util.ts
601
- var cache_tenant_util = __webpack_require__(154);
602
- ;// CONCATENATED MODULE: ./projects/nestjs-shared/src/utils/date-time.util.ts
603
- function getOffsetMinutes(utcDate, timezone) {
604
- const extract = (tz)=>{
605
- const parts = new Intl.DateTimeFormat('en-US', {
606
- timeZone: tz,
607
- year: 'numeric',
608
- month: '2-digit',
609
- day: '2-digit',
610
- hour: '2-digit',
611
- minute: '2-digit',
612
- second: '2-digit',
613
- hour12: false
614
- }).formatToParts(utcDate);
615
- const p = Object.fromEntries(parts.filter((x)=>x.type !== 'literal').map((x)=>[
616
- x.type,
617
- parseInt(x.value)
618
- ]));
619
- return Date.UTC(p['year'], p['month'] - 1, p['day'], p['hour'], p['minute'], p['second']);
620
- };
621
- return (extract(timezone) - extract('UTC')) / 60_000;
622
- }
623
- function localToUtc(dateStr, timeStr, timezone) {
624
- const [year, month, day] = dateStr.split('-').map(Number);
625
- const [hours, minutes] = timeStr.split(':').map(Number);
626
- const approx = new Date(Date.UTC(year, month - 1, day, hours, minutes, 0));
627
- const offset = getOffsetMinutes(approx, timezone);
628
- const utcDate = new Date(approx.getTime() - offset * 60_000);
629
- // DST boundary guard: re-check offset after adjustment
630
- const finalOffset = getOffsetMinutes(utcDate, timezone);
631
- return finalOffset !== offset ? new Date(approx.getTime() - finalOffset * 60_000) : utcDate;
632
- }
633
- function utcToLocal(utcDate, timezone) {
634
- const d = typeof utcDate === 'string' ? new Date(utcDate) : utcDate;
635
- const parts = new Intl.DateTimeFormat('en-CA', {
636
- timeZone: timezone,
637
- year: 'numeric',
638
- month: '2-digit',
639
- day: '2-digit',
640
- hour: '2-digit',
641
- minute: '2-digit',
642
- hour12: false
643
- }).formatToParts(d);
644
- const p = Object.fromEntries(parts.filter((x)=>x.type !== 'literal').map((x)=>[
645
- x.type,
646
- x.value
647
- ]));
648
- // en-CA formats midnight hour as '24' — normalize to '00'
649
- const hour = p['hour'] === '24' ? '00' : p['hour'];
650
- return {
651
- date: `${p['year']}-${p['month']}-${p['day']}`,
652
- time: `${hour}:${p['minute']}`,
653
- display: new Intl.DateTimeFormat('en-US', {
654
- timeZone: timezone,
655
- dateStyle: 'medium',
656
- timeStyle: 'short'
657
- }).format(d)
658
- };
659
- }
660
- function formatInTimezone(utcDate, timezone, options = {
661
- dateStyle: 'medium',
662
- timeStyle: 'short'
663
- }) {
664
- const d = typeof utcDate === 'string' ? new Date(utcDate) : utcDate;
665
- return new Intl.DateTimeFormat('en-US', {
666
- ...options,
667
- timeZone: timezone
668
- }).format(d);
669
- }
670
- function getTodayInTimezone(timezone) {
671
- return utcToLocal(new Date(), timezone).date;
672
- }
673
-
674
- // EXTERNAL MODULE: ./projects/nestjs-shared/src/utils/event-pattern.util.ts
675
- var event_pattern_util = __webpack_require__(456);
676
- ;// CONCATENATED MODULE: ./projects/nestjs-shared/src/utils/html-sanitizer.util.ts
677
- /**
678
- * HTML Sanitizer Utilities
679
- *
680
- * Provides functions for escaping HTML content to prevent XSS attacks.
681
- * Use these utilities when interpolating user-provided variables into HTML content.
682
- */ /**
683
- * HTML entity mapping for escaping special characters
684
- */ const HTML_ESCAPE_MAP = {
685
- '&': '&',
686
- '<': '&lt;',
687
- '>': '&gt;',
688
- '"': '&quot;',
689
- "'": '&#x27;',
690
- '/': '&#x2F;',
691
- '`': '&#x60;',
692
- '=': '&#x3D;'
693
- };
694
- /**
695
- * Regex pattern matching characters that need HTML escaping
696
- */ const HTML_ESCAPE_REGEX = /[&<>"'`=/]/g;
697
- /**
698
- * Escapes HTML special characters to prevent XSS attacks.
699
- *
700
- * @param str - The string to escape
701
- * @returns The escaped string safe for HTML insertion
702
- *
703
- * @example
704
- * ```typescript
705
- * escapeHtml('<script>alert("xss")</script>')
706
- * // Returns: '&lt;script&gt;alert(&quot;xss&quot;)&lt;/script&gt;'
707
- * ```
708
- */ function escapeHtml(str) {
709
- if (!str || typeof str !== 'string') {
710
- return str ?? '';
711
- }
712
- return str.replace(HTML_ESCAPE_REGEX, (char)=>HTML_ESCAPE_MAP[char]);
713
- }
714
- /**
715
- * Escapes all string values in a variables object for safe HTML interpolation.
716
- * Non-string values are converted to strings and escaped.
717
- *
718
- * @param variables - Object containing template variables
719
- * @returns New object with all string values HTML-escaped
720
- *
721
- * @example
722
- * ```typescript
723
- * escapeHtmlVariables({ name: '<script>xss</script>', count: 5 })
724
- * // Returns: { name: '&lt;script&gt;xss&lt;/script&gt;', count: '5' }
725
- * ```
726
- */ function escapeHtmlVariables(variables) {
727
- if (!variables || typeof variables !== 'object') {
728
- return {};
729
- }
730
- const escaped = {};
731
- for (const [key, value] of Object.entries(variables)){
732
- if (value === null || value === undefined) {
733
- escaped[key] = '';
734
- } else if (typeof value === 'string') {
735
- escaped[key] = escapeHtml(value);
736
- } else if (typeof value === 'object') {
737
- // For objects/arrays, stringify and escape
738
- escaped[key] = escapeHtml(JSON.stringify(value));
739
- } else {
740
- // For numbers, booleans, etc., convert to string (no escaping needed)
741
- escaped[key] = String(value);
742
- }
743
- }
744
- return escaped;
745
- }
746
-
747
- ;// CONCATENATED MODULE: ./projects/nestjs-shared/src/utils/message-ref.util.ts
748
- function messageRef(messageKey, messageVariables) {
749
- return messageVariables ? {
750
- messageKey,
751
- messageVariables
752
- } : {
753
- messageKey
754
- };
755
- }
756
-
757
- // EXTERNAL MODULE: ./projects/nestjs-shared/src/utils/optional-module.util.ts
758
- var optional_module_util = __webpack_require__(368);
759
- // EXTERNAL MODULE: ./projects/nestjs-shared/src/utils/permission-cache-key.util.ts
760
- var permission_cache_key_util = __webpack_require__(199);
761
- // EXTERNAL MODULE: ./projects/nestjs-shared/src/utils/permission-match.util.ts
762
- var permission_match_util = __webpack_require__(788);
763
- // EXTERNAL MODULE: external "@nestjs/common"
764
- var common_ = __webpack_require__(157);
765
- // EXTERNAL MODULE: ./projects/nestjs-shared/src/constants/index.ts
766
- var constants = __webpack_require__(43);
767
- ;// CONCATENATED MODULE: ./projects/nestjs-shared/src/utils/query-helpers.util.ts
768
-
769
-
770
- /**
771
- * Apply company filter to a query builder if company feature is enabled.
772
- * Centralizes the common pattern of filtering by user's company.
773
- *
774
- * @param query - TypeORM SelectQueryBuilder
775
- * @param config - Company filter configuration
776
- * @param user - Current logged user (may be null)
777
- * @returns The modified query builder
778
- *
779
- * @example
780
- * ```typescript
781
- * applyCompanyFilter(query, {
782
- * isCompanyFeatureEnabled: this.config.isCompanyFeatureEnabled(),
783
- * entityAlias: 'file_manager',
784
- * }, user);
785
- * ```
786
- */ function applyCompanyFilter(query, config, user) {
787
- const columnName = config.columnName ?? 'companyId';
788
- if (config.isCompanyFeatureEnabled && user?.companyId) {
789
- query.andWhere(`${config.entityAlias}.${columnName} = :companyId`, {
790
- companyId: user.companyId
791
- });
792
- }
793
- return query;
794
- }
795
- /**
796
- * Build a where condition object with optional company filter.
797
- *
798
- * @param baseWhere - Base where condition object
799
- * @param isCompanyFeatureEnabled - Whether company feature is enabled
800
- * @param user - Current logged user
801
- * @returns Where condition with companyId added if applicable
802
- *
803
- * @example
804
- * ```typescript
805
- * const where = buildCompanyWhereCondition(
806
- * { id: dto.id },
807
- * this.config.isCompanyFeatureEnabled(),
808
- * user
809
- * );
810
- * // Returns { id: dto.id, companyId: user.companyId } if company feature enabled
811
- * ```
812
- */ function buildCompanyWhereCondition(baseWhere, isCompanyFeatureEnabled, user) {
813
- if (isCompanyFeatureEnabled && user?.companyId) {
814
- return {
815
- ...baseWhere,
816
- companyId: user.companyId
817
- };
818
- }
819
- return baseWhere;
820
- }
821
- /**
822
- * Type guard to check if entity has companyId property
823
- */ function hasCompanyId(entity) {
824
- return entity !== null && typeof entity === 'object' && 'companyId' in entity;
825
- }
826
- /**
827
- * Validates that an entity with companyId belongs to the user's company.
828
- * Throws BadRequestException if the entity belongs to another company.
829
- *
830
- * @param entity - Entity to validate
831
- * @param user - Current logged user
832
- * @param isCompanyFeatureEnabled - Whether company feature is enabled
833
- * @param entityName - Name of entity for error message (e.g., 'Email configuration')
834
- * @throws BadRequestException if entity belongs to another company
835
- *
836
- * @example
837
- * ```typescript
838
- * validateCompanyOwnership(config, user, this.config.isCompanyFeatureEnabled(), 'Email configuration');
839
- * ```
840
- */ function validateCompanyOwnership(entity, user, isCompanyFeatureEnabled, entityName) {
841
- if (isCompanyFeatureEnabled && user?.companyId && hasCompanyId(entity)) {
842
- if (entity.companyId && entity.companyId !== user.companyId) {
843
- throw new common_.BadRequestException({
844
- message: `${entityName} belongs to another company`,
845
- messageKey: constants/* .AUTH_MESSAGES.ENTITY_BELOG_ANOTHER_COMPANY */.mK.ENTITY_BELOG_ANOTHER_COMPANY,
846
- messageVariables: {
847
- entity: entityName
848
- }
849
- });
850
- }
851
- }
852
- }
853
- /**
854
- * Runs `fn` inside a savepoint of an already-open transaction. A failure rolls back only what
855
- * `fn` did, so the caller can swallow the error and keep using the transaction - without it,
856
- * PostgreSQL aborts the whole transaction after any failed statement.
857
- */ async function withSavepoint(queryRunner, fn) {
858
- await queryRunner.startTransaction();
859
- try {
860
- const result = await fn();
861
- await queryRunner.commitTransaction();
862
- return result;
863
- } catch (error) {
864
- await queryRunner.rollbackTransaction();
865
- throw error;
866
- }
867
- }
868
-
869
- // EXTERNAL MODULE: external "@flusys/nestjs-core/config"
870
- var config_ = __webpack_require__(307);
871
- ;// CONCATENATED MODULE: ./projects/nestjs-shared/src/utils/request.util.ts
872
-
873
-
874
-
875
- /** Time unit multipliers in milliseconds */ const TIME_UNIT_MS = {
876
- s: 1000,
877
- m: 60 * 1000,
878
- h: 60 * 60 * 1000,
879
- d: 24 * 60 * 60 * 1000,
880
- w: 7 * 24 * 60 * 60 * 1000
881
- };
882
- /** Get normalized client type from request header */ function getClientType(req) {
883
- const clientType = req.headers[constants/* .CLIENT_TYPE_HEADER */.xw];
884
- return clientType ? clientType.toLowerCase() : null;
885
- }
886
- /**
887
- * Detect if request is from a browser client.
888
- * Uses x-client-type header first, then falls back to user-agent detection.
889
- */ function isBrowserRequest(req) {
890
- const clientType = getClientType(req);
891
- if (clientType) {
892
- return clientType === 'browser' || clientType === 'web';
893
- }
894
- const accept = req.headers['accept'] || '';
895
- if (accept.includes('text/html')) return true;
896
- const userAgent = req.headers['user-agent'] || '';
897
- const browserPatterns = /mozilla|chrome|safari|firefox|edge|opera|msie/i;
898
- return browserPatterns.test(userAgent) && !userAgent.includes('Postman');
899
- }
900
- /**
901
- * Build secure cookie options based on request context.
902
- * Handles HTTPS detection for TLS-terminating proxies.
903
- */ function buildCookieOptions(req) {
904
- const hostname = req.hostname || '';
905
- const origin = req.headers.origin || '';
906
- const isProduction = config_.envConfig.isProduction();
907
- const forwardedProto = req.headers['x-forwarded-proto'];
908
- const isHttps = isProduction || forwardedProto === 'https' || origin.startsWith('https://') || req.secure;
909
- let domain;
910
- const domainParts = hostname.split('.');
911
- if (domainParts.length > 2 && !hostname.includes('localhost')) {
912
- domain = '.' + domainParts.slice(-2).join('.');
913
- }
914
- return {
915
- secure: isHttps,
916
- sameSite: isHttps ? 'strict' : 'lax',
917
- ...domain && {
918
- domain
919
- }
920
- };
921
- }
922
- /**
923
- * Parse duration string to milliseconds.
924
- * @example '7d' → 604800000, '24h' → 86400000, '30m' → 1800000
925
- */ function parseDurationToMs(duration, defaultMs = TIME_UNIT_MS.w) {
926
- const match = duration.match(/^(\d+)(s|m|h|d|w)$/);
927
- if (!match) return defaultMs;
928
- const value = parseInt(match[1], 10);
929
- const unit = match[2];
930
- return value * (TIME_UNIT_MS[unit] ?? TIME_UNIT_MS.d);
931
- }
932
- /**
933
- * Determine the frontend base URL for auth-related links.
934
- * Matches the request's Origin/Referer against configured allowed origins.
935
- * Never trusts the request's Host header - it is client-controlled and would
936
- * let an attacker redirect password-reset/verification links to their own domain.
937
- */ function extractFrontendUrl(req) {
938
- const allowedOrigins = config_.envConfig.getOrigins();
939
- const origin = (req.get('origin') ?? req.get('referer') ?? '').trim();
940
- if (origin && allowedOrigins.length) {
941
- const matched = allowedOrigins.find((u)=>origin.startsWith(u));
942
- if (matched) {
943
- return matched;
944
- }
945
- }
946
- if (allowedOrigins.length) {
947
- return allowedOrigins[0];
948
- }
949
- throw new common_.BadRequestException({
950
- message: 'Unable to determine frontend URL from request',
951
- messageKey: constants/* .ERROR_MESSAGES.VALIDATION */.UU.VALIDATION
952
- });
953
- }
954
- function buildFrontendUrl(req, path) {
955
- const baseUrl = extractFrontendUrl(req);
956
- return `${baseUrl}${path}`;
957
- }
958
-
959
- ;// CONCATENATED MODULE: ./projects/nestjs-shared/src/utils/string.util.ts
960
- /**
961
- * String utility functions
962
- */ /**
963
- * Generate URL-friendly slug from a string
964
- * @param text - The text to convert to slug
965
- * @param maxLength - Maximum length of the slug (default: 100)
966
- * @returns URL-friendly slug
967
- *
968
- * @example
969
- * generateSlug('My Company Name') // 'my-company-name'
970
- * generateSlug('Hello World!') // 'hello-world'
971
- */ function generateSlug(text, maxLength = 100) {
972
- return text.toLowerCase().trim().replace(/[^a-z0-9\s-]/g, '') // Remove special characters
973
- .replace(/\s+/g, '-') // Replace spaces with hyphens
974
- .replace(/-+/g, '-') // Replace multiple hyphens with single
975
- .replace(/^-+|-+$/g, '') // Remove leading/trailing hyphens
976
- .substring(0, maxLength);
977
- }
978
- /**
979
- * Generate unique slug using single database query (optimized for large datasets)
980
- * Appends numeric suffix if collision exists (e.g., 'my-company-1', 'my-company-2')
981
- *
982
- * Performance: O(1) database query regardless of collision count
983
- * - Works efficiently with millions of records
984
- * - Requires index on slug column for best performance
985
- *
986
- * @param text - The text to convert to slug
987
- * @param findMatchingSlugs - Async function that returns all slugs matching base pattern
988
- * Should query: WHERE slug = baseSlug OR slug LIKE 'baseSlug-%'
989
- * @param maxLength - Maximum length of the base slug (default: 100)
990
- * @returns Unique URL-friendly slug
991
- *
992
- * @example
993
- * const slug = await generateUniqueSlug('My Company', async (baseSlug) => {
994
- * return repo
995
- * .createQueryBuilder('c')
996
- * .select('c.slug')
997
- * .where('c.slug = :base OR c.slug LIKE :pattern', {
998
- * base: baseSlug,
999
- * pattern: `${baseSlug}-%`,
1000
- * })
1001
- * .getMany()
1002
- * .then(rows => rows.map(r => r.slug));
1003
- * });
1004
- */ async function generateUniqueSlug(text, findMatchingSlugs, maxLength = 100) {
1005
- const baseSlug = generateSlug(text, maxLength);
1006
- // Single query to get all matching slugs
1007
- const existingSlugs = await findMatchingSlugs(baseSlug);
1008
- // No collisions - base slug is available
1009
- if (existingSlugs.length === 0) {
1010
- return baseSlug;
1011
- }
1012
- // Build set for O(1) lookup
1013
- const existingSet = new Set(existingSlugs);
1014
- // Base slug not taken (only suffixed versions exist)
1015
- if (!existingSet.has(baseSlug)) {
1016
- return baseSlug;
1017
- }
1018
- // Find next available suffix in memory (fast)
1019
- let counter = 1;
1020
- while(counter < 10000){
1021
- const slugWithSuffix = `${baseSlug}-${counter}`;
1022
- if (!existingSet.has(slugWithSuffix)) {
1023
- return slugWithSuffix;
1024
- }
1025
- counter++;
1026
- }
1027
- // Fallback: append random string (extremely rare edge case)
1028
- const randomSuffix = Math.random().toString(36).substring(2, 8);
1029
- return `${baseSlug}-${randomSuffix}`;
1030
- }
1031
-
1032
- // EXTERNAL MODULE: external "crypto"
1033
- var external_crypto_ = __webpack_require__(823);
1034
- ;// CONCATENATED MODULE: ./projects/nestjs-shared/src/utils/token.util.ts
1035
-
1036
- /** 32 random bytes as hex, suitable for a password-reset/OTP/access-style token */ function generateSecureToken() {
1037
- return external_crypto_.randomBytes(32).toString('hex');
1038
- }
1039
- /** SHA-256 hash of a token, for storing/comparing tokens without keeping the raw value */ function hashToken(value) {
1040
- return external_crypto_.createHash('sha256').update(value).digest('hex');
1041
- }
1042
- function isExpired(expiresAt) {
1043
- return expiresAt < new Date();
1044
- }
1045
-
1046
- ;// CONCATENATED MODULE: ./projects/nestjs-shared/src/utils/index.ts
1047
-
1048
-
1049
-
1050
-
1051
-
1052
-
1053
-
1054
-
1055
-
1056
-
1057
-
1058
-
1059
-
1060
-
1061
- },
1062
- 368(__unused_rspack_module, __webpack_exports__, __webpack_require__) {
1063
- /**
1064
- * Loads an optional peer dependency at runtime.
1065
- *
1066
- * The specifier has to stay invisible to the bundler. A plain `import(name)`
1067
- * looks dynamic in source but webpack rewrites it into a context module rooted
1068
- * at this directory - one that can only ever resolve files sitting next to this
1069
- * one - so `amqplib` and `kafkajs` fail to load in the bundled app no matter how
1070
- * correctly they are installed. Both lookups below are assembled at runtime,
1071
- * where no bundler can rewrite them.
1072
- *
1073
- * `require` is used first because it is what the CommonJS builds actually run
1074
- * on; native ESM has no `require`, and there `import()` is the only way in.
1075
- */ async function loadOptionalModule(name) {
1076
- const runtimeRequire = resolveRuntimeRequire();
1077
- if (runtimeRequire) return runtimeRequire(name);
1078
- const runtimeImport = new Function('specifier', 'return import(specifier);');
1079
- return runtimeImport(name);
1080
- }
1081
- /** Undefined under native ESM and inside a bundle that shadows nothing back */ function resolveRuntimeRequire() {
1082
- try {
1083
- return eval('typeof require === "function" ? require : undefined');
1084
- } catch {
1085
- return undefined;
1086
- }
1087
- }
1088
-
1089
- __webpack_require__.d(__webpack_exports__, {
1090
- l: () => (loadOptionalModule)
1091
- });
1092
-
1093
-
1094
608
  },
1095
609
  199(__unused_rspack_module, __webpack_exports__, __webpack_require__) {
1096
610
  /* import */ var _constants_index_js__rspack_import_0 = __webpack_require__(43);
@@ -1115,6 +629,8 @@ __webpack_require__.d(__webpack_exports__, {
1115
629
 
1116
630
  },
1117
631
  788(__unused_rspack_module, __webpack_exports__, __webpack_require__) {
632
+ /* import */ var _exceptions_permission_exception_js__rspack_import_0 = __webpack_require__(374);
633
+
1118
634
  /** Whether `granted` covers `required`, honoring `*` (everything) and `prefix.*` (everything under prefix) wildcards */ function matchesPermission(granted, required) {
1119
635
  if (granted.includes(required)) return true;
1120
636
  for (const permission of granted){
@@ -1123,9 +639,73 @@ __webpack_require__.d(__webpack_exports__, {
1123
639
  }
1124
640
  return false;
1125
641
  }
642
+ /** Evaluates an AND / OR permission tree against `granted`; a group without children fails closed. */ function evaluatePermissionLogic(node, granted) {
643
+ if (node.type === 'action') {
644
+ const passed = matchesPermission(granted, node.actionId);
645
+ return {
646
+ passed,
647
+ missingPermissions: passed ? [] : [
648
+ node.actionId
649
+ ],
650
+ operator: 'AND'
651
+ };
652
+ }
653
+ const { operator, children } = node;
654
+ if (!children?.length) return {
655
+ passed: false,
656
+ missingPermissions: [],
657
+ operator
658
+ };
659
+ const results = children.map((child)=>evaluatePermissionLogic(child, granted));
660
+ const passed = operator === 'OR' ? results.some((r)=>r.passed) : results.every((r)=>r.passed);
661
+ return {
662
+ passed,
663
+ missingPermissions: passed ? [] : results.flatMap((r)=>r.missingPermissions),
664
+ operator
665
+ };
666
+ }
667
+ /** Every permission code a tree names, each once, in order. */ function permissionLogicCodes(node) {
668
+ if (node.type === 'action') return node.actionId ? [
669
+ node.actionId
670
+ ] : [];
671
+ return [
672
+ ...new Set((node.children ?? []).flatMap(permissionLogicCodes))
673
+ ];
674
+ }
675
+ /** Groups nested in a permission rule, the outer one included. */ const MAX_PERMISSION_LOGIC_DEPTH = 5;
676
+ /** Permission codes one rule may name. */ const MAX_PERMISSION_LOGIC_CODES = 50;
677
+ const MAX_PERMISSION_CODE_LENGTH = 255;
678
+ /** What is wrong with a stored permission rule: `invalid` (its shape), `empty` (it names no permission), `too_many` codes, or null. */ function permissionLogicProblem(value) {
679
+ let codes = 0;
680
+ const walk = (node, depth)=>{
681
+ if (!node || typeof node !== 'object' || Array.isArray(node)) return false;
682
+ const n = node;
683
+ if (n['type'] === 'action') {
684
+ codes++;
685
+ return typeof n['actionId'] === 'string' && n['actionId'].trim() !== '' && n['actionId'].length <= MAX_PERMISSION_CODE_LENGTH;
686
+ }
687
+ if (n['type'] !== 'group' || n['operator'] !== 'AND' && n['operator'] !== 'OR' || !Array.isArray(n['children'])) return false;
688
+ if (depth >= MAX_PERMISSION_LOGIC_DEPTH || !n['children'].length) return false;
689
+ return n['children'].every((child)=>walk(child, depth + 1));
690
+ };
691
+ if (!value || typeof value !== 'object') return 'invalid';
692
+ const node = value;
693
+ if (node['type'] === 'group' && (node['operator'] === 'AND' || node['operator'] === 'OR') && Array.isArray(node['children']) && !node['children'].length) return 'empty';
694
+ if (!walk(value, 0)) return 'invalid';
695
+ return codes > MAX_PERMISSION_LOGIC_CODES ? 'too_many' : null;
696
+ }
697
+ /** A user's codes in `scope`, from the resolver or else the cache; with neither, fails closed with PermissionSystemUnavailableException. */ async function loadPermissionCodes(scope, sources) {
698
+ if (sources.resolver) return sources.resolver.getPermissionCodes(scope);
699
+ if (sources.cache?.isCacheConfigured()) return sources.cache.getPermissions(scope);
700
+ throw new _exceptions_permission_exception_js__rspack_import_0/* .PermissionSystemUnavailableException */.GS();
701
+ }
1126
702
 
1127
703
  __webpack_require__.d(__webpack_exports__, {
1128
- x: () => (matchesPermission)
704
+ CQ: () => (loadPermissionCodes),
705
+ HU: () => (permissionLogicProblem),
706
+ MO: () => (permissionLogicCodes),
707
+ Ng: () => (evaluatePermissionLogic),
708
+ xY: () => (matchesPermission)
1129
709
  });
1130
710
 
1131
711
 
@@ -1152,37 +732,64 @@ module.exports = __rspack_external_crypto;
1152
732
  };
1153
733
  import { __webpack_require__ } from '../runtime.js';
1154
734
  var __webpack_exec__ = function(moduleId) { return __webpack_require__(__webpack_require__.s = moduleId); }
1155
- import * as __rspack_chunk_1 from './index.js';
735
+ import * as __rspack_chunk_1 from '../282.js';
1156
736
  __webpack_require__.C(__rspack_chunk_1);
1157
- var __webpack_exports__ = __webpack_exec__(528);var __webpack_exports__DEFAULT_COMPANY_PERMISSION_KEY_FORMAT = __webpack_exports__.s8;
737
+ import * as __rspack_chunk_2 from './index.js';
738
+ __webpack_require__.C(__rspack_chunk_2);
739
+ var __webpack_exports__ = __webpack_exec__(129);var __webpack_exports__DEFAULT_COMPANY_PERMISSION_KEY_FORMAT = __webpack_exports__.s8;
1158
740
  var __webpack_exports__DEFAULT_USER_PERMISSION_KEY_FORMAT = __webpack_exports__.q6;
741
+ var __webpack_exports__EXPRESSION_FUNCTIONS = __webpack_exports__.Dd;
742
+ var __webpack_exports__EXPRESSION_LIMITS = __webpack_exports__.wj;
743
+ var __webpack_exports__ExpressionError = __webpack_exports__.tj;
744
+ var __webpack_exports__MAX_PERMISSION_LOGIC_CODES = __webpack_exports__.Df;
745
+ var __webpack_exports__MAX_PERMISSION_LOGIC_DEPTH = __webpack_exports__.kW;
746
+ var __webpack_exports__SAFE_REGEX_LIMITS = __webpack_exports__.ZA;
747
+ var __webpack_exports__applyBranchFilter = __webpack_exports__.f7;
1159
748
  var __webpack_exports__applyCompanyFilter = __webpack_exports__.wf;
749
+ var __webpack_exports__assertValidConditionGroup = __webpack_exports__.P;
750
+ var __webpack_exports__assertValidExpression = __webpack_exports__.bh;
1160
751
  var __webpack_exports__buildCompanyWhereCondition = __webpack_exports__.kQ;
1161
752
  var __webpack_exports__buildCookieOptions = __webpack_exports__.fJ;
1162
753
  var __webpack_exports__buildFrontendUrl = __webpack_exports__.oV;
1163
754
  var __webpack_exports__buildPermissionCacheKey = __webpack_exports__.nt;
1164
755
  var __webpack_exports__cacheTenantId = __webpack_exports__.qW;
756
+ var __webpack_exports__collectBranchAncestorIds = __webpack_exports__.Sf;
757
+ var __webpack_exports__collectBranchDescendantIds = __webpack_exports__.qY;
758
+ var __webpack_exports__compileSafeRegex = __webpack_exports__.Yc;
1165
759
  var __webpack_exports__escapeHtml = __webpack_exports__.ZD;
1166
760
  var __webpack_exports__escapeHtmlVariables = __webpack_exports__.qo;
761
+ var __webpack_exports__evaluateConditionGroup = __webpack_exports__.Sb;
762
+ var __webpack_exports__evaluateExpression = __webpack_exports__.O4;
763
+ var __webpack_exports__evaluatePermissionLogic = __webpack_exports__.Ng;
764
+ var __webpack_exports__expressionToNumber = __webpack_exports__.HK;
765
+ var __webpack_exports__expressionToText = __webpack_exports__.zj;
1167
766
  var __webpack_exports__extractFrontendUrl = __webpack_exports__.hX;
1168
767
  var __webpack_exports__formatInTimezone = __webpack_exports__.Sd;
1169
768
  var __webpack_exports__generateSecureToken = __webpack_exports__.sN;
1170
769
  var __webpack_exports__generateSlug = __webpack_exports__.z9;
1171
770
  var __webpack_exports__generateUniqueSlug = __webpack_exports__.oo;
771
+ var __webpack_exports__getBranchPath = __webpack_exports__.gN;
1172
772
  var __webpack_exports__getTodayInTimezone = __webpack_exports__.oB;
1173
773
  var __webpack_exports__hasCompanyId = __webpack_exports__.f;
1174
774
  var __webpack_exports__hashToken = __webpack_exports__.QK;
775
+ var __webpack_exports__isBranchWithin = __webpack_exports__.QU;
1175
776
  var __webpack_exports__isBrowserRequest = __webpack_exports__.wh;
1176
777
  var __webpack_exports__isExpired = __webpack_exports__._d;
778
+ var __webpack_exports__isExpressionError = __webpack_exports__.tX;
1177
779
  var __webpack_exports__isMultiWildcard = __webpack_exports__._c;
1178
780
  var __webpack_exports__loadOptionalModule = __webpack_exports__.lk;
781
+ var __webpack_exports__loadPermissionCodes = __webpack_exports__.CQ;
1179
782
  var __webpack_exports__localToUtc = __webpack_exports__.vf;
1180
783
  var __webpack_exports__matchEventPattern = __webpack_exports__.qe;
1181
784
  var __webpack_exports__matchesPermission = __webpack_exports__.xY;
1182
785
  var __webpack_exports__messageRef = __webpack_exports__.RG;
786
+ var __webpack_exports__normalizeBranchIds = __webpack_exports__.Ty;
1183
787
  var __webpack_exports__parseDurationToMs = __webpack_exports__.Ju;
788
+ var __webpack_exports__permissionLogicCodes = __webpack_exports__.MO;
789
+ var __webpack_exports__permissionLogicProblem = __webpack_exports__.HU;
790
+ var __webpack_exports__safeRegexProblem = __webpack_exports__._X;
1184
791
  var __webpack_exports__sanitizeEventPayload = __webpack_exports__.IN;
1185
792
  var __webpack_exports__utcToLocal = __webpack_exports__.v4;
1186
793
  var __webpack_exports__validateCompanyOwnership = __webpack_exports__.bE;
1187
794
  var __webpack_exports__withSavepoint = __webpack_exports__.o0;
1188
- export { __webpack_exports__DEFAULT_COMPANY_PERMISSION_KEY_FORMAT as DEFAULT_COMPANY_PERMISSION_KEY_FORMAT, __webpack_exports__DEFAULT_USER_PERMISSION_KEY_FORMAT as DEFAULT_USER_PERMISSION_KEY_FORMAT, __webpack_exports__applyCompanyFilter as applyCompanyFilter, __webpack_exports__buildCompanyWhereCondition as buildCompanyWhereCondition, __webpack_exports__buildCookieOptions as buildCookieOptions, __webpack_exports__buildFrontendUrl as buildFrontendUrl, __webpack_exports__buildPermissionCacheKey as buildPermissionCacheKey, __webpack_exports__cacheTenantId as cacheTenantId, __webpack_exports__escapeHtml as escapeHtml, __webpack_exports__escapeHtmlVariables as escapeHtmlVariables, __webpack_exports__extractFrontendUrl as extractFrontendUrl, __webpack_exports__formatInTimezone as formatInTimezone, __webpack_exports__generateSecureToken as generateSecureToken, __webpack_exports__generateSlug as generateSlug, __webpack_exports__generateUniqueSlug as generateUniqueSlug, __webpack_exports__getTodayInTimezone as getTodayInTimezone, __webpack_exports__hasCompanyId as hasCompanyId, __webpack_exports__hashToken as hashToken, __webpack_exports__isBrowserRequest as isBrowserRequest, __webpack_exports__isExpired as isExpired, __webpack_exports__isMultiWildcard as isMultiWildcard, __webpack_exports__loadOptionalModule as loadOptionalModule, __webpack_exports__localToUtc as localToUtc, __webpack_exports__matchEventPattern as matchEventPattern, __webpack_exports__matchesPermission as matchesPermission, __webpack_exports__messageRef as messageRef, __webpack_exports__parseDurationToMs as parseDurationToMs, __webpack_exports__sanitizeEventPayload as sanitizeEventPayload, __webpack_exports__utcToLocal as utcToLocal, __webpack_exports__validateCompanyOwnership as validateCompanyOwnership, __webpack_exports__withSavepoint as withSavepoint };
795
+ export { __webpack_exports__DEFAULT_COMPANY_PERMISSION_KEY_FORMAT as DEFAULT_COMPANY_PERMISSION_KEY_FORMAT, __webpack_exports__DEFAULT_USER_PERMISSION_KEY_FORMAT as DEFAULT_USER_PERMISSION_KEY_FORMAT, __webpack_exports__EXPRESSION_FUNCTIONS as EXPRESSION_FUNCTIONS, __webpack_exports__EXPRESSION_LIMITS as EXPRESSION_LIMITS, __webpack_exports__ExpressionError as ExpressionError, __webpack_exports__MAX_PERMISSION_LOGIC_CODES as MAX_PERMISSION_LOGIC_CODES, __webpack_exports__MAX_PERMISSION_LOGIC_DEPTH as MAX_PERMISSION_LOGIC_DEPTH, __webpack_exports__SAFE_REGEX_LIMITS as SAFE_REGEX_LIMITS, __webpack_exports__applyBranchFilter as applyBranchFilter, __webpack_exports__applyCompanyFilter as applyCompanyFilter, __webpack_exports__assertValidConditionGroup as assertValidConditionGroup, __webpack_exports__assertValidExpression as assertValidExpression, __webpack_exports__buildCompanyWhereCondition as buildCompanyWhereCondition, __webpack_exports__buildCookieOptions as buildCookieOptions, __webpack_exports__buildFrontendUrl as buildFrontendUrl, __webpack_exports__buildPermissionCacheKey as buildPermissionCacheKey, __webpack_exports__cacheTenantId as cacheTenantId, __webpack_exports__collectBranchAncestorIds as collectBranchAncestorIds, __webpack_exports__collectBranchDescendantIds as collectBranchDescendantIds, __webpack_exports__compileSafeRegex as compileSafeRegex, __webpack_exports__escapeHtml as escapeHtml, __webpack_exports__escapeHtmlVariables as escapeHtmlVariables, __webpack_exports__evaluateConditionGroup as evaluateConditionGroup, __webpack_exports__evaluateExpression as evaluateExpression, __webpack_exports__evaluatePermissionLogic as evaluatePermissionLogic, __webpack_exports__expressionToNumber as expressionToNumber, __webpack_exports__expressionToText as expressionToText, __webpack_exports__extractFrontendUrl as extractFrontendUrl, __webpack_exports__formatInTimezone as formatInTimezone, __webpack_exports__generateSecureToken as generateSecureToken, __webpack_exports__generateSlug as generateSlug, __webpack_exports__generateUniqueSlug as generateUniqueSlug, __webpack_exports__getBranchPath as getBranchPath, __webpack_exports__getTodayInTimezone as getTodayInTimezone, __webpack_exports__hasCompanyId as hasCompanyId, __webpack_exports__hashToken as hashToken, __webpack_exports__isBranchWithin as isBranchWithin, __webpack_exports__isBrowserRequest as isBrowserRequest, __webpack_exports__isExpired as isExpired, __webpack_exports__isExpressionError as isExpressionError, __webpack_exports__isMultiWildcard as isMultiWildcard, __webpack_exports__loadOptionalModule as loadOptionalModule, __webpack_exports__loadPermissionCodes as loadPermissionCodes, __webpack_exports__localToUtc as localToUtc, __webpack_exports__matchEventPattern as matchEventPattern, __webpack_exports__matchesPermission as matchesPermission, __webpack_exports__messageRef as messageRef, __webpack_exports__normalizeBranchIds as normalizeBranchIds, __webpack_exports__parseDurationToMs as parseDurationToMs, __webpack_exports__permissionLogicCodes as permissionLogicCodes, __webpack_exports__permissionLogicProblem as permissionLogicProblem, __webpack_exports__safeRegexProblem as safeRegexProblem, __webpack_exports__sanitizeEventPayload as sanitizeEventPayload, __webpack_exports__utcToLocal as utcToLocal, __webpack_exports__validateCompanyOwnership as validateCompanyOwnership, __webpack_exports__withSavepoint as withSavepoint };