@userflow-tech/react-native 0.1.0 → 0.1.1

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 (142) hide show
  1. package/README.md +24 -14
  2. package/app.plugin.js +3 -0
  3. package/babel-plugin/index.d.ts +43 -0
  4. package/babel-plugin/index.d.ts.map +1 -0
  5. package/babel-plugin/index.js +854 -0
  6. package/babel-plugin/index.js.map +1 -0
  7. package/docs/deep-linking.md +2 -2
  8. package/docs/troubleshooting.md +2 -2
  9. package/expo-plugin/withUserflow.d.ts +17 -0
  10. package/expo-plugin/withUserflow.d.ts.map +1 -0
  11. package/expo-plugin/withUserflow.js +99 -0
  12. package/expo-plugin/withUserflow.js.map +1 -0
  13. package/lib/commonjs/anchors/AutoAnchorRegistry.js +1 -1
  14. package/lib/commonjs/anchors/autoAnchor.js +1 -1
  15. package/lib/commonjs/anchors/hintsCodec.js +1 -1
  16. package/lib/commonjs/pairing/diagnostics.js +2 -2
  17. package/lib/commonjs/pairing/diagnostics.js.map +1 -1
  18. package/lib/commonjs/version.js +1 -1
  19. package/lib/module/anchors/AutoAnchorRegistry.js +1 -1
  20. package/lib/module/anchors/autoAnchor.js +1 -1
  21. package/lib/module/anchors/hintsCodec.js +1 -1
  22. package/lib/module/pairing/diagnostics.js +2 -2
  23. package/lib/module/pairing/diagnostics.js.map +1 -1
  24. package/lib/module/version.js +1 -1
  25. package/lib/typescript/commonjs/src/anchors/AutoAnchorRegistry.d.ts +1 -1
  26. package/lib/typescript/commonjs/src/anchors/AutoAnchorRegistry.d.ts.map +1 -1
  27. package/lib/typescript/commonjs/src/anchors/autoAnchor.d.ts +1 -1
  28. package/lib/typescript/commonjs/src/anchors/hintsCodec.d.ts +1 -1
  29. package/lib/typescript/commonjs/src/version.d.ts +1 -1
  30. package/lib/typescript/module/src/anchors/AutoAnchorRegistry.d.ts +1 -1
  31. package/lib/typescript/module/src/anchors/AutoAnchorRegistry.d.ts.map +1 -1
  32. package/lib/typescript/module/src/anchors/autoAnchor.d.ts +1 -1
  33. package/lib/typescript/module/src/anchors/hintsCodec.d.ts +1 -1
  34. package/lib/typescript/module/src/version.d.ts +1 -1
  35. package/package.json +15 -7
  36. package/src/anchors/AutoAnchorRegistry.ts +1 -1
  37. package/src/anchors/autoAnchor.ts +1 -1
  38. package/src/anchors/hintsCodec.ts +1 -1
  39. package/src/pairing/diagnostics.ts +2 -2
  40. package/src/version.ts +1 -1
  41. package/vendor/core/LICENSE +76 -0
  42. package/vendor/core/README.md +9 -0
  43. package/vendor/core/dist/attributes.d.ts +16 -0
  44. package/vendor/core/dist/attributes.d.ts.map +1 -0
  45. package/vendor/core/dist/attributes.js +55 -0
  46. package/vendor/core/dist/attributes.js.map +1 -0
  47. package/vendor/core/dist/constants.d.ts +21 -0
  48. package/vendor/core/dist/constants.d.ts.map +1 -0
  49. package/vendor/core/dist/constants.js +21 -0
  50. package/vendor/core/dist/constants.js.map +1 -0
  51. package/vendor/core/dist/engine/ClientConditionEngine.d.ts +68 -0
  52. package/vendor/core/dist/engine/ClientConditionEngine.d.ts.map +1 -0
  53. package/vendor/core/dist/engine/ClientConditionEngine.js +197 -0
  54. package/vendor/core/dist/engine/ClientConditionEngine.js.map +1 -0
  55. package/vendor/core/dist/engine/FlowSessionEngine.d.ts +92 -0
  56. package/vendor/core/dist/engine/FlowSessionEngine.d.ts.map +1 -0
  57. package/vendor/core/dist/engine/FlowSessionEngine.js +310 -0
  58. package/vendor/core/dist/engine/FlowSessionEngine.js.map +1 -0
  59. package/vendor/core/dist/engine/PairingEngine.d.ts +92 -0
  60. package/vendor/core/dist/engine/PairingEngine.d.ts.map +1 -0
  61. package/vendor/core/dist/engine/PairingEngine.js +302 -0
  62. package/vendor/core/dist/engine/PairingEngine.js.map +1 -0
  63. package/vendor/core/dist/engine/UserflowCore.d.ts +126 -0
  64. package/vendor/core/dist/engine/UserflowCore.d.ts.map +1 -0
  65. package/vendor/core/dist/engine/UserflowCore.js +266 -0
  66. package/vendor/core/dist/engine/UserflowCore.js.map +1 -0
  67. package/vendor/core/dist/engine/handleActions.d.ts +19 -0
  68. package/vendor/core/dist/engine/handleActions.d.ts.map +1 -0
  69. package/vendor/core/dist/engine/handleActions.js +71 -0
  70. package/vendor/core/dist/engine/handleActions.js.map +1 -0
  71. package/vendor/core/dist/engine/interpolateSessionData.d.ts +7 -0
  72. package/vendor/core/dist/engine/interpolateSessionData.d.ts.map +1 -0
  73. package/vendor/core/dist/engine/interpolateSessionData.js +29 -0
  74. package/vendor/core/dist/engine/interpolateSessionData.js.map +1 -0
  75. package/vendor/core/dist/errors.d.ts +9 -0
  76. package/vendor/core/dist/errors.d.ts.map +1 -0
  77. package/vendor/core/dist/errors.js +15 -0
  78. package/vendor/core/dist/errors.js.map +1 -0
  79. package/vendor/core/dist/eventAttributes.d.ts +7 -0
  80. package/vendor/core/dist/eventAttributes.d.ts.map +1 -0
  81. package/vendor/core/dist/eventAttributes.js +31 -0
  82. package/vendor/core/dist/eventAttributes.js.map +1 -0
  83. package/vendor/core/dist/index.d.ts +30 -0
  84. package/vendor/core/dist/index.d.ts.map +1 -0
  85. package/vendor/core/dist/index.js +21 -0
  86. package/vendor/core/dist/index.js.map +1 -0
  87. package/vendor/core/dist/transport/backoff.d.ts +11 -0
  88. package/vendor/core/dist/transport/backoff.d.ts.map +1 -0
  89. package/vendor/core/dist/transport/backoff.js +17 -0
  90. package/vendor/core/dist/transport/backoff.js.map +1 -0
  91. package/vendor/core/dist/transport/pairing.d.ts +121 -0
  92. package/vendor/core/dist/transport/pairing.d.ts.map +1 -0
  93. package/vendor/core/dist/transport/pairing.js +551 -0
  94. package/vendor/core/dist/transport/pairing.js.map +1 -0
  95. package/vendor/core/dist/transport/phoenix.d.ts +223 -0
  96. package/vendor/core/dist/transport/phoenix.d.ts.map +1 -0
  97. package/vendor/core/dist/transport/phoenix.js +757 -0
  98. package/vendor/core/dist/transport/phoenix.js.map +1 -0
  99. package/vendor/core/dist/transport/rateLimiter.d.ts +17 -0
  100. package/vendor/core/dist/transport/rateLimiter.d.ts.map +1 -0
  101. package/vendor/core/dist/transport/rateLimiter.js +30 -0
  102. package/vendor/core/dist/transport/rateLimiter.js.map +1 -0
  103. package/vendor/core/dist/transport/scheduler.d.ts +12 -0
  104. package/vendor/core/dist/transport/scheduler.d.ts.map +1 -0
  105. package/vendor/core/dist/transport/scheduler.js +11 -0
  106. package/vendor/core/dist/transport/scheduler.js.map +1 -0
  107. package/vendor/core/dist/types.d.ts +35 -0
  108. package/vendor/core/dist/types.d.ts.map +1 -0
  109. package/vendor/core/dist/types.js +2 -0
  110. package/vendor/core/dist/types.js.map +1 -0
  111. package/vendor/core/dist/version.d.ts +3 -0
  112. package/vendor/core/dist/version.d.ts.map +1 -0
  113. package/vendor/core/dist/version.js +3 -0
  114. package/vendor/core/dist/version.js.map +1 -0
  115. package/vendor/core/package.json +20 -0
  116. package/vendor/protocol/LICENSE +76 -0
  117. package/vendor/protocol/README.md +8 -0
  118. package/vendor/protocol/dist/autoData.d.ts +232 -0
  119. package/vendor/protocol/dist/autoData.d.ts.map +1 -0
  120. package/vendor/protocol/dist/autoData.js +78 -0
  121. package/vendor/protocol/dist/autoData.js.map +1 -0
  122. package/vendor/protocol/dist/events.d.ts +16 -0
  123. package/vendor/protocol/dist/events.d.ts.map +1 -0
  124. package/vendor/protocol/dist/events.js +8 -0
  125. package/vendor/protocol/dist/events.js.map +1 -0
  126. package/vendor/protocol/dist/flows.d.ts +298 -0
  127. package/vendor/protocol/dist/flows.d.ts.map +1 -0
  128. package/vendor/protocol/dist/flows.js +154 -0
  129. package/vendor/protocol/dist/flows.js.map +1 -0
  130. package/vendor/protocol/dist/index.d.ts +24 -0
  131. package/vendor/protocol/dist/index.d.ts.map +1 -0
  132. package/vendor/protocol/dist/index.js +16 -0
  133. package/vendor/protocol/dist/index.js.map +1 -0
  134. package/vendor/protocol/dist/messages.d.ts +166 -0
  135. package/vendor/protocol/dist/messages.d.ts.map +1 -0
  136. package/vendor/protocol/dist/messages.js +57 -0
  137. package/vendor/protocol/dist/messages.js.map +1 -0
  138. package/vendor/protocol/dist/pairing.d.ts +56 -0
  139. package/vendor/protocol/dist/pairing.d.ts.map +1 -0
  140. package/vendor/protocol/dist/pairing.js +146 -0
  141. package/vendor/protocol/dist/pairing.js.map +1 -0
  142. package/vendor/protocol/package.json +17 -0
@@ -85,7 +85,7 @@ export function pairingForeignSchemeMessage(url: string): string {
85
85
  `link, but it was opened with a scheme Userflow does not own. Use your ` +
86
86
  `environment's scheme_id ("userflow-" followed by digits) exactly as the ` +
87
87
  `pairing QR code provides it — do not substitute your app's own scheme. ` +
88
- `Register it via the @userflow-tech/expo plugin ({ schemeId: 'userflow-…' }) and ` +
88
+ `Register it via the @userflow-tech/react-native plugin ({ schemeId: 'userflow-…' }) and ` +
89
89
  `rebuild the native app; a Metro reload is not enough.`
90
90
  );
91
91
  }
@@ -130,6 +130,6 @@ export function pairingSchemeShapeMessage(url: string): string {
130
130
  return (
131
131
  `${PREFIX} Ignoring Builder pairing link "${redactPairingToken(url)}": the scheme must be your ` +
132
132
  `environment's scheme_id — "userflow-" followed by digits (e.g. userflow-257587). ` +
133
- `Check the schemeId passed to the @userflow-tech/expo plugin.`
133
+ `Check the schemeId passed to the @userflow-tech/react-native plugin.`
134
134
  );
135
135
  }
package/src/version.ts CHANGED
@@ -1,2 +1,2 @@
1
1
  /** Generated by scripts/write-version.mjs — do not edit. */
2
- export const SDK_VERSION = '0.1.0';
2
+ export const SDK_VERSION = '0.1.1';
@@ -0,0 +1,76 @@
1
+ Userflow React Native SDK
2
+
3
+ Copyright (c) 2026 Userflow Inc. All rights reserved.
4
+
5
+ PROPRIETARY AND CONFIDENTIAL
6
+
7
+ This software and its source code (the "Software") are the proprietary and
8
+ confidential property of Userflow Inc. ("Userflow"). The Software is licensed,
9
+ not sold.
10
+
11
+ 1. Grant of License
12
+
13
+ Use of the Software is permitted only by parties who have entered into a
14
+ written agreement with Userflow that expressly covers the Software (the
15
+ "Agreement"), and only for the duration, purposes, and scope set out in
16
+ that Agreement. Where the Software is provided for evaluation or as part
17
+ of an early-access or design-partner program, use is limited to internal
18
+ evaluation and integration testing unless the Agreement states otherwise.
19
+
20
+ If you have not entered into such an Agreement with Userflow, you are
21
+ granted no rights to use, copy, modify, or distribute the Software.
22
+
23
+ 2. Restrictions
24
+
25
+ Except as expressly permitted by the Agreement or by applicable law, you
26
+ may not:
27
+
28
+ (a) redistribute, sublicense, sell, rent, lease, or otherwise transfer the
29
+ Software or any portion of it to any third party, except that you may
30
+ ship the Software in compiled or bundled form as an integral part of
31
+ your own applications distributed to your end users, provided you do
32
+ not redistribute the Software as a standalone library or SDK;
33
+ (b) modify or create derivative works of the Software, except for
34
+ automated transformations performed by ordinary build tooling
35
+ (including transpilation, bundling, minification, tree-shaking, and
36
+ source-map generation) and other modifications reasonably necessary
37
+ to integrate the Software into your applications, provided that any
38
+ such modifications remain subject to this notice;
39
+ (c) remove or obscure any copyright, trademark, or other proprietary
40
+ notices contained in the Software;
41
+ (d) disclose the Software's source code to any third party, except to your
42
+ employees and contractors who need access for a purpose permitted by
43
+ the Agreement and who are bound by written confidentiality
44
+ obligations no less protective than those in the Agreement.
45
+
46
+ 3. Pre-Release Software
47
+
48
+ The Software may be provided prior to general availability. It may be
49
+ incomplete, may change without notice, and may not be backward compatible
50
+ between versions. Userflow is under no obligation to provide maintenance,
51
+ support, updates, or future releases except as set out in the Agreement.
52
+
53
+ 4. No Warranty
54
+
55
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
56
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
57
+ FITNESS FOR A PARTICULAR PURPOSE, AND NONINFRINGEMENT.
58
+
59
+ 5. Limitation of Liability
60
+
61
+ TO THE MAXIMUM EXTENT PERMITTED BY APPLICABLE LAW, IN NO EVENT SHALL
62
+ USERFLOW BE LIABLE FOR ANY CLAIM, DAMAGES, OR OTHER LIABILITY, WHETHER IN
63
+ AN ACTION OF CONTRACT, TORT, OR OTHERWISE, ARISING FROM, OUT OF, OR IN
64
+ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
65
+
66
+ 6. Precedence
67
+
68
+ In the event of a conflict between this notice and the Agreement, the
69
+ Agreement governs.
70
+
71
+ For licensing enquiries, contact:
72
+
73
+ Userflow Inc.
74
+ 548 Market St PMB 69598
75
+ San Francisco, CA 94104-5401
76
+ United States
@@ -0,0 +1,9 @@
1
+ # `@userflow-tech/core`
2
+
3
+ Platform-agnostic engine for the Userflow React Native SDK (transport, identity,
4
+ flow session, client conditions).
5
+
6
+ **This package is an implementation detail and is not published.** It is
7
+ vendored into `@userflow-tech/react-native` at pack time. Host apps should
8
+ depend on that package only. Do not import `@userflow-tech/core` from
9
+ application code.
@@ -0,0 +1,16 @@
1
+ import type { UserflowAttributes } from '@userflow-tech/protocol';
2
+ /** Attribute scope used when validating names. Matches backend scopes. */
3
+ export type AttributeScope = 'user' | 'group' | 'group_membership' | 'event';
4
+ /** Keep in sync with `Flows.validate_attribute_name` / `validateAttributeDefinitionName`. */
5
+ export declare const MAX_ATTRIBUTE_NAME_LENGTH = 100;
6
+ /** Keep in sync with `Flows.validate_event_name` max length. */
7
+ export declare const MAX_EVENT_NAME_LENGTH = 255;
8
+ /** Returns true when `name` only contains supported attribute name characters. */
9
+ export declare function isAttributeNameSoftValid(name: string): boolean;
10
+ /** Returns an error message when invalid, or `null` when the name is allowed. */
11
+ export declare function validateAttributeName(scope: AttributeScope, name: string): string | null;
12
+ /** Throws when any attribute name is invalid for the given scope. */
13
+ export declare function assertValidAttributeNames(scope: AttributeScope, attributes: UserflowAttributes): void;
14
+ /** Throws when the custom event name is invalid. */
15
+ export declare function assertValidEventName(name: string): void;
16
+ //# sourceMappingURL=attributes.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"attributes.d.ts","sourceRoot":"","sources":["../src/attributes.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,yBAAyB,CAAC;AAIlE,0EAA0E;AAC1E,MAAM,MAAM,cAAc,GAAG,MAAM,GAAG,OAAO,GAAG,kBAAkB,GAAG,OAAO,CAAC;AAE7E,6FAA6F;AAC7F,eAAO,MAAM,yBAAyB,MAAM,CAAC;AAE7C,gEAAgE;AAChE,eAAO,MAAM,qBAAqB,MAAM,CAAC;AAczC,kFAAkF;AAClF,wBAAgB,wBAAwB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAE9D;AAED,iFAAiF;AACjF,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,cAAc,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAcxF;AAED,qEAAqE;AACrE,wBAAgB,yBAAyB,CACvC,KAAK,EAAE,cAAc,EACrB,UAAU,EAAE,kBAAkB,GAC7B,IAAI,CAQN;AAED,oDAAoD;AACpD,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAcvD"}
@@ -0,0 +1,55 @@
1
+ import { UserflowAuthError } from './errors.js';
2
+ /** Keep in sync with `Flows.validate_attribute_name` / `validateAttributeDefinitionName`. */
3
+ export const MAX_ATTRIBUTE_NAME_LENGTH = 100;
4
+ /** Keep in sync with `Flows.validate_event_name` max length. */
5
+ export const MAX_EVENT_NAME_LENGTH = 255;
6
+ const ATTRIBUTE_NAME_PATTERN = /^[a-z0-9_ :|#()-]+$/i;
7
+ /** Matches Userflow docs / web SDK event naming rules (subset of backend validator). */
8
+ const EVENT_NAME_PATTERN = /^[a-z0-9_ \-.()]+$/i;
9
+ const RESERVED_ATTRIBUTE_NAMES = {
10
+ user: ['id', 'groups', 'traits'],
11
+ event: [],
12
+ group: [],
13
+ group_membership: [],
14
+ };
15
+ /** Returns true when `name` only contains supported attribute name characters. */
16
+ export function isAttributeNameSoftValid(name) {
17
+ return typeof name === 'string' && ATTRIBUTE_NAME_PATTERN.test(name);
18
+ }
19
+ /** Returns an error message when invalid, or `null` when the name is allowed. */
20
+ export function validateAttributeName(scope, name) {
21
+ if (!isAttributeNameSoftValid(name)) {
22
+ return `${name}: Invalid attribute name. Attribute names must consist of only a-z, A-Z, 0-9, underscores, dashes, colons, vertical lines (|), hash signs (#), parentheses and spaces.`;
23
+ }
24
+ if (name.length > MAX_ATTRIBUTE_NAME_LENGTH) {
25
+ return `${name}: Attribute names must not be longer than ${MAX_ATTRIBUTE_NAME_LENGTH} characters.`;
26
+ }
27
+ if (RESERVED_ATTRIBUTE_NAMES[scope].includes(name)) {
28
+ return `${name}: Is a reserved name and cannot be used for attributes.`;
29
+ }
30
+ return null;
31
+ }
32
+ /** Throws when any attribute name is invalid for the given scope. */
33
+ export function assertValidAttributeNames(scope, attributes) {
34
+ for (const name of Object.keys(attributes)) {
35
+ if (!Object.prototype.hasOwnProperty.call(attributes, name))
36
+ continue;
37
+ const error = validateAttributeName(scope, name);
38
+ if (error) {
39
+ throw new UserflowAuthError(error);
40
+ }
41
+ }
42
+ }
43
+ /** Throws when the custom event name is invalid. */
44
+ export function assertValidEventName(name) {
45
+ if (!name) {
46
+ throw new UserflowAuthError('Missing name');
47
+ }
48
+ if (!EVENT_NAME_PATTERN.test(name)) {
49
+ throw new UserflowAuthError('Invalid event name. Event names must consist of only a-z, A-Z, 0-9, underscores, dashes, periods(.), parentheses and spaces.');
50
+ }
51
+ if (name.length > MAX_EVENT_NAME_LENGTH) {
52
+ throw new UserflowAuthError(`Event names must not be longer than ${MAX_EVENT_NAME_LENGTH} characters.`);
53
+ }
54
+ }
55
+ //# sourceMappingURL=attributes.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"attributes.js","sourceRoot":"","sources":["../src/attributes.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAKhD,6FAA6F;AAC7F,MAAM,CAAC,MAAM,yBAAyB,GAAG,GAAG,CAAC;AAE7C,gEAAgE;AAChE,MAAM,CAAC,MAAM,qBAAqB,GAAG,GAAG,CAAC;AAEzC,MAAM,sBAAsB,GAAG,sBAAsB,CAAC;AAEtD,wFAAwF;AACxF,MAAM,kBAAkB,GAAG,qBAAqB,CAAC;AAEjD,MAAM,wBAAwB,GAAwD;IACpF,IAAI,EAAE,CAAC,IAAI,EAAE,QAAQ,EAAE,QAAQ,CAAC;IAChC,KAAK,EAAE,EAAE;IACT,KAAK,EAAE,EAAE;IACT,gBAAgB,EAAE,EAAE;CACrB,CAAC;AAEF,kFAAkF;AAClF,MAAM,UAAU,wBAAwB,CAAC,IAAY;IACnD,OAAO,OAAO,IAAI,KAAK,QAAQ,IAAI,sBAAsB,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AACvE,CAAC;AAED,iFAAiF;AACjF,MAAM,UAAU,qBAAqB,CAAC,KAAqB,EAAE,IAAY;IACvE,IAAI,CAAC,wBAAwB,CAAC,IAAI,CAAC,EAAE,CAAC;QACpC,OAAO,GAAG,IAAI,wKAAwK,CAAC;IACzL,CAAC;IAED,IAAI,IAAI,CAAC,MAAM,GAAG,yBAAyB,EAAE,CAAC;QAC5C,OAAO,GAAG,IAAI,6CAA6C,yBAAyB,cAAc,CAAC;IACrG,CAAC;IAED,IAAI,wBAAwB,CAAC,KAAK,CAAC,CAAC,QAAQ,CAAC,IAAI,CAAC,EAAE,CAAC;QACnD,OAAO,GAAG,IAAI,yDAAyD,CAAC;IAC1E,CAAC;IAED,OAAO,IAAI,CAAC;AACd,CAAC;AAED,qEAAqE;AACrE,MAAM,UAAU,yBAAyB,CACvC,KAAqB,EACrB,UAA8B;IAE9B,KAAK,MAAM,IAAI,IAAI,MAAM,CAAC,IAAI,CAAC,UAAU,CAAC,EAAE,CAAC;QAC3C,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,cAAc,CAAC,IAAI,CAAC,UAAU,EAAE,IAAI,CAAC;YAAE,SAAS;QACtE,MAAM,KAAK,GAAG,qBAAqB,CAAC,KAAK,EAAE,IAAI,CAAC,CAAC;QACjD,IAAI,KAAK,EAAE,CAAC;YACV,MAAM,IAAI,iBAAiB,CAAC,KAAK,CAAC,CAAC;QACrC,CAAC;IACH,CAAC;AACH,CAAC;AAED,oDAAoD;AACpD,MAAM,UAAU,oBAAoB,CAAC,IAAY;IAC/C,IAAI,CAAC,IAAI,EAAE,CAAC;QACV,MAAM,IAAI,iBAAiB,CAAC,cAAc,CAAC,CAAC;IAC9C,CAAC;IACD,IAAI,CAAC,kBAAkB,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;QACnC,MAAM,IAAI,iBAAiB,CACzB,8HAA8H,CAC/H,CAAC;IACJ,CAAC;IACD,IAAI,IAAI,CAAC,MAAM,GAAG,qBAAqB,EAAE,CAAC;QACxC,MAAM,IAAI,iBAAiB,CACzB,uCAAuC,qBAAqB,cAAc,CAC3E,CAAC;IACJ,CAAC;AACH,CAAC"}
@@ -0,0 +1,21 @@
1
+ /**
2
+ * Default backend endpoint used when a host app does not pass an explicit
3
+ * `serverEndpoint` to `init()`. Points at Userflow production.
4
+ *
5
+ * For local development against the Userflow dev stack, pass the local endpoint
6
+ * explicitly, e.g. `init({ ..., serverEndpoint: 'http://localhost:40401' })`.
7
+ *
8
+ * TODO(publish): production server-driven `Publish` delivery for mobile is not
9
+ * wired yet (only the Preview/dev-device path is validated). Revisit this
10
+ * default and the transport once Publish lands.
11
+ */
12
+ export declare const DEFAULT_SERVER_ENDPOINT = "https://e.userflow.com";
13
+ /**
14
+ * US-pinned host for the Builder pairing websocket (`/socket/pairing`).
15
+ * The builder dashboard and pairing PubSub are US-only; do not use a regional
16
+ * `serverEndpoint` for pairing.
17
+ *
18
+ * Override via {@link PairingConnection} options for local/staging stacks.
19
+ */
20
+ export declare const DEFAULT_PAIRING_ENDPOINT = "https://e.userflow.com";
21
+ //# sourceMappingURL=constants.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../src/constants.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AACH,eAAO,MAAM,uBAAuB,2BAA2B,CAAC;AAEhE;;;;;;GAMG;AACH,eAAO,MAAM,wBAAwB,2BAA2B,CAAC"}
@@ -0,0 +1,21 @@
1
+ /**
2
+ * Default backend endpoint used when a host app does not pass an explicit
3
+ * `serverEndpoint` to `init()`. Points at Userflow production.
4
+ *
5
+ * For local development against the Userflow dev stack, pass the local endpoint
6
+ * explicitly, e.g. `init({ ..., serverEndpoint: 'http://localhost:40401' })`.
7
+ *
8
+ * TODO(publish): production server-driven `Publish` delivery for mobile is not
9
+ * wired yet (only the Preview/dev-device path is validated). Revisit this
10
+ * default and the transport once Publish lands.
11
+ */
12
+ export const DEFAULT_SERVER_ENDPOINT = 'https://e.userflow.com';
13
+ /**
14
+ * US-pinned host for the Builder pairing websocket (`/socket/pairing`).
15
+ * The builder dashboard and pairing PubSub are US-only; do not use a regional
16
+ * `serverEndpoint` for pairing.
17
+ *
18
+ * Override via {@link PairingConnection} options for local/staging stacks.
19
+ */
20
+ export const DEFAULT_PAIRING_ENDPOINT = 'https://e.userflow.com';
21
+ //# sourceMappingURL=constants.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"constants.js","sourceRoot":"","sources":["../src/constants.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AACH,MAAM,CAAC,MAAM,uBAAuB,GAAG,wBAAwB,CAAC;AAEhE;;;;;;GAMG;AACH,MAAM,CAAC,MAAM,wBAAwB,GAAG,wBAAwB,CAAC"}
@@ -0,0 +1,68 @@
1
+ import type { ClientMessage, FlowConditionFragment, FlowConditionType, JoinPayloadClientCondition, ServerMessage } from '@userflow-tech/protocol';
2
+ import { type Scheduler } from '../transport/scheduler.js';
3
+ /** Narrow transport surface the client-condition engine depends on. */
4
+ export interface ClientConditionTransport {
5
+ send(message: ClientMessage): Promise<void>;
6
+ onServerMessage(handler: (message: ServerMessage) => void): () => void;
7
+ setClientConditions(clientConditions: JoinPayloadClientCondition[]): void;
8
+ }
9
+ /**
10
+ * Platform-specific evaluator for one condition type. Calls `report` whenever
11
+ * truthiness changes (including the initial evaluation). Returns an untrack
12
+ * function that tears down listeners. `null` means "unknown / cannot evaluate".
13
+ */
14
+ export type ConditionEvaluator = (condition: FlowConditionFragment, report: (isTrue: boolean | null) => void) => () => void;
15
+ export interface ClientConditionEngineDeps {
16
+ transport: ClientConditionTransport;
17
+ /** Per-type evaluators. Types without an entry stay unknown and warn once. */
18
+ evaluators?: Partial<Record<FlowConditionType, ConditionEvaluator>>;
19
+ warn?: (message: string) => void;
20
+ scheduler?: Scheduler;
21
+ /** Override the ToggleClientCondition debounce window (tests). */
22
+ reportDebounceMs?: number;
23
+ }
24
+ /**
25
+ * Framework-agnostic client-condition state machine.
26
+ *
27
+ * Mirrors web `userflow-client` TrackClientCondition / UntrackClientCondition /
28
+ * ToggleClientCondition semantics: starts each condition at `isTrue: null`
29
+ * (unknown), reports transitions via ToggleClientCondition, and seeds the
30
+ * join payload snapshot so reconnects do not lose tracked state.
31
+ *
32
+ * Call {@link attach} after identify. Re-attach rebinds evaluators for any
33
+ * conditions still in the map (so identify → identify does not freeze tracking).
34
+ */
35
+ export declare class ClientConditionEngine {
36
+ private readonly transport;
37
+ private readonly evaluators;
38
+ private readonly warn;
39
+ private readonly scheduler;
40
+ private readonly reportDebounceMs;
41
+ private readonly conditions;
42
+ private unsubscribeServerMessages;
43
+ constructor(deps: ClientConditionEngineDeps);
44
+ /**
45
+ * Subscribe to inbound server messages and (re)bind evaluators for any
46
+ * conditions still in the map. Must be called after identify.
47
+ */
48
+ attach(): () => void;
49
+ /**
50
+ * Unsubscribe from server messages and stop evaluators, but keep the
51
+ * condition map so a later attach can resume (rebind evaluators + snapshot).
52
+ */
53
+ detach(): void;
54
+ /** Tear down all tracked conditions and clear the join snapshot. */
55
+ reset(): void;
56
+ /** Current join-payload snapshot (including `isTrue: null` unknowns). */
57
+ getSnapshot(): JoinPayloadClientCondition[];
58
+ private handleServerMessage;
59
+ private trackClientCondition;
60
+ private untrackClientCondition;
61
+ private rebindEvaluators;
62
+ private bindEvaluator;
63
+ private report;
64
+ private sendToggle;
65
+ private pushSnapshot;
66
+ private clearDebounce;
67
+ }
68
+ //# sourceMappingURL=ClientConditionEngine.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ClientConditionEngine.d.ts","sourceRoot":"","sources":["../../src/engine/ClientConditionEngine.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,aAAa,EACb,qBAAqB,EACrB,iBAAiB,EACjB,0BAA0B,EAC1B,aAAa,EACd,MAAM,yBAAyB,CAAC;AAEjC,OAAO,EAAoB,KAAK,SAAS,EAAoB,MAAM,2BAA2B,CAAC;AAK/F,uEAAuE;AACvE,MAAM,WAAW,wBAAwB;IACvC,IAAI,CAAC,OAAO,EAAE,aAAa,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC5C,eAAe,CAAC,OAAO,EAAE,CAAC,OAAO,EAAE,aAAa,KAAK,IAAI,GAAG,MAAM,IAAI,CAAC;IACvE,mBAAmB,CAAC,gBAAgB,EAAE,0BAA0B,EAAE,GAAG,IAAI,CAAC;CAC3E;AAED;;;;GAIG;AACH,MAAM,MAAM,kBAAkB,GAAG,CAC/B,SAAS,EAAE,qBAAqB,EAChC,MAAM,EAAE,CAAC,MAAM,EAAE,OAAO,GAAG,IAAI,KAAK,IAAI,KACrC,MAAM,IAAI,CAAC;AAEhB,MAAM,WAAW,yBAAyB;IACxC,SAAS,EAAE,wBAAwB,CAAC;IACpC,8EAA8E;IAC9E,UAAU,CAAC,EAAE,OAAO,CAAC,MAAM,CAAC,iBAAiB,EAAE,kBAAkB,CAAC,CAAC,CAAC;IACpE,IAAI,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;IACjC,SAAS,CAAC,EAAE,SAAS,CAAC;IACtB,kEAAkE;IAClE,gBAAgB,CAAC,EAAE,MAAM,CAAC;CAC3B;AAWD;;;;;;;;;;GAUG;AACH,qBAAa,qBAAqB;IAChC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAA2B;IACrD,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAyD;IACpF,OAAO,CAAC,QAAQ,CAAC,IAAI,CAA4B;IACjD,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAY;IACtC,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAAS;IAE1C,OAAO,CAAC,QAAQ,CAAC,UAAU,CAA0C;IACrE,OAAO,CAAC,yBAAyB,CAA6B;gBAElD,IAAI,EAAE,yBAAyB;IAQ3C;;;OAGG;IACH,MAAM,IAAI,MAAM,IAAI;IAapB;;;OAGG;IACH,MAAM,IAAI,IAAI;IAWd,oEAAoE;IACpE,KAAK,IAAI,IAAI;IASb,yEAAyE;IACzE,WAAW,IAAI,0BAA0B,EAAE;IAO3C,OAAO,CAAC,mBAAmB;IAa3B,OAAO,CAAC,oBAAoB;IAiB5B,OAAO,CAAC,sBAAsB;IAS9B,OAAO,CAAC,gBAAgB;IAMxB,OAAO,CAAC,aAAa;IAqBrB,OAAO,CAAC,MAAM;IA+Cd,OAAO,CAAC,UAAU;IAelB,OAAO,CAAC,YAAY;IAIpB,OAAO,CAAC,aAAa;CAMtB"}
@@ -0,0 +1,197 @@
1
+ import { defaultScheduler } from '../transport/scheduler.js';
2
+ /** Cap for coalescing rapid truthiness flips before sending ToggleClientCondition. */
3
+ const REPORT_DEBOUNCE_MS = 250;
4
+ /**
5
+ * Framework-agnostic client-condition state machine.
6
+ *
7
+ * Mirrors web `userflow-client` TrackClientCondition / UntrackClientCondition /
8
+ * ToggleClientCondition semantics: starts each condition at `isTrue: null`
9
+ * (unknown), reports transitions via ToggleClientCondition, and seeds the
10
+ * join payload snapshot so reconnects do not lose tracked state.
11
+ *
12
+ * Call {@link attach} after identify. Re-attach rebinds evaluators for any
13
+ * conditions still in the map (so identify → identify does not freeze tracking).
14
+ */
15
+ export class ClientConditionEngine {
16
+ constructor(deps) {
17
+ this.conditions = new Map();
18
+ this.unsubscribeServerMessages = null;
19
+ this.transport = deps.transport;
20
+ this.evaluators = deps.evaluators ?? {};
21
+ this.warn = deps.warn ?? ((message) => console.warn(message));
22
+ this.scheduler = deps.scheduler ?? defaultScheduler;
23
+ this.reportDebounceMs = deps.reportDebounceMs ?? REPORT_DEBOUNCE_MS;
24
+ }
25
+ /**
26
+ * Subscribe to inbound server messages and (re)bind evaluators for any
27
+ * conditions still in the map. Must be called after identify.
28
+ */
29
+ attach() {
30
+ this.detach();
31
+ this.unsubscribeServerMessages = this.transport.onServerMessage((message) => {
32
+ this.handleServerMessage(message);
33
+ });
34
+ // Rebind after detach so a second identify/attach keeps live evaluation
35
+ // even when the server skips TrackClientCondition (join snapshot already
36
+ // lists these IDs as tracked).
37
+ this.rebindEvaluators();
38
+ this.pushSnapshot();
39
+ return () => this.detach();
40
+ }
41
+ /**
42
+ * Unsubscribe from server messages and stop evaluators, but keep the
43
+ * condition map so a later attach can resume (rebind evaluators + snapshot).
44
+ */
45
+ detach() {
46
+ this.unsubscribeServerMessages?.();
47
+ this.unsubscribeServerMessages = null;
48
+ for (const meta of this.conditions.values()) {
49
+ this.clearDebounce(meta);
50
+ meta.pendingReport = undefined;
51
+ meta.untrack?.();
52
+ delete meta.untrack;
53
+ }
54
+ }
55
+ /** Tear down all tracked conditions and clear the join snapshot. */
56
+ reset() {
57
+ const ids = [...this.conditions.keys()];
58
+ for (const id of ids) {
59
+ this.untrackClientCondition(id);
60
+ }
61
+ this.detach();
62
+ this.pushSnapshot();
63
+ }
64
+ /** Current join-payload snapshot (including `isTrue: null` unknowns). */
65
+ getSnapshot() {
66
+ return Array.from(this.conditions.values()).map((meta) => ({
67
+ conditionId: meta.condition.id,
68
+ isTrue: meta.isTrue,
69
+ }));
70
+ }
71
+ handleServerMessage(message) {
72
+ switch (message.kind) {
73
+ case 'TrackClientCondition':
74
+ this.trackClientCondition(message.condition, message.flowId);
75
+ return;
76
+ case 'UntrackClientCondition':
77
+ this.untrackClientCondition(message.conditionId);
78
+ return;
79
+ default:
80
+ return;
81
+ }
82
+ }
83
+ trackClientCondition(condition, flowId) {
84
+ if (this.conditions.has(condition.id)) {
85
+ return;
86
+ }
87
+ const meta = {
88
+ condition,
89
+ flowId,
90
+ isTrue: null,
91
+ pendingReport: undefined,
92
+ debounceHandle: null,
93
+ };
94
+ this.conditions.set(condition.id, meta);
95
+ this.pushSnapshot();
96
+ this.bindEvaluator(meta, { warnIfMissing: true });
97
+ }
98
+ untrackClientCondition(conditionId) {
99
+ const meta = this.conditions.get(conditionId);
100
+ if (!meta)
101
+ return;
102
+ this.clearDebounce(meta);
103
+ meta.untrack?.();
104
+ this.conditions.delete(conditionId);
105
+ this.pushSnapshot();
106
+ }
107
+ rebindEvaluators() {
108
+ for (const meta of this.conditions.values()) {
109
+ this.bindEvaluator(meta, { warnIfMissing: false });
110
+ }
111
+ }
112
+ bindEvaluator(meta, options) {
113
+ meta.untrack?.();
114
+ delete meta.untrack;
115
+ const evaluator = this.evaluators[meta.condition.type];
116
+ if (!evaluator) {
117
+ if (options.warnIfMissing) {
118
+ this.warn(`Userflow: cannot evaluate client condition type "${meta.condition.type}" ` +
119
+ `for flow "${meta.flowId}" (condition ${meta.condition.id}); leaving unknown so ` +
120
+ 'the gated flow will not start until this type is supported');
121
+ }
122
+ return;
123
+ }
124
+ meta.untrack = evaluator(meta.condition, (isTrue) => {
125
+ this.report(meta.condition.id, isTrue);
126
+ });
127
+ }
128
+ report(conditionId, isTrue) {
129
+ const meta = this.conditions.get(conditionId);
130
+ if (!meta)
131
+ return;
132
+ if (isTrue === null) {
133
+ // Evaluator cannot decide — stay unknown and never send Toggle.
134
+ // (Specific why-warnings belong to the evaluator; missing-evaluator
135
+ // warnings are emitted at track time.)
136
+ this.clearDebounce(meta);
137
+ meta.pendingReport = undefined;
138
+ if (meta.isTrue !== null) {
139
+ meta.isTrue = null;
140
+ this.pushSnapshot();
141
+ }
142
+ return;
143
+ }
144
+ if (meta.isTrue === isTrue && meta.pendingReport === undefined) {
145
+ return;
146
+ }
147
+ // First resolution from unknown: send immediately so gated flows are not
148
+ // delayed by the remount-churn debounce.
149
+ if (meta.isTrue === null) {
150
+ this.clearDebounce(meta);
151
+ meta.pendingReport = undefined;
152
+ meta.isTrue = isTrue;
153
+ this.pushSnapshot();
154
+ this.sendToggle(conditionId, isTrue);
155
+ return;
156
+ }
157
+ // Subsequent flips: trailing debounce to absorb anchor remount churn.
158
+ meta.pendingReport = isTrue;
159
+ this.clearDebounce(meta);
160
+ meta.debounceHandle = this.scheduler.setTimeout(() => {
161
+ meta.debounceHandle = null;
162
+ const next = meta.pendingReport;
163
+ meta.pendingReport = undefined;
164
+ if (next === undefined)
165
+ return;
166
+ if (meta.isTrue === next)
167
+ return;
168
+ meta.isTrue = next;
169
+ this.pushSnapshot();
170
+ this.sendToggle(conditionId, next);
171
+ }, this.reportDebounceMs);
172
+ }
173
+ sendToggle(conditionId, isTrue) {
174
+ // Swallow send failures (e.g. not joined). The value is already in the
175
+ // join-payload snapshot, so reconnect will re-seed the server without loss.
176
+ // ToggleClientCondition is rate-limit-exempt on the transport (web parity).
177
+ void this.transport
178
+ .send({
179
+ kind: 'ToggleClientCondition',
180
+ conditionId,
181
+ isTrue,
182
+ })
183
+ .catch(() => {
184
+ // Intentionally empty — see comment above.
185
+ });
186
+ }
187
+ pushSnapshot() {
188
+ this.transport.setClientConditions(this.getSnapshot());
189
+ }
190
+ clearDebounce(meta) {
191
+ if (meta.debounceHandle !== null) {
192
+ this.scheduler.clearTimeout(meta.debounceHandle);
193
+ meta.debounceHandle = null;
194
+ }
195
+ }
196
+ }
197
+ //# sourceMappingURL=ClientConditionEngine.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ClientConditionEngine.js","sourceRoot":"","sources":["../../src/engine/ClientConditionEngine.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,92 @@
1
+ import type { ClientMessage, FlowActionFragment, FlowSessionFragment, FlowStepFragment, ServerMessage } from '@userflow-tech/protocol';
2
+ import { EndFlowReasonInput } from '@userflow-tech/protocol';
3
+ import { type Scheduler } from '../transport/scheduler.js';
4
+ /** Narrow transport surface the flow session engine depends on. */
5
+ export interface FlowTransport {
6
+ send(message: ClientMessage): Promise<void>;
7
+ onServerMessage(handler: (message: ServerMessage) => void): () => void;
8
+ setFlowSessionId(flowSessionId: string | null): void;
9
+ }
10
+ export type FlowSessionEngineEvent = {
11
+ type: 'sessionChange';
12
+ session: FlowSessionFragment | null;
13
+ } | {
14
+ type: 'goToStep';
15
+ sessionId: string;
16
+ stepId: string;
17
+ } | {
18
+ type: 'flowEnded';
19
+ flowId: string;
20
+ endReason: EndFlowReasonInput;
21
+ };
22
+ export type FlowSessionEngineListener = (event: FlowSessionEngineEvent) => void;
23
+ export interface FlowSessionEngineDeps {
24
+ transport: FlowTransport;
25
+ /** Open a URL for NAVIGATE actions (both SAME_TAB and NEW_TAB on mobile). */
26
+ onOpenExternalUrl?: (url: string) => void;
27
+ warn?: (message: string) => void;
28
+ scheduler?: Scheduler;
29
+ }
30
+ /**
31
+ * Framework-agnostic flow session state machine (Phase 3 WS2).
32
+ *
33
+ * Ports web `userflow-client` session/clock/wait-timer semantics onto the
34
+ * Phoenix transport seam. Call {@link attach} after identify.
35
+ */
36
+ export declare class FlowSessionEngine {
37
+ private readonly transport;
38
+ private readonly onOpenExternalUrl;
39
+ private readonly warn;
40
+ private readonly scheduler;
41
+ private clientClock;
42
+ private serverClock;
43
+ private flowSessionClock;
44
+ private flowSession;
45
+ private readonly listeners;
46
+ private readonly conditionWaitTimers;
47
+ private readonly pendingSends;
48
+ private flushingPending;
49
+ private unsubscribeServerMessages;
50
+ constructor(deps: FlowSessionEngineDeps);
51
+ /** Subscribe to inbound server messages. Must be called after identify. */
52
+ attach(): () => void;
53
+ /**
54
+ * Unsubscribe from server messages and clear wait timers (keeps session state).
55
+ * Note: in-flight condition wait timers are dropped and not restarted on
56
+ * re-attach; the server must send StartConditionWaitTimer again if needed.
57
+ */
58
+ detach(): void;
59
+ on(listener: FlowSessionEngineListener): () => void;
60
+ getFlowSession(): FlowSessionFragment | null;
61
+ getCurrentStep(): FlowStepFragment | null;
62
+ /** Visible for tests / advanced callers; prefer {@link attach}. */
63
+ handleServerMessage(message: ServerMessage): void;
64
+ goToStep(step: FlowStepFragment): Promise<void>;
65
+ endFlow(endReason: EndFlowReasonInput): Promise<void>;
66
+ reportTooltipTargetMissing(stepId: string): Promise<void>;
67
+ handleActions(actions: FlowActionFragment[]): Promise<void>;
68
+ /**
69
+ * Clear the local flow session and join resume id without detaching from
70
+ * server messages. Used when a long background makes mid-flow resume stale.
71
+ */
72
+ clearLocalSession(): void;
73
+ /** Clear session, clocks, timers, pending sends, and join resume id; detach from transport. */
74
+ reset(): void;
75
+ /**
76
+ * Flush client messages queued after transport failure.
77
+ * Call after reconnect / lifecycle resume (`ensureConnected` path).
78
+ */
79
+ flushPendingSends(): Promise<void>;
80
+ /** @internal Test helper. */
81
+ getPendingSendCountForTests(): number;
82
+ private setFlowSession;
83
+ private optimisticClockUIUpdate;
84
+ private enqueuePendingSend;
85
+ private sendTracked;
86
+ private startConditionWaitTimer;
87
+ private cancelConditionWaitTimer;
88
+ private clearConditionWaitTimers;
89
+ private requireFlowSession;
90
+ private emit;
91
+ }
92
+ //# sourceMappingURL=FlowSessionEngine.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"FlowSessionEngine.d.ts","sourceRoot":"","sources":["../../src/engine/FlowSessionEngine.ts"],"names":[],"mappings":"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"}