From 5a2960e24bc4d9e1621616964bc7493a2bc06b15 Mon Sep 17 00:00:00 2001 From: Tim Yiu <137842098+tyiuhc@users.noreply.github.com> Date: Thu, 18 Dec 2025 11:37:10 -0800 Subject: [PATCH] feat: add analytics_event trigger --- packages/experiment-tag/package.json | 1 + packages/experiment-tag/src/experiment.ts | 40 +++++++++ packages/experiment-tag/src/message-bus.ts | 6 +- packages/experiment-tag/src/subscriptions.ts | 37 +++++--- yarn.lock | 93 ++------------------ 5 files changed, 75 insertions(+), 102 deletions(-) diff --git a/packages/experiment-tag/package.json b/packages/experiment-tag/package.json index 86372e7a..8b8e8a21 100644 --- a/packages/experiment-tag/package.json +++ b/packages/experiment-tag/package.json @@ -28,6 +28,7 @@ }, "dependencies": { "@amplitude/analytics-core": "^2.21.0", + "@amplitude/analytics-types": "^2.11.0", "@amplitude/experiment-core": "^0.12.0", "@amplitude/experiment-js-client": "^1.20.1", "dom-mutator": "git+ssh://git@github.com:amplitude/dom-mutator#ef95c531a822bce55c197a6bf5e1d86d03bc086c", diff --git a/packages/experiment-tag/src/experiment.ts b/packages/experiment-tag/src/experiment.ts index c78c51b9..7afa110a 100644 --- a/packages/experiment-tag/src/experiment.ts +++ b/packages/experiment-tag/src/experiment.ts @@ -1,4 +1,5 @@ import { AnalyticsConnector } from '@amplitude/analytics-connector'; +import { Event, Plugin } from '@amplitude/analytics-types'; import { EvaluationFlag, getGlobalScope, @@ -538,6 +539,45 @@ export class DefaultWebExperimentClient implements WebExperimentClient { this.messageBus.publish('manual', { name }); } + /** + * Track an analytics event that can trigger page objects. + * @param event_type The event type/name + * @param event_properties Optional event properties + */ + public trackEvent( + event_type: string, + event_properties?: Record, + ) { + this.messageBus.publish('analytics_event', { + event_type, + event_properties: event_properties || {}, + }); + } + + /** + * Returns an Amplitude plugin that forwards analytics events to trigger page objects. + * @returns An Amplitude Plugin that intercepts analytics events + */ + public plugin(): Plugin { + return { + name: '@amplitude/experiment-tag', + type: 'enrichment', + + setup: async (): Promise => { + // No setup required + }, + + execute: async (context: Event): Promise => { + this.trackEvent( + context.event_type, + context.event_properties as Record, + ); + + return context; + }, + }; + } + private async fetchRemoteFlags() { try { // eslint-disable-next-line @typescript-eslint/ban-ts-comment diff --git a/packages/experiment-tag/src/message-bus.ts b/packages/experiment-tag/src/message-bus.ts index 14d27fd7..f527b11d 100644 --- a/packages/experiment-tag/src/message-bus.ts +++ b/packages/experiment-tag/src/message-bus.ts @@ -1,10 +1,6 @@ -export interface EventProperties { - [k: string]: unknown; -} - export interface AnalyticsEvent { event_type: string; - event_properties: EventProperties; + event_properties: Record; } type Subscriber = { diff --git a/packages/experiment-tag/src/subscriptions.ts b/packages/experiment-tag/src/subscriptions.ts index c62c3b9b..6551b141 100644 --- a/packages/experiment-tag/src/subscriptions.ts +++ b/packages/experiment-tag/src/subscriptions.ts @@ -6,6 +6,7 @@ import { MessagePayloads, ElementAppearedPayload, ManualTriggerPayload, + AnalyticsEventPayload, MessageType, } from './message-bus'; import { DebouncedMutationManager } from './mutation-manager'; @@ -417,11 +418,28 @@ export class SubscriptionManager { page: PageObject, message: MessagePayloads[T], ): boolean => { - // Check conditions + let evalContext: Record = { + page: { url: this.globalScope.location.href }, + }; + + if (page.trigger_type === 'analytics_event') { + const eventMessage = message as AnalyticsEventPayload; + + evalContext = { + ...evalContext, + type: 'analytics_event', + data: { + event: eventMessage.event_type, + properties: eventMessage.event_properties, + }, + }; + } + + // Check conditions with enriched context if (page.conditions && page.conditions.length > 0) { const matchConditions = evaluationEngine.evaluateConditions( { - context: { page: { url: this.globalScope.location.href } }, + context: evalContext, result: {}, }, page.conditions, @@ -431,7 +449,7 @@ export class SubscriptionManager { } } - // Check if page is active + // Check if page is active based on trigger type switch (page.trigger_type) { case 'url_change': return true; @@ -441,15 +459,10 @@ export class SubscriptionManager { return (message as ManualTriggerPayload).name === triggerValue.name; } - // case 'analytics_event': { - // const eventMessage = message as AnalyticsEventPayload; - // return ( - // eventMessage.event_type === page.trigger_value.event_type && - // Object.entries(page.trigger_value.event_properties || {}).every( - // ([key, value]) => eventMessage.event_properties[key] === value, - // ) - // ); - // } + case 'analytics_event': { + // Event type already matched above, conditions evaluated + return true; + } case 'element_appeared': { const triggerValue = page.trigger_value as ElementAppearedTriggerValue; diff --git a/yarn.lock b/yarn.lock index a7d7b34f..6711eee3 100644 --- a/yarn.lock +++ b/yarn.lock @@ -10,6 +10,11 @@ "@amplitude/analytics-connector" "^1.6.4" tslib "^2.4.1" +"@amplitude/analytics-types@^2.11.0": + version "2.11.0" + resolved "https://registry.yarnpkg.com/@amplitude/analytics-types/-/analytics-types-2.11.0.tgz#7aa83d82f49a5905628a676d44131f5e55ec9a82" + integrity sha512-L1niBXYSWmbyHUE/GNuf6YBljbafaxWI3X5jjEIZDFCjQvdWO3DKalY1VPFUbhgYQgWw7+bC6I/AlUaporyfig== + "@amplitude/types@^1.10.2": version "1.10.2" resolved "https://registry.yarnpkg.com/@amplitude/types/-/types-1.10.2.tgz#8f3c6c3c9ee24f401ee037b351c3c67eb945eefc" @@ -43,7 +48,7 @@ dependencies: "@babel/highlight" "^7.10.4" -"@babel/code-frame@^7.0.0", "@babel/code-frame@^7.10.4", "@babel/code-frame@^7.12.13", "@babel/code-frame@^7.27.1": +"@babel/code-frame@^7.0.0", "@babel/code-frame@^7.12.13", "@babel/code-frame@^7.27.1": version "7.27.1" resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.27.1.tgz#200f715e66d52a23b221a9435534a91cc13ad5be" integrity sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg== @@ -923,14 +928,7 @@ "@babel/plugin-transform-modules-commonjs" "^7.27.1" "@babel/plugin-transform-typescript" "^7.27.1" -"@babel/runtime-corejs3@^7.10.2": - version "7.27.6" - resolved "https://registry.yarnpkg.com/@babel/runtime-corejs3/-/runtime-corejs3-7.27.6.tgz#97644153808a62898e7c05f3361501417db3c48b" - integrity sha512-vDVrlmRAY8z9Ul/HxT+8ceAru95LQgkSKiXkSYZvqtbkPSfhZJgpRp45Cldbh1GJ1kxzQkI70AqyrTI58KpaWQ== - dependencies: - core-js-pure "^3.30.2" - -"@babel/runtime@^7.10.2", "@babel/runtime@^7.10.3", "@babel/runtime@^7.11.2", "@babel/runtime@^7.21.0": +"@babel/runtime@^7.11.2", "@babel/runtime@^7.21.0": version "7.27.6" resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.27.6.tgz#ec4070a04d76bae8ddbb10770ba55714a417b7c6" integrity sha512-vbavdySgbTTrmFE+EsiqUTzlOr5bzlnJtUv9PynGCAKvfQqjIXbvFdumPM/GxMDfyuGMJaJAU6TO4zc1Jf1i8Q== @@ -1443,17 +1441,6 @@ slash "^3.0.0" write-file-atomic "^4.0.2" -"@jest/types@^26.6.2": - version "26.6.2" - resolved "https://registry.yarnpkg.com/@jest/types/-/types-26.6.2.tgz#bef5a532030e1d88a2f5a6d933f84e97226ed48e" - integrity sha512-fC6QCp7Sc5sX6g8Tvbmj4XUTbyrik0akgRy03yjXbQaBWWNWGE7SGtJk98m0N8nzegD/7SggrUlivxo5ax4KWQ== - dependencies: - "@types/istanbul-lib-coverage" "^2.0.0" - "@types/istanbul-reports" "^3.0.0" - "@types/node" "*" - "@types/yargs" "^15.0.0" - chalk "^4.0.0" - "@jest/types@^29.6.3": version "29.6.3" resolved "https://registry.yarnpkg.com/@jest/types/-/types-29.6.3.tgz#1131f8cf634e7e84c5e77bab12f052af585fba59" @@ -2286,20 +2273,6 @@ dependencies: "@sinonjs/commons" "^3.0.0" -"@testing-library/dom@7.26.0": - version "7.26.0" - resolved "https://registry.yarnpkg.com/@testing-library/dom/-/dom-7.26.0.tgz#da4d052dc426a4ccc916303369c6e7552126f680" - integrity sha512-fyKFrBbS1IigaE3FV21LyeC7kSGF84lqTlSYdKmGaHuK2eYQ/bXVPM5vAa2wx/AU1iPD6oQHsxy2QQ17q9AMCg== - dependencies: - "@babel/code-frame" "^7.10.4" - "@babel/runtime" "^7.10.3" - "@types/aria-query" "^4.2.0" - aria-query "^4.2.2" - chalk "^4.1.0" - dom-accessibility-api "^0.5.1" - lz-string "^1.4.4" - pretty-format "^26.4.2" - "@tootallnate/once@2": version "2.0.0" resolved "https://registry.yarnpkg.com/@tootallnate/once/-/once-2.0.0.tgz#f544a148d3ab35801c1f633a7441fd87c2e484bf" @@ -2330,11 +2303,6 @@ resolved "https://registry.yarnpkg.com/@types/amplitude-js/-/amplitude-js-8.16.5.tgz#ad8c2c0f2f8b436f014f8b72ddb5535dd00368e6" integrity sha512-W73JfDpwDH4VijOGo+nVuQOqUCiqyEGGVdajU4ziWTLn27cn+QtFuFuBdlhCraIIrO52fDRO4NSOGkawtn77Jw== -"@types/aria-query@^4.2.0": - version "4.2.2" - resolved "https://registry.yarnpkg.com/@types/aria-query/-/aria-query-4.2.2.tgz#ed4e0ad92306a704f9fb132a0cfcf77486dbe2bc" - integrity sha512-HnYpAE1Y6kRyKM/XkEuiRQhTHvkzMBurTHnpFLYLBGPIylZNPs9jJcuOOYWxPLJCSEtmZT0Y8rHDokKN7rRTig== - "@types/babel__core@^7.1.14": version "7.20.5" resolved "https://registry.yarnpkg.com/@types/babel__core/-/babel__core-7.20.5.tgz#3df15f27ba85319caa07ba08d0721889bb39c017" @@ -2499,13 +2467,6 @@ resolved "https://registry.yarnpkg.com/@types/yargs-parser/-/yargs-parser-21.0.3.tgz#815e30b786d2e8f0dcd85fd5bcf5e1a04d008f15" integrity sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ== -"@types/yargs@^15.0.0": - version "15.0.19" - resolved "https://registry.yarnpkg.com/@types/yargs/-/yargs-15.0.19.tgz#328fb89e46109ecbdb70c295d96ff2f46dfd01b9" - integrity sha512-2XUaGVmyQjgyAZldf0D0c14vvo/yv0MhQBSTJcejMMaitsn3nxCB6TmH4G0ZQf+uxROOa9mpanoSm8h6SG/1ZA== - dependencies: - "@types/yargs-parser" "*" - "@types/yargs@^17.0.8": version "17.0.33" resolved "https://registry.yarnpkg.com/@types/yargs/-/yargs-17.0.33.tgz#8c32303da83eec050a84b3c7ae7b9f922d13e32d" @@ -2739,7 +2700,7 @@ ansi-escapes@^4.2.1: dependencies: type-fest "^0.21.3" -ansi-regex@^5.0.0, ansi-regex@^5.0.1: +ansi-regex@^5.0.1: version "5.0.1" resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.1.tgz#082cb2c89c9fe8659a311a53bd6a4dc5301db304" integrity sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ== @@ -2798,14 +2759,6 @@ argparse@^2.0.1: resolved "https://registry.yarnpkg.com/argparse/-/argparse-2.0.1.tgz#246f50f3ca78a3240f6c997e8a9bd1eac49e4b38" integrity sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q== -aria-query@^4.2.2: - version "4.2.2" - resolved "https://registry.yarnpkg.com/aria-query/-/aria-query-4.2.2.tgz#0d2ca6c9aceb56b8977e9fed6aed7e15bbd2f83b" - integrity sha512-o/HelwhuKpTj/frsOsbNLNgnNGVIFsVP/SW2BSF14gVl7kAfMOJ6/8wUAUvG1R1NHKrfG+2sHZTu0yauT1qBrA== - dependencies: - "@babel/runtime" "^7.10.2" - "@babel/runtime-corejs3" "^7.10.2" - array-buffer-byte-length@^1.0.1, array-buffer-byte-length@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/array-buffer-byte-length/-/array-buffer-byte-length-1.0.2.tgz#384d12a37295aec3769ab022ad323a18a51ccf8b" @@ -3533,11 +3486,6 @@ core-js-compat@^3.40.0: dependencies: browserslist "^4.25.0" -core-js-pure@^3.30.2: - version "3.43.0" - resolved "https://registry.yarnpkg.com/core-js-pure/-/core-js-pure-3.43.0.tgz#4df9c949c7abde839a8398d16a827a76856b1f0c" - integrity sha512-i/AgxU2+A+BbJdMxh3v7/vxi2SbFqxiFmg6VsDwYB4jkucrd1BZNA9a9gphC0fYMG5IBSgQcbQnk865VCLe7xA== - core-util-is@~1.0.0: version "1.0.3" resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.3.tgz#a6042d3634c2b27e9328f837b965fac83808db85" @@ -3783,11 +3731,6 @@ doctrine@^3.0.0: dependencies: esutils "^2.0.2" -dom-accessibility-api@^0.5.1: - version "0.5.16" - resolved "https://registry.yarnpkg.com/dom-accessibility-api/-/dom-accessibility-api-0.5.16.tgz#5a7429e6066eb3664d911e33fb0e45de8eb08453" - integrity sha512-X7BJ2yElsnOJ30pZF4uIIDfBEVgF4XEBxL9Bxhy6dnrm5hkzqmsWHGTiHqRiITNhMyFLyAiWndIJP7Z1NTteDg== - "dom-mutator@git+ssh://git@github.com:amplitude/dom-mutator#ef95c531a822bce55c197a6bf5e1d86d03bc086c": version "0.7.1" resolved "git+ssh://git@github.com:amplitude/dom-mutator#ef95c531a822bce55c197a6bf5e1d86d03bc086c" @@ -6265,11 +6208,6 @@ lunr@^2.3.9: resolved "https://registry.yarnpkg.com/lunr/-/lunr-2.3.9.tgz#18b123142832337dd6e964df1a5a7707b25d35e1" integrity sha512-zTU3DaZaF3Rt9rhN3uBMGQD3dD2/vFQqnvZCDv4dl5iOzq2IZQqTxu90r4E5J+nP70J3ilqVCrbho2eWaeW8Ow== -lz-string@^1.4.4: - version "1.5.0" - resolved "https://registry.yarnpkg.com/lz-string/-/lz-string-1.5.0.tgz#c1ab50f77887b712621201ba9fd4e3a6ed099941" - integrity sha512-h5bgJWpxJNswbU7qCrV0tIKQCaS3blPDrqKWx+QxzuzL1zGUzij9XCWLrSLsJPu5t+eWA/ycetzYAO5IOMcWAQ== - magic-string@^0.25.7: version "0.25.9" resolved "https://registry.yarnpkg.com/magic-string/-/magic-string-0.25.9.tgz#de7f9faf91ef8a1c91d02c2e5314c8277dbcdd1c" @@ -7413,16 +7351,6 @@ pretty-format@30.2.0: ansi-styles "^5.2.0" react-is "^18.3.1" -pretty-format@^26.4.2: - version "26.6.2" - resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-26.6.2.tgz#e35c2705f14cb7fe2fe94fa078345b444120fc93" - integrity sha512-7AeGuCYNGmycyQbCqd/3PWH4eOoX/OiCa0uphp57NVTeAGdJGaAliecxwBDHYQCIvrW7aDBZCYeNTP/WX69mkg== - dependencies: - "@jest/types" "^26.6.2" - ansi-regex "^5.0.0" - ansi-styles "^4.0.0" - react-is "^17.0.1" - pretty-format@^29.0.0, pretty-format@^29.7.0: version "29.7.0" resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-29.7.0.tgz#ca42c758310f365bfa71a0bda0a807160b776812" @@ -7553,11 +7481,6 @@ randombytes@^2.1.0: dependencies: safe-buffer "^5.1.0" -react-is@^17.0.1: - version "17.0.2" - resolved "https://registry.yarnpkg.com/react-is/-/react-is-17.0.2.tgz#e691d4a8e9c789365655539ab372762b0efb54f0" - integrity sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w== - react-is@^18.0.0, react-is@^18.3.1: version "18.3.1" resolved "https://registry.yarnpkg.com/react-is/-/react-is-18.3.1.tgz#e83557dc12eae63a99e003a46388b1dcbb44db7e"