@funnelsgrove/runtime 0.1.55 → 0.1.59
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/components/FunnelContext.d.ts +5 -1
- package/dist/components/FunnelContext.js +2 -1
- package/dist/components/ManageSubscriptionScreen.js +4 -1
- package/dist/components/SubscriptionHandoffScreen.d.ts +2 -1
- package/dist/components/SubscriptionHandoffScreen.js +11 -6
- package/dist/config/funnel.manifest.types.d.ts +4 -2
- package/dist/generated/step-contract-hash.d.ts +1 -0
- package/dist/generated/step-contract-hash.js +2 -0
- package/dist/index.d.ts +12 -0
- package/dist/index.js +10 -0
- package/dist/migrations/step-contract-v2.d.ts +50 -0
- package/dist/migrations/step-contract-v2.js +310 -0
- package/dist/migrations/step-contract-v3.d.ts +41 -0
- package/dist/migrations/step-contract-v3.js +26 -0
- package/dist/runtime/funnel-attribution.d.ts +1 -0
- package/dist/runtime/funnel-attribution.js +24 -0
- package/dist/runtime/funnel-manifest.validation.d.ts +7 -2
- package/dist/runtime/funnel-manifest.validation.js +24 -68
- package/dist/runtime/funnel-step-lifecycle.d.ts +35 -0
- package/dist/runtime/funnel-step-lifecycle.js +49 -0
- package/dist/runtime/funnel-step-metadata.validation.d.ts +7 -0
- package/dist/runtime/funnel-step-metadata.validation.js +88 -0
- package/dist/runtime/submit-email-capture.d.ts +23 -0
- package/dist/runtime/submit-email-capture.js +54 -0
- package/dist/runtime/use-funnel-flow-controller.d.ts +71 -5
- package/dist/runtime/use-funnel-flow-controller.js +850 -116
- package/dist/runtime/use-step-choices.d.ts +58 -0
- package/dist/runtime/use-step-choices.js +187 -0
- package/dist/sdk/userAnswers.d.ts +1 -0
- package/dist/services/api.service.d.ts +10 -0
- package/dist/services/api.service.js +32 -0
- package/dist/services/funnel-sdk.service.d.ts +17 -0
- package/dist/services/funnel-sdk.service.js +13 -0
- package/dist/steps/choice-contract.d.ts +33 -0
- package/dist/steps/choice-contract.js +302 -0
- package/dist/steps/step-contract.d.ts +498 -0
- package/dist/steps/step-contract.js +506 -0
- package/dist/steps/types.d.ts +7 -5
- package/dist/steps/types.js +1 -18
- package/dist/testing/funnel-contract-journey-driver.d.ts +35 -0
- package/dist/testing/funnel-contract-journey-driver.js +266 -0
- package/dist/testing/index.d.ts +1 -0
- package/dist/testing/index.js +1 -0
- package/dist/validation/fixtures/invalid-reserved-identities.d.ts +249 -0
- package/dist/validation/fixtures/invalid-reserved-identities.js +20 -0
- package/dist/validation/fixtures/valid-v2-project.d.ts +419 -0
- package/dist/validation/fixtures/valid-v2-project.js +228 -0
- package/dist/validation/fixtures/valid-v3-project.d.ts +419 -0
- package/dist/validation/fixtures/valid-v3-project.js +30 -0
- package/dist/validation/funnel-contract-diagnostics.d.ts +49 -0
- package/dist/validation/funnel-contract-diagnostics.js +67 -0
- package/dist/validation/funnel-contract-validator.d.ts +30 -0
- package/dist/validation/funnel-contract-validator.js +1458 -0
- package/dist/validation/funnel-manifest-input.normalization.d.ts +67 -0
- package/dist/validation/funnel-manifest-input.normalization.js +386 -0
- package/package.json +9 -1
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { type ReactNode } from 'react';
|
|
2
|
+
import { type FunnelChoiceCompletion } from '../steps/choice-contract.js';
|
|
3
|
+
import { type FunnelChoiceOption, type FunnelEmojiChoiceOption } from '../steps/step-contract.js';
|
|
4
|
+
import type { FunnelStepMeta } from '../steps/types.js';
|
|
5
|
+
import { type FunnelContractDiagnostic } from '../validation/funnel-contract-diagnostics.js';
|
|
6
|
+
type ChoiceOptions = readonly (FunnelChoiceOption | FunnelEmojiChoiceOption)[];
|
|
7
|
+
type ChoiceResult = FunnelChoiceCompletion | FunnelContractDiagnostic;
|
|
8
|
+
type ChoiceCommit = (stepMeta: FunnelStepMeta, result: ChoiceResult) => void;
|
|
9
|
+
export type FunnelStepChoices = {
|
|
10
|
+
selectSingle: (optionId: string) => void;
|
|
11
|
+
completeMulti: (optionIds: readonly string[]) => void;
|
|
12
|
+
};
|
|
13
|
+
export declare const createStepChoiceActions: (stepMeta: FunnelStepMeta, options: ChoiceOptions, commit: ChoiceCommit) => FunnelStepChoices;
|
|
14
|
+
export declare const registerInternalChoiceCommit: (carrier: object, commit: ChoiceCommit) => void;
|
|
15
|
+
export declare const FunnelChoiceCommitProvider: ({ carrier, children, }: {
|
|
16
|
+
carrier: object;
|
|
17
|
+
children: ReactNode;
|
|
18
|
+
}) => import("react").FunctionComponentElement<import("react").ProviderProps<ChoiceCommit | null>>;
|
|
19
|
+
export declare const useStepChoices: (stepMeta: FunnelStepMeta, options: ChoiceOptions) => FunnelStepChoices;
|
|
20
|
+
export type AtomicChoiceCommitResult = 'committed' | 'ignored' | 'quarantined' | 'record-failed';
|
|
21
|
+
export declare const consumeChoiceCompletionMarker: (marker: string | null, previousStepId: string) => {
|
|
22
|
+
consumed: boolean;
|
|
23
|
+
nextMarker: string | null;
|
|
24
|
+
};
|
|
25
|
+
type AtomicChoiceCommitInput<StepId extends string> = {
|
|
26
|
+
activeStepId: StepId;
|
|
27
|
+
activeStepMeta: FunnelStepMeta | undefined;
|
|
28
|
+
stepMeta: FunnelStepMeta;
|
|
29
|
+
result: ChoiceResult;
|
|
30
|
+
environment: 'development' | 'production' | 'test';
|
|
31
|
+
getAttributes: () => Record<string, unknown>;
|
|
32
|
+
writeAttributes: (attributes: Record<string, unknown>) => void;
|
|
33
|
+
isInFlight: () => boolean;
|
|
34
|
+
beginCommit: () => void;
|
|
35
|
+
cancelCommit: () => void;
|
|
36
|
+
recordCompletion: (input: {
|
|
37
|
+
selected?: Record<string, unknown>;
|
|
38
|
+
metadata?: Record<string, unknown>;
|
|
39
|
+
}) => boolean;
|
|
40
|
+
markCompletedBeforeNavigation: () => void;
|
|
41
|
+
clearCompletedBeforeNavigation: (markerWasInstalled: boolean) => void;
|
|
42
|
+
resolveNext: (attributes: Record<string, unknown>) => StepId;
|
|
43
|
+
navigate: (stepId: StepId) => void;
|
|
44
|
+
reportDiagnostic: (diagnostic: FunnelContractDiagnostic) => void;
|
|
45
|
+
};
|
|
46
|
+
export declare const runAtomicChoiceCommit: <StepId extends string>(input: AtomicChoiceCommitInput<StepId>) => AtomicChoiceCommitResult;
|
|
47
|
+
type ChoiceBypassOperation = 'setAnswer' | 'setAttribute' | 'goChoice' | 'completeStep';
|
|
48
|
+
export declare const enforceChoiceBypassGuard: (input: {
|
|
49
|
+
operation: ChoiceBypassOperation;
|
|
50
|
+
activeStepId: string;
|
|
51
|
+
sourceStepId?: string;
|
|
52
|
+
key?: string;
|
|
53
|
+
stepById: Record<string, FunnelStepMeta>;
|
|
54
|
+
stepContractVersion: number;
|
|
55
|
+
environment: "development" | "production" | "test";
|
|
56
|
+
reportDiagnostic: (diagnostic: FunnelContractDiagnostic) => void;
|
|
57
|
+
}) => boolean;
|
|
58
|
+
export {};
|
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { createContext, createElement, useContext, useMemo, } from 'react';
|
|
3
|
+
import { buildChoiceCompletion, parseFunnelChoiceDescriptor, } from '../steps/choice-contract.js';
|
|
4
|
+
import { LEGACY_UNVERSIONED_STEP_CONTRACT_VERSION, SUPPORTED_STEP_CONTRACT_VERSIONS, } from '../steps/step-contract.js';
|
|
5
|
+
import { createFunnelContractDiagnostic, } from '../validation/funnel-contract-diagnostics.js';
|
|
6
|
+
export const createStepChoiceActions = (stepMeta, options, commit) => ({
|
|
7
|
+
selectSingle: (optionId) => commit(stepMeta, buildChoiceCompletion(stepMeta, options, optionId)),
|
|
8
|
+
completeMulti: (optionIds) => commit(stepMeta, buildChoiceCompletion(stepMeta, options, optionIds)),
|
|
9
|
+
});
|
|
10
|
+
const FunnelChoiceCommitContext = createContext(null);
|
|
11
|
+
const internalChoiceCommits = new WeakMap();
|
|
12
|
+
export const registerInternalChoiceCommit = (carrier, commit) => {
|
|
13
|
+
internalChoiceCommits.set(carrier, commit);
|
|
14
|
+
};
|
|
15
|
+
export const FunnelChoiceCommitProvider = ({ carrier, children, }) => {
|
|
16
|
+
var _a;
|
|
17
|
+
return createElement(FunnelChoiceCommitContext.Provider, { value: (_a = internalChoiceCommits.get(carrier)) !== null && _a !== void 0 ? _a : null }, children);
|
|
18
|
+
};
|
|
19
|
+
export const useStepChoices = (stepMeta, options) => {
|
|
20
|
+
const commit = useContext(FunnelChoiceCommitContext);
|
|
21
|
+
if (!commit) {
|
|
22
|
+
throw new Error('useStepChoices must be used inside FunnelProvider');
|
|
23
|
+
}
|
|
24
|
+
return useMemo(() => createStepChoiceActions(stepMeta, options, commit), [commit, options, stepMeta]);
|
|
25
|
+
};
|
|
26
|
+
const isChoiceCompletion = (value) => {
|
|
27
|
+
try {
|
|
28
|
+
return typeof value === 'object'
|
|
29
|
+
&& value !== null
|
|
30
|
+
&& typeof value.answerKey === 'string'
|
|
31
|
+
&& 'selected' in value
|
|
32
|
+
&& 'value' in value;
|
|
33
|
+
}
|
|
34
|
+
catch (_a) {
|
|
35
|
+
return false;
|
|
36
|
+
}
|
|
37
|
+
};
|
|
38
|
+
const choiceIdentityMatches = (candidate, active) => {
|
|
39
|
+
if (!active) {
|
|
40
|
+
return false;
|
|
41
|
+
}
|
|
42
|
+
try {
|
|
43
|
+
const candidateDescriptor = parseFunnelChoiceDescriptor(candidate.type, candidate.choice);
|
|
44
|
+
const activeDescriptor = parseFunnelChoiceDescriptor(active.type, active.choice);
|
|
45
|
+
return candidate.id === active.id
|
|
46
|
+
&& candidate.type === active.type
|
|
47
|
+
&& candidateDescriptor.ok
|
|
48
|
+
&& activeDescriptor.ok
|
|
49
|
+
&& candidateDescriptor.descriptor.answerKey === activeDescriptor.descriptor.answerKey
|
|
50
|
+
&& candidateDescriptor.descriptor.allowEmpty === activeDescriptor.descriptor.allowEmpty;
|
|
51
|
+
}
|
|
52
|
+
catch (_a) {
|
|
53
|
+
return false;
|
|
54
|
+
}
|
|
55
|
+
};
|
|
56
|
+
export const consumeChoiceCompletionMarker = (marker, previousStepId) => marker === previousStepId
|
|
57
|
+
? { consumed: true, nextMarker: null }
|
|
58
|
+
: { consumed: false, nextMarker: marker };
|
|
59
|
+
export const runAtomicChoiceCommit = (input) => {
|
|
60
|
+
if (input.isInFlight()) {
|
|
61
|
+
return 'ignored';
|
|
62
|
+
}
|
|
63
|
+
const completion = isChoiceCompletion(input.result) ? input.result : null;
|
|
64
|
+
if (completion === null) {
|
|
65
|
+
const diagnostic = input.result;
|
|
66
|
+
if (diagnostic.stepId !== input.activeStepId) {
|
|
67
|
+
input.reportDiagnostic(diagnostic);
|
|
68
|
+
return 'ignored';
|
|
69
|
+
}
|
|
70
|
+
if (input.environment !== 'production') {
|
|
71
|
+
throw new Error(`${diagnostic.code}: ${diagnostic.reason}`);
|
|
72
|
+
}
|
|
73
|
+
input.beginCommit();
|
|
74
|
+
const currentAttributes = input.getAttributes();
|
|
75
|
+
let abortStarted = false;
|
|
76
|
+
let markerInstallationStarted = false;
|
|
77
|
+
const abortCommit = () => {
|
|
78
|
+
if (abortStarted) {
|
|
79
|
+
return;
|
|
80
|
+
}
|
|
81
|
+
abortStarted = true;
|
|
82
|
+
try {
|
|
83
|
+
input.clearCompletedBeforeNavigation(markerInstallationStarted);
|
|
84
|
+
}
|
|
85
|
+
finally {
|
|
86
|
+
input.cancelCommit();
|
|
87
|
+
}
|
|
88
|
+
};
|
|
89
|
+
try {
|
|
90
|
+
input.reportDiagnostic(diagnostic);
|
|
91
|
+
const nextStepId = input.resolveNext(currentAttributes);
|
|
92
|
+
if (!input.recordCompletion({})) {
|
|
93
|
+
abortCommit();
|
|
94
|
+
return 'record-failed';
|
|
95
|
+
}
|
|
96
|
+
markerInstallationStarted = true;
|
|
97
|
+
input.markCompletedBeforeNavigation();
|
|
98
|
+
input.navigate(nextStepId);
|
|
99
|
+
return 'quarantined';
|
|
100
|
+
}
|
|
101
|
+
catch (error) {
|
|
102
|
+
abortCommit();
|
|
103
|
+
throw error;
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
if (input.activeStepId !== input.stepMeta.id
|
|
107
|
+
|| !choiceIdentityMatches(input.stepMeta, input.activeStepMeta)) {
|
|
108
|
+
return 'ignored';
|
|
109
|
+
}
|
|
110
|
+
input.beginCommit();
|
|
111
|
+
const currentAttributes = input.getAttributes();
|
|
112
|
+
let abortStarted = false;
|
|
113
|
+
let attributesWritten = false;
|
|
114
|
+
let markerInstallationStarted = false;
|
|
115
|
+
const abortCommit = () => {
|
|
116
|
+
if (abortStarted) {
|
|
117
|
+
return;
|
|
118
|
+
}
|
|
119
|
+
abortStarted = true;
|
|
120
|
+
try {
|
|
121
|
+
if (attributesWritten) {
|
|
122
|
+
input.writeAttributes(currentAttributes);
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
finally {
|
|
126
|
+
try {
|
|
127
|
+
input.clearCompletedBeforeNavigation(markerInstallationStarted);
|
|
128
|
+
}
|
|
129
|
+
finally {
|
|
130
|
+
input.cancelCommit();
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
};
|
|
134
|
+
try {
|
|
135
|
+
const nextAttributes = Object.assign(Object.assign({}, currentAttributes), { [completion.answerKey]: completion.value });
|
|
136
|
+
const nextStepId = input.resolveNext(nextAttributes);
|
|
137
|
+
attributesWritten = true;
|
|
138
|
+
input.writeAttributes(nextAttributes);
|
|
139
|
+
if (!input.recordCompletion({
|
|
140
|
+
selected: completion.selected,
|
|
141
|
+
metadata: { choice_answer_key: completion.answerKey },
|
|
142
|
+
})) {
|
|
143
|
+
abortCommit();
|
|
144
|
+
return 'record-failed';
|
|
145
|
+
}
|
|
146
|
+
markerInstallationStarted = true;
|
|
147
|
+
input.markCompletedBeforeNavigation();
|
|
148
|
+
input.navigate(nextStepId);
|
|
149
|
+
return 'committed';
|
|
150
|
+
}
|
|
151
|
+
catch (error) {
|
|
152
|
+
abortCommit();
|
|
153
|
+
throw error;
|
|
154
|
+
}
|
|
155
|
+
};
|
|
156
|
+
export const enforceChoiceBypassGuard = (input) => {
|
|
157
|
+
var _a;
|
|
158
|
+
const enforcesTypedChoices = SUPPORTED_STEP_CONTRACT_VERSIONS.some((version) => (version !== LEGACY_UNVERSIONED_STEP_CONTRACT_VERSION
|
|
159
|
+
&& version === input.stepContractVersion));
|
|
160
|
+
if (!enforcesTypedChoices) {
|
|
161
|
+
return true;
|
|
162
|
+
}
|
|
163
|
+
const sourceStepId = (_a = input.sourceStepId) !== null && _a !== void 0 ? _a : input.activeStepId;
|
|
164
|
+
const sourceMeta = input.stepById[sourceStepId];
|
|
165
|
+
const activeMeta = input.stepById[input.activeStepId];
|
|
166
|
+
const shouldReject = input.operation === 'setAnswer' || input.operation === 'setAttribute'
|
|
167
|
+
? Boolean((activeMeta === null || activeMeta === void 0 ? void 0 : activeMeta.choice) && input.key === activeMeta.choice.answerKey)
|
|
168
|
+
: Boolean(sourceMeta === null || sourceMeta === void 0 ? void 0 : sourceMeta.choice);
|
|
169
|
+
if (!shouldReject) {
|
|
170
|
+
return true;
|
|
171
|
+
}
|
|
172
|
+
const diagnostic = createFunnelContractDiagnostic({
|
|
173
|
+
code: 'FG-CHOICE-006',
|
|
174
|
+
file: null,
|
|
175
|
+
stepId: sourceStepId,
|
|
176
|
+
expected: 'useStepChoices',
|
|
177
|
+
received: { operation: input.operation },
|
|
178
|
+
reason: 'A generic answer or completion API attempted to bypass the typed choice transaction.',
|
|
179
|
+
guide: 'docs/product-specs/funnel-template-step-authoring.md',
|
|
180
|
+
repair: 'Complete descriptor-bearing choice steps with useStepChoices.',
|
|
181
|
+
});
|
|
182
|
+
if (input.environment !== 'production') {
|
|
183
|
+
throw new Error(`${diagnostic.code}: ${diagnostic.reason}`);
|
|
184
|
+
}
|
|
185
|
+
input.reportDiagnostic(diagnostic);
|
|
186
|
+
return false;
|
|
187
|
+
};
|
|
@@ -51,6 +51,11 @@ export type TempPhotoUploadResult = {
|
|
|
51
51
|
sizeBytes: number;
|
|
52
52
|
uploadedAt: string;
|
|
53
53
|
};
|
|
54
|
+
export type EmailCaptureResult = {
|
|
55
|
+
user: AppUser;
|
|
56
|
+
eventId: string;
|
|
57
|
+
eventCreated: boolean;
|
|
58
|
+
};
|
|
54
59
|
declare class ApiService {
|
|
55
60
|
private readFileAsDataUrl;
|
|
56
61
|
getOrCreateClientUserId(): string;
|
|
@@ -68,6 +73,11 @@ declare class ApiService {
|
|
|
68
73
|
updateUser(user: AppUser, options?: {
|
|
69
74
|
attribution?: FunnelUserAttribution;
|
|
70
75
|
}): Promise<AppUser>;
|
|
76
|
+
captureEmail(input: {
|
|
77
|
+
userId: string;
|
|
78
|
+
email: string;
|
|
79
|
+
environment: 'test' | 'live';
|
|
80
|
+
}): Promise<EmailCaptureResult>;
|
|
71
81
|
syncUrlUserAttributes(input: {
|
|
72
82
|
user: AppUserInput;
|
|
73
83
|
attributes?: FunnelUserAnswers;
|
|
@@ -286,6 +286,38 @@ class ApiService {
|
|
|
286
286
|
fallbackDocument: withMergedAttributionDocument(user.document, options === null || options === void 0 ? void 0 : options.attribution),
|
|
287
287
|
});
|
|
288
288
|
}
|
|
289
|
+
async captureEmail(input) {
|
|
290
|
+
var _a, _b;
|
|
291
|
+
const userId = input.userId.trim();
|
|
292
|
+
const email = input.email.trim().toLowerCase();
|
|
293
|
+
const payload = await funnelSdkService.captureEmail({
|
|
294
|
+
userId,
|
|
295
|
+
email,
|
|
296
|
+
environment: input.environment,
|
|
297
|
+
});
|
|
298
|
+
const eventId = asString(payload.eventId);
|
|
299
|
+
const responseUser = isRecord(payload.user) ? payload.user : null;
|
|
300
|
+
const responseUserId = responseUser === null || responseUser === void 0 ? void 0 : responseUser.user_id;
|
|
301
|
+
const responseEmail = (_b = (_a = asString(responseUser === null || responseUser === void 0 ? void 0 : responseUser.email)) === null || _a === void 0 ? void 0 : _a.toLowerCase()) !== null && _b !== void 0 ? _b : null;
|
|
302
|
+
if (!eventId
|
|
303
|
+
|| typeof payload.eventCreated !== 'boolean'
|
|
304
|
+
|| !responseUser
|
|
305
|
+
|| typeof responseUserId !== 'string'
|
|
306
|
+
|| responseUserId !== userId
|
|
307
|
+
|| responseEmail !== email) {
|
|
308
|
+
throw new Error('Invalid email capture response');
|
|
309
|
+
}
|
|
310
|
+
const persistedUserId = persistUserId(userId, FUNNEL_ID);
|
|
311
|
+
return {
|
|
312
|
+
user: toAppUser({
|
|
313
|
+
apiUser: Object.assign(Object.assign({}, responseUser), { user_id: userId, email }),
|
|
314
|
+
userId: persistedUserId,
|
|
315
|
+
fallbackEmail: email,
|
|
316
|
+
}),
|
|
317
|
+
eventId,
|
|
318
|
+
eventCreated: payload.eventCreated,
|
|
319
|
+
};
|
|
320
|
+
}
|
|
289
321
|
async syncUrlUserAttributes(input) {
|
|
290
322
|
var _a;
|
|
291
323
|
if (!hasUrlUserProfileAttributes(input.urlAttributes)) {
|
|
@@ -20,6 +20,22 @@ export type FunnelSdkBootstrapUserInput = {
|
|
|
20
20
|
attribution?: FunnelSdkJsonObject | null;
|
|
21
21
|
runtimeConfig?: FunnelSdkRuntimeConfigOverrides;
|
|
22
22
|
};
|
|
23
|
+
export type FunnelSdkCaptureEmailInput = {
|
|
24
|
+
userId: string;
|
|
25
|
+
email: string;
|
|
26
|
+
environment: FunnelSdkRuntimeMode;
|
|
27
|
+
runtimeConfig?: FunnelSdkRuntimeConfigOverrides;
|
|
28
|
+
};
|
|
29
|
+
export type FunnelSdkCaptureEmailResponse = {
|
|
30
|
+
user: {
|
|
31
|
+
user_id?: unknown;
|
|
32
|
+
fullName?: unknown;
|
|
33
|
+
email?: unknown;
|
|
34
|
+
document?: unknown;
|
|
35
|
+
};
|
|
36
|
+
eventId: string;
|
|
37
|
+
eventCreated: boolean;
|
|
38
|
+
};
|
|
23
39
|
export type FunnelSdkGetCurrentUserInput = {
|
|
24
40
|
userId: string;
|
|
25
41
|
runtimeConfig?: FunnelSdkRuntimeConfigOverrides;
|
|
@@ -211,6 +227,7 @@ declare class FunnelSdkService {
|
|
|
211
227
|
private getJson;
|
|
212
228
|
private postJson;
|
|
213
229
|
bootstrapUser<ResponsePayload = FunnelSdkJsonObject>(input?: FunnelSdkBootstrapUserInput): Promise<ResponsePayload>;
|
|
230
|
+
captureEmail(input: FunnelSdkCaptureEmailInput): Promise<FunnelSdkCaptureEmailResponse>;
|
|
214
231
|
getCurrentUser<ResponsePayload = FunnelSdkJsonObject>(input: FunnelSdkGetCurrentUserInput): Promise<ResponsePayload>;
|
|
215
232
|
getUserSubscriptionStatus<ResponsePayload = FunnelSdkJsonObject>(input?: FunnelSdkGetUserSubscriptionStatusInput): Promise<ResponsePayload>;
|
|
216
233
|
claimUserSubscription<ResponsePayload = FunnelSdkJsonObject>(input: FunnelSdkClaimUserSubscriptionInput): Promise<ResponsePayload>;
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { buildMainApiUrl, FUNNEL_ID, FUNNEL_SDK_PUBLISHABLE_KEY, FUNNEL_VERSION_ID, } from './runtime-api.config.js';
|
|
2
2
|
import { resolvePreviewPaymentPublishableKey } from './public-env.js';
|
|
3
|
+
import { CURRENT_STEP_CONTRACT_VERSION } from '../steps/step-contract.js';
|
|
3
4
|
const trimTrailingSlash = (value) => value.replace(/\/+$/, '');
|
|
4
5
|
const toNormalizedPath = (path) => (path.startsWith('/') ? path : `/${path}`);
|
|
5
6
|
const asTrimmedStringOrNull = (value) => {
|
|
@@ -113,6 +114,18 @@ class FunnelSdkService {
|
|
|
113
114
|
errorMessage: 'Failed to bootstrap user session',
|
|
114
115
|
});
|
|
115
116
|
}
|
|
117
|
+
async captureEmail(input) {
|
|
118
|
+
return this.postJson('/sdk/public/users/email-capture', {
|
|
119
|
+
runtimeConfig: input.runtimeConfig,
|
|
120
|
+
body: {
|
|
121
|
+
user_id: input.userId,
|
|
122
|
+
email: input.email,
|
|
123
|
+
environment: input.environment,
|
|
124
|
+
stepContractVersion: CURRENT_STEP_CONTRACT_VERSION,
|
|
125
|
+
},
|
|
126
|
+
errorMessage: 'Failed to capture email',
|
|
127
|
+
});
|
|
128
|
+
}
|
|
116
129
|
async getCurrentUser(input) {
|
|
117
130
|
const { funnelId } = resolveRuntimeConfig(input.runtimeConfig);
|
|
118
131
|
return this.getJson('/sdk/public/users/me', {
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import type { FunnelAnswerKey } from '../sdk/userAnswers.js';
|
|
2
|
+
import { type FunnelContractDiagnostic } from '../validation/funnel-contract-diagnostics.js';
|
|
3
|
+
import { FUNNEL_STEP_CONTRACTS, type FunnelChoiceDescriptor, type FunnelChoiceOption, type FunnelEmojiChoiceOption, type FunnelStepChoiceContract, type FunnelStepType } from './step-contract.js';
|
|
4
|
+
import type { FunnelStepMeta } from './types.js';
|
|
5
|
+
export type FunnelChoiceStepType = {
|
|
6
|
+
[Type in FunnelStepType]: (typeof FUNNEL_STEP_CONTRACTS)[Type] extends {
|
|
7
|
+
readonly choice: FunnelStepChoiceContract;
|
|
8
|
+
} ? Type : never;
|
|
9
|
+
}[FunnelStepType];
|
|
10
|
+
export declare const FUNNEL_CHOICE_STEP_TYPES: readonly FunnelChoiceStepType[];
|
|
11
|
+
export type FunnelChoiceValue = string | string[];
|
|
12
|
+
export type FunnelChoiceCompletion = {
|
|
13
|
+
answerKey: FunnelAnswerKey;
|
|
14
|
+
value: FunnelChoiceValue;
|
|
15
|
+
selected: Record<string, FunnelChoiceValue>;
|
|
16
|
+
};
|
|
17
|
+
type FunnelChoiceStepMeta = Pick<FunnelStepMeta, 'id' | 'type' | 'choice'>;
|
|
18
|
+
type FunnelChoiceOptions = readonly (FunnelChoiceOption | FunnelEmojiChoiceOption)[];
|
|
19
|
+
export declare const MAX_FUNNEL_CHOICE_ITEMS = 4096;
|
|
20
|
+
export declare const MAX_FUNNEL_CHOICE_IDENTIFIER_LENGTH = 256;
|
|
21
|
+
export declare const MAX_FUNNEL_CHOICE_CONTENT_STRING_LENGTH = 4096;
|
|
22
|
+
export type FunnelChoiceDescriptorParseResult = {
|
|
23
|
+
ok: true;
|
|
24
|
+
descriptor: FunnelChoiceDescriptor;
|
|
25
|
+
contract: FunnelStepChoiceContract;
|
|
26
|
+
stepType: FunnelChoiceStepType;
|
|
27
|
+
} | {
|
|
28
|
+
ok: false;
|
|
29
|
+
issue: 'not-choice-step-type' | 'invalid-descriptor-shape' | 'invalid-answer-key' | 'invalid-allow-empty';
|
|
30
|
+
};
|
|
31
|
+
export declare const parseFunnelChoiceDescriptor: (stepType: unknown, choice: unknown) => FunnelChoiceDescriptorParseResult;
|
|
32
|
+
export declare const buildChoiceCompletion: (stepMeta: FunnelChoiceStepMeta, options: FunnelChoiceOptions, ids: string | readonly string[]) => FunnelChoiceCompletion | FunnelContractDiagnostic;
|
|
33
|
+
export {};
|