refactor: [SDK-5308] pass an options object to operation constructors with 4 or more parameters - #1501
Conversation
…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.
There was a problem hiding this comment.
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).
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; |
There was a problem hiding this comment.
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.


Description
1 Line Summary
Every operation constructor with 4 or more parameters takes one options object, so
externalIdcan 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-verificationnow that the M2 stack is merged. Review follow-up from #1492 (SDK-5227), whereexternalIdpushed 5 constructors to 5 positional parameters and the operations mixed 2 call styles.Mechanical refactor, no behavior change.
src/core/operations/Operation.tsOperationOpts(appId,onesignalId,externalId?).Operationitself stays positional;RefreshUserOperation(3 params),TrackCustomEventOperationandBaseTagOperationare unchanged.Base classes take
(name, opts)BaseAliasOperation(name, opts?: AliasOpts),BaseSubscriptionOperation(name, opts?: SubscriptionOpts),BaseFullSubscriptionOperation(name, subscription?: SubscriptionWithAppId).BaseSubscriptionOperationnow setssubscriptionIdfor the full subscription operations too, soBaseFullSubscriptionOperationno longer sets it a second time. This movessubscriptionIdearlier intoJSON()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 thatOperationModelStore._createuses to rehydrate rows still works.CreateSubscriptionOperationandUpdateSubscriptionOperationpass their object straight through to the base.LoginUserOperationoptions key isexistingOnesignalId, the same spelling as the persisted property.Call sites
LoginUserOperationExecutor._loginUser, the identifySetAliasOperationpassedloginUserOp._externalIdtwice. A localexternalIdnow 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.jsgrows 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.jsis unchanged.Systems Affected
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.tsbuilds every concrete operation in its identified and anonymous form and round-trips each one throughOperationModelStore._create.OperationRepotests build the same operations through the new options objects.vp check,vp test(684 tests), andvp run build:prodpass on this branch after the rebase onto the merged M2 stack.Checklist
Programming Checklist
Interfaces:
Functions:
Typescript:
Other:
elem of arraysyntax. PreferforEachor usemapcontextif possible. Instead, we can pass it to function/constructor so that we don't callOneSignal.contextScreenshots
Info
Not needed. No user-visible change.
Checklist
Related Tickets