@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.
- package/dist/utils/index.js +1143 -0
- package/dist/utils/index.js.map +1 -0
- package/dist/utils/index.mjs +1086 -0
- package/dist/utils/index.mjs.map +1 -0
- package/package.json +11 -5
|
@@ -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
|