@funnelsgrove/runtime 0.11.2 → 0.11.4

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 (129) hide show
  1. package/README.md +51 -0
  2. package/dist/components/ManageSubscriptionScreen.js +7 -2
  3. package/dist/index.d.ts +1 -0
  4. package/dist/index.js +1 -0
  5. package/dist/services/api.service.d.ts +28 -104
  6. package/dist/services/api.service.js +24 -519
  7. package/dist/services/checkout-access.service.d.ts +2 -0
  8. package/dist/services/checkout-access.service.js +19 -0
  9. package/dist/services/checkout-identity.service.d.ts +4 -0
  10. package/dist/services/checkout-identity.service.js +28 -0
  11. package/dist/services/funnel-sdk/checkout/chargeOneClickPayment.d.ts +2 -0
  12. package/dist/services/funnel-sdk/checkout/chargeOneClickPayment.js +17 -0
  13. package/dist/services/funnel-sdk/checkout/createHostedCheckoutSession.d.ts +2 -0
  14. package/dist/services/funnel-sdk/checkout/createHostedCheckoutSession.js +9 -0
  15. package/dist/services/funnel-sdk/checkout/createOneTimeCheckout.d.ts +2 -0
  16. package/dist/services/funnel-sdk/checkout/createOneTimeCheckout.js +21 -0
  17. package/dist/services/funnel-sdk/checkout/createOneTimePaymentIntent.d.ts +3 -0
  18. package/dist/services/funnel-sdk/checkout/createOneTimePaymentIntent.js +10 -0
  19. package/dist/services/funnel-sdk/checkout/createSubscriptionCheckout.d.ts +2 -0
  20. package/dist/services/funnel-sdk/checkout/createSubscriptionCheckout.js +22 -0
  21. package/dist/services/funnel-sdk/checkout/requests.d.ts +127 -0
  22. package/dist/services/funnel-sdk/checkout/requests.js +112 -0
  23. package/dist/services/{funnel-sdk-payments.functions.d.ts → funnel-sdk/checkout/selection.d.ts} +2 -0
  24. package/dist/services/{funnel-sdk-payments.functions.js → funnel-sdk/checkout/selection.js} +1 -1
  25. package/dist/services/funnel-sdk/checkout/types.d.ts +187 -0
  26. package/dist/services/funnel-sdk/checkout/types.js +1 -0
  27. package/dist/services/funnel-sdk/checkout/updateSubscriptionCheckoutPlan.d.ts +2 -0
  28. package/dist/services/funnel-sdk/checkout/updateSubscriptionCheckoutPlan.js +18 -0
  29. package/dist/services/funnel-sdk/checkout/verifySubscriptionCheckout.d.ts +2 -0
  30. package/dist/services/funnel-sdk/checkout/verifySubscriptionCheckout.js +25 -0
  31. package/dist/services/funnel-sdk/shared/http.d.ts +21 -0
  32. package/dist/services/funnel-sdk/shared/http.js +65 -0
  33. package/dist/services/funnel-sdk/shared/runtimeConfig.d.ts +17 -0
  34. package/dist/services/funnel-sdk/shared/runtimeConfig.js +42 -0
  35. package/dist/services/funnel-sdk/shared/types.d.ts +9 -0
  36. package/dist/services/funnel-sdk/shared/types.js +1 -0
  37. package/dist/services/funnel-sdk/subscriptions/listSubscriptions.d.ts +3 -0
  38. package/dist/services/funnel-sdk/subscriptions/listSubscriptions.js +17 -0
  39. package/dist/services/funnel-sdk/subscriptions/types.d.ts +24 -0
  40. package/dist/services/funnel-sdk/subscriptions/types.js +1 -0
  41. package/dist/services/funnel-sdk/subscriptions/updateSubscription.d.ts +2 -0
  42. package/dist/services/funnel-sdk/subscriptions/updateSubscription.js +33 -0
  43. package/dist/services/funnel-sdk/subscriptions/validator.d.ts +2 -0
  44. package/dist/services/funnel-sdk/subscriptions/validator.js +15 -0
  45. package/dist/services/funnel-sdk/uploads/preparePhotoUpload.d.ts +4 -0
  46. package/dist/services/funnel-sdk/uploads/preparePhotoUpload.js +19 -0
  47. package/dist/services/funnel-sdk/uploads/types.d.ts +32 -0
  48. package/dist/services/funnel-sdk/uploads/types.js +1 -0
  49. package/dist/services/funnel-sdk/uploads/uploadPhoto.d.ts +7 -0
  50. package/dist/services/funnel-sdk/uploads/uploadPhoto.js +18 -0
  51. package/dist/services/funnel-sdk/uploads/uploadPhotoFile.d.ts +2 -0
  52. package/dist/services/funnel-sdk/uploads/uploadPhotoFile.js +26 -0
  53. package/dist/services/funnel-sdk/users/bootstrapUser.d.ts +3 -0
  54. package/dist/services/funnel-sdk/users/bootstrapUser.js +15 -0
  55. package/dist/services/funnel-sdk/users/captureEmail.d.ts +2 -0
  56. package/dist/services/funnel-sdk/users/captureEmail.js +15 -0
  57. package/dist/services/funnel-sdk/users/claimUserSubscription.d.ts +3 -0
  58. package/dist/services/funnel-sdk/users/claimUserSubscription.js +7 -0
  59. package/dist/services/funnel-sdk/users/getCurrentUser.d.ts +3 -0
  60. package/dist/services/funnel-sdk/users/getCurrentUser.js +13 -0
  61. package/dist/services/funnel-sdk/users/getUserSubscriptionStatus.d.ts +3 -0
  62. package/dist/services/funnel-sdk/users/getUserSubscriptionStatus.js +14 -0
  63. package/dist/services/funnel-sdk/users/types.d.ts +57 -0
  64. package/dist/services/funnel-sdk/users/types.js +1 -0
  65. package/dist/services/funnel-sdk/users/updateFunnelUser.d.ts +3 -0
  66. package/dist/services/funnel-sdk/users/updateFunnelUser.js +18 -0
  67. package/dist/services/funnel-sdk.service.d.ts +26 -319
  68. package/dist/services/funnel-sdk.service.js +38 -456
  69. package/dist/services/session/bootstrap/bootstrapSession.d.ts +8 -0
  70. package/dist/services/session/bootstrap/bootstrapSession.js +25 -0
  71. package/dist/services/session/bootstrap/currentUser.d.ts +1 -0
  72. package/dist/services/session/bootstrap/currentUser.js +37 -0
  73. package/dist/services/session/bootstrap/idempotency.d.ts +9 -0
  74. package/dist/services/session/bootstrap/idempotency.js +97 -0
  75. package/dist/services/session/bootstrap/requests.d.ts +23 -0
  76. package/dist/services/session/bootstrap/requests.js +25 -0
  77. package/dist/services/session/bootstrap/responses.d.ts +3 -0
  78. package/dist/services/session/bootstrap/responses.js +10 -0
  79. package/dist/services/session/bootstrap/types.d.ts +12 -0
  80. package/dist/services/session/bootstrap/types.js +1 -0
  81. package/dist/services/session/emailCapture/captureEmail.d.ts +8 -0
  82. package/dist/services/session/emailCapture/captureEmail.js +13 -0
  83. package/dist/services/session/emailCapture/requests.d.ts +12 -0
  84. package/dist/services/session/emailCapture/requests.js +6 -0
  85. package/dist/services/session/emailCapture/responses.d.ts +3 -0
  86. package/dist/services/session/emailCapture/responses.js +14 -0
  87. package/dist/services/session/emailCapture/types.d.ts +8 -0
  88. package/dist/services/session/emailCapture/types.js +1 -0
  89. package/dist/services/session/emailCapture/validator.d.ts +16 -0
  90. package/dist/services/session/emailCapture/validator.js +37 -0
  91. package/dist/services/session/identity.d.ts +6 -0
  92. package/dist/services/session/identity.js +48 -0
  93. package/dist/services/session/ping/pingUserContext.d.ts +2 -0
  94. package/dist/services/session/ping/pingUserContext.js +10 -0
  95. package/dist/services/session/ping/transport.d.ts +2 -0
  96. package/dist/services/session/ping/transport.js +41 -0
  97. package/dist/services/session/shared/values.d.ts +6 -0
  98. package/dist/services/session/shared/values.js +25 -0
  99. package/dist/services/session/subscriptions/getManageSubscriptions.d.ts +3 -0
  100. package/dist/services/session/subscriptions/getManageSubscriptions.js +8 -0
  101. package/dist/services/session/subscriptions/identity.d.ts +3 -0
  102. package/dist/services/session/subscriptions/identity.js +8 -0
  103. package/dist/services/session/subscriptions/types.d.ts +35 -0
  104. package/dist/services/session/subscriptions/types.js +1 -0
  105. package/dist/services/session/subscriptions/updateSubscription.d.ts +10 -0
  106. package/dist/services/session/subscriptions/updateSubscription.js +7 -0
  107. package/dist/services/session/subscriptions/validator.d.ts +6 -0
  108. package/dist/services/session/subscriptions/validator.js +13 -0
  109. package/dist/services/session/uploads/localPhoto.d.ts +2 -0
  110. package/dist/services/session/uploads/localPhoto.js +10 -0
  111. package/dist/services/session/uploads/requests.d.ts +10 -0
  112. package/dist/services/session/uploads/requests.js +7 -0
  113. package/dist/services/session/uploads/types.d.ts +7 -0
  114. package/dist/services/session/uploads/types.js +1 -0
  115. package/dist/services/session/uploads/uploadTempPhoto.d.ts +6 -0
  116. package/dist/services/session/uploads/uploadTempPhoto.js +13 -0
  117. package/dist/services/session/uploads/validator.d.ts +3 -0
  118. package/dist/services/session/uploads/validator.js +12 -0
  119. package/dist/services/session/users/mappers.d.ts +13 -0
  120. package/dist/services/session/users/mappers.js +33 -0
  121. package/dist/services/session/users/requests.d.ts +15 -0
  122. package/dist/services/session/users/requests.js +8 -0
  123. package/dist/services/session/users/syncUrlUserAttributes.d.ts +10 -0
  124. package/dist/services/session/users/syncUrlUserAttributes.js +21 -0
  125. package/dist/services/session/users/types.d.ts +20 -0
  126. package/dist/services/session/users/types.js +1 -0
  127. package/dist/services/session/users/updateUser.d.ts +6 -0
  128. package/dist/services/session/users/updateUser.js +18 -0
  129. package/package.json +1 -1
package/README.md CHANGED
@@ -43,6 +43,57 @@ documented in [`docs/product-specs/funnel-template-runtime.md`](../../docs/produ
43
43
  - `components/*`: generic runtime context, subscription handoff/management, editor panel, dev info, and base controls.
44
44
  - `content/*` and `steps/types`: shared step content and taxonomy contracts.
45
45
 
46
+ ## Browser SDK structure
47
+
48
+ `funnelSdkService` remains the public entry point, with the same eighteen methods
49
+ and exported input/response types. It is a plain object of async functions; methods
50
+ can also be destructured without losing a `this` binding.
51
+
52
+ Implementation lives in `services/funnel-sdk/`, grouped by `users`, `uploads`,
53
+ `checkout`, and `subscriptions`. Each use case owns its endpoint and sequencing.
54
+ Keep request mappers beside their use cases (for example,
55
+ `checkout/requests.ts`), not in a generic mapping layer. Only runtime identity,
56
+ headers, JSON transport, and API error handling belong in `shared/`.
57
+
58
+ SDK HTTP requests serve the funnel. Server-to-server integration clients and direct
59
+ payment-provider calls do not belong here. Do not create workflow classes or files
60
+ for these short request sequences. Photo uploads prepare an upload through the API,
61
+ then send the original blob directly to its signed destination, without SDK headers.
62
+
63
+ The V1/V2 switch, legacy method signatures, field casing, empty-field filtering,
64
+ errors, and retry behavior are compatibility contracts. Mutations are not retried
65
+ automatically and V2 failures do not fall back to V1. Before changing these contracts,
66
+ add focused regressions; `funnel-sdk.contract.test.ts` captures the pre-refactor
67
+ requests, and `funnel-sdk.http.test.ts` exercises real loopback HTTP and signed uploads.
68
+
69
+ ## Session API structure
70
+
71
+ `apiService` keeps its thirteen public methods and four public type exports. It is
72
+ also a plain object of functions, implemented in `services/session/`:
73
+
74
+ - `bootstrap/`: verify candidate identity, reuse a request-scoped idempotency key,
75
+ bootstrap once, clear only the completed record, and map the resulting user.
76
+ - `users/` and `emailCapture/`: profile updates and email capture, with named
77
+ request/response mappers and email-response validation beside the functions.
78
+ - `uploads/`: delegate the original file to the SDK signed upload, then validate
79
+ its result; local object URLs are only the no-key fallback.
80
+ - `subscriptions/`: resolve identity from the management URL, validate it, and
81
+ call the SDK. Never substitute the stored session identity for a missing link.
82
+ - `identity.ts` and `bootstrap/idempotency.ts`: own session storage mechanics.
83
+ - `ping/transport.ts`: owns the same-origin, best-effort ping and its error handling.
84
+
85
+ These are use-case folders, not another workflow or data layer. Keep request and
86
+ response mappers pure: pass runtime mode/revision explicitly instead of reading
87
+ browser configuration inside a mapping function. SDK requests still go through
88
+ `funnelSdkService`; only the same-origin ping owns a separate HTTP boundary.
89
+ Validate email/canonical identity before persistence. Preserve failed bootstrap
90
+ keys for explicit retry and never add an automatic retry to an uncertain write.
91
+
92
+ The pre-extraction session tests remain unchanged behavior baselines. Session
93
+ boundary tests protect the facade and infrastructure ownership; real loopback
94
+ tests exercise bootstrap/profile/email, URL-only subscription management, explicit
95
+ bootstrap retry, and binary uploads through both API facades.
96
+
46
97
  ## Storage Conventions
47
98
 
48
99
  - runtime user ids stay funnel-scoped under `funnel:<funnelId>:user-id`
@@ -5,6 +5,7 @@ import { apiService } from '../services/api.service.js';
5
5
  import { buildPreviewManageSubscriptionsFallback, isPreviewFrameRuntime, } from '../services/preview-frame.service.js';
6
6
  import { runtimePublicConfig } from '../services/public-env.js';
7
7
  import { useFunnel } from './FunnelContext.js';
8
+ import { useFunnelRuntimeConfig } from './FunnelRuntimeConfigBoundary.js';
8
9
  const supportEmail = runtimePublicConfig.supportEmail;
9
10
  const isCancellableSubscription = (subscription) => {
10
11
  const normalizedStatus = subscription.status.trim().toLowerCase();
@@ -34,6 +35,8 @@ const getRowActionLabel = (subscription, isSelected, content) => {
34
35
  export function ManageSubscriptionScreen({ stepId, content, homeStepId, nodeId, }) {
35
36
  var _a, _b, _c, _d;
36
37
  const { goToStep, setAnswer } = useFunnel();
38
+ const { config: publishedRuntimeConfig } = useFunnelRuntimeConfig();
39
+ const paymentsApiVersion = publishedRuntimeConfig === null || publishedRuntimeConfig === void 0 ? void 0 : publishedRuntimeConfig.paymentsApiVersion;
37
40
  const supportLinkLabel = supportEmail;
38
41
  const supportLinkHref = `mailto:${supportEmail}`;
39
42
  const [stage, setStage] = useState('subscriptions');
@@ -60,7 +63,7 @@ export function ManageSubscriptionScreen({ stepId, content, homeStepId, nodeId,
60
63
  return;
61
64
  }
62
65
  try {
63
- const payload = await apiService.getManageSubscriptions();
66
+ const payload = await apiService.getManageSubscriptions({ paymentsApiVersion });
64
67
  if (!active) {
65
68
  return;
66
69
  }
@@ -93,7 +96,7 @@ export function ManageSubscriptionScreen({ stepId, content, homeStepId, nodeId,
93
96
  return () => {
94
97
  active = false;
95
98
  };
96
- }, [content.errorMessages.load]);
99
+ }, [content.errorMessages.load, paymentsApiVersion]);
97
100
  const subscriptions = useMemo(() => { var _a; return (_a = data === null || data === void 0 ? void 0 : data.subscriptions) !== null && _a !== void 0 ? _a : []; }, [data]);
98
101
  const cancellableSubscriptions = useMemo(() => {
99
102
  return subscriptions.filter(isCancellableSubscription);
@@ -124,7 +127,9 @@ export function ManageSubscriptionScreen({ stepId, content, homeStepId, nodeId,
124
127
  try {
125
128
  const payload = await apiService.updateSubscription({
126
129
  subscriptionId: selectedSubscription.id,
130
+ subscriptionRef: selectedSubscription.subscriptionRef,
127
131
  action: 'cancel',
132
+ runtimeConfig: { paymentsApiVersion },
128
133
  });
129
134
  setData(payload);
130
135
  setAnswer('manageSubscriptionCancelled', true);
package/dist/index.d.ts CHANGED
@@ -32,6 +32,7 @@ export * from './runtime/step-asset-preloader.js';
32
32
  export * from './runtime/url-user-attributes.js';
33
33
  export * from './runtime/use-url-user-attributes-sync.js';
34
34
  export * from './services/api.service.js';
35
+ export * from './services/checkout-identity.service.js';
35
36
  export * from './services/funnel-sdk.service.js';
36
37
  export * from './services/funnel-state.service.js';
37
38
  export * from './services/logger.js';
package/dist/index.js CHANGED
@@ -34,6 +34,7 @@ export * from './runtime/url-user-attributes.js';
34
34
  export * from './runtime/use-url-user-attributes-sync.js';
35
35
  // Browser-safe services.
36
36
  export * from './services/api.service.js';
37
+ export * from './services/checkout-identity.service.js';
37
38
  export * from './services/funnel-sdk.service.js';
38
39
  export * from './services/funnel-state.service.js';
39
40
  export * from './services/logger.js';
@@ -1,105 +1,29 @@
1
- import type { FunnelUserAnswers } from '../sdk/userAnswers.js';
2
- import { type FunnelUserAttribution } from '../runtime/funnel-attribution.js';
3
- import { type UrlUserAttributes } from '../runtime/url-user-attributes.js';
4
- export type AppUser = {
5
- id: string;
6
- name: string;
7
- email: string;
8
- attributes: FunnelUserAnswers;
9
- document: Record<string, unknown>;
1
+ import { getOrCreateClientUserId, persistCanonicalUserId, getBootstrapCandidateUserId } from './session/identity.js';
2
+ import { getSubscriptionManagementUserId, getSubscriptionManagementStripeCustomerId } from './session/subscriptions/identity.js';
3
+ import { bootstrapSession } from './session/bootstrap/bootstrapSession.js';
4
+ import { updateUser } from './session/users/updateUser.js';
5
+ import { syncUrlUserAttributes } from './session/users/syncUrlUserAttributes.js';
6
+ import { captureEmail } from './session/emailCapture/captureEmail.js';
7
+ import { uploadTempPhoto } from './session/uploads/uploadTempPhoto.js';
8
+ import { pingUserContext } from './session/ping/pingUserContext.js';
9
+ import { getManageSubscriptions } from './session/subscriptions/getManageSubscriptions.js';
10
+ import { updateSubscription } from './session/subscriptions/updateSubscription.js';
11
+ export type { AppUser } from './session/users/types.js';
12
+ export type { EmailCaptureResult } from './session/emailCapture/types.js';
13
+ export type { TempPhotoUploadResult } from './session/uploads/types.js';
14
+ export type { ManageSubscriptionsResponse } from './session/subscriptions/types.js';
15
+ export declare const apiService: {
16
+ getOrCreateClientUserId: typeof getOrCreateClientUserId;
17
+ persistCanonicalUserId: typeof persistCanonicalUserId;
18
+ getSubscriptionManagementUserId: typeof getSubscriptionManagementUserId;
19
+ getSubscriptionManagementStripeCustomerId: typeof getSubscriptionManagementStripeCustomerId;
20
+ getBootstrapCandidateUserId: typeof getBootstrapCandidateUserId;
21
+ bootstrapSession: typeof bootstrapSession;
22
+ updateUser: typeof updateUser;
23
+ captureEmail: typeof captureEmail;
24
+ syncUrlUserAttributes: typeof syncUrlUserAttributes;
25
+ pingUserContext: typeof pingUserContext;
26
+ uploadTempPhoto: typeof uploadTempPhoto;
27
+ getManageSubscriptions: typeof getManageSubscriptions;
28
+ updateSubscription: typeof updateSubscription;
10
29
  };
11
- type AppUserInput = Omit<AppUser, 'document'> & {
12
- document?: Record<string, unknown>;
13
- };
14
- export type ManageSubscriptionsResponse = {
15
- user: {
16
- id: string;
17
- user_id: string;
18
- email: string | null;
19
- fullName: string | null;
20
- subscriptionStatus: string | null;
21
- } | null;
22
- subscriptions: Array<{
23
- id: string;
24
- status: string;
25
- providerSubscriptionId: string;
26
- providerPlanId: string | null;
27
- cancelAtPeriodEnd: boolean;
28
- currentPeriodStart: string | null;
29
- currentPeriodEnd: string | null;
30
- createdAt: string | null;
31
- amountCents?: number | null;
32
- currency?: string | null;
33
- billingInterval?: string | null;
34
- billingIntervalCount?: number | null;
35
- discountCouponIds?: string[];
36
- retentionDiscount?: {
37
- couponId: string;
38
- discountPercent: number;
39
- amountCents: number;
40
- discountedAmountCents: number;
41
- originalAmountCents: number;
42
- };
43
- environment: 'test' | 'live';
44
- }>;
45
- eventsCount: number;
46
- };
47
- export type TempPhotoUploadResult = {
48
- key: string;
49
- publicUrl: string;
50
- contentType: string;
51
- sizeBytes: number;
52
- uploadedAt: string;
53
- };
54
- export type EmailCaptureResult = {
55
- user: AppUser;
56
- eventId: string;
57
- eventCreated: boolean;
58
- canonicalUser: AppUser | null;
59
- activeSubscription: boolean;
60
- };
61
- declare class ApiService {
62
- getOrCreateClientUserId(): string;
63
- persistCanonicalUserId(userId: string): string;
64
- getSubscriptionManagementUserId(): string | null;
65
- getSubscriptionManagementStripeCustomerId(): string | null;
66
- getBootstrapCandidateUserId(fallbackUserId?: string | null): string | null;
67
- private fetchCurrentUserState;
68
- private resolveBootstrapUserId;
69
- bootstrapSession(input?: {
70
- userId?: string;
71
- email?: string;
72
- name?: string;
73
- attribution?: FunnelUserAttribution;
74
- }): Promise<AppUser>;
75
- updateUser(user: AppUser, options?: {
76
- attribution?: FunnelUserAttribution;
77
- persistClientIdentity?: boolean;
78
- }): Promise<AppUser>;
79
- captureEmail(input: {
80
- userId: string;
81
- email: string;
82
- environment: 'test' | 'live';
83
- answers?: FunnelUserAnswers;
84
- }): Promise<EmailCaptureResult>;
85
- syncUrlUserAttributes(input: {
86
- user: AppUserInput;
87
- attributes?: FunnelUserAnswers;
88
- urlAttributes?: UrlUserAttributes | null;
89
- attribution?: FunnelUserAttribution;
90
- }): Promise<AppUser | null>;
91
- pingUserContext(userId: string): Promise<AppUser | null>;
92
- uploadTempPhoto(input: {
93
- file: File;
94
- userId?: string;
95
- source?: 'camera' | 'upload';
96
- }): Promise<TempPhotoUploadResult>;
97
- getManageSubscriptions(): Promise<ManageSubscriptionsResponse>;
98
- updateSubscription(input: {
99
- subscriptionId: string;
100
- action: 'cancel' | 'renew' | 'apply-discount';
101
- couponId?: string | null;
102
- }): Promise<ManageSubscriptionsResponse>;
103
- }
104
- export declare const apiService: ApiService;
105
- export {};