diff --git a/.gitmodules b/.gitmodules index f9b9019..bba15b6 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,4 +1,4 @@ [submodule "tests/engine/engine-tests/engine-test-data"] path = tests/engine/engine-tests/engine-test-data url = git@github.com:Flagsmith/engine-test-data.git - branch = v2.5.0 + branch = v1.0.0 diff --git a/.husky/pre-commit b/.husky/pre-commit index c221482..938cbdb 100755 --- a/.husky/pre-commit +++ b/.husky/pre-commit @@ -1,7 +1,6 @@ #!/bin/sh . "$(dirname "$0")/_/husky.sh" -npm run generate-engine-types npm run lint git add ./flagsmith-engine ./sdk ./tests ./index.ts ./.github npm run test \ No newline at end of file diff --git a/flagsmith-engine/evaluation/evaluationContext/evaluationContext.types.ts b/flagsmith-engine/evaluation/evaluationContext/evaluationContext.types.ts deleted file mode 100644 index d1ec209..0000000 --- a/flagsmith-engine/evaluation/evaluationContext/evaluationContext.types.ts +++ /dev/null @@ -1,252 +0,0 @@ -/* eslint-disable */ -/** - * This file was automatically generated by json-schema-to-typescript. - * DO NOT MODIFY IT BY HAND. Instead, modify the source JSONSchema file, - * and run json-schema-to-typescript to regenerate this file. - */ - -/** - * An environment's unique identifier. - */ -export type Key = string; -/** - * An environment's human-readable name. - */ -export type Name = string; -/** - * A unique identifier for an identity, used for segment and multivariate feature flag targeting, and displayed in the Flagsmith UI. - */ -export type Identifier = string; -/** - * Key used when selecting a value for a multivariate feature, or for % split segmentation. Set to an internal identifier or a composite value based on the environment key and identifier, depending on Flagsmith implementation. - */ -export type Key1 = string; -/** - * Key used for % split segmentation. - */ -export type Key2 = string; -/** - * The name of the segment. - */ -export type Name1 = string; -/** - * Segment rule type. Represents a logical quantifier for the conditions and sub-rules. - */ -export type Type = 'ALL' | 'ANY' | 'NONE'; -export type SegmentCondition = SegmentCondition1 | InSegmentCondition; -/** - * A reference to the identity trait or value in the evaluation context. - */ -export type Property = string; -/** - * The operator to use for evaluating the condition. - */ -export type Operator = - | 'EQUAL' - | 'GREATER_THAN' - | 'LESS_THAN' - | 'LESS_THAN_INCLUSIVE' - | 'CONTAINS' - | 'GREATER_THAN_INCLUSIVE' - | 'NOT_CONTAINS' - | 'NOT_EQUAL' - | 'REGEX' - | 'PERCENTAGE_SPLIT' - | 'MODULO' - | 'IS_SET' - | 'IS_NOT_SET' - | 'IN'; -/** - * The value to compare against the trait or context value. - */ -export type Value = string; -/** - * A reference to the identity trait or value in the evaluation context. - */ -export type Property1 = string; -/** - * The operator to use for evaluating the condition. - */ -export type Operator1 = 'IN'; -/** - * The values to compare against the trait or context value. - */ -export type Value1 = string[]; -/** - * Conditions that must be met for the rule to apply. - */ -export type Conditions = SegmentCondition[]; -/** - * Sub-rules nested within the segment rule. - */ -export type SubRules = SegmentRule[]; -/** - * Rules that define the segment. - */ -export type Rules = SegmentRule[]; -/** - * Key used when selecting a value for a multivariate feature. Set to an internal identifier or a UUID, depending on Flagsmith implementation. - */ -export type Key3 = string; -/** - * Unique feature identifier. - */ -export type FeatureKey = string; -/** - * Feature name. - */ -export type Name2 = string; -/** - * Indicates whether the feature is enabled in the environment. - */ -export type Enabled = boolean; -/** - * A default environment value for the feature. If the feature is multivariate, this will be the control value. - */ -export type Value2 = string | number | boolean | null; -/** - * The value of the feature. - */ -export type Value3 = string | number | boolean | null; -/** - * The weight of the feature value variant, as a percentage number (i.e. 100.0). - */ -export type Weight = number; -/** - * Priority of the feature flag variant. Lower values indicate a higher priority when multiple variants apply to the same context key. - */ -export type VariantPriority = number; -/** - * An array of environment default values associated with the feature. Empty for standard features, or contains multiple values for multivariate features. - */ -export type Variants = FeatureValue[]; -/** - * Priority of the feature context. Lower values indicate a higher priority when multiple contexts apply to the same feature. - */ -export type FeaturePriority = number; -/** - * Feature overrides for the segment. - */ -export type Overrides = FeatureContext[]; - -/** - * A context object containing the necessary information to evaluate Flagsmith feature flags. - */ -export interface EvaluationContext { - environment: EnvironmentContext; - /** - * Identity context used for identity-based evaluation. - */ - identity?: IdentityContext | null; - segments?: Segments; - features?: Features; - [k: string]: unknown; -} -/** - * Environment context required for evaluation. - */ -export interface EnvironmentContext { - key: Key; - name: Name; - [k: string]: unknown; -} -/** - * Represents an identity context for feature flag evaluation. - */ -export interface IdentityContext { - identifier: Identifier; - key: Key1; - traits?: Traits; - [k: string]: unknown; -} -/** - * A map of traits associated with the identity, where the key is the trait name and the value is the trait value. - */ -export interface Traits { - [k: string]: string | number | boolean | null; -} -/** - * Segments applicable to the evaluation context. - */ -export interface Segments { - [k: string]: SegmentContext; -} -/** - * Represents a segment context for feature flag evaluation. - */ -export interface SegmentContext { - key: Key2; - name: Name1; - rules: Rules; - overrides?: Overrides; - metadata?: SegmentMetadata; - [k: string]: unknown; -} -/** - * Represents a rule within a segment for feature flag evaluation. - */ -export interface SegmentRule { - type: Type; - conditions?: Conditions; - rules?: SubRules; - [k: string]: unknown; -} -/** - * Represents a condition within a segment rule for feature flag evaluation. - */ -export interface SegmentCondition1 { - property: Property; - operator: Operator; - value: Value; - [k: string]: unknown; -} -/** - * Represents an IN condition within a segment rule for feature flag evaluation. - */ -export interface InSegmentCondition { - property: Property1; - operator: Operator1; - value: Value1; - [k: string]: unknown; -} -/** - * Represents a feature context for feature flag evaluation. - */ -export interface FeatureContext { - key: Key3; - feature_key: FeatureKey; - name: Name2; - enabled: Enabled; - value: Value2; - variants?: Variants; - priority?: FeaturePriority; - metadata?: FeatureMetadata; - [k: string]: unknown; -} -/** - * Represents a multivariate value for a feature flag. - */ -export interface FeatureValue { - value: Value3; - weight: Weight; - priority: VariantPriority; - [k: string]: unknown; -} -/** - * Additional metadata associated with the feature. - */ -export interface FeatureMetadata { - [k: string]: unknown; -} -/** - * Additional metadata associated with the segment. - */ -export interface SegmentMetadata { - [k: string]: unknown; -} -/** - * Features to be evaluated in the context. - */ -export interface Features { - [k: string]: FeatureContext; -} diff --git a/flagsmith-engine/evaluation/evaluationContext/mappers.ts b/flagsmith-engine/evaluation/evaluationContext/mappers.ts deleted file mode 100644 index dede412..0000000 --- a/flagsmith-engine/evaluation/evaluationContext/mappers.ts +++ /dev/null @@ -1,195 +0,0 @@ -import { - FeaturesWithMetadata, - Segments, - Traits, - GenericEvaluationContext, - EnvironmentContext, - IdentityContext, - SegmentSource -} from '../models.js'; -import { EnvironmentModel } from '../../environments/models.js'; -import { IdentityModel } from '../../identities/models.js'; -import { TraitModel } from '../../identities/traits/models.js'; -import { IDENTITY_OVERRIDE_SEGMENT_NAME } from '../../segments/constants.js'; -import { createHash } from 'node:crypto'; -import { uuidToBigInt } from '../../features/util.js'; - -export function getEvaluationContext( - environment: EnvironmentModel, - identity?: IdentityModel, - overrideTraits?: TraitModel[] -): GenericEvaluationContext { - const environmentContext = mapEnvironmentModelToEvaluationContext(environment); - const identityContext = identity - ? mapIdentityModelToIdentityContext(identity, overrideTraits) - : undefined; - - const context = { - ...environmentContext, - ...(identityContext && { identity: identityContext }) - }; - - return context; -} - -function mapEnvironmentModelToEvaluationContext( - environment: EnvironmentModel -): GenericEvaluationContext { - const environmentContext: EnvironmentContext = { - key: environment.apiKey, - name: environment.project.name - }; - - const features: FeaturesWithMetadata = {}; - for (const fs of environment.featureStates) { - const variants = - fs.multivariateFeatureStateValues?.length > 0 - ? fs.multivariateFeatureStateValues.map(mv => ({ - value: mv.multivariateFeatureOption.value, - weight: mv.percentageAllocation, - priority: mv.id ?? uuidToBigInt(mv.mvFsValueUuid) - })) - : undefined; - - features[fs.feature.name] = { - key: fs.djangoID?.toString() || fs.featurestateUUID, - feature_key: fs.feature.id.toString(), - name: fs.feature.name, - enabled: fs.enabled, - value: fs.getValue(), - variants, - priority: fs.featureSegment?.priority, - metadata: { - flagsmithId: fs.feature.id - } - }; - } - - const segmentOverrides: Segments = {}; - for (const segment of environment.project.segments) { - segmentOverrides[segment.id.toString()] = { - key: segment.id.toString(), - name: segment.name, - rules: segment.rules.map(rule => mapSegmentRuleModelToRule(rule)), - overrides: - segment.featureStates.length > 0 - ? segment.featureStates.map(fs => ({ - key: fs.djangoID?.toString() || fs.featurestateUUID, - feature_key: fs.feature.id.toString(), - name: fs.feature.name, - enabled: fs.enabled, - value: fs.getValue(), - priority: fs.featureSegment?.priority - })) - : [], - metadata: { - source: SegmentSource.API, - flagsmith_id: segment.id - } - }; - } - - let identityOverrideSegments: Segments = {}; - if (environment.identityOverrides && environment.identityOverrides.length > 0) { - identityOverrideSegments = mapIdentityOverridesToSegments(environment.identityOverrides); - } - - return { - environment: environmentContext, - features, - segments: { - ...segmentOverrides, - ...identityOverrideSegments - } - }; -} - -function mapIdentityModelToIdentityContext( - identity: IdentityModel, - overrideTraits?: TraitModel[] -): IdentityContext { - const traits = overrideTraits || identity.identityTraits; - const traitsContext: Traits = {}; - - for (const trait of traits) { - traitsContext[trait.traitKey] = trait.traitValue; - } - - return { - identifier: identity.identifier, - key: identity.djangoID?.toString() || identity.compositeKey, - traits: traitsContext - }; -} - -function mapSegmentRuleModelToRule(rule: any): any { - return { - type: rule.type, - conditions: rule.conditions.map((condition: any) => ({ - property: condition.property, - operator: condition.operator, - value: condition.value - })), - rules: rule.rules.map((subRule: any) => mapSegmentRuleModelToRule(subRule)) - }; -} - -function mapIdentityOverridesToSegments(identityOverrides: IdentityModel[]): Segments { - const segments: Segments = {}; - const featuresToIdentifiers = new Map(); - - for (const identity of identityOverrides) { - if (!identity.identityFeatures || identity.identityFeatures.length === 0) { - continue; - } - - const sortedFeatures = [...identity.identityFeatures].sort((a, b) => - a.feature.name.localeCompare(b.feature.name) - ); - const overridesKey = sortedFeatures.map(fs => ({ - feature_key: fs.feature.id.toString(), - name: fs.feature.name, - enabled: fs.enabled, - value: fs.getValue(), - priority: -Infinity, - metadata: { - flagsmithId: fs.feature.id - } - })); - - const overridesHash = createHash('sha1').update(JSON.stringify(overridesKey)).digest('hex'); - - if (!featuresToIdentifiers.has(overridesHash)) { - featuresToIdentifiers.set(overridesHash, { identifiers: [], overrides: overridesKey }); - } - - featuresToIdentifiers.get(overridesHash)!.identifiers.push(identity.identifier); - } - - for (const [overrideHash, { identifiers, overrides }] of featuresToIdentifiers.entries()) { - const segmentKey = `identity_override_${overrideHash}`; - - segments[segmentKey] = { - key: segmentKey, - name: IDENTITY_OVERRIDE_SEGMENT_NAME, - rules: [ - { - type: 'ALL', - conditions: [ - { - property: '$.identity.identifier', - operator: 'IN', - value: identifiers.join(',') - } - ] - } - ], - metadata: { - source: SegmentSource.IDENTITY_OVERRIDE - }, - overrides: overrides - }; - } - - return segments; -} diff --git a/flagsmith-engine/evaluation/evaluationContext/types.ts b/flagsmith-engine/evaluation/evaluationContext/types.ts deleted file mode 100644 index e671005..0000000 --- a/flagsmith-engine/evaluation/evaluationContext/types.ts +++ /dev/null @@ -1,233 +0,0 @@ -/* eslint-disable */ -/** - * This file was automatically generated by json-schema-to-typescript. - * DO NOT MODIFY IT BY HAND. Instead, modify the source JSONSchema file, - * and run json-schema-to-typescript to regenerate this file. - */ - -/** - * An environment's unique identifier. - */ -export type Key = string; -/** - * An environment's human-readable name. - */ -export type Name = string; -/** - * A unique identifier for an identity, used for segment and multivariate feature flag targeting, and displayed in the Flagsmith UI. - */ -export type Identifier = string; -/** - * Key used when selecting a value for a multivariate feature, or for % split segmentation. Set to an internal identifier or a composite value based on the environment key and identifier, depending on Flagsmith implementation. - */ -export type Key1 = string; -/** - * Key used for % split segmentation. - */ -export type Key2 = string; -/** - * The name of the segment. - */ -export type Name1 = string; -/** - * Segment rule type. Represents a logical quantifier for the conditions and sub-rules. - */ -export type Type = 'ALL' | 'ANY' | 'NONE'; -export type SegmentCondition = SegmentCondition1 | InSegmentCondition; -/** - * A reference to the identity trait or value in the evaluation context. - */ -export type Property = string; -/** - * The operator to use for evaluating the condition. - */ -export type Operator = - | 'EQUAL' - | 'GREATER_THAN' - | 'LESS_THAN' - | 'LESS_THAN_INCLUSIVE' - | 'CONTAINS' - | 'GREATER_THAN_INCLUSIVE' - | 'NOT_CONTAINS' - | 'NOT_EQUAL' - | 'REGEX' - | 'PERCENTAGE_SPLIT' - | 'MODULO' - | 'IS_SET' - | 'IS_NOT_SET' - | 'IN'; -/** - * The value to compare against the trait or context value. - */ -export type Value = string; -/** - * A reference to the identity trait or value in the evaluation context. - */ -export type Property1 = string; -/** - * The operator to use for evaluating the condition. - */ -export type Operator1 = 'IN'; -/** - * The values to compare against the trait or context value. - */ -export type Value1 = string[]; -/** - * Conditions that must be met for the rule to apply. - */ -export type Conditions = SegmentCondition[]; -/** - * Sub-rules nested within the segment rule. - */ -export type SubRules = SegmentRule[]; -/** - * Rules that define the segment. - */ -export type Rules = SegmentRule[]; -/** - * Key used when selecting a value for a multivariate feature. Set to an internal identifier or a UUID, depending on Flagsmith implementation. - */ -export type Key3 = string; -/** - * Unique feature identifier. - */ -export type FeatureKey = string; -/** - * Feature name. - */ -export type Name2 = string; -/** - * Indicates whether the feature is enabled in the environment. - */ -export type Enabled = boolean; -/** - * A default environment value for the feature. If the feature is multivariate, this will be the control value. - */ -export type Value2 = string; -/** - * The value of the feature. - */ -export type Value3 = string; -/** - * The weight of the feature value variant, as a percentage number (i.e. 100.0). - */ -export type Weight = number; -/** - * An array of environment default values associated with the feature. Contains a single value for standard features, or multiple values for multivariate features. - */ -export type Variants = FeatureValue[]; -/** - * Priority of the feature context. Lower values indicate a higher priority when multiple contexts apply to the same feature. - */ -export type Priority = number; -/** - * Feature overrides for the segment. - */ -export type Overrides = FeatureContext[]; - -/** - * A context object containing the necessary information to evaluate Flagsmith feature flags. - */ -export interface EvaluationContext { - environment: EnvironmentContext; - /** - * Identity context used for identity-based evaluation. - */ - identity?: IdentityContext | null; - segments?: Segments; - features?: Features; - [k: string]: unknown; -} -/** - * Environment context required for evaluation. - */ -export interface EnvironmentContext { - key: Key; - name: Name; - [k: string]: unknown; -} -/** - * Represents an identity context for feature flag evaluation. - */ -export interface IdentityContext { - identifier: Identifier; - key: Key1; - traits?: Traits; - [k: string]: unknown; -} -/** - * A map of traits associated with the identity, where the key is the trait name and the value is the trait value. - */ -export interface Traits { - [k: string]: string | number | boolean | null; -} -/** - * Segments applicable to the evaluation context. - */ -export interface Segments { - [k: string]: SegmentContext; -} -/** - * Represents a segment context for feature flag evaluation. - */ -export interface SegmentContext { - key: Key2; - name: Name1; - rules: Rules; - overrides?: Overrides; - [k: string]: unknown; -} -/** - * Represents a rule within a segment for feature flag evaluation. - */ -export interface SegmentRule { - type: Type; - conditions?: Conditions; - rules?: SubRules; - [k: string]: unknown; -} -/** - * Represents a condition within a segment rule for feature flag evaluation. - */ -export interface SegmentCondition1 { - property: Property; - operator: Operator; - value: Value; - [k: string]: unknown; -} -/** - * Represents an IN condition within a segment rule for feature flag evaluation. - */ -export interface InSegmentCondition { - property: Property1; - operator: Operator1; - value: Value1; - [k: string]: unknown; -} -/** - * Represents a feature context for feature flag evaluation. - */ -export interface FeatureContext { - key: Key3; - feature_key: FeatureKey; - name: Name2; - enabled: Enabled; - value: Value2; - variants?: Variants; - priority?: Priority; - [k: string]: unknown; -} -/** - * Represents a multivariate value for a feature flag. - */ -export interface FeatureValue { - value: Value3; - weight: Weight; - [k: string]: unknown; -} -/** - * Features to be evaluated in the context. - */ -export interface Features { - [k: string]: FeatureContext; -} diff --git a/flagsmith-engine/evaluation/evaluationResult/evaluationResult.types.ts b/flagsmith-engine/evaluation/evaluationResult/evaluationResult.types.ts deleted file mode 100644 index 390146a..0000000 --- a/flagsmith-engine/evaluation/evaluationResult/evaluationResult.types.ts +++ /dev/null @@ -1,81 +0,0 @@ -/* eslint-disable */ -/** - * This file was automatically generated by json-schema-to-typescript. - * DO NOT MODIFY IT BY HAND. Instead, modify the source JSONSchema file, - * and run json-schema-to-typescript to regenerate this file. - */ - -/** - * Unique feature identifier. - */ -export type FeatureKey = string; -/** - * Feature name. - */ -export type Name = string; -/** - * Indicates if the feature flag is enabled. - */ -export type Enabled = boolean; -/** - * Feature flag value. - */ -export type Value = string | number | boolean | null; -/** - * Reason for the feature flag evaluation. - */ -export type Reason = string; -/** - * Unique segment identifier. - */ -export type Key = string; -/** - * Segment name. - */ -export type Name1 = string; -/** - * List of segments which the provided context belongs to. - */ -export type Segments = SegmentResult[]; - -/** - * Evaluation result object containing the used context, flag evaluation results, and segments used in the evaluation. - */ -export interface EvaluationResult { - flags: Flags; - segments: Segments; - [k: string]: unknown; -} -/** - * Feature flags evaluated for the context, mapped by feature names. - */ -export interface Flags { - [k: string]: FlagResult; -} -export interface FlagResult { - feature_key: FeatureKey; - name: Name; - enabled: Enabled; - value: Value; - reason: Reason; - metadata?: FeatureMetadata; - [k: string]: unknown; -} -/** - * Additional metadata associated with the feature. - */ -export interface FeatureMetadata { - [k: string]: unknown; -} -export interface SegmentResult { - key: Key; - name: Name1; - metadata?: SegmentMetadata; - [k: string]: unknown; -} -/** - * Additional metadata associated with the segment. - */ -export interface SegmentMetadata { - [k: string]: unknown; -} diff --git a/flagsmith-engine/evaluation/models.ts b/flagsmith-engine/evaluation/models.ts deleted file mode 100644 index 71ea56b..0000000 --- a/flagsmith-engine/evaluation/models.ts +++ /dev/null @@ -1,64 +0,0 @@ -// This file is the entry point for the evaluation module types -// All types from evaluations should be at least imported here and re-exported -// Do not use types directly from generated files - -import type { - EvaluationResult as EvaluationContextResult, - FlagResult, - FeatureMetadata -} from './evaluationResult/evaluationResult.types.js'; - -import type { - FeatureContext, - EnvironmentContext, - IdentityContext, - Segments -} from './evaluationContext/evaluationContext.types.js'; - -export interface CustomFeatureMetadata extends FeatureMetadata { - flagsmithId?: number; -} - -export interface FeatureContextWithMetadata - extends FeatureContext { - metadata: T; - [k: string]: unknown; -} - -export type FeaturesWithMetadata = { - [k: string]: FeatureContextWithMetadata; -}; - -export interface GenericEvaluationContext { - environment: EnvironmentContext; - identity?: IdentityContext | null; - segments?: Segments; - features?: FeaturesWithMetadata; - [k: string]: unknown; -} - -export type FlagResultWithMetadata = FlagResult & { - metadata: T; -}; - -export type EvaluationResultFlags = Record< - string, - FlagResultWithMetadata ->; - -export type EvaluationResultSegments = EvaluationContextResult['segments']; - -export type EvaluationResult = { - flags: EvaluationResultFlags; - segments: EvaluationResultSegments; -}; - -export type EvaluationResultWithMetadata = EvaluationResult; -export type EvaluationContextWithMetadata = GenericEvaluationContext; - -export enum SegmentSource { - API = 'api', - IDENTITY_OVERRIDE = 'identity_override' -} - -export * from './evaluationContext/evaluationContext.types.js'; diff --git a/flagsmith-engine/features/models.ts b/flagsmith-engine/features/models.ts index 1549e5d..686fbed 100644 --- a/flagsmith-engine/features/models.ts +++ b/flagsmith-engine/features/models.ts @@ -1,5 +1,5 @@ import { randomUUID as uuidv4 } from 'node:crypto'; -import { getHashedPercentageForObjIds } from '../utils/hashing/index.js'; +import { getHashedPercentateForObjIds } from '../utils/hashing/index.js'; export class FeatureModel { id: number; @@ -103,7 +103,6 @@ export class FeatureStateModel { const sortedF = this.multivariateFeatureStateValues.sort((a, b) => { return a.id - b.id; }); - for (const myValue of sortedF) { switch (myValue.percentageAllocation) { case 0: @@ -112,7 +111,7 @@ export class FeatureStateModel { return myValue.multivariateFeatureOption.value; default: if (percentageValue === undefined) { - percentageValue = getHashedPercentageForObjIds([ + percentageValue = getHashedPercentateForObjIds([ this.djangoID || this.featurestateUUID, identityID ]); diff --git a/flagsmith-engine/features/types.ts b/flagsmith-engine/features/types.ts deleted file mode 100644 index f792e2d..0000000 --- a/flagsmith-engine/features/types.ts +++ /dev/null @@ -1,5 +0,0 @@ -export enum TARGETING_REASONS { - DEFAULT = 'DEFAULT', - TARGETING_MATCH = 'TARGETING_MATCH', - SPLIT = 'SPLIT' -} diff --git a/flagsmith-engine/features/util.ts b/flagsmith-engine/features/util.ts index ef7224d..0a19589 100644 --- a/flagsmith-engine/features/util.ts +++ b/flagsmith-engine/features/util.ts @@ -46,7 +46,3 @@ export function buildFeatureStateModel(featuresStateModelJSON: any): FeatureStat export function buildFeatureSegment(featureSegmentJSON: any): FeatureSegment { return new FeatureSegment(featureSegmentJSON.priority); } - -export function uuidToBigInt(uuid: string): BigInt { - return BigInt('0x' + uuid.replace(/-/g, '')); -} diff --git a/flagsmith-engine/index.ts b/flagsmith-engine/index.ts index 534b9e1..fb641ee 100644 --- a/flagsmith-engine/index.ts +++ b/flagsmith-engine/index.ts @@ -1,238 +1,102 @@ -import { - EvaluationContextWithMetadata, - EvaluationResultSegments, - EvaluationResultWithMetadata, - FeatureContextWithMetadata, - CustomFeatureMetadata, - FlagResultWithMetadata -} from './evaluation/models.js'; +import { EnvironmentModel } from './environments/models.js'; +import { FeatureStateModel } from './features/models.js'; +import { IdentityModel } from './identities/models.js'; +import { TraitModel } from './identities/traits/models.js'; import { getIdentitySegments } from './segments/evaluators.js'; -import { EvaluationResultFlags } from './evaluation/models.js'; -import { TARGETING_REASONS } from './features/types.js'; -import { getHashedPercentageForObjIds } from './utils/hashing/index.js'; +import { SegmentModel } from './segments/models.js'; +import { FeatureStateNotFound } from './utils/errors.js'; + export { EnvironmentModel } from './environments/models.js'; +export { FeatureModel, FeatureStateModel } from './features/models.js'; export { IdentityModel } from './identities/models.js'; export { TraitModel } from './identities/traits/models.js'; export { SegmentModel } from './segments/models.js'; -export { FeatureModel, FeatureStateModel } from './features/models.js'; export { OrganisationModel } from './organisations/models.js'; -type SegmentOverride = { - feature: FeatureContextWithMetadata; - segmentName: string; -}; - -export type SegmentOverrides = Record; - -/** - * Evaluates flags and segments for the given context. - * - * This is the main entry point for the evaluation engine. It processes segments, - * applies feature overrides based on segment priority, and returns the final flag states with - * evaluation reasons. - * - * @param context - EvaluationContext containing environment, identity, and segment data - * @returns EvaluationResult with flags, segments, and original context - */ -export function getEvaluationResult( - context: EvaluationContextWithMetadata -): EvaluationResultWithMetadata { - const { segments, segmentOverrides } = evaluateSegments(context); - const flags = evaluateFeatures(context, segmentOverrides); - - return { flags, segments }; -} - -/** - * Evaluates which segments the identity belongs to and collects feature overrides. - * - * @param context - EvaluationContext containing identity and segment definitions - * @returns Object containing segments the identity belongs to and any feature overrides - */ -export function evaluateSegments(context: EvaluationContextWithMetadata): { - segments: EvaluationResultSegments; - segmentOverrides: Record; -} { - if (!context.identity || !context.segments) { - return { - segments: [], - segmentOverrides: {} as Record - }; +function getIdentityFeatureStatesDict( + environment: EnvironmentModel, + identity: IdentityModel, + overrideTraits?: TraitModel[] +) { + // Get feature states from the environment + const featureStates: { [key: number]: FeatureStateModel } = {}; + for (const fs of environment.featureStates) { + featureStates[fs.feature.id] = fs; } - const identitySegments = getIdentitySegments(context); - - const segments = identitySegments.map(segment => ({ - key: segment.key, - name: segment.name, - ...(segment.metadata - ? { - metadata: { - ...segment.metadata - } - } - : {}) - })); - const segmentOverrides = processSegmentOverrides(identitySegments); - return { segments, segmentOverrides }; -} - -/** - * Processes feature overrides from segments, applying priority rules. - * - * When multiple segments override the same feature, the segment with - * higher priority (lower numeric value) takes precedence. - * - * @param identitySegments - Segments that the identity belongs to - * @returns Map of feature keys to their highest-priority segment overrides - */ -export function processSegmentOverrides(identitySegments: any[]): Record { - const segmentOverrides: Record = {}; - - for (const segment of identitySegments) { - if (!segment.overrides) continue; - - const overridesList = Array.isArray(segment.overrides) ? segment.overrides : []; - - for (const override of overridesList) { - if (shouldApplyOverride(override, segmentOverrides)) { - segmentOverrides[override.feature_key] = { - feature: override, - segmentName: segment.name - }; + // Override with any feature states defined by matching segments + const identitySegments: SegmentModel[] = getIdentitySegments( + environment, + identity, + overrideTraits + ); + for (const matchingSegment of identitySegments) { + for (const featureState of matchingSegment.featureStates) { + if (featureStates[featureState.feature.id]) { + if (featureStates[featureState.feature.id].isHigherSegmentPriority(featureState)) { + continue; + } } + featureStates[featureState.feature.id] = featureState; } } - return segmentOverrides; -} - -/** - * Evaluates all features in the context, applying segment overrides where applicable. - * For each feature: - * - Checks if a segment override exists - * - Uses override values if present, otherwise evaluates the base feature - * - Determines appropriate evaluation reason - * - Handles multivariate evaluation for features without overrides - * - * @param context - EvaluationContext containing features and identity - * @param segmentOverrides - Map of feature keys to their segment overrides - * @returns EvaluationResultFlags containing evaluated flag results - */ -export function evaluateFeatures( - context: EvaluationContextWithMetadata, - segmentOverrides: Record -): EvaluationResultFlags { - const flags: EvaluationResultFlags = {}; - - for (const feature of Object.values(context.features || {})) { - const segmentOverride = segmentOverrides[feature.feature_key]; - const finalFeature = segmentOverride ? segmentOverride.feature : feature; - const hasOverride = !!segmentOverride; - - const { value: evaluatedValue, reason: evaluatedReason } = hasOverride - ? { value: finalFeature.value, reason: undefined } - : evaluateFeatureValue(finalFeature, context.identity?.key); - - flags[finalFeature.name] = { - feature_key: finalFeature.feature_key, - name: finalFeature.name, - enabled: finalFeature.enabled, - value: evaluatedValue, - ...(finalFeature.metadata ? { metadata: finalFeature.metadata } : {}), - reason: - evaluatedReason ?? - getTargetingMatchReason({ type: 'SEGMENT', override: segmentOverride }) - } as FlagResultWithMetadata; + // Override with any feature states defined directly the identity + for (const fs of identity.identityFeatures) { + if (featureStates[fs.feature.id]) { + featureStates[fs.feature.id] = fs; + } } - - return flags; + return featureStates; } -function evaluateFeatureValue( - feature: FeatureContextWithMetadata, - identityKey?: string -): { value: any; reason?: string } { - if (!!feature.variants && feature.variants.length > 0 && !!identityKey) { - return getMultivariateFeatureValue(feature, identityKey); - } - - return { value: feature.value, reason: undefined }; -} +export function getIdentityFeatureState( + environment: EnvironmentModel, + identity: IdentityModel, + featureName: string, + overrideTraits?: TraitModel[] +): FeatureStateModel { + const featureStates = getIdentityFeatureStatesDict(environment, identity, overrideTraits); -/** - * Evaluates a multivariate feature flag to determine which variant value to return for a given identity. - * - * Uses deterministic hashing to ensure the same identity always receives the same variant, - * while distributing variants according to their configured weight percentages. - * - * @param feature - The feature context containing variants and their weights - * @param identityKey - The identity key used for deterministic variant selection - * @returns The variant value if the identity falls within a variant's range, otherwise the default feature value - */ -function getMultivariateFeatureValue( - feature: FeatureContextWithMetadata, - identityKey?: string -): { value: any; reason?: string } { - const percentageValue = getHashedPercentageForObjIds([feature.key, identityKey]); - const sortedVariants = [...(feature?.variants || [])].sort((a, b) => { - return (a.priority ?? Infinity) - (b.priority ?? Infinity); - }); + const matchingFeature = Object.values(featureStates).filter( + f => f.feature.name === featureName + ); - let startPercentage = 0; - for (const variant of sortedVariants) { - const limit = startPercentage + variant.weight; - if (startPercentage <= percentageValue && percentageValue < limit) { - return { - value: variant.value, - reason: getTargetingMatchReason({ type: 'SPLIT', weight: variant.weight }) - }; - } - startPercentage = limit; + if (matchingFeature.length === 0) { + throw new FeatureStateNotFound('Feature State Not Found'); } - return { value: feature.value, reason: undefined }; + return matchingFeature[0]; } -export function shouldApplyOverride( - override: any, - existingOverrides: Record -): boolean { - const currentOverride = existingOverrides[override.feature_key]; - return ( - !currentOverride || isHigherPriority(override.priority, currentOverride.feature.priority) +export function getIdentityFeatureStates( + environment: EnvironmentModel, + identity: IdentityModel, + overrideTraits?: TraitModel[] +): FeatureStateModel[] { + const featureStates = Object.values( + getIdentityFeatureStatesDict(environment, identity, overrideTraits) ); -} -export function isHigherPriority( - priorityA: number | undefined, - priorityB: number | undefined -): boolean { - return (priorityA ?? Infinity) < (priorityB ?? Infinity); + if (environment.project.hideDisabledFlags) { + return featureStates.filter(fs => !!fs.enabled); + } + return featureStates; } -export type TargetingMatchReason = - | { - type: 'SEGMENT'; - override: SegmentOverride; - } - | { - type: 'SPLIT'; - weight: number; - }; - -const getTargetingMatchReason = (matchObject: TargetingMatchReason) => { - const { type } = matchObject; +export function getEnvironmentFeatureState(environment: EnvironmentModel, featureName: string) { + const featuresStates = environment.featureStates.filter(f => f.feature.name === featureName); - if (type === 'SEGMENT') { - return matchObject.override - ? `${TARGETING_REASONS.TARGETING_MATCH}; segment=${matchObject.override.segmentName}` - : TARGETING_REASONS.DEFAULT; + if (featuresStates.length === 0) { + throw new FeatureStateNotFound('Feature State Not Found'); } - if (type === 'SPLIT') { - return `${TARGETING_REASONS.SPLIT}; weight=${matchObject.weight}`; - } + return featuresStates[0]; +} - return TARGETING_REASONS.DEFAULT; -}; +export function getEnvironmentFeatureStates(environment: EnvironmentModel): FeatureStateModel[] { + if (environment.project.hideDisabledFlags) { + return environment.featureStates.filter(fs => !!fs.enabled); + } + return environment.featureStates; +} diff --git a/flagsmith-engine/segments/constants.ts b/flagsmith-engine/segments/constants.ts index fad1660..d2a3e9b 100644 --- a/flagsmith-engine/segments/constants.ts +++ b/flagsmith-engine/segments/constants.ts @@ -4,7 +4,6 @@ export const ANY_RULE = 'ANY'; export const NONE_RULE = 'NONE'; export const RULE_TYPES = [ALL_RULE, ANY_RULE, NONE_RULE]; -export const IDENTITY_OVERRIDE_SEGMENT_NAME = 'identity_overrides'; // Segment Condition Operators export const EQUAL = 'EQUAL'; diff --git a/flagsmith-engine/segments/evaluators.ts b/flagsmith-engine/segments/evaluators.ts index 0b08526..f5d0081 100644 --- a/flagsmith-engine/segments/evaluators.ts +++ b/flagsmith-engine/segments/evaluators.ts @@ -1,175 +1,76 @@ -import * as jsonpath from 'jsonpath'; -import { - GenericEvaluationContext, - InSegmentCondition, - SegmentCondition, - SegmentContext, - SegmentRule -} from '../evaluation/models.js'; -import { getHashedPercentageForObjIds } from '../utils/hashing/index.js'; -import { SegmentConditionModel } from './models.js'; -import { IS_NOT_SET, IS_SET, PERCENTAGE_SPLIT } from './constants.js'; - -/** - * Returns all segments that the identity belongs to based on segment rules evaluation. - * - * An identity belongs to a segment if it matches ALL of the segment's rules. - * If the context has no identity or segments, returns an empty array. - * - * @param context - Evaluation context containing identity and segment definitions - * @returns Array of segments that the identity matches - */ -export function getIdentitySegments(context: GenericEvaluationContext): SegmentContext[] { - if (!context.identity || !context.segments) return []; - - return Object.values(context.segments).filter(segment => { - if (segment.rules.length === 0) return false; - return segment.rules.every(rule => traitsMatchSegmentRule(rule, segment.key, context)); - }); +import { EnvironmentModel } from '../environments/models.js'; +import { IdentityModel } from '../identities/models.js'; +import { TraitModel } from '../identities/traits/models.js'; +import { getHashedPercentateForObjIds } from '../utils/hashing/index.js'; +import { PERCENTAGE_SPLIT, IS_SET, IS_NOT_SET } from './constants.js'; +import { SegmentConditionModel, SegmentModel, SegmentRuleModel } from './models.js'; + +export function getIdentitySegments( + environment: EnvironmentModel, + identity: IdentityModel, + overrideTraits?: TraitModel[] +): SegmentModel[] { + return environment.project.segments.filter(segment => + evaluateIdentityInSegment(identity, segment, overrideTraits) + ); } -/** - * Evaluates whether a segment condition matches the identity's traits or context values. - * - * Handles different types of conditions: - * - PERCENTAGE_SPLIT: Deterministic percentage-based bucketing using identity key - * - IS_SET/IS_NOT_SET: Checks for trait existence - * - Standard operators: EQUAL, NOT_EQUAL, etc. via SegmentConditionModel - * - JSONPath expressions: $.identity.identifier, $.environment.name, etc. - * - * @param condition - The condition to evaluate (property, operator, value) - * @param segmentKey - Key of the segment (used for percentage split hashing) - * @param context - Evaluation context containing identity, traits, and environment - * @returns true if the condition matches - */ -export function traitsMatchSegmentCondition( - condition: SegmentCondition | InSegmentCondition, - segmentKey: string, - context?: GenericEvaluationContext +export function evaluateIdentityInSegment( + identity: IdentityModel, + segment: SegmentModel, + overrideTraits?: TraitModel[] ): boolean { - if (condition.operator === PERCENTAGE_SPLIT) { - const contextValueKey = - getContextValue(condition.property, context) || context?.identity?.key; - const hashedPercentage = getHashedPercentageForObjIds([segmentKey, contextValueKey]); - return hashedPercentage <= parseFloat(String(condition.value)); - } - if (!condition.property) { - return false; - } - - const traitValue = getTraitValue(condition.property, context); - - if (condition.operator === IS_SET) { - return traitValue !== undefined && traitValue !== null; - } - if (condition.operator === IS_NOT_SET) { - return traitValue === undefined || traitValue === null; - } - - if (traitValue !== undefined && traitValue !== null) { - const segmentCondition = new SegmentConditionModel( - condition.operator, - condition.value as string, - condition.property - ); - return segmentCondition.matchesTraitValue(traitValue); - } - - return false; + return ( + segment.rules.length > 0 && + segment.rules.filter(rule => + traitsMatchSegmentRule( + overrideTraits || identity.identityTraits, + rule, + segment.id, + identity.djangoID || identity.compositeKey + ) + ).length === segment.rules.length + ); } function traitsMatchSegmentRule( - rule: SegmentRule, - segmentKey: string, - context?: GenericEvaluationContext -): boolean { - const matchesConditions = evaluateConditions(rule, segmentKey, context); - const matchesSubRules = evaluateSubRules(rule, segmentKey, context); - - return matchesConditions && matchesSubRules; -} - -function evaluateConditions( - rule: SegmentRule, - segmentKey: string, - context?: GenericEvaluationContext + identityTraits: TraitModel[], + rule: SegmentRuleModel, + segmentId: number | string, + identityId: number | string ): boolean { - if (!rule.conditions || rule.conditions.length === 0) return true; - - const conditionResults = rule.conditions.map((condition: SegmentCondition) => - traitsMatchSegmentCondition(condition, segmentKey, context) + const matchesConditions = + rule.conditions.length > 0 + ? rule.matchingFunction()( + rule.conditions.map(condition => + traitsMatchSegmentCondition(identityTraits, condition, segmentId, identityId) + ) + ) + : true; + return ( + matchesConditions && + rule.rules.filter(rule => + traitsMatchSegmentRule(identityTraits, rule, segmentId, identityId) + ).length === rule.rules.length ); - - return evaluateRuleConditions(rule.type, conditionResults); } -function evaluateSubRules( - rule: SegmentRule, - segmentKey: string, - context?: GenericEvaluationContext +export function traitsMatchSegmentCondition( + identityTraits: TraitModel[], + condition: SegmentConditionModel, + segmentId: number | string, + identityId: number | string ): boolean { - if (!rule.rules || rule.rules.length === 0) return true; - - return rule.rules.every((subRule: SegmentRule) => - traitsMatchSegmentRule(subRule, segmentKey, context) - ); -} - -function evaluateRuleConditions(ruleType: string, conditionResults: boolean[]): boolean { - switch (ruleType) { - case 'ALL': - return conditionResults.length === 0 || conditionResults.every(result => result); - case 'ANY': - return conditionResults.length > 0 && conditionResults.some(result => result); - case 'NONE': - return conditionResults.length === 0 || conditionResults.every(result => !result); - default: - return false; - } -} - -function getTraitValue(property: string, context?: GenericEvaluationContext): any { - if (property.startsWith('$.')) { - const contextValue = getContextValue(property, context); - if (contextValue && !isNonPrimitive(contextValue)) { - return contextValue; - } - } - - const traits = context?.identity?.traits || {}; - return traits[property]; -} - -function isNonPrimitive(value: any): boolean { - if (value === null || value === undefined) { - return false; + if (condition.operator == PERCENTAGE_SPLIT) { + var hashedPercentage = getHashedPercentateForObjIds([segmentId, identityId]); + return hashedPercentage <= parseFloat(String(condition.value)); } - - // Objects and arrays are non-primitive - return typeof value === 'object'; -} - -/** - * Evaluates JSONPath expressions against the evaluation context. - * - * Supports accessing nested context values using JSONPath syntax. - * Commonly used paths: - * - $.identity.identifier - User's unique identifier - * - $.identity.key - User's internal key - * - $.environment.name - Environment name - * - $.environment.key - Environment key - * - * @param jsonPath - JSONPath expression starting with '$.' - * @param context - Evaluation context to query against - * @returns The resolved value, or undefined if path doesn't exist or is invalid - */ -export function getContextValue(jsonPath: string, context?: GenericEvaluationContext): any { - if (!context || !jsonPath?.startsWith('$.')) return undefined; - - try { - const results = jsonpath.query(context, jsonPath); - return results.length > 0 ? results[0] : undefined; - } catch (error) { - return undefined; + const traits = identityTraits.filter(t => t.traitKey === condition.property_); + const trait = traits.length > 0 ? traits[0] : undefined; + if (condition.operator === IS_SET) { + return !!trait; + } else if (condition.operator === IS_NOT_SET) { + return trait == undefined; } + return trait ? condition.matchesTraitValue(trait.traitValue) : false; } diff --git a/flagsmith-engine/segments/models.ts b/flagsmith-engine/segments/models.ts index b912867..67aca0d 100644 --- a/flagsmith-engine/segments/models.ts +++ b/flagsmith-engine/segments/models.ts @@ -1,11 +1,6 @@ import * as semver from 'semver'; -import { - FeatureModel, - FeatureStateModel, - MultivariateFeatureOptionModel, - MultivariateFeatureStateValueModel -} from '../features/models.js'; +import { FeatureStateModel } from '../features/models.js'; import { getCastingFunction as getCastingFunction } from '../utils/index.js'; import { ALL_RULE, @@ -18,12 +13,6 @@ import { CONDITION_OPERATORS } from './constants.js'; import { isSemver } from './util.js'; -import { - EvaluationContext, - Overrides -} from '../evaluation/evaluationContext/evaluationContext.types.js'; -import { CONSTANTS } from '../features/constants.js'; -import { EvaluationResultSegments, SegmentSource } from '../evaluation/models.js'; export const all = (iterable: Array) => iterable.filter(e => !!e).length === iterable.length; export const any = (iterable: Array) => iterable.filter(e => !!e).length > 0; @@ -37,45 +26,22 @@ export const matchingFunctions = { [CONDITION_OPERATORS.LESS_THAN_INCLUSIVE]: (thisValue: any, otherValue: any) => thisValue >= otherValue, [CONDITION_OPERATORS.NOT_EQUAL]: (thisValue: any, otherValue: any) => thisValue != otherValue, - [CONDITION_OPERATORS.CONTAINS]: (thisValue: any, otherValue: any) => { - try { - return !!otherValue && otherValue.includes(thisValue); - } catch { - return false; - } - } -}; - -// Semver library throws an error if the version is invalid, in this case, we want to catch and return false -const safeSemverCompare = ( - semverMatchingFunction: (conditionValue: any, traitValue: any) => boolean -) => { - return (conditionValue: any, traitValue: any) => { - try { - return semverMatchingFunction(conditionValue, traitValue); - } catch { - return false; - } - }; + [CONDITION_OPERATORS.CONTAINS]: (thisValue: any, otherValue: any) => + !!otherValue && otherValue.includes(thisValue) }; export const semverMatchingFunction = { ...matchingFunctions, - [CONDITION_OPERATORS.EQUAL]: safeSemverCompare((conditionValue, traitValue) => - semver.eq(traitValue, conditionValue) - ), - [CONDITION_OPERATORS.GREATER_THAN]: safeSemverCompare((conditionValue, traitValue) => - semver.gt(traitValue, conditionValue) - ), - [CONDITION_OPERATORS.GREATER_THAN_INCLUSIVE]: safeSemverCompare((conditionValue, traitValue) => - semver.gte(traitValue, conditionValue) - ), - [CONDITION_OPERATORS.LESS_THAN]: safeSemverCompare((conditionValue, traitValue) => - semver.lt(traitValue, conditionValue) - ), - [CONDITION_OPERATORS.LESS_THAN_INCLUSIVE]: safeSemverCompare((conditionValue, traitValue) => - semver.lte(traitValue, conditionValue) - ) + [CONDITION_OPERATORS.EQUAL]: (thisValue: any, otherValue: any) => + semver.eq(thisValue, otherValue), + [CONDITION_OPERATORS.GREATER_THAN]: (thisValue: any, otherValue: any) => + semver.gt(otherValue, thisValue), + [CONDITION_OPERATORS.GREATER_THAN_INCLUSIVE]: (thisValue: any, otherValue: any) => + semver.gte(otherValue, thisValue), + [CONDITION_OPERATORS.LESS_THAN]: (thisValue: any, otherValue: any) => + semver.gt(thisValue, otherValue), + [CONDITION_OPERATORS.LESS_THAN_INCLUSIVE]: (thisValue: any, otherValue: any) => + semver.gte(thisValue, otherValue) }; export const getMatchingFunctions = (semver: boolean) => @@ -90,17 +56,17 @@ export class SegmentConditionModel { }; operator: string; - value: string | null | undefined | string[]; - property: string | null | undefined; + value: string | null | undefined; + property_: string | null | undefined; constructor( operator: string, - value?: string | null | undefined | string[], + value?: string | null | undefined, property?: string | null | undefined ) { this.operator = operator; this.value = value; - this.property = property; + this.property_ = property; } matchesTraitValue(traitValue: any) { @@ -113,49 +79,17 @@ export class SegmentConditionModel { ); }, evaluateRegex: (traitValue: any) => { - try { - if (!this.value) { - return false; - } - const regex = new RegExp(this.value?.toString()); - return !!traitValue?.toString().match(regex); - } catch { - return false; - } + return !!this.value && !!traitValue?.toString().match(new RegExp(this.value)); }, evaluateModulo: (traitValue: any) => { - const parsedTraitValue = parseFloat(traitValue); - if (isNaN(parsedTraitValue) || !this.value) { - return false; - } - - const parts = this.value.toString().split('|'); - if (parts.length !== 2) { + if (isNaN(parseFloat(traitValue)) || !this.value) { return false; } - - const divisor = parseFloat(parts[0]); - const remainder = parseFloat(parts[1]); - - if (isNaN(divisor) || isNaN(remainder) || divisor === 0) { - return false; - } - - return parsedTraitValue % divisor === remainder; + const parts = this.value.split('|'); + const [divisor, reminder] = [parseFloat(parts[0]), parseFloat(parts[1])]; + return traitValue % divisor === reminder; }, - evaluateIn: (traitValue: string[] | string) => { - if (Array.isArray(this.value)) { - return this.value.includes(traitValue.toString()); - } - - if (typeof this.value === 'string') { - try { - const parsed = JSON.parse(this.value); - if (Array.isArray(parsed)) { - return parsed.includes(traitValue.toString()); - } - } catch {} - } + evaluateIn: (traitValue: any) => { return this.value?.split(',').includes(traitValue.toString()); } }; @@ -210,73 +144,4 @@ export class SegmentModel { this.id = id; this.name = name; } - - static fromSegmentResult( - segmentResults: EvaluationResultSegments, - evaluationContext: EvaluationContext - ): SegmentModel[] { - const segmentModels: SegmentModel[] = []; - if (!evaluationContext.segments) { - return []; - } - - for (const segmentResult of segmentResults) { - if (segmentResult.metadata?.source === SegmentSource.IDENTITY_OVERRIDE) { - continue; - } - const segmentContext = evaluationContext.segments[segmentResult.key]; - if (segmentContext) { - const segment = new SegmentModel(parseInt(segmentContext.key), segmentContext.name); - segment.rules = segmentContext.rules.map(rule => new SegmentRuleModel(rule.type)); - segment.featureStates = SegmentModel.createFeatureStatesFromOverrides( - segmentContext.overrides || [] - ); - segmentModels.push(segment); - } - } - - return segmentModels; - } - - private static createFeatureStatesFromOverrides(overrides: Overrides): FeatureStateModel[] { - if (!overrides) return []; - return overrides.map(override => { - const feature = new FeatureModel( - parseInt(override.feature_key), - override.name, - override.variants?.length && override.variants.length > 0 - ? CONSTANTS.MULTIVARIATE - : CONSTANTS.STANDARD - ); - - const featureState = new FeatureStateModel( - feature, - override.enabled, - override.priority || 0 - ); - - if (override.value !== undefined) { - featureState.setValue(override.value); - } - - if (override.variants && override.variants.length > 0) { - featureState.multivariateFeatureStateValues = this.createMultivariateValues( - override.variants - ); - } - - return featureState; - }); - } - - private static createMultivariateValues(variants: any[]): MultivariateFeatureStateValueModel[] { - return variants.map( - variant => - new MultivariateFeatureStateValueModel( - new MultivariateFeatureOptionModel(variant.value, variant.id as number), - variant.weight as number, - variant.id as number - ) - ); - } } diff --git a/flagsmith-engine/utils/hashing/index.ts b/flagsmith-engine/utils/hashing/index.ts index 1390d13..72f3f46 100644 --- a/flagsmith-engine/utils/hashing/index.ts +++ b/flagsmith-engine/utils/hashing/index.ts @@ -14,7 +14,7 @@ const makeRepeated = (arr: Array, repeats: number) => * @param {} iterations=1 num times to include each id in the generated string to hash * @returns number number between 0 (inclusive) and 100 (exclusive) */ -export function getHashedPercentageForObjIds(objectIds: Array, iterations = 1): number { +export function getHashedPercentateForObjIds(objectIds: Array, iterations = 1): number { let toHash = makeRepeated(objectIds, iterations).join(','); const hashedValue = md5(toHash); const hashedInt = BigInt('0x' + hashedValue); @@ -24,7 +24,7 @@ export function getHashedPercentageForObjIds(objectIds: Array, iterations = /* istanbul ignore next */ if (value === 100) { /* istanbul ignore next */ - return getHashedPercentageForObjIds(objectIds, iterations + 1); + return getHashedPercentateForObjIds(objectIds, iterations + 1); } return value; diff --git a/package-lock.json b/package-lock.json index 933acba..ffdb63f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,24 +9,17 @@ "version": "6.1.0", "license": "MIT", "dependencies": { - "jsonpath": "^1.1.1", "pino": "^8.8.0", "semver": "^7.3.7", "undici-types": "^6.19.8" }, "devDependencies": { - "@types/jest": "^30.0.0", - "@types/jsonpath": "^0.2.4", "@types/node": "^20.16.10", "@types/semver": "^7.3.9", "@types/uuid": "^8.3.4", "@vitest/coverage-v8": "^2.1.2", "esbuild": "^0.25.0", "husky": "^7.0.4", - "install": "^0.13.0", - "json-schema-to-typescript": "^15.0.4", - "jsonc-parser": "^3.3.1", - "npm": "^11.6.1", "prettier": "^2.2.1", "typescript": "^4.9.5", "undici": "^6.19.8", @@ -49,39 +42,6 @@ "node": ">=6.0.0" } }, - "node_modules/@apidevtools/json-schema-ref-parser": { - "version": "11.9.3", - "resolved": "https://registry.npmjs.org/@apidevtools/json-schema-ref-parser/-/json-schema-ref-parser-11.9.3.tgz", - "integrity": "sha512-60vepv88RwcJtSHrD6MjIL6Ta3SOYbgfnkHb+ppAVK+o9mXprRtulx7VlRl3lN3bbvysAfCS7WMVfhUYemB0IQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jsdevtools/ono": "^7.1.3", - "@types/json-schema": "^7.0.15", - "js-yaml": "^4.1.0" - }, - "engines": { - "node": ">= 16" - }, - "funding": { - "url": "https://github.com/sponsors/philsturgeon" - } - }, - "node_modules/@babel/code-frame": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.27.1.tgz", - "integrity": "sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-validator-identifier": "^7.27.1", - "js-tokens": "^4.0.0", - "picocolors": "^1.1.1" - }, - "engines": { - "node": ">=6.9.0" - } - }, "node_modules/@babel/helper-string-parser": { "version": "7.25.7", "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.25.7.tgz", @@ -92,11 +52,10 @@ } }, "node_modules/@babel/helper-validator-identifier": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.27.1.tgz", - "integrity": "sha512-D2hP9eA+Sqx1kBZgzxZh0y1trbuU+JoDkiEwqhQ36nodYqJwyEIhPSdMNd7lOm/4io72luTPWH20Yda0xOuUow==", + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.25.7.tgz", + "integrity": "sha512-AM6TzwYqGChO45oiuPqwL2t20/HdMC1rTPAesnBCgPCSF1x3oN9MVUwQV2iyz4xqWrctwK5RNC8LV22kaQCNYg==", "dev": true, - "license": "MIT", "engines": { "node": ">=6.9.0" } @@ -666,85 +625,6 @@ "node": ">=8" } }, - "node_modules/@jest/diff-sequences": { - "version": "30.0.1", - "resolved": "https://registry.npmjs.org/@jest/diff-sequences/-/diff-sequences-30.0.1.tgz", - "integrity": "sha512-n5H8QLDJ47QqbCNn5SuFjCRDrOLEZ0h8vAHCK5RL9Ls7Xa8AQLa/YxAc9UjFqoEDM48muwtBGjtMY5cr0PLDCw==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/@jest/expect-utils": { - "version": "30.1.2", - "resolved": "https://registry.npmjs.org/@jest/expect-utils/-/expect-utils-30.1.2.tgz", - "integrity": "sha512-HXy1qT/bfdjCv7iC336ExbqqYtZvljrV8odNdso7dWK9bSeHtLlvwWWC3YSybSPL03Gg5rug6WLCZAZFH72m0A==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/get-type": "30.1.0" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/@jest/get-type": { - "version": "30.1.0", - "resolved": "https://registry.npmjs.org/@jest/get-type/-/get-type-30.1.0.tgz", - "integrity": "sha512-eMbZE2hUnx1WV0pmURZY9XoXPkUYjpc55mb0CrhtdWLtzMQPFvu/rZkTLZFTsdaVQa+Tr4eWAteqcUzoawq/uA==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/@jest/pattern": { - "version": "30.0.1", - "resolved": "https://registry.npmjs.org/@jest/pattern/-/pattern-30.0.1.tgz", - "integrity": "sha512-gWp7NfQW27LaBQz3TITS8L7ZCQ0TLvtmI//4OwlQRx4rnWxcPNIYjxZpDcN4+UlGxgm3jS5QPz8IPTCkb59wZA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/node": "*", - "jest-regex-util": "30.0.1" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/@jest/schemas": { - "version": "30.0.5", - "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-30.0.5.tgz", - "integrity": "sha512-DmdYgtezMkh3cpU8/1uyXakv3tJRcmcXxBOcO0tbaozPwpmh4YMsnWrQm9ZmZMfa5ocbxzbFk6O4bDPEc/iAnA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@sinclair/typebox": "^0.34.0" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/@jest/types": { - "version": "30.0.5", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-30.0.5.tgz", - "integrity": "sha512-aREYa3aku9SSnea4aX6bhKn4bgv3AXkgijoQgbYV3yvbiGt6z+MQ85+6mIhx9DsKW2BuB/cLR/A+tcMThx+KLQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/pattern": "30.0.1", - "@jest/schemas": "30.0.5", - "@types/istanbul-lib-coverage": "^2.0.6", - "@types/istanbul-reports": "^3.0.4", - "@types/node": "*", - "@types/yargs": "^17.0.33", - "chalk": "^4.1.2" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, "node_modules/@jridgewell/gen-mapping": { "version": "0.3.5", "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.5.tgz", @@ -793,13 +673,6 @@ "@jridgewell/sourcemap-codec": "^1.4.14" } }, - "node_modules/@jsdevtools/ono": { - "version": "7.1.3", - "resolved": "https://registry.npmjs.org/@jsdevtools/ono/-/ono-7.1.3.tgz", - "integrity": "sha512-4JQNk+3mVzK3xh2rqd6RB4J46qUR19azEHBneZyTZM+c456qOrbbM/5xcR8huNCCcbVt7+UmizG6GuUvPvKUYg==", - "dev": true, - "license": "MIT" - }, "node_modules/@pkgjs/parseargs": { "version": "0.11.0", "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", @@ -1076,13 +949,6 @@ "win32" ] }, - "node_modules/@sinclair/typebox": { - "version": "0.34.41", - "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.34.41.tgz", - "integrity": "sha512-6gS8pZzSXdyRHTIqoqSVknxolr1kzfy4/CeDnrzsVz8TTIWUbOBr6gnzOmTYJ3eXQNh4IYHIGi5aIL7sOZ2G/g==", - "dev": true, - "license": "MIT" - }, "node_modules/@types/estree": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.6.tgz", @@ -1090,65 +956,6 @@ "dev": true, "license": "MIT" }, - "node_modules/@types/istanbul-lib-coverage": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.6.tgz", - "integrity": "sha512-2QF/t/auWm0lsy8XtKVPG19v3sSOQlJe/YHZgfjb/KBBHOGSV+J2q/S671rcq9uTBrLAXmZpqJiaQbMT+zNU1w==", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/istanbul-lib-report": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.3.tgz", - "integrity": "sha512-NQn7AHQnk/RSLOxrBbGyJM/aVQ+pjj5HCgasFxc0K/KhoATfQ/47AyUl15I2yBUpihjmas+a+VJBOqecrFH+uA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/istanbul-lib-coverage": "*" - } - }, - "node_modules/@types/istanbul-reports": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.4.tgz", - "integrity": "sha512-pk2B1NWalF9toCRu6gjBzR69syFjP4Od8WRAX+0mmf9lAjCRicLOWc+ZrxZHx/0XRjotgkF9t6iaMJ+aXcOdZQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/istanbul-lib-report": "*" - } - }, - "node_modules/@types/jest": { - "version": "30.0.0", - "resolved": "https://registry.npmjs.org/@types/jest/-/jest-30.0.0.tgz", - "integrity": "sha512-XTYugzhuwqWjws0CVz8QpM36+T+Dz5mTEBKhNs/esGLnCIlGdRy+Dq78NRjd7ls7r8BC8ZRMOrKlkO1hU0JOwA==", - "dev": true, - "license": "MIT", - "dependencies": { - "expect": "^30.0.0", - "pretty-format": "^30.0.0" - } - }, - "node_modules/@types/json-schema": { - "version": "7.0.15", - "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", - "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/jsonpath": { - "version": "0.2.4", - "resolved": "https://registry.npmjs.org/@types/jsonpath/-/jsonpath-0.2.4.tgz", - "integrity": "sha512-K3hxB8Blw0qgW6ExKgMbXQv2UPZBoE2GqLpVY+yr7nMD2Pq86lsuIzyAaiQ7eMqFL5B6di6pxSkogLJEyEHoGA==", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/lodash": { - "version": "4.17.20", - "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.17.20.tgz", - "integrity": "sha512-H3MHACvFUEiujabxhaI/ImO6gUrd8oOurg7LQtS7mbwIXA/cUqWrvBsaeJ23aZEPk1TAYkurjfMbSELfoCXlGA==", - "dev": true, - "license": "MIT" - }, "node_modules/@types/node": { "version": "20.16.11", "resolved": "https://registry.npmjs.org/@types/node/-/node-20.16.11.tgz", @@ -1164,36 +971,12 @@ "integrity": "sha512-I8EUhyrgfLrcTkzV3TSsGyl1tSuPrEDzr0yd5m90UgNxQkyDXULk3b6MlQqTCpZpNtWe1K0hzclnZkTcLBe2UQ==", "dev": true }, - "node_modules/@types/stack-utils": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.3.tgz", - "integrity": "sha512-9aEbYZ3TbYMznPdcdr3SmIrLXwC/AKZXQeCf9Pgao5CKb8CyHuEX5jzWPTkvregvhRJHcpRO6BFoGW9ycaOkYw==", - "dev": true, - "license": "MIT" - }, "node_modules/@types/uuid": { "version": "8.3.4", "resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-8.3.4.tgz", "integrity": "sha512-c/I8ZRb51j+pYGAu5CrFMRxqZ2ke4y2grEBO5AUjgSkSk+qT2Ea+OdWElz/OiMf5MNpn2b17kuVBwZLQJXzihw==", "dev": true }, - "node_modules/@types/yargs": { - "version": "17.0.33", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.33.tgz", - "integrity": "sha512-WpxBCKWPLr4xSsHgz511rFJAM+wS28w2zEO1QDNY5zM/S8ok70NNfztH0xwhqKyaK0OHCbN98LDAZuy1ctxDkA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/yargs-parser": "*" - } - }, - "node_modules/@types/yargs-parser": { - "version": "21.0.3", - "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-21.0.3.tgz", - "integrity": "sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ==", - "dev": true, - "license": "MIT" - }, "node_modules/@vitest/coverage-v8": { "version": "2.1.9", "resolved": "https://registry.npmjs.org/@vitest/coverage-v8/-/coverage-v8-2.1.9.tgz", @@ -1375,13 +1158,6 @@ "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/argparse": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", - "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", - "dev": true, - "license": "Python-2.0" - }, "node_modules/assertion-error": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-2.0.1.tgz", @@ -1404,8 +1180,7 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", - "dev": true, - "license": "MIT" + "dev": true }, "node_modules/base64-js": { "version": "1.5.1", @@ -1426,29 +1201,6 @@ } ] }, - "node_modules/brace-expansion": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", - "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0" - } - }, - "node_modules/braces": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", - "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", - "dev": true, - "license": "MIT", - "dependencies": { - "fill-range": "^7.1.1" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/buffer": { "version": "6.0.3", "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", @@ -1499,23 +1251,6 @@ "node": ">=12" } }, - "node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, "node_modules/check-error": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/check-error/-/check-error-2.1.1.tgz", @@ -1526,22 +1261,6 @@ "node": ">= 16" } }, - "node_modules/ci-info": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-4.3.0.tgz", - "integrity": "sha512-l+2bNRMiQgcfILUi33labAZYIWlH1kWDp+ecNo5iisRKrbm0xcRyCww71/YU0Fkw0mAFpz9bJayXPjey6vkmaQ==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/sibiraj-s" - } - ], - "license": "MIT", - "engines": { - "node": ">=8" - } - }, "node_modules/color-convert": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", @@ -1601,12 +1320,6 @@ "node": ">=6" } }, - "node_modules/deep-is": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", - "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", - "license": "MIT" - }, "node_modules/eastasianwidth": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", @@ -2041,72 +1754,6 @@ "node": ">=18" } }, - "node_modules/escape-string-regexp": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz", - "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/escodegen": { - "version": "1.14.3", - "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-1.14.3.tgz", - "integrity": "sha512-qFcX0XJkdg+PB3xjZZG/wKSuT1PnQWx57+TVSjIMmILd2yC/6ByYElPwJnslDsuWuSAp4AwJGumarAAmJch5Kw==", - "license": "BSD-2-Clause", - "dependencies": { - "esprima": "^4.0.1", - "estraverse": "^4.2.0", - "esutils": "^2.0.2", - "optionator": "^0.8.1" - }, - "bin": { - "escodegen": "bin/escodegen.js", - "esgenerate": "bin/esgenerate.js" - }, - "engines": { - "node": ">=4.0" - }, - "optionalDependencies": { - "source-map": "~0.6.1" - } - }, - "node_modules/escodegen/node_modules/esprima": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", - "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", - "license": "BSD-2-Clause", - "bin": { - "esparse": "bin/esparse.js", - "esvalidate": "bin/esvalidate.js" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/esprima": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/esprima/-/esprima-1.2.2.tgz", - "integrity": "sha512-+JpPZam9w5DuJ3Q67SqsMGtiHKENSMRVoxvArfJZK01/BfLEObtZ6orJa/MtoGNR/rfMgp5837T41PAmTwAv/A==", - "bin": { - "esparse": "bin/esparse.js", - "esvalidate": "bin/esvalidate.js" - }, - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/estraverse": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", - "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", - "license": "BSD-2-Clause", - "engines": { - "node": ">=4.0" - } - }, "node_modules/estree-walker": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-3.0.3.tgz", @@ -2117,15 +1764,6 @@ "@types/estree": "^1.0.0" } }, - "node_modules/esutils": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", - "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", - "license": "BSD-2-Clause", - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/event-target-shim": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz", @@ -2142,24 +1780,6 @@ "node": ">=0.8.x" } }, - "node_modules/expect": { - "version": "30.1.2", - "resolved": "https://registry.npmjs.org/expect/-/expect-30.1.2.tgz", - "integrity": "sha512-xvHszRavo28ejws8FpemjhwswGj4w/BetHIL8cU49u4sGyXDw2+p3YbeDbj6xzlxi6kWTjIRSTJ+9sNXPnF0Zg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/expect-utils": "30.1.2", - "@jest/get-type": "30.1.0", - "jest-matcher-utils": "30.1.2", - "jest-message-util": "30.1.0", - "jest-mock": "30.0.5", - "jest-util": "30.0.5" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, "node_modules/expect-type": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/expect-type/-/expect-type-1.1.0.tgz", @@ -2170,12 +1790,6 @@ "node": ">=12.0.0" } }, - "node_modules/fast-levenshtein": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", - "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", - "license": "MIT" - }, "node_modules/fast-redact": { "version": "3.5.0", "resolved": "https://registry.npmjs.org/fast-redact/-/fast-redact-3.5.0.tgz", @@ -2184,37 +1798,6 @@ "node": ">=6" } }, - "node_modules/fdir": { - "version": "6.5.0", - "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", - "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12.0.0" - }, - "peerDependencies": { - "picomatch": "^3 || ^4" - }, - "peerDependenciesMeta": { - "picomatch": { - "optional": true - } - } - }, - "node_modules/fill-range": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", - "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", - "dev": true, - "license": "MIT", - "dependencies": { - "to-regex-range": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/foreground-child": { "version": "3.3.0", "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.0.tgz", @@ -2258,34 +1841,6 @@ "node": "^8.16.0 || ^10.6.0 || >=11.0.0" } }, - "node_modules/glob": { - "version": "10.4.5", - "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz", - "integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==", - "dev": true, - "license": "ISC", - "dependencies": { - "foreground-child": "^3.1.0", - "jackspeak": "^3.1.2", - "minimatch": "^9.0.4", - "minipass": "^7.1.2", - "package-json-from-dist": "^1.0.0", - "path-scurry": "^1.11.1" - }, - "bin": { - "glob": "dist/esm/bin.mjs" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/graceful-fs": { - "version": "4.2.11", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", - "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", - "dev": true, - "license": "ISC" - }, "node_modules/has-flag": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", @@ -2335,26 +1890,6 @@ } ] }, - "node_modules/install": { - "version": "0.13.0", - "resolved": "https://registry.npmjs.org/install/-/install-0.13.0.tgz", - "integrity": "sha512-zDml/jzr2PKU9I8J/xyZBQn8rPCAY//UOYNmR01XwNwyfhEWObo2SWfSl1+0tm1u6PhxLwDnfsT/6jB7OUxqFA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.10" - } - }, - "node_modules/is-extglob": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/is-fullwidth-code-point": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", @@ -2364,29 +1899,6 @@ "node": ">=8" } }, - "node_modules/is-glob": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", - "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", - "dev": true, - "license": "MIT", - "dependencies": { - "is-extglob": "^2.1.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-number": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", - "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.12.0" - } - }, "node_modules/isexe": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", @@ -2410,3002 +1922,131 @@ "dependencies": { "istanbul-lib-coverage": "^3.0.0", "make-dir": "^4.0.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/istanbul-lib-source-maps": { - "version": "5.0.6", - "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-5.0.6.tgz", - "integrity": "sha512-yg2d+Em4KizZC5niWhQaIomgf5WlL4vOOjZ5xGCmF8SnPE/mDWWXgvRExdcpCgh9lLRRa1/fSYp2ymmbJ1pI+A==", - "dev": true, - "dependencies": { - "@jridgewell/trace-mapping": "^0.3.23", - "debug": "^4.1.1", - "istanbul-lib-coverage": "^3.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/istanbul-reports": { - "version": "3.1.7", - "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.7.tgz", - "integrity": "sha512-BewmUXImeuRk2YY0PVbxgKAysvhRPUQE0h5QRM++nVWyubKGV0l8qQ5op8+B2DOmwSe63Jivj0BjkPQVf8fP5g==", - "dev": true, - "dependencies": { - "html-escaper": "^2.0.0", - "istanbul-lib-report": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/jackspeak": { - "version": "3.4.3", - "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz", - "integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==", - "dev": true, - "dependencies": { - "@isaacs/cliui": "^8.0.2" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - }, - "optionalDependencies": { - "@pkgjs/parseargs": "^0.11.0" - } - }, - "node_modules/jest-diff": { - "version": "30.1.2", - "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-30.1.2.tgz", - "integrity": "sha512-4+prq+9J61mOVXCa4Qp8ZjavdxzrWQXrI80GNxP8f4tkI2syPuPrJgdRPZRrfUTRvIoUwcmNLbqEJy9W800+NQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/diff-sequences": "30.0.1", - "@jest/get-type": "30.1.0", - "chalk": "^4.1.2", - "pretty-format": "30.0.5" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/jest-matcher-utils": { - "version": "30.1.2", - "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-30.1.2.tgz", - "integrity": "sha512-7ai16hy4rSbDjvPTuUhuV8nyPBd6EX34HkBsBcBX2lENCuAQ0qKCPb/+lt8OSWUa9WWmGYLy41PrEzkwRwoGZQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/get-type": "30.1.0", - "chalk": "^4.1.2", - "jest-diff": "30.1.2", - "pretty-format": "30.0.5" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/jest-message-util": { - "version": "30.1.0", - "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-30.1.0.tgz", - "integrity": "sha512-HizKDGG98cYkWmaLUHChq4iN+oCENohQLb7Z5guBPumYs+/etonmNFlg1Ps6yN9LTPyZn+M+b/9BbnHx3WTMDg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/code-frame": "^7.27.1", - "@jest/types": "30.0.5", - "@types/stack-utils": "^2.0.3", - "chalk": "^4.1.2", - "graceful-fs": "^4.2.11", - "micromatch": "^4.0.8", - "pretty-format": "30.0.5", - "slash": "^3.0.0", - "stack-utils": "^2.0.6" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/jest-mock": { - "version": "30.0.5", - "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-30.0.5.tgz", - "integrity": "sha512-Od7TyasAAQX/6S+QCbN6vZoWOMwlTtzzGuxJku1GhGanAjz9y+QsQkpScDmETvdc9aSXyJ/Op4rhpMYBWW91wQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/types": "30.0.5", - "@types/node": "*", - "jest-util": "30.0.5" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/jest-regex-util": { - "version": "30.0.1", - "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-30.0.1.tgz", - "integrity": "sha512-jHEQgBXAgc+Gh4g0p3bCevgRCVRkB4VB70zhoAE48gxeSr1hfUOsM/C2WoJgVL7Eyg//hudYENbm3Ne+/dRVVA==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/jest-util": { - "version": "30.0.5", - "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-30.0.5.tgz", - "integrity": "sha512-pvyPWssDZR0FlfMxCBoc0tvM8iUEskaRFALUtGQYzVEAqisAztmy+R8LnU14KT4XA0H/a5HMVTXat1jLne010g==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/types": "30.0.5", - "@types/node": "*", - "chalk": "^4.1.2", - "ci-info": "^4.2.0", - "graceful-fs": "^4.2.11", - "picomatch": "^4.0.2" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/js-tokens": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", - "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/js-yaml": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", - "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", - "dev": true, - "license": "MIT", - "dependencies": { - "argparse": "^2.0.1" - }, - "bin": { - "js-yaml": "bin/js-yaml.js" - } - }, - "node_modules/json-schema-to-typescript": { - "version": "15.0.4", - "resolved": "https://registry.npmjs.org/json-schema-to-typescript/-/json-schema-to-typescript-15.0.4.tgz", - "integrity": "sha512-Su9oK8DR4xCmDsLlyvadkXzX6+GGXJpbhwoLtOGArAG61dvbW4YQmSEno2y66ahpIdmLMg6YUf/QHLgiwvkrHQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@apidevtools/json-schema-ref-parser": "^11.5.5", - "@types/json-schema": "^7.0.15", - "@types/lodash": "^4.17.7", - "is-glob": "^4.0.3", - "js-yaml": "^4.1.0", - "lodash": "^4.17.21", - "minimist": "^1.2.8", - "prettier": "^3.2.5", - "tinyglobby": "^0.2.9" - }, - "bin": { - "json2ts": "dist/src/cli.js" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/json-schema-to-typescript/node_modules/prettier": { - "version": "3.6.2", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.6.2.tgz", - "integrity": "sha512-I7AIg5boAr5R0FFtJ6rCfD+LFsWHp81dolrFD8S79U9tb8Az2nGrJncnMSnys+bpQJfRUzqs9hnA81OAA3hCuQ==", - "dev": true, - "license": "MIT", - "bin": { - "prettier": "bin/prettier.cjs" - }, - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/prettier/prettier?sponsor=1" - } - }, - "node_modules/jsonc-parser": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.3.1.tgz", - "integrity": "sha512-HUgH65KyejrUFPvHFPbqOY0rsFip3Bo5wb4ngvdi1EpCYWUQDC5V+Y7mZws+DLkr4M//zQJoanu1SP+87Dv1oQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/jsonpath": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/jsonpath/-/jsonpath-1.1.1.tgz", - "integrity": "sha512-l6Cg7jRpixfbgoWgkrl77dgEj8RPvND0wMH6TwQmi9Qs4TFfS9u5cUFnbeKTwj5ga5Y3BTGGNI28k117LJ009w==", - "license": "MIT", - "dependencies": { - "esprima": "1.2.2", - "static-eval": "2.0.2", - "underscore": "1.12.1" - } - }, - "node_modules/levn": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz", - "integrity": "sha512-0OO4y2iOHix2W6ujICbKIaEQXvFQHue65vUG3pb5EUomzPI90z9hsA1VsO/dbIIpC53J8gxM9Q4Oho0jrCM/yA==", - "license": "MIT", - "dependencies": { - "prelude-ls": "~1.1.2", - "type-check": "~0.3.2" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/lodash": { - "version": "4.17.21", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", - "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", - "dev": true, - "license": "MIT" - }, - "node_modules/loupe": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/loupe/-/loupe-3.1.3.tgz", - "integrity": "sha512-kkIp7XSkP78ZxJEsSxW3712C6teJVoeHHwgo9zJ380de7IYyJ2ISlxojcH2pC5OFLewESmnRi/+XCDIEEVyoug==", - "dev": true, - "license": "MIT" - }, - "node_modules/magic-string": { - "version": "0.30.17", - "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.17.tgz", - "integrity": "sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jridgewell/sourcemap-codec": "^1.5.0" - } - }, - "node_modules/magicast": { - "version": "0.3.5", - "resolved": "https://registry.npmjs.org/magicast/-/magicast-0.3.5.tgz", - "integrity": "sha512-L0WhttDl+2BOsybvEOLK7fW3UA0OQ0IQ2d6Zl2x/a6vVRs3bAY0ECOSHHeL5jD+SbOpOCUEi0y1DgHEn9Qn1AQ==", - "dev": true, - "dependencies": { - "@babel/parser": "^7.25.4", - "@babel/types": "^7.25.4", - "source-map-js": "^1.2.0" - } - }, - "node_modules/make-dir": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-4.0.0.tgz", - "integrity": "sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==", - "dev": true, - "dependencies": { - "semver": "^7.5.3" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/micromatch": { - "version": "4.0.8", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", - "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", - "dev": true, - "license": "MIT", - "dependencies": { - "braces": "^3.0.3", - "picomatch": "^2.3.1" - }, - "engines": { - "node": ">=8.6" - } - }, - "node_modules/micromatch/node_modules/picomatch": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", - "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8.6" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, - "node_modules/minimatch": { - "version": "9.0.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", - "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", - "dev": true, - "license": "ISC", - "dependencies": { - "brace-expansion": "^2.0.1" - }, - "engines": { - "node": ">=16 || 14 >=14.17" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/minimist": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", - "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", - "dev": true, - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/minipass": { - "version": "7.1.2", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", - "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", - "dev": true, - "engines": { - "node": ">=16 || 14 >=14.17" - } - }, - "node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "dev": true - }, - "node_modules/nanoid": { - "version": "3.3.8", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.8.tgz", - "integrity": "sha512-WNLf5Sd8oZxOm+TzppcYk8gVOgP+l58xNy58D0nbUnOxOWRWvlcCV4kUF7ltmI6PsrLl/BgKEyS4mqsGChFN0w==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "bin": { - "nanoid": "bin/nanoid.cjs" - }, - "engines": { - "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" - } - }, - "node_modules/npm": { - "version": "11.6.1", - "resolved": "https://registry.npmjs.org/npm/-/npm-11.6.1.tgz", - "integrity": "sha512-7iDSHDoup6uMQJ37yWrhfqcbMhF0UEfGRap6Nv+aKQcrIJXlCi2cKbj75WBmiHlcwsQCy/U0zEwDZdAx6H/Vaw==", - "bundleDependencies": [ - "@isaacs/string-locale-compare", - "@npmcli/arborist", - "@npmcli/config", - "@npmcli/fs", - "@npmcli/map-workspaces", - "@npmcli/package-json", - "@npmcli/promise-spawn", - "@npmcli/redact", - "@npmcli/run-script", - "@sigstore/tuf", - "abbrev", - "archy", - "cacache", - "chalk", - "ci-info", - "cli-columns", - "fastest-levenshtein", - "fs-minipass", - "glob", - "graceful-fs", - "hosted-git-info", - "ini", - "init-package-json", - "is-cidr", - "json-parse-even-better-errors", - "libnpmaccess", - "libnpmdiff", - "libnpmexec", - "libnpmfund", - "libnpmorg", - "libnpmpack", - "libnpmpublish", - "libnpmsearch", - "libnpmteam", - "libnpmversion", - "make-fetch-happen", - "minimatch", - "minipass", - "minipass-pipeline", - "ms", - "node-gyp", - "nopt", - "normalize-package-data", - "npm-audit-report", - "npm-install-checks", - "npm-package-arg", - "npm-pick-manifest", - "npm-profile", - "npm-registry-fetch", - "npm-user-validate", - "p-map", - "pacote", - "parse-conflict-json", - "proc-log", - "qrcode-terminal", - "read", - "semver", - "spdx-expression-parse", - "ssri", - "supports-color", - "tar", - "text-table", - "tiny-relative-date", - "treeverse", - "validate-npm-package-name", - "which" - ], - "dev": true, - "license": "Artistic-2.0", - "workspaces": [ - "docs", - "smoke-tests", - "mock-globals", - "mock-registry", - "workspaces/*" - ], - "dependencies": { - "@isaacs/string-locale-compare": "^1.1.0", - "@npmcli/arborist": "^9.1.5", - "@npmcli/config": "^10.4.1", - "@npmcli/fs": "^4.0.0", - "@npmcli/map-workspaces": "^5.0.0", - "@npmcli/package-json": "^7.0.1", - "@npmcli/promise-spawn": "^8.0.3", - "@npmcli/redact": "^3.2.2", - "@npmcli/run-script": "^10.0.0", - "@sigstore/tuf": "^4.0.0", - "abbrev": "^3.0.1", - "archy": "~1.0.0", - "cacache": "^20.0.1", - "chalk": "^5.6.2", - "ci-info": "^4.3.0", - "cli-columns": "^4.0.0", - "fastest-levenshtein": "^1.0.16", - "fs-minipass": "^3.0.3", - "glob": "^11.0.3", - "graceful-fs": "^4.2.11", - "hosted-git-info": "^9.0.0", - "ini": "^5.0.0", - "init-package-json": "^8.2.2", - "is-cidr": "^6.0.0", - "json-parse-even-better-errors": "^4.0.0", - "libnpmaccess": "^10.0.2", - "libnpmdiff": "^8.0.8", - "libnpmexec": "^10.1.7", - "libnpmfund": "^7.0.8", - "libnpmorg": "^8.0.1", - "libnpmpack": "^9.0.8", - "libnpmpublish": "^11.1.1", - "libnpmsearch": "^9.0.1", - "libnpmteam": "^8.0.2", - "libnpmversion": "^8.0.2", - "make-fetch-happen": "^15.0.2", - "minimatch": "^10.0.3", - "minipass": "^7.1.1", - "minipass-pipeline": "^1.2.4", - "ms": "^2.1.2", - "node-gyp": "^11.4.2", - "nopt": "^8.1.0", - "normalize-package-data": "^8.0.0", - "npm-audit-report": "^6.0.0", - "npm-install-checks": "^7.1.2", - "npm-package-arg": "^13.0.0", - "npm-pick-manifest": "^11.0.1", - "npm-profile": "^12.0.0", - "npm-registry-fetch": "^19.0.0", - "npm-user-validate": "^3.0.0", - "p-map": "^7.0.3", - "pacote": "^21.0.3", - "parse-conflict-json": "^4.0.0", - "proc-log": "^5.0.0", - "qrcode-terminal": "^0.12.0", - "read": "^4.1.0", - "semver": "^7.7.2", - "spdx-expression-parse": "^4.0.0", - "ssri": "^12.0.0", - "supports-color": "^10.2.2", - "tar": "^7.5.1", - "text-table": "~0.2.0", - "tiny-relative-date": "^2.0.2", - "treeverse": "^3.0.0", - "validate-npm-package-name": "^6.0.2", - "which": "^5.0.0" - }, - "bin": { - "npm": "bin/npm-cli.js", - "npx": "bin/npx-cli.js" - }, - "engines": { - "node": "^20.17.0 || >=22.9.0" - } - }, - "node_modules/npm/node_modules/@isaacs/balanced-match": { - "version": "4.0.1", - "dev": true, - "inBundle": true, - "license": "MIT", - "engines": { - "node": "20 || >=22" - } - }, - "node_modules/npm/node_modules/@isaacs/brace-expansion": { - "version": "5.0.0", - "dev": true, - "inBundle": true, - "license": "MIT", - "dependencies": { - "@isaacs/balanced-match": "^4.0.1" - }, - "engines": { - "node": "20 || >=22" - } - }, - "node_modules/npm/node_modules/@isaacs/cliui": { - "version": "8.0.2", - "dev": true, - "inBundle": true, - "license": "ISC", - "dependencies": { - "string-width": "^5.1.2", - "string-width-cjs": "npm:string-width@^4.2.0", - "strip-ansi": "^7.0.1", - "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", - "wrap-ansi": "^8.1.0", - "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/npm/node_modules/@isaacs/cliui/node_modules/ansi-regex": { - "version": "6.2.2", - "dev": true, - "inBundle": true, - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/ansi-regex?sponsor=1" - } - }, - "node_modules/npm/node_modules/@isaacs/cliui/node_modules/emoji-regex": { - "version": "9.2.2", - "dev": true, - "inBundle": true, - "license": "MIT" - }, - "node_modules/npm/node_modules/@isaacs/cliui/node_modules/string-width": { - "version": "5.1.2", - "dev": true, - "inBundle": true, - "license": "MIT", - "dependencies": { - "eastasianwidth": "^0.2.0", - "emoji-regex": "^9.2.2", - "strip-ansi": "^7.0.1" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/npm/node_modules/@isaacs/cliui/node_modules/strip-ansi": { - "version": "7.1.2", - "dev": true, - "inBundle": true, - "license": "MIT", - "dependencies": { - "ansi-regex": "^6.0.1" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/strip-ansi?sponsor=1" - } - }, - "node_modules/npm/node_modules/@isaacs/fs-minipass": { - "version": "4.0.1", - "dev": true, - "inBundle": true, - "license": "ISC", - "dependencies": { - "minipass": "^7.0.4" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/npm/node_modules/@isaacs/string-locale-compare": { - "version": "1.1.0", - "dev": true, - "inBundle": true, - "license": "ISC" - }, - "node_modules/npm/node_modules/@npmcli/agent": { - "version": "4.0.0", - "dev": true, - "inBundle": true, - "license": "ISC", - "dependencies": { - "agent-base": "^7.1.0", - "http-proxy-agent": "^7.0.0", - "https-proxy-agent": "^7.0.1", - "lru-cache": "^11.2.1", - "socks-proxy-agent": "^8.0.3" - }, - "engines": { - "node": "^20.17.0 || >=22.9.0" - } - }, - "node_modules/npm/node_modules/@npmcli/arborist": { - "version": "9.1.5", - "dev": true, - "inBundle": true, - "license": "ISC", - "dependencies": { - "@isaacs/string-locale-compare": "^1.1.0", - "@npmcli/fs": "^4.0.0", - "@npmcli/installed-package-contents": "^3.0.0", - "@npmcli/map-workspaces": "^5.0.0", - "@npmcli/metavuln-calculator": "^9.0.2", - "@npmcli/name-from-folder": "^3.0.0", - "@npmcli/node-gyp": "^4.0.0", - "@npmcli/package-json": "^7.0.0", - "@npmcli/query": "^4.0.0", - "@npmcli/redact": "^3.0.0", - "@npmcli/run-script": "^10.0.0", - "bin-links": "^5.0.0", - "cacache": "^20.0.1", - "common-ancestor-path": "^1.0.1", - "hosted-git-info": "^9.0.0", - "json-stringify-nice": "^1.1.4", - "lru-cache": "^11.2.1", - "minimatch": "^10.0.3", - "nopt": "^8.0.0", - "npm-install-checks": "^7.1.0", - "npm-package-arg": "^13.0.0", - "npm-pick-manifest": "^11.0.1", - "npm-registry-fetch": "^19.0.0", - "pacote": "^21.0.2", - "parse-conflict-json": "^4.0.0", - "proc-log": "^5.0.0", - "proggy": "^3.0.0", - "promise-all-reject-late": "^1.0.0", - "promise-call-limit": "^3.0.1", - "semver": "^7.3.7", - "ssri": "^12.0.0", - "treeverse": "^3.0.0", - "walk-up-path": "^4.0.0" - }, - "bin": { - "arborist": "bin/index.js" - }, - "engines": { - "node": "^20.17.0 || >=22.9.0" - } - }, - "node_modules/npm/node_modules/@npmcli/config": { - "version": "10.4.1", - "dev": true, - "inBundle": true, - "license": "ISC", - "dependencies": { - "@npmcli/map-workspaces": "^5.0.0", - "@npmcli/package-json": "^7.0.0", - "ci-info": "^4.0.0", - "ini": "^5.0.0", - "nopt": "^8.1.0", - "proc-log": "^5.0.0", - "semver": "^7.3.5", - "walk-up-path": "^4.0.0" - }, - "engines": { - "node": "^20.17.0 || >=22.9.0" - } - }, - "node_modules/npm/node_modules/@npmcli/fs": { - "version": "4.0.0", - "dev": true, - "inBundle": true, - "license": "ISC", - "dependencies": { - "semver": "^7.3.5" - }, - "engines": { - "node": "^18.17.0 || >=20.5.0" - } - }, - "node_modules/npm/node_modules/@npmcli/git": { - "version": "7.0.0", - "dev": true, - "inBundle": true, - "license": "ISC", - "dependencies": { - "@npmcli/promise-spawn": "^8.0.0", - "ini": "^5.0.0", - "lru-cache": "^11.2.1", - "npm-pick-manifest": "^11.0.1", - "proc-log": "^5.0.0", - "promise-retry": "^2.0.1", - "semver": "^7.3.5", - "which": "^5.0.0" - }, - "engines": { - "node": "^20.17.0 || >=22.9.0" - } - }, - "node_modules/npm/node_modules/@npmcli/installed-package-contents": { - "version": "3.0.0", - "dev": true, - "inBundle": true, - "license": "ISC", - "dependencies": { - "npm-bundled": "^4.0.0", - "npm-normalize-package-bin": "^4.0.0" - }, - "bin": { - "installed-package-contents": "bin/index.js" - }, - "engines": { - "node": "^18.17.0 || >=20.5.0" - } - }, - "node_modules/npm/node_modules/@npmcli/map-workspaces": { - "version": "5.0.0", - "dev": true, - "inBundle": true, - "license": "ISC", - "dependencies": { - "@npmcli/name-from-folder": "^3.0.0", - "@npmcli/package-json": "^7.0.0", - "glob": "^11.0.3", - "minimatch": "^10.0.3" - }, - "engines": { - "node": "^20.17.0 || >=22.9.0" - } - }, - "node_modules/npm/node_modules/@npmcli/metavuln-calculator": { - "version": "9.0.2", - "dev": true, - "inBundle": true, - "license": "ISC", - "dependencies": { - "cacache": "^20.0.0", - "json-parse-even-better-errors": "^4.0.0", - "pacote": "^21.0.0", - "proc-log": "^5.0.0", - "semver": "^7.3.5" - }, - "engines": { - "node": "^20.17.0 || >=22.9.0" - } - }, - "node_modules/npm/node_modules/@npmcli/name-from-folder": { - "version": "3.0.0", - "dev": true, - "inBundle": true, - "license": "ISC", - "engines": { - "node": "^18.17.0 || >=20.5.0" - } - }, - "node_modules/npm/node_modules/@npmcli/node-gyp": { - "version": "4.0.0", - "dev": true, - "inBundle": true, - "license": "ISC", - "engines": { - "node": "^18.17.0 || >=20.5.0" - } - }, - "node_modules/npm/node_modules/@npmcli/package-json": { - "version": "7.0.1", - "dev": true, - "inBundle": true, - "license": "ISC", - "dependencies": { - "@npmcli/git": "^7.0.0", - "glob": "^11.0.3", - "hosted-git-info": "^9.0.0", - "json-parse-even-better-errors": "^4.0.0", - "proc-log": "^5.0.0", - "semver": "^7.5.3", - "validate-npm-package-license": "^3.0.4" - }, - "engines": { - "node": "^20.17.0 || >=22.9.0" - } - }, - "node_modules/npm/node_modules/@npmcli/promise-spawn": { - "version": "8.0.3", - "dev": true, - "inBundle": true, - "license": "ISC", - "dependencies": { - "which": "^5.0.0" - }, - "engines": { - "node": "^18.17.0 || >=20.5.0" - } - }, - "node_modules/npm/node_modules/@npmcli/query": { - "version": "4.0.1", - "dev": true, - "inBundle": true, - "license": "ISC", - "dependencies": { - "postcss-selector-parser": "^7.0.0" - }, - "engines": { - "node": "^18.17.0 || >=20.5.0" - } - }, - "node_modules/npm/node_modules/@npmcli/redact": { - "version": "3.2.2", - "dev": true, - "inBundle": true, - "license": "ISC", - "engines": { - "node": "^18.17.0 || >=20.5.0" - } - }, - "node_modules/npm/node_modules/@npmcli/run-script": { - "version": "10.0.0", - "dev": true, - "inBundle": true, - "license": "ISC", - "dependencies": { - "@npmcli/node-gyp": "^4.0.0", - "@npmcli/package-json": "^7.0.0", - "@npmcli/promise-spawn": "^8.0.0", - "node-gyp": "^11.0.0", - "proc-log": "^5.0.0", - "which": "^5.0.0" - }, - "engines": { - "node": "^20.17.0 || >=22.9.0" - } - }, - "node_modules/npm/node_modules/@pkgjs/parseargs": { - "version": "0.11.0", - "dev": true, - "inBundle": true, - "license": "MIT", - "optional": true, - "engines": { - "node": ">=14" - } - }, - "node_modules/npm/node_modules/@sigstore/bundle": { - "version": "4.0.0", - "dev": true, - "inBundle": true, - "license": "Apache-2.0", - "dependencies": { - "@sigstore/protobuf-specs": "^0.5.0" - }, - "engines": { - "node": "^20.17.0 || >=22.9.0" - } - }, - "node_modules/npm/node_modules/@sigstore/core": { - "version": "3.0.0", - "dev": true, - "inBundle": true, - "license": "Apache-2.0", - "engines": { - "node": "^20.17.0 || >=22.9.0" - } - }, - "node_modules/npm/node_modules/@sigstore/protobuf-specs": { - "version": "0.5.0", - "dev": true, - "inBundle": true, - "license": "Apache-2.0", - "engines": { - "node": "^18.17.0 || >=20.5.0" - } - }, - "node_modules/npm/node_modules/@sigstore/sign": { - "version": "4.0.0", - "dev": true, - "inBundle": true, - "license": "Apache-2.0", - "dependencies": { - "@sigstore/bundle": "^4.0.0", - "@sigstore/core": "^3.0.0", - "@sigstore/protobuf-specs": "^0.5.0", - "make-fetch-happen": "^15.0.0", - "proc-log": "^5.0.0", - "promise-retry": "^2.0.1" - }, - "engines": { - "node": "^20.17.0 || >=22.9.0" - } - }, - "node_modules/npm/node_modules/@sigstore/tuf": { - "version": "4.0.0", - "dev": true, - "inBundle": true, - "license": "Apache-2.0", - "dependencies": { - "@sigstore/protobuf-specs": "^0.5.0", - "tuf-js": "^4.0.0" - }, - "engines": { - "node": "^20.17.0 || >=22.9.0" - } - }, - "node_modules/npm/node_modules/@sigstore/verify": { - "version": "3.0.0", - "dev": true, - "inBundle": true, - "license": "Apache-2.0", - "dependencies": { - "@sigstore/bundle": "^4.0.0", - "@sigstore/core": "^3.0.0", - "@sigstore/protobuf-specs": "^0.5.0" - }, - "engines": { - "node": "^20.17.0 || >=22.9.0" - } - }, - "node_modules/npm/node_modules/@tufjs/canonical-json": { - "version": "2.0.0", - "dev": true, - "inBundle": true, - "license": "MIT", - "engines": { - "node": "^16.14.0 || >=18.0.0" - } - }, - "node_modules/npm/node_modules/@tufjs/models": { - "version": "4.0.0", - "dev": true, - "inBundle": true, - "license": "MIT", - "dependencies": { - "@tufjs/canonical-json": "2.0.0", - "minimatch": "^9.0.5" - }, - "engines": { - "node": "^20.17.0 || >=22.9.0" - } - }, - "node_modules/npm/node_modules/@tufjs/models/node_modules/minimatch": { - "version": "9.0.5", - "dev": true, - "inBundle": true, - "license": "ISC", - "dependencies": { - "brace-expansion": "^2.0.1" - }, - "engines": { - "node": ">=16 || 14 >=14.17" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/npm/node_modules/abbrev": { - "version": "3.0.1", - "dev": true, - "inBundle": true, - "license": "ISC", - "engines": { - "node": "^18.17.0 || >=20.5.0" - } - }, - "node_modules/npm/node_modules/agent-base": { - "version": "7.1.4", - "dev": true, - "inBundle": true, - "license": "MIT", - "engines": { - "node": ">= 14" - } - }, - "node_modules/npm/node_modules/ansi-regex": { - "version": "5.0.1", - "dev": true, - "inBundle": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/npm/node_modules/ansi-styles": { - "version": "6.2.3", - "dev": true, - "inBundle": true, - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/npm/node_modules/aproba": { - "version": "2.1.0", - "dev": true, - "inBundle": true, - "license": "ISC" - }, - "node_modules/npm/node_modules/archy": { - "version": "1.0.0", - "dev": true, - "inBundle": true, - "license": "MIT" - }, - "node_modules/npm/node_modules/balanced-match": { - "version": "1.0.2", - "dev": true, - "inBundle": true, - "license": "MIT" - }, - "node_modules/npm/node_modules/bin-links": { - "version": "5.0.0", - "dev": true, - "inBundle": true, - "license": "ISC", - "dependencies": { - "cmd-shim": "^7.0.0", - "npm-normalize-package-bin": "^4.0.0", - "proc-log": "^5.0.0", - "read-cmd-shim": "^5.0.0", - "write-file-atomic": "^6.0.0" - }, - "engines": { - "node": "^18.17.0 || >=20.5.0" - } - }, - "node_modules/npm/node_modules/binary-extensions": { - "version": "3.1.0", - "dev": true, - "inBundle": true, - "license": "MIT", - "engines": { - "node": ">=18.20" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/npm/node_modules/brace-expansion": { - "version": "2.0.2", - "dev": true, - "inBundle": true, - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0" - } - }, - "node_modules/npm/node_modules/cacache": { - "version": "20.0.1", - "dev": true, - "inBundle": true, - "license": "ISC", - "dependencies": { - "@npmcli/fs": "^4.0.0", - "fs-minipass": "^3.0.0", - "glob": "^11.0.3", - "lru-cache": "^11.1.0", - "minipass": "^7.0.3", - "minipass-collect": "^2.0.1", - "minipass-flush": "^1.0.5", - "minipass-pipeline": "^1.2.4", - "p-map": "^7.0.2", - "ssri": "^12.0.0", - "unique-filename": "^4.0.0" - }, - "engines": { - "node": "^20.17.0 || >=22.9.0" - } - }, - "node_modules/npm/node_modules/chalk": { - "version": "5.6.2", - "dev": true, - "inBundle": true, - "license": "MIT", - "engines": { - "node": "^12.17.0 || ^14.13 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/npm/node_modules/chownr": { - "version": "3.0.0", - "dev": true, - "inBundle": true, - "license": "BlueOak-1.0.0", - "engines": { - "node": ">=18" - } - }, - "node_modules/npm/node_modules/ci-info": { - "version": "4.3.0", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/sibiraj-s" - } - ], - "inBundle": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/npm/node_modules/cidr-regex": { - "version": "5.0.0", - "dev": true, - "inBundle": true, - "license": "BSD-2-Clause", - "dependencies": { - "ip-regex": "^5.0.0" - }, - "engines": { - "node": ">=20" - } - }, - "node_modules/npm/node_modules/cli-columns": { - "version": "4.0.0", - "dev": true, - "inBundle": true, - "license": "MIT", - "dependencies": { - "string-width": "^4.2.3", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">= 10" - } - }, - "node_modules/npm/node_modules/cmd-shim": { - "version": "7.0.0", - "dev": true, - "inBundle": true, - "license": "ISC", - "engines": { - "node": "^18.17.0 || >=20.5.0" - } - }, - "node_modules/npm/node_modules/color-convert": { - "version": "2.0.1", - "dev": true, - "inBundle": true, - "license": "MIT", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/npm/node_modules/color-name": { - "version": "1.1.4", - "dev": true, - "inBundle": true, - "license": "MIT" - }, - "node_modules/npm/node_modules/common-ancestor-path": { - "version": "1.0.1", - "dev": true, - "inBundle": true, - "license": "ISC" - }, - "node_modules/npm/node_modules/cross-spawn": { - "version": "7.0.6", - "dev": true, - "inBundle": true, - "license": "MIT", - "dependencies": { - "path-key": "^3.1.0", - "shebang-command": "^2.0.0", - "which": "^2.0.1" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/npm/node_modules/cross-spawn/node_modules/isexe": { - "version": "2.0.0", - "dev": true, - "inBundle": true, - "license": "ISC" - }, - "node_modules/npm/node_modules/cross-spawn/node_modules/which": { - "version": "2.0.2", - "dev": true, - "inBundle": true, - "license": "ISC", - "dependencies": { - "isexe": "^2.0.0" - }, - "bin": { - "node-which": "bin/node-which" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/npm/node_modules/cssesc": { - "version": "3.0.0", - "dev": true, - "inBundle": true, - "license": "MIT", - "bin": { - "cssesc": "bin/cssesc" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/npm/node_modules/debug": { - "version": "4.4.3", - "dev": true, - "inBundle": true, - "license": "MIT", - "dependencies": { - "ms": "^2.1.3" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/npm/node_modules/diff": { - "version": "8.0.2", - "dev": true, - "inBundle": true, - "license": "BSD-3-Clause", - "engines": { - "node": ">=0.3.1" - } - }, - "node_modules/npm/node_modules/eastasianwidth": { - "version": "0.2.0", - "dev": true, - "inBundle": true, - "license": "MIT" - }, - "node_modules/npm/node_modules/emoji-regex": { - "version": "8.0.0", - "dev": true, - "inBundle": true, - "license": "MIT" - }, - "node_modules/npm/node_modules/encoding": { - "version": "0.1.13", - "dev": true, - "inBundle": true, - "license": "MIT", - "optional": true, - "dependencies": { - "iconv-lite": "^0.6.2" - } - }, - "node_modules/npm/node_modules/env-paths": { - "version": "2.2.1", - "dev": true, - "inBundle": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/npm/node_modules/err-code": { - "version": "2.0.3", - "dev": true, - "inBundle": true, - "license": "MIT" - }, - "node_modules/npm/node_modules/exponential-backoff": { - "version": "3.1.2", - "dev": true, - "inBundle": true, - "license": "Apache-2.0" - }, - "node_modules/npm/node_modules/fastest-levenshtein": { - "version": "1.0.16", - "dev": true, - "inBundle": true, - "license": "MIT", - "engines": { - "node": ">= 4.9.1" - } - }, - "node_modules/npm/node_modules/foreground-child": { - "version": "3.3.1", - "dev": true, - "inBundle": true, - "license": "ISC", - "dependencies": { - "cross-spawn": "^7.0.6", - "signal-exit": "^4.0.1" - }, - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/npm/node_modules/fs-minipass": { - "version": "3.0.3", - "dev": true, - "inBundle": true, - "license": "ISC", - "dependencies": { - "minipass": "^7.0.3" - }, - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" - } - }, - "node_modules/npm/node_modules/glob": { - "version": "11.0.3", - "dev": true, - "inBundle": true, - "license": "ISC", - "dependencies": { - "foreground-child": "^3.3.1", - "jackspeak": "^4.1.1", - "minimatch": "^10.0.3", - "minipass": "^7.1.2", - "package-json-from-dist": "^1.0.0", - "path-scurry": "^2.0.0" - }, - "bin": { - "glob": "dist/esm/bin.mjs" - }, - "engines": { - "node": "20 || >=22" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/npm/node_modules/graceful-fs": { - "version": "4.2.11", - "dev": true, - "inBundle": true, - "license": "ISC" - }, - "node_modules/npm/node_modules/hosted-git-info": { - "version": "9.0.0", - "dev": true, - "inBundle": true, - "license": "ISC", - "dependencies": { - "lru-cache": "^11.1.0" - }, - "engines": { - "node": "^20.17.0 || >=22.9.0" - } - }, - "node_modules/npm/node_modules/http-cache-semantics": { - "version": "4.2.0", - "dev": true, - "inBundle": true, - "license": "BSD-2-Clause" - }, - "node_modules/npm/node_modules/http-proxy-agent": { - "version": "7.0.2", - "dev": true, - "inBundle": true, - "license": "MIT", - "dependencies": { - "agent-base": "^7.1.0", - "debug": "^4.3.4" - }, - "engines": { - "node": ">= 14" - } - }, - "node_modules/npm/node_modules/https-proxy-agent": { - "version": "7.0.6", - "dev": true, - "inBundle": true, - "license": "MIT", - "dependencies": { - "agent-base": "^7.1.2", - "debug": "4" - }, - "engines": { - "node": ">= 14" - } - }, - "node_modules/npm/node_modules/iconv-lite": { - "version": "0.6.3", - "dev": true, - "inBundle": true, - "license": "MIT", - "optional": true, - "dependencies": { - "safer-buffer": ">= 2.1.2 < 3.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/npm/node_modules/ignore-walk": { - "version": "8.0.0", - "dev": true, - "inBundle": true, - "license": "ISC", - "dependencies": { - "minimatch": "^10.0.3" - }, - "engines": { - "node": "^20.17.0 || >=22.9.0" - } - }, - "node_modules/npm/node_modules/imurmurhash": { - "version": "0.1.4", - "dev": true, - "inBundle": true, - "license": "MIT", - "engines": { - "node": ">=0.8.19" - } - }, - "node_modules/npm/node_modules/ini": { - "version": "5.0.0", - "dev": true, - "inBundle": true, - "license": "ISC", - "engines": { - "node": "^18.17.0 || >=20.5.0" - } - }, - "node_modules/npm/node_modules/init-package-json": { - "version": "8.2.2", - "dev": true, - "inBundle": true, - "license": "ISC", - "dependencies": { - "@npmcli/package-json": "^7.0.0", - "npm-package-arg": "^13.0.0", - "promzard": "^2.0.0", - "read": "^4.0.0", - "semver": "^7.7.2", - "validate-npm-package-license": "^3.0.4", - "validate-npm-package-name": "^6.0.2" - }, - "engines": { - "node": "^20.17.0 || >=22.9.0" - } - }, - "node_modules/npm/node_modules/ip-address": { - "version": "10.0.1", - "dev": true, - "inBundle": true, - "license": "MIT", - "engines": { - "node": ">= 12" - } - }, - "node_modules/npm/node_modules/ip-regex": { - "version": "5.0.0", - "dev": true, - "inBundle": true, - "license": "MIT", - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/npm/node_modules/is-cidr": { - "version": "6.0.0", - "dev": true, - "inBundle": true, - "license": "BSD-2-Clause", - "dependencies": { - "cidr-regex": "^5.0.0" - }, - "engines": { - "node": ">=20" - } - }, - "node_modules/npm/node_modules/is-fullwidth-code-point": { - "version": "3.0.0", - "dev": true, - "inBundle": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/npm/node_modules/isexe": { - "version": "3.1.1", - "dev": true, - "inBundle": true, - "license": "ISC", - "engines": { - "node": ">=16" - } - }, - "node_modules/npm/node_modules/jackspeak": { - "version": "4.1.1", - "dev": true, - "inBundle": true, - "license": "BlueOak-1.0.0", - "dependencies": { - "@isaacs/cliui": "^8.0.2" - }, - "engines": { - "node": "20 || >=22" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/npm/node_modules/json-parse-even-better-errors": { - "version": "4.0.0", - "dev": true, - "inBundle": true, - "license": "MIT", - "engines": { - "node": "^18.17.0 || >=20.5.0" - } - }, - "node_modules/npm/node_modules/json-stringify-nice": { - "version": "1.1.4", - "dev": true, - "inBundle": true, - "license": "ISC", - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/npm/node_modules/jsonparse": { - "version": "1.3.1", - "dev": true, - "engines": [ - "node >= 0.2.0" - ], - "inBundle": true, - "license": "MIT" - }, - "node_modules/npm/node_modules/just-diff": { - "version": "6.0.2", - "dev": true, - "inBundle": true, - "license": "MIT" - }, - "node_modules/npm/node_modules/just-diff-apply": { - "version": "5.5.0", - "dev": true, - "inBundle": true, - "license": "MIT" - }, - "node_modules/npm/node_modules/libnpmaccess": { - "version": "10.0.2", - "dev": true, - "inBundle": true, - "license": "ISC", - "dependencies": { - "npm-package-arg": "^13.0.0", - "npm-registry-fetch": "^19.0.0" - }, - "engines": { - "node": "^20.17.0 || >=22.9.0" - } - }, - "node_modules/npm/node_modules/libnpmdiff": { - "version": "8.0.8", - "dev": true, - "inBundle": true, - "license": "ISC", - "dependencies": { - "@npmcli/arborist": "^9.1.5", - "@npmcli/installed-package-contents": "^3.0.0", - "binary-extensions": "^3.0.0", - "diff": "^8.0.2", - "minimatch": "^10.0.3", - "npm-package-arg": "^13.0.0", - "pacote": "^21.0.2", - "tar": "^7.5.1" - }, - "engines": { - "node": "^20.17.0 || >=22.9.0" - } - }, - "node_modules/npm/node_modules/libnpmexec": { - "version": "10.1.7", - "dev": true, - "inBundle": true, - "license": "ISC", - "dependencies": { - "@npmcli/arborist": "^9.1.5", - "@npmcli/package-json": "^7.0.0", - "@npmcli/run-script": "^10.0.0", - "ci-info": "^4.0.0", - "npm-package-arg": "^13.0.0", - "pacote": "^21.0.2", - "proc-log": "^5.0.0", - "promise-retry": "^2.0.1", - "read": "^4.0.0", - "semver": "^7.3.7", - "signal-exit": "^4.1.0", - "walk-up-path": "^4.0.0" - }, - "engines": { - "node": "^20.17.0 || >=22.9.0" - } - }, - "node_modules/npm/node_modules/libnpmfund": { - "version": "7.0.8", - "dev": true, - "inBundle": true, - "license": "ISC", - "dependencies": { - "@npmcli/arborist": "^9.1.5" - }, - "engines": { - "node": "^20.17.0 || >=22.9.0" - } - }, - "node_modules/npm/node_modules/libnpmorg": { - "version": "8.0.1", - "dev": true, - "inBundle": true, - "license": "ISC", - "dependencies": { - "aproba": "^2.0.0", - "npm-registry-fetch": "^19.0.0" - }, - "engines": { - "node": "^20.17.0 || >=22.9.0" - } - }, - "node_modules/npm/node_modules/libnpmpack": { - "version": "9.0.8", - "dev": true, - "inBundle": true, - "license": "ISC", - "dependencies": { - "@npmcli/arborist": "^9.1.5", - "@npmcli/run-script": "^10.0.0", - "npm-package-arg": "^13.0.0", - "pacote": "^21.0.2" - }, - "engines": { - "node": "^20.17.0 || >=22.9.0" - } - }, - "node_modules/npm/node_modules/libnpmpublish": { - "version": "11.1.1", - "dev": true, - "inBundle": true, - "license": "ISC", - "dependencies": { - "@npmcli/package-json": "^7.0.0", - "ci-info": "^4.0.0", - "npm-package-arg": "^13.0.0", - "npm-registry-fetch": "^19.0.0", - "proc-log": "^5.0.0", - "semver": "^7.3.7", - "sigstore": "^4.0.0", - "ssri": "^12.0.0" - }, - "engines": { - "node": "^20.17.0 || >=22.9.0" - } - }, - "node_modules/npm/node_modules/libnpmsearch": { - "version": "9.0.1", - "dev": true, - "inBundle": true, - "license": "ISC", - "dependencies": { - "npm-registry-fetch": "^19.0.0" - }, - "engines": { - "node": "^20.17.0 || >=22.9.0" - } - }, - "node_modules/npm/node_modules/libnpmteam": { - "version": "8.0.2", - "dev": true, - "inBundle": true, - "license": "ISC", - "dependencies": { - "aproba": "^2.0.0", - "npm-registry-fetch": "^19.0.0" - }, - "engines": { - "node": "^20.17.0 || >=22.9.0" - } - }, - "node_modules/npm/node_modules/libnpmversion": { - "version": "8.0.2", - "dev": true, - "inBundle": true, - "license": "ISC", - "dependencies": { - "@npmcli/git": "^7.0.0", - "@npmcli/run-script": "^10.0.0", - "json-parse-even-better-errors": "^4.0.0", - "proc-log": "^5.0.0", - "semver": "^7.3.7" - }, - "engines": { - "node": "^20.17.0 || >=22.9.0" - } - }, - "node_modules/npm/node_modules/lru-cache": { - "version": "11.2.1", - "dev": true, - "inBundle": true, - "license": "ISC", - "engines": { - "node": "20 || >=22" - } - }, - "node_modules/npm/node_modules/make-fetch-happen": { - "version": "15.0.2", - "dev": true, - "inBundle": true, - "license": "ISC", - "dependencies": { - "@npmcli/agent": "^4.0.0", - "cacache": "^20.0.1", - "http-cache-semantics": "^4.1.1", - "minipass": "^7.0.2", - "minipass-fetch": "^4.0.0", - "minipass-flush": "^1.0.5", - "minipass-pipeline": "^1.2.4", - "negotiator": "^1.0.0", - "proc-log": "^5.0.0", - "promise-retry": "^2.0.1", - "ssri": "^12.0.0" - }, - "engines": { - "node": "^20.17.0 || >=22.9.0" - } - }, - "node_modules/npm/node_modules/minimatch": { - "version": "10.0.3", - "dev": true, - "inBundle": true, - "license": "ISC", - "dependencies": { - "@isaacs/brace-expansion": "^5.0.0" - }, - "engines": { - "node": "20 || >=22" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/npm/node_modules/minipass": { - "version": "7.1.2", - "dev": true, - "inBundle": true, - "license": "ISC", - "engines": { - "node": ">=16 || 14 >=14.17" - } - }, - "node_modules/npm/node_modules/minipass-collect": { - "version": "2.0.1", - "dev": true, - "inBundle": true, - "license": "ISC", - "dependencies": { - "minipass": "^7.0.3" - }, - "engines": { - "node": ">=16 || 14 >=14.17" - } - }, - "node_modules/npm/node_modules/minipass-fetch": { - "version": "4.0.1", - "dev": true, - "inBundle": true, - "license": "MIT", - "dependencies": { - "minipass": "^7.0.3", - "minipass-sized": "^1.0.3", - "minizlib": "^3.0.1" - }, - "engines": { - "node": "^18.17.0 || >=20.5.0" - }, - "optionalDependencies": { - "encoding": "^0.1.13" - } - }, - "node_modules/npm/node_modules/minipass-flush": { - "version": "1.0.5", - "dev": true, - "inBundle": true, - "license": "ISC", - "dependencies": { - "minipass": "^3.0.0" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/npm/node_modules/minipass-flush/node_modules/minipass": { - "version": "3.3.6", - "dev": true, - "inBundle": true, - "license": "ISC", - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/npm/node_modules/minipass-pipeline": { - "version": "1.2.4", - "dev": true, - "inBundle": true, - "license": "ISC", - "dependencies": { - "minipass": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/npm/node_modules/minipass-pipeline/node_modules/minipass": { - "version": "3.3.6", - "dev": true, - "inBundle": true, - "license": "ISC", - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/npm/node_modules/minipass-sized": { - "version": "1.0.3", - "dev": true, - "inBundle": true, - "license": "ISC", - "dependencies": { - "minipass": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/npm/node_modules/minipass-sized/node_modules/minipass": { - "version": "3.3.6", - "dev": true, - "inBundle": true, - "license": "ISC", - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/npm/node_modules/minizlib": { - "version": "3.1.0", - "dev": true, - "inBundle": true, - "license": "MIT", - "dependencies": { - "minipass": "^7.1.2" - }, - "engines": { - "node": ">= 18" - } - }, - "node_modules/npm/node_modules/ms": { - "version": "2.1.3", - "dev": true, - "inBundle": true, - "license": "MIT" - }, - "node_modules/npm/node_modules/mute-stream": { - "version": "2.0.0", - "dev": true, - "inBundle": true, - "license": "ISC", - "engines": { - "node": "^18.17.0 || >=20.5.0" - } - }, - "node_modules/npm/node_modules/negotiator": { - "version": "1.0.0", - "dev": true, - "inBundle": true, - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/npm/node_modules/node-gyp": { - "version": "11.4.2", - "dev": true, - "inBundle": true, - "license": "MIT", - "dependencies": { - "env-paths": "^2.2.0", - "exponential-backoff": "^3.1.1", - "graceful-fs": "^4.2.6", - "make-fetch-happen": "^14.0.3", - "nopt": "^8.0.0", - "proc-log": "^5.0.0", - "semver": "^7.3.5", - "tar": "^7.4.3", - "tinyglobby": "^0.2.12", - "which": "^5.0.0" - }, - "bin": { - "node-gyp": "bin/node-gyp.js" - }, - "engines": { - "node": "^18.17.0 || >=20.5.0" - } - }, - "node_modules/npm/node_modules/node-gyp/node_modules/@npmcli/agent": { - "version": "3.0.0", - "dev": true, - "inBundle": true, - "license": "ISC", - "dependencies": { - "agent-base": "^7.1.0", - "http-proxy-agent": "^7.0.0", - "https-proxy-agent": "^7.0.1", - "lru-cache": "^10.0.1", - "socks-proxy-agent": "^8.0.3" - }, - "engines": { - "node": "^18.17.0 || >=20.5.0" - } - }, - "node_modules/npm/node_modules/node-gyp/node_modules/cacache": { - "version": "19.0.1", - "dev": true, - "inBundle": true, - "license": "ISC", - "dependencies": { - "@npmcli/fs": "^4.0.0", - "fs-minipass": "^3.0.0", - "glob": "^10.2.2", - "lru-cache": "^10.0.1", - "minipass": "^7.0.3", - "minipass-collect": "^2.0.1", - "minipass-flush": "^1.0.5", - "minipass-pipeline": "^1.2.4", - "p-map": "^7.0.2", - "ssri": "^12.0.0", - "tar": "^7.4.3", - "unique-filename": "^4.0.0" - }, - "engines": { - "node": "^18.17.0 || >=20.5.0" - } - }, - "node_modules/npm/node_modules/node-gyp/node_modules/glob": { - "version": "10.4.5", - "dev": true, - "inBundle": true, - "license": "ISC", - "dependencies": { - "foreground-child": "^3.1.0", - "jackspeak": "^3.1.2", - "minimatch": "^9.0.4", - "minipass": "^7.1.2", - "package-json-from-dist": "^1.0.0", - "path-scurry": "^1.11.1" - }, - "bin": { - "glob": "dist/esm/bin.mjs" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/npm/node_modules/node-gyp/node_modules/jackspeak": { - "version": "3.4.3", - "dev": true, - "inBundle": true, - "license": "BlueOak-1.0.0", - "dependencies": { - "@isaacs/cliui": "^8.0.2" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - }, - "optionalDependencies": { - "@pkgjs/parseargs": "^0.11.0" - } - }, - "node_modules/npm/node_modules/node-gyp/node_modules/lru-cache": { - "version": "10.4.3", - "dev": true, - "inBundle": true, - "license": "ISC" - }, - "node_modules/npm/node_modules/node-gyp/node_modules/make-fetch-happen": { - "version": "14.0.3", - "dev": true, - "inBundle": true, - "license": "ISC", - "dependencies": { - "@npmcli/agent": "^3.0.0", - "cacache": "^19.0.1", - "http-cache-semantics": "^4.1.1", - "minipass": "^7.0.2", - "minipass-fetch": "^4.0.0", - "minipass-flush": "^1.0.5", - "minipass-pipeline": "^1.2.4", - "negotiator": "^1.0.0", - "proc-log": "^5.0.0", - "promise-retry": "^2.0.1", - "ssri": "^12.0.0" - }, - "engines": { - "node": "^18.17.0 || >=20.5.0" - } - }, - "node_modules/npm/node_modules/node-gyp/node_modules/minimatch": { - "version": "9.0.5", - "dev": true, - "inBundle": true, - "license": "ISC", - "dependencies": { - "brace-expansion": "^2.0.1" - }, - "engines": { - "node": ">=16 || 14 >=14.17" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/npm/node_modules/node-gyp/node_modules/path-scurry": { - "version": "1.11.1", - "dev": true, - "inBundle": true, - "license": "BlueOak-1.0.0", - "dependencies": { - "lru-cache": "^10.2.0", - "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" - }, - "engines": { - "node": ">=16 || 14 >=14.18" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/npm/node_modules/nopt": { - "version": "8.1.0", - "dev": true, - "inBundle": true, - "license": "ISC", - "dependencies": { - "abbrev": "^3.0.0" - }, - "bin": { - "nopt": "bin/nopt.js" - }, - "engines": { - "node": "^18.17.0 || >=20.5.0" - } - }, - "node_modules/npm/node_modules/normalize-package-data": { - "version": "8.0.0", - "dev": true, - "inBundle": true, - "license": "BSD-2-Clause", - "dependencies": { - "hosted-git-info": "^9.0.0", - "semver": "^7.3.5", - "validate-npm-package-license": "^3.0.4" - }, - "engines": { - "node": "^20.17.0 || >=22.9.0" - } - }, - "node_modules/npm/node_modules/npm-audit-report": { - "version": "6.0.0", - "dev": true, - "inBundle": true, - "license": "ISC", - "engines": { - "node": "^18.17.0 || >=20.5.0" - } - }, - "node_modules/npm/node_modules/npm-bundled": { - "version": "4.0.0", - "dev": true, - "inBundle": true, - "license": "ISC", - "dependencies": { - "npm-normalize-package-bin": "^4.0.0" - }, - "engines": { - "node": "^18.17.0 || >=20.5.0" - } - }, - "node_modules/npm/node_modules/npm-install-checks": { - "version": "7.1.2", - "dev": true, - "inBundle": true, - "license": "BSD-2-Clause", - "dependencies": { - "semver": "^7.1.1" - }, - "engines": { - "node": "^18.17.0 || >=20.5.0" - } - }, - "node_modules/npm/node_modules/npm-normalize-package-bin": { - "version": "4.0.0", - "dev": true, - "inBundle": true, - "license": "ISC", - "engines": { - "node": "^18.17.0 || >=20.5.0" - } - }, - "node_modules/npm/node_modules/npm-package-arg": { - "version": "13.0.0", - "dev": true, - "inBundle": true, - "license": "ISC", - "dependencies": { - "hosted-git-info": "^9.0.0", - "proc-log": "^5.0.0", - "semver": "^7.3.5", - "validate-npm-package-name": "^6.0.0" - }, - "engines": { - "node": "^20.17.0 || >=22.9.0" - } - }, - "node_modules/npm/node_modules/npm-packlist": { - "version": "10.0.1", - "dev": true, - "inBundle": true, - "license": "ISC", - "dependencies": { - "ignore-walk": "^8.0.0" - }, - "engines": { - "node": "^20.17.0 || >=22.9.0" - } - }, - "node_modules/npm/node_modules/npm-pick-manifest": { - "version": "11.0.1", - "dev": true, - "inBundle": true, - "license": "ISC", - "dependencies": { - "npm-install-checks": "^7.1.0", - "npm-normalize-package-bin": "^4.0.0", - "npm-package-arg": "^13.0.0", - "semver": "^7.3.5" - }, - "engines": { - "node": "^20.17.0 || >=22.9.0" - } - }, - "node_modules/npm/node_modules/npm-profile": { - "version": "12.0.0", - "dev": true, - "inBundle": true, - "license": "ISC", - "dependencies": { - "npm-registry-fetch": "^19.0.0", - "proc-log": "^5.0.0" - }, - "engines": { - "node": "^20.17.0 || >=22.9.0" - } - }, - "node_modules/npm/node_modules/npm-registry-fetch": { - "version": "19.0.0", - "dev": true, - "inBundle": true, - "license": "ISC", - "dependencies": { - "@npmcli/redact": "^3.0.0", - "jsonparse": "^1.3.1", - "make-fetch-happen": "^15.0.0", - "minipass": "^7.0.2", - "minipass-fetch": "^4.0.0", - "minizlib": "^3.0.1", - "npm-package-arg": "^13.0.0", - "proc-log": "^5.0.0" - }, - "engines": { - "node": "^20.17.0 || >=22.9.0" - } - }, - "node_modules/npm/node_modules/npm-user-validate": { - "version": "3.0.0", - "dev": true, - "inBundle": true, - "license": "BSD-2-Clause", - "engines": { - "node": "^18.17.0 || >=20.5.0" - } - }, - "node_modules/npm/node_modules/p-map": { - "version": "7.0.3", - "dev": true, - "inBundle": true, - "license": "MIT", - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/npm/node_modules/package-json-from-dist": { - "version": "1.0.1", - "dev": true, - "inBundle": true, - "license": "BlueOak-1.0.0" - }, - "node_modules/npm/node_modules/pacote": { - "version": "21.0.3", - "dev": true, - "inBundle": true, - "license": "ISC", - "dependencies": { - "@npmcli/git": "^7.0.0", - "@npmcli/installed-package-contents": "^3.0.0", - "@npmcli/package-json": "^7.0.0", - "@npmcli/promise-spawn": "^8.0.0", - "@npmcli/run-script": "^10.0.0", - "cacache": "^20.0.0", - "fs-minipass": "^3.0.0", - "minipass": "^7.0.2", - "npm-package-arg": "^13.0.0", - "npm-packlist": "^10.0.1", - "npm-pick-manifest": "^11.0.1", - "npm-registry-fetch": "^19.0.0", - "proc-log": "^5.0.0", - "promise-retry": "^2.0.1", - "sigstore": "^4.0.0", - "ssri": "^12.0.0", - "tar": "^7.4.3" - }, - "bin": { - "pacote": "bin/index.js" - }, - "engines": { - "node": "^20.17.0 || >=22.9.0" - } - }, - "node_modules/npm/node_modules/parse-conflict-json": { - "version": "4.0.0", - "dev": true, - "inBundle": true, - "license": "ISC", - "dependencies": { - "json-parse-even-better-errors": "^4.0.0", - "just-diff": "^6.0.0", - "just-diff-apply": "^5.2.0" - }, - "engines": { - "node": "^18.17.0 || >=20.5.0" - } - }, - "node_modules/npm/node_modules/path-key": { - "version": "3.1.1", - "dev": true, - "inBundle": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/npm/node_modules/path-scurry": { - "version": "2.0.0", - "dev": true, - "inBundle": true, - "license": "BlueOak-1.0.0", - "dependencies": { - "lru-cache": "^11.0.0", - "minipass": "^7.1.2" - }, - "engines": { - "node": "20 || >=22" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/npm/node_modules/postcss-selector-parser": { - "version": "7.1.0", - "dev": true, - "inBundle": true, - "license": "MIT", - "dependencies": { - "cssesc": "^3.0.0", - "util-deprecate": "^1.0.2" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/npm/node_modules/proc-log": { - "version": "5.0.0", - "dev": true, - "inBundle": true, - "license": "ISC", - "engines": { - "node": "^18.17.0 || >=20.5.0" - } - }, - "node_modules/npm/node_modules/proggy": { - "version": "3.0.0", - "dev": true, - "inBundle": true, - "license": "ISC", - "engines": { - "node": "^18.17.0 || >=20.5.0" - } - }, - "node_modules/npm/node_modules/promise-all-reject-late": { - "version": "1.0.1", - "dev": true, - "inBundle": true, - "license": "ISC", - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/npm/node_modules/promise-call-limit": { - "version": "3.0.2", - "dev": true, - "inBundle": true, - "license": "ISC", - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/npm/node_modules/promise-retry": { - "version": "2.0.1", - "dev": true, - "inBundle": true, - "license": "MIT", - "dependencies": { - "err-code": "^2.0.2", - "retry": "^0.12.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/npm/node_modules/promzard": { - "version": "2.0.0", - "dev": true, - "inBundle": true, - "license": "ISC", - "dependencies": { - "read": "^4.0.0" - }, - "engines": { - "node": "^18.17.0 || >=20.5.0" - } - }, - "node_modules/npm/node_modules/qrcode-terminal": { - "version": "0.12.0", - "dev": true, - "inBundle": true, - "bin": { - "qrcode-terminal": "bin/qrcode-terminal.js" - } - }, - "node_modules/npm/node_modules/read": { - "version": "4.1.0", - "dev": true, - "inBundle": true, - "license": "ISC", - "dependencies": { - "mute-stream": "^2.0.0" - }, - "engines": { - "node": "^18.17.0 || >=20.5.0" - } - }, - "node_modules/npm/node_modules/read-cmd-shim": { - "version": "5.0.0", - "dev": true, - "inBundle": true, - "license": "ISC", - "engines": { - "node": "^18.17.0 || >=20.5.0" - } - }, - "node_modules/npm/node_modules/retry": { - "version": "0.12.0", - "dev": true, - "inBundle": true, - "license": "MIT", - "engines": { - "node": ">= 4" - } - }, - "node_modules/npm/node_modules/safer-buffer": { - "version": "2.1.2", - "dev": true, - "inBundle": true, - "license": "MIT", - "optional": true - }, - "node_modules/npm/node_modules/semver": { - "version": "7.7.2", - "dev": true, - "inBundle": true, - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/npm/node_modules/shebang-command": { - "version": "2.0.0", - "dev": true, - "inBundle": true, - "license": "MIT", - "dependencies": { - "shebang-regex": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/npm/node_modules/shebang-regex": { - "version": "3.0.0", - "dev": true, - "inBundle": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/npm/node_modules/signal-exit": { - "version": "4.1.0", - "dev": true, - "inBundle": true, - "license": "ISC", - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/npm/node_modules/sigstore": { - "version": "4.0.0", - "dev": true, - "inBundle": true, - "license": "Apache-2.0", - "dependencies": { - "@sigstore/bundle": "^4.0.0", - "@sigstore/core": "^3.0.0", - "@sigstore/protobuf-specs": "^0.5.0", - "@sigstore/sign": "^4.0.0", - "@sigstore/tuf": "^4.0.0", - "@sigstore/verify": "^3.0.0" - }, - "engines": { - "node": "^20.17.0 || >=22.9.0" - } - }, - "node_modules/npm/node_modules/smart-buffer": { - "version": "4.2.0", - "dev": true, - "inBundle": true, - "license": "MIT", - "engines": { - "node": ">= 6.0.0", - "npm": ">= 3.0.0" - } - }, - "node_modules/npm/node_modules/socks": { - "version": "2.8.7", - "dev": true, - "inBundle": true, - "license": "MIT", - "dependencies": { - "ip-address": "^10.0.1", - "smart-buffer": "^4.2.0" - }, - "engines": { - "node": ">= 10.0.0", - "npm": ">= 3.0.0" - } - }, - "node_modules/npm/node_modules/socks-proxy-agent": { - "version": "8.0.5", - "dev": true, - "inBundle": true, - "license": "MIT", - "dependencies": { - "agent-base": "^7.1.2", - "debug": "^4.3.4", - "socks": "^2.8.3" - }, - "engines": { - "node": ">= 14" - } - }, - "node_modules/npm/node_modules/spdx-correct": { - "version": "3.2.0", - "dev": true, - "inBundle": true, - "license": "Apache-2.0", - "dependencies": { - "spdx-expression-parse": "^3.0.0", - "spdx-license-ids": "^3.0.0" - } - }, - "node_modules/npm/node_modules/spdx-correct/node_modules/spdx-expression-parse": { - "version": "3.0.1", - "dev": true, - "inBundle": true, - "license": "MIT", - "dependencies": { - "spdx-exceptions": "^2.1.0", - "spdx-license-ids": "^3.0.0" - } - }, - "node_modules/npm/node_modules/spdx-exceptions": { - "version": "2.5.0", - "dev": true, - "inBundle": true, - "license": "CC-BY-3.0" - }, - "node_modules/npm/node_modules/spdx-expression-parse": { - "version": "4.0.0", - "dev": true, - "inBundle": true, - "license": "MIT", - "dependencies": { - "spdx-exceptions": "^2.1.0", - "spdx-license-ids": "^3.0.0" - } - }, - "node_modules/npm/node_modules/spdx-license-ids": { - "version": "3.0.22", - "dev": true, - "inBundle": true, - "license": "CC0-1.0" - }, - "node_modules/npm/node_modules/ssri": { - "version": "12.0.0", - "dev": true, - "inBundle": true, - "license": "ISC", - "dependencies": { - "minipass": "^7.0.3" - }, - "engines": { - "node": "^18.17.0 || >=20.5.0" - } - }, - "node_modules/npm/node_modules/string-width": { - "version": "4.2.3", - "dev": true, - "inBundle": true, - "license": "MIT", - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/npm/node_modules/string-width-cjs": { - "name": "string-width", - "version": "4.2.3", - "dev": true, - "inBundle": true, - "license": "MIT", - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/npm/node_modules/strip-ansi": { - "version": "6.0.1", - "dev": true, - "inBundle": true, - "license": "MIT", - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/npm/node_modules/strip-ansi-cjs": { - "name": "strip-ansi", - "version": "6.0.1", - "dev": true, - "inBundle": true, - "license": "MIT", - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/npm/node_modules/supports-color": { - "version": "10.2.2", - "dev": true, - "inBundle": true, - "license": "MIT", - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/chalk/supports-color?sponsor=1" - } - }, - "node_modules/npm/node_modules/tar": { - "version": "7.5.1", - "dev": true, - "inBundle": true, - "license": "ISC", - "dependencies": { - "@isaacs/fs-minipass": "^4.0.0", - "chownr": "^3.0.0", - "minipass": "^7.1.2", - "minizlib": "^3.1.0", - "yallist": "^5.0.0" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/npm/node_modules/tar/node_modules/yallist": { - "version": "5.0.0", - "dev": true, - "inBundle": true, - "license": "BlueOak-1.0.0", - "engines": { - "node": ">=18" - } - }, - "node_modules/npm/node_modules/text-table": { - "version": "0.2.0", - "dev": true, - "inBundle": true, - "license": "MIT" - }, - "node_modules/npm/node_modules/tiny-relative-date": { - "version": "2.0.2", - "dev": true, - "inBundle": true, - "license": "MIT" - }, - "node_modules/npm/node_modules/tinyglobby": { - "version": "0.2.15", - "dev": true, - "inBundle": true, - "license": "MIT", - "dependencies": { - "fdir": "^6.5.0", - "picomatch": "^4.0.3" - }, - "engines": { - "node": ">=12.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/SuperchupuDev" - } - }, - "node_modules/npm/node_modules/tinyglobby/node_modules/fdir": { - "version": "6.5.0", - "dev": true, - "inBundle": true, - "license": "MIT", - "engines": { - "node": ">=12.0.0" - }, - "peerDependencies": { - "picomatch": "^3 || ^4" - }, - "peerDependenciesMeta": { - "picomatch": { - "optional": true - } - } - }, - "node_modules/npm/node_modules/tinyglobby/node_modules/picomatch": { - "version": "4.0.3", - "dev": true, - "inBundle": true, - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, - "node_modules/npm/node_modules/treeverse": { - "version": "3.0.0", - "dev": true, - "inBundle": true, - "license": "ISC", - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" - } - }, - "node_modules/npm/node_modules/tuf-js": { - "version": "4.0.0", - "dev": true, - "inBundle": true, - "license": "MIT", - "dependencies": { - "@tufjs/models": "4.0.0", - "debug": "^4.4.1", - "make-fetch-happen": "^15.0.0" - }, - "engines": { - "node": "^20.17.0 || >=22.9.0" - } - }, - "node_modules/npm/node_modules/unique-filename": { - "version": "4.0.0", - "dev": true, - "inBundle": true, - "license": "ISC", - "dependencies": { - "unique-slug": "^5.0.0" - }, - "engines": { - "node": "^18.17.0 || >=20.5.0" - } - }, - "node_modules/npm/node_modules/unique-slug": { - "version": "5.0.0", - "dev": true, - "inBundle": true, - "license": "ISC", - "dependencies": { - "imurmurhash": "^0.1.4" - }, - "engines": { - "node": "^18.17.0 || >=20.5.0" - } - }, - "node_modules/npm/node_modules/util-deprecate": { - "version": "1.0.2", - "dev": true, - "inBundle": true, - "license": "MIT" - }, - "node_modules/npm/node_modules/validate-npm-package-license": { - "version": "3.0.4", - "dev": true, - "inBundle": true, - "license": "Apache-2.0", - "dependencies": { - "spdx-correct": "^3.0.0", - "spdx-expression-parse": "^3.0.0" - } - }, - "node_modules/npm/node_modules/validate-npm-package-license/node_modules/spdx-expression-parse": { - "version": "3.0.1", - "dev": true, - "inBundle": true, - "license": "MIT", - "dependencies": { - "spdx-exceptions": "^2.1.0", - "spdx-license-ids": "^3.0.0" - } - }, - "node_modules/npm/node_modules/validate-npm-package-name": { - "version": "6.0.2", - "dev": true, - "inBundle": true, - "license": "ISC", - "engines": { - "node": "^18.17.0 || >=20.5.0" - } - }, - "node_modules/npm/node_modules/walk-up-path": { - "version": "4.0.0", - "dev": true, - "inBundle": true, - "license": "ISC", - "engines": { - "node": "20 || >=22" - } - }, - "node_modules/npm/node_modules/which": { - "version": "5.0.0", - "dev": true, - "inBundle": true, - "license": "ISC", - "dependencies": { - "isexe": "^3.1.1" - }, - "bin": { - "node-which": "bin/which.js" - }, - "engines": { - "node": "^18.17.0 || >=20.5.0" - } - }, - "node_modules/npm/node_modules/wrap-ansi": { - "version": "8.1.0", - "dev": true, - "inBundle": true, - "license": "MIT", - "dependencies": { - "ansi-styles": "^6.1.0", - "string-width": "^5.0.1", - "strip-ansi": "^7.0.1" + "supports-color": "^7.1.0" }, "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + "node": ">=10" } }, - "node_modules/npm/node_modules/wrap-ansi-cjs": { - "name": "wrap-ansi", - "version": "7.0.0", + "node_modules/istanbul-lib-source-maps": { + "version": "5.0.6", + "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-5.0.6.tgz", + "integrity": "sha512-yg2d+Em4KizZC5niWhQaIomgf5WlL4vOOjZ5xGCmF8SnPE/mDWWXgvRExdcpCgh9lLRRa1/fSYp2ymmbJ1pI+A==", "dev": true, - "inBundle": true, - "license": "MIT", "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" + "@jridgewell/trace-mapping": "^0.3.23", + "debug": "^4.1.1", + "istanbul-lib-coverage": "^3.0.0" }, "engines": { "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" } }, - "node_modules/npm/node_modules/wrap-ansi-cjs/node_modules/ansi-styles": { - "version": "4.3.0", + "node_modules/istanbul-reports": { + "version": "3.1.7", + "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.7.tgz", + "integrity": "sha512-BewmUXImeuRk2YY0PVbxgKAysvhRPUQE0h5QRM++nVWyubKGV0l8qQ5op8+B2DOmwSe63Jivj0BjkPQVf8fP5g==", "dev": true, - "inBundle": true, - "license": "MIT", "dependencies": { - "color-convert": "^2.0.1" + "html-escaper": "^2.0.0", + "istanbul-lib-report": "^3.0.0" }, "engines": { "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/npm/node_modules/wrap-ansi/node_modules/ansi-regex": { - "version": "6.2.2", + "node_modules/jackspeak": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz", + "integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==", "dev": true, - "inBundle": true, - "license": "MIT", - "engines": { - "node": ">=12" + "dependencies": { + "@isaacs/cliui": "^8.0.2" }, "funding": { - "url": "https://github.com/chalk/ansi-regex?sponsor=1" + "url": "https://github.com/sponsors/isaacs" + }, + "optionalDependencies": { + "@pkgjs/parseargs": "^0.11.0" } }, - "node_modules/npm/node_modules/wrap-ansi/node_modules/emoji-regex": { - "version": "9.2.2", + "node_modules/loupe": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/loupe/-/loupe-3.1.3.tgz", + "integrity": "sha512-kkIp7XSkP78ZxJEsSxW3712C6teJVoeHHwgo9zJ380de7IYyJ2ISlxojcH2pC5OFLewESmnRi/+XCDIEEVyoug==", "dev": true, - "inBundle": true, "license": "MIT" }, - "node_modules/npm/node_modules/wrap-ansi/node_modules/string-width": { - "version": "5.1.2", + "node_modules/magic-string": { + "version": "0.30.17", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.17.tgz", + "integrity": "sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA==", "dev": true, - "inBundle": true, "license": "MIT", "dependencies": { - "eastasianwidth": "^0.2.0", - "emoji-regex": "^9.2.2", - "strip-ansi": "^7.0.1" + "@jridgewell/sourcemap-codec": "^1.5.0" + } + }, + "node_modules/magicast": { + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/magicast/-/magicast-0.3.5.tgz", + "integrity": "sha512-L0WhttDl+2BOsybvEOLK7fW3UA0OQ0IQ2d6Zl2x/a6vVRs3bAY0ECOSHHeL5jD+SbOpOCUEi0y1DgHEn9Qn1AQ==", + "dev": true, + "dependencies": { + "@babel/parser": "^7.25.4", + "@babel/types": "^7.25.4", + "source-map-js": "^1.2.0" + } + }, + "node_modules/make-dir": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-4.0.0.tgz", + "integrity": "sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==", + "dev": true, + "dependencies": { + "semver": "^7.5.3" }, "engines": { - "node": ">=12" + "node": ">=10" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/npm/node_modules/wrap-ansi/node_modules/strip-ansi": { + "node_modules/minipass": { "version": "7.1.2", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", + "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", "dev": true, - "inBundle": true, - "license": "MIT", - "dependencies": { - "ansi-regex": "^6.0.1" - }, "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/strip-ansi?sponsor=1" + "node": ">=16 || 14 >=14.17" } }, - "node_modules/npm/node_modules/write-file-atomic": { - "version": "6.0.0", + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true + }, + "node_modules/nanoid": { + "version": "3.3.8", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.8.tgz", + "integrity": "sha512-WNLf5Sd8oZxOm+TzppcYk8gVOgP+l58xNy58D0nbUnOxOWRWvlcCV4kUF7ltmI6PsrLl/BgKEyS4mqsGChFN0w==", "dev": true, - "inBundle": true, - "license": "ISC", - "dependencies": { - "imurmurhash": "^0.1.4", - "signal-exit": "^4.0.1" + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "bin": { + "nanoid": "bin/nanoid.cjs" }, "engines": { - "node": "^18.17.0 || >=20.5.0" + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" } }, - "node_modules/npm/node_modules/yallist": { - "version": "4.0.0", - "dev": true, - "inBundle": true, - "license": "ISC" - }, "node_modules/on-exit-leak-free": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/on-exit-leak-free/-/on-exit-leak-free-2.1.2.tgz", @@ -5414,23 +2055,6 @@ "node": ">=14.0.0" } }, - "node_modules/optionator": { - "version": "0.8.3", - "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.3.tgz", - "integrity": "sha512-+IW9pACdk3XWmmTXG8m3upGUJst5XRGzxMRjXzAuJ1XnIFNvfhjjIuYkDvysnPQ7qzqVzLt78BCruntqRhWQbA==", - "license": "MIT", - "dependencies": { - "deep-is": "~0.1.3", - "fast-levenshtein": "~2.0.6", - "levn": "~0.3.0", - "prelude-ls": "~1.1.2", - "type-check": "~0.3.2", - "word-wrap": "~1.2.3" - }, - "engines": { - "node": ">= 0.8.0" - } - }, "node_modules/package-json-from-dist": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz", @@ -5492,19 +2116,6 @@ "dev": true, "license": "ISC" }, - "node_modules/picomatch": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", - "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, "node_modules/pino": { "version": "8.21.0", "resolved": "https://registry.npmjs.org/pino/-/pino-8.21.0.tgz", @@ -5569,14 +2180,6 @@ "node": "^10 || ^12 || >=14" } }, - "node_modules/prelude-ls": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz", - "integrity": "sha512-ESF23V4SKG6lVSGZgYNpbsiaAkdab6ZgOxe52p7+Kid3W3u3bxR4Vfd/o21dmN7jSt0IwgZ4v5MUd26FEtXE9w==", - "engines": { - "node": ">= 0.8.0" - } - }, "node_modules/prettier": { "version": "2.8.8", "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.8.8.tgz", @@ -5592,34 +2195,6 @@ "url": "https://github.com/prettier/prettier?sponsor=1" } }, - "node_modules/pretty-format": { - "version": "30.0.5", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-30.0.5.tgz", - "integrity": "sha512-D1tKtYvByrBkFLe2wHJl2bwMJIiT8rW+XA+TiataH79/FszLQMrpGEvzUVkzPau7OCO0Qnrhpe87PqtOAIB8Yw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/schemas": "30.0.5", - "ansi-styles": "^5.2.0", - "react-is": "^18.3.1" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/pretty-format/node_modules/ansi-styles": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", - "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, "node_modules/process": { "version": "0.11.10", "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", @@ -5638,13 +2213,6 @@ "resolved": "https://registry.npmjs.org/quick-format-unescaped/-/quick-format-unescaped-4.0.4.tgz", "integrity": "sha512-tYC1Q1hgyRuHgloV/YXs2w15unPVh8qfu/qCTfhTYamaw7fyhumKa2yGpdSo87vY32rIclj+4fWYQXUMs9EHvg==" }, - "node_modules/react-is": { - "version": "18.3.1", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", - "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==", - "dev": true, - "license": "MIT" - }, "node_modules/readable-stream": { "version": "4.5.2", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-4.5.2.tgz", @@ -5773,16 +2341,6 @@ "dev": true, "license": "ISC" }, - "node_modules/slash": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", - "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, "node_modules/sonic-boom": { "version": "3.8.1", "resolved": "https://registry.npmjs.org/sonic-boom/-/sonic-boom-3.8.1.tgz", @@ -5791,16 +2349,6 @@ "atomic-sleep": "^1.0.0" } }, - "node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "license": "BSD-3-Clause", - "optional": true, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/source-map-js": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", @@ -5818,19 +2366,6 @@ "node": ">= 10.x" } }, - "node_modules/stack-utils": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.6.tgz", - "integrity": "sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "escape-string-regexp": "^2.0.0" - }, - "engines": { - "node": ">=10" - } - }, "node_modules/stackback": { "version": "0.0.2", "resolved": "https://registry.npmjs.org/stackback/-/stackback-0.0.2.tgz", @@ -5838,15 +2373,6 @@ "dev": true, "license": "MIT" }, - "node_modules/static-eval": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/static-eval/-/static-eval-2.0.2.tgz", - "integrity": "sha512-N/D219Hcr2bPjLxPiV+TQE++Tsmrady7TqAJugLy7Xk1EumfDWS/f5dtBbkRCGE7wKKXuYockQoj8Rm2/pVKyg==", - "license": "MIT", - "dependencies": { - "escodegen": "^1.8.1" - } - }, "node_modules/std-env": { "version": "3.8.0", "resolved": "https://registry.npmjs.org/std-env/-/std-env-3.8.0.tgz", @@ -5942,6 +2468,50 @@ "node": ">=18" } }, + "node_modules/test-exclude/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/test-exclude/node_modules/glob": { + "version": "10.4.5", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz", + "integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==", + "dev": true, + "dependencies": { + "foreground-child": "^3.1.0", + "jackspeak": "^3.1.2", + "minimatch": "^9.0.4", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^1.11.1" + }, + "bin": { + "glob": "dist/esm/bin.mjs" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/test-exclude/node_modules/minimatch": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "dev": true, + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/thread-stream": { "version": "2.7.0", "resolved": "https://registry.npmjs.org/thread-stream/-/thread-stream-2.7.0.tgz", @@ -5964,23 +2534,6 @@ "dev": true, "license": "MIT" }, - "node_modules/tinyglobby": { - "version": "0.2.15", - "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.15.tgz", - "integrity": "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "fdir": "^6.5.0", - "picomatch": "^4.0.3" - }, - "engines": { - "node": ">=12.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/SuperchupuDev" - } - }, "node_modules/tinypool": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/tinypool/-/tinypool-1.0.2.tgz", @@ -6020,31 +2573,6 @@ "node": ">=4" } }, - "node_modules/to-regex-range": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", - "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "is-number": "^7.0.0" - }, - "engines": { - "node": ">=8.0" - } - }, - "node_modules/type-check": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz", - "integrity": "sha512-ZCmOJdvOWDBYJlzAoFkC+Q0+bUyEOS1ltgp1MGU03fqHG+dbi9tBFU2Rd9QKiDZFAYrhPh2JUf7rZRIuHRKtOg==", - "license": "MIT", - "dependencies": { - "prelude-ls": "~1.1.2" - }, - "engines": { - "node": ">= 0.8.0" - } - }, "node_modules/typescript": { "version": "4.9.5", "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.5.tgz", @@ -6058,12 +2586,6 @@ "node": ">=4.2.0" } }, - "node_modules/underscore": { - "version": "1.12.1", - "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.12.1.tgz", - "integrity": "sha512-hEQt0+ZLDVUMhebKxL4x1BTtDY7bavVofhZ9KZ4aI26X9SRaE+Y3m83XUL1UP2jn8ynjndwCCpEHdUG+9pP1Tw==", - "license": "MIT" - }, "node_modules/undici": { "version": "6.21.2", "resolved": "https://registry.npmjs.org/undici/-/undici-6.21.2.tgz", @@ -6316,15 +2838,6 @@ "node": ">=8" } }, - "node_modules/word-wrap": { - "version": "1.2.5", - "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", - "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/wrap-ansi-cjs": { "name": "wrap-ansi", "version": "7.0.0", diff --git a/package.json b/package.json index dff70e4..aa632b6 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "flagsmith-nodejs", - "version": "7.0.0", + "version": "6.1.0", "description": "Flagsmith lets you manage features flags and remote config across web, mobile and server side applications. Deliver true Continuous Integration. Get builds out faster. Control who has access to new features.", "main": "./build/cjs/index.js", "type": "module", @@ -57,30 +57,20 @@ "build": "tsc -b tsconfig.cjs.json tsconfig.esm.json && echo '{\"type\": \"commonjs\"}'> build/cjs/package.json", "deploy": "npm i && npm run build && npm publish", "deploy:beta": "npm i && npm run build && npm publish --tag beta", - "prepare": "husky install", - "generate-evaluation-result-types": "curl -o evaluation-result.json https://raw.githubusercontent.com/Flagsmith/flagsmith/main/sdk/evaluation-result.json && npx json2ts -i evaluation-result.json -o flagsmith-engine/evaluation/evaluationResult/evaluationResult.types.ts && rm evaluation-result.json", - "generate-evaluation-context-types": "curl -o evaluation-context.json https://raw.githubusercontent.com/Flagsmith/flagsmith/main/sdk/evaluation-context.json && npx json2ts -i evaluation-context.json -o flagsmith-engine/evaluation/evaluationContext/evaluationContext.types.ts && rm evaluation-context.json", - "generate-engine-types": "npm run generate-evaluation-result-types && npm run generate-evaluation-context-types" + "prepare": "husky install" }, "dependencies": { - "jsonpath": "^1.1.1", "pino": "^8.8.0", "semver": "^7.3.7", "undici-types": "^6.19.8" }, "devDependencies": { - "@types/jest": "^30.0.0", - "@types/jsonpath": "^0.2.4", "@types/node": "^20.16.10", "@types/semver": "^7.3.9", "@types/uuid": "^8.3.4", "@vitest/coverage-v8": "^2.1.2", "esbuild": "^0.25.0", "husky": "^7.0.4", - "install": "^0.13.0", - "json-schema-to-typescript": "^15.0.4", - "jsonc-parser": "^3.3.1", - "npm": "^11.6.1", "prettier": "^2.2.1", "typescript": "^4.9.5", "undici": "^6.19.8", diff --git a/sdk/index.ts b/sdk/index.ts index 81cbde7..7438b99 100644 --- a/sdk/index.ts +++ b/sdk/index.ts @@ -1,21 +1,22 @@ import { Dispatcher } from 'undici-types'; - +import { + getEnvironmentFeatureStates, + getIdentityFeatureStates +} from '../flagsmith-engine/index.js'; +import { EnvironmentModel } from '../flagsmith-engine/index.js'; import { buildEnvironmentModel } from '../flagsmith-engine/environments/util.js'; +import { IdentityModel } from '../flagsmith-engine/index.js'; +import { TraitModel } from '../flagsmith-engine/index.js'; import { ANALYTICS_ENDPOINT, AnalyticsProcessor } from './analytics.js'; import { BaseOfflineHandler } from './offline_handlers.js'; -import { FlagsmithAPIError, FlagsmithClientError } from './errors.js'; +import { FlagsmithAPIError } from './errors.js'; import { DefaultFlag, Flags } from './models.js'; import { EnvironmentDataPollingManager } from './polling_manager.js'; import { Deferred, generateIdentitiesData, getUserAgent, retryFetch } from './utils.js'; -import { - SegmentModel, - EnvironmentModel, - IdentityModel, - TraitModel, - getEvaluationResult -} from '../flagsmith-engine/index.js'; +import { SegmentModel } from '../flagsmith-engine/index.js'; +import { getIdentitySegments } from '../flagsmith-engine/segments/evaluators.js'; import { Fetch, FlagsmithCache, @@ -24,7 +25,6 @@ import { TraitConfig } from './types.js'; import { pino, Logger } from 'pino'; -import { getEvaluationContext } from '../flagsmith-engine/evaluation/evaluationContext/mappers.js'; export { AnalyticsProcessor, AnalyticsProcessorOptions } from './analytics.js'; export { FlagsmithAPIError, FlagsmithClientError } from './errors.js'; @@ -278,13 +278,7 @@ export class Flagsmith { })) ); - const context = getEvaluationContext(environment, identityModel); - if (!context) { - throw new FlagsmithClientError('Local evaluation required to obtain identity segments'); - } - const evaluationResult = getEvaluationResult(context); - - return SegmentModel.fromSegmentResult(evaluationResult.segments, context); + return getIdentitySegments(environment, identityModel); } private async fetchEnvironment(): Promise { @@ -449,17 +443,14 @@ export class Flagsmith { private async getEnvironmentFlagsFromDocument(): Promise { const environment = await this.getEnvironment(); - const context = getEvaluationContext(environment); - if (!context) { - throw new FlagsmithClientError('Unable to get flags. No environment present.'); - } - const evaluationResult = getEvaluationResult(context); - const flags = Flags.fromEvaluationResult(evaluationResult); - + const flags = Flags.fromFeatureStateModels({ + featureStates: getEnvironmentFeatureStates(environment), + analyticsProcessor: this.analyticsProcessor, + defaultFlagHandler: this.defaultFlagHandler + }); if (!!this.cache) { await this.cache.set('flags', flags); } - return flags; } @@ -477,17 +468,14 @@ export class Flagsmith { })) ); - const context = getEvaluationContext(environment, identityModel); - if (!context) { - throw new FlagsmithClientError('Unable to get flags. No environment present.'); - } - const evaluationResult = getEvaluationResult(context); + const featureStates = getIdentityFeatureStates(environment, identityModel); - const flags = Flags.fromEvaluationResult( - evaluationResult, - this.defaultFlagHandler, - this.analyticsProcessor - ); + const flags = Flags.fromFeatureStateModels({ + featureStates: featureStates, + analyticsProcessor: this.analyticsProcessor, + defaultFlagHandler: this.defaultFlagHandler, + identityID: identityModel.djangoID || identityModel.compositeKey + }); if (!!this.cache) { await this.cache.set(`flags-${identifier}`, flags); diff --git a/sdk/models.ts b/sdk/models.ts index f6c48a1..90cffae 100644 --- a/sdk/models.ts +++ b/sdk/models.ts @@ -1,12 +1,7 @@ -import { - CustomFeatureMetadata, - FlagResultWithMetadata, - EvaluationResultWithMetadata -} from '../flagsmith-engine/evaluation/models.js'; import { FeatureStateModel } from '../flagsmith-engine/features/models.js'; import { AnalyticsProcessor } from './analytics.js'; -type FlagValue = string | number | boolean | undefined | null; +type FlagValue = string | number | boolean | undefined; /** * A Flagsmith feature. It has an enabled/disabled state, and an optional {@link FlagValue}. @@ -54,10 +49,6 @@ export class Flag extends BaseFlag { * The programmatic name for this feature, unique per Flagsmith project. */ featureName: string; - /** - * The reason for this feature, unique per Flagsmith project. - */ - reason?: string; constructor(params: { value: FlagValue; @@ -65,12 +56,10 @@ export class Flag extends BaseFlag { isDefault?: boolean; featureId: number; featureName: string; - reason?: string; }) { super(params.value, params.enabled, !!params.isDefault); this.featureId = params.featureId; this.featureName = params.featureName; - this.reason = params.reason; } static fromFeatureStateModel( @@ -90,8 +79,7 @@ export class Flag extends BaseFlag { enabled: flagData['enabled'], value: flagData['feature_state_value'] ?? flagData['value'], featureId: flagData['feature']['id'], - featureName: flagData['feature']['name'], - reason: flagData['feature']['reason'] + featureName: flagData['feature']['name'] }); } } @@ -111,33 +99,6 @@ export class Flags { this.analyticsProcessor = data.analyticsProcessor; } - static fromEvaluationResult( - evaluationResult: EvaluationResultWithMetadata, - defaultFlagHandler?: (v: string) => DefaultFlag, - analyticsProcessor?: AnalyticsProcessor - ): Flags { - const flags: { [key: string]: Flag } = {}; - for (const flag of Object.values(evaluationResult.flags)) { - const flagsmithId = flag.metadata?.flagsmithId; - if (!flagsmithId) { - continue; - } - - flags[flag.name] = new Flag({ - enabled: flag.enabled, - value: flag.value ?? null, - featureId: flagsmithId, - featureName: flag.name, - reason: flag.reason - }); - } - return new Flags({ - flags: flags, - defaultFlagHandler: defaultFlagHandler, - analyticsProcessor: analyticsProcessor - }); - } - static fromFeatureStateModels(data: { featureStates: FeatureStateModel[]; analyticsProcessor?: AnalyticsProcessor; diff --git a/tests/engine/e2e/engine.test.ts b/tests/engine/e2e/engine.test.ts index a2a00b1..87d045f 100644 --- a/tests/engine/e2e/engine.test.ts +++ b/tests/engine/e2e/engine.test.ts @@ -1,51 +1,46 @@ -import fs from 'fs'; -import path from 'path'; -import { fileURLToPath } from 'url'; -import { getEvaluationResult } from '../../../flagsmith-engine/index.js'; -import { Flags } from '../../../sdk/models.js'; -import { EvaluationContext } from '../../../flagsmith-engine/evaluation/evaluationContext/evaluationContext.types.js'; -import { parse as parseJsonc } from 'jsonc-parser'; -import { - EvaluationContextWithMetadata, - EvaluationResult -} from '../../../flagsmith-engine/evaluation/models.js'; +import { getIdentityFeatureStates } from '../../../flagsmith-engine/index.js'; +import { EnvironmentModel } from '../../../flagsmith-engine/environments/models.js'; +import { buildEnvironmentModel } from '../../../flagsmith-engine/environments/util.js'; +import { IdentityModel } from '../../../flagsmith-engine/identities/models.js'; +import { buildIdentityModel } from '../../../flagsmith-engine/identities/util.js'; +import * as testData from '../engine-tests/engine-test-data/data/environment_n9fbf9h3v4fFgH3U3ngWhb.json'; -const __filename = fileURLToPath(import.meta.url); -const __dirname = path.dirname(__filename); -const TEST_DATA_DIR = path.join(__dirname, '../engine-tests/engine-test-data/test_cases'); -interface TestCase { - context: EvaluationContext; - result: EvaluationResult; -} - -function getTestFiles(): string[] { - const files = fs.readdirSync(TEST_DATA_DIR); - return files - .filter(f => f.endsWith('.json') || f.endsWith('.jsonc')) - .map(f => path.join(TEST_DATA_DIR, f)); -} +function extractTestCases(data: any): { + environment: EnvironmentModel; + identity: IdentityModel; + response: any; +}[] { + const environmentModel = buildEnvironmentModel(data['environment']); + const test_data = data['identities_and_responses'].map((test_case: any) => { + const identity = buildIdentityModel(test_case['identity']); -function loadTestFile(filePath: string): TestCase { - const content = fs.readFileSync(filePath, 'utf-8'); - return parseJsonc(content); + return { + environment: environmentModel, + identity: identity, + response: test_case['response'] + }; + }); + return test_data; } -describe('Engine Integration Tests', () => { - const testFiles = getTestFiles(); +test('Test Engine', () => { + const testCases = extractTestCases(testData); + for (const testCase of testCases) { + const engine_response = getIdentityFeatureStates(testCase.environment, testCase.identity); + const sortedEngineFlags = engine_response.sort((a, b) => + a.feature.name > b.feature.name ? 1 : -1 + ); + const sortedAPIFlags = testCase.response['flags'].sort((a: any, b: any) => + a.feature.name > b.feature.name ? 1 : -1 + ); - if (testFiles.length === 0) { - throw new Error(`No test files found in ${TEST_DATA_DIR}`); - } - - testFiles.forEach(filePath => { - const testName = path.basename(filePath, path.extname(filePath)); + expect(sortedEngineFlags.length).toBe(sortedAPIFlags.length); - test(testName, () => { - const testCase = loadTestFile(filePath); - const engine_response = getEvaluationResult( - testCase.context as EvaluationContextWithMetadata + for (let i = 0; i < sortedEngineFlags.length; i++) { + expect(sortedEngineFlags[i].getValue(testCase.identity.djangoID)).toBe( + sortedAPIFlags[i]['feature_state_value'] ); - expect(engine_response).toStrictEqual(testCase.result); - }); - }); + expect(sortedEngineFlags[i].enabled).toBe(sortedAPIFlags[i]['enabled']); + } + } }); diff --git a/tests/engine/engine-tests/engine-test-data b/tests/engine/engine-tests/engine-test-data index 41c2021..95a077f 160000 --- a/tests/engine/engine-tests/engine-test-data +++ b/tests/engine/engine-tests/engine-test-data @@ -1 +1 @@ -Subproject commit 41c202145e375c712600e318c439456de5b221d7 +Subproject commit 95a077f8c260b730b20d084b9a67d426f2ecade5 diff --git a/tests/engine/unit/engine.test.ts b/tests/engine/unit/engine.test.ts index 8eb7d5d..9dea3a9 100644 --- a/tests/engine/unit/engine.test.ts +++ b/tests/engine/unit/engine.test.ts @@ -1,10 +1,8 @@ import { - evaluateFeatures, - evaluateSegments, - getEvaluationResult, - isHigherPriority, - SegmentOverrides, - shouldApplyOverride + getEnvironmentFeatureState, + getEnvironmentFeatureStates, + getIdentityFeatureState, + getIdentityFeatureStates } from '../../../flagsmith-engine/index.js'; import { CONSTANTS } from '../../../flagsmith-engine/features/constants.js'; import { FeatureModel, FeatureStateModel } from '../../../flagsmith-engine/features/models.js'; @@ -13,355 +11,101 @@ import { environment, environmentWithSegmentOverride, feature1, + getEnvironmentFeatureStateForFeature, identity, identityInSegment, segmentConditionProperty, segmentConditionStringValue } from './utils.js'; -import { getEvaluationContext } from '../../../flagsmith-engine/evaluation/evaluationContext/mappers.js'; -import { TARGETING_REASONS } from '../../../flagsmith-engine/features/types.js'; -import { EvaluationContext } from '../../../flagsmith-engine/evaluation/evaluationContext/evaluationContext.types.js'; -import { IDENTITY_OVERRIDE_SEGMENT_NAME } from '../../../flagsmith-engine/segments/constants.js'; -test('test_get_evaluation_result_without_any_override', () => { - const context = getEvaluationContext(environment(), identity()); - const result = getEvaluationResult(context); +test('test_identity_get_feature_state_without_any_override', () => { + const feature_state = getIdentityFeatureState(environment(), identity(), feature1().name); - const flag = Object.values(result.flags).find(f => f.name === feature1().name); - expect(flag).toBeDefined(); - expect(flag?.name).toBe(feature1().name); - expect(flag?.feature_key).toBe(feature1().id.toString()); - expect(flag?.reason).toBe(TARGETING_REASONS.DEFAULT); + expect(feature_state.feature).toStrictEqual(feature1()); }); -test('test_get_evaluation_result_with_identity_override_and_no_segment_override', () => { +test('test_identity_get_feature_state_without_any_override_no_fs', () => { + expect(() => { + getIdentityFeatureState(environment(), identity(), 'nonExistentName'); + }).toThrowError('Feature State Not Found'); +}); + +test('test_identity_get_all_feature_states_no_segments', () => { const env = environment(); const ident = identity(); const overridden_feature = new FeatureModel(3, 'overridden_feature', CONSTANTS.STANDARD); env.featureStates.push(new FeatureStateModel(overridden_feature, false, 3)); - ident.identityFeatures = [new FeatureStateModel(overridden_feature, true, 4)]; - env.identityOverrides = [ident]; - - const context = getEvaluationContext(env, ident); - const result = getEvaluationResult(context); - expect(Object.keys(result.flags).length).toBe(3); - - for (const flag of Object.values(result.flags)) { - const environmentFeature = Object.values(context.features || {}).find( - f => f.name === flag.name - ); + ident.identityFeatures = [new FeatureStateModel(overridden_feature, true, 4)]; - const expected = flag.name === 'overridden_feature' ? true : environmentFeature?.enabled; + const featureStates = getIdentityFeatureStates(env, ident); - expect(flag.enabled).toBe(expected); - expect(flag.reason).toBe( - flag.name === 'overridden_feature' - ? `${TARGETING_REASONS.TARGETING_MATCH}; segment=${IDENTITY_OVERRIDE_SEGMENT_NAME}` - : TARGETING_REASONS.DEFAULT + expect(featureStates.length).toBe(3); + for (const featuresState of featureStates) { + const environmentFeatureState = getEnvironmentFeatureStateForFeature( + env, + featuresState.feature ); + const expected = + environmentFeatureState?.feature == overridden_feature + ? true + : environmentFeatureState?.enabled; + expect(featuresState.enabled).toBe(expected); } }); test('test_identity_get_all_feature_states_with_traits', () => { const trait_models = new TraitModel(segmentConditionProperty, segmentConditionStringValue); - const context = getEvaluationContext(environmentWithSegmentOverride(), identityInSegment(), [ - trait_models - ]); - - const result = getEvaluationResult(context); - - const overriddenFlag = Object.values(result.flags).find(f => f.value === 'segment_override'); - expect(overriddenFlag).toBeDefined(); - expect(overriddenFlag?.value).toBe('segment_override'); - expect(overriddenFlag?.reason).toEqual( - `${TARGETING_REASONS.TARGETING_MATCH}; segment=test name` + const featureStates = getIdentityFeatureStates( + environmentWithSegmentOverride(), + identityInSegment(), + [trait_models] ); + expect(featureStates[0].getValue()).toBe('segment_override'); }); -test('test_environment_get_all_feature_states', () => { - const env = environment(); - const context = getEvaluationContext(env); - const result = getEvaluationResult(context); - - expect(Object.keys(result.flags).length).toBe(Object.keys(context.features || {}).length); - - Object.values(result.flags).forEach(flag => { - expect(flag.reason).toBe(TARGETING_REASONS.DEFAULT); - }); - - for (const flag of Object.values(result.flags)) { - const envFeature = Object.values(context.features || {}).find(f => f.name === flag.name); - expect(flag.enabled).toBe(envFeature?.enabled); - expect(flag.value).toBe(envFeature?.value); - } -}); - -test('isHigherPriority should handle undefined priorities correctly', () => { - expect(isHigherPriority(1, 2)).toBe(true); - expect(isHigherPriority(2, 1)).toBe(false); - expect(isHigherPriority(undefined, 5)).toBe(false); - expect(isHigherPriority(5, undefined)).toBe(true); - expect(isHigherPriority(undefined, undefined)).toBe(false); -}); +test('test_identity_get_all_feature_states_with_traits_hideDisabledFlags', () => { + const trait_models = new TraitModel(segmentConditionProperty, segmentConditionStringValue); -test('shouldApplyOverride with priority conflicts', () => { - const existingOverrides: SegmentOverrides = { - feature1: { - feature: { - key: 'key', - feature_key: 'feature1', - name: 'name', - enabled: true, - value: 'value', - priority: 5 - }, - segmentName: 'segment1' - } - }; + const env = environmentWithSegmentOverride(); + env.project.hideDisabledFlags = true; - expect(shouldApplyOverride({ feature_key: 'feature1', priority: 2 }, existingOverrides)).toBe( - true - ); - expect(shouldApplyOverride({ feature_key: 'feature1', priority: 10 }, existingOverrides)).toBe( - false - ); + const featureStates = getIdentityFeatureStates(env, identityInSegment(), [trait_models]); + expect(featureStates.length).toBe(0); }); -test('evaluateSegments handles segments with identity identifier matching', () => { - const context: EvaluationContext = { - environment: { - key: 'test-env', - name: 'Test Environment' - }, - identity: { - key: 'test-user', - identifier: 'test-user' - }, - segments: { - '1': { - key: '1', - name: 'segment_with_no_overrides', - rules: [ - { - type: 'ALL', - conditions: [ - { - property: '$.identity.identifier', - operator: 'EQUAL', - value: 'test-user' - } - ] - } - ], - overrides: [] - }, - '2': { - key: '2', - name: 'segment_with_overrides', - rules: [ - { - type: 'ALL', - conditions: [ - { - property: '$.identity.identifier', - operator: 'EQUAL', - value: 'test-user' - } - ] - } - ], - overrides: [ - { - key: 'override1', - feature_key: 'feature1', - name: 'feature1', - enabled: true, - value: 'overridden_value', - priority: 1 - } - ] - } - }, - features: { - feature1: { - key: 'fs1', - feature_key: 'feature1', - name: 'feature1', - enabled: false, - value: 'default_value' - } - } - }; - - const result = evaluateSegments(context); - - expect(result.segments).toHaveLength(2); - expect(result.segments).toEqual( - expect.arrayContaining([ - { key: '1', name: 'segment_with_no_overrides' }, - { key: '2', name: 'segment_with_overrides' } - ]) - ); +test('test_environment_get_all_feature_states', () => { + const env = environment(); + const featureStates = getEnvironmentFeatureStates(env); - expect(Object.keys(result.segmentOverrides)).toEqual(['feature1']); - expect(result.segmentOverrides.feature1.segmentName).toBe('segment_with_overrides'); + expect(featureStates).toBe(env.featureStates); }); -test('evaluateSegments handles priority conflicts correctly', () => { - const context: EvaluationContext = { - environment: { - key: 'test-env', - name: 'Test Environment' - }, - identity: { - key: 'test-user', - identifier: 'test-user' - }, - segments: { - '1': { - key: '1', - name: 'low_priority_segment', - rules: [ - { - type: 'ALL', - conditions: [ - { - property: '$.identity.identifier', - operator: 'EQUAL', - value: 'test-user' - } - ] - } - ], - overrides: [ - { - key: 'override1', - feature_key: 'feature1', - name: 'feature1', - enabled: true, - value: 'low_priority_value', - priority: 10 - } - ] - }, - '2': { - key: '2', - name: 'high_priority_segment', - rules: [ - { - type: 'ALL', - conditions: [ - { - property: '$.identity.identifier', - operator: 'EQUAL', - value: 'test-user' - } - ] - } - ], - overrides: [ - { - key: 'override2', - feature_key: 'feature1', - name: 'feature1', - enabled: false, - value: 'high_priority_value', - priority: 1 - } - ] - } - }, - features: { - feature1: { - key: 'fs1', - feature_key: 'feature1', - name: 'feature1', - enabled: false, - value: 'default_value' - } - } - }; +test('test_environment_get_feature_states_hides_disabled_flags_if_enabled', () => { + const env = environment(); - const result = evaluateSegments(context); + env.project.hideDisabledFlags = true; - expect(result.segments).toHaveLength(2); + const featureStates = getEnvironmentFeatureStates(env); - expect(result.segmentOverrides.feature1.segmentName).toBe('high_priority_segment'); - expect(result.segmentOverrides.feature1.feature.value).toBe('high_priority_value'); - expect(result.segmentOverrides.feature1.feature.priority).toBe(1); + expect(featureStates).not.toBe(env.featureStates); + for (const fs of featureStates) { + expect(fs.enabled).toBe(true); + } }); -test('evaluateSegments with non-matching identity returns empty', () => { - const context: EvaluationContext = { - environment: { - key: 'test-env', - name: 'Test Environment' - }, - identity: { - key: 'test-user', - identifier: 'test-user' - }, - segments: { - '1': { - key: '1', - name: 'segment_for_specific_user', - rules: [ - { - type: 'ALL', - conditions: [ - { - property: '$.identity.identifier', - operator: 'EQUAL', - value: 'test-user-123' - } - ] - } - ], - overrides: [ - { - key: 'override1', - feature_key: 'feature1', - name: 'feature1', - enabled: true, - value: 'overridden_value' - } - ] - } - }, - features: {} - }; - - const result = evaluateSegments(context); +test('test_environment_get_feature_state', () => { + const env = environment(); + const feature = feature1(); + const featureState = getEnvironmentFeatureState(env, feature.name); - expect(result.segments).toEqual([]); - expect(result.segmentOverrides).toEqual({}); + expect(featureState.feature).toStrictEqual(feature); }); -test('evaluateFeatures with multivariate evaluation', () => { - const context = { - features: { - mv_feature: { - key: 'mv', - feature_key: 'mv_feature', - name: 'Multivariate Feature', - enabled: true, - value: 'default', - variants: [ - { value: 'variant_a', weight: 0 }, - { value: 'variant_b', weight: 100 } - ] - } - }, - identity: { key: 'test_user', identifier: 'test_user' }, - environment: { - key: 'test_env', - name: 'Test Environment' - } - }; - - const flags = evaluateFeatures(context, {}); - expect(flags['Multivariate Feature'].value).toBe('variant_b'); +test('test_environment_get_feature_state_raises_feature_state_not_found', () => { + expect(() => { + getEnvironmentFeatureState(environment(), 'not_a_feature_name'); + }).toThrowError('Feature State Not Found'); }); diff --git a/tests/engine/unit/segments/segment_evaluators.test.ts b/tests/engine/unit/segments/segment_evaluators.test.ts index 6d260c7..1a73eec 100644 --- a/tests/engine/unit/segments/segment_evaluators.test.ts +++ b/tests/engine/unit/segments/segment_evaluators.test.ts @@ -3,27 +3,19 @@ import { CONDITION_OPERATORS, PERCENTAGE_SPLIT } from '../../../../flagsmith-engine/segments/constants.js'; - +import { SegmentConditionModel } from '../../../../flagsmith-engine/segments/models.js'; import { traitsMatchSegmentCondition, - getContextValue, - getIdentitySegments + evaluateIdentityInSegment } from '../../../../flagsmith-engine/segments/evaluators.js'; import { TraitModel, IdentityModel } from '../../../../flagsmith-engine/index.js'; import { environment } from '../utils.js'; import { buildSegmentModel } from '../../../../flagsmith-engine/segments/util.js'; -import { getHashedPercentageForObjIds } from '../../../../flagsmith-engine/utils/hashing/index.js'; -import { getEvaluationContext } from '../../../../flagsmith-engine/evaluation/evaluationContext/mappers.js'; -import { - EvaluationContext, - InSegmentCondition, - SegmentCondition, - SegmentCondition1 -} from '../../../../flagsmith-engine/evaluation/models.js'; +import { getHashedPercentateForObjIds } from '../../../../flagsmith-engine/utils/hashing/index.js'; // todo: work out how to implement this in a test function or before hook vi.mock('../../../../flagsmith-engine/utils/hashing', () => ({ - getHashedPercentageForObjIds: vi.fn(() => 1) + getHashedPercentateForObjIds: vi.fn(() => 1) })); let traitExistenceTestCases: [ @@ -56,33 +48,14 @@ let traitExistenceTestCases: [ test('test_traits_match_segment_condition_for_trait_existence_operators', () => { for (const testCase of traitExistenceTestCases) { const [operator, conditionProperty, conditionValue, traits, expectedResult] = testCase; - let segmentConditionModel = { - operator, - value: conditionValue, - property: conditionProperty - }; - const traitsMap = traits.reduce((acc, trait) => { - acc[trait.traitKey] = trait.traitValue; - return acc; - }, {}); - const context: EvaluationContext = { - environment: { - key: 'any', - name: 'any' - }, - identity: { - traits: traitsMap, - key: 'any', - identifier: 'any' - } - }; - expect( - traitsMatchSegmentCondition(segmentConditionModel as SegmentCondition, 'any', context) - ).toBe(expectedResult); + let segmentModel = new SegmentConditionModel(operator, conditionValue, conditionProperty); + expect(traitsMatchSegmentCondition(traits, segmentModel, 'any', 'any')).toBe( + expectedResult + ); } }); -test('getIdentitySegments uses django ID for hashed percentage when present', () => { +test('evaluateIdentityInSegment uses django ID for hashed percentage when present', () => { var identityModel = new IdentityModel( Date.now().toString(), [], @@ -111,376 +84,13 @@ test('getIdentitySegments uses django ID for hashed percentage when present', () feature_states: [] }; const segmentModel = buildSegmentModel(segmentDefinition); - const environmentModel = environment(); - environmentModel.project.segments = [segmentModel]; - const context = getEvaluationContext(environmentModel, identityModel); - var result = getIdentitySegments(context); + var result = evaluateIdentityInSegment(identityModel, segmentModel); - expect(result).toHaveLength(1); - expect(getHashedPercentageForObjIds).toHaveBeenCalledTimes(1); - expect(getHashedPercentageForObjIds).toHaveBeenCalledWith([ - result[0].key, - context.identity!.key + expect(result).toBe(true); + expect(getHashedPercentateForObjIds).toHaveBeenCalledTimes(1); + expect(getHashedPercentateForObjIds).toHaveBeenCalledWith([ + segmentModel.id, + identityModel.djangoID ]); }); - -describe('getIdentitySegments integration', () => { - test('returns only matching segments', () => { - const context: EvaluationContext = { - environment: { key: 'env', name: 'test' }, - identity: { - key: 'user', - identifier: 'premium@example.com', - traits: { subscription: 'premium' } - }, - segments: { - '1': { - key: '1', - name: 'premium_users', - rules: [ - { - type: 'ALL', - conditions: [ - { property: 'subscription', operator: 'EQUAL', value: 'premium' } - ] - } - ], - overrides: [] - }, - '2': { - key: '2', - name: 'basic_users', - rules: [ - { - type: 'ALL', - conditions: [ - { property: 'subscription', operator: 'EQUAL', value: 'basic' } - ] - } - ], - overrides: [] - } - }, - features: {} - }; - - const result = getIdentitySegments(context); - - expect(result).toHaveLength(1); - expect(result[0].name).toBe('premium_users'); - }); - - test('returns empty array when no segments match', () => { - const context: EvaluationContext = { - environment: { key: 'env', name: 'test' }, - identity: { - key: 'user', - identifier: 'test@example.com', - traits: { subscription: 'free' } - }, - segments: { - '1': { - key: '1', - name: 'premium_users', - rules: [ - { - type: 'ALL', - conditions: [ - { property: 'subscription', operator: 'EQUAL', value: 'premium' } - ] - } - ], - overrides: [] - } - }, - features: {} - }; - - const result = getIdentitySegments(context); - expect(result).toEqual([]); - }); -}); - -describe('IN operator', () => { - const mockContext: EvaluationContext = { - environment: { key: 'env', name: 'test' }, - identity: { - key: 'test-user', - identifier: 'test', - traits: { name: 'test' } - }, - segments: {}, - features: {} - }; - - test.each([ - // Array of strings - [ - { - property: '$.identity.identifier', - operator: CONDITION_OPERATORS.IN, - value: ['test', 'john-doe'] - }, - true - ], - [ - { - property: '$.identity.identifier', - operator: CONDITION_OPERATORS.IN, - value: ['john-doe'] - }, - false - ], - - // JSON encoded - [ - { - property: '$.identity.identifier', - operator: CONDITION_OPERATORS.IN, - value: '["test", "john-doe"]' - }, - true - ], - [ - { - property: '$.identity.identifier', - operator: CONDITION_OPERATORS.IN, - value: '["john-doe"]' - }, - false - ], - - // Legacy value string to split - [ - { - property: '$.identity.identifier', - operator: CONDITION_OPERATORS.IN, - value: 'test,john-doe' - }, - true - ], - [ - { - property: '$.identity.identifier', - operator: CONDITION_OPERATORS.IN, - value: 'john-doe' - }, - false - ], - // Fails because the value is split in middle - [ - { - property: '$.identity.identifier', - operator: CONDITION_OPERATORS.IN, - value: 'te,st,john-doe' - }, - false - ], - - // Edge cases - [{ property: '$.identity.identifier', operator: CONDITION_OPERATORS.IN, value: '' }, false], - [{ property: '$.identity.identifier', operator: CONDITION_OPERATORS.IN, value: [] }, false], - [ - { property: '$.identity.identifier', operator: CONDITION_OPERATORS.IN, value: '[]' }, - false - ] - ] as Array<[SegmentCondition | InSegmentCondition, boolean]>)( - 'evaluates IN condition %j to %s', - (condition: SegmentCondition | InSegmentCondition, expected: boolean) => { - const result = traitsMatchSegmentCondition(condition, 'segment', mockContext); - expect(result).toBe(expected); - } - ); -}); - -describe('getIdentitySegments single segment evaluation', () => { - const baseContext: EvaluationContext = { - environment: { key: 'env', name: 'test' }, - identity: { key: 'user', identifier: 'test@example.com', traits: { age: 25 } }, - segments: {}, - features: {} - }; - - test('returns empty array for segment with no rules', () => { - const context = { - ...baseContext, - segments: { - '1': { - key: '1', - name: 'empty_segment', - rules: [], - overrides: [] - } - } - }; - - expect(getIdentitySegments(context)).toEqual([]); - }); - - test('returns segment when all rules match', () => { - const context: EvaluationContext = { - ...baseContext, - segments: { - '1': { - key: '1', - name: 'matching_segment', - rules: [ - { - type: ALL_RULE, - conditions: [ - { - property: '$.identity.identifier', - operator: 'EQUAL', - value: 'test@example.com' - } - ], - rules: [] - }, - { - type: ALL_RULE, - conditions: [ - { - property: '$.identity.identifier', - operator: 'CONTAINS', - value: 'test@example.com' - } - ], - rules: [] - } - ], - overrides: [] - } - } - }; - - const result = getIdentitySegments(context); - expect(result).toHaveLength(1); - expect(result[0].name).toBe('matching_segment'); - }); - - test('returns empty array when any rule fails', () => { - const context: EvaluationContext = { - ...baseContext, - segments: { - '1': { - key: '1', - name: 'failing_segment', - rules: [ - { - type: ALL_RULE, - conditions: [ - { - property: '$.identity.identifier', - operator: 'EQUAL', - value: 'test@example.com' - } - ], - rules: [] - }, - { - type: ALL_RULE, - conditions: [{ property: 'age', operator: 'EQUAL', value: '30' }], - rules: [] - } - ], - overrides: [] - } - } - }; - - expect(getIdentitySegments(context)).toEqual([]); - }); -}); - -describe('getContextValue', () => { - const mockContext: EvaluationContext = { - environment: { - key: 'test-env-key', - name: 'Test Environment' - }, - identity: { - key: 'user-123', - identifier: 'user@example.com' - }, - segments: {}, - features: {} - }; - - // Success cases - test.each([ - ['$.identity.identifier', 'user@example.com'], - ['$.environment.name', 'Test Environment'], - ['$.environment.key', 'test-env-key'] - ])('returns correct value for path %s', (jsonPath, expected) => { - const result = getContextValue(jsonPath, mockContext); - expect(result).toBe(expected); - }); - - // Undefined or invalid cases - test.each([ - ['$.identity.traits.user_type', 'unsupported nested path'], - ['identity.identifier', 'missing $ prefix'], - ['$.invalid.path', 'completely invalid path'], - ['$.identity.nonexistent', 'valid structure but missing property'], - ['', 'empty string'], - ['$', 'just $ symbol'] - ])('returns undefined for %s (%s)', jsonPath => { - const result = getContextValue(jsonPath, mockContext); - expect(result).toBeUndefined(); - }); - - // Context error cases - test.each([ - [undefined, '$.identity.identifier', 'undefined context'], - [{ segments: {}, features: {} }, '$.identity.identifier', 'missing identity'], - [ - { identity: { key: 'test', identifier: 'test' }, segments: {}, features: {} }, - '$.environment.name', - 'missing environment' - ] - ])('returns undefined when %s', (context, jsonPath, _) => { - const result = getContextValue(jsonPath, context as EvaluationContext); - expect(result).toBeUndefined(); - }); -}); - -describe('percentage split operator', () => { - const mockContext: EvaluationContext = { - environment: { key: 'env', name: 'Test Env' }, - identity: { - key: 'user-123', - identifier: 'test@example.com', - traits: { - age: 25, - subscription: 'premium', - active: true - } - }, - segments: {}, - features: {} - }; - beforeEach(() => { - vi.clearAllMocks(); - }); - - test.each([ - [25.5, 30, true], - [25.5, 20, false], - [25.5, 25.5, true], - [0, 0, true], - [100, 99.9, false] - ])('percentage %d with threshold %d returns %s', (hashedValue, threshold, expected) => { - const mockHashFn = getHashedPercentageForObjIds; - mockHashFn.mockReturnValue(hashedValue); - const condition = { - property: 'any', - operator: 'PERCENTAGE_SPLIT', - value: threshold.toString() - } as SegmentCondition1 | InSegmentCondition; - const result = traitsMatchSegmentCondition(condition, 'seg1', mockContext); - - expect(result).toBe(expected); - expect(getHashedPercentageForObjIds).toHaveBeenCalledWith(['seg1', 'user-123']); - }); -}); diff --git a/tests/engine/unit/segments/segments_model.test.ts b/tests/engine/unit/segments/segments_model.test.ts index 5607f03..17d9166 100644 --- a/tests/engine/unit/segments/segments_model.test.ts +++ b/tests/engine/unit/segments/segments_model.test.ts @@ -1,5 +1,3 @@ -import { EvaluationContext } from '../../../../flagsmith-engine/evaluationContext/evaluationContext.types'; -import { CONSTANTS } from '../../../../flagsmith-engine/features/constants'; import { ALL_RULE, ANY_RULE, @@ -10,7 +8,6 @@ import { all, any, SegmentConditionModel, - SegmentModel, SegmentRuleModel } from '../../../../flagsmith-engine/segments/models'; @@ -138,78 +135,3 @@ test('test_segment_rule_matching_function', () => { expect(new SegmentRuleModel(testCase[0]).matchingFunction()).toBe(testCase[1]); } }); - -test('test_fromSegmentResult_with_multiple_variants', () => { - const segmentResults = [{ key: '1', name: 'test_segment' }]; - - const evaluationContext: EvaluationContext = { - identity: { - key: 'not_exist', - identifier: 'not_exist' - }, - environment: { - key: 'test', - name: 'test' - }, - features: {}, - segments: { - '1': { - key: '1', - name: 'test_segment', - rules: [ - { - type: 'ALL', - conditions: [ - { - property: '$.identity.identifier', - operator: 'EQUAL', - value: 'test-user' - } - ] - } - ], - overrides: [ - { - key: 'override', - feature_key: '1', - name: 'multivariate_feature', - enabled: true, - value: 'default_value', - priority: 1, - variants: [ - { id: 1, value: 'variant_a', weight: 30 }, - { id: 2, value: 'variant_b', weight: 70 } - ] - } - ] - } - } - }; - - const result = SegmentModel.fromSegmentResult(segmentResults, evaluationContext); - - expect(result).toHaveLength(1); - - const segment = result[0]; - expect(segment.name).toBe('test_segment'); - expect(segment.featureStates).toHaveLength(1); - - const featureState = segment.featureStates[0]; - expect(featureState.feature.name).toBe('multivariate_feature'); - expect(featureState.feature.type).toBe(CONSTANTS.MULTIVARIATE); - expect(featureState.enabled).toBe(true); - expect(featureState.getValue()).toBe('default_value'); - - // Test multivariate variants - expect(featureState.multivariateFeatureStateValues).toHaveLength(2); - - const variant1 = featureState.multivariateFeatureStateValues[0]; - expect(variant1.multivariateFeatureOption.value).toBe('variant_a'); - expect(variant1.percentageAllocation).toBe(30); - expect(variant1.id).toBe(1); - - const variant2 = featureState.multivariateFeatureStateValues[1]; - expect(variant2.multivariateFeatureOption.value).toBe('variant_b'); - expect(variant2.percentageAllocation).toBe(70); - expect(variant2.id).toBe(2); -}); diff --git a/tests/engine/unit/utils.ts b/tests/engine/unit/utils.ts index 4e89fca..cdb73b2 100644 --- a/tests/engine/unit/utils.ts +++ b/tests/engine/unit/utils.ts @@ -20,7 +20,7 @@ export function segmentCondition() { } export function traitMatchingSegment() { - return new TraitModel(segmentCondition().property as string, segmentCondition().value); + return new TraitModel(segmentCondition().property_ as string, segmentCondition().value); } export function organisation() { diff --git a/tests/engine/unit/utils/utils.test.ts b/tests/engine/unit/utils/utils.test.ts index 15a1a30..041adfc 100644 --- a/tests/engine/unit/utils/utils.test.ts +++ b/tests/engine/unit/utils/utils.test.ts @@ -1,11 +1,11 @@ import { randomUUID as uuidv4 } from 'node:crypto'; -import { getHashedPercentageForObjIds } from '../../../../flagsmith-engine/utils/hashing/index.js'; +import { getHashedPercentateForObjIds } from '../../../../flagsmith-engine/utils/hashing/index.js'; describe('getHashedPercentageForObjIds', () => { it.each([[[12, 93]], [[uuidv4(), 99]], [[99, uuidv4()]], [[uuidv4(), uuidv4()]]])( 'returns x where 0 <= x < 100', (objIds: (string | number)[]) => { - let result = getHashedPercentageForObjIds(objIds); + let result = getHashedPercentateForObjIds(objIds); expect(result).toBeLessThan(100); expect(result).toBeGreaterThanOrEqual(0); } @@ -14,15 +14,15 @@ describe('getHashedPercentageForObjIds', () => { it.each([[[12, 93]], [[uuidv4(), 99]], [[99, uuidv4()]], [[uuidv4(), uuidv4()]]])( 'returns the same value each time', (objIds: (string | number)[]) => { - let resultOne = getHashedPercentageForObjIds(objIds); - let resultTwo = getHashedPercentageForObjIds(objIds); + let resultOne = getHashedPercentateForObjIds(objIds); + let resultTwo = getHashedPercentateForObjIds(objIds); expect(resultOne).toEqual(resultTwo); } ); it('is unique for different object ids', () => { - let resultOne = getHashedPercentageForObjIds([14, 106]); - let resultTwo = getHashedPercentageForObjIds([53, 200]); + let resultOne = getHashedPercentateForObjIds([14, 106]); + let resultTwo = getHashedPercentateForObjIds([53, 200]); expect(resultOne).not.toEqual(resultTwo); }); @@ -40,7 +40,7 @@ describe('getHashedPercentageForObjIds', () => { ); // When - let values = objectIdPairs.map(objIds => getHashedPercentageForObjIds(objIds)); + let values = objectIdPairs.map(objIds => getHashedPercentateForObjIds(objIds)); // Then for (let i = 0; i++; i < numTestBuckets) {