@funnelsgrove/runtime 0.1.56 → 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.
Files changed (54) hide show
  1. package/dist/components/FunnelContext.d.ts +5 -1
  2. package/dist/components/FunnelContext.js +2 -1
  3. package/dist/components/ManageSubscriptionScreen.js +4 -1
  4. package/dist/components/SubscriptionHandoffScreen.d.ts +2 -1
  5. package/dist/components/SubscriptionHandoffScreen.js +11 -6
  6. package/dist/config/funnel.manifest.types.d.ts +4 -2
  7. package/dist/generated/step-contract-hash.d.ts +1 -0
  8. package/dist/generated/step-contract-hash.js +2 -0
  9. package/dist/index.d.ts +12 -0
  10. package/dist/index.js +10 -0
  11. package/dist/migrations/step-contract-v2.d.ts +50 -0
  12. package/dist/migrations/step-contract-v2.js +310 -0
  13. package/dist/migrations/step-contract-v3.d.ts +41 -0
  14. package/dist/migrations/step-contract-v3.js +26 -0
  15. package/dist/runtime/funnel-manifest.validation.d.ts +7 -2
  16. package/dist/runtime/funnel-manifest.validation.js +24 -68
  17. package/dist/runtime/funnel-step-lifecycle.d.ts +35 -0
  18. package/dist/runtime/funnel-step-lifecycle.js +49 -0
  19. package/dist/runtime/funnel-step-metadata.validation.d.ts +7 -0
  20. package/dist/runtime/funnel-step-metadata.validation.js +88 -0
  21. package/dist/runtime/submit-email-capture.d.ts +23 -0
  22. package/dist/runtime/submit-email-capture.js +54 -0
  23. package/dist/runtime/use-funnel-flow-controller.d.ts +58 -5
  24. package/dist/runtime/use-funnel-flow-controller.js +804 -122
  25. package/dist/runtime/use-step-choices.d.ts +58 -0
  26. package/dist/runtime/use-step-choices.js +187 -0
  27. package/dist/sdk/userAnswers.d.ts +1 -0
  28. package/dist/services/api.service.d.ts +10 -0
  29. package/dist/services/api.service.js +32 -0
  30. package/dist/services/funnel-sdk.service.d.ts +17 -0
  31. package/dist/services/funnel-sdk.service.js +13 -0
  32. package/dist/steps/choice-contract.d.ts +33 -0
  33. package/dist/steps/choice-contract.js +302 -0
  34. package/dist/steps/step-contract.d.ts +498 -0
  35. package/dist/steps/step-contract.js +506 -0
  36. package/dist/steps/types.d.ts +7 -5
  37. package/dist/steps/types.js +1 -18
  38. package/dist/testing/funnel-contract-journey-driver.d.ts +35 -0
  39. package/dist/testing/funnel-contract-journey-driver.js +266 -0
  40. package/dist/testing/index.d.ts +1 -0
  41. package/dist/testing/index.js +1 -0
  42. package/dist/validation/fixtures/invalid-reserved-identities.d.ts +249 -0
  43. package/dist/validation/fixtures/invalid-reserved-identities.js +20 -0
  44. package/dist/validation/fixtures/valid-v2-project.d.ts +419 -0
  45. package/dist/validation/fixtures/valid-v2-project.js +228 -0
  46. package/dist/validation/fixtures/valid-v3-project.d.ts +419 -0
  47. package/dist/validation/fixtures/valid-v3-project.js +30 -0
  48. package/dist/validation/funnel-contract-diagnostics.d.ts +49 -0
  49. package/dist/validation/funnel-contract-diagnostics.js +67 -0
  50. package/dist/validation/funnel-contract-validator.d.ts +30 -0
  51. package/dist/validation/funnel-contract-validator.js +1458 -0
  52. package/dist/validation/funnel-manifest-input.normalization.d.ts +67 -0
  53. package/dist/validation/funnel-manifest-input.normalization.js +386 -0
  54. package/package.json +9 -1
@@ -0,0 +1,506 @@
1
+ const deepFreezeContract = (value) => {
2
+ if (value === null || typeof value !== 'object' || Object.isFrozen(value)) {
3
+ return value;
4
+ }
5
+ for (const nestedValue of Object.values(value)) {
6
+ deepFreezeContract(nestedValue);
7
+ }
8
+ Object.freeze(value);
9
+ return value;
10
+ };
11
+ export const CURRENT_STEP_CONTRACT_VERSION = 3;
12
+ export const PREVIOUS_STEP_CONTRACT_VERSION = 2;
13
+ export const LEGACY_UNVERSIONED_STEP_CONTRACT_VERSION = 1;
14
+ export const SUPPORTED_STEP_CONTRACT_VERSIONS = Object.freeze([
15
+ LEGACY_UNVERSIONED_STEP_CONTRACT_VERSION,
16
+ PREVIOUS_STEP_CONTRACT_VERSION,
17
+ CURRENT_STEP_CONTRACT_VERSION,
18
+ ]);
19
+ const GLOBALLY_FORBIDDEN_CAPABILITIES = [
20
+ 'hardcoded-navigation',
21
+ 'provider-direct-analytics',
22
+ 'browser-registration-completed',
23
+ ];
24
+ const capabilitiesExcept = (...allowed) => {
25
+ const scopedCapabilities = [
26
+ 'choice-write',
27
+ 'email-capture',
28
+ 'checkout',
29
+ 'subscription-management',
30
+ 'subscription-handoff',
31
+ ];
32
+ return [
33
+ ...scopedCapabilities.filter((capability) => !allowed.includes(capability)),
34
+ ...GLOBALLY_FORBIDDEN_CAPABILITIES,
35
+ ];
36
+ };
37
+ export const FUNNEL_STEP_CONTRACTS = deepFreezeContract({
38
+ intro_hero: {
39
+ family: 'content',
40
+ description: 'Opening promise or low-friction entry screen.',
41
+ allowedKinds: [],
42
+ actionOwner: 'shared-shell',
43
+ allowedCustomEvents: [],
44
+ forbiddenCapabilities: capabilitiesExcept(),
45
+ terminal: false,
46
+ },
47
+ value_prop_story: {
48
+ family: 'content',
49
+ description: 'Benefit, explanation, motivation, or narrative value screen.',
50
+ allowedKinds: [],
51
+ actionOwner: 'shared-shell',
52
+ allowedCustomEvents: [],
53
+ forbiddenCapabilities: capabilitiesExcept(),
54
+ terminal: false,
55
+ },
56
+ single_step_choice: {
57
+ family: 'choice',
58
+ description: 'Exactly one standard option is selected.',
59
+ allowedKinds: [],
60
+ choice: {
61
+ cardinality: 'one',
62
+ presentation: 'standard',
63
+ completion: 'selection',
64
+ allowEmptyConfig: false,
65
+ },
66
+ actionOwner: 'step',
67
+ allowedCustomEvents: [],
68
+ forbiddenCapabilities: capabilitiesExcept('choice-write'),
69
+ terminal: false,
70
+ },
71
+ single_step_choice_emoji: {
72
+ family: 'choice',
73
+ description: 'Exactly one emoji-led option is selected.',
74
+ allowedKinds: [],
75
+ choice: {
76
+ cardinality: 'one',
77
+ presentation: 'emoji',
78
+ completion: 'selection',
79
+ allowEmptyConfig: false,
80
+ },
81
+ actionOwner: 'step',
82
+ allowedCustomEvents: [],
83
+ forbiddenCapabilities: capabilitiesExcept('choice-write'),
84
+ terminal: false,
85
+ },
86
+ multi_select_choice: {
87
+ family: 'choice',
88
+ description: 'Multiple options are selected before explicit completion.',
89
+ allowedKinds: [],
90
+ choice: {
91
+ cardinality: 'many',
92
+ presentation: 'standard',
93
+ completion: 'explicit',
94
+ allowEmptyConfig: true,
95
+ },
96
+ actionOwner: 'either',
97
+ allowedCustomEvents: [],
98
+ forbiddenCapabilities: capabilitiesExcept('choice-write'),
99
+ terminal: false,
100
+ },
101
+ form_input: {
102
+ family: 'form',
103
+ description: 'Non-choice structured input, including reserved email capture.',
104
+ allowedKinds: [],
105
+ actionOwner: 'either',
106
+ allowedCustomEvents: ['email_captured'],
107
+ forbiddenCapabilities: capabilitiesExcept('email-capture'),
108
+ terminal: false,
109
+ },
110
+ social_proof: {
111
+ family: 'content',
112
+ description: 'Testimonials, reviews, credibility, or evidence.',
113
+ allowedKinds: [],
114
+ actionOwner: 'shared-shell',
115
+ allowedCustomEvents: [],
116
+ forbiddenCapabilities: capabilitiesExcept(),
117
+ terminal: false,
118
+ },
119
+ progress_interstitial: {
120
+ family: 'transition',
121
+ description: 'Progress, calculation, loading, reveal, or transition screen.',
122
+ allowedKinds: [],
123
+ actionOwner: 'either',
124
+ allowedCustomEvents: [],
125
+ forbiddenCapabilities: capabilitiesExcept(),
126
+ terminal: false,
127
+ },
128
+ paywall_offer: {
129
+ family: 'commerce',
130
+ description: 'A monetization offer that presents purchasable plans.',
131
+ allowedKinds: ['paywall'],
132
+ requiredKind: 'paywall',
133
+ actionOwner: 'step',
134
+ allowedCustomEvents: ['checkout_started', 'add_payment_info', 'checkout_completed'],
135
+ forbiddenCapabilities: capabilitiesExcept('checkout'),
136
+ terminal: false,
137
+ analyticsRole: 'paywall',
138
+ },
139
+ upsell_offer: {
140
+ family: 'commerce',
141
+ description: 'A post-purchase or upgrade offer.',
142
+ allowedKinds: ['upsell'],
143
+ requiredKind: 'upsell',
144
+ actionOwner: 'step',
145
+ allowedCustomEvents: ['checkout_started', 'add_payment_info', 'checkout_completed'],
146
+ forbiddenCapabilities: capabilitiesExcept('checkout'),
147
+ terminal: false,
148
+ },
149
+ checkout: {
150
+ family: 'commerce',
151
+ description: 'A dedicated payment-entry screen.',
152
+ allowedKinds: [],
153
+ actionOwner: 'step',
154
+ allowedCustomEvents: ['checkout_started', 'add_payment_info', 'checkout_completed'],
155
+ forbiddenCapabilities: capabilitiesExcept('checkout'),
156
+ terminal: false,
157
+ },
158
+ subscription_management: {
159
+ family: 'commerce',
160
+ description: 'A screen for managing or inspecting an existing subscription.',
161
+ allowedKinds: ['manage-subscription'],
162
+ requiredKind: 'manage-subscription',
163
+ actionOwner: 'step',
164
+ allowedCustomEvents: [],
165
+ forbiddenCapabilities: capabilitiesExcept('subscription-management'),
166
+ terminal: false,
167
+ },
168
+ subscription_handoff: {
169
+ family: 'commerce',
170
+ description: 'A non-terminal app, store, or deep-link handoff.',
171
+ allowedKinds: ['subscription-handoff'],
172
+ requiredKind: 'subscription-handoff',
173
+ actionOwner: 'step',
174
+ allowedCustomEvents: [],
175
+ forbiddenCapabilities: capabilitiesExcept('subscription-handoff'),
176
+ terminal: false,
177
+ },
178
+ cancellation_offer: {
179
+ family: 'commerce',
180
+ description: 'A cancellation retention or downsell surface.',
181
+ allowedKinds: ['cancellation'],
182
+ requiredKind: 'cancellation',
183
+ actionOwner: 'step',
184
+ allowedCustomEvents: [],
185
+ forbiddenCapabilities: capabilitiesExcept('subscription-management'),
186
+ terminal: false,
187
+ },
188
+ summary_confirmation: {
189
+ family: 'content',
190
+ description: 'A summary, review, or confirmation before the next action.',
191
+ allowedKinds: [],
192
+ actionOwner: 'either',
193
+ allowedCustomEvents: [],
194
+ forbiddenCapabilities: capabilitiesExcept(),
195
+ terminal: false,
196
+ },
197
+ complete_registration: {
198
+ family: 'terminal',
199
+ description: 'The canonical post-purchase terminal that continues into the app.',
200
+ allowedKinds: ['subscription-handoff'],
201
+ requiredKind: 'subscription-handoff',
202
+ actionOwner: 'step',
203
+ allowedCustomEvents: ['funnel_completed'],
204
+ forbiddenCapabilities: capabilitiesExcept('subscription-handoff'),
205
+ terminal: true,
206
+ analyticsRole: 'complete-registration',
207
+ },
208
+ purchase_completed: {
209
+ family: 'terminal',
210
+ description: 'The canonical terminal shown after a verified initial purchase.',
211
+ allowedKinds: ['subscription-handoff'],
212
+ requiredKind: 'subscription-handoff',
213
+ actionOwner: 'step',
214
+ allowedCustomEvents: ['funnel_completed'],
215
+ forbiddenCapabilities: capabilitiesExcept('subscription-handoff'),
216
+ terminal: true,
217
+ analyticsRole: 'purchase-completed',
218
+ },
219
+ });
220
+ export const FUNNEL_STEP_TYPES = Object.freeze(Object.keys(FUNNEL_STEP_CONTRACTS));
221
+ export const FUNNEL_RESERVED_STEP_CONTRACTS = deepFreezeContract({
222
+ 'email-capture': {
223
+ match: 'exact',
224
+ id: 'email-capture',
225
+ name: 'email-capture',
226
+ type: 'form_input',
227
+ unique: true,
228
+ },
229
+ paywall: {
230
+ match: 'exact',
231
+ id: 'paywall',
232
+ name: 'paywall',
233
+ type: 'paywall_offer',
234
+ kind: 'paywall',
235
+ unique: true,
236
+ primary: true,
237
+ },
238
+ 'paywall-variant': {
239
+ match: 'pattern',
240
+ idPattern: '^paywall-[a-z0-9-]+$',
241
+ namePattern: '^paywall-[a-z0-9-]+$',
242
+ type: 'paywall_offer',
243
+ kind: 'paywall',
244
+ unique: false,
245
+ primary: false,
246
+ },
247
+ 'subscription-started': {
248
+ match: 'exact',
249
+ id: 'subscription-started',
250
+ name: 'subscription-started',
251
+ type: 'purchase_completed',
252
+ kind: 'subscription-handoff',
253
+ unique: true,
254
+ },
255
+ 'manage-subscription': {
256
+ match: 'exact',
257
+ id: 'manage-subscription',
258
+ name: 'manage-subscription',
259
+ type: 'subscription_management',
260
+ kind: 'manage-subscription',
261
+ unique: true,
262
+ },
263
+ });
264
+ const PREVIOUS_SUBSCRIPTION_TERMINAL_CONTRACT = deepFreezeContract(Object.assign(Object.assign({}, FUNNEL_RESERVED_STEP_CONTRACTS['subscription-started']), { type: 'complete_registration' }));
265
+ export const resolveStepTypeContract = (version, type) => {
266
+ if (!SUPPORTED_STEP_CONTRACT_VERSIONS.includes(version)) {
267
+ return undefined;
268
+ }
269
+ if ((version < CURRENT_STEP_CONTRACT_VERSION && type === 'purchase_completed')
270
+ || (version === CURRENT_STEP_CONTRACT_VERSION && type === 'complete_registration')) {
271
+ return undefined;
272
+ }
273
+ return Object.prototype.hasOwnProperty.call(FUNNEL_STEP_CONTRACTS, type)
274
+ ? FUNNEL_STEP_CONTRACTS[type]
275
+ : undefined;
276
+ };
277
+ export const resolveReservedStepContract = (version, identity) => {
278
+ if (!SUPPORTED_STEP_CONTRACT_VERSIONS.includes(version)) {
279
+ return undefined;
280
+ }
281
+ if (identity === 'subscription-started' && version < CURRENT_STEP_CONTRACT_VERSION) {
282
+ return PREVIOUS_SUBSCRIPTION_TERMINAL_CONTRACT;
283
+ }
284
+ return Object.prototype.hasOwnProperty.call(FUNNEL_RESERVED_STEP_CONTRACTS, identity)
285
+ ? FUNNEL_RESERVED_STEP_CONTRACTS[identity]
286
+ : undefined;
287
+ };
288
+ export const FUNNEL_STEP_CAPABILITY_RULES = deepFreezeContract({
289
+ 'choice-write': {
290
+ allowedStepTypes: [
291
+ 'single_step_choice',
292
+ 'single_step_choice_emoji',
293
+ 'multi_select_choice',
294
+ ],
295
+ allowedStepIdentities: 'any',
296
+ requiredOwner: 'useStepChoices',
297
+ },
298
+ 'email-capture': {
299
+ allowedStepTypes: ['form_input'],
300
+ allowedStepIdentities: ['email-capture'],
301
+ requiredOwner: 'submitEmailCapture',
302
+ },
303
+ checkout: {
304
+ allowedStepTypes: ['paywall_offer', 'upsell_offer', 'checkout'],
305
+ allowedStepIdentities: 'any',
306
+ requiredOwner: 'named-payment-helpers',
307
+ },
308
+ 'subscription-management': {
309
+ allowedStepTypes: ['subscription_management', 'cancellation_offer'],
310
+ allowedStepIdentities: 'any',
311
+ requiredOwner: 'runtime-subscription-helpers',
312
+ },
313
+ 'subscription-handoff': {
314
+ allowedStepTypes: ['subscription_handoff', 'complete_registration', 'purchase_completed'],
315
+ allowedStepIdentities: 'any',
316
+ requiredOwner: 'runtime-handoff-helper',
317
+ },
318
+ 'hardcoded-navigation': {
319
+ allowedStepTypes: [],
320
+ allowedStepIdentities: [],
321
+ requiredOwner: 'manifest-flow-controller',
322
+ },
323
+ 'provider-direct-analytics': {
324
+ allowedStepTypes: [],
325
+ allowedStepIdentities: [],
326
+ requiredOwner: 'analytics-adapter-outbox',
327
+ },
328
+ 'browser-registration-completed': {
329
+ allowedStepTypes: [],
330
+ allowedStepIdentities: [],
331
+ requiredOwner: 'trusted-subscription-claim-server',
332
+ },
333
+ });
334
+ const COMMON_FUNNEL_EVENT_METADATA = [
335
+ 'funnelId',
336
+ 'funnelVersionId',
337
+ 'environment',
338
+ 'stepContractVersion',
339
+ ];
340
+ const COMMON_STEP_EVENT_METADATA = [
341
+ ...COMMON_FUNNEL_EVENT_METADATA,
342
+ 'stepId',
343
+ 'stepName',
344
+ 'stepType',
345
+ ];
346
+ export const FUNNEL_ANALYTICS_EVENT_CONTRACTS = deepFreezeContract({
347
+ funnel_started: {
348
+ rawName: 'funnel_start',
349
+ publicName: 'funnel_started',
350
+ owner: 'controller',
351
+ automatic: true,
352
+ requiredMetadata: COMMON_FUNNEL_EVENT_METADATA,
353
+ allowedStepTypes: 'any',
354
+ allowedStepIdentities: 'any',
355
+ dedupeScope: 'funnel-run',
356
+ },
357
+ step_started: {
358
+ rawName: 'step_start',
359
+ publicName: 'step_started',
360
+ owner: 'controller',
361
+ automatic: true,
362
+ requiredMetadata: COMMON_STEP_EVENT_METADATA,
363
+ allowedStepTypes: 'any',
364
+ allowedStepIdentities: 'any',
365
+ dedupeScope: 'step-visit',
366
+ },
367
+ step_completed: {
368
+ rawName: 'step_end',
369
+ publicName: 'step_completed',
370
+ owner: 'controller',
371
+ automatic: true,
372
+ requiredMetadata: COMMON_STEP_EVENT_METADATA,
373
+ allowedStepTypes: 'any',
374
+ allowedStepIdentities: 'any',
375
+ dedupeScope: 'step-visit',
376
+ },
377
+ step_exited: {
378
+ rawName: 'step_exit',
379
+ publicName: 'step_exited',
380
+ owner: 'controller',
381
+ automatic: true,
382
+ requiredMetadata: COMMON_STEP_EVENT_METADATA,
383
+ allowedStepTypes: 'any',
384
+ allowedStepIdentities: 'any',
385
+ dedupeScope: 'step-visit',
386
+ },
387
+ first_step_viewed: {
388
+ rawName: 'first_step_viewed',
389
+ publicName: 'first_step_viewed',
390
+ owner: 'controller',
391
+ automatic: true,
392
+ requiredMetadata: COMMON_STEP_EVENT_METADATA,
393
+ allowedStepTypes: 'any',
394
+ allowedStepIdentities: 'any',
395
+ dedupeScope: 'step-visit',
396
+ },
397
+ first_step_clicked: {
398
+ rawName: 'first_step_clicked',
399
+ publicName: 'first_step_clicked',
400
+ owner: 'controller',
401
+ automatic: true,
402
+ requiredMetadata: COMMON_STEP_EVENT_METADATA,
403
+ allowedStepTypes: 'any',
404
+ allowedStepIdentities: 'any',
405
+ dedupeScope: 'step-visit',
406
+ },
407
+ step_engaged: {
408
+ rawName: 'step_engaged',
409
+ publicName: 'step_engaged',
410
+ owner: 'controller',
411
+ automatic: false,
412
+ requiredMetadata: [...COMMON_STEP_EVENT_METADATA, 'durationMs'],
413
+ allowedStepTypes: 'any',
414
+ allowedStepIdentities: 'any',
415
+ dedupeScope: 'step-visit',
416
+ },
417
+ email_captured: {
418
+ rawName: 'email_captured',
419
+ publicName: 'email_captured',
420
+ owner: 'transactional-server',
421
+ automatic: false,
422
+ requiredMetadata: [
423
+ ...COMMON_STEP_EVENT_METADATA,
424
+ 'projectId',
425
+ 'funnelEndUserId',
426
+ 'eventId',
427
+ ],
428
+ allowedStepTypes: ['form_input'],
429
+ allowedStepIdentities: ['email-capture'],
430
+ dedupeScope: 'logical-email-conversion',
431
+ },
432
+ checkout_started: {
433
+ rawName: 'checkout_started',
434
+ publicName: 'checkout_started',
435
+ owner: 'named-helper',
436
+ automatic: false,
437
+ requiredMetadata: [...COMMON_STEP_EVENT_METADATA, 'eventId'],
438
+ allowedStepTypes: ['paywall_offer', 'upsell_offer', 'checkout'],
439
+ allowedStepIdentities: 'any',
440
+ dedupeScope: 'event-id',
441
+ },
442
+ add_payment_info: {
443
+ rawName: 'add_payment_info',
444
+ publicName: 'add_payment_info',
445
+ owner: 'named-helper',
446
+ automatic: false,
447
+ requiredMetadata: [...COMMON_STEP_EVENT_METADATA, 'eventId'],
448
+ allowedStepTypes: ['paywall_offer', 'upsell_offer', 'checkout'],
449
+ allowedStepIdentities: 'any',
450
+ dedupeScope: 'event-id',
451
+ },
452
+ checkout_completed: {
453
+ rawName: 'checkout_completed',
454
+ publicName: 'checkout_completed',
455
+ owner: 'named-helper',
456
+ automatic: false,
457
+ requiredMetadata: [...COMMON_STEP_EVENT_METADATA, 'eventId'],
458
+ allowedStepTypes: ['paywall_offer', 'upsell_offer', 'checkout'],
459
+ allowedStepIdentities: 'any',
460
+ dedupeScope: 'event-id',
461
+ },
462
+ funnel_completed: {
463
+ rawName: 'funnel_completed',
464
+ publicName: 'funnel_completed',
465
+ owner: 'terminal-helper',
466
+ automatic: false,
467
+ requiredMetadata: [...COMMON_STEP_EVENT_METADATA, 'eventId'],
468
+ allowedStepTypes: ['complete_registration', 'purchase_completed'],
469
+ allowedStepIdentities: 'any',
470
+ dedupeScope: 'funnel-run',
471
+ },
472
+ registration_completed: {
473
+ rawName: 'registration_completed',
474
+ publicName: 'registration_completed',
475
+ owner: 'trusted-server',
476
+ automatic: false,
477
+ requiredMetadata: [
478
+ ...COMMON_FUNNEL_EVENT_METADATA,
479
+ 'projectId',
480
+ 'funnelEndUserId',
481
+ 'eventId',
482
+ 'subscriptionClaimId',
483
+ ],
484
+ allowedStepTypes: [],
485
+ allowedStepIdentities: [],
486
+ dedupeScope: 'subscription-claim',
487
+ },
488
+ purchase_completed: {
489
+ rawName: 'purchase_completed',
490
+ publicName: 'purchase_completed',
491
+ owner: 'trusted-server',
492
+ automatic: false,
493
+ requiredMetadata: [
494
+ ...COMMON_FUNNEL_EVENT_METADATA,
495
+ 'projectId',
496
+ 'funnelEndUserId',
497
+ 'eventId',
498
+ 'providerPaymentId',
499
+ 'purchaseKind',
500
+ ],
501
+ allowedStepTypes: [],
502
+ allowedStepIdentities: [],
503
+ dedupeScope: 'provider-payment',
504
+ },
505
+ });
506
+ export const DEFERRABLE_FUNNEL_CONTRACT_DIAGNOSTIC_CODES = deepFreezeContract(['FG-PARITY-001', 'FG-REGISTRY-001', 'FG-CONTENT-001', 'FG-FLOW-001']);
@@ -1,20 +1,22 @@
1
- export declare const FUNNEL_STEP_TYPES: readonly ["intro_hero", "value_prop_story", "single_step_choice", "single_step_choice_emoji", "multi_select_choice", "form_input", "social_proof", "progress_interstitial", "paywall_offer", "upsell_offer", "checkout", "subscription_management", "subscription_handoff", "cancellation_offer", "summary_confirmation", "complete_registration"];
2
- export type FunnelStepType = (typeof FUNNEL_STEP_TYPES)[number];
1
+ import type { FunnelChoiceDescriptor, FunnelStepKind, FunnelStepType } from './step-contract.js';
2
+ export { FUNNEL_STEP_TYPES } from './step-contract.js';
3
+ export type { FunnelChoiceDescriptor, FunnelChoiceOption, FunnelEmojiChoiceOption, FunnelStepKind, FunnelStepType, } from './step-contract.js';
3
4
  export type FunnelStepActionBar = {
4
5
  hidden?: boolean;
5
6
  buttonText?: string;
6
7
  buttonVariant?: 'muted';
7
8
  autoAdvanceMs?: number;
8
9
  };
9
- export type FunnelStepKind = 'default' | 'paywall' | 'upsell' | 'manage-subscription' | 'subscription-handoff' | 'cancellation';
10
10
  export type FunnelStepMeta = {
11
11
  id: string;
12
- /** Optional short name to make step intent obvious for non-engineers. */
13
- name?: string;
12
+ /** Stable kebab-case analytics slug. */
13
+ name: string;
14
14
  /** Canonical structural taxonomy used across planning, runtime, and RAG examples. */
15
15
  type: FunnelStepType;
16
16
  /** Optional semantic class used by tooling (e.g. paywall detection). */
17
17
  kind?: FunnelStepKind;
18
+ /** Stable answer field configuration for choice steps. */
19
+ choice?: FunnelChoiceDescriptor;
18
20
  figmaNodeId: string;
19
21
  title: string;
20
22
  description: string;
@@ -1,18 +1 @@
1
- export const FUNNEL_STEP_TYPES = [
2
- 'intro_hero',
3
- 'value_prop_story',
4
- 'single_step_choice',
5
- 'single_step_choice_emoji',
6
- 'multi_select_choice',
7
- 'form_input',
8
- 'social_proof',
9
- 'progress_interstitial',
10
- 'paywall_offer',
11
- 'upsell_offer',
12
- 'checkout',
13
- 'subscription_management',
14
- 'subscription_handoff',
15
- 'cancellation_offer',
16
- 'summary_confirmation',
17
- 'complete_registration',
18
- ];
1
+ export { FUNNEL_STEP_TYPES } from './step-contract.js';
@@ -0,0 +1,35 @@
1
+ import type { FunnelFlowApiAdapter } from '../runtime/use-funnel-flow-controller.js';
2
+ export type FunnelContractJourneyEnvelope = {
3
+ eventType: string;
4
+ eventId: string;
5
+ userId?: string;
6
+ environment?: 'test' | 'live';
7
+ occurredAt?: string;
8
+ stepId?: string;
9
+ stepName?: string;
10
+ stepType?: string;
11
+ startedAt?: string;
12
+ endedAt?: string;
13
+ stepContractVersion?: number;
14
+ selected?: Record<string, unknown>;
15
+ metadata?: Record<string, unknown>;
16
+ featureFlags?: Record<string, string>;
17
+ };
18
+ export type FunnelContractJourneyResult = {
19
+ envelopes: FunnelContractJourneyEnvelope[];
20
+ emailCaptureCalls: Array<{
21
+ userId: string;
22
+ email: string;
23
+ environment: 'test' | 'live';
24
+ }>;
25
+ browserEmailEvents: Array<{
26
+ eventId: string;
27
+ stepId: string;
28
+ stepName: string;
29
+ stepType: string;
30
+ }>;
31
+ visitedStepIds: string[];
32
+ };
33
+ export declare const runFunnelContractJourney: (options: {
34
+ captureEmail: FunnelFlowApiAdapter["captureEmail"];
35
+ }) => Promise<FunnelContractJourneyResult>;