@namiml/sdk-core 3.4.3-dev.202606180014 → 3.4.3-dev.202606181527
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 +216 -2
- package/dist/index.d.ts +111 -3
- package/dist/index.mjs +215 -3
- 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.3",
|
|
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.3-dev.
|
|
101
|
+
NAMI_SDK_PACKAGE_VERSION: exports.NAMI_SDK_PACKAGE_VERSION = "3.4.3-dev.202606181527",
|
|
102
102
|
// environments
|
|
103
103
|
PRODUCTION: exports.PRODUCTION = "production", DEVELOPMENT: exports.DEVELOPMENT = "development",
|
|
104
104
|
// error messages
|
|
@@ -3193,6 +3193,22 @@ class StorageService {
|
|
|
3193
3193
|
.filter(key => key.includes(exports.CUSTOMER_ATTRIBUTES_KEY_PREFIX))
|
|
3194
3194
|
.map(key => key.replace(exports.CUSTOMER_ATTRIBUTES_KEY_PREFIX, ""));
|
|
3195
3195
|
}
|
|
3196
|
+
/**
|
|
3197
|
+
* All persisted customer attributes as a `{ key: value }` map. Used to seed
|
|
3198
|
+
* `PaywallState.customer` when a paywall screen initializes, so attributes set
|
|
3199
|
+
* on an earlier flow screen (e.g. via setTagsFromForm) are available to the
|
|
3200
|
+
* `${customer.x}` smart-text resolver on subsequent screens.
|
|
3201
|
+
*/
|
|
3202
|
+
getAllCustomerAttributesMap() {
|
|
3203
|
+
const out = {};
|
|
3204
|
+
this.getAllCustomerAttributesKeys().forEach((key) => {
|
|
3205
|
+
const value = this.getCustomerAttribute(key);
|
|
3206
|
+
if (value !== null && value !== undefined) {
|
|
3207
|
+
out[key] = String(value);
|
|
3208
|
+
}
|
|
3209
|
+
});
|
|
3210
|
+
return out;
|
|
3211
|
+
}
|
|
3196
3212
|
clearCustomerAttribute(attribute) {
|
|
3197
3213
|
const key = exports.CUSTOMER_ATTRIBUTES_KEY_PREFIX + attribute;
|
|
3198
3214
|
const itemExists = this.getItem(key);
|
|
@@ -11317,6 +11333,8 @@ const initialState = {
|
|
|
11317
11333
|
groups: [],
|
|
11318
11334
|
selectedProducts: {},
|
|
11319
11335
|
formStates: {},
|
|
11336
|
+
formFieldErrors: {},
|
|
11337
|
+
formFieldValidators: {},
|
|
11320
11338
|
timerStates: {},
|
|
11321
11339
|
currentPage: 'page1',
|
|
11322
11340
|
pageHistory: ['page1'],
|
|
@@ -11419,6 +11437,11 @@ class PaywallState extends SimpleEventTarget {
|
|
|
11419
11437
|
this.state = cloneDeep(initialState);
|
|
11420
11438
|
const profile = storageService.getNamiProfile();
|
|
11421
11439
|
this.state.isLoggedIn = !!profile?.externalId;
|
|
11440
|
+
// Seed customer attributes persisted on earlier screens (e.g. via
|
|
11441
|
+
// setTagsFromForm / setTags) so `${customer.x}` smart text resolves on every
|
|
11442
|
+
// flow screen — each screen builds a fresh provider, so without this the
|
|
11443
|
+
// value captured on page 1 would be invisible on page 2.
|
|
11444
|
+
this.state.customer = { ...this.state.customer, ...storageService.getAllCustomerAttributesMap() };
|
|
11422
11445
|
this.setPaywall(paywall, context, campaign);
|
|
11423
11446
|
this.subscribers = new Set();
|
|
11424
11447
|
}
|
|
@@ -11456,6 +11479,41 @@ class PaywallState extends SimpleEventTarget {
|
|
|
11456
11479
|
setFormState(formId, state) {
|
|
11457
11480
|
this.setState({ ...this.state, formStates: { ...this.state.formStates, [formId]: state } });
|
|
11458
11481
|
}
|
|
11482
|
+
/**
|
|
11483
|
+
* Register the validator for a text-input field (keyed by formId). Used by the
|
|
11484
|
+
* implicit form-submit gate in NamiFlow.triggerActions and by the web element
|
|
11485
|
+
* when it mounts.
|
|
11486
|
+
*/
|
|
11487
|
+
registerFormFieldValidator(formId, validator) {
|
|
11488
|
+
this.setState({
|
|
11489
|
+
...this.state,
|
|
11490
|
+
formFieldValidators: { ...this.state.formFieldValidators, [formId]: validator },
|
|
11491
|
+
});
|
|
11492
|
+
}
|
|
11493
|
+
getFormFieldValidators() {
|
|
11494
|
+
return this.state.formFieldValidators;
|
|
11495
|
+
}
|
|
11496
|
+
/**
|
|
11497
|
+
* Replace the full set of field errors (formId → message). The gate writes the
|
|
11498
|
+
* result of validateForm here; an empty map clears all errors.
|
|
11499
|
+
*/
|
|
11500
|
+
setFormFieldErrors(errors) {
|
|
11501
|
+
this.setState({ ...this.state, formFieldErrors: { ...errors } });
|
|
11502
|
+
}
|
|
11503
|
+
setFormFieldError(formId, message) {
|
|
11504
|
+
this.setState({
|
|
11505
|
+
...this.state,
|
|
11506
|
+
formFieldErrors: { ...this.state.formFieldErrors, [formId]: message },
|
|
11507
|
+
});
|
|
11508
|
+
}
|
|
11509
|
+
clearFormFieldError(formId) {
|
|
11510
|
+
const nextErrors = { ...this.state.formFieldErrors };
|
|
11511
|
+
delete nextErrors[formId];
|
|
11512
|
+
this.setState({ ...this.state, formFieldErrors: nextErrors });
|
|
11513
|
+
}
|
|
11514
|
+
getFormFieldErrors() {
|
|
11515
|
+
return this.state.formFieldErrors;
|
|
11516
|
+
}
|
|
11459
11517
|
setTimerState(timerId, remainingSeconds, savedAt, hasEmittedCompletion) {
|
|
11460
11518
|
this.setState({
|
|
11461
11519
|
...this.state,
|
|
@@ -11674,7 +11732,12 @@ class PaywallState extends SimpleEventTarget {
|
|
|
11674
11732
|
this.emitEvent('user-interaction-changed', { enabled });
|
|
11675
11733
|
}
|
|
11676
11734
|
setUserTags(tags) {
|
|
11677
|
-
|
|
11735
|
+
// User tags ARE customer attributes for personalization — they must land in
|
|
11736
|
+
// `state.customer`, the store the `${customer.x}` smart-text resolver reads
|
|
11737
|
+
// (see valueFromSmartText: replacements.customer = state.customer). Writing a
|
|
11738
|
+
// separate `state.userTags` (read nowhere) meant setTags / setTagsFromForm
|
|
11739
|
+
// values never reached `${customer.firstName}` on a subsequent flow page.
|
|
11740
|
+
this.setState({ ...this.state, customer: { ...this.state.customer, ...tags } });
|
|
11678
11741
|
}
|
|
11679
11742
|
setCurrentSlideIndex(index) {
|
|
11680
11743
|
this.setState({ ...this.state, currentSlideIndex: index });
|
|
@@ -12551,6 +12614,7 @@ exports.NamiFlowActionFunction = void 0;
|
|
|
12551
12614
|
NamiFlowActionFunction["FLOW_ENABLED"] = "flowInteractionEnabled";
|
|
12552
12615
|
NamiFlowActionFunction["FLOW_DISABLED"] = "flowInteractionDisabled";
|
|
12553
12616
|
NamiFlowActionFunction["SET_TAGS"] = "setTags";
|
|
12617
|
+
NamiFlowActionFunction["SET_TAGS_FROM_FORM"] = "setTagsFromForm";
|
|
12554
12618
|
NamiFlowActionFunction["PAUSE"] = "flowPause";
|
|
12555
12619
|
NamiFlowActionFunction["RESUME"] = "flowResume";
|
|
12556
12620
|
NamiFlowActionFunction["SET_LAUNCH_CONTEXT"] = "setLaunchContext";
|
|
@@ -13077,6 +13141,42 @@ class URLParams {
|
|
|
13077
13141
|
URLParams._instance = null;
|
|
13078
13142
|
const getUrlParams = () => URLParams.instance.params;
|
|
13079
13143
|
|
|
13144
|
+
/**
|
|
13145
|
+
* Coerce a single form-state value to the string form used by tags / customer
|
|
13146
|
+
* attributes / `{{ form.x }}` interpolation. Shared by the `setTagsFromForm`
|
|
13147
|
+
* flow action and FlowLiquidResolver so they stay in lockstep.
|
|
13148
|
+
*
|
|
13149
|
+
* Rules (order matters — Boolean is checked before number):
|
|
13150
|
+
* - `null` / `undefined` → skip (returns `undefined`)
|
|
13151
|
+
* - empty string `""` → skip
|
|
13152
|
+
* - `false` → "false", `true` → "true" (booleans are kept)
|
|
13153
|
+
* - `0` → "0" (zero is kept)
|
|
13154
|
+
* - integral number → no trailing ".0" (e.g. 3 → "3", 3.5 → "3.5")
|
|
13155
|
+
* - everything else → String(value)
|
|
13156
|
+
*/
|
|
13157
|
+
function formTagValue(value) {
|
|
13158
|
+
if (value === null || value === undefined) {
|
|
13159
|
+
return undefined;
|
|
13160
|
+
}
|
|
13161
|
+
if (value === '') {
|
|
13162
|
+
return undefined;
|
|
13163
|
+
}
|
|
13164
|
+
// Boolean BEFORE number: typeof true !== 'number', but be explicit so the
|
|
13165
|
+
// intent (and the false→"false" / true→"true" mapping) is unmistakable.
|
|
13166
|
+
if (typeof value === 'boolean') {
|
|
13167
|
+
return value ? 'true' : 'false';
|
|
13168
|
+
}
|
|
13169
|
+
if (typeof value === 'number') {
|
|
13170
|
+
if (!Number.isFinite(value)) {
|
|
13171
|
+
return undefined;
|
|
13172
|
+
}
|
|
13173
|
+
// JS `String` already renders integers without a trailing ".0"
|
|
13174
|
+
// (3 → "3", 3.5 → "3.5"), which is exactly the contract we want.
|
|
13175
|
+
return String(value);
|
|
13176
|
+
}
|
|
13177
|
+
return String(value);
|
|
13178
|
+
}
|
|
13179
|
+
|
|
13080
13180
|
const LIQUID_VAR_REGEX = /\{\{\s*([^}]+?)\s*\}\}/g;
|
|
13081
13181
|
/**
|
|
13082
13182
|
* Resolver for liquid-style flow variables, without requiring namespace prefixes.
|
|
@@ -13101,6 +13201,13 @@ class FlowLiquidResolver {
|
|
|
13101
13201
|
}
|
|
13102
13202
|
return undefined;
|
|
13103
13203
|
}
|
|
13204
|
+
// Handle form field paths: {{ form.fieldId }} reads the live form state and
|
|
13205
|
+
// coerces it through the same helper used by setTagsFromForm.
|
|
13206
|
+
if (key.startsWith("form.")) {
|
|
13207
|
+
const fieldId = key.substring("form.".length);
|
|
13208
|
+
const value = PaywallState.currentProvider?.state.formStates[fieldId];
|
|
13209
|
+
return formTagValue(value);
|
|
13210
|
+
}
|
|
13104
13211
|
switch (key) {
|
|
13105
13212
|
case "campaignId":
|
|
13106
13213
|
return screenState?.campaign?.rule;
|
|
@@ -14102,6 +14209,67 @@ let NamiCustomerManager$2 = class NamiCustomerManager {
|
|
|
14102
14209
|
};
|
|
14103
14210
|
NamiCustomerManager$2.instance = new NamiCustomerManager$2();
|
|
14104
14211
|
|
|
14212
|
+
/**
|
|
14213
|
+
* Pure, platform-agnostic text-input validation. Shared by the flow submit gate
|
|
14214
|
+
* (sdk/core) and the web Lit element (sdk/web). The rules are identical to the
|
|
14215
|
+
* Apple (NAM-1142) and Android (NAM-1143) ports.
|
|
14216
|
+
*/
|
|
14217
|
+
const DEFAULT_MESSAGES = {
|
|
14218
|
+
required: 'This field is required',
|
|
14219
|
+
email: 'Please enter a valid email address',
|
|
14220
|
+
name: 'Please enter at least 2 characters',
|
|
14221
|
+
};
|
|
14222
|
+
/**
|
|
14223
|
+
* An email must contain an `@` and a domain with a dot, e.g. `a@b.co`.
|
|
14224
|
+
* `a@b` (no dotted domain) and `nope` (no `@`) are invalid.
|
|
14225
|
+
*/
|
|
14226
|
+
const EMAIL_REGEX = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
|
14227
|
+
/**
|
|
14228
|
+
* Validate a single field. Returns `undefined` when valid, otherwise a default
|
|
14229
|
+
* error message for the failing rule. Callers that want author-supplied text
|
|
14230
|
+
* should use {@link validateForm}.
|
|
14231
|
+
*
|
|
14232
|
+
* Rules:
|
|
14233
|
+
* - required && blank → invalid
|
|
14234
|
+
* - email && non-blank && not email-format → invalid
|
|
14235
|
+
* - name && non-blank && len < 2 → invalid
|
|
14236
|
+
* - freeText → only the required rule applies
|
|
14237
|
+
*/
|
|
14238
|
+
function validateTextInput(type, required, value) {
|
|
14239
|
+
const text = typeof value === 'string' ? value : value == null ? '' : String(value);
|
|
14240
|
+
const trimmed = text.trim();
|
|
14241
|
+
if (required && trimmed.length === 0) {
|
|
14242
|
+
return DEFAULT_MESSAGES.required;
|
|
14243
|
+
}
|
|
14244
|
+
if (trimmed.length === 0) {
|
|
14245
|
+
// Blank optional value: no format rule applies.
|
|
14246
|
+
return undefined;
|
|
14247
|
+
}
|
|
14248
|
+
if (type === 'email' && !EMAIL_REGEX.test(trimmed)) {
|
|
14249
|
+
return DEFAULT_MESSAGES.email;
|
|
14250
|
+
}
|
|
14251
|
+
if (type === 'name' && trimmed.length < 2) {
|
|
14252
|
+
return DEFAULT_MESSAGES.name;
|
|
14253
|
+
}
|
|
14254
|
+
return undefined;
|
|
14255
|
+
}
|
|
14256
|
+
/**
|
|
14257
|
+
* Validate every registered field against the current form state. Returns a map
|
|
14258
|
+
* of `formId → error message` for invalid fields only (valid fields are omitted).
|
|
14259
|
+
* The validator's own `message` is preferred; otherwise the per-rule default
|
|
14260
|
+
* from {@link validateTextInput} is used.
|
|
14261
|
+
*/
|
|
14262
|
+
function validateForm(validators, formStates) {
|
|
14263
|
+
const errors = {};
|
|
14264
|
+
for (const [formId, validator] of Object.entries(validators)) {
|
|
14265
|
+
const defaultError = validateTextInput(validator.type, validator.required, formStates[formId]);
|
|
14266
|
+
if (defaultError !== undefined) {
|
|
14267
|
+
errors[formId] = validator.message ?? defaultError;
|
|
14268
|
+
}
|
|
14269
|
+
}
|
|
14270
|
+
return errors;
|
|
14271
|
+
}
|
|
14272
|
+
|
|
14105
14273
|
class BasicNamiFlow {
|
|
14106
14274
|
constructor(flowObject = {}) {
|
|
14107
14275
|
this.id = flowObject.id ?? '';
|
|
@@ -14333,12 +14501,40 @@ class NamiFlow extends BasicNamiFlow {
|
|
|
14333
14501
|
}
|
|
14334
14502
|
return lifecycles;
|
|
14335
14503
|
}
|
|
14504
|
+
/**
|
|
14505
|
+
* A lifecycle chain is a "form submit" if any phase (before/action/after) runs
|
|
14506
|
+
* one of the advancing functions: setTagsFromForm, flowNext, or flowNav. The
|
|
14507
|
+
* POC submit buttons use flowNext/flowNav (no dedicated submit action), so the
|
|
14508
|
+
* gate keys off these. Close/dismiss/back/pause are deliberately excluded —
|
|
14509
|
+
* they must never be blocked by validation. Mirrors Apple
|
|
14510
|
+
* NamiFlowControl.isFormSubmitLifecycle / Android NamiFlow.isFormSubmitLifecycle.
|
|
14511
|
+
*/
|
|
14512
|
+
isFormSubmitLifecycle(lifecycles) {
|
|
14513
|
+
const submitFns = [
|
|
14514
|
+
exports.NamiFlowActionFunction.SET_TAGS_FROM_FORM,
|
|
14515
|
+
exports.NamiFlowActionFunction.NEXT,
|
|
14516
|
+
exports.NamiFlowActionFunction.NAVIGATE,
|
|
14517
|
+
];
|
|
14518
|
+
return lifecycles.some(cycle => [cycle.before, cycle.action, cycle.after].some(actions => actions?.some(action => submitFns.includes(action.function))));
|
|
14519
|
+
}
|
|
14336
14520
|
triggerActions(actionId, component, data) {
|
|
14337
14521
|
const lifecycles = this.lifecycles(actionId);
|
|
14338
14522
|
if (!lifecycles)
|
|
14339
14523
|
return;
|
|
14340
14524
|
this.flowLog(`triggerActions(${actionId}) — step=${this.currentFlowStep?.id}, isPaused=${this.isPaused}`);
|
|
14341
14525
|
this.currentButton = component;
|
|
14526
|
+
// Implicit form-submit validation gate (the NAM-1143 on-device lesson).
|
|
14527
|
+
// When this action advances the flow and validators are registered, validate
|
|
14528
|
+
// first; write the errors and halt the chain if any field is invalid.
|
|
14529
|
+
const validators = PaywallState.currentProvider?.state.formFieldValidators ?? {};
|
|
14530
|
+
if (this.isFormSubmitLifecycle(lifecycles) && Object.keys(validators).length) {
|
|
14531
|
+
const errors = validateForm(validators, this.getFormData());
|
|
14532
|
+
PaywallState.currentProvider?.setFormFieldErrors(errors);
|
|
14533
|
+
if (Object.keys(errors).length) {
|
|
14534
|
+
this.flowLog(`triggerActions(${actionId}) — HALTED by form validation: ${JSON.stringify(errors)}`);
|
|
14535
|
+
return;
|
|
14536
|
+
}
|
|
14537
|
+
}
|
|
14342
14538
|
this.executeFullLifecycles(lifecycles, data);
|
|
14343
14539
|
}
|
|
14344
14540
|
executeFullLifecycles(lifecycles, data) {
|
|
@@ -14545,6 +14741,22 @@ class NamiFlow extends BasicNamiFlow {
|
|
|
14545
14741
|
});
|
|
14546
14742
|
}
|
|
14547
14743
|
break;
|
|
14744
|
+
case exports.NamiFlowActionFunction.SET_TAGS_FROM_FORM: {
|
|
14745
|
+
const formData = this.getFormData();
|
|
14746
|
+
const tags = {};
|
|
14747
|
+
Object.entries(formData).forEach(([key, raw]) => {
|
|
14748
|
+
const value = formTagValue(raw);
|
|
14749
|
+
if (value === undefined) {
|
|
14750
|
+
return; // skip null / "" (false and 0 are kept by formTagValue)
|
|
14751
|
+
}
|
|
14752
|
+
tags[key] = value;
|
|
14753
|
+
NamiCustomerManager$2.setCustomerAttribute(key, value);
|
|
14754
|
+
});
|
|
14755
|
+
if (Object.keys(tags).length) {
|
|
14756
|
+
PaywallState.setUserTags(tags);
|
|
14757
|
+
}
|
|
14758
|
+
break;
|
|
14759
|
+
}
|
|
14548
14760
|
case exports.NamiFlowActionFunction.SET_LAUNCH_CONTEXT: {
|
|
14549
14761
|
// Two supported shapes: { customAttributes: {...} } (nav-action wire shape)
|
|
14550
14762
|
// and { key, value } (used by Apple/Android/Roku). We deliberately do NOT
|
|
@@ -64908,4 +65120,6 @@ exports.tryParseB64Gzip = tryParseB64Gzip;
|
|
|
64908
65120
|
exports.tryParseJson = tryParseJson;
|
|
64909
65121
|
exports.updateRelatedSKUsForNamiEntitlement = updateRelatedSKUsForNamiEntitlement;
|
|
64910
65122
|
exports.uuidFromSplitPosition = uuidFromSplitPosition;
|
|
65123
|
+
exports.validateForm = validateForm;
|
|
64911
65124
|
exports.validateMinSDKVersion = validateMinSDKVersion;
|
|
65125
|
+
exports.validateTextInput = validateTextInput;
|
package/dist/index.d.ts
CHANGED
|
@@ -133,6 +133,7 @@ declare enum NamiFlowActionFunction {
|
|
|
133
133
|
FLOW_ENABLED = "flowInteractionEnabled",
|
|
134
134
|
FLOW_DISABLED = "flowInteractionDisabled",
|
|
135
135
|
SET_TAGS = "setTags",
|
|
136
|
+
SET_TAGS_FROM_FORM = "setTagsFromForm",
|
|
136
137
|
PAUSE = "flowPause",
|
|
137
138
|
RESUME = "flowResume",
|
|
138
139
|
SET_LAUNCH_CONTEXT = "setLaunchContext"
|
|
@@ -553,6 +554,36 @@ type TToggleButtonComponent = TBaseComponent & {
|
|
|
553
554
|
mode?: "radio" | "toggle";
|
|
554
555
|
value?: string;
|
|
555
556
|
};
|
|
557
|
+
type TTextInputComponent = TBaseComponent & {
|
|
558
|
+
component: "textInput";
|
|
559
|
+
type?: "name" | "email";
|
|
560
|
+
formId: string;
|
|
561
|
+
/** Required flag — the real (misspelled) wire key. `required` never populates. */
|
|
562
|
+
reqed?: boolean;
|
|
563
|
+
validateOn?: "submit" | "change";
|
|
564
|
+
placeholderText?: string;
|
|
565
|
+
placeholderFontColor?: string;
|
|
566
|
+
labelText?: string;
|
|
567
|
+
labelPosition?: "top" | "none";
|
|
568
|
+
labelFontColor?: string;
|
|
569
|
+
labelFontName?: string;
|
|
570
|
+
labelFontSize?: number | string;
|
|
571
|
+
validationMessage?: string;
|
|
572
|
+
validationTextFontColor?: string;
|
|
573
|
+
validationTextFontName?: string;
|
|
574
|
+
validationTextFontSize?: number | string;
|
|
575
|
+
fontColor?: string;
|
|
576
|
+
fontName?: string;
|
|
577
|
+
fontSize?: number | string;
|
|
578
|
+
errorBorderColor?: string;
|
|
579
|
+
errorBorderWidth?: number | string;
|
|
580
|
+
errorBorderRadius?: number | string;
|
|
581
|
+
innerTopPadding?: number | string;
|
|
582
|
+
innerBottomPadding?: number | string;
|
|
583
|
+
innerLeftPadding?: number | string;
|
|
584
|
+
innerRightPadding?: number | string;
|
|
585
|
+
showPrefixIcon?: boolean;
|
|
586
|
+
};
|
|
556
587
|
type TCountdownTimerTextComponent = TBaseComponent & {
|
|
557
588
|
component: "countdownTimerText";
|
|
558
589
|
mode?: "duration" | "targetDateTime";
|
|
@@ -885,7 +916,7 @@ interface TBaseComponent {
|
|
|
885
916
|
hidden?: boolean;
|
|
886
917
|
animation?: NamiAnimationSpec;
|
|
887
918
|
}
|
|
888
|
-
type TComponent = TButtonContainer | TContainer | TTextListComponent | TTextComponent | TSpacerComponent | TImageComponent | TSvgImageComponent | TSymbolComponent | TCarouselContainer | TProductContainer | TFlexProductContainer | TStack | TConditionalComponent | TSegmentPicker | TSegmentPickerItem | TVideoComponent | TCollapseContainer | TResponsiveGrid | TRepeatingGrid | TVolumeButton | TPlayPauseButton | TQRCodeComponent | TToggleSwitch | TRadioButton | TProgressIndicatorComponent | TToggleButtonComponent | TCountdownTimerTextComponent | TProgressBarComponent;
|
|
919
|
+
type TComponent = TButtonContainer | TContainer | TTextListComponent | TTextComponent | TSpacerComponent | TImageComponent | TSvgImageComponent | TSymbolComponent | TCarouselContainer | TProductContainer | TFlexProductContainer | TStack | TConditionalComponent | TSegmentPicker | TSegmentPickerItem | TVideoComponent | TCollapseContainer | TResponsiveGrid | TRepeatingGrid | TVolumeButton | TPlayPauseButton | TQRCodeComponent | TToggleSwitch | TRadioButton | TProgressIndicatorComponent | TToggleButtonComponent | TCountdownTimerTextComponent | TTextInputComponent | TProgressBarComponent;
|
|
889
920
|
type DirectionType = "vertical" | "horizontal";
|
|
890
921
|
type AlignmentType = "center" | "right" | "left" | "top" | "bottom";
|
|
891
922
|
type BorderLocationType = "upperLeft" | "upperRight" | "lowerLeft" | "lowerRight";
|
|
@@ -1490,6 +1521,38 @@ type NamiPurchaseDetails = {
|
|
|
1490
1521
|
|
|
1491
1522
|
type Optional<T, K extends keyof T> = Pick<Partial<T>, K> & Omit<T, K>;
|
|
1492
1523
|
|
|
1524
|
+
/**
|
|
1525
|
+
* Pure, platform-agnostic text-input validation. Shared by the flow submit gate
|
|
1526
|
+
* (sdk/core) and the web Lit element (sdk/web). The rules are identical to the
|
|
1527
|
+
* Apple (NAM-1142) and Android (NAM-1143) ports.
|
|
1528
|
+
*/
|
|
1529
|
+
type TTextInputType = 'name' | 'email' | 'freeText';
|
|
1530
|
+
interface FormFieldValidator {
|
|
1531
|
+
type: TTextInputType;
|
|
1532
|
+
required: boolean;
|
|
1533
|
+
/** Author-supplied error text; preferred over the per-rule default. */
|
|
1534
|
+
message?: string;
|
|
1535
|
+
}
|
|
1536
|
+
/**
|
|
1537
|
+
* Validate a single field. Returns `undefined` when valid, otherwise a default
|
|
1538
|
+
* error message for the failing rule. Callers that want author-supplied text
|
|
1539
|
+
* should use {@link validateForm}.
|
|
1540
|
+
*
|
|
1541
|
+
* Rules:
|
|
1542
|
+
* - required && blank → invalid
|
|
1543
|
+
* - email && non-blank && not email-format → invalid
|
|
1544
|
+
* - name && non-blank && len < 2 → invalid
|
|
1545
|
+
* - freeText → only the required rule applies
|
|
1546
|
+
*/
|
|
1547
|
+
declare function validateTextInput(type: TTextInputType, required: boolean, value: unknown): string | undefined;
|
|
1548
|
+
/**
|
|
1549
|
+
* Validate every registered field against the current form state. Returns a map
|
|
1550
|
+
* of `formId → error message` for invalid fields only (valid fields are omitted).
|
|
1551
|
+
* The validator's own `message` is preferred; otherwise the per-rule default
|
|
1552
|
+
* from {@link validateTextInput} is used.
|
|
1553
|
+
*/
|
|
1554
|
+
declare function validateForm(validators: Record<string, FormFieldValidator>, formStates: Record<string, unknown>): Record<string, string>;
|
|
1555
|
+
|
|
1493
1556
|
type NamiPresentationStyle = 'fullscreen' | 'sheet' | 'compact_sheet' | 'modal';
|
|
1494
1557
|
declare function getEffectiveWebStyle(style: NamiPresentationStyle | undefined | null): NamiPresentationStyle;
|
|
1495
1558
|
type TPaywallContext = TInitialState & {
|
|
@@ -1528,6 +1591,12 @@ type TPaywallContext = TInitialState & {
|
|
|
1528
1591
|
formStates: {
|
|
1529
1592
|
[formId: string]: boolean | string;
|
|
1530
1593
|
};
|
|
1594
|
+
formFieldErrors: {
|
|
1595
|
+
[formId: string]: string;
|
|
1596
|
+
};
|
|
1597
|
+
formFieldValidators: {
|
|
1598
|
+
[formId: string]: FormFieldValidator;
|
|
1599
|
+
};
|
|
1531
1600
|
timerStates: {
|
|
1532
1601
|
[timerId: string]: TimerState;
|
|
1533
1602
|
};
|
|
@@ -1959,6 +2028,15 @@ declare class NamiFlow extends BasicNamiFlow {
|
|
|
1959
2028
|
resumeFromPause(): void;
|
|
1960
2029
|
executeLifecycle(step: NamiFlowStep, key: string, data?: Record<string, any>): void;
|
|
1961
2030
|
private lifecycles;
|
|
2031
|
+
/**
|
|
2032
|
+
* A lifecycle chain is a "form submit" if any phase (before/action/after) runs
|
|
2033
|
+
* one of the advancing functions: setTagsFromForm, flowNext, or flowNav. The
|
|
2034
|
+
* POC submit buttons use flowNext/flowNav (no dedicated submit action), so the
|
|
2035
|
+
* gate keys off these. Close/dismiss/back/pause are deliberately excluded —
|
|
2036
|
+
* they must never be blocked by validation. Mirrors Apple
|
|
2037
|
+
* NamiFlowControl.isFormSubmitLifecycle / Android NamiFlow.isFormSubmitLifecycle.
|
|
2038
|
+
*/
|
|
2039
|
+
isFormSubmitLifecycle(lifecycles: NamiFlowOn[]): boolean;
|
|
1962
2040
|
triggerActions(actionId: string, component?: any, data?: Record<string, any>): void;
|
|
1963
2041
|
executeFullLifecycles(lifecycles: NamiFlowOn[], data?: Record<string, any>): void;
|
|
1964
2042
|
triggerBeforeActions(actionId: string, component?: any, data?: Record<string, any>): void;
|
|
@@ -2026,6 +2104,27 @@ declare class PaywallState extends SimpleEventTarget {
|
|
|
2026
2104
|
getProductDetails(): NamiProductDetails[];
|
|
2027
2105
|
setCurrentGroupData(currentGroupId: string, currentGroupName: string): void;
|
|
2028
2106
|
setFormState(formId: string, state: boolean | string): void;
|
|
2107
|
+
/**
|
|
2108
|
+
* Register the validator for a text-input field (keyed by formId). Used by the
|
|
2109
|
+
* implicit form-submit gate in NamiFlow.triggerActions and by the web element
|
|
2110
|
+
* when it mounts.
|
|
2111
|
+
*/
|
|
2112
|
+
registerFormFieldValidator(formId: string, validator: FormFieldValidator): void;
|
|
2113
|
+
getFormFieldValidators(): {
|
|
2114
|
+
[formId: string]: FormFieldValidator;
|
|
2115
|
+
};
|
|
2116
|
+
/**
|
|
2117
|
+
* Replace the full set of field errors (formId → message). The gate writes the
|
|
2118
|
+
* result of validateForm here; an empty map clears all errors.
|
|
2119
|
+
*/
|
|
2120
|
+
setFormFieldErrors(errors: {
|
|
2121
|
+
[formId: string]: string;
|
|
2122
|
+
}): void;
|
|
2123
|
+
setFormFieldError(formId: string, message: string): void;
|
|
2124
|
+
clearFormFieldError(formId: string): void;
|
|
2125
|
+
getFormFieldErrors(): {
|
|
2126
|
+
[formId: string]: string;
|
|
2127
|
+
};
|
|
2029
2128
|
setTimerState(timerId: string, remainingSeconds: number, savedAt: number, hasEmittedCompletion: boolean): void;
|
|
2030
2129
|
getTimerState(timerId: string): TimerState | undefined;
|
|
2031
2130
|
getCurrentGroupId(): string;
|
|
@@ -2422,6 +2521,15 @@ declare class StorageService {
|
|
|
2422
2521
|
getCustomerAttribute<T>(attribute: string): T | null;
|
|
2423
2522
|
getAllCustomerAttributes(): string[];
|
|
2424
2523
|
getAllCustomerAttributesKeys(): string[];
|
|
2524
|
+
/**
|
|
2525
|
+
* All persisted customer attributes as a `{ key: value }` map. Used to seed
|
|
2526
|
+
* `PaywallState.customer` when a paywall screen initializes, so attributes set
|
|
2527
|
+
* on an earlier flow screen (e.g. via setTagsFromForm) are available to the
|
|
2528
|
+
* `${customer.x}` smart-text resolver on subsequent screens.
|
|
2529
|
+
*/
|
|
2530
|
+
getAllCustomerAttributesMap(): {
|
|
2531
|
+
[key: string]: string;
|
|
2532
|
+
};
|
|
2425
2533
|
clearCustomerAttribute(attribute: string): boolean;
|
|
2426
2534
|
clearAllCustomerAttributes(): void;
|
|
2427
2535
|
setNamiProfile(profileData: NamiProfile): void;
|
|
@@ -3489,5 +3597,5 @@ declare namespace internal {
|
|
|
3489
3597
|
};
|
|
3490
3598
|
}
|
|
3491
3599
|
|
|
3492
|
-
export { ALREADY_CONFIGURED, ANONYMOUS_MODE, ANONYMOUS_MODE_ALREADY_OFF, ANONYMOUS_MODE_ALREADY_ON, ANONYMOUS_MODE_LOGIN_NOT_ALLOWED, ANONYMOUS_UUID, APIError, API_ACTIVE_ENTITLEMENTS, API_CAMPAIGN_RULES, API_CAMPAIGN_SESSION_TIMESTAMP, API_CONFIG, API_MAX_CALLS_LIMIT, API_PAYWALLS, API_PRODUCTS, API_RETRY_DELAY_SEC, API_TIMEOUT_LIMIT, API_VERSION, AUTH_DEVICE, AVAILABLE_ACTIVE_ENTITLEMENTS_CHANGED, AVAILABLE_CAMPAIGNS_CHANGED, AccountStateAction, AnonymousCDPError, AnonymousLoginError, AnonymousModeAlreadyOffError, AnonymousModeAlreadyOnError, BASE_STAGING_URL, BASE_URL, BASE_URL_PATH, BadRequestError, BasicNamiFlow, BorderMap, BorderSideMap, CAMPAIGN_NOT_AVAILABLE, CUSTOMER_ATTRIBUTES_KEY_PREFIX, CUSTOMER_JOURNEY_STATE_CHANGED, CUSTOM_HOST_PREFIX, CampaignNotAvailableError, CampaignRuleConversionEventType, CampaignRuleRepository, Capabilities, ClientError, ConfigRepository, ConflictError, CustomerJourneyRepository, DEVELOPMENT, DEVICE_API_TIMEOUT_LIMIT, DEVICE_ID_NOT_SET, DEVICE_ID_REQUIRED, DISABLE_ASYNC_LOGIN_LOGOUT, DeviceIDRequiredError, DeviceRepository, EXTENDED_CLIENT_INFO_DELIMITER, EXTENDED_CLIENT_INFO_PREFIX, EXTENDED_PLATFORM, EXTENDED_PLATFORM_VERSION, EXTERNAL_ID_REQUIRED, EntitlementRepository, EntitlementUtils, ExternalIDRequiredError, FLOW_SCREENS_NOT_AVAILABLE, FlowScreensNotAvailableError, HTML_REGEX, INITIAL_APP_CONFIG, INITIAL_CAMPAIGN_RULES, INITIAL_PAYWALLS, INITIAL_PRODUCTS, INITIAL_SESSION_COUNTER_VALUE, INITIAL_SUCCESS, InternalServerError, KEY_SESSION_COUNTER, LIQUID_VARIABLE_REGEX, LOCAL_NAMI_ENTITLEMENTS, LOG_HTTP_REQUESTS, LOG_HTTP_TRAFFIC, LaunchCampaignError, LaunchContextResolver, LogLevel, NAMI_CONFIGURATION, NAMI_CUSTOMER_JOURNEY_STATE, NAMI_LANGUAGE_CODE, NAMI_LAST_IMPRESSION_ID, NAMI_LAUNCH_ID, NAMI_PROFILE, NAMI_PURCHASE_CHANNEL, NAMI_PURCHASE_IMPRESSION_ID, NAMI_SDK_PACKAGE_VERSION, NAMI_SDK_VERSION, NAMI_SESSION_ID, NAMI_STORAGE_KEYS, Nami, NamiAPI, NamiAnimationType, NamiCampaignManager$2 as NamiCampaignManager, NamiCampaignRuleType, NamiConditionEvaluator, NamiCustomerManager$2 as NamiCustomerManager, NamiEntitlementManager$2 as NamiEntitlementManager, NamiEventEmitter, NamiFlow, NamiFlowActionFunction, NamiFlowManager$1 as NamiFlowManager, NamiFlowStepType, NamiPaywallAction, NamiPaywallManager$2 as NamiPaywallManager, PaywallManagerEvents as NamiPaywallManagerEvents, NamiPurchaseManager$2 as NamiPurchaseManager, NamiRefs, NamiReservedActions, NotFoundError, PAYWALL_ACTION_EVENT, PLATFORM_ID_REQUIRED, PRODUCTION, PaywallManagerEvents, PaywallRepository, PaywallState, PlacementLabelResolver, PlatformIDRequiredError, ProductRepository, RECONFIG_SUCCESS, RetryLimitExceededError, SDKNotInitializedError, SDK_NOT_INITIALIZED, SERVER_NAMI_ENTITLEMENTS, SESSION_REQUIRED, SHOULD_SHOW_LOADING_INDICATOR, SKU_TEXT_REGEX, SMART_TEXT_PATTERN, STARTUP_TELEMETRY, STATUS_BAD_REQUEST, STATUS_CONFLICT, STATUS_INTERNAL_SERVER_ERROR, STATUS_NOT_FOUND, STATUS_SUCCESS, SessionService, SimpleEventTarget, StorageService, UNABLE_TO_UPDATE_CDP_ID, USE_STAGING_API, VALIDATE_PRODUCT_GROUPS, VAR_REGEX, NamiProfileManager$1 as _NamiProfileManager, internal as _internal, activateEntitlementByPurchase, activeEntitlements, aggregateScreenreaderText, allCampaigns, allPaywalls, applyEntitlementActivation, audienceSplitPosition, bestUrlCampaignMatch, bigintToUuid, checkAnySkuHasPromoOffer, checkAnySkuHasTrialOffer, convertISO8601PeriodToText, convertLocale, convertOfferToPricingPhase, createNamiEntitlements, currentSku, empty, extractStandardPricingPhases, formatDate, formattedPrice, generateUUID, getApiCampaigns, getApiPaywalls, getBaseUrl, getBillingPeriodNumber, getCurrencyFormat, getDeviceData, getDeviceFormFactor, getDeviceScaleFactor, getEffectiveWebStyle, getEntitlementRefIdsForSku, getExtendedClientInfo, getFreeTrialPeriod, getInitialCampaigns, getInitialPaywalls, getPaywall, getPaywallDataFromLabel, getPercentagePriceDifference, getPeriodNumberInDays, getPeriodNumberInWeeks, getPlatformAdapters, getPriceDifference, getPricePerMonth, getProductDetail, getPurchaseAdapter, getReferenceSku, getSkuProductDetailKeys, getSkuSmartTextValue, getSlideSmartTextValue, getStandardBillingPeriod, getTranslate, getUrlParams, handleErrors, hasAllPaywalls, hasCapability, hasPurchaseManagement, initialState, invokeHandler, isAnonymousMode, isInitialConfigCompressed, isNamiFlowCampaign, isSubscription, isValidISODate, isValidUrl, logger, mapAnonymousCampaigns, namiBuySKU, normalizeLaunchContext, parseToSemver, postConversion, productDetail, registerPlatformAdapters, registerPurchaseAdapter, selectSegment, setActiveNamiEntitlements, shouldValidateProductGroups, skuItems, skuMapFromEntitlements, storageService, toDouble, toNamiEntitlements, toNamiSKU, tryParseB64Gzip, tryParseJson, updateRelatedSKUsForNamiEntitlement, uuidFromSplitPosition, validateMinSDKVersion };
|
|
3493
|
-
export type { AccountStateHandler$1 as AccountStateHandler, AlignmentType, AmazonProduct, ApiResponse, AppleProduct, AvailableCampaignsResponseHandler, BorderLocationType, BorderSideType, Callback$1 as Callback, CloseHandler, CustomerJourneyState, DeepLinkUrlHandler, Device, DevicePayload, DeviceProfile, DirectionType, ExtendedPlatformInfo, FlexDirectionObject, FlowNavigationOptions, FontCollection, FontDetails, FormFactor, GoogleProduct, IConfig, IDeviceAdapter, IEntitlements$1 as IEntitlements, IPaywall, IPlatformAdapters, IProductsWithComponents, IPurchaseAdapter, ISkuMenu, IStorageAdapter, IUIAdapter, Impression, InitialConfig, InitialConfigCompressed, InitiateStateGroup, LoginResponse, NamiAnimation, NamiAnimationObjectSpec, NamiAnimationSpec, NamiAnonymousCampaign, NamiAppSuppliedVideoDetails, NamiCampaign, NamiCampaignManagerStatic, NamiCampaignSegment, NamiConfiguration, NamiConfigurationState, NamiCustomAttributeValue, NamiCustomerManagerStatic, NamiEntitlement$1 as NamiEntitlement, NamiEntitlementManagerStatic, NamiFlowAction, NamiFlowAnimation, NamiFlowCampaign, NamiFlowDTO, NamiFlowEventHandler, NamiFlowHandoffStepHandler, NamiFlowManagerStatic, NamiFlowObjectDTO, NamiFlowOn, NamiFlowStep, NamiFlowTransition, NamiFlowTransitionDirection, NamiFlowWithObject, NamiInitialConfig, NamiLanguageCodes, NamiLogLevel, NamiPaywallActionHandler, NamiPaywallComponentChange, NamiPaywallEvent, NamiPaywallEventVideoMetadata, NamiPaywallLaunchContext, NamiPaywallManagerStatic, NamiPresentationStyle, NamiProductDetails, NamiProductOffer, NamiProfile, NamiPurchase, NamiPurchaseCompleteResult, NamiPurchaseDetails, NamiPurchaseManagerStatic, NamiPurchasesState, NamiSKU, NamiSKUType, NamiSubscriptionInterval, NamiSubscriptionPeriod, None, NoneSpec, PaywallActionEvent, PaywallHandle, PaywallResultHandler, PaywallSKU, PricingPhase, ProductGroup, Pulse, PulseSpec, PurchaseContext, PurchaseResult, PurchaseValidationRequest, SKU, SKUActionHandler, ScreenInfo, Session, TBaseComponent, TButtonContainer, TCarouselContainer, TCarouselSlide, TCarouselSlidesState, TCollapseContainer, TComponent, TConditionalAttributes, TConditionalComponent, TContainer, TContainerPosition, TCountdownTimerTextComponent, TDevice, TDisabledButton, TField, TFieldSettings, TFlexProductContainer, THeaderFooter, TImageComponent, TInitialState, TMediaTypes, TOffer, TPages, TPaywallContext, TPaywallLaunchContext, TPaywallMedia, TPaywallTemplate, TPlayPauseButton, TProductContainer, TProductGroup, TProgressBarComponent, TProgressIndicatorComponent, TQRCodeComponent, TRadioButton, TRepeatingGrid, TResponsiveGrid, TSegmentPicker, TSegmentPickerItem, TSemverObj, TSpacerComponent, TStack, TSvgImageComponent, TSymbolComponent, TTestObject, TTextComponent, TTextLikeComponent, TTextListComponent, TTimelineRail, TToggleButtonComponent, TToggleSwitch, TVariablePattern, TVideoComponent, TVolumeButton, TimerState, TransactionRequest, UserAction, UserActionParameters, Wave, WaveSpec };
|
|
3600
|
+
export { ALREADY_CONFIGURED, ANONYMOUS_MODE, ANONYMOUS_MODE_ALREADY_OFF, ANONYMOUS_MODE_ALREADY_ON, ANONYMOUS_MODE_LOGIN_NOT_ALLOWED, ANONYMOUS_UUID, APIError, API_ACTIVE_ENTITLEMENTS, API_CAMPAIGN_RULES, API_CAMPAIGN_SESSION_TIMESTAMP, API_CONFIG, API_MAX_CALLS_LIMIT, API_PAYWALLS, API_PRODUCTS, API_RETRY_DELAY_SEC, API_TIMEOUT_LIMIT, API_VERSION, AUTH_DEVICE, AVAILABLE_ACTIVE_ENTITLEMENTS_CHANGED, AVAILABLE_CAMPAIGNS_CHANGED, AccountStateAction, AnonymousCDPError, AnonymousLoginError, AnonymousModeAlreadyOffError, AnonymousModeAlreadyOnError, BASE_STAGING_URL, BASE_URL, BASE_URL_PATH, BadRequestError, BasicNamiFlow, BorderMap, BorderSideMap, CAMPAIGN_NOT_AVAILABLE, CUSTOMER_ATTRIBUTES_KEY_PREFIX, CUSTOMER_JOURNEY_STATE_CHANGED, CUSTOM_HOST_PREFIX, CampaignNotAvailableError, CampaignRuleConversionEventType, CampaignRuleRepository, Capabilities, ClientError, ConfigRepository, ConflictError, CustomerJourneyRepository, DEVELOPMENT, DEVICE_API_TIMEOUT_LIMIT, DEVICE_ID_NOT_SET, DEVICE_ID_REQUIRED, DISABLE_ASYNC_LOGIN_LOGOUT, DeviceIDRequiredError, DeviceRepository, EXTENDED_CLIENT_INFO_DELIMITER, EXTENDED_CLIENT_INFO_PREFIX, EXTENDED_PLATFORM, EXTENDED_PLATFORM_VERSION, EXTERNAL_ID_REQUIRED, EntitlementRepository, EntitlementUtils, ExternalIDRequiredError, FLOW_SCREENS_NOT_AVAILABLE, FlowScreensNotAvailableError, HTML_REGEX, INITIAL_APP_CONFIG, INITIAL_CAMPAIGN_RULES, INITIAL_PAYWALLS, INITIAL_PRODUCTS, INITIAL_SESSION_COUNTER_VALUE, INITIAL_SUCCESS, InternalServerError, KEY_SESSION_COUNTER, LIQUID_VARIABLE_REGEX, LOCAL_NAMI_ENTITLEMENTS, LOG_HTTP_REQUESTS, LOG_HTTP_TRAFFIC, LaunchCampaignError, LaunchContextResolver, LogLevel, NAMI_CONFIGURATION, NAMI_CUSTOMER_JOURNEY_STATE, NAMI_LANGUAGE_CODE, NAMI_LAST_IMPRESSION_ID, NAMI_LAUNCH_ID, NAMI_PROFILE, NAMI_PURCHASE_CHANNEL, NAMI_PURCHASE_IMPRESSION_ID, NAMI_SDK_PACKAGE_VERSION, NAMI_SDK_VERSION, NAMI_SESSION_ID, NAMI_STORAGE_KEYS, Nami, NamiAPI, NamiAnimationType, NamiCampaignManager$2 as NamiCampaignManager, NamiCampaignRuleType, NamiConditionEvaluator, NamiCustomerManager$2 as NamiCustomerManager, NamiEntitlementManager$2 as NamiEntitlementManager, NamiEventEmitter, NamiFlow, NamiFlowActionFunction, NamiFlowManager$1 as NamiFlowManager, NamiFlowStepType, NamiPaywallAction, NamiPaywallManager$2 as NamiPaywallManager, PaywallManagerEvents as NamiPaywallManagerEvents, NamiPurchaseManager$2 as NamiPurchaseManager, NamiRefs, NamiReservedActions, NotFoundError, PAYWALL_ACTION_EVENT, PLATFORM_ID_REQUIRED, PRODUCTION, PaywallManagerEvents, PaywallRepository, PaywallState, PlacementLabelResolver, PlatformIDRequiredError, ProductRepository, RECONFIG_SUCCESS, RetryLimitExceededError, SDKNotInitializedError, SDK_NOT_INITIALIZED, SERVER_NAMI_ENTITLEMENTS, SESSION_REQUIRED, SHOULD_SHOW_LOADING_INDICATOR, SKU_TEXT_REGEX, SMART_TEXT_PATTERN, STARTUP_TELEMETRY, STATUS_BAD_REQUEST, STATUS_CONFLICT, STATUS_INTERNAL_SERVER_ERROR, STATUS_NOT_FOUND, STATUS_SUCCESS, SessionService, SimpleEventTarget, StorageService, UNABLE_TO_UPDATE_CDP_ID, USE_STAGING_API, VALIDATE_PRODUCT_GROUPS, VAR_REGEX, NamiProfileManager$1 as _NamiProfileManager, internal as _internal, activateEntitlementByPurchase, activeEntitlements, aggregateScreenreaderText, allCampaigns, allPaywalls, applyEntitlementActivation, audienceSplitPosition, bestUrlCampaignMatch, bigintToUuid, checkAnySkuHasPromoOffer, checkAnySkuHasTrialOffer, convertISO8601PeriodToText, convertLocale, convertOfferToPricingPhase, createNamiEntitlements, currentSku, empty, extractStandardPricingPhases, formatDate, formattedPrice, generateUUID, getApiCampaigns, getApiPaywalls, getBaseUrl, getBillingPeriodNumber, getCurrencyFormat, getDeviceData, getDeviceFormFactor, getDeviceScaleFactor, getEffectiveWebStyle, getEntitlementRefIdsForSku, getExtendedClientInfo, getFreeTrialPeriod, getInitialCampaigns, getInitialPaywalls, getPaywall, getPaywallDataFromLabel, getPercentagePriceDifference, getPeriodNumberInDays, getPeriodNumberInWeeks, getPlatformAdapters, getPriceDifference, getPricePerMonth, getProductDetail, getPurchaseAdapter, getReferenceSku, getSkuProductDetailKeys, getSkuSmartTextValue, getSlideSmartTextValue, getStandardBillingPeriod, getTranslate, getUrlParams, handleErrors, hasAllPaywalls, hasCapability, hasPurchaseManagement, initialState, invokeHandler, isAnonymousMode, isInitialConfigCompressed, isNamiFlowCampaign, isSubscription, isValidISODate, isValidUrl, logger, mapAnonymousCampaigns, namiBuySKU, normalizeLaunchContext, parseToSemver, postConversion, productDetail, registerPlatformAdapters, registerPurchaseAdapter, selectSegment, setActiveNamiEntitlements, shouldValidateProductGroups, skuItems, skuMapFromEntitlements, storageService, toDouble, toNamiEntitlements, toNamiSKU, tryParseB64Gzip, tryParseJson, updateRelatedSKUsForNamiEntitlement, uuidFromSplitPosition, validateForm, validateMinSDKVersion, validateTextInput };
|
|
3601
|
+
export type { AccountStateHandler$1 as AccountStateHandler, AlignmentType, AmazonProduct, ApiResponse, AppleProduct, AvailableCampaignsResponseHandler, BorderLocationType, BorderSideType, Callback$1 as Callback, CloseHandler, CustomerJourneyState, DeepLinkUrlHandler, Device, DevicePayload, DeviceProfile, DirectionType, ExtendedPlatformInfo, FlexDirectionObject, FlowNavigationOptions, FontCollection, FontDetails, FormFactor, FormFieldValidator, GoogleProduct, IConfig, IDeviceAdapter, IEntitlements$1 as IEntitlements, IPaywall, IPlatformAdapters, IProductsWithComponents, IPurchaseAdapter, ISkuMenu, IStorageAdapter, IUIAdapter, Impression, InitialConfig, InitialConfigCompressed, InitiateStateGroup, LoginResponse, NamiAnimation, NamiAnimationObjectSpec, NamiAnimationSpec, NamiAnonymousCampaign, NamiAppSuppliedVideoDetails, NamiCampaign, NamiCampaignManagerStatic, NamiCampaignSegment, NamiConfiguration, NamiConfigurationState, NamiCustomAttributeValue, NamiCustomerManagerStatic, NamiEntitlement$1 as NamiEntitlement, NamiEntitlementManagerStatic, NamiFlowAction, NamiFlowAnimation, NamiFlowCampaign, NamiFlowDTO, NamiFlowEventHandler, NamiFlowHandoffStepHandler, NamiFlowManagerStatic, NamiFlowObjectDTO, NamiFlowOn, NamiFlowStep, NamiFlowTransition, NamiFlowTransitionDirection, NamiFlowWithObject, NamiInitialConfig, NamiLanguageCodes, NamiLogLevel, NamiPaywallActionHandler, NamiPaywallComponentChange, NamiPaywallEvent, NamiPaywallEventVideoMetadata, NamiPaywallLaunchContext, NamiPaywallManagerStatic, NamiPresentationStyle, NamiProductDetails, NamiProductOffer, NamiProfile, NamiPurchase, NamiPurchaseCompleteResult, NamiPurchaseDetails, NamiPurchaseManagerStatic, NamiPurchasesState, NamiSKU, NamiSKUType, NamiSubscriptionInterval, NamiSubscriptionPeriod, None, NoneSpec, PaywallActionEvent, PaywallHandle, PaywallResultHandler, PaywallSKU, PricingPhase, ProductGroup, Pulse, PulseSpec, PurchaseContext, PurchaseResult, PurchaseValidationRequest, SKU, SKUActionHandler, ScreenInfo, Session, TBaseComponent, TButtonContainer, TCarouselContainer, TCarouselSlide, TCarouselSlidesState, TCollapseContainer, TComponent, TConditionalAttributes, TConditionalComponent, TContainer, TContainerPosition, TCountdownTimerTextComponent, TDevice, TDisabledButton, TField, TFieldSettings, TFlexProductContainer, THeaderFooter, TImageComponent, TInitialState, TMediaTypes, TOffer, TPages, TPaywallContext, TPaywallLaunchContext, TPaywallMedia, TPaywallTemplate, TPlayPauseButton, TProductContainer, TProductGroup, TProgressBarComponent, TProgressIndicatorComponent, TQRCodeComponent, TRadioButton, TRepeatingGrid, TResponsiveGrid, TSegmentPicker, TSegmentPickerItem, TSemverObj, TSpacerComponent, TStack, TSvgImageComponent, TSymbolComponent, TTestObject, TTextComponent, TTextInputComponent, TTextInputType, TTextLikeComponent, TTextListComponent, TTimelineRail, TToggleButtonComponent, TToggleSwitch, TVariablePattern, TVideoComponent, TVolumeButton, TimerState, TransactionRequest, UserAction, UserActionParameters, Wave, WaveSpec };
|
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.3",
|
|
98
98
|
// full package version including dev suffix — stamped by scripts/version.sh
|
|
99
|
-
NAMI_SDK_PACKAGE_VERSION = "3.4.3-dev.
|
|
99
|
+
NAMI_SDK_PACKAGE_VERSION = "3.4.3-dev.202606181527",
|
|
100
100
|
// environments
|
|
101
101
|
PRODUCTION = "production", DEVELOPMENT = "development",
|
|
102
102
|
// error messages
|
|
@@ -3191,6 +3191,22 @@ class StorageService {
|
|
|
3191
3191
|
.filter(key => key.includes(CUSTOMER_ATTRIBUTES_KEY_PREFIX))
|
|
3192
3192
|
.map(key => key.replace(CUSTOMER_ATTRIBUTES_KEY_PREFIX, ""));
|
|
3193
3193
|
}
|
|
3194
|
+
/**
|
|
3195
|
+
* All persisted customer attributes as a `{ key: value }` map. Used to seed
|
|
3196
|
+
* `PaywallState.customer` when a paywall screen initializes, so attributes set
|
|
3197
|
+
* on an earlier flow screen (e.g. via setTagsFromForm) are available to the
|
|
3198
|
+
* `${customer.x}` smart-text resolver on subsequent screens.
|
|
3199
|
+
*/
|
|
3200
|
+
getAllCustomerAttributesMap() {
|
|
3201
|
+
const out = {};
|
|
3202
|
+
this.getAllCustomerAttributesKeys().forEach((key) => {
|
|
3203
|
+
const value = this.getCustomerAttribute(key);
|
|
3204
|
+
if (value !== null && value !== undefined) {
|
|
3205
|
+
out[key] = String(value);
|
|
3206
|
+
}
|
|
3207
|
+
});
|
|
3208
|
+
return out;
|
|
3209
|
+
}
|
|
3194
3210
|
clearCustomerAttribute(attribute) {
|
|
3195
3211
|
const key = CUSTOMER_ATTRIBUTES_KEY_PREFIX + attribute;
|
|
3196
3212
|
const itemExists = this.getItem(key);
|
|
@@ -11315,6 +11331,8 @@ const initialState = {
|
|
|
11315
11331
|
groups: [],
|
|
11316
11332
|
selectedProducts: {},
|
|
11317
11333
|
formStates: {},
|
|
11334
|
+
formFieldErrors: {},
|
|
11335
|
+
formFieldValidators: {},
|
|
11318
11336
|
timerStates: {},
|
|
11319
11337
|
currentPage: 'page1',
|
|
11320
11338
|
pageHistory: ['page1'],
|
|
@@ -11417,6 +11435,11 @@ class PaywallState extends SimpleEventTarget {
|
|
|
11417
11435
|
this.state = cloneDeep(initialState);
|
|
11418
11436
|
const profile = storageService.getNamiProfile();
|
|
11419
11437
|
this.state.isLoggedIn = !!profile?.externalId;
|
|
11438
|
+
// Seed customer attributes persisted on earlier screens (e.g. via
|
|
11439
|
+
// setTagsFromForm / setTags) so `${customer.x}` smart text resolves on every
|
|
11440
|
+
// flow screen — each screen builds a fresh provider, so without this the
|
|
11441
|
+
// value captured on page 1 would be invisible on page 2.
|
|
11442
|
+
this.state.customer = { ...this.state.customer, ...storageService.getAllCustomerAttributesMap() };
|
|
11420
11443
|
this.setPaywall(paywall, context, campaign);
|
|
11421
11444
|
this.subscribers = new Set();
|
|
11422
11445
|
}
|
|
@@ -11454,6 +11477,41 @@ class PaywallState extends SimpleEventTarget {
|
|
|
11454
11477
|
setFormState(formId, state) {
|
|
11455
11478
|
this.setState({ ...this.state, formStates: { ...this.state.formStates, [formId]: state } });
|
|
11456
11479
|
}
|
|
11480
|
+
/**
|
|
11481
|
+
* Register the validator for a text-input field (keyed by formId). Used by the
|
|
11482
|
+
* implicit form-submit gate in NamiFlow.triggerActions and by the web element
|
|
11483
|
+
* when it mounts.
|
|
11484
|
+
*/
|
|
11485
|
+
registerFormFieldValidator(formId, validator) {
|
|
11486
|
+
this.setState({
|
|
11487
|
+
...this.state,
|
|
11488
|
+
formFieldValidators: { ...this.state.formFieldValidators, [formId]: validator },
|
|
11489
|
+
});
|
|
11490
|
+
}
|
|
11491
|
+
getFormFieldValidators() {
|
|
11492
|
+
return this.state.formFieldValidators;
|
|
11493
|
+
}
|
|
11494
|
+
/**
|
|
11495
|
+
* Replace the full set of field errors (formId → message). The gate writes the
|
|
11496
|
+
* result of validateForm here; an empty map clears all errors.
|
|
11497
|
+
*/
|
|
11498
|
+
setFormFieldErrors(errors) {
|
|
11499
|
+
this.setState({ ...this.state, formFieldErrors: { ...errors } });
|
|
11500
|
+
}
|
|
11501
|
+
setFormFieldError(formId, message) {
|
|
11502
|
+
this.setState({
|
|
11503
|
+
...this.state,
|
|
11504
|
+
formFieldErrors: { ...this.state.formFieldErrors, [formId]: message },
|
|
11505
|
+
});
|
|
11506
|
+
}
|
|
11507
|
+
clearFormFieldError(formId) {
|
|
11508
|
+
const nextErrors = { ...this.state.formFieldErrors };
|
|
11509
|
+
delete nextErrors[formId];
|
|
11510
|
+
this.setState({ ...this.state, formFieldErrors: nextErrors });
|
|
11511
|
+
}
|
|
11512
|
+
getFormFieldErrors() {
|
|
11513
|
+
return this.state.formFieldErrors;
|
|
11514
|
+
}
|
|
11457
11515
|
setTimerState(timerId, remainingSeconds, savedAt, hasEmittedCompletion) {
|
|
11458
11516
|
this.setState({
|
|
11459
11517
|
...this.state,
|
|
@@ -11672,7 +11730,12 @@ class PaywallState extends SimpleEventTarget {
|
|
|
11672
11730
|
this.emitEvent('user-interaction-changed', { enabled });
|
|
11673
11731
|
}
|
|
11674
11732
|
setUserTags(tags) {
|
|
11675
|
-
|
|
11733
|
+
// User tags ARE customer attributes for personalization — they must land in
|
|
11734
|
+
// `state.customer`, the store the `${customer.x}` smart-text resolver reads
|
|
11735
|
+
// (see valueFromSmartText: replacements.customer = state.customer). Writing a
|
|
11736
|
+
// separate `state.userTags` (read nowhere) meant setTags / setTagsFromForm
|
|
11737
|
+
// values never reached `${customer.firstName}` on a subsequent flow page.
|
|
11738
|
+
this.setState({ ...this.state, customer: { ...this.state.customer, ...tags } });
|
|
11676
11739
|
}
|
|
11677
11740
|
setCurrentSlideIndex(index) {
|
|
11678
11741
|
this.setState({ ...this.state, currentSlideIndex: index });
|
|
@@ -12549,6 +12612,7 @@ var NamiFlowActionFunction;
|
|
|
12549
12612
|
NamiFlowActionFunction["FLOW_ENABLED"] = "flowInteractionEnabled";
|
|
12550
12613
|
NamiFlowActionFunction["FLOW_DISABLED"] = "flowInteractionDisabled";
|
|
12551
12614
|
NamiFlowActionFunction["SET_TAGS"] = "setTags";
|
|
12615
|
+
NamiFlowActionFunction["SET_TAGS_FROM_FORM"] = "setTagsFromForm";
|
|
12552
12616
|
NamiFlowActionFunction["PAUSE"] = "flowPause";
|
|
12553
12617
|
NamiFlowActionFunction["RESUME"] = "flowResume";
|
|
12554
12618
|
NamiFlowActionFunction["SET_LAUNCH_CONTEXT"] = "setLaunchContext";
|
|
@@ -13075,6 +13139,42 @@ class URLParams {
|
|
|
13075
13139
|
URLParams._instance = null;
|
|
13076
13140
|
const getUrlParams = () => URLParams.instance.params;
|
|
13077
13141
|
|
|
13142
|
+
/**
|
|
13143
|
+
* Coerce a single form-state value to the string form used by tags / customer
|
|
13144
|
+
* attributes / `{{ form.x }}` interpolation. Shared by the `setTagsFromForm`
|
|
13145
|
+
* flow action and FlowLiquidResolver so they stay in lockstep.
|
|
13146
|
+
*
|
|
13147
|
+
* Rules (order matters — Boolean is checked before number):
|
|
13148
|
+
* - `null` / `undefined` → skip (returns `undefined`)
|
|
13149
|
+
* - empty string `""` → skip
|
|
13150
|
+
* - `false` → "false", `true` → "true" (booleans are kept)
|
|
13151
|
+
* - `0` → "0" (zero is kept)
|
|
13152
|
+
* - integral number → no trailing ".0" (e.g. 3 → "3", 3.5 → "3.5")
|
|
13153
|
+
* - everything else → String(value)
|
|
13154
|
+
*/
|
|
13155
|
+
function formTagValue(value) {
|
|
13156
|
+
if (value === null || value === undefined) {
|
|
13157
|
+
return undefined;
|
|
13158
|
+
}
|
|
13159
|
+
if (value === '') {
|
|
13160
|
+
return undefined;
|
|
13161
|
+
}
|
|
13162
|
+
// Boolean BEFORE number: typeof true !== 'number', but be explicit so the
|
|
13163
|
+
// intent (and the false→"false" / true→"true" mapping) is unmistakable.
|
|
13164
|
+
if (typeof value === 'boolean') {
|
|
13165
|
+
return value ? 'true' : 'false';
|
|
13166
|
+
}
|
|
13167
|
+
if (typeof value === 'number') {
|
|
13168
|
+
if (!Number.isFinite(value)) {
|
|
13169
|
+
return undefined;
|
|
13170
|
+
}
|
|
13171
|
+
// JS `String` already renders integers without a trailing ".0"
|
|
13172
|
+
// (3 → "3", 3.5 → "3.5"), which is exactly the contract we want.
|
|
13173
|
+
return String(value);
|
|
13174
|
+
}
|
|
13175
|
+
return String(value);
|
|
13176
|
+
}
|
|
13177
|
+
|
|
13078
13178
|
const LIQUID_VAR_REGEX = /\{\{\s*([^}]+?)\s*\}\}/g;
|
|
13079
13179
|
/**
|
|
13080
13180
|
* Resolver for liquid-style flow variables, without requiring namespace prefixes.
|
|
@@ -13099,6 +13199,13 @@ class FlowLiquidResolver {
|
|
|
13099
13199
|
}
|
|
13100
13200
|
return undefined;
|
|
13101
13201
|
}
|
|
13202
|
+
// Handle form field paths: {{ form.fieldId }} reads the live form state and
|
|
13203
|
+
// coerces it through the same helper used by setTagsFromForm.
|
|
13204
|
+
if (key.startsWith("form.")) {
|
|
13205
|
+
const fieldId = key.substring("form.".length);
|
|
13206
|
+
const value = PaywallState.currentProvider?.state.formStates[fieldId];
|
|
13207
|
+
return formTagValue(value);
|
|
13208
|
+
}
|
|
13102
13209
|
switch (key) {
|
|
13103
13210
|
case "campaignId":
|
|
13104
13211
|
return screenState?.campaign?.rule;
|
|
@@ -14100,6 +14207,67 @@ let NamiCustomerManager$2 = class NamiCustomerManager {
|
|
|
14100
14207
|
};
|
|
14101
14208
|
NamiCustomerManager$2.instance = new NamiCustomerManager$2();
|
|
14102
14209
|
|
|
14210
|
+
/**
|
|
14211
|
+
* Pure, platform-agnostic text-input validation. Shared by the flow submit gate
|
|
14212
|
+
* (sdk/core) and the web Lit element (sdk/web). The rules are identical to the
|
|
14213
|
+
* Apple (NAM-1142) and Android (NAM-1143) ports.
|
|
14214
|
+
*/
|
|
14215
|
+
const DEFAULT_MESSAGES = {
|
|
14216
|
+
required: 'This field is required',
|
|
14217
|
+
email: 'Please enter a valid email address',
|
|
14218
|
+
name: 'Please enter at least 2 characters',
|
|
14219
|
+
};
|
|
14220
|
+
/**
|
|
14221
|
+
* An email must contain an `@` and a domain with a dot, e.g. `a@b.co`.
|
|
14222
|
+
* `a@b` (no dotted domain) and `nope` (no `@`) are invalid.
|
|
14223
|
+
*/
|
|
14224
|
+
const EMAIL_REGEX = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
|
14225
|
+
/**
|
|
14226
|
+
* Validate a single field. Returns `undefined` when valid, otherwise a default
|
|
14227
|
+
* error message for the failing rule. Callers that want author-supplied text
|
|
14228
|
+
* should use {@link validateForm}.
|
|
14229
|
+
*
|
|
14230
|
+
* Rules:
|
|
14231
|
+
* - required && blank → invalid
|
|
14232
|
+
* - email && non-blank && not email-format → invalid
|
|
14233
|
+
* - name && non-blank && len < 2 → invalid
|
|
14234
|
+
* - freeText → only the required rule applies
|
|
14235
|
+
*/
|
|
14236
|
+
function validateTextInput(type, required, value) {
|
|
14237
|
+
const text = typeof value === 'string' ? value : value == null ? '' : String(value);
|
|
14238
|
+
const trimmed = text.trim();
|
|
14239
|
+
if (required && trimmed.length === 0) {
|
|
14240
|
+
return DEFAULT_MESSAGES.required;
|
|
14241
|
+
}
|
|
14242
|
+
if (trimmed.length === 0) {
|
|
14243
|
+
// Blank optional value: no format rule applies.
|
|
14244
|
+
return undefined;
|
|
14245
|
+
}
|
|
14246
|
+
if (type === 'email' && !EMAIL_REGEX.test(trimmed)) {
|
|
14247
|
+
return DEFAULT_MESSAGES.email;
|
|
14248
|
+
}
|
|
14249
|
+
if (type === 'name' && trimmed.length < 2) {
|
|
14250
|
+
return DEFAULT_MESSAGES.name;
|
|
14251
|
+
}
|
|
14252
|
+
return undefined;
|
|
14253
|
+
}
|
|
14254
|
+
/**
|
|
14255
|
+
* Validate every registered field against the current form state. Returns a map
|
|
14256
|
+
* of `formId → error message` for invalid fields only (valid fields are omitted).
|
|
14257
|
+
* The validator's own `message` is preferred; otherwise the per-rule default
|
|
14258
|
+
* from {@link validateTextInput} is used.
|
|
14259
|
+
*/
|
|
14260
|
+
function validateForm(validators, formStates) {
|
|
14261
|
+
const errors = {};
|
|
14262
|
+
for (const [formId, validator] of Object.entries(validators)) {
|
|
14263
|
+
const defaultError = validateTextInput(validator.type, validator.required, formStates[formId]);
|
|
14264
|
+
if (defaultError !== undefined) {
|
|
14265
|
+
errors[formId] = validator.message ?? defaultError;
|
|
14266
|
+
}
|
|
14267
|
+
}
|
|
14268
|
+
return errors;
|
|
14269
|
+
}
|
|
14270
|
+
|
|
14103
14271
|
class BasicNamiFlow {
|
|
14104
14272
|
constructor(flowObject = {}) {
|
|
14105
14273
|
this.id = flowObject.id ?? '';
|
|
@@ -14331,12 +14499,40 @@ class NamiFlow extends BasicNamiFlow {
|
|
|
14331
14499
|
}
|
|
14332
14500
|
return lifecycles;
|
|
14333
14501
|
}
|
|
14502
|
+
/**
|
|
14503
|
+
* A lifecycle chain is a "form submit" if any phase (before/action/after) runs
|
|
14504
|
+
* one of the advancing functions: setTagsFromForm, flowNext, or flowNav. The
|
|
14505
|
+
* POC submit buttons use flowNext/flowNav (no dedicated submit action), so the
|
|
14506
|
+
* gate keys off these. Close/dismiss/back/pause are deliberately excluded —
|
|
14507
|
+
* they must never be blocked by validation. Mirrors Apple
|
|
14508
|
+
* NamiFlowControl.isFormSubmitLifecycle / Android NamiFlow.isFormSubmitLifecycle.
|
|
14509
|
+
*/
|
|
14510
|
+
isFormSubmitLifecycle(lifecycles) {
|
|
14511
|
+
const submitFns = [
|
|
14512
|
+
NamiFlowActionFunction.SET_TAGS_FROM_FORM,
|
|
14513
|
+
NamiFlowActionFunction.NEXT,
|
|
14514
|
+
NamiFlowActionFunction.NAVIGATE,
|
|
14515
|
+
];
|
|
14516
|
+
return lifecycles.some(cycle => [cycle.before, cycle.action, cycle.after].some(actions => actions?.some(action => submitFns.includes(action.function))));
|
|
14517
|
+
}
|
|
14334
14518
|
triggerActions(actionId, component, data) {
|
|
14335
14519
|
const lifecycles = this.lifecycles(actionId);
|
|
14336
14520
|
if (!lifecycles)
|
|
14337
14521
|
return;
|
|
14338
14522
|
this.flowLog(`triggerActions(${actionId}) — step=${this.currentFlowStep?.id}, isPaused=${this.isPaused}`);
|
|
14339
14523
|
this.currentButton = component;
|
|
14524
|
+
// Implicit form-submit validation gate (the NAM-1143 on-device lesson).
|
|
14525
|
+
// When this action advances the flow and validators are registered, validate
|
|
14526
|
+
// first; write the errors and halt the chain if any field is invalid.
|
|
14527
|
+
const validators = PaywallState.currentProvider?.state.formFieldValidators ?? {};
|
|
14528
|
+
if (this.isFormSubmitLifecycle(lifecycles) && Object.keys(validators).length) {
|
|
14529
|
+
const errors = validateForm(validators, this.getFormData());
|
|
14530
|
+
PaywallState.currentProvider?.setFormFieldErrors(errors);
|
|
14531
|
+
if (Object.keys(errors).length) {
|
|
14532
|
+
this.flowLog(`triggerActions(${actionId}) — HALTED by form validation: ${JSON.stringify(errors)}`);
|
|
14533
|
+
return;
|
|
14534
|
+
}
|
|
14535
|
+
}
|
|
14340
14536
|
this.executeFullLifecycles(lifecycles, data);
|
|
14341
14537
|
}
|
|
14342
14538
|
executeFullLifecycles(lifecycles, data) {
|
|
@@ -14543,6 +14739,22 @@ class NamiFlow extends BasicNamiFlow {
|
|
|
14543
14739
|
});
|
|
14544
14740
|
}
|
|
14545
14741
|
break;
|
|
14742
|
+
case NamiFlowActionFunction.SET_TAGS_FROM_FORM: {
|
|
14743
|
+
const formData = this.getFormData();
|
|
14744
|
+
const tags = {};
|
|
14745
|
+
Object.entries(formData).forEach(([key, raw]) => {
|
|
14746
|
+
const value = formTagValue(raw);
|
|
14747
|
+
if (value === undefined) {
|
|
14748
|
+
return; // skip null / "" (false and 0 are kept by formTagValue)
|
|
14749
|
+
}
|
|
14750
|
+
tags[key] = value;
|
|
14751
|
+
NamiCustomerManager$2.setCustomerAttribute(key, value);
|
|
14752
|
+
});
|
|
14753
|
+
if (Object.keys(tags).length) {
|
|
14754
|
+
PaywallState.setUserTags(tags);
|
|
14755
|
+
}
|
|
14756
|
+
break;
|
|
14757
|
+
}
|
|
14546
14758
|
case NamiFlowActionFunction.SET_LAUNCH_CONTEXT: {
|
|
14547
14759
|
// Two supported shapes: { customAttributes: {...} } (nav-action wire shape)
|
|
14548
14760
|
// and { key, value } (used by Apple/Android/Roku). We deliberately do NOT
|
|
@@ -64765,4 +64977,4 @@ var internal = /*#__PURE__*/Object.freeze({
|
|
|
64765
64977
|
NamiPurchaseManager: NamiPurchaseManager
|
|
64766
64978
|
});
|
|
64767
64979
|
|
|
64768
|
-
export { ALREADY_CONFIGURED, ANONYMOUS_MODE, ANONYMOUS_MODE_ALREADY_OFF, ANONYMOUS_MODE_ALREADY_ON, ANONYMOUS_MODE_LOGIN_NOT_ALLOWED, ANONYMOUS_UUID, APIError, API_ACTIVE_ENTITLEMENTS, API_CAMPAIGN_RULES, API_CAMPAIGN_SESSION_TIMESTAMP, API_CONFIG, API_MAX_CALLS_LIMIT, API_PAYWALLS, API_PRODUCTS, API_RETRY_DELAY_SEC, API_TIMEOUT_LIMIT, API_VERSION, AUTH_DEVICE, AVAILABLE_ACTIVE_ENTITLEMENTS_CHANGED, AVAILABLE_CAMPAIGNS_CHANGED, AccountStateAction, AnonymousCDPError, AnonymousLoginError, AnonymousModeAlreadyOffError, AnonymousModeAlreadyOnError, BASE_STAGING_URL, BASE_URL, BASE_URL_PATH, BadRequestError, BasicNamiFlow, BorderMap, BorderSideMap, CAMPAIGN_NOT_AVAILABLE, CUSTOMER_ATTRIBUTES_KEY_PREFIX, CUSTOMER_JOURNEY_STATE_CHANGED, CUSTOM_HOST_PREFIX, CampaignNotAvailableError, CampaignRuleConversionEventType, CampaignRuleRepository, Capabilities, ClientError, ConfigRepository, ConflictError, CustomerJourneyRepository, DEVELOPMENT, DEVICE_API_TIMEOUT_LIMIT, DEVICE_ID_NOT_SET, DEVICE_ID_REQUIRED, DISABLE_ASYNC_LOGIN_LOGOUT, DeviceIDRequiredError, DeviceRepository, EXTENDED_CLIENT_INFO_DELIMITER, EXTENDED_CLIENT_INFO_PREFIX, EXTENDED_PLATFORM, EXTENDED_PLATFORM_VERSION, EXTERNAL_ID_REQUIRED, EntitlementRepository, EntitlementUtils, ExternalIDRequiredError, FLOW_SCREENS_NOT_AVAILABLE, FlowScreensNotAvailableError, HTML_REGEX, INITIAL_APP_CONFIG, INITIAL_CAMPAIGN_RULES, INITIAL_PAYWALLS, INITIAL_PRODUCTS, INITIAL_SESSION_COUNTER_VALUE, INITIAL_SUCCESS, InternalServerError, KEY_SESSION_COUNTER, LIQUID_VARIABLE_REGEX, LOCAL_NAMI_ENTITLEMENTS, LOG_HTTP_REQUESTS, LOG_HTTP_TRAFFIC, LaunchCampaignError, LaunchContextResolver, LogLevel, NAMI_CONFIGURATION, NAMI_CUSTOMER_JOURNEY_STATE, NAMI_LANGUAGE_CODE, NAMI_LAST_IMPRESSION_ID, NAMI_LAUNCH_ID, NAMI_PROFILE, NAMI_PURCHASE_CHANNEL, NAMI_PURCHASE_IMPRESSION_ID, NAMI_SDK_PACKAGE_VERSION, NAMI_SDK_VERSION, NAMI_SESSION_ID, NAMI_STORAGE_KEYS, Nami, NamiAPI, NamiAnimationType, NamiCampaignManager$1 as NamiCampaignManager, NamiCampaignRuleType, NamiConditionEvaluator, NamiCustomerManager$1 as NamiCustomerManager, NamiEntitlementManager$1 as NamiEntitlementManager, NamiEventEmitter, NamiFlow, NamiFlowActionFunction, NamiFlowManager$1 as NamiFlowManager, NamiFlowStepType, NamiPaywallAction, NamiPaywallManager$1 as NamiPaywallManager, PaywallManagerEvents as NamiPaywallManagerEvents, NamiPurchaseManager$1 as NamiPurchaseManager, NamiRefs, NamiReservedActions, NotFoundError, PAYWALL_ACTION_EVENT, PLATFORM_ID_REQUIRED, PRODUCTION, PaywallManagerEvents, PaywallRepository, PaywallState, PlacementLabelResolver, PlatformIDRequiredError, ProductRepository, RECONFIG_SUCCESS, RetryLimitExceededError, SDKNotInitializedError, SDK_NOT_INITIALIZED, SERVER_NAMI_ENTITLEMENTS, SESSION_REQUIRED, SHOULD_SHOW_LOADING_INDICATOR, SKU_TEXT_REGEX, SMART_TEXT_PATTERN, STARTUP_TELEMETRY, STATUS_BAD_REQUEST, STATUS_CONFLICT, STATUS_INTERNAL_SERVER_ERROR, STATUS_NOT_FOUND, STATUS_SUCCESS, SessionService, SimpleEventTarget, StorageService, UNABLE_TO_UPDATE_CDP_ID, USE_STAGING_API, VALIDATE_PRODUCT_GROUPS, VAR_REGEX, NamiProfileManager$1 as _NamiProfileManager, internal as _internal, activateEntitlementByPurchase, activeEntitlements, aggregateScreenreaderText, allCampaigns, allPaywalls, applyEntitlementActivation, audienceSplitPosition, bestUrlCampaignMatch, bigintToUuid, checkAnySkuHasPromoOffer, checkAnySkuHasTrialOffer, convertISO8601PeriodToText, convertLocale, convertOfferToPricingPhase, createNamiEntitlements, currentSku, empty, extractStandardPricingPhases, formatDate, formattedPrice, generateUUID, getApiCampaigns, getApiPaywalls, getBaseUrl, getBillingPeriodNumber, getCurrencyFormat, getDeviceData, getDeviceFormFactor, getDeviceScaleFactor, getEffectiveWebStyle, getEntitlementRefIdsForSku, getExtendedClientInfo, getFreeTrialPeriod, getInitialCampaigns, getInitialPaywalls, getPaywall, getPaywallDataFromLabel, getPercentagePriceDifference, getPeriodNumberInDays, getPeriodNumberInWeeks, getPlatformAdapters, getPriceDifference, getPricePerMonth, getProductDetail, getPurchaseAdapter, getReferenceSku, getSkuProductDetailKeys, getSkuSmartTextValue, getSlideSmartTextValue, getStandardBillingPeriod, getTranslate, getUrlParams, handleErrors, hasAllPaywalls, hasCapability, hasPurchaseManagement, initialState, invokeHandler, isAnonymousMode, isInitialConfigCompressed, isNamiFlowCampaign, isSubscription, isValidISODate, isValidUrl, logger, mapAnonymousCampaigns, namiBuySKU, normalizeLaunchContext, parseToSemver, postConversion, productDetail, registerPlatformAdapters, registerPurchaseAdapter, selectSegment, setActiveNamiEntitlements, shouldValidateProductGroups, skuItems, skuMapFromEntitlements, storageService, toDouble, toNamiEntitlements, toNamiSKU, tryParseB64Gzip, tryParseJson, updateRelatedSKUsForNamiEntitlement, uuidFromSplitPosition, validateMinSDKVersion };
|
|
64980
|
+
export { ALREADY_CONFIGURED, ANONYMOUS_MODE, ANONYMOUS_MODE_ALREADY_OFF, ANONYMOUS_MODE_ALREADY_ON, ANONYMOUS_MODE_LOGIN_NOT_ALLOWED, ANONYMOUS_UUID, APIError, API_ACTIVE_ENTITLEMENTS, API_CAMPAIGN_RULES, API_CAMPAIGN_SESSION_TIMESTAMP, API_CONFIG, API_MAX_CALLS_LIMIT, API_PAYWALLS, API_PRODUCTS, API_RETRY_DELAY_SEC, API_TIMEOUT_LIMIT, API_VERSION, AUTH_DEVICE, AVAILABLE_ACTIVE_ENTITLEMENTS_CHANGED, AVAILABLE_CAMPAIGNS_CHANGED, AccountStateAction, AnonymousCDPError, AnonymousLoginError, AnonymousModeAlreadyOffError, AnonymousModeAlreadyOnError, BASE_STAGING_URL, BASE_URL, BASE_URL_PATH, BadRequestError, BasicNamiFlow, BorderMap, BorderSideMap, CAMPAIGN_NOT_AVAILABLE, CUSTOMER_ATTRIBUTES_KEY_PREFIX, CUSTOMER_JOURNEY_STATE_CHANGED, CUSTOM_HOST_PREFIX, CampaignNotAvailableError, CampaignRuleConversionEventType, CampaignRuleRepository, Capabilities, ClientError, ConfigRepository, ConflictError, CustomerJourneyRepository, DEVELOPMENT, DEVICE_API_TIMEOUT_LIMIT, DEVICE_ID_NOT_SET, DEVICE_ID_REQUIRED, DISABLE_ASYNC_LOGIN_LOGOUT, DeviceIDRequiredError, DeviceRepository, EXTENDED_CLIENT_INFO_DELIMITER, EXTENDED_CLIENT_INFO_PREFIX, EXTENDED_PLATFORM, EXTENDED_PLATFORM_VERSION, EXTERNAL_ID_REQUIRED, EntitlementRepository, EntitlementUtils, ExternalIDRequiredError, FLOW_SCREENS_NOT_AVAILABLE, FlowScreensNotAvailableError, HTML_REGEX, INITIAL_APP_CONFIG, INITIAL_CAMPAIGN_RULES, INITIAL_PAYWALLS, INITIAL_PRODUCTS, INITIAL_SESSION_COUNTER_VALUE, INITIAL_SUCCESS, InternalServerError, KEY_SESSION_COUNTER, LIQUID_VARIABLE_REGEX, LOCAL_NAMI_ENTITLEMENTS, LOG_HTTP_REQUESTS, LOG_HTTP_TRAFFIC, LaunchCampaignError, LaunchContextResolver, LogLevel, NAMI_CONFIGURATION, NAMI_CUSTOMER_JOURNEY_STATE, NAMI_LANGUAGE_CODE, NAMI_LAST_IMPRESSION_ID, NAMI_LAUNCH_ID, NAMI_PROFILE, NAMI_PURCHASE_CHANNEL, NAMI_PURCHASE_IMPRESSION_ID, NAMI_SDK_PACKAGE_VERSION, NAMI_SDK_VERSION, NAMI_SESSION_ID, NAMI_STORAGE_KEYS, Nami, NamiAPI, NamiAnimationType, NamiCampaignManager$1 as NamiCampaignManager, NamiCampaignRuleType, NamiConditionEvaluator, NamiCustomerManager$1 as NamiCustomerManager, NamiEntitlementManager$1 as NamiEntitlementManager, NamiEventEmitter, NamiFlow, NamiFlowActionFunction, NamiFlowManager$1 as NamiFlowManager, NamiFlowStepType, NamiPaywallAction, NamiPaywallManager$1 as NamiPaywallManager, PaywallManagerEvents as NamiPaywallManagerEvents, NamiPurchaseManager$1 as NamiPurchaseManager, NamiRefs, NamiReservedActions, NotFoundError, PAYWALL_ACTION_EVENT, PLATFORM_ID_REQUIRED, PRODUCTION, PaywallManagerEvents, PaywallRepository, PaywallState, PlacementLabelResolver, PlatformIDRequiredError, ProductRepository, RECONFIG_SUCCESS, RetryLimitExceededError, SDKNotInitializedError, SDK_NOT_INITIALIZED, SERVER_NAMI_ENTITLEMENTS, SESSION_REQUIRED, SHOULD_SHOW_LOADING_INDICATOR, SKU_TEXT_REGEX, SMART_TEXT_PATTERN, STARTUP_TELEMETRY, STATUS_BAD_REQUEST, STATUS_CONFLICT, STATUS_INTERNAL_SERVER_ERROR, STATUS_NOT_FOUND, STATUS_SUCCESS, SessionService, SimpleEventTarget, StorageService, UNABLE_TO_UPDATE_CDP_ID, USE_STAGING_API, VALIDATE_PRODUCT_GROUPS, VAR_REGEX, NamiProfileManager$1 as _NamiProfileManager, internal as _internal, activateEntitlementByPurchase, activeEntitlements, aggregateScreenreaderText, allCampaigns, allPaywalls, applyEntitlementActivation, audienceSplitPosition, bestUrlCampaignMatch, bigintToUuid, checkAnySkuHasPromoOffer, checkAnySkuHasTrialOffer, convertISO8601PeriodToText, convertLocale, convertOfferToPricingPhase, createNamiEntitlements, currentSku, empty, extractStandardPricingPhases, formatDate, formattedPrice, generateUUID, getApiCampaigns, getApiPaywalls, getBaseUrl, getBillingPeriodNumber, getCurrencyFormat, getDeviceData, getDeviceFormFactor, getDeviceScaleFactor, getEffectiveWebStyle, getEntitlementRefIdsForSku, getExtendedClientInfo, getFreeTrialPeriod, getInitialCampaigns, getInitialPaywalls, getPaywall, getPaywallDataFromLabel, getPercentagePriceDifference, getPeriodNumberInDays, getPeriodNumberInWeeks, getPlatformAdapters, getPriceDifference, getPricePerMonth, getProductDetail, getPurchaseAdapter, getReferenceSku, getSkuProductDetailKeys, getSkuSmartTextValue, getSlideSmartTextValue, getStandardBillingPeriod, getTranslate, getUrlParams, handleErrors, hasAllPaywalls, hasCapability, hasPurchaseManagement, initialState, invokeHandler, isAnonymousMode, isInitialConfigCompressed, isNamiFlowCampaign, isSubscription, isValidISODate, isValidUrl, logger, mapAnonymousCampaigns, namiBuySKU, normalizeLaunchContext, parseToSemver, postConversion, productDetail, registerPlatformAdapters, registerPurchaseAdapter, selectSegment, setActiveNamiEntitlements, shouldValidateProductGroups, skuItems, skuMapFromEntitlements, storageService, toDouble, toNamiEntitlements, toNamiSKU, tryParseB64Gzip, tryParseJson, updateRelatedSKUsForNamiEntitlement, uuidFromSplitPosition, validateForm, validateMinSDKVersion, validateTextInput };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@namiml/sdk-core",
|
|
3
|
-
"version": "3.4.3-dev.
|
|
3
|
+
"version": "3.4.3-dev.202606181527",
|
|
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",
|