@plyaz/core 1.16.0 → 1.17.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.
@@ -0,0 +1,1143 @@
1
+ 'use strict';
2
+
3
+ var config = require('@plyaz/config');
4
+ var types = require('@plyaz/types');
5
+ var errors = require('@plyaz/errors');
6
+ var logger$1 = require('@plyaz/logger');
7
+ var core = require('@plyaz/types/core');
8
+
9
+ // @plyaz package - Built with tsup
10
+ var __defProp = Object.defineProperty;
11
+ var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
12
+ function hashString(str) {
13
+ if (str.length === 0) return 0;
14
+ let hash = config.FNV_CONSTANTS.FNV_32_OFFSET;
15
+ for (let i = 0; i < str.length; i++) {
16
+ hash ^= str.charCodeAt(i);
17
+ hash = Math.imul(hash, config.FNV_CONSTANTS.FNV_32_PRIME) >>> 0;
18
+ }
19
+ return hash >>> 0;
20
+ }
21
+ __name(hashString, "hashString");
22
+ function isInRollout(identifier, percentage) {
23
+ if (percentage >= config.MATH_CONSTANTS.PERCENTAGE_MAX) return true;
24
+ if (percentage <= 0) return false;
25
+ const hash = hashString(identifier);
26
+ return hash % config.MATH_CONSTANTS.PERCENTAGE_MAX < percentage;
27
+ }
28
+ __name(isInRollout, "isInRollout");
29
+ function createRolloutIdentifier(featureKey, userId) {
30
+ const trimmedUserId = userId?.trim();
31
+ const effectiveUserId = trimmedUserId && trimmedUserId.length > 0 ? trimmedUserId : "anonymous";
32
+ return `${featureKey}:${effectiveUserId}`;
33
+ }
34
+ __name(createRolloutIdentifier, "createRolloutIdentifier");
35
+ var HashUtils = {
36
+ /**
37
+ * Generates a hash-based bucket for load balancing or distribution.
38
+ *
39
+ * @param identifier - Unique identifier
40
+ * @param bucketCount - Number of buckets (default: 10)
41
+ * @returns Bucket number (0 to bucketCount-1)
42
+ */
43
+ getBucket: /* @__PURE__ */ __name((identifier, bucketCount = 10) => {
44
+ return hashString(identifier) % bucketCount;
45
+ }, "getBucket"),
46
+ /**
47
+ * Checks if an identifier falls within a specific bucket range.
48
+ *
49
+ * @param identifier - Unique identifier
50
+ * @param startBucket - Starting bucket (inclusive)
51
+ * @param endBucket - Ending bucket (inclusive)
52
+ * @param totalBuckets - Total number of buckets (default: 100)
53
+ * @returns true if identifier is in the bucket range
54
+ */
55
+ isInBucketRange: /* @__PURE__ */ __name((identifier, startBucket, endBucket, totalBuckets = config.MATH_CONSTANTS.PERCENTAGE_MAX) => {
56
+ const bucket = hashString(identifier) % totalBuckets;
57
+ return bucket >= startBucket && bucket <= endBucket;
58
+ }, "isInBucketRange"),
59
+ /**
60
+ * Creates a deterministic random seed from a string.
61
+ * Uses the improved hash function and ensures the seed is within
62
+ * the safe range for JavaScript's Math.random seeding.
63
+ *
64
+ * @param str - String to convert to seed
65
+ * @returns Deterministic seed value (0 to 2^31-1)
66
+ */
67
+ createSeed: /* @__PURE__ */ __name((str) => {
68
+ return hashString(str) % config.HASH_SEED_CONSTANTS.MAX_SAFE_SEED;
69
+ }, "createSeed")
70
+ };
71
+
72
+ // src/utils/common/id.ts
73
+ var RANDOM_ID_RADIX = 36;
74
+ var RANDOM_ID_SLICE_START = 2;
75
+ var SHORT_ID_LENGTH = 8;
76
+ var HEX_RADIX = 16;
77
+ var HEX_SLICE_START = 2;
78
+ var HEX_SLICE_END = 18;
79
+ var SPAN_ID_LENGTH = 16;
80
+ function generateId() {
81
+ const cryptoApi = globalThis.crypto;
82
+ if (cryptoApi?.randomUUID) {
83
+ return cryptoApi.randomUUID();
84
+ }
85
+ return `${Date.now()}-${Math.random().toString(RANDOM_ID_RADIX).slice(RANDOM_ID_SLICE_START)}`;
86
+ }
87
+ __name(generateId, "generateId");
88
+ function generateShortId() {
89
+ const cryptoApi = globalThis.crypto;
90
+ if (cryptoApi?.randomUUID) {
91
+ return cryptoApi.randomUUID().replace(/-/g, "").slice(0, SHORT_ID_LENGTH);
92
+ }
93
+ return Math.random().toString(RANDOM_ID_RADIX).slice(RANDOM_ID_SLICE_START, RANDOM_ID_SLICE_START + SHORT_ID_LENGTH);
94
+ }
95
+ __name(generateShortId, "generateShortId");
96
+ function generateCorrelationId() {
97
+ return `${Date.now()}-${generateShortId()}`;
98
+ }
99
+ __name(generateCorrelationId, "generateCorrelationId");
100
+ function generateTraceId() {
101
+ const cryptoApi = globalThis.crypto;
102
+ if (cryptoApi?.randomUUID) {
103
+ return cryptoApi.randomUUID().replace(/-/g, "");
104
+ }
105
+ return Math.random().toString(HEX_RADIX).substring(HEX_SLICE_START, HEX_SLICE_END) + Math.random().toString(HEX_RADIX).substring(HEX_SLICE_START, HEX_SLICE_END);
106
+ }
107
+ __name(generateTraceId, "generateTraceId");
108
+ function generateSpanId() {
109
+ const cryptoApi = globalThis.crypto;
110
+ if (cryptoApi?.randomUUID) {
111
+ return cryptoApi.randomUUID().replace(/-/g, "").substring(0, SPAN_ID_LENGTH);
112
+ }
113
+ return Math.random().toString(HEX_RADIX).substring(HEX_SLICE_START, HEX_SLICE_END);
114
+ }
115
+ __name(generateSpanId, "generateSpanId");
116
+ var UUID_REGEX = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
117
+ function isValidUUID(value) {
118
+ if (!value) return false;
119
+ return UUID_REGEX.test(value);
120
+ }
121
+ __name(isValidUUID, "isValidUUID");
122
+ var IdUtils = {
123
+ generate: generateId,
124
+ generateShort: generateShortId,
125
+ generateCorrelation: generateCorrelationId,
126
+ generateTraceId,
127
+ generateSpanId,
128
+ isValidUUID
129
+ };
130
+ function isStringFalsy(value) {
131
+ if (value === "") return true;
132
+ const lower = value.toLowerCase().trim();
133
+ return ["false", "no", "0", "off", "disabled"].includes(lower);
134
+ }
135
+ __name(isStringFalsy, "isStringFalsy");
136
+ function isObjectTruthy(value) {
137
+ if (Array.isArray(value)) return value.length > 0;
138
+ if (value instanceof Map || value instanceof Set) return value.size > 0;
139
+ if (value.constructor === Object) return Object.keys(value).length > 0;
140
+ return true;
141
+ }
142
+ __name(isObjectTruthy, "isObjectTruthy");
143
+ function isTruthy(value) {
144
+ if (value === null || value === void 0) return false;
145
+ switch (typeof value) {
146
+ case "boolean":
147
+ return value;
148
+ case "string":
149
+ return !isStringFalsy(value);
150
+ case "number":
151
+ return value !== 0 && !isNaN(value);
152
+ case "function":
153
+ case "symbol":
154
+ return true;
155
+ case "object":
156
+ return isObjectTruthy(value);
157
+ default:
158
+ return false;
159
+ }
160
+ }
161
+ __name(isTruthy, "isTruthy");
162
+ function parseStringToBoolean(value, defaultValue) {
163
+ const lower = value.toLowerCase();
164
+ if (["true", "yes", "1", "on", "enabled"].includes(lower)) return true;
165
+ if (["false", "no", "0", "off", "disabled"].includes(lower)) return false;
166
+ return defaultValue;
167
+ }
168
+ __name(parseStringToBoolean, "parseStringToBoolean");
169
+ function toBoolean(value, defaultValue = false) {
170
+ if (value === null || value === void 0) return false;
171
+ switch (typeof value) {
172
+ case "boolean":
173
+ return value;
174
+ case "string":
175
+ return parseStringToBoolean(value, defaultValue);
176
+ case "number":
177
+ return value !== 0 && !isNaN(value);
178
+ case "object":
179
+ case "function":
180
+ return true;
181
+ default:
182
+ return defaultValue;
183
+ }
184
+ }
185
+ __name(toBoolean, "toBoolean");
186
+ function toNumber(value, defaultValue = 0) {
187
+ if (value === null || value === void 0) return defaultValue;
188
+ if (typeof value === "bigint") return Number(value);
189
+ if (typeof value === "number") return isNaN(value) ? defaultValue : value;
190
+ if (typeof value === "string") {
191
+ const parsed = Number.parseFloat(value);
192
+ return isNaN(parsed) ? defaultValue : parsed;
193
+ }
194
+ return defaultValue;
195
+ }
196
+ __name(toNumber, "toNumber");
197
+ var ValueUtils = {
198
+ /**
199
+ * Checks if a value is a valid percentage (0-100).
200
+ *
201
+ * @param value - Value to check
202
+ * @returns true if valid percentage
203
+ */
204
+ isValidPercentage: /* @__PURE__ */ __name((value) => {
205
+ if (typeof value !== "number") return false;
206
+ return !isNaN(value) && isFinite(value) && value >= 0 && value <= config.MATH_CONSTANTS.PERCENTAGE_MAX;
207
+ }, "isValidPercentage"),
208
+ /**
209
+ * Clamps a number to a specific range.
210
+ *
211
+ * @param value - Value to clamp
212
+ * @param min - Minimum value
213
+ * @param max - Maximum value
214
+ * @returns Clamped value
215
+ */
216
+ clamp: /* @__PURE__ */ __name((value, min, max) => {
217
+ return Math.min(Math.max(value, min), max);
218
+ }, "clamp"),
219
+ /**
220
+ * Checks if a value is empty (null, undefined, empty string, empty array).
221
+ *
222
+ * @param value - Value to check
223
+ * @returns true if empty
224
+ */
225
+ isEmpty: /* @__PURE__ */ __name((value) => {
226
+ if (value === null || value === void 0) return true;
227
+ if (typeof value === "string") return value.trim() === "";
228
+ if (Array.isArray(value)) return value.length === 0;
229
+ if (typeof value === "object") return Object.keys(value).length === 0;
230
+ return false;
231
+ }, "isEmpty"),
232
+ /**
233
+ * Safely gets a nested property from an object.
234
+ *
235
+ * @param obj - Object to query
236
+ * @param path - Dot-separated path (e.g., 'user.profile.name')
237
+ * @param defaultValue - Default if path doesn't exist
238
+ * @returns Property value or default
239
+ */
240
+ getNestedProperty: /* @__PURE__ */ __name((obj, path, defaultValue) => {
241
+ if (!obj || typeof obj !== "object") return defaultValue;
242
+ const keys = path.split(".");
243
+ let current = obj;
244
+ for (const key of keys) {
245
+ if (current == null || typeof current !== "object") return defaultValue;
246
+ current = current[key];
247
+ if (current === void 0) return defaultValue;
248
+ }
249
+ return current;
250
+ }, "getNestedProperty")
251
+ };
252
+
253
+ // src/utils/common/object.ts
254
+ function setNestedProperty(obj, path, value) {
255
+ const keys = path.split(".");
256
+ let current = obj;
257
+ for (let i = 0; i < keys.length - 1; i++) {
258
+ const key = keys[i];
259
+ if (!(key in current) || typeof current[key] !== "object" || current[key] === null) {
260
+ current[key] = {};
261
+ }
262
+ current = current[key];
263
+ }
264
+ const finalKey = keys[keys.length - 1];
265
+ current[finalKey] = value;
266
+ }
267
+ __name(setNestedProperty, "setNestedProperty");
268
+ function getNestedProperty(obj, path, defaultValue) {
269
+ const keys = path.split(".");
270
+ let current = obj;
271
+ for (const key of keys) {
272
+ if (current === null || current === void 0 || typeof current !== "object") {
273
+ return defaultValue;
274
+ }
275
+ current = current[key];
276
+ }
277
+ return current !== void 0 ? current : defaultValue;
278
+ }
279
+ __name(getNestedProperty, "getNestedProperty");
280
+ function hasNestedProperty(obj, path) {
281
+ const keys = path.split(".");
282
+ let current = obj;
283
+ for (const key of keys) {
284
+ if (current === null || current === void 0 || typeof current !== "object") {
285
+ return false;
286
+ }
287
+ if (!(key in current)) {
288
+ return false;
289
+ }
290
+ current = current[key];
291
+ }
292
+ return current !== void 0;
293
+ }
294
+ __name(hasNestedProperty, "hasNestedProperty");
295
+ var ObjectUtils = {
296
+ setNestedProperty,
297
+ getNestedProperty,
298
+ hasNestedProperty
299
+ };
300
+
301
+ // src/utils/common/typeGuards.ts
302
+ function isObject(value) {
303
+ return typeof value === "object" && value !== null;
304
+ }
305
+ __name(isObject, "isObject");
306
+ function hasProperty(obj, key) {
307
+ return isObject(obj) && key in obj;
308
+ }
309
+ __name(hasProperty, "hasProperty");
310
+ var TypeGuardUtils = {
311
+ isObject,
312
+ hasProperty
313
+ };
314
+ var isString = /* @__PURE__ */ __name((value) => typeof value === "string", "isString");
315
+ var isDefined = /* @__PURE__ */ __name((value) => value !== void 0, "isDefined");
316
+ var isNumber = /* @__PURE__ */ __name((value) => typeof value === "number", "isNumber");
317
+ var getValidProviders = /* @__PURE__ */ __name(() => Object.values(types.FEATURE_FLAG_PROVIDERS), "getValidProviders");
318
+ var FeatureFlagContextBuilder = class _FeatureFlagContextBuilder {
319
+ constructor() {
320
+ this.context = {};
321
+ }
322
+ static {
323
+ __name(this, "FeatureFlagContextBuilder");
324
+ }
325
+ /**
326
+ * Sets the user ID in the context.
327
+ *
328
+ * @param userId - User identifier
329
+ * @returns Builder instance for chaining
330
+ */
331
+ setUserId(userId) {
332
+ this.context.userId = userId;
333
+ return this;
334
+ }
335
+ /**
336
+ * Sets the user email in the context.
337
+ *
338
+ * @param userEmail - User email address
339
+ * @returns Builder instance for chaining
340
+ */
341
+ setUserEmail(userEmail) {
342
+ this.context.userEmail = userEmail;
343
+ return this;
344
+ }
345
+ /**
346
+ * Sets the user role in the context.
347
+ *
348
+ * @param userRole - User role or permission level
349
+ * @returns Builder instance for chaining
350
+ */
351
+ setUserRole(userRole) {
352
+ this.context.userRole = userRole;
353
+ return this;
354
+ }
355
+ /**
356
+ * Sets the country in the context.
357
+ *
358
+ * @param country - Country code (ISO 3166-1 alpha-2)
359
+ * @returns Builder instance for chaining
360
+ */
361
+ setCountry(country) {
362
+ this.context.country = country;
363
+ return this;
364
+ }
365
+ /**
366
+ * Sets the platform in the context.
367
+ *
368
+ * @param platform - Platform type
369
+ * @returns Builder instance for chaining
370
+ */
371
+ setPlatform(platform) {
372
+ this.context.platform = platform;
373
+ return this;
374
+ }
375
+ /**
376
+ * Sets the version in the context.
377
+ *
378
+ * @param version - Application version
379
+ * @returns Builder instance for chaining
380
+ */
381
+ setVersion(version) {
382
+ this.context.version = version;
383
+ return this;
384
+ }
385
+ /**
386
+ * Sets the environment in the context.
387
+ *
388
+ * @param environment - Current environment
389
+ * @returns Builder instance for chaining
390
+ */
391
+ setEnvironment(environment) {
392
+ this.context.environment = environment;
393
+ return this;
394
+ }
395
+ /**
396
+ * Sets custom context data.
397
+ *
398
+ * @param custom - Custom context properties
399
+ * @returns Builder instance for chaining
400
+ */
401
+ setCustom(custom) {
402
+ this.context.custom = { ...this.context.custom, ...custom };
403
+ return this;
404
+ }
405
+ /**
406
+ * Adds a single custom property to the context.
407
+ *
408
+ * @param key - Custom property key
409
+ * @param value - Custom property value
410
+ * @returns Builder instance for chaining
411
+ */
412
+ addCustomProperty(key, value) {
413
+ this.context.custom ??= {};
414
+ this.context.custom[key] = value;
415
+ return this;
416
+ }
417
+ /**
418
+ * Builds the final context object.
419
+ * Validates required fields and returns the context.
420
+ *
421
+ * @returns Complete feature flag context
422
+ * @throws Error if required environment is not set
423
+ */
424
+ build() {
425
+ return {
426
+ environment: this.context.environment ?? "development",
427
+ userId: this.context.userId,
428
+ userEmail: this.context.userEmail,
429
+ userRole: this.context.userRole,
430
+ country: this.context.country,
431
+ platform: this.context.platform,
432
+ version: this.context.version,
433
+ custom: this.context.custom
434
+ };
435
+ }
436
+ /**
437
+ * Clears all context data and resets the builder.
438
+ *
439
+ * @returns Builder instance for chaining
440
+ */
441
+ clear() {
442
+ this.context = {};
443
+ return this;
444
+ }
445
+ /**
446
+ * Creates a copy of the current builder state.
447
+ *
448
+ * @returns New builder instance with copied context
449
+ */
450
+ clone() {
451
+ const cloned = new _FeatureFlagContextBuilder();
452
+ cloned.context = { ...this.context };
453
+ if (this.context.custom) {
454
+ cloned.context.custom = { ...this.context.custom };
455
+ }
456
+ return cloned;
457
+ }
458
+ };
459
+ var ContextUtils = {
460
+ /**
461
+ * Creates a basic context for anonymous users using the builder.
462
+ *
463
+ * @param environment - Target environment
464
+ * @param platform - User platform
465
+ * @returns Basic anonymous context
466
+ */
467
+ createAnonymousContext(environment, platform = "web") {
468
+ return new FeatureFlagContextBuilder().setEnvironment(environment).setPlatform(platform).build();
469
+ },
470
+ /**
471
+ * Creates a context for authenticated users using the builder.
472
+ *
473
+ * @param params - User context parameters
474
+ * @returns User context
475
+ */
476
+ createUserContext(params) {
477
+ const builder = new FeatureFlagContextBuilder().setUserId(params.userId).setEnvironment(params.environment);
478
+ if (params.userEmail) builder.setUserEmail(params.userEmail);
479
+ if (params.userRole) builder.setUserRole(params.userRole);
480
+ if (params.platform) builder.setPlatform(params.platform);
481
+ if (params.country) builder.setCountry(params.country);
482
+ if (params.version) builder.setVersion(params.version);
483
+ if (params.custom) builder.setCustom(params.custom);
484
+ return builder.build();
485
+ },
486
+ /**
487
+ * Creates a testing context with minimal required fields.
488
+ *
489
+ * @param overrides - Optional context overrides
490
+ * @returns Testing context
491
+ */
492
+ createTestingContext(overrides = {}) {
493
+ return {
494
+ environment: "development",
495
+ platform: "web",
496
+ ...overrides
497
+ };
498
+ },
499
+ /**
500
+ * Validates if a context object is complete and valid.
501
+ *
502
+ * @param context - Context to validate
503
+ * @returns Validation result
504
+ */
505
+ validateContext(context) {
506
+ const errors = [];
507
+ if (!context.environment) {
508
+ errors.push("Environment is required");
509
+ } else if (!["development", "staging", "production"].includes(context.environment)) {
510
+ errors.push("Environment must be development, staging, or production");
511
+ }
512
+ if (context.platform && !["web", "mobile", "desktop"].includes(context.platform)) {
513
+ errors.push("Platform must be web, mobile, or desktop");
514
+ }
515
+ if (context.country && context.country.length !== config.ISO_STANDARDS.ISO_COUNTRY_CODE_LENGTH) {
516
+ errors.push("Country must be a 2-letter ISO country code");
517
+ }
518
+ return {
519
+ isValid: errors.length === 0,
520
+ errors
521
+ };
522
+ },
523
+ /**
524
+ * Merges multiple context objects, with later contexts taking precedence.
525
+ *
526
+ * @param contexts - Array of contexts to merge
527
+ * @returns Merged context
528
+ */
529
+ mergeContexts(...contexts) {
530
+ const merged = contexts.reduce((acc, context) => {
531
+ const result = {
532
+ ...acc,
533
+ ...context
534
+ };
535
+ if (acc.custom || context.custom) {
536
+ result.custom = {
537
+ ...acc.custom,
538
+ ...context.custom
539
+ };
540
+ }
541
+ return result;
542
+ }, {});
543
+ merged.environment ??= "development";
544
+ return merged;
545
+ },
546
+ /**
547
+ * Extracts a specific field value from a context.
548
+ *
549
+ * @param field - Field name to extract
550
+ * @param context - Context object
551
+ * @returns Field value or undefined
552
+ */
553
+ getContextValue(field, context) {
554
+ const standardFields = {
555
+ userId: /* @__PURE__ */ __name((ctx) => ctx.userId, "userId"),
556
+ userEmail: /* @__PURE__ */ __name((ctx) => ctx.userEmail, "userEmail"),
557
+ userRole: /* @__PURE__ */ __name((ctx) => ctx.userRole, "userRole"),
558
+ country: /* @__PURE__ */ __name((ctx) => ctx.country, "country"),
559
+ platform: /* @__PURE__ */ __name((ctx) => ctx.platform, "platform"),
560
+ version: /* @__PURE__ */ __name((ctx) => ctx.version, "version"),
561
+ environment: /* @__PURE__ */ __name((ctx) => ctx.environment, "environment"),
562
+ custom: /* @__PURE__ */ __name((ctx) => ctx.custom, "custom")
563
+ };
564
+ if (field in standardFields) {
565
+ return standardFields[field](context);
566
+ }
567
+ return context.custom?.[field];
568
+ },
569
+ /**
570
+ * Creates a context fingerprint for caching and consistency.
571
+ *
572
+ * @param context - Context to fingerprint
573
+ * @returns String fingerprint
574
+ */
575
+ createFingerprint(context) {
576
+ const relevant = {
577
+ userId: context.userId,
578
+ userRole: context.userRole,
579
+ environment: context.environment,
580
+ platform: context.platform,
581
+ country: context.country,
582
+ version: context.version,
583
+ custom: context.custom
584
+ };
585
+ const filtered = Object.entries(relevant).filter(([, value]) => value !== void 0).sort(([a], [b]) => a.localeCompare(b)).reduce((acc, [key, value]) => ({ ...acc, [key]: value }), {});
586
+ return JSON.stringify(filtered);
587
+ },
588
+ /**
589
+ * Sanitizes a context by removing sensitive information.
590
+ *
591
+ * @param context - Context to sanitize
592
+ * @param sensitiveFields - Fields to remove (default: ['userEmail'])
593
+ * @returns Sanitized context
594
+ */
595
+ sanitizeContext(context, sensitiveFields = ["userEmail"]) {
596
+ const sanitized = { ...context };
597
+ if (context.custom) {
598
+ sanitized.custom = { ...context.custom };
599
+ }
600
+ for (const field of sensitiveFields) {
601
+ if (field in sanitized) {
602
+ delete sanitized[field];
603
+ }
604
+ if (sanitized.custom && field in sanitized.custom) {
605
+ delete sanitized.custom[field];
606
+ }
607
+ }
608
+ return sanitized;
609
+ }
610
+ };
611
+ function createBackendContext(params) {
612
+ return {
613
+ environment: params.environment,
614
+ userId: params.userId,
615
+ userEmail: params.userEmail,
616
+ userRole: params.userRole,
617
+ platform: params.platform ?? "api",
618
+ country: params.country,
619
+ version: params.version,
620
+ custom: params.custom
621
+ };
622
+ }
623
+ __name(createBackendContext, "createBackendContext");
624
+ function createFrontendContext(params) {
625
+ return {
626
+ environment: params.environment,
627
+ userId: params.userId,
628
+ userEmail: params.userEmail,
629
+ userRole: params.userRole,
630
+ platform: params.platform ?? "web",
631
+ country: params.country,
632
+ version: params.version,
633
+ custom: params.custom
634
+ };
635
+ }
636
+ __name(createFrontendContext, "createFrontendContext");
637
+ function evaluateConditionOperator(condition, contextValue) {
638
+ const { operator } = condition;
639
+ if (isEqualityOperator(operator)) {
640
+ return evaluateEqualityOperator(operator, contextValue, condition.value);
641
+ }
642
+ if (isStringOperator(operator)) {
643
+ return evaluateStringOperator(operator, contextValue, condition.value);
644
+ }
645
+ if (isArrayOperator(operator)) {
646
+ return evaluateArrayOperator(operator, condition.value, contextValue);
647
+ }
648
+ if (isNumericOperator(operator)) {
649
+ return evaluateNumericOperator(operator, contextValue, condition.value);
650
+ }
651
+ return false;
652
+ }
653
+ __name(evaluateConditionOperator, "evaluateConditionOperator");
654
+ function isEqualityOperator(operator) {
655
+ return operator === "equals" || operator === "not_equals";
656
+ }
657
+ __name(isEqualityOperator, "isEqualityOperator");
658
+ function isStringOperator(operator) {
659
+ return operator === "contains" || operator === "not_contains";
660
+ }
661
+ __name(isStringOperator, "isStringOperator");
662
+ function isArrayOperator(operator) {
663
+ return operator === "in" || operator === "not_in";
664
+ }
665
+ __name(isArrayOperator, "isArrayOperator");
666
+ function isNumericOperator(operator) {
667
+ return operator === "greater_than" || operator === "less_than";
668
+ }
669
+ __name(isNumericOperator, "isNumericOperator");
670
+ function evaluateEqualityOperator(operator, contextValue, conditionValue) {
671
+ const isEqual = contextValue === conditionValue;
672
+ return operator === "equals" ? isEqual : !isEqual;
673
+ }
674
+ __name(evaluateEqualityOperator, "evaluateEqualityOperator");
675
+ function evaluateStringOperator(operator, contextValue, conditionValue) {
676
+ if (typeof contextValue === "string" && typeof conditionValue === "string") {
677
+ const contains = contextValue.includes(conditionValue);
678
+ return operator === "contains" ? contains : !contains;
679
+ }
680
+ if (Array.isArray(contextValue) && typeof conditionValue === "string") {
681
+ const contains = contextValue.includes(conditionValue);
682
+ return operator === "contains" ? contains : !contains;
683
+ }
684
+ return operator === "not_contains";
685
+ }
686
+ __name(evaluateStringOperator, "evaluateStringOperator");
687
+ function evaluateArrayOperator(operator, conditionValue, contextValue) {
688
+ if (!Array.isArray(conditionValue)) {
689
+ return operator === "not_in";
690
+ }
691
+ const isIncluded = conditionValue.includes(contextValue);
692
+ return operator === "in" ? isIncluded : !isIncluded;
693
+ }
694
+ __name(evaluateArrayOperator, "evaluateArrayOperator");
695
+ function compareValues(operator, left, right) {
696
+ switch (operator) {
697
+ case "greater_than":
698
+ return left > right;
699
+ case "less_than":
700
+ return left < right;
701
+ default:
702
+ return false;
703
+ }
704
+ }
705
+ __name(compareValues, "compareValues");
706
+ function evaluateNumericOperator(operator, contextValue, conditionValue) {
707
+ const contextNum = Number(contextValue);
708
+ const conditionNum = Number(conditionValue);
709
+ if (!isNaN(contextNum) && !isNaN(conditionNum)) {
710
+ return compareValues(operator, contextNum, conditionNum);
711
+ }
712
+ if (typeof contextValue === "string" && typeof conditionValue === "string") {
713
+ return compareValues(operator, contextValue, conditionValue);
714
+ }
715
+ return false;
716
+ }
717
+ __name(evaluateNumericOperator, "evaluateNumericOperator");
718
+ var ConditionUtils = {
719
+ /**
720
+ * Evaluates multiple conditions with AND logic.
721
+ *
722
+ * @param conditions - Array of conditions
723
+ * @param contextValue - Context value getter function
724
+ * @returns true if all conditions match
725
+ */
726
+ evaluateConditionsAnd: /* @__PURE__ */ __name((conditions, contextValue) => {
727
+ if (conditions.length === 0) return true;
728
+ return conditions.every((condition) => {
729
+ const value = contextValue(condition.field);
730
+ if (value === void 0) return false;
731
+ return evaluateConditionOperator(condition, value);
732
+ });
733
+ }, "evaluateConditionsAnd"),
734
+ /**
735
+ * Evaluates multiple conditions with OR logic.
736
+ *
737
+ * @param conditions - Array of conditions
738
+ * @param contextValue - Context value getter function
739
+ * @returns true if any condition matches
740
+ */
741
+ evaluateConditionsOr: /* @__PURE__ */ __name((conditions, contextValue) => {
742
+ if (conditions.length === 0) return true;
743
+ return conditions.some((condition) => {
744
+ const value = contextValue(condition.field);
745
+ if (value === void 0) return false;
746
+ return evaluateConditionOperator(condition, value);
747
+ });
748
+ }, "evaluateConditionsOr"),
749
+ /**
750
+ * Validates a condition structure.
751
+ *
752
+ * @param condition - Condition to validate
753
+ * @returns Validation result
754
+ */
755
+ validateCondition: /* @__PURE__ */ __name((condition) => {
756
+ const errors = [];
757
+ if (!condition.field) {
758
+ errors.push("Field is required");
759
+ }
760
+ if (!condition.operator) {
761
+ errors.push("Operator is required");
762
+ } else {
763
+ const validOperators = [
764
+ "equals",
765
+ "not_equals",
766
+ "contains",
767
+ "not_contains",
768
+ "in",
769
+ "not_in",
770
+ "greater_than",
771
+ "less_than"
772
+ ];
773
+ if (!validOperators.includes(condition.operator)) {
774
+ errors.push(`Invalid operator: ${condition.operator}`);
775
+ }
776
+ }
777
+ if (condition.value === void 0) {
778
+ errors.push("Value is required");
779
+ }
780
+ return {
781
+ isValid: errors.length === 0,
782
+ errors
783
+ };
784
+ }, "validateCondition"),
785
+ /**
786
+ * Creates a condition object with validation.
787
+ *
788
+ * @param field - Context field to evaluate
789
+ * @param operator - Comparison operator
790
+ * @param value - Value to compare against
791
+ * @returns Valid condition object
792
+ */
793
+ createCondition: /* @__PURE__ */ __name((field, operator, value) => {
794
+ const condition = { field, operator, value };
795
+ const validation = ConditionUtils.validateCondition(condition);
796
+ if (!validation.isValid) {
797
+ throw new errors.CorePackageError(
798
+ `Invalid condition: ${validation.errors.join(", ")}`,
799
+ types.ERROR_CODES.VALIDATION_ERROR
800
+ );
801
+ }
802
+ return condition;
803
+ }, "createCondition")
804
+ };
805
+ var getGlobalProperty = /* @__PURE__ */ __name((key) => {
806
+ return globalThis[key];
807
+ }, "getGlobalProperty");
808
+ var logger = new logger$1.PackageLogger({ packageName: "core", service: "Runtime" });
809
+ function isModuleAvailable(moduleName) {
810
+ const nodeRequire = globalThis.require;
811
+ if (!nodeRequire) return false;
812
+ const resolveModule = nodeRequire.resolve;
813
+ if (!resolveModule) return false;
814
+ try {
815
+ resolveModule(moduleName);
816
+ return true;
817
+ } catch {
818
+ return false;
819
+ }
820
+ }
821
+ __name(isModuleAvailable, "isModuleAvailable");
822
+ function detectAltRuntime() {
823
+ if (typeof getGlobalProperty("Deno") !== "undefined") return "deno";
824
+ if (typeof getGlobalProperty("Bun") !== "undefined") return "bun";
825
+ return null;
826
+ }
827
+ __name(detectAltRuntime, "detectAltRuntime");
828
+ function detectEdgeRuntime() {
829
+ if (typeof getGlobalProperty("EdgeRuntime") !== "undefined") return "edge";
830
+ const isEdgeLike = typeof globalThis.caches !== "undefined" && typeof globalThis.Request !== "undefined" && typeof process === "undefined";
831
+ return isEdgeLike ? "edge" : null;
832
+ }
833
+ __name(detectEdgeRuntime, "detectEdgeRuntime");
834
+ function detectBrowser() {
835
+ if (typeof window !== "undefined" && typeof document !== "undefined") {
836
+ return "browser";
837
+ }
838
+ return null;
839
+ }
840
+ __name(detectBrowser, "detectBrowser");
841
+ function detectNodeFramework() {
842
+ if (process.env.__NUXT__ || process.env.NUXT_APP) return "nuxt";
843
+ if (process.env.__NEXT_PRIVATE_ORIGIN || process.env.NEXT_RUNTIME) return "nextjs";
844
+ if (isModuleAvailable("@nestjs/core")) return "nestjs";
845
+ if (isModuleAvailable("express")) return "express";
846
+ return "node";
847
+ }
848
+ __name(detectNodeFramework, "detectNodeFramework");
849
+ function detectRuntime() {
850
+ const altRuntime = detectAltRuntime();
851
+ if (altRuntime) return altRuntime;
852
+ const edgeRuntime = detectEdgeRuntime();
853
+ if (edgeRuntime) return edgeRuntime;
854
+ const browser = detectBrowser();
855
+ if (browser) return browser;
856
+ if (typeof process !== "undefined" && process.versions?.node) {
857
+ return detectNodeFramework();
858
+ }
859
+ return "unknown";
860
+ }
861
+ __name(detectRuntime, "detectRuntime");
862
+ async function loadEnvFile(envPath, verbose) {
863
+ try {
864
+ const dotenv = await import('dotenv');
865
+ const result = dotenv.config({ path: envPath });
866
+ if (result.error) {
867
+ logger.warn(`Failed to load env file: ${result.error.message}`);
868
+ } else if (verbose) {
869
+ logger.info(`Loaded env from: ${envPath}`);
870
+ }
871
+ } catch {
872
+ logger.warn("dotenv not available, skipping .env file loading");
873
+ }
874
+ }
875
+ __name(loadEnvFile, "loadEnvFile");
876
+ var FILE_KEY_FULL_PATH_SEGMENTS = 4;
877
+ var FILE_KEY_THREE_SEGMENTS = 3;
878
+ var FILE_KEY_TWO_SEGMENTS = 2;
879
+ var MIME_TYPE_PARTS = 2;
880
+ function fileToBase64(file) {
881
+ return new Promise((resolve, reject) => {
882
+ const reader = new FileReader();
883
+ reader.onload = () => {
884
+ const result = reader.result;
885
+ const base64 = result.split(",")[1] || result;
886
+ resolve(base64);
887
+ };
888
+ reader.onerror = () => reject(reader.error);
889
+ reader.readAsDataURL(file);
890
+ });
891
+ }
892
+ __name(fileToBase64, "fileToBase64");
893
+ function base64ToBlob(base64, mimeType = "application/octet-stream") {
894
+ const binary = atob(base64);
895
+ const bytes = new Uint8Array(binary.length);
896
+ for (let i = 0; i < binary.length; i++) {
897
+ bytes[i] = binary.charCodeAt(i);
898
+ }
899
+ return new Blob([bytes], { type: mimeType });
900
+ }
901
+ __name(base64ToBlob, "base64ToBlob");
902
+ function base64ToBuffer(base64) {
903
+ return Buffer.from(base64, "base64");
904
+ }
905
+ __name(base64ToBuffer, "base64ToBuffer");
906
+ function bufferToBase64(data) {
907
+ if (Buffer.isBuffer(data)) {
908
+ return data.toString("base64");
909
+ }
910
+ return Buffer.from(data).toString("base64");
911
+ }
912
+ __name(bufferToBase64, "bufferToBase64");
913
+ function downloadBlob(blob, filename) {
914
+ const win = globalThis;
915
+ if (!win.document) return;
916
+ const url = URL.createObjectURL(blob);
917
+ const a = win.document.createElement("a");
918
+ a.href = url;
919
+ a.download = filename;
920
+ win.document.body.appendChild(a);
921
+ a.click();
922
+ win.document.body.removeChild(a);
923
+ URL.revokeObjectURL(url);
924
+ }
925
+ __name(downloadBlob, "downloadBlob");
926
+ function downloadBase64(base64, filename, mimeType = "application/octet-stream") {
927
+ const blob = base64ToBlob(base64, mimeType);
928
+ downloadBlob(blob, filename);
929
+ }
930
+ __name(downloadBase64, "downloadBase64");
931
+ function parseFileKey(key) {
932
+ const parts = key.split("/");
933
+ const result = { raw: key };
934
+ if (parts.length >= FILE_KEY_FULL_PATH_SEGMENTS) {
935
+ result.entityType = parts[0];
936
+ result.entityId = parts[1];
937
+ result.category = parts[2];
938
+ result.filename = parts.slice(FILE_KEY_THREE_SEGMENTS).join("/");
939
+ } else if (parts.length === FILE_KEY_THREE_SEGMENTS) {
940
+ result.entityType = parts[0];
941
+ result.entityId = parts[1];
942
+ result.filename = parts[FILE_KEY_TWO_SEGMENTS];
943
+ } else if (parts.length === FILE_KEY_TWO_SEGMENTS) {
944
+ result.category = parts[0];
945
+ result.filename = parts[1];
946
+ } else {
947
+ result.filename = parts[0];
948
+ }
949
+ return result;
950
+ }
951
+ __name(parseFileKey, "parseFileKey");
952
+ function getFilenameFromKey(key) {
953
+ return key.split("/").pop() ?? key;
954
+ }
955
+ __name(getFilenameFromKey, "getFilenameFromKey");
956
+ function generateFilename(templateId, outputFormat) {
957
+ const baseName = templateId.split("/").pop() ?? "document";
958
+ return `${baseName}.${outputFormat}`;
959
+ }
960
+ __name(generateFilename, "generateFilename");
961
+ function generateUniqueFilename(prefix = "file", extension = "bin") {
962
+ return `${prefix}-${Date.now()}.${extension}`;
963
+ }
964
+ __name(generateUniqueFilename, "generateUniqueFilename");
965
+ function inferExtensionFromMimeType(mimeType) {
966
+ if (core.MIME_TYPE_EXTENSIONS[mimeType]) {
967
+ return core.MIME_TYPE_EXTENSIONS[mimeType];
968
+ }
969
+ const parts = mimeType.split("/");
970
+ if (parts.length === MIME_TYPE_PARTS) {
971
+ return parts[1].split(";")[0].trim();
972
+ }
973
+ return "bin";
974
+ }
975
+ __name(inferExtensionFromMimeType, "inferExtensionFromMimeType");
976
+ function formatFileSize(bytes, decimals = 2) {
977
+ if (bytes === 0) return "0 Bytes";
978
+ const k = 1024;
979
+ const dm = decimals < 0 ? 0 : decimals;
980
+ const i = Math.floor(Math.log(bytes) / Math.log(k));
981
+ const size = Number.parseFloat((bytes / Math.pow(k, i)).toFixed(dm));
982
+ return `${size} ${core.FILE_SIZE_UNITS[i]}`;
983
+ }
984
+ __name(formatFileSize, "formatFileSize");
985
+ function parseFileSize(sizeString) {
986
+ const match = sizeString.trim().match(/^([\d.]+)\s*([A-Z]+)$/i);
987
+ if (!match) {
988
+ return Number.parseInt(sizeString, 10) || 0;
989
+ }
990
+ const value = Number.parseFloat(match[1]);
991
+ const unit = match[2].toUpperCase();
992
+ return Math.round(value * (core.FILE_SIZE_MULTIPLIERS[unit] ?? 1));
993
+ }
994
+ __name(parseFileSize, "parseFileSize");
995
+
996
+ // src/utils/hmr-singleton.ts
997
+ var GLOBAL_PREFIX = "__plyaz_hmr_singleton__";
998
+ function hasResetMethod(obj) {
999
+ return typeof obj === "object" && obj !== null && "reset" in obj && typeof obj.reset === "function";
1000
+ }
1001
+ __name(hasResetMethod, "hasResetMethod");
1002
+ function createHmrSafeSingleton(key, initializer, options = {}) {
1003
+ const { onInitialized, onError, onReset } = options;
1004
+ const instanceKey = `${GLOBAL_PREFIX}${key}_instance`;
1005
+ const promiseKey = `${GLOBAL_PREFIX}${key}_promise`;
1006
+ const initializedKey = `${GLOBAL_PREFIX}${key}_initialized`;
1007
+ const g = globalThis;
1008
+ function getInstance() {
1009
+ return g[instanceKey];
1010
+ }
1011
+ __name(getInstance, "getInstance");
1012
+ function setInstance(instance) {
1013
+ g[instanceKey] = instance;
1014
+ }
1015
+ __name(setInstance, "setInstance");
1016
+ function clearInstance() {
1017
+ delete g[instanceKey];
1018
+ }
1019
+ __name(clearInstance, "clearInstance");
1020
+ function getPromise() {
1021
+ return g[promiseKey];
1022
+ }
1023
+ __name(getPromise, "getPromise");
1024
+ function setPromise(promise) {
1025
+ if (promise === null) {
1026
+ delete g[promiseKey];
1027
+ } else {
1028
+ g[promiseKey] = promise;
1029
+ }
1030
+ }
1031
+ __name(setPromise, "setPromise");
1032
+ function isInit() {
1033
+ return g[initializedKey] === true;
1034
+ }
1035
+ __name(isInit, "isInit");
1036
+ function setInit(value) {
1037
+ g[initializedKey] = value;
1038
+ }
1039
+ __name(setInit, "setInit");
1040
+ async function accessor() {
1041
+ if (isInit()) {
1042
+ const instance = getInstance();
1043
+ if (instance !== void 0) {
1044
+ return instance;
1045
+ }
1046
+ }
1047
+ const existingPromise = getPromise();
1048
+ if (existingPromise) {
1049
+ return existingPromise;
1050
+ }
1051
+ const initPromise = (async () => {
1052
+ try {
1053
+ const instance = await initializer();
1054
+ setInstance(instance);
1055
+ setInit(true);
1056
+ onInitialized?.();
1057
+ return instance;
1058
+ } catch (err) {
1059
+ setPromise(null);
1060
+ const error = err instanceof Error ? err : new Error(String(err));
1061
+ onError?.(error);
1062
+ throw error;
1063
+ }
1064
+ })();
1065
+ setPromise(initPromise);
1066
+ return initPromise;
1067
+ }
1068
+ __name(accessor, "accessor");
1069
+ accessor.isInitialized = () => isInit();
1070
+ accessor.reset = async () => {
1071
+ if (isInit()) {
1072
+ const instance = getInstance();
1073
+ if (hasResetMethod(instance)) {
1074
+ await instance.reset();
1075
+ }
1076
+ clearInstance();
1077
+ setInit(false);
1078
+ setPromise(null);
1079
+ onReset?.();
1080
+ }
1081
+ };
1082
+ return accessor;
1083
+ }
1084
+ __name(createHmrSafeSingleton, "createHmrSafeSingleton");
1085
+
1086
+ exports.ConditionUtils = ConditionUtils;
1087
+ exports.ContextUtils = ContextUtils;
1088
+ exports.FeatureFlagContextBuilder = FeatureFlagContextBuilder;
1089
+ exports.HashUtils = HashUtils;
1090
+ exports.IdUtils = IdUtils;
1091
+ exports.ObjectUtils = ObjectUtils;
1092
+ exports.TypeGuardUtils = TypeGuardUtils;
1093
+ exports.ValueUtils = ValueUtils;
1094
+ exports.base64ToBlob = base64ToBlob;
1095
+ exports.base64ToBuffer = base64ToBuffer;
1096
+ exports.bufferToBase64 = bufferToBase64;
1097
+ exports.createBackendContext = createBackendContext;
1098
+ exports.createFrontendContext = createFrontendContext;
1099
+ exports.createHmrSafeSingleton = createHmrSafeSingleton;
1100
+ exports.createRolloutIdentifier = createRolloutIdentifier;
1101
+ exports.detectRuntime = detectRuntime;
1102
+ exports.downloadBase64 = downloadBase64;
1103
+ exports.downloadBlob = downloadBlob;
1104
+ exports.evaluateArrayOperator = evaluateArrayOperator;
1105
+ exports.evaluateConditionOperator = evaluateConditionOperator;
1106
+ exports.evaluateEqualityOperator = evaluateEqualityOperator;
1107
+ exports.evaluateNumericOperator = evaluateNumericOperator;
1108
+ exports.evaluateStringOperator = evaluateStringOperator;
1109
+ exports.fileToBase64 = fileToBase64;
1110
+ exports.formatFileSize = formatFileSize;
1111
+ exports.generateCorrelationId = generateCorrelationId;
1112
+ exports.generateFilename = generateFilename;
1113
+ exports.generateId = generateId;
1114
+ exports.generateShortId = generateShortId;
1115
+ exports.generateSpanId = generateSpanId;
1116
+ exports.generateTraceId = generateTraceId;
1117
+ exports.generateUniqueFilename = generateUniqueFilename;
1118
+ exports.getFilenameFromKey = getFilenameFromKey;
1119
+ exports.getNestedProperty = getNestedProperty;
1120
+ exports.getValidProviders = getValidProviders;
1121
+ exports.hasNestedProperty = hasNestedProperty;
1122
+ exports.hasProperty = hasProperty;
1123
+ exports.hashString = hashString;
1124
+ exports.inferExtensionFromMimeType = inferExtensionFromMimeType;
1125
+ exports.isArrayOperator = isArrayOperator;
1126
+ exports.isDefined = isDefined;
1127
+ exports.isEqualityOperator = isEqualityOperator;
1128
+ exports.isInRollout = isInRollout;
1129
+ exports.isNumber = isNumber;
1130
+ exports.isNumericOperator = isNumericOperator;
1131
+ exports.isObject = isObject;
1132
+ exports.isString = isString;
1133
+ exports.isStringOperator = isStringOperator;
1134
+ exports.isTruthy = isTruthy;
1135
+ exports.isValidUUID = isValidUUID;
1136
+ exports.loadEnvFile = loadEnvFile;
1137
+ exports.parseFileKey = parseFileKey;
1138
+ exports.parseFileSize = parseFileSize;
1139
+ exports.setNestedProperty = setNestedProperty;
1140
+ exports.toBoolean = toBoolean;
1141
+ exports.toNumber = toNumber;
1142
+ //# sourceMappingURL=index.js.map
1143
+ //# sourceMappingURL=index.js.map