Skip to content

refactor: [SDK-5308] pass an options object to operation constructors with 4 or more parameters - #1501

Merged
sherwinski merged 1 commit into
feat/identity-verificationfrom
sherwin/sdk-5308
Sep 22, 2026
Merged

sherwinski merged 1 commit into
feat/identity-verificationfrom
sherwin/sdk-5308

Conversation

@sherwinski

Copy link
Copy Markdown
Contributor

Description

1 Line Summary

Every operation constructor with 4 or more parameters takes one options object, so externalId can no longer land in the wrong positional slot.

Details

First pull request of the M3 stack for Identity Verification for Web SDK. Targets feat/identity-verification now that the M2 stack is merged. Review follow-up from #1492 (SDK-5227), where externalId pushed 5 constructors to 5 positional parameters and the operations mixed 2 call styles.

Mechanical refactor, no behavior change.

src/core/operations/Operation.ts

  • New exported OperationOpts (appId, onesignalId, externalId?). Operation itself stays positional; RefreshUserOperation (3 params), TrackCustomEventOperation and BaseTagOperation are unchanged.

Base classes take (name, opts)

  • BaseAliasOperation(name, opts?: AliasOpts), BaseSubscriptionOperation(name, opts?: SubscriptionOpts), BaseFullSubscriptionOperation(name, subscription?: SubscriptionWithAppId).
  • BaseSubscriptionOperation now sets subscriptionId for the full subscription operations too, so BaseFullSubscriptionOperation no longer sets it a second time. This moves subscriptionId earlier in toJSON() key order for create and update subscription operations. No consumer or test depends on key order.

Concrete operations take one optional options object

  • SetAliasOperation, DeleteAliasOperation, SetPropertyOperation, DeleteSubscriptionOperation, TransferSubscriptionOperation, LoginUserOperation. The no-argument call that OperationModelStore._create uses to rehydrate rows still works.
  • CreateSubscriptionOperation and UpdateSubscriptionOperation pass their object straight through to the base.
  • The LoginUserOperation options key is existingOnesignalId, the same spelling as the persisted property.

Call sites

  • 8 source call sites and 6 test files updated.
  • In LoginUserOperationExecutor._loginUser, the identify SetAliasOperation passed loginUserOp._externalId twice. A local externalId now shows the 2 roles: value: externalId, externalId.

Bundle size. Object keys are not mangled (the mangle regex is /^_/), so each key name costs bytes. page.es6.js grows by 52 B gzipped (44230 B to 44282 B, +472 B raw). The page limit moves from 44.23 kB to 44.29 kB. sw.js is unchanged.

Systems Affected

  • WebSDK
  • Backend
  • Dashboard

Validation

Tests

Info

No new tests. This is a signature change with no behavior change, and the existing suite covers every constructor:

  • src/core/operations/Operation.test.ts builds every concrete operation in its identified and anonymous form and round-trips each one through OperationModelStore._create.
  • The executor and OperationRepo tests build the same operations through the new options objects.

vp check, vp test (684 tests), and vp run build:prod pass on this branch after the rebase onto the merged M2 stack.

Checklist

  • All the automated tests pass or I explained why that is not possible
  • I have personally tested this on my machine or explained why that is not possible
  • I have included test coverage for these changes or explained why they are not needed

Programming Checklist
Interfaces:

  • Don't use default export
  • New interfaces are in model files

Functions:

  • Don't use default export
  • All function signatures have return types
  • Helpers should not access any data but rather be given the data to operate on.

Typescript:

  • No Typescript warnings
  • Avoid silencing null/undefined warnings with the exclamation point

Other:

  • Iteration: refrain from using elem of array syntax. Prefer forEach or use map
  • Avoid using global OneSignal accessor for context if possible. Instead, we can pass it to function/constructor so that we don't call OneSignal.context

Screenshots

Info

Not needed. No user-visible change.

Checklist

  • I have included screenshots/recordings of the intended results or explained why they are not needed

Related Tickets

…more parameters

SetAliasOperation, SetPropertyOperation, DeleteAliasOperation,
DeleteSubscriptionOperation, TransferSubscriptionOperation and
LoginUserOperation take one options object. The base constructors of
BaseAliasOperation, BaseSubscriptionOperation and
BaseFullSubscriptionOperation take (name, opts). The no-argument call
that OperationModelStore._create uses to rehydrate rows still works.
RefreshUserOperation stays positional.

Object keys are not mangled, so the page bundle grows by 52 B gzipped
(44230 B to 44282 B). The size limit moves to 44.29 kB.

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Multi-model review (Claude Opus 5, GPT 5.6 Sol, Grok 4.7) of the options-object constructor refactor.

Act on: none. The six 4+ parameter operations now take one options object, call sites are converted, and the no-arg path OperationModelStore._create uses still hydrates. No leftover positional constructors for those types.

Consider (2/3 Opus+Sol; Grok no findings): BaseSubscriptionOperation now assigns subscriptionId only when truthy. The old BaseFullSubscriptionOperation wrote subscription.subscriptionId unguarded, so '' is now omitted instead of stored. No current call site produces an empty id — not a live bug, but the delta is slightly larger than a toJSON() key-order change. Assign when !== undefined if you want the old write.

Consider (Opus): Operation itself stays positional (name, appId?, onesignalId?, externalId?). Seven subclasses still forward three interchangeable strings, so the original wrong-slot hazard is one level down. Out of scope as written.

Noted: leftover 5-arg BaseTagOperation (unused); LoginUserOpts.onesignalId is required while OperationModelStore still exempts login; TrackCustomEventOperation still hand-rolls OperationOpts; existingOneSignalId vs existingOnesignalId on the same LoginManager line.

Dismissed: empty subscriptionId as a runtime regression (Grok; Opus impact nil).

Open in Web View Automation 

Sent by Cursor Automation: PR Reviews

if (subscriptionId) this._subscriptionId = subscriptionId;
constructor(operationName: string, opts?: SubscriptionOpts) {
super(operationName, opts?.appId, opts?.onesignalId, opts?.externalId);
if (opts?.subscriptionId) this._subscriptionId = opts.subscriptionId;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

if (opts?.subscriptionId) drops '', which the old BaseFullSubscriptionOperation constructor stored unguarded. No current caller passes an empty id, so this looks inert — assign when !== undefined if you want the previous write.

Comment thread src/core/operations/BaseFullSubscriptionOperation.ts
@sherwinski
sherwinski merged commit 87288bc into feat/identity-verification Sep 22, 2026
2 checks passed
@sherwinski
sherwinski deleted the sherwin/sdk-5308 branch September 22, 2026 23:37
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.

2 participants