@namiml/sdk-core 3.4.4-dev.202607071644 → 3.4.4-dev.202607072319
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +77 -17
- package/dist/index.d.ts +15 -0
- package/dist/index.mjs +77 -17
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -98,7 +98,7 @@ const {
|
|
|
98
98
|
// version — stamped by scripts/version.sh
|
|
99
99
|
NAMI_SDK_VERSION: exports.NAMI_SDK_VERSION = "3.4.4",
|
|
100
100
|
// full package version including dev suffix — stamped by scripts/version.sh
|
|
101
|
-
NAMI_SDK_PACKAGE_VERSION: exports.NAMI_SDK_PACKAGE_VERSION = "3.4.4-dev.
|
|
101
|
+
NAMI_SDK_PACKAGE_VERSION: exports.NAMI_SDK_PACKAGE_VERSION = "3.4.4-dev.202607072319",
|
|
102
102
|
// environments
|
|
103
103
|
PRODUCTION: exports.PRODUCTION = "production", DEVELOPMENT: exports.DEVELOPMENT = "development",
|
|
104
104
|
// error messages
|
|
@@ -61151,12 +61151,6 @@ class EntitlementRepository {
|
|
|
61151
61151
|
if (isAnonymousMode()) {
|
|
61152
61152
|
logger.debug("Skipping active entitlements update - anonymous mode");
|
|
61153
61153
|
}
|
|
61154
|
-
if (hasCapability(exports.Capabilities.THIRD_PARTY_TRANSACTIONS)) {
|
|
61155
|
-
logger.debug("Skipping active entitlements update - paywalls-only mode");
|
|
61156
|
-
}
|
|
61157
|
-
if (!hasCapability(exports.Capabilities.ENTITLEMENT_MANAGEMENT)) {
|
|
61158
|
-
logger.debug("Skipping active entitlements update - no entitlement_management capability");
|
|
61159
|
-
}
|
|
61160
61154
|
}
|
|
61161
61155
|
return activeEntitlements;
|
|
61162
61156
|
}
|
|
@@ -61475,10 +61469,12 @@ class NamiRefs {
|
|
|
61475
61469
|
}
|
|
61476
61470
|
const paywallsDuration = Date.now() - paywallsStartTime;
|
|
61477
61471
|
const totalDuration = Date.now() - startTime;
|
|
61478
|
-
|
|
61479
|
-
`
|
|
61480
|
-
|
|
61481
|
-
|
|
61472
|
+
if (Nami.instance.maxLogging) {
|
|
61473
|
+
logger.info(`Paywall fetch telemetry: strategy=${useIndividual ? "individual" : "bulk"}, ` +
|
|
61474
|
+
`count=${paywalls.length}, total=${totalDuration}ms, ` +
|
|
61475
|
+
`campaigns=${campaignRulesDuration}ms, paywalls=${paywallsDuration}ms` +
|
|
61476
|
+
`${useIndividual ? `, retry_count=${retryCount}` : ""}`);
|
|
61477
|
+
}
|
|
61482
61478
|
return campaignRepo.finalizeCampaignRules(rawCampaigns, paywalls);
|
|
61483
61479
|
}
|
|
61484
61480
|
reRenderPaywall() {
|
|
@@ -61895,6 +61891,13 @@ const NamiHandoffTag = {
|
|
|
61895
61891
|
BuySku: 'buysku',
|
|
61896
61892
|
UserData: 'userdata',
|
|
61897
61893
|
};
|
|
61894
|
+
/**
|
|
61895
|
+
* True iff `tag` is a value of the customer-facing `NamiHandoffTag` vocabulary.
|
|
61896
|
+
* Unknown tags are still delivered (pass-through) — this only drives a warning.
|
|
61897
|
+
*/
|
|
61898
|
+
function isKnownHandoffTag(tag) {
|
|
61899
|
+
return Object.values(NamiHandoffTag).includes(tag);
|
|
61900
|
+
}
|
|
61898
61901
|
/**
|
|
61899
61902
|
* Permission types reportable through a handoff outcome.
|
|
61900
61903
|
*
|
|
@@ -63992,6 +63995,8 @@ function validateForm(validators, formStates) {
|
|
|
63992
63995
|
return errors;
|
|
63993
63996
|
}
|
|
63994
63997
|
|
|
63998
|
+
/** Form field key convention for grouped handoffs: `<tag>::<payloadKey>`. */
|
|
63999
|
+
const HANDOFF_GROUP_KEY = /^([^:]+)::(.+)$/;
|
|
63995
64000
|
class BasicNamiFlow {
|
|
63996
64001
|
constructor(flowObject = {}) {
|
|
63997
64002
|
this.id = flowObject.id ?? '';
|
|
@@ -64760,13 +64765,25 @@ class NamiFlow extends BasicNamiFlow {
|
|
|
64760
64765
|
logger.warn("No formStates available for handoff sequence");
|
|
64761
64766
|
return;
|
|
64762
64767
|
}
|
|
64763
|
-
const keys = Object.keys(formStates)
|
|
64768
|
+
const keys = Object.keys(formStates);
|
|
64764
64769
|
if (keys.length === 0) {
|
|
64765
64770
|
logger.warn("formStates is empty, no handoffs to perform");
|
|
64766
64771
|
return;
|
|
64767
64772
|
}
|
|
64768
|
-
//
|
|
64769
|
-
|
|
64773
|
+
// Grouped path: at least one field uses the `tag::field` convention.
|
|
64774
|
+
if (keys.some((k) => HANDOFF_GROUP_KEY.test(k))) {
|
|
64775
|
+
const groups = this.buildHandoffGroups(formStates);
|
|
64776
|
+
if (groups.length === 0) {
|
|
64777
|
+
logger.info("No handoff groups after grouping");
|
|
64778
|
+
this.manager.resume();
|
|
64779
|
+
return;
|
|
64780
|
+
}
|
|
64781
|
+
this.activeHandoffSequence = { groups };
|
|
64782
|
+
this.resumeNextHandoff();
|
|
64783
|
+
return;
|
|
64784
|
+
}
|
|
64785
|
+
// Legacy path (unchanged): one handoff per true key, tag = field name.
|
|
64786
|
+
const eligibleKeys = keys.sort().filter((k) => formStates[k] === true);
|
|
64770
64787
|
if (eligibleKeys.length === 0) {
|
|
64771
64788
|
logger.info("No eligible handoff keys after filtering");
|
|
64772
64789
|
this.manager.resume();
|
|
@@ -64778,6 +64795,34 @@ class NamiFlow extends BasicNamiFlow {
|
|
|
64778
64795
|
};
|
|
64779
64796
|
this.resumeNextHandoff();
|
|
64780
64797
|
}
|
|
64798
|
+
/**
|
|
64799
|
+
* Reduce a flat `formStates` map into one bucket per distinct handoff tag,
|
|
64800
|
+
* using the `<tag>::<payloadKey>` field-key convention. Insertion order is
|
|
64801
|
+
* preserved (group order = first-appearance). Reserved-prefixed tags are
|
|
64802
|
+
* skipped so they never reach the host handler (NAM-1556). A group is kept
|
|
64803
|
+
* only if at least one of its fields is `true` (a permission the user
|
|
64804
|
+
* declined is not handed off); a kept group's payload rides verbatim,
|
|
64805
|
+
* including its `false` fields.
|
|
64806
|
+
*/
|
|
64807
|
+
buildHandoffGroups(formStates) {
|
|
64808
|
+
const buckets = new Map();
|
|
64809
|
+
for (const key of Object.keys(formStates)) {
|
|
64810
|
+
const match = key.match(HANDOFF_GROUP_KEY);
|
|
64811
|
+
if (!match)
|
|
64812
|
+
continue;
|
|
64813
|
+
const [, tag, field] = match;
|
|
64814
|
+
if (tag.startsWith(RESERVED_HANDOFF_TAG_PREFIX)) {
|
|
64815
|
+
logger.warn(`Ignoring reserved handoff group tag '${tag}'`);
|
|
64816
|
+
continue;
|
|
64817
|
+
}
|
|
64818
|
+
if (!buckets.has(tag))
|
|
64819
|
+
buckets.set(tag, {});
|
|
64820
|
+
buckets.get(tag)[field] = formStates[key];
|
|
64821
|
+
}
|
|
64822
|
+
return Array.from(buckets, ([tag, data]) => ({ tag, data }))
|
|
64823
|
+
.filter((group) => Object.values(group.data).some((value) => value === true))
|
|
64824
|
+
.sort((a, b) => a.tag.localeCompare(b.tag));
|
|
64825
|
+
}
|
|
64781
64826
|
resumeNextHandoff() {
|
|
64782
64827
|
const sequence = this.activeHandoffSequence;
|
|
64783
64828
|
if (!sequence) {
|
|
@@ -64785,6 +64830,24 @@ class NamiFlow extends BasicNamiFlow {
|
|
|
64785
64830
|
this.manager.resume();
|
|
64786
64831
|
return;
|
|
64787
64832
|
}
|
|
64833
|
+
// Grouped path.
|
|
64834
|
+
if ("groups" in sequence) {
|
|
64835
|
+
if (sequence.groups.length === 0) {
|
|
64836
|
+
logger.info("All handoffs in the sequence are complete");
|
|
64837
|
+
this.activeHandoffSequence = null;
|
|
64838
|
+
this.manager.resume();
|
|
64839
|
+
return;
|
|
64840
|
+
}
|
|
64841
|
+
const next = sequence.groups.shift(); // safe: length > 0
|
|
64842
|
+
if (!isKnownHandoffTag(next.tag)) {
|
|
64843
|
+
logger.warn(`handoff group tag '${next.tag}' is outside the NamiHandoffTag vocabulary — passing through`);
|
|
64844
|
+
}
|
|
64845
|
+
this.activeHandoffSequence = sequence; // persist mutated groups
|
|
64846
|
+
logger.debug(`Starting grouped handoff for '${next.tag}' → ${JSON.stringify(next.data)}`);
|
|
64847
|
+
this.manager.deliverHandoff(next.tag, next.data);
|
|
64848
|
+
return;
|
|
64849
|
+
}
|
|
64850
|
+
// Legacy path (unchanged).
|
|
64788
64851
|
if (sequence.remainingKeys.length === 0) {
|
|
64789
64852
|
logger.info("All handoffs in the sequence are complete");
|
|
64790
64853
|
this.activeHandoffSequence = null;
|
|
@@ -64794,10 +64857,7 @@ class NamiFlow extends BasicNamiFlow {
|
|
|
64794
64857
|
const nextKey = sequence.remainingKeys.shift(); // safe: length > 0
|
|
64795
64858
|
const value = sequence.formStates[nextKey];
|
|
64796
64859
|
logger.debug(`Starting handoff for ${nextKey} → ${String(value)}`);
|
|
64797
|
-
// persist updated sequence (remainingKeys mutated)
|
|
64798
64860
|
this.activeHandoffSequence = sequence;
|
|
64799
|
-
// Route through the manager's single delivery point so v2 handlers get
|
|
64800
|
-
// a working complete() for sequence-key handoffs too.
|
|
64801
64861
|
this.manager.deliverHandoff(`${nextKey}`, undefined);
|
|
64802
64862
|
}
|
|
64803
64863
|
}
|
package/dist/index.d.ts
CHANGED
|
@@ -2249,6 +2249,11 @@ declare class NamiFlow extends BasicNamiFlow {
|
|
|
2249
2249
|
activeHandoffSequence: {
|
|
2250
2250
|
formStates: Record<string, boolean | string>;
|
|
2251
2251
|
remainingKeys: string[];
|
|
2252
|
+
} | {
|
|
2253
|
+
groups: Array<{
|
|
2254
|
+
tag: string;
|
|
2255
|
+
data: Record<string, boolean | string>;
|
|
2256
|
+
}>;
|
|
2252
2257
|
} | null;
|
|
2253
2258
|
currentScreenState: TPaywallContext | null;
|
|
2254
2259
|
isPaused: boolean;
|
|
@@ -2357,6 +2362,16 @@ declare class NamiFlow extends BasicNamiFlow {
|
|
|
2357
2362
|
*/
|
|
2358
2363
|
buildUserDataEnvelope(formId: string): NamiUserDataEnvelope;
|
|
2359
2364
|
flowHandoffFormSequence(): void;
|
|
2365
|
+
/**
|
|
2366
|
+
* Reduce a flat `formStates` map into one bucket per distinct handoff tag,
|
|
2367
|
+
* using the `<tag>::<payloadKey>` field-key convention. Insertion order is
|
|
2368
|
+
* preserved (group order = first-appearance). Reserved-prefixed tags are
|
|
2369
|
+
* skipped so they never reach the host handler (NAM-1556). A group is kept
|
|
2370
|
+
* only if at least one of its fields is `true` (a permission the user
|
|
2371
|
+
* declined is not handed off); a kept group's payload rides verbatim,
|
|
2372
|
+
* including its `false` fields.
|
|
2373
|
+
*/
|
|
2374
|
+
private buildHandoffGroups;
|
|
2360
2375
|
resumeNextHandoff(): void;
|
|
2361
2376
|
}
|
|
2362
2377
|
|
package/dist/index.mjs
CHANGED
|
@@ -96,7 +96,7 @@ const {
|
|
|
96
96
|
// version — stamped by scripts/version.sh
|
|
97
97
|
NAMI_SDK_VERSION = "3.4.4",
|
|
98
98
|
// full package version including dev suffix — stamped by scripts/version.sh
|
|
99
|
-
NAMI_SDK_PACKAGE_VERSION = "3.4.4-dev.
|
|
99
|
+
NAMI_SDK_PACKAGE_VERSION = "3.4.4-dev.202607072319",
|
|
100
100
|
// environments
|
|
101
101
|
PRODUCTION = "production", DEVELOPMENT = "development",
|
|
102
102
|
// error messages
|
|
@@ -61149,12 +61149,6 @@ class EntitlementRepository {
|
|
|
61149
61149
|
if (isAnonymousMode()) {
|
|
61150
61150
|
logger.debug("Skipping active entitlements update - anonymous mode");
|
|
61151
61151
|
}
|
|
61152
|
-
if (hasCapability(Capabilities.THIRD_PARTY_TRANSACTIONS)) {
|
|
61153
|
-
logger.debug("Skipping active entitlements update - paywalls-only mode");
|
|
61154
|
-
}
|
|
61155
|
-
if (!hasCapability(Capabilities.ENTITLEMENT_MANAGEMENT)) {
|
|
61156
|
-
logger.debug("Skipping active entitlements update - no entitlement_management capability");
|
|
61157
|
-
}
|
|
61158
61152
|
}
|
|
61159
61153
|
return activeEntitlements;
|
|
61160
61154
|
}
|
|
@@ -61473,10 +61467,12 @@ class NamiRefs {
|
|
|
61473
61467
|
}
|
|
61474
61468
|
const paywallsDuration = Date.now() - paywallsStartTime;
|
|
61475
61469
|
const totalDuration = Date.now() - startTime;
|
|
61476
|
-
|
|
61477
|
-
`
|
|
61478
|
-
|
|
61479
|
-
|
|
61470
|
+
if (Nami.instance.maxLogging) {
|
|
61471
|
+
logger.info(`Paywall fetch telemetry: strategy=${useIndividual ? "individual" : "bulk"}, ` +
|
|
61472
|
+
`count=${paywalls.length}, total=${totalDuration}ms, ` +
|
|
61473
|
+
`campaigns=${campaignRulesDuration}ms, paywalls=${paywallsDuration}ms` +
|
|
61474
|
+
`${useIndividual ? `, retry_count=${retryCount}` : ""}`);
|
|
61475
|
+
}
|
|
61480
61476
|
return campaignRepo.finalizeCampaignRules(rawCampaigns, paywalls);
|
|
61481
61477
|
}
|
|
61482
61478
|
reRenderPaywall() {
|
|
@@ -61893,6 +61889,13 @@ const NamiHandoffTag = {
|
|
|
61893
61889
|
BuySku: 'buysku',
|
|
61894
61890
|
UserData: 'userdata',
|
|
61895
61891
|
};
|
|
61892
|
+
/**
|
|
61893
|
+
* True iff `tag` is a value of the customer-facing `NamiHandoffTag` vocabulary.
|
|
61894
|
+
* Unknown tags are still delivered (pass-through) — this only drives a warning.
|
|
61895
|
+
*/
|
|
61896
|
+
function isKnownHandoffTag(tag) {
|
|
61897
|
+
return Object.values(NamiHandoffTag).includes(tag);
|
|
61898
|
+
}
|
|
61896
61899
|
/**
|
|
61897
61900
|
* Permission types reportable through a handoff outcome.
|
|
61898
61901
|
*
|
|
@@ -63990,6 +63993,8 @@ function validateForm(validators, formStates) {
|
|
|
63990
63993
|
return errors;
|
|
63991
63994
|
}
|
|
63992
63995
|
|
|
63996
|
+
/** Form field key convention for grouped handoffs: `<tag>::<payloadKey>`. */
|
|
63997
|
+
const HANDOFF_GROUP_KEY = /^([^:]+)::(.+)$/;
|
|
63993
63998
|
class BasicNamiFlow {
|
|
63994
63999
|
constructor(flowObject = {}) {
|
|
63995
64000
|
this.id = flowObject.id ?? '';
|
|
@@ -64758,13 +64763,25 @@ class NamiFlow extends BasicNamiFlow {
|
|
|
64758
64763
|
logger.warn("No formStates available for handoff sequence");
|
|
64759
64764
|
return;
|
|
64760
64765
|
}
|
|
64761
|
-
const keys = Object.keys(formStates)
|
|
64766
|
+
const keys = Object.keys(formStates);
|
|
64762
64767
|
if (keys.length === 0) {
|
|
64763
64768
|
logger.warn("formStates is empty, no handoffs to perform");
|
|
64764
64769
|
return;
|
|
64765
64770
|
}
|
|
64766
|
-
//
|
|
64767
|
-
|
|
64771
|
+
// Grouped path: at least one field uses the `tag::field` convention.
|
|
64772
|
+
if (keys.some((k) => HANDOFF_GROUP_KEY.test(k))) {
|
|
64773
|
+
const groups = this.buildHandoffGroups(formStates);
|
|
64774
|
+
if (groups.length === 0) {
|
|
64775
|
+
logger.info("No handoff groups after grouping");
|
|
64776
|
+
this.manager.resume();
|
|
64777
|
+
return;
|
|
64778
|
+
}
|
|
64779
|
+
this.activeHandoffSequence = { groups };
|
|
64780
|
+
this.resumeNextHandoff();
|
|
64781
|
+
return;
|
|
64782
|
+
}
|
|
64783
|
+
// Legacy path (unchanged): one handoff per true key, tag = field name.
|
|
64784
|
+
const eligibleKeys = keys.sort().filter((k) => formStates[k] === true);
|
|
64768
64785
|
if (eligibleKeys.length === 0) {
|
|
64769
64786
|
logger.info("No eligible handoff keys after filtering");
|
|
64770
64787
|
this.manager.resume();
|
|
@@ -64776,6 +64793,34 @@ class NamiFlow extends BasicNamiFlow {
|
|
|
64776
64793
|
};
|
|
64777
64794
|
this.resumeNextHandoff();
|
|
64778
64795
|
}
|
|
64796
|
+
/**
|
|
64797
|
+
* Reduce a flat `formStates` map into one bucket per distinct handoff tag,
|
|
64798
|
+
* using the `<tag>::<payloadKey>` field-key convention. Insertion order is
|
|
64799
|
+
* preserved (group order = first-appearance). Reserved-prefixed tags are
|
|
64800
|
+
* skipped so they never reach the host handler (NAM-1556). A group is kept
|
|
64801
|
+
* only if at least one of its fields is `true` (a permission the user
|
|
64802
|
+
* declined is not handed off); a kept group's payload rides verbatim,
|
|
64803
|
+
* including its `false` fields.
|
|
64804
|
+
*/
|
|
64805
|
+
buildHandoffGroups(formStates) {
|
|
64806
|
+
const buckets = new Map();
|
|
64807
|
+
for (const key of Object.keys(formStates)) {
|
|
64808
|
+
const match = key.match(HANDOFF_GROUP_KEY);
|
|
64809
|
+
if (!match)
|
|
64810
|
+
continue;
|
|
64811
|
+
const [, tag, field] = match;
|
|
64812
|
+
if (tag.startsWith(RESERVED_HANDOFF_TAG_PREFIX)) {
|
|
64813
|
+
logger.warn(`Ignoring reserved handoff group tag '${tag}'`);
|
|
64814
|
+
continue;
|
|
64815
|
+
}
|
|
64816
|
+
if (!buckets.has(tag))
|
|
64817
|
+
buckets.set(tag, {});
|
|
64818
|
+
buckets.get(tag)[field] = formStates[key];
|
|
64819
|
+
}
|
|
64820
|
+
return Array.from(buckets, ([tag, data]) => ({ tag, data }))
|
|
64821
|
+
.filter((group) => Object.values(group.data).some((value) => value === true))
|
|
64822
|
+
.sort((a, b) => a.tag.localeCompare(b.tag));
|
|
64823
|
+
}
|
|
64779
64824
|
resumeNextHandoff() {
|
|
64780
64825
|
const sequence = this.activeHandoffSequence;
|
|
64781
64826
|
if (!sequence) {
|
|
@@ -64783,6 +64828,24 @@ class NamiFlow extends BasicNamiFlow {
|
|
|
64783
64828
|
this.manager.resume();
|
|
64784
64829
|
return;
|
|
64785
64830
|
}
|
|
64831
|
+
// Grouped path.
|
|
64832
|
+
if ("groups" in sequence) {
|
|
64833
|
+
if (sequence.groups.length === 0) {
|
|
64834
|
+
logger.info("All handoffs in the sequence are complete");
|
|
64835
|
+
this.activeHandoffSequence = null;
|
|
64836
|
+
this.manager.resume();
|
|
64837
|
+
return;
|
|
64838
|
+
}
|
|
64839
|
+
const next = sequence.groups.shift(); // safe: length > 0
|
|
64840
|
+
if (!isKnownHandoffTag(next.tag)) {
|
|
64841
|
+
logger.warn(`handoff group tag '${next.tag}' is outside the NamiHandoffTag vocabulary — passing through`);
|
|
64842
|
+
}
|
|
64843
|
+
this.activeHandoffSequence = sequence; // persist mutated groups
|
|
64844
|
+
logger.debug(`Starting grouped handoff for '${next.tag}' → ${JSON.stringify(next.data)}`);
|
|
64845
|
+
this.manager.deliverHandoff(next.tag, next.data);
|
|
64846
|
+
return;
|
|
64847
|
+
}
|
|
64848
|
+
// Legacy path (unchanged).
|
|
64786
64849
|
if (sequence.remainingKeys.length === 0) {
|
|
64787
64850
|
logger.info("All handoffs in the sequence are complete");
|
|
64788
64851
|
this.activeHandoffSequence = null;
|
|
@@ -64792,10 +64855,7 @@ class NamiFlow extends BasicNamiFlow {
|
|
|
64792
64855
|
const nextKey = sequence.remainingKeys.shift(); // safe: length > 0
|
|
64793
64856
|
const value = sequence.formStates[nextKey];
|
|
64794
64857
|
logger.debug(`Starting handoff for ${nextKey} → ${String(value)}`);
|
|
64795
|
-
// persist updated sequence (remainingKeys mutated)
|
|
64796
64858
|
this.activeHandoffSequence = sequence;
|
|
64797
|
-
// Route through the manager's single delivery point so v2 handlers get
|
|
64798
|
-
// a working complete() for sequence-key handoffs too.
|
|
64799
64859
|
this.manager.deliverHandoff(`${nextKey}`, undefined);
|
|
64800
64860
|
}
|
|
64801
64861
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@namiml/sdk-core",
|
|
3
|
-
"version": "3.4.4-dev.
|
|
3
|
+
"version": "3.4.4-dev.202607072319",
|
|
4
4
|
"description": "Platform-agnostic core for the Nami SDK — business logic, API, types, and state management",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.cjs",
|