Skip to content

refactor: migrate identity module to TS#1248

Open
jaissica12 wants to merge 4 commits into
developmentfrom
refactor/SDKE-1103-migrate-identity-module-to-ts
Open

refactor: migrate identity module to TS#1248
jaissica12 wants to merge 4 commits into
developmentfrom
refactor/SDKE-1103-migrate-identity-module-to-ts

Conversation

@jaissica12
Copy link
Copy Markdown
Contributor

@jaissica12 jaissica12 commented Apr 13, 2026

Background

  • The identity.js module was one of the remaining JavaScript files in the SDK. As part of the ongoing TypeScript migration effort (SDKE-1103), it needed to be converted to TypeScript to improve type safety and maintainability. This is a direct migration with no behavior, API, or breaking changes.

What Has Changed

  • Renamed src/identity.js to src/identity.ts with type annotations added to all functions, parameters, and variables
  • Corrected pre-existing type mismatches in identity.interfaces.ts, identityApiClient.ts, persistence.interfaces.ts, store.ts, and mp-instance.ts where interfaces didn't match actual runtime behavior
  • Updated test files to align with corrected interface types
  • No runtime logic, public API, or deprecated behavior was changed — compiled output is functionally identical

Testing

  • All existing Karma + Mocha integration tests pass
  • All existing Jest unit tests pass
  • Manual end-to-end testing performed via rokt-web-playground with local SDK build, covering:
    • SDK initialization and identify callback (httpCode 200)
    • Login with multiple identity types (email, customerid, facebook, mobile_number)
    • Modify with no-op (httpCode 400 — expected) and actual changes (httpCode 200)
    • Logout → anonymous MPID transition (ephemeral, is_logged_in: false)
    • Anonymous user: page views, commerce events, user attributes all fire with correct anonymous MPID and empty identities
    • Re-login from anonymous state restores known MPID with full identity set
    • User attribute set/remove with forwarder callbacks (Rokt Kit)
    • Session end/start across identity transitions
    • All event batches uploaded successfully (202) with correct MPID and identity payloads

Screenshots/Video

Screenshot 2026-05-20 at 4 01 44 PM Screenshot 2026-05-20 at 4 02 06 PM Screenshot 2026-05-20 at 4 02 42 PM Screenshot 2026-05-20 at 4 03 04 PM

Checklist

  • I have performed a self-review of my own code.
  • I have made corresponding changes to the documentation.
  • I have added tests that prove my fix is effective or that my feature works.
  • I have tested this locally.

Additional Notes

  • The main self = this pattern in the Identity constructor (~31 usages) was intentionally left as-is for a follow-up refactor to arrow functions, to keep this PR focused on the JS→TS migration only.
  • varconst/let replacements and removal of unnecessary type assertions were included as part of SonarCloud compliance.

Reference Issue (For employees only. Ignore if you are an outside contributor)

@jaissica12 jaissica12 force-pushed the refactor/SDKE-1103-migrate-identity-module-to-ts branch from 54ee61d to 6013f98 Compare May 12, 2026 21:20
@jaissica12 jaissica12 force-pushed the refactor/SDKE-1103-migrate-identity-module-to-ts branch from 6013f98 to 1cdacb3 Compare May 12, 2026 21:36
@sonarqubecloud
Copy link
Copy Markdown

@jaissica12 jaissica12 marked this pull request as ready for review May 20, 2026 19:58
@jaissica12 jaissica12 requested a review from a team as a code owner May 20, 2026 19:58
@cursor
Copy link
Copy Markdown

cursor Bot commented May 20, 2026

PR Summary

Medium Risk
Touches identify/login/logout/modify, persistence, and forwarder hooks across a large file; risk is mainly regression despite the stated no-behavior-change goal and existing test coverage.

Overview
This PR migrates the core identity implementation from JavaScript to TypeScript (identity.jsidentity.ts) by adding types across identity APIs, request builders, user helpers, and response parsing, while keeping the public behavior the same.

Interface and wiring updates align types with runtime usage: identity request payloads use request_timestamp_ms, Context, and IKnownIdentities; preprocess results expose cleanedIdentities; modify flows type createIdentityChanges as an array; alias callbacks use IAliasCallback; IIdentity / IIdentityApiClient / store and persistence signatures are tightened (e.g. optional knownIdentities on modify, mpid on store, first/last seen times as numbers).

Small defensive tweaks include optional kit logOut, checking typeof forwarder.logOut === 'function', safer validation logging, and test updates for the corrected types. varconst/let is included for lint compliance without intended logic changes.

Reviewed by Cursor Bugbot for commit d408003. Bugbot is set up for automated code reviews on this repo. Configure here.

Copy link
Copy Markdown

@cursor cursor Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, have a team admin enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 8f4c21b. Configure here.

Comment thread src/identity.ts
Comment thread src/identity-utils.ts
UserIdentities,
IdentityCallback,
} from '@mparticle/web-sdk';
import { IdentityCallback } from './identity-user-interfaces';
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Clean up the imports

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

Comment thread src/identity-utils.ts Outdated
identityApiData: IdentityApiData,
identityMethod: string,
knownIdentities: IKnownIdentities,
knownIdentities: IKnownIdentities | UserIdentities,
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure if these overlap cleanly

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch. IKnownIdentities | UserIdentities collapses to plain UserIdentities at the type level (since IKnownIdentities extends UserIdentities), so the union added zero safety and actually hid device_application_stamp

environment: Environment;
request_id: string;
request_timestamp_unixtime_ms: number;
request_timestamp_ms: number;
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why are we renaming this?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, it's really a typo fix on the interface. The SDK has always been sending request_timestamp_ms, the interface was just declaring the different key name

previousIdentities: UserIdentities,
newIdentitie: UserIdentities
): IIdentityAPIIdentityChangeData;
newIdentities: UserIdentities
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How long has this typo been here? 😓

Comment thread src/identity.interfaces.ts Outdated
): IUserAttributeChangeEvent;
createUserIdentityChange(
identityType: SDKIdentityTypeEnum,
identityType: SDKIdentityTypeEnum | string,
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think we should use string but really limit this to known types

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Tightened to SDKIdentityTypeEnum on the interface and narrowed the loop

Comment thread src/identity.ts
aliasRequest.destinationMpid
);
var aliasRequestMessage = mpInstance._Identity.IdentityRequest.createAliasNetworkRequest(
const aliasRequestMessage = mpInstance._Identity.IdentityRequest.createAliasNetworkRequest(
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This cast hides a real mismatch and is worth fixing before merge.

createAliasNetworkRequest returns the network wire shape:

{ request_id, request_type: 'alias', environment, data: { destination_mpid, source_mpid, start_unixtime_ms, end_unixtime_ms, device_application_stamp } }

But IAliasRequest is:

{ destinationMpid, sourceMpid, startTime, endTime, scope? }

Those are completely different shapes. The as IAliasRequest cast tells the compiler "trust me, it's an IAliasRequest" and from there sendAliasRequest(aliasRequest: IAliasRequest) is happy. At runtime sendAliasRequest just does JSON.stringify(aliasRequest) (identityApiClient.ts:113) so the actual wire format is still right, but the types are lying.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed, the cast is hiding a real type drift. Already addressed in fix/SDKE-1103-alias-network-request-type branch

callback: IdentityCallback,
method: IdentityAPIMethod
): IdentityPreProcessResult;
createAliasNetworkRequest(aliasRequest: IAliasRequest): object;
Copy link
Copy Markdown

@crisryantan crisryantan May 21, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Tied to the earlier comment. These three methods all return object, which is barely better than any. The migration goal is type safety, so this is a missed opportunity.

If you do above for createAliasNetworkRequest, the same pattern applies here, declare proper interfaces for the native shapes and use them. convertToNative returning object | void is especially confusing because void is meaningless as a return type union with anything else (it means "ignore the return value"), and the actual code returns undefined explicitly. IdentityNativeRequest | undefined would be clearer.

Happy to defer to a follow-up if you want to keep this PR scoped, but please file a ticket so it does not slip.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed, object is barely better than any, and object | void is genuinely confusing semantics.
Picking up the two alias-related ones on fix/SDKE-1103-alias-network-request-type:
createAliasNetworkRequest: : object → : IAliasNetworkRequest
convertAliasToNative: : object → : INativeAliasRequest (new interface — { DestinationMpid, SourceMpid, StartUnixtimeMs, EndUnixtimeMs, Scope? })
Creating a separate ticket (https://go/j/SDKE-1204) for convertToNative since it's a different code path (identity native bridge, not alias), will switch to INativeIdentityRequest | undefined per your suggestion

Comment thread src/identity.ts Outdated
) {
if (forwarder.logOut) {
forwarder.logOut(evt);
const fwd = forwarder as unknown as Record<string, Function>;
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why the double-cast to Record<string, Function>? That tells the compiler "this thing is a string-keyed bag of arbitrary functions" which is the loosest possible cast and erases all type information from forwarder.

I checked ConfiguredKit in forwarders.interfaces.ts does not have a logOut method. So either:

  1. The old JS code (if (forwarder.logOut)) was always-falsy defensive code that never actually fired. If that is the case, this whole block can be deleted.
  2. There is a logOut method present at runtime on some kit implementations that the ConfiguredKit interface does not describe. If that is the case, add logOut?: (evt: SDKEvent) => void to ConfiguredKit and the cast disappears.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Option 2, logOut is real at runtime (MockForwarder defines it and the CBT test asserts logOutCalled === true after logout). Added logOut?: (evt: SDKEvent) => void to ConfiguredKit and dropped the cast.

@sonarqubecloud
Copy link
Copy Markdown

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants