@rpcbase/client 0.283.0 → 0.285.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/RootProvider/index.d.ts +7 -0
- package/dist/RootProvider/index.d.ts.map +1 -0
- package/dist/RootProvider/index.js +8 -0
- package/dist/apiClient/getServerApiClient.d.ts +10 -0
- package/dist/apiClient/getServerApiClient.d.ts.map +1 -0
- package/dist/apiClient/getServerApiClient.js +97 -0
- package/dist/apiClient/index.d.ts +16 -0
- package/dist/apiClient/index.d.ts.map +1 -0
- package/dist/apiClient/index.js +50 -0
- package/dist/cleanupURL.d.ts +2 -0
- package/dist/cleanupURL.d.ts.map +1 -0
- package/dist/cleanupURL.js +37 -0
- package/dist/getFeatureFlag.d.ts +2 -0
- package/dist/getFeatureFlag.d.ts.map +1 -0
- package/dist/getFeatureFlag.js +43 -0
- package/dist/hooks/index.d.ts +3 -0
- package/dist/hooks/index.d.ts.map +1 -0
- package/dist/hooks/index.js +2 -0
- package/dist/hooks/useMediaQuery.d.ts +2 -0
- package/dist/hooks/useMediaQuery.d.ts.map +1 -0
- package/dist/hooks/useMediaQuery.js +24 -0
- package/dist/hooks/useThrottledMeasure.d.ts +11 -0
- package/dist/hooks/useThrottledMeasure.d.ts.map +1 -0
- package/dist/hooks/useThrottledMeasure.js +32 -0
- package/dist/hooks.d.ts +2 -0
- package/dist/hooks.d.ts.map +1 -0
- package/dist/hooks.js +1 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +5 -0
- package/dist/initWithRoutes.d.ts +8 -0
- package/dist/initWithRoutes.d.ts.map +1 -0
- package/dist/initWithRoutes.js +80 -0
- package/dist/instrument.d.ts +2 -0
- package/dist/instrument.d.ts.map +1 -0
- package/dist/instrument.js +29 -0
- package/dist/types.d.ts +8 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +1 -0
- package/dist/utils/useApplyScroll.d.ts +2 -0
- package/dist/utils/useApplyScroll.d.ts.map +1 -0
- package/dist/utils/useApplyScroll.js +138 -0
- package/package.json +27 -7
- package/hooks.ts +0 -1
- package/instrument.ts +0 -32
- package/src/RootProvider/index.tsx +0 -15
- package/src/apiClient/getServerApiClient.ts +0 -133
- package/src/apiClient/index.ts +0 -86
- package/src/cleanupURL.ts +0 -46
- package/src/getFeatureFlag.ts +0 -58
- package/src/hooks/index.ts +0 -2
- package/src/hooks/useMediaQuery.ts +0 -31
- package/src/hooks/useThrottledMeasure.ts +0 -49
- package/src/index.ts +0 -5
- package/src/initWithRoutes.tsx +0 -115
- package/src/types.ts +0 -8
- package/src/utils/useApplyScroll.ts +0 -169
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
import { useCallback, useEffect, useRef } from "react";
|
|
2
|
+
import { useLocation } from "@rpcbase/router";
|
|
3
|
+
function throttle(callback, limit) {
|
|
4
|
+
let wait = false;
|
|
5
|
+
return (...args) => {
|
|
6
|
+
if (!wait) {
|
|
7
|
+
callback(...args);
|
|
8
|
+
wait = true;
|
|
9
|
+
setTimeout(() => {
|
|
10
|
+
wait = false;
|
|
11
|
+
}, limit);
|
|
12
|
+
}
|
|
13
|
+
};
|
|
14
|
+
}
|
|
15
|
+
export function useApplyScroll() {
|
|
16
|
+
const location = useLocation();
|
|
17
|
+
const previousPathRef = useRef(location.pathname);
|
|
18
|
+
const isScrollingProgrammatically = useRef(false);
|
|
19
|
+
const scrollTimeoutRef = useRef(null);
|
|
20
|
+
const lastAppliedHashRef = useRef("");
|
|
21
|
+
useEffect(() => {
|
|
22
|
+
if (typeof window !== "undefined") {
|
|
23
|
+
lastAppliedHashRef.current = window.location.hash || "";
|
|
24
|
+
}
|
|
25
|
+
}, []);
|
|
26
|
+
useEffect(() => {
|
|
27
|
+
lastAppliedHashRef.current = location.hash || "";
|
|
28
|
+
}, [location.hash]);
|
|
29
|
+
const replaceHashSilently = useCallback((hash) => {
|
|
30
|
+
if (typeof window === "undefined") {
|
|
31
|
+
return;
|
|
32
|
+
}
|
|
33
|
+
if (lastAppliedHashRef.current === hash) {
|
|
34
|
+
return;
|
|
35
|
+
}
|
|
36
|
+
const base = `${window.location.pathname}${window.location.search}`;
|
|
37
|
+
window.history.replaceState(window.history.state, "", `${base}${hash}`);
|
|
38
|
+
lastAppliedHashRef.current = hash;
|
|
39
|
+
}, []);
|
|
40
|
+
const markProgrammaticScroll = useCallback(() => {
|
|
41
|
+
isScrollingProgrammatically.current = true;
|
|
42
|
+
if (scrollTimeoutRef.current) {
|
|
43
|
+
clearTimeout(scrollTimeoutRef.current);
|
|
44
|
+
}
|
|
45
|
+
scrollTimeoutRef.current = setTimeout(() => {
|
|
46
|
+
isScrollingProgrammatically.current = false;
|
|
47
|
+
}, 1000);
|
|
48
|
+
}, []);
|
|
49
|
+
useEffect(() => {
|
|
50
|
+
const pathChanged = previousPathRef.current !== location.pathname;
|
|
51
|
+
if (pathChanged) {
|
|
52
|
+
previousPathRef.current = location.pathname;
|
|
53
|
+
if (!location.hash) {
|
|
54
|
+
window.scrollTo({ top: 0, left: 0, behavior: "auto" });
|
|
55
|
+
return;
|
|
56
|
+
}
|
|
57
|
+
setTimeout(() => {
|
|
58
|
+
const id = location.hash.substring(1);
|
|
59
|
+
const element = document.getElementById(id);
|
|
60
|
+
if (element) {
|
|
61
|
+
markProgrammaticScroll();
|
|
62
|
+
element.scrollIntoView({ behavior: "smooth" });
|
|
63
|
+
}
|
|
64
|
+
}, 100);
|
|
65
|
+
return;
|
|
66
|
+
}
|
|
67
|
+
if (!location.hash) {
|
|
68
|
+
return;
|
|
69
|
+
}
|
|
70
|
+
const id = location.hash.substring(1);
|
|
71
|
+
const element = document.getElementById(id);
|
|
72
|
+
if (element) {
|
|
73
|
+
markProgrammaticScroll();
|
|
74
|
+
element.scrollIntoView({ behavior: "smooth" });
|
|
75
|
+
}
|
|
76
|
+
}, [location.hash, location.pathname, markProgrammaticScroll]);
|
|
77
|
+
useEffect(() => {
|
|
78
|
+
if (typeof window === "undefined") {
|
|
79
|
+
return;
|
|
80
|
+
}
|
|
81
|
+
const handleScroll = throttle(() => {
|
|
82
|
+
if (isScrollingProgrammatically.current) {
|
|
83
|
+
return;
|
|
84
|
+
}
|
|
85
|
+
const sections = Array.from(document.querySelectorAll("section[id]"));
|
|
86
|
+
if (sections.length === 0) {
|
|
87
|
+
replaceHashSilently("");
|
|
88
|
+
return;
|
|
89
|
+
}
|
|
90
|
+
const scrollPosition = window.scrollY;
|
|
91
|
+
const viewportHeight = window.innerHeight;
|
|
92
|
+
const checkPoint = scrollPosition + viewportHeight / 3;
|
|
93
|
+
let activeSectionId = null;
|
|
94
|
+
for (const section of sections) {
|
|
95
|
+
if (section.offsetTop <= checkPoint &&
|
|
96
|
+
section.offsetTop + section.offsetHeight > checkPoint) {
|
|
97
|
+
activeSectionId = section.id;
|
|
98
|
+
break;
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
const newHash = activeSectionId ? `#${activeSectionId}` : "";
|
|
102
|
+
replaceHashSilently(newHash);
|
|
103
|
+
}, 150);
|
|
104
|
+
document.addEventListener("scroll", handleScroll);
|
|
105
|
+
return () => {
|
|
106
|
+
document.removeEventListener("scroll", handleScroll);
|
|
107
|
+
if (scrollTimeoutRef.current) {
|
|
108
|
+
clearTimeout(scrollTimeoutRef.current);
|
|
109
|
+
scrollTimeoutRef.current = null;
|
|
110
|
+
}
|
|
111
|
+
};
|
|
112
|
+
}, [replaceHashSilently]);
|
|
113
|
+
useEffect(() => {
|
|
114
|
+
const handleClick = (event) => {
|
|
115
|
+
const target = event.target;
|
|
116
|
+
const link = target?.closest("a");
|
|
117
|
+
const currentHash = typeof window !== "undefined"
|
|
118
|
+
? window.location.hash
|
|
119
|
+
: location.hash || "";
|
|
120
|
+
if (!link ||
|
|
121
|
+
!link.hash ||
|
|
122
|
+
link.pathname !== location.pathname ||
|
|
123
|
+
link.hash !== currentHash) {
|
|
124
|
+
return;
|
|
125
|
+
}
|
|
126
|
+
const id = link.hash.substring(1);
|
|
127
|
+
const element = document.getElementById(id);
|
|
128
|
+
if (element) {
|
|
129
|
+
event.preventDefault();
|
|
130
|
+
event.stopPropagation();
|
|
131
|
+
markProgrammaticScroll();
|
|
132
|
+
element.scrollIntoView({ behavior: "smooth" });
|
|
133
|
+
}
|
|
134
|
+
};
|
|
135
|
+
document.addEventListener("click", handleClick, true);
|
|
136
|
+
return () => document.removeEventListener("click", handleClick, true);
|
|
137
|
+
}, [location.hash, location.pathname, markProgrammaticScroll]);
|
|
138
|
+
}
|
package/package.json
CHANGED
|
@@ -1,20 +1,40 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rpcbase/client",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.285.0",
|
|
4
4
|
"type": "module",
|
|
5
|
-
"
|
|
5
|
+
"files": [
|
|
6
|
+
"dist"
|
|
7
|
+
],
|
|
8
|
+
"main": "./dist/index.js",
|
|
9
|
+
"types": "./dist/index.d.ts",
|
|
6
10
|
"scripts": {
|
|
7
|
-
"build": "
|
|
11
|
+
"build": "wireit",
|
|
8
12
|
"release": "wireit"
|
|
9
13
|
},
|
|
10
14
|
"wireit": {
|
|
15
|
+
"build": {
|
|
16
|
+
"command": "node ../../scripts/build-package.js client",
|
|
17
|
+
"files": [
|
|
18
|
+
"src/**/*",
|
|
19
|
+
"hooks.ts",
|
|
20
|
+
"instrument.ts",
|
|
21
|
+
"../../tsconfig.json",
|
|
22
|
+
"../../tsconfig.base.json",
|
|
23
|
+
"../../scripts/build-package.js"
|
|
24
|
+
],
|
|
25
|
+
"output": [
|
|
26
|
+
"dist/"
|
|
27
|
+
]
|
|
28
|
+
},
|
|
11
29
|
"release": {
|
|
12
30
|
"command": "../../scripts/publish.js",
|
|
13
|
-
"dependencies": [
|
|
31
|
+
"dependencies": [
|
|
32
|
+
"build"
|
|
33
|
+
],
|
|
14
34
|
"files": [
|
|
15
35
|
"package.json",
|
|
16
|
-
"
|
|
17
|
-
"
|
|
36
|
+
"hooks.ts",
|
|
37
|
+
"instrument.ts",
|
|
18
38
|
"src/**/*"
|
|
19
39
|
],
|
|
20
40
|
"output": [],
|
|
@@ -27,7 +47,7 @@
|
|
|
27
47
|
},
|
|
28
48
|
"peerDependencies": {},
|
|
29
49
|
"dependencies": {
|
|
30
|
-
"axios": "1.
|
|
50
|
+
"axios": "1.13.2",
|
|
31
51
|
"fast-equals": "5.2.2",
|
|
32
52
|
"lodash": "4.17.21",
|
|
33
53
|
"react-use": "17.6.0"
|
package/hooks.ts
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export * from "./src/hooks"
|
package/instrument.ts
DELETED
|
@@ -1,32 +0,0 @@
|
|
|
1
|
-
import posthog from "posthog-js"
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
const isProduction = import.meta.env.MODE === "production"
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
if (isProduction) {
|
|
8
|
-
if (import.meta.env.RB_PUBLIC_POSTHOG_KEY) {
|
|
9
|
-
posthog.init(import.meta.env.RB_PUBLIC_POSTHOG_KEY, {
|
|
10
|
-
api_host: "/ingest",
|
|
11
|
-
ui_host: "https://eu.posthog.com",
|
|
12
|
-
person_profiles: "always",
|
|
13
|
-
session_recording: {
|
|
14
|
-
maskAllInputs: false
|
|
15
|
-
},
|
|
16
|
-
capture_pageview: isProduction,
|
|
17
|
-
capture_exceptions: {
|
|
18
|
-
capture_unhandled_errors: true,
|
|
19
|
-
capture_unhandled_rejections: true,
|
|
20
|
-
capture_console_errors: true,
|
|
21
|
-
},
|
|
22
|
-
autocapture: isProduction,
|
|
23
|
-
disable_session_recording: !isProduction,
|
|
24
|
-
})
|
|
25
|
-
} else {
|
|
26
|
-
console.warn("missing POSTHOG_KEY")
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
} else {
|
|
31
|
-
console.info("ℹ️ Not initializing posthog in development")
|
|
32
|
-
}
|
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
import posthog from "posthog-js"
|
|
2
|
-
import { PostHogProvider } from "posthog-js/react"
|
|
3
|
-
|
|
4
|
-
import { useApplyScroll } from "../utils/useApplyScroll"
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
export const RootProvider = ({children}) => {
|
|
8
|
-
useApplyScroll()
|
|
9
|
-
|
|
10
|
-
return (
|
|
11
|
-
<PostHogProvider client={posthog}>
|
|
12
|
-
{children}
|
|
13
|
-
</PostHogProvider>
|
|
14
|
-
)
|
|
15
|
-
}
|
|
@@ -1,133 +0,0 @@
|
|
|
1
|
-
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
2
|
-
import { Application, IRouter, Request, Response } from "express"
|
|
3
|
-
|
|
4
|
-
// import { Ctx } from "@rpcbase/api"
|
|
5
|
-
type Ctx = any
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
export const getServerApiClient = async(app: Application) => {
|
|
9
|
-
const callRoute = async <TResponse = Record<string, unknown>>(
|
|
10
|
-
app: Application,
|
|
11
|
-
method: string,
|
|
12
|
-
path: string,
|
|
13
|
-
req: Partial<Request>,
|
|
14
|
-
res: Partial<Response>
|
|
15
|
-
): Promise<TResponse> => {
|
|
16
|
-
return new Promise((resolve, reject) => {
|
|
17
|
-
let isEnded = false
|
|
18
|
-
|
|
19
|
-
const mockReq = {
|
|
20
|
-
...req,
|
|
21
|
-
method: method.toUpperCase(),
|
|
22
|
-
url: path
|
|
23
|
-
} as Request
|
|
24
|
-
|
|
25
|
-
const mockRes = {
|
|
26
|
-
...res,
|
|
27
|
-
json: (data: any) => {
|
|
28
|
-
if (!isEnded) {
|
|
29
|
-
isEnded = true
|
|
30
|
-
resolve(data)
|
|
31
|
-
}
|
|
32
|
-
},
|
|
33
|
-
status: (statusCode: number) => {
|
|
34
|
-
console.log("Status:", statusCode)
|
|
35
|
-
return mockRes
|
|
36
|
-
},
|
|
37
|
-
} as Response
|
|
38
|
-
|
|
39
|
-
const routerStack: any[] = (app.router as unknown as IRouter).stack
|
|
40
|
-
|
|
41
|
-
const firstApiMiddlewareIndex = routerStack.findIndex((layer) => layer.name === "__FIRST_API_MIDDLEWARE__")
|
|
42
|
-
if (!(firstApiMiddlewareIndex > -1)) {
|
|
43
|
-
throw new Error("middleware: __FIRST_API_MIDDLEWARE__ was not found in stack")
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
const apiStack = routerStack.slice(firstApiMiddlewareIndex + 1)
|
|
47
|
-
|
|
48
|
-
const processLayer = async (index: number) => {
|
|
49
|
-
if (index >= apiStack.length || isEnded) return
|
|
50
|
-
|
|
51
|
-
const layer = apiStack[index]
|
|
52
|
-
|
|
53
|
-
const isNonMatchingLayer = !layer.match(path)
|
|
54
|
-
if (isNonMatchingLayer) {
|
|
55
|
-
// console.log("not machthing route:", path, "to layer:", index, layer.name, layer, "reason: ", {isNonMatchingLayer})
|
|
56
|
-
await processLayer(index + 1)
|
|
57
|
-
return
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
const runHandler = async(handler: any) => new Promise<void>((resolveMiddleware, rejectMiddleware) => {
|
|
61
|
-
handler(mockReq, mockRes, (err?: any) => {
|
|
62
|
-
if (err) {
|
|
63
|
-
console.error("Middleware error:", err)
|
|
64
|
-
if (!isEnded) {
|
|
65
|
-
isEnded = true
|
|
66
|
-
rejectMiddleware(err)
|
|
67
|
-
}
|
|
68
|
-
return
|
|
69
|
-
}
|
|
70
|
-
resolveMiddleware()
|
|
71
|
-
})
|
|
72
|
-
})
|
|
73
|
-
|
|
74
|
-
if (layer.route) {
|
|
75
|
-
if (!layer.route.methods[method.toLowerCase()]) {
|
|
76
|
-
// console.log("not machthing route:", path, "to route layer:", index, layer.name, layer, "reason: method not matching")
|
|
77
|
-
await processLayer(index + 1)
|
|
78
|
-
return
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
if (layer.route.stack.length !== 1) {
|
|
82
|
-
throw new Error(`expected only one handler per route for route: ${layer.route.path}`)
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
await runHandler(layer.route.stack[0].handle)
|
|
86
|
-
|
|
87
|
-
} else {
|
|
88
|
-
await runHandler(layer.handle)
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
if (!isEnded) {
|
|
92
|
-
await processLayer(index + 1)
|
|
93
|
-
}
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
// AWAIT ??
|
|
97
|
-
processLayer(0)
|
|
98
|
-
|
|
99
|
-
// Set a timeout to prevent hanging
|
|
100
|
-
setTimeout(() => {
|
|
101
|
-
if (!isEnded) {
|
|
102
|
-
reject("Route handler timed out")
|
|
103
|
-
}
|
|
104
|
-
}, 30000)
|
|
105
|
-
})
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
const createMethod = (method: string) => {
|
|
109
|
-
return async <TResponse = Record<string, unknown>>(
|
|
110
|
-
path: string,
|
|
111
|
-
payload: Record<string, unknown>,
|
|
112
|
-
ctx?: Ctx,
|
|
113
|
-
): Promise<TResponse> => {
|
|
114
|
-
if (!ctx) {
|
|
115
|
-
throw new Error("Context must be provided in SSR mode")
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
ctx.req.body = payload
|
|
119
|
-
return callRoute<TResponse>(app, method, path, ctx.req, ctx.res)
|
|
120
|
-
}
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
const apiClient = {
|
|
124
|
-
get: createMethod("get"),
|
|
125
|
-
post: createMethod("post"),
|
|
126
|
-
put: createMethod("put"),
|
|
127
|
-
delete: createMethod("delete")
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
return apiClient
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
export default getServerApiClient
|
package/src/apiClient/index.ts
DELETED
|
@@ -1,86 +0,0 @@
|
|
|
1
|
-
import type { Application } from "express"
|
|
2
|
-
|
|
3
|
-
// import { Ctx } from "@rpcbase/api"
|
|
4
|
-
type Ctx = any
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
type ServerArgs = {
|
|
8
|
-
app: Application;
|
|
9
|
-
};
|
|
10
|
-
|
|
11
|
-
type PayloadNotCtx = Record<string, unknown> & {
|
|
12
|
-
[P in keyof Ctx]?: never;
|
|
13
|
-
};
|
|
14
|
-
|
|
15
|
-
type ApiClientMethod = <TResponse = Record<string, unknown>>(
|
|
16
|
-
path: string,
|
|
17
|
-
payload: PayloadNotCtx,
|
|
18
|
-
ctx?: Ctx,
|
|
19
|
-
) => Promise<TResponse>;
|
|
20
|
-
|
|
21
|
-
export type HttpMethod = "get" | "put" | "post" | "delete";
|
|
22
|
-
type MethodRecord<T> = Record<HttpMethod, T>;
|
|
23
|
-
|
|
24
|
-
export type ApiClient = MethodRecord<ApiClientMethod>;
|
|
25
|
-
|
|
26
|
-
let apiClient: ApiClient
|
|
27
|
-
|
|
28
|
-
export const initApiClient = async (args?: ServerArgs) => {
|
|
29
|
-
// @ts-expect-error -- Suppress TS2339: Property 'env' does not exist on type 'ImportMeta'
|
|
30
|
-
if (import.meta.env.SSR) {
|
|
31
|
-
if (!args) {
|
|
32
|
-
throw new Error("Server args must be provided in SSR mode")
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
const { getServerApiClient } = await import("./getServerApiClient")
|
|
36
|
-
|
|
37
|
-
apiClient = await getServerApiClient(args.app)
|
|
38
|
-
} else {
|
|
39
|
-
const axios = (await import("axios")).default
|
|
40
|
-
|
|
41
|
-
const axiosClient = axios.create({
|
|
42
|
-
baseURL: "/",
|
|
43
|
-
withCredentials: true,
|
|
44
|
-
headers: {
|
|
45
|
-
"Content-Type": "application/json",
|
|
46
|
-
},
|
|
47
|
-
})
|
|
48
|
-
|
|
49
|
-
const createMethod = (method: string): ApiClientMethod => {
|
|
50
|
-
return async <TResponse = Record<string, unknown>>(
|
|
51
|
-
path: string,
|
|
52
|
-
payload: PayloadNotCtx,
|
|
53
|
-
_ctx?: Ctx,
|
|
54
|
-
): Promise<TResponse> => {
|
|
55
|
-
const config = {
|
|
56
|
-
method,
|
|
57
|
-
url: path,
|
|
58
|
-
data: payload,
|
|
59
|
-
headers: {
|
|
60
|
-
// ...(typeof ctxOrPath !== 'string' && {
|
|
61
|
-
// // 'X-Custom-Header': ctxOrPath.someHeaderValue,
|
|
62
|
-
// // ...ctxOrPath.additionalHeaders,
|
|
63
|
-
// }),
|
|
64
|
-
},
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
try {
|
|
68
|
-
const response = await axiosClient(config)
|
|
69
|
-
return response.data
|
|
70
|
-
} catch (error) {
|
|
71
|
-
console.log("AXIOS API ERROR", error)
|
|
72
|
-
throw error
|
|
73
|
-
}
|
|
74
|
-
}
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
apiClient = {
|
|
78
|
-
get: createMethod("get"),
|
|
79
|
-
put: createMethod("put"),
|
|
80
|
-
post: createMethod("post"),
|
|
81
|
-
delete: createMethod("delete"),
|
|
82
|
-
}
|
|
83
|
-
}
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
export { apiClient }
|
package/src/cleanupURL.ts
DELETED
|
@@ -1,46 +0,0 @@
|
|
|
1
|
-
const CLEANUP_WAIT_DELAY = 1000
|
|
2
|
-
|
|
3
|
-
// Function to clean UTM params while preserving others
|
|
4
|
-
export const cleanupURL = () => {
|
|
5
|
-
if (import.meta.env.SSR) {
|
|
6
|
-
return
|
|
7
|
-
}
|
|
8
|
-
|
|
9
|
-
const runCleanup = () => {
|
|
10
|
-
// Add a small delay before running cleanupURL
|
|
11
|
-
setTimeout(() => {
|
|
12
|
-
const url = new URL(window.location.href)
|
|
13
|
-
const params = new URLSearchParams(url.search)
|
|
14
|
-
|
|
15
|
-
// Get all current query parameter keys
|
|
16
|
-
const paramKeys = Array.from(params.keys())
|
|
17
|
-
|
|
18
|
-
// Remove any parameter starting with 'utm_'
|
|
19
|
-
paramKeys.forEach((key) => {
|
|
20
|
-
if (key.startsWith("utm_")) {
|
|
21
|
-
params.delete(key)
|
|
22
|
-
}
|
|
23
|
-
})
|
|
24
|
-
|
|
25
|
-
// Build the new URL: keep pathname and append remaining query params (if any)
|
|
26
|
-
const cleanUrl =
|
|
27
|
-
url.pathname +
|
|
28
|
-
(params.toString() ? "?" + params.toString() : "") +
|
|
29
|
-
url.hash
|
|
30
|
-
|
|
31
|
-
// Update the browser URL without reloading
|
|
32
|
-
window.history.replaceState({}, document.title, cleanUrl)
|
|
33
|
-
}, CLEANUP_WAIT_DELAY)
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
// If DOM is already loaded, schedule the cleanup
|
|
37
|
-
if (
|
|
38
|
-
document.readyState === "complete" ||
|
|
39
|
-
document.readyState === "interactive"
|
|
40
|
-
) {
|
|
41
|
-
runCleanup()
|
|
42
|
-
} else {
|
|
43
|
-
// Otherwise wait for the DOM content to be loaded
|
|
44
|
-
document.addEventListener("DOMContentLoaded", runCleanup, { once: true })
|
|
45
|
-
}
|
|
46
|
-
}
|
package/src/getFeatureFlag.ts
DELETED
|
@@ -1,58 +0,0 @@
|
|
|
1
|
-
import _snakeCase from "lodash/snakeCase"
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
export const getFeatureFlag = async (
|
|
5
|
-
flag: string,
|
|
6
|
-
): Promise<boolean | string | undefined> => {
|
|
7
|
-
const envKey = `RB_PUBLIC_FLAG_${_snakeCase(flag).toUpperCase()}`
|
|
8
|
-
|
|
9
|
-
if (import.meta.env.SSR) {
|
|
10
|
-
if (process.env[envKey] !== undefined) {
|
|
11
|
-
return process.env[envKey]
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
const startTime = performance.now()
|
|
15
|
-
const { PostHog } = await import("posthog-node")
|
|
16
|
-
const client = new PostHog(
|
|
17
|
-
process.env.RB_PUBLIC_POSTHOG_KEY!,
|
|
18
|
-
{ host: "https://eu.i.posthog.com" },
|
|
19
|
-
)
|
|
20
|
-
|
|
21
|
-
const distinctId = "server"
|
|
22
|
-
console.log("TODO: NYI server side feature flags client distinctId")
|
|
23
|
-
const value = await client.getFeatureFlag(flag, distinctId)
|
|
24
|
-
const endTime = performance.now()
|
|
25
|
-
console.log(`SSR: Feature flag "${flag}" loaded in ${(endTime - startTime).toFixed(2)}ms`)
|
|
26
|
-
|
|
27
|
-
return value
|
|
28
|
-
} else {
|
|
29
|
-
if (import.meta.env[envKey] !== undefined) {
|
|
30
|
-
return import.meta.env[envKey]
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
const startTime = performance.now()
|
|
34
|
-
const { posthog } = await import("posthog-js")
|
|
35
|
-
|
|
36
|
-
let hasLoadedFeatureFlags = false
|
|
37
|
-
|
|
38
|
-
function waitForFeatureFlags(): Promise<void> {
|
|
39
|
-
return new Promise((resolve) => {
|
|
40
|
-
if (hasLoadedFeatureFlags) {
|
|
41
|
-
resolve()
|
|
42
|
-
} else {
|
|
43
|
-
posthog.onFeatureFlags(() => {
|
|
44
|
-
hasLoadedFeatureFlags = true
|
|
45
|
-
resolve()
|
|
46
|
-
})
|
|
47
|
-
}
|
|
48
|
-
})
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
await waitForFeatureFlags()
|
|
52
|
-
const endTime = performance.now()
|
|
53
|
-
|
|
54
|
-
console.log(`Client: Feature flag "${flag}" loaded in ${(endTime - startTime).toFixed(2)}ms`)
|
|
55
|
-
|
|
56
|
-
return posthog.getFeatureFlag(flag)
|
|
57
|
-
}
|
|
58
|
-
}
|
package/src/hooks/index.ts
DELETED
|
@@ -1,31 +0,0 @@
|
|
|
1
|
-
import { useSyncExternalStore } from "react"
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
const emptyUnsubscribe = () => {}
|
|
5
|
-
|
|
6
|
-
export const useMediaQuery = (query: string): boolean => {
|
|
7
|
-
const isServer = typeof window === "undefined"
|
|
8
|
-
|
|
9
|
-
const subscribe = (callback: () => void) => {
|
|
10
|
-
if (isServer) return emptyUnsubscribe
|
|
11
|
-
|
|
12
|
-
const mql = window.matchMedia(query)
|
|
13
|
-
|
|
14
|
-
// Modern browsers
|
|
15
|
-
if (mql.addEventListener) {
|
|
16
|
-
mql.addEventListener("change", callback)
|
|
17
|
-
return () => mql.removeEventListener("change", callback)
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
// Legacy fallback
|
|
21
|
-
mql.addListener(callback)
|
|
22
|
-
return () => mql.removeListener(callback)
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
const getSnapshot = () => {
|
|
26
|
-
if (isServer) return false
|
|
27
|
-
return window.matchMedia(query).matches
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
return useSyncExternalStore(subscribe, getSnapshot, () => false)
|
|
31
|
-
}
|
|
@@ -1,49 +0,0 @@
|
|
|
1
|
-
import {useState, useEffect, useRef, useCallback} from "react"
|
|
2
|
-
import useMeasure from "react-use/lib/useMeasure"
|
|
3
|
-
import _throttle from "lodash/throttle"
|
|
4
|
-
import { deepEqual } from "fast-equals"
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
const useMeasureHook = useMeasure.default || useMeasure
|
|
8
|
-
|
|
9
|
-
const DEFAULT_THROTTLE_TIME = 16
|
|
10
|
-
|
|
11
|
-
export const useThrottledMeasure = (throttleDuration = DEFAULT_THROTTLE_TIME) => {
|
|
12
|
-
const hasInitialMeasure = useRef(false)
|
|
13
|
-
|
|
14
|
-
const [ref, measuredRect] = useMeasureHook()
|
|
15
|
-
const [rect, setRect] = useState(() => {
|
|
16
|
-
return {x: 0, y: 0, width: 0, height: 0, top: 0, left: 0, bottom: 0, right: 0}
|
|
17
|
-
})
|
|
18
|
-
|
|
19
|
-
const throttledSetRect = useCallback(
|
|
20
|
-
_throttle(
|
|
21
|
-
(newRect) => {
|
|
22
|
-
setRect((current) => {
|
|
23
|
-
return deepEqual(current, newRect) ? current : newRect
|
|
24
|
-
})
|
|
25
|
-
},
|
|
26
|
-
throttleDuration,
|
|
27
|
-
{leading: true, trailing: true},
|
|
28
|
-
),
|
|
29
|
-
[throttleDuration],
|
|
30
|
-
)
|
|
31
|
-
|
|
32
|
-
useEffect(() => {
|
|
33
|
-
if (measuredRect.width > 0 && !hasInitialMeasure.current) {
|
|
34
|
-
hasInitialMeasure.current = true
|
|
35
|
-
setRect((current) => {
|
|
36
|
-
return deepEqual(current, measuredRect) ? current : measuredRect
|
|
37
|
-
})
|
|
38
|
-
return
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
throttledSetRect(measuredRect)
|
|
42
|
-
|
|
43
|
-
return () => {
|
|
44
|
-
throttledSetRect.cancel()
|
|
45
|
-
}
|
|
46
|
-
}, [measuredRect, throttledSetRect])
|
|
47
|
-
|
|
48
|
-
return [ref, rect]
|
|
49
|
-
}
|