@flusys/nestjs-shared 9.1.2 → 9.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +94 -18
- package/classes/api-controller.class.d.ts +1 -0
- package/classes/api-service.class.d.ts +7 -2
- package/classes/hybrid-cache.class.d.ts +17 -4
- package/constants/index.d.ts +1 -0
- package/constants/permissions.d.ts +9 -0
- package/fesm/183.js +7 -7
- package/fesm/{282.js → 47.js} +98 -158
- package/fesm/470.js +333 -382
- package/fesm/667.js +17 -17
- package/fesm/681.js +326 -129
- package/fesm/{948.js → 997.js} +45 -23
- package/fesm/chunks/123.js +17 -9
- package/fesm/chunks/37.js +5 -5
- package/fesm/classes/index.js +4 -3
- package/fesm/constants/index.js +18 -7
- package/fesm/decorators/index.js +120 -39
- package/fesm/dtos/index.js +1 -1
- package/fesm/entities/index.js +8 -8
- package/fesm/enums/index.js +2 -2
- package/fesm/exceptions/index.js +12 -12
- package/fesm/guards/index.js +201 -68
- package/fesm/index.js +50 -41
- package/fesm/interceptors/index.js +290 -590
- package/fesm/interfaces/index.js +17 -8
- package/fesm/middlewares/index.js +4 -4
- package/fesm/modules/index.js +2 -2
- package/fesm/pipes/index.js +8 -8
- package/fesm/utils/index.js +137 -478
- package/interceptors/idempotency.interceptor.d.ts +14 -2
- package/interceptors/slug.interceptor.d.ts +1 -0
- package/interfaces/permission-action-registry.interface.d.ts +1 -0
- package/interfaces/permission.interface.d.ts +1 -0
- package/modules/shared-permission-cache/shared-permission-cache.service.d.ts +3 -1
- package/modules/utils/utils.service.d.ts +6 -6
- package/package.json +2 -2
- package/utils/date-time.util.d.ts +0 -1
- package/utils/event-pattern.util.d.ts +1 -0
- package/utils/permission-cache-key.util.d.ts +3 -0
- package/utils/query-helpers.util.d.ts +2 -2
package/fesm/{282.js → 47.js}
RENAMED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { RE2JS } from "re2js";
|
|
2
|
-
export const __rspack_esm_id =
|
|
3
|
-
export const __rspack_esm_ids = [
|
|
2
|
+
export const __rspack_esm_id = 47;
|
|
3
|
+
export const __rspack_esm_ids = [47];
|
|
4
4
|
export const __webpack_modules__ = {
|
|
5
|
-
|
|
5
|
+
6079(__unused_rspack_module, __webpack_exports__, __webpack_require__) {
|
|
6
6
|
const EXPRESSION_ARITHMETIC_OPS = [
|
|
7
7
|
'sum',
|
|
8
8
|
'subtract',
|
|
@@ -69,7 +69,7 @@ __webpack_require__.d(__webpack_exports__, {
|
|
|
69
69
|
|
|
70
70
|
|
|
71
71
|
},
|
|
72
|
-
|
|
72
|
+
4578(__unused_rspack_module, __webpack_exports__, __webpack_require__) {
|
|
73
73
|
|
|
74
74
|
// EXPORTS
|
|
75
75
|
__webpack_require__.d(__webpack_exports__, {
|
|
@@ -103,17 +103,18 @@ __webpack_require__.d(__webpack_exports__, {
|
|
|
103
103
|
hX: () => (/* reexport */ extractFrontendUrl),
|
|
104
104
|
Sd: () => (/* reexport */ formatInTimezone),
|
|
105
105
|
sN: () => (/* reexport */ generateSecureToken),
|
|
106
|
-
z9: () => (/* reexport */ generateSlug),
|
|
107
|
-
oo: () => (/* reexport */ generateUniqueSlug),
|
|
106
|
+
z9: () => (/* reexport */ string_util/* .generateSlug */.z),
|
|
107
|
+
oo: () => (/* reexport */ string_util/* .generateUniqueSlug */.o),
|
|
108
108
|
gN: () => (/* reexport */ getBranchPath),
|
|
109
|
-
oB: () => (/* reexport */ getTodayInTimezone),
|
|
110
109
|
f: () => (/* reexport */ hasCompanyId),
|
|
111
110
|
QK: () => (/* reexport */ hashToken),
|
|
111
|
+
xp: () => (/* reexport */ permission_cache_key_util/* .invalidatePermissionCache */.xp),
|
|
112
112
|
QU: () => (/* reexport */ isBranchWithin),
|
|
113
113
|
wh: () => (/* reexport */ isBrowserRequest),
|
|
114
114
|
_d: () => (/* reexport */ isExpired),
|
|
115
115
|
tX: () => (/* reexport */ isExpressionError),
|
|
116
116
|
_c: () => (/* reexport */ event_pattern_util/* .isMultiWildcard */._c),
|
|
117
|
+
Gd: () => (/* reexport */ event_pattern_util/* .isSensitiveKey */.Gd),
|
|
117
118
|
lk: () => (/* reexport */ optional_module_util/* .loadOptionalModule */.l),
|
|
118
119
|
CQ: () => (/* reexport */ permission_match_util/* .loadPermissionCodes */.CQ),
|
|
119
120
|
vf: () => (/* reexport */ localToUtc),
|
|
@@ -124,6 +125,7 @@ __webpack_require__.d(__webpack_exports__, {
|
|
|
124
125
|
Ju: () => (/* reexport */ parseDurationToMs),
|
|
125
126
|
MO: () => (/* reexport */ permission_match_util/* .permissionLogicCodes */.MO),
|
|
126
127
|
HU: () => (/* reexport */ permission_match_util/* .permissionLogicProblem */.HU),
|
|
128
|
+
Rw: () => (/* reexport */ permission_cache_key_util/* .resolvePermissionCacheKey */.Rw),
|
|
127
129
|
_X: () => (/* reexport */ safeRegexProblem),
|
|
128
130
|
IN: () => (/* reexport */ event_pattern_util/* .sanitizeEventPayload */.IN),
|
|
129
131
|
v4: () => (/* reexport */ utcToLocal),
|
|
@@ -227,7 +229,7 @@ function childrenIndex(nodes) {
|
|
|
227
229
|
}
|
|
228
230
|
|
|
229
231
|
// EXTERNAL MODULE: ./projects/nestjs-shared/src/utils/cache-tenant.util.ts
|
|
230
|
-
var cache_tenant_util = __webpack_require__(
|
|
232
|
+
var cache_tenant_util = __webpack_require__(8154);
|
|
231
233
|
;// CONCATENATED MODULE: ./projects/nestjs-shared/src/utils/date-time.util.ts
|
|
232
234
|
function getOffsetMinutes(utcDate, timezone) {
|
|
233
235
|
const extract = (tz)=>{
|
|
@@ -296,14 +298,11 @@ function formatInTimezone(utcDate, timezone, options = {
|
|
|
296
298
|
timeZone: timezone
|
|
297
299
|
}).format(d);
|
|
298
300
|
}
|
|
299
|
-
function getTodayInTimezone(timezone) {
|
|
300
|
-
return utcToLocal(new Date(), timezone).date;
|
|
301
|
-
}
|
|
302
301
|
|
|
303
302
|
// EXTERNAL MODULE: ./projects/nestjs-shared/src/utils/event-pattern.util.ts
|
|
304
|
-
var event_pattern_util = __webpack_require__(
|
|
303
|
+
var event_pattern_util = __webpack_require__(8456);
|
|
305
304
|
// EXTERNAL MODULE: ./projects/nestjs-shared/src/interfaces/expression.interface.ts
|
|
306
|
-
var expression_interface = __webpack_require__(
|
|
305
|
+
var expression_interface = __webpack_require__(6079);
|
|
307
306
|
;// CONCATENATED MODULE: external "re2js"
|
|
308
307
|
|
|
309
308
|
;// CONCATENATED MODULE: ./projects/nestjs-shared/src/utils/safe-regex.util.ts
|
|
@@ -590,13 +589,12 @@ function addMonths(time, months) {
|
|
|
590
589
|
date.setUTCDate(Math.min(day, lastDay));
|
|
591
590
|
return date.getTime();
|
|
592
591
|
}
|
|
593
|
-
/** Whole calendar months from `from` to `to
|
|
592
|
+
/** Whole calendar months from `from` to `to`; negative when `to` is earlier, always `-monthsBetween(to, from)`. */ function monthsBetween(from, to) {
|
|
593
|
+
if (to < from) return -monthsBetween(to, from);
|
|
594
594
|
const a = new Date(from);
|
|
595
595
|
const b = new Date(to);
|
|
596
596
|
let months = (b.getUTCFullYear() - a.getUTCFullYear()) * 12 + (b.getUTCMonth() - a.getUTCMonth());
|
|
597
|
-
|
|
598
|
-
if (months > 0 && shifted > to) months--;
|
|
599
|
-
if (months < 0 && shifted < to) months++;
|
|
597
|
+
if (addMonths(from, months) > to) months--;
|
|
600
598
|
return months;
|
|
601
599
|
}
|
|
602
600
|
function dateDiff(a, b, unit) {
|
|
@@ -604,9 +602,8 @@ function dateDiff(a, b, unit) {
|
|
|
604
602
|
const from = toTime(b);
|
|
605
603
|
const resolved = toUnit(unit);
|
|
606
604
|
if (to === null || from === null) return null;
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
return Math.trunc((to - from) / FIXED_UNIT_MS[resolved]);
|
|
605
|
+
const diff = resolved === 'month' ? monthsBetween(from, to) : resolved === 'year' ? Math.trunc(monthsBetween(from, to) / 12) : Math.trunc((to - from) / FIXED_UNIT_MS[resolved]);
|
|
606
|
+
return diff + 0;
|
|
610
607
|
}
|
|
611
608
|
function addDuration(value, amount, unit) {
|
|
612
609
|
const time = toTime(value);
|
|
@@ -770,7 +767,7 @@ const EXPRESSION_FUNCTIONS = {
|
|
|
770
767
|
const items = Array.isArray(value) ? value : isBlank(value) ? [] : [
|
|
771
768
|
value
|
|
772
769
|
];
|
|
773
|
-
const sep =
|
|
770
|
+
const sep = separator === null || separator === undefined || separator === '' ? ', ' : expressionToText(separator);
|
|
774
771
|
return limitText(items.map((item)=>expressionToText(item) ?? '').filter(Boolean).join(sep));
|
|
775
772
|
}
|
|
776
773
|
},
|
|
@@ -928,7 +925,14 @@ function enter(depth, budget) {
|
|
|
928
925
|
max: EXPRESSION_LIMITS.MAX_NODES
|
|
929
926
|
});
|
|
930
927
|
}
|
|
931
|
-
function applyArithmetic(op, values) {
|
|
928
|
+
/** Overflow (`10 ** 400`) and undefined results (`(-8) ** 0.5`) are errors, never Infinity / NaN. */ function applyArithmetic(op, values) {
|
|
929
|
+
const result = arithmeticResult(op, values);
|
|
930
|
+
if (result !== null && !Number.isFinite(result)) throw new ExpressionError('expected_number', {
|
|
931
|
+
value: String(result)
|
|
932
|
+
});
|
|
933
|
+
return result;
|
|
934
|
+
}
|
|
935
|
+
function arithmeticResult(op, values) {
|
|
932
936
|
if (values.length === 0) return null;
|
|
933
937
|
const [first, ...rest] = values;
|
|
934
938
|
switch(op){
|
|
@@ -1222,27 +1226,26 @@ function messageRef(messageKey, messageVariables) {
|
|
|
1222
1226
|
}
|
|
1223
1227
|
|
|
1224
1228
|
// EXTERNAL MODULE: ./projects/nestjs-shared/src/utils/optional-module.util.ts
|
|
1225
|
-
var optional_module_util = __webpack_require__(
|
|
1229
|
+
var optional_module_util = __webpack_require__(8368);
|
|
1226
1230
|
// EXTERNAL MODULE: ./projects/nestjs-shared/src/utils/permission-cache-key.util.ts
|
|
1227
|
-
var permission_cache_key_util = __webpack_require__(
|
|
1231
|
+
var permission_cache_key_util = __webpack_require__(3199);
|
|
1228
1232
|
// EXTERNAL MODULE: ./projects/nestjs-shared/src/utils/permission-match.util.ts
|
|
1229
1233
|
var permission_match_util = __webpack_require__(788);
|
|
1230
1234
|
// EXTERNAL MODULE: external "@nestjs/common"
|
|
1231
|
-
var common_ = __webpack_require__(
|
|
1235
|
+
var common_ = __webpack_require__(6157);
|
|
1236
|
+
// EXTERNAL MODULE: external "typeorm"
|
|
1237
|
+
var external_typeorm_ = __webpack_require__(7158);
|
|
1232
1238
|
// EXTERNAL MODULE: ./projects/nestjs-shared/src/constants/index.ts
|
|
1233
|
-
var constants = __webpack_require__(
|
|
1239
|
+
var constants = __webpack_require__(6043);
|
|
1234
1240
|
;// CONCATENATED MODULE: ./projects/nestjs-shared/src/utils/query-helpers.util.ts
|
|
1235
1241
|
|
|
1236
1242
|
|
|
1237
1243
|
|
|
1244
|
+
|
|
1238
1245
|
/**
|
|
1239
|
-
*
|
|
1240
|
-
*
|
|
1241
|
-
*
|
|
1242
|
-
* @param query - TypeORM SelectQueryBuilder
|
|
1243
|
-
* @param config - Company filter configuration
|
|
1244
|
-
* @param user - Current logged user (may be null)
|
|
1245
|
-
* @returns The modified query builder
|
|
1246
|
+
* Restricts a query to the user's company when the company feature is on. A logged-in user without a
|
|
1247
|
+
* company matches nothing (never every company's rows); no user at all is a system call and is left
|
|
1248
|
+
* unfiltered.
|
|
1246
1249
|
*
|
|
1247
1250
|
* @example
|
|
1248
1251
|
* ```typescript
|
|
@@ -1252,13 +1255,11 @@ var constants = __webpack_require__(43);
|
|
|
1252
1255
|
* }, user);
|
|
1253
1256
|
* ```
|
|
1254
1257
|
*/ function applyCompanyFilter(query, config, user) {
|
|
1255
|
-
|
|
1256
|
-
if (
|
|
1257
|
-
|
|
1258
|
-
|
|
1259
|
-
|
|
1260
|
-
}
|
|
1261
|
-
return query;
|
|
1258
|
+
if (!config.isCompanyFeatureEnabled || !user) return query;
|
|
1259
|
+
if (!user.companyId) return query.andWhere('1 = 0');
|
|
1260
|
+
return query.andWhere(`${config.entityAlias}.${config.columnName ?? 'companyId'} = :companyId`, {
|
|
1261
|
+
companyId: user.companyId
|
|
1262
|
+
});
|
|
1262
1263
|
}
|
|
1263
1264
|
/**
|
|
1264
1265
|
* Restrict a query to rows of the given branches - typically a branch plus the granted branches under
|
|
@@ -1274,30 +1275,19 @@ var constants = __webpack_require__(43);
|
|
|
1274
1275
|
});
|
|
1275
1276
|
}
|
|
1276
1277
|
/**
|
|
1277
|
-
*
|
|
1278
|
-
*
|
|
1279
|
-
* @param baseWhere - Base where condition object
|
|
1280
|
-
* @param isCompanyFeatureEnabled - Whether company feature is enabled
|
|
1281
|
-
* @param user - Current logged user
|
|
1282
|
-
* @returns Where condition with companyId added if applicable
|
|
1278
|
+
* Adds the user's company to a find `where` when the company feature is on - `In([])` (matches
|
|
1279
|
+
* nothing) for a logged-in user without a company; no user at all is a system call and is unchanged.
|
|
1283
1280
|
*
|
|
1284
1281
|
* @example
|
|
1285
1282
|
* ```typescript
|
|
1286
|
-
* const where = buildCompanyWhereCondition(
|
|
1287
|
-
* { id: dto.id },
|
|
1288
|
-
* this.config.isCompanyFeatureEnabled(),
|
|
1289
|
-
* user
|
|
1290
|
-
* );
|
|
1291
|
-
* // Returns { id: dto.id, companyId: user.companyId } if company feature enabled
|
|
1283
|
+
* const where = buildCompanyWhereCondition({ id: dto.id }, this.config.isCompanyFeatureEnabled(), user);
|
|
1292
1284
|
* ```
|
|
1293
1285
|
*/ function buildCompanyWhereCondition(baseWhere, isCompanyFeatureEnabled, user) {
|
|
1294
|
-
if (isCompanyFeatureEnabled
|
|
1295
|
-
|
|
1296
|
-
|
|
1297
|
-
|
|
1298
|
-
|
|
1299
|
-
}
|
|
1300
|
-
return baseWhere;
|
|
1286
|
+
if (!isCompanyFeatureEnabled || !user) return baseWhere;
|
|
1287
|
+
return {
|
|
1288
|
+
...baseWhere,
|
|
1289
|
+
companyId: user.companyId ? user.companyId : (0,external_typeorm_.In)([])
|
|
1290
|
+
};
|
|
1301
1291
|
}
|
|
1302
1292
|
/**
|
|
1303
1293
|
* Type guard to check if entity has companyId property
|
|
@@ -1305,30 +1295,23 @@ var constants = __webpack_require__(43);
|
|
|
1305
1295
|
return entity !== null && typeof entity === 'object' && 'companyId' in entity;
|
|
1306
1296
|
}
|
|
1307
1297
|
/**
|
|
1308
|
-
*
|
|
1309
|
-
*
|
|
1310
|
-
*
|
|
1311
|
-
* @param entity - Entity to validate
|
|
1312
|
-
* @param user - Current logged user
|
|
1313
|
-
* @param isCompanyFeatureEnabled - Whether company feature is enabled
|
|
1314
|
-
* @param entityName - Name of entity for error message (e.g., 'Email configuration')
|
|
1315
|
-
* @throws BadRequestException if entity belongs to another company
|
|
1298
|
+
* Throws when the company feature is on and `entity` belongs to a company other than the user's - a
|
|
1299
|
+
* logged-in user without a company may only use company-less rows. No user at all is a system call.
|
|
1316
1300
|
*
|
|
1317
1301
|
* @example
|
|
1318
1302
|
* ```typescript
|
|
1319
1303
|
* validateCompanyOwnership(config, user, this.config.isCompanyFeatureEnabled(), 'Email configuration');
|
|
1320
1304
|
* ```
|
|
1321
1305
|
*/ function validateCompanyOwnership(entity, user, isCompanyFeatureEnabled, entityName) {
|
|
1322
|
-
if (isCompanyFeatureEnabled
|
|
1323
|
-
|
|
1324
|
-
|
|
1325
|
-
|
|
1326
|
-
|
|
1327
|
-
|
|
1328
|
-
|
|
1329
|
-
|
|
1330
|
-
|
|
1331
|
-
}
|
|
1306
|
+
if (!isCompanyFeatureEnabled || !user || !hasCompanyId(entity)) return;
|
|
1307
|
+
if (entity.companyId && entity.companyId !== user.companyId) {
|
|
1308
|
+
throw new common_.BadRequestException({
|
|
1309
|
+
message: `${entityName} belongs to another company`,
|
|
1310
|
+
messageKey: constants/* .AUTH_MESSAGES.ENTITY_BELOG_ANOTHER_COMPANY */.mK.ENTITY_BELOG_ANOTHER_COMPANY,
|
|
1311
|
+
messageVariables: {
|
|
1312
|
+
entity: entityName
|
|
1313
|
+
}
|
|
1314
|
+
});
|
|
1332
1315
|
}
|
|
1333
1316
|
}
|
|
1334
1317
|
/**
|
|
@@ -1348,7 +1331,7 @@ var constants = __webpack_require__(43);
|
|
|
1348
1331
|
}
|
|
1349
1332
|
|
|
1350
1333
|
// EXTERNAL MODULE: external "@flusys/nestjs-core/config"
|
|
1351
|
-
var config_ = __webpack_require__(
|
|
1334
|
+
var config_ = __webpack_require__(8307);
|
|
1352
1335
|
;// CONCATENATED MODULE: ./projects/nestjs-shared/src/utils/request.util.ts
|
|
1353
1336
|
|
|
1354
1337
|
|
|
@@ -1360,6 +1343,38 @@ var config_ = __webpack_require__(307);
|
|
|
1360
1343
|
d: 24 * 60 * 60 * 1000,
|
|
1361
1344
|
w: 7 * 24 * 60 * 60 * 1000
|
|
1362
1345
|
};
|
|
1346
|
+
const IPV4_HOST = /^\d{1,3}(\.\d{1,3}){3}$/;
|
|
1347
|
+
/** Second-level labels used under two-letter country codes (`co.uk`, `com.au`, `ac.jp`, ...). */ const COUNTRY_SECOND_LEVEL = new Set([
|
|
1348
|
+
'co',
|
|
1349
|
+
'com',
|
|
1350
|
+
'net',
|
|
1351
|
+
'org',
|
|
1352
|
+
'gov',
|
|
1353
|
+
'edu',
|
|
1354
|
+
'ac',
|
|
1355
|
+
'or',
|
|
1356
|
+
'ne',
|
|
1357
|
+
'go',
|
|
1358
|
+
'ltd',
|
|
1359
|
+
'plc',
|
|
1360
|
+
'mil',
|
|
1361
|
+
'nic',
|
|
1362
|
+
'sch',
|
|
1363
|
+
'gen'
|
|
1364
|
+
]);
|
|
1365
|
+
/**
|
|
1366
|
+
* The registrable domain to share the auth cookies across subdomains (`app.example.com` -> `.example.com`,
|
|
1367
|
+
* `app.example.co.uk` -> `.example.co.uk`), or undefined for a host-only cookie: IP addresses, localhost and
|
|
1368
|
+
* hosts that already are the registrable domain - a cookie scoped to `.1.10` or `.co.uk` is rejected by browsers.
|
|
1369
|
+
*/ function sharedCookieDomain(hostname) {
|
|
1370
|
+
const host = hostname.toLowerCase().replace(/\.$/, '');
|
|
1371
|
+
if (!host || host.includes(':') || IPV4_HOST.test(host) || host.includes('localhost')) return undefined;
|
|
1372
|
+
const labels = host.split('.');
|
|
1373
|
+
const last = labels[labels.length - 1];
|
|
1374
|
+
const secondLast = labels[labels.length - 2];
|
|
1375
|
+
const keep = labels.length >= 3 && last.length === 2 && COUNTRY_SECOND_LEVEL.has(secondLast) ? 3 : 2;
|
|
1376
|
+
return labels.length > keep ? '.' + labels.slice(-keep).join('.') : undefined;
|
|
1377
|
+
}
|
|
1363
1378
|
/** Get normalized client type from request header */ function getClientType(req) {
|
|
1364
1379
|
const clientType = req.headers[constants/* .CLIENT_TYPE_HEADER */.xw];
|
|
1365
1380
|
return clientType ? clientType.toLowerCase() : null;
|
|
@@ -1387,11 +1402,7 @@ var config_ = __webpack_require__(307);
|
|
|
1387
1402
|
const isProduction = config_.envConfig.isProduction();
|
|
1388
1403
|
const forwardedProto = req.headers['x-forwarded-proto'];
|
|
1389
1404
|
const isHttps = isProduction || forwardedProto === 'https' || origin.startsWith('https://') || req.secure;
|
|
1390
|
-
|
|
1391
|
-
const domainParts = hostname.split('.');
|
|
1392
|
-
if (domainParts.length > 2 && !hostname.includes('localhost')) {
|
|
1393
|
-
domain = '.' + domainParts.slice(-2).join('.');
|
|
1394
|
-
}
|
|
1405
|
+
const domain = sharedCookieDomain(hostname);
|
|
1395
1406
|
return {
|
|
1396
1407
|
secure: isHttps,
|
|
1397
1408
|
sameSite: isHttps ? 'strict' : 'lax',
|
|
@@ -1437,81 +1448,10 @@ function buildFrontendUrl(req, path) {
|
|
|
1437
1448
|
return `${baseUrl}${path}`;
|
|
1438
1449
|
}
|
|
1439
1450
|
|
|
1440
|
-
|
|
1441
|
-
|
|
1442
|
-
* String utility functions
|
|
1443
|
-
*/ /**
|
|
1444
|
-
* Generate URL-friendly slug from a string
|
|
1445
|
-
* @param text - The text to convert to slug
|
|
1446
|
-
* @param maxLength - Maximum length of the slug (default: 100)
|
|
1447
|
-
* @returns URL-friendly slug
|
|
1448
|
-
*
|
|
1449
|
-
* @example
|
|
1450
|
-
* generateSlug('My Company Name') // 'my-company-name'
|
|
1451
|
-
* generateSlug('Hello World!') // 'hello-world'
|
|
1452
|
-
*/ function generateSlug(text, maxLength = 100) {
|
|
1453
|
-
return text.toLowerCase().trim().replace(/[^a-z0-9\s-]/g, '') // Remove special characters
|
|
1454
|
-
.replace(/\s+/g, '-') // Replace spaces with hyphens
|
|
1455
|
-
.replace(/-+/g, '-') // Replace multiple hyphens with single
|
|
1456
|
-
.replace(/^-+|-+$/g, '') // Remove leading/trailing hyphens
|
|
1457
|
-
.substring(0, maxLength);
|
|
1458
|
-
}
|
|
1459
|
-
/**
|
|
1460
|
-
* Generate unique slug using single database query (optimized for large datasets)
|
|
1461
|
-
* Appends numeric suffix if collision exists (e.g., 'my-company-1', 'my-company-2')
|
|
1462
|
-
*
|
|
1463
|
-
* Performance: O(1) database query regardless of collision count
|
|
1464
|
-
* - Works efficiently with millions of records
|
|
1465
|
-
* - Requires index on slug column for best performance
|
|
1466
|
-
*
|
|
1467
|
-
* @param text - The text to convert to slug
|
|
1468
|
-
* @param findMatchingSlugs - Async function that returns all slugs matching base pattern
|
|
1469
|
-
* Should query: WHERE slug = baseSlug OR slug LIKE 'baseSlug-%'
|
|
1470
|
-
* @param maxLength - Maximum length of the base slug (default: 100)
|
|
1471
|
-
* @returns Unique URL-friendly slug
|
|
1472
|
-
*
|
|
1473
|
-
* @example
|
|
1474
|
-
* const slug = await generateUniqueSlug('My Company', async (baseSlug) => {
|
|
1475
|
-
* return repo
|
|
1476
|
-
* .createQueryBuilder('c')
|
|
1477
|
-
* .select('c.slug')
|
|
1478
|
-
* .where('c.slug = :base OR c.slug LIKE :pattern', {
|
|
1479
|
-
* base: baseSlug,
|
|
1480
|
-
* pattern: `${baseSlug}-%`,
|
|
1481
|
-
* })
|
|
1482
|
-
* .getMany()
|
|
1483
|
-
* .then(rows => rows.map(r => r.slug));
|
|
1484
|
-
* });
|
|
1485
|
-
*/ async function generateUniqueSlug(text, findMatchingSlugs, maxLength = 100) {
|
|
1486
|
-
const baseSlug = generateSlug(text, maxLength);
|
|
1487
|
-
// Single query to get all matching slugs
|
|
1488
|
-
const existingSlugs = await findMatchingSlugs(baseSlug);
|
|
1489
|
-
// No collisions - base slug is available
|
|
1490
|
-
if (existingSlugs.length === 0) {
|
|
1491
|
-
return baseSlug;
|
|
1492
|
-
}
|
|
1493
|
-
// Build set for O(1) lookup
|
|
1494
|
-
const existingSet = new Set(existingSlugs);
|
|
1495
|
-
// Base slug not taken (only suffixed versions exist)
|
|
1496
|
-
if (!existingSet.has(baseSlug)) {
|
|
1497
|
-
return baseSlug;
|
|
1498
|
-
}
|
|
1499
|
-
// Find next available suffix in memory (fast)
|
|
1500
|
-
let counter = 1;
|
|
1501
|
-
while(counter < 10000){
|
|
1502
|
-
const slugWithSuffix = `${baseSlug}-${counter}`;
|
|
1503
|
-
if (!existingSet.has(slugWithSuffix)) {
|
|
1504
|
-
return slugWithSuffix;
|
|
1505
|
-
}
|
|
1506
|
-
counter++;
|
|
1507
|
-
}
|
|
1508
|
-
// Fallback: append random string (extremely rare edge case)
|
|
1509
|
-
const randomSuffix = Math.random().toString(36).substring(2, 8);
|
|
1510
|
-
return `${baseSlug}-${randomSuffix}`;
|
|
1511
|
-
}
|
|
1512
|
-
|
|
1451
|
+
// EXTERNAL MODULE: ./projects/nestjs-shared/src/utils/string.util.ts
|
|
1452
|
+
var string_util = __webpack_require__(3366);
|
|
1513
1453
|
// EXTERNAL MODULE: external "crypto"
|
|
1514
|
-
var external_crypto_ = __webpack_require__(
|
|
1454
|
+
var external_crypto_ = __webpack_require__(7823);
|
|
1515
1455
|
;// CONCATENATED MODULE: ./projects/nestjs-shared/src/utils/token.util.ts
|
|
1516
1456
|
|
|
1517
1457
|
/** 32 random bytes as hex, suitable for a password-reset/OTP/access-style token */ function generateSecureToken() {
|
|
@@ -1543,7 +1483,7 @@ function isExpired(expiresAt) {
|
|
|
1543
1483
|
|
|
1544
1484
|
|
|
1545
1485
|
},
|
|
1546
|
-
|
|
1486
|
+
8368(__unused_rspack_module, __webpack_exports__, __webpack_require__) {
|
|
1547
1487
|
/**
|
|
1548
1488
|
* Loads an optional peer dependency at runtime.
|
|
1549
1489
|
*
|