flowboard-react 0.6.0 → 0.6.2
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/README.md +9 -9
- package/lib/module/Flowboard.js +16 -13
- package/lib/module/Flowboard.js.map +1 -1
- package/lib/module/components/FlowboardFlow.js +25 -3
- package/lib/module/components/FlowboardFlow.js.map +1 -1
- package/lib/module/components/FlowboardRenderer.js +413 -79
- package/lib/module/components/FlowboardRenderer.js.map +1 -1
- package/lib/module/core/apiConfig.js +47 -0
- package/lib/module/core/apiConfig.js.map +1 -0
- package/lib/module/core/resolverService.js +33 -37
- package/lib/module/core/resolverService.js.map +1 -1
- package/lib/module/utils/flowboardUtils.js +4 -1
- package/lib/module/utils/flowboardUtils.js.map +1 -1
- package/lib/typescript/src/Flowboard.d.ts +2 -4
- package/lib/typescript/src/Flowboard.d.ts.map +1 -1
- package/lib/typescript/src/components/FlowboardFlow.d.ts.map +1 -1
- package/lib/typescript/src/components/FlowboardRenderer.d.ts +33 -0
- package/lib/typescript/src/components/FlowboardRenderer.d.ts.map +1 -1
- package/lib/typescript/src/core/apiConfig.d.ts +6 -0
- package/lib/typescript/src/core/apiConfig.d.ts.map +1 -0
- package/lib/typescript/src/core/resolverService.d.ts +8 -5
- package/lib/typescript/src/core/resolverService.d.ts.map +1 -1
- package/lib/typescript/src/index.d.ts +1 -1
- package/lib/typescript/src/index.d.ts.map +1 -1
- package/lib/typescript/src/types/flowboard.d.ts +2 -0
- package/lib/typescript/src/types/flowboard.d.ts.map +1 -1
- package/lib/typescript/src/utils/flowboardUtils.d.ts.map +1 -1
- package/package.json +18 -19
- package/src/Flowboard.ts +17 -16
- package/src/components/FlowboardFlow.tsx +37 -2
- package/src/components/FlowboardRenderer.tsx +681 -100
- package/src/core/apiConfig.ts +59 -0
- package/src/core/resolverService.ts +40 -41
- package/src/index.tsx +1 -0
- package/src/types/flowboard.ts +3 -0
- package/src/utils/flowboardUtils.ts +9 -1
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
const DEFAULT_API_BASE_URL = 'api.flow-board.co';
|
|
2
|
+
const DEFAULT_API_VERSION = 'v0';
|
|
3
|
+
|
|
4
|
+
function trimTrailingSlashes(value: string): string {
|
|
5
|
+
return value.replace(/\/+$/, '');
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
function trimSurroundingSlashes(value: string): string {
|
|
9
|
+
return value.replace(/^\/+|\/+$/g, '');
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
function normalizeApiBaseUrl(value: string): string {
|
|
13
|
+
const trimmed = trimTrailingSlashes(String(value || '').trim());
|
|
14
|
+
if (!trimmed) {
|
|
15
|
+
throw new Error('Flowboard API base URL must be a non-empty string.');
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
if (/^https?:\/\//i.test(trimmed)) {
|
|
19
|
+
return trimmed;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
return `https://${trimmed}`;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function normalizeApiVersion(value: string): string {
|
|
26
|
+
const trimmed = trimSurroundingSlashes(String(value || '').trim());
|
|
27
|
+
if (!trimmed) {
|
|
28
|
+
throw new Error('Flowboard API version must be a non-empty string.');
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
return `/${trimmed}`;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export function getFlowboardApiBaseUrl(): string {
|
|
35
|
+
return normalizeApiBaseUrl(DEFAULT_API_BASE_URL);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export function getFlowboardApiVersion(): string {
|
|
39
|
+
return normalizeApiVersion(DEFAULT_API_VERSION);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export function getFlowboardRuntimeBaseUrl(): string {
|
|
43
|
+
return `${getFlowboardApiBaseUrl()}${getFlowboardApiVersion()}`;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export function getResolveFlowUrl(): string {
|
|
47
|
+
return `${getFlowboardRuntimeBaseUrl()}/flows`;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export function getFlowByIdUrl(flowId: string): string {
|
|
51
|
+
const normalizedFlowId = String(flowId || '').trim();
|
|
52
|
+
if (!normalizedFlowId) {
|
|
53
|
+
throw new Error('flowId must be a non-empty string.');
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
return `${getFlowboardRuntimeBaseUrl()}/flows/${encodeURIComponent(
|
|
57
|
+
normalizedFlowId
|
|
58
|
+
)}`;
|
|
59
|
+
}
|
|
@@ -1,48 +1,31 @@
|
|
|
1
1
|
import type { ClientContext } from './clientContext';
|
|
2
|
-
import type { FlowboardData } from '../types/flowboard';
|
|
3
|
-
|
|
4
|
-
const DEFAULT_ENDPOINT = 'https://test-638704832888.europe-west1.run.app';
|
|
5
|
-
const SPECIFIC_ONBOARDING_ENDPOINT =
|
|
6
|
-
'https://onboardsolo-638704832888.europe-west1.run.app';
|
|
2
|
+
import type { FlowboardData, FlowboardFlowVersion } from '../types/flowboard';
|
|
3
|
+
import { getFlowByIdUrl, getResolveFlowUrl } from './apiConfig';
|
|
7
4
|
|
|
8
5
|
export class ResolverService {
|
|
9
|
-
private
|
|
10
|
-
private specificOnboardingEndpoint: string;
|
|
6
|
+
private fetchImpl: typeof fetch;
|
|
11
7
|
|
|
12
|
-
constructor(
|
|
13
|
-
this.
|
|
14
|
-
this.specificOnboardingEndpoint = SPECIFIC_ONBOARDING_ENDPOINT;
|
|
8
|
+
constructor(fetchImpl: typeof fetch = fetch) {
|
|
9
|
+
this.fetchImpl = fetchImpl;
|
|
15
10
|
}
|
|
16
11
|
|
|
17
12
|
async fetchOnboardingJson(params: {
|
|
18
13
|
context: ClientContext;
|
|
19
|
-
|
|
14
|
+
apiToken: string;
|
|
20
15
|
}): Promise<FlowboardData> {
|
|
21
|
-
const { context,
|
|
22
|
-
const
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
payload.buildNumber = context.buildNumber;
|
|
29
|
-
payload.os = context.os;
|
|
30
|
-
payload.osVersion = context.osVersion;
|
|
31
|
-
payload.deviceType = context.deviceType;
|
|
32
|
-
payload.locale = context.locale;
|
|
33
|
-
payload.country = context.country;
|
|
34
|
-
|
|
35
|
-
payload.sdk = {
|
|
36
|
-
name: 'flowboard',
|
|
37
|
-
version: '1.0.0',
|
|
38
|
-
};
|
|
16
|
+
const { context, apiToken } = params;
|
|
17
|
+
const url = new URL(getResolveFlowUrl());
|
|
18
|
+
url.searchParams.set('installId', context.installId);
|
|
19
|
+
url.searchParams.set('appVersion', context.appVersion);
|
|
20
|
+
url.searchParams.set('locale', context.locale);
|
|
21
|
+
url.searchParams.set('country', context.country);
|
|
22
|
+
url.searchParams.set('os', context.os);
|
|
39
23
|
|
|
40
24
|
let response: Response;
|
|
41
25
|
try {
|
|
42
|
-
response = await
|
|
43
|
-
method: '
|
|
44
|
-
headers:
|
|
45
|
-
body: JSON.stringify(payload),
|
|
26
|
+
response = await this.fetchImpl(url.toString(), {
|
|
27
|
+
method: 'GET',
|
|
28
|
+
headers: this.createHeaders(apiToken),
|
|
46
29
|
});
|
|
47
30
|
} catch (error) {
|
|
48
31
|
throw new Error(`Failed to connect to resolver: ${String(error)}`);
|
|
@@ -56,6 +39,7 @@ export class ResolverService {
|
|
|
56
39
|
}
|
|
57
40
|
|
|
58
41
|
const body = (await response.json()) as FlowboardData;
|
|
42
|
+
this.normalizeFlowIdentifier(body);
|
|
59
43
|
this.applyHeaderMetadata(response, body);
|
|
60
44
|
|
|
61
45
|
return body;
|
|
@@ -64,18 +48,18 @@ export class ResolverService {
|
|
|
64
48
|
async fetchOnboardingById(params: {
|
|
65
49
|
onboardingId: string;
|
|
66
50
|
locale: string;
|
|
51
|
+
apiToken: string;
|
|
52
|
+
version?: FlowboardFlowVersion;
|
|
67
53
|
}): Promise<FlowboardData> {
|
|
68
|
-
const
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
};
|
|
54
|
+
const url = new URL(getFlowByIdUrl(params.onboardingId));
|
|
55
|
+
url.searchParams.set('locale', params.locale);
|
|
56
|
+
url.searchParams.set('version', params.version ?? 'live');
|
|
72
57
|
|
|
73
58
|
let response: Response;
|
|
74
59
|
try {
|
|
75
|
-
response = await
|
|
76
|
-
method: '
|
|
77
|
-
headers:
|
|
78
|
-
body: JSON.stringify(payload),
|
|
60
|
+
response = await this.fetchImpl(url.toString(), {
|
|
61
|
+
method: 'GET',
|
|
62
|
+
headers: this.createHeaders(params.apiToken),
|
|
79
63
|
});
|
|
80
64
|
} catch (error) {
|
|
81
65
|
throw new Error(`Failed to connect to resolver: ${String(error)}`);
|
|
@@ -89,11 +73,26 @@ export class ResolverService {
|
|
|
89
73
|
}
|
|
90
74
|
|
|
91
75
|
const body = (await response.json()) as FlowboardData;
|
|
76
|
+
this.normalizeFlowIdentifier(body);
|
|
92
77
|
this.applyHeaderMetadata(response, body);
|
|
93
78
|
|
|
94
79
|
return body;
|
|
95
80
|
}
|
|
96
81
|
|
|
82
|
+
private createHeaders(apiToken: string): Record<string, string> {
|
|
83
|
+
return {
|
|
84
|
+
Accept: '*/*',
|
|
85
|
+
Authorization: `Bearer ${apiToken}`,
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
private normalizeFlowIdentifier(body: FlowboardData): void {
|
|
90
|
+
const legacyFlowId = (body as FlowboardData & { flowId?: unknown }).flowId;
|
|
91
|
+
if (!body.flow_id && typeof legacyFlowId === 'string' && legacyFlowId) {
|
|
92
|
+
body.flow_id = legacyFlowId;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
97
96
|
private applyHeaderMetadata(response: Response, body: FlowboardData): void {
|
|
98
97
|
const flowId = response.headers.get('x-flowboard-flow-id');
|
|
99
98
|
if (flowId) body.flow_id = flowId;
|
package/src/index.tsx
CHANGED
package/src/types/flowboard.ts
CHANGED
|
@@ -13,6 +13,8 @@ export type FlowboardData = {
|
|
|
13
13
|
experiment_id?: string;
|
|
14
14
|
} & JsonMap;
|
|
15
15
|
|
|
16
|
+
export type FlowboardFlowVersion = 'live' | 'draft';
|
|
17
|
+
|
|
16
18
|
export type FlowboardContext = {
|
|
17
19
|
context?: any;
|
|
18
20
|
currentIndex: number;
|
|
@@ -53,4 +55,5 @@ export type FlowboardLaunchOptions = {
|
|
|
53
55
|
|
|
54
56
|
export type FlowboardLaunchByIdOptions = FlowboardLaunchOptions & {
|
|
55
57
|
locale?: string;
|
|
58
|
+
version?: FlowboardFlowVersion;
|
|
56
59
|
};
|
|
@@ -91,7 +91,15 @@ export function parseDimension(
|
|
|
91
91
|
return value;
|
|
92
92
|
}
|
|
93
93
|
if (typeof value === 'string') {
|
|
94
|
-
|
|
94
|
+
const normalized = value.trim().toLowerCase();
|
|
95
|
+
if (
|
|
96
|
+
normalized === 'infinity' ||
|
|
97
|
+
normalized === 'infinite' ||
|
|
98
|
+
normalized === 'double.infinity' ||
|
|
99
|
+
normalized === '+infinity'
|
|
100
|
+
) {
|
|
101
|
+
return Number.POSITIVE_INFINITY;
|
|
102
|
+
}
|
|
95
103
|
const numeric = Number(value);
|
|
96
104
|
if (!Number.isNaN(numeric)) {
|
|
97
105
|
if (zeroIsNull && numeric === 0) return undefined;
|