revine 1.4.1 → 1.5.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/README.md CHANGED
@@ -53,6 +53,48 @@ src/pages/about.tsx → /about
53
53
 
54
54
  src/pages/blog/[slug].tsx → /blog/:slug
55
55
 
56
+ ## Data Fetching & Caching
57
+
58
+ Revine includes built-in support for cached API calls, allowing you to easily store and reuse server responses.
59
+
60
+ ### `revineFetch`
61
+
62
+ A wrapper around the native `fetch` API with caching capabilities.
63
+
64
+ ```typescript
65
+ import { revineFetch } from "revine";
66
+
67
+ const data = await revineFetch("https://api.example.com/data", {
68
+ cacheTTL: 60000, // Cache for 1 minute (in ms)
69
+ persist: true // Optional: Persist to localStorage
70
+ });
71
+ ```
72
+
73
+ ### `useFetch` Hook
74
+
75
+ A React hook for making cached API calls within components.
76
+
77
+ ```tsx
78
+ import { useFetch } from "revine";
79
+
80
+ function MyComponent() {
81
+ const { data, loading, error, revalidate } = useFetch("https://api.example.com/data", {
82
+ cacheTTL: 300000, // 5 minutes
83
+ persist: true
84
+ });
85
+
86
+ if (loading) return <div>Loading...</div>;
87
+ if (error) return <div>Error: {error.message}</div>;
88
+
89
+ return (
90
+ <div>
91
+ <pre>{JSON.stringify(data, null, 2)}</pre>
92
+ <button onClick={() => revalidate()}>Refresh Data</button>
93
+ </div>
94
+ );
95
+ }
96
+ ```
97
+
56
98
  ## Contributing
57
99
 
58
100
  ### Clone repository
package/dist/client.d.ts CHANGED
@@ -6,6 +6,9 @@ export type { LinkProps } from "./components/Link.js";
6
6
  export { NavLink } from "./components/NavLink.js";
7
7
  export type { NavLinkProps } from "./components/NavLink.js";
8
8
  export { useRouter } from "./hooks/useRouter.js";
9
+ export { useFetch } from "./hooks/useFetch.js";
10
+ export { revineFetch } from "./runtime/fetch.js";
11
+ export type { RevineFetchOptions } from "./runtime/fetch.js";
9
12
  export { defineConfig } from "./runtime/defineConfig.js";
10
13
  export { env, envAll } from "./runtime/env.js";
11
14
  export { middlewareResponse } from "./runtime/middleware.js";
@@ -1 +1 @@
1
- {"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,MAAM,EACN,cAAc,EACd,WAAW,EACX,WAAW,EACX,SAAS,EACT,eAAe,EAChB,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EAAE,KAAK,EAAE,MAAM,uBAAuB,CAAC;AAC9C,YAAY,EAAE,UAAU,EAAE,MAAM,uBAAuB,CAAC;AACxD,OAAO,EAAE,IAAI,EAAE,MAAM,sBAAsB,CAAC;AAC5C,YAAY,EAAE,SAAS,EAAE,MAAM,sBAAsB,CAAC;AACtD,OAAO,EAAE,OAAO,EAAE,MAAM,yBAAyB,CAAC;AAClD,YAAY,EAAE,YAAY,EAAE,MAAM,yBAAyB,CAAC;AAC5D,OAAO,EAAE,SAAS,EAAE,MAAM,sBAAsB,CAAC;AACjD,OAAO,EAAE,YAAY,EAAE,MAAM,2BAA2B,CAAC;AACzD,OAAO,EAAE,GAAG,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAC;AAC/C,OAAO,EAAE,kBAAkB,EAAE,MAAM,yBAAyB,CAAC;AAC7D,YAAY,EAAE,gBAAgB,EAAE,YAAY,EAAE,iBAAiB,EAAE,kBAAkB,EAAE,MAAM,yBAAyB,CAAC;AACrH,YAAY,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC"}
1
+ {"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,MAAM,EACN,cAAc,EACd,WAAW,EACX,WAAW,EACX,SAAS,EACT,eAAe,EAChB,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EAAE,KAAK,EAAE,MAAM,uBAAuB,CAAC;AAC9C,YAAY,EAAE,UAAU,EAAE,MAAM,uBAAuB,CAAC;AACxD,OAAO,EAAE,IAAI,EAAE,MAAM,sBAAsB,CAAC;AAC5C,YAAY,EAAE,SAAS,EAAE,MAAM,sBAAsB,CAAC;AACtD,OAAO,EAAE,OAAO,EAAE,MAAM,yBAAyB,CAAC;AAClD,YAAY,EAAE,YAAY,EAAE,MAAM,yBAAyB,CAAC;AAC5D,OAAO,EAAE,SAAS,EAAE,MAAM,sBAAsB,CAAC;AACjD,OAAO,EAAE,QAAQ,EAAE,MAAM,qBAAqB,CAAC;AAC/C,OAAO,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AACjD,YAAY,EAAE,kBAAkB,EAAE,MAAM,oBAAoB,CAAC;AAC7D,OAAO,EAAE,YAAY,EAAE,MAAM,2BAA2B,CAAC;AACzD,OAAO,EAAE,GAAG,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAC;AAC/C,OAAO,EAAE,kBAAkB,EAAE,MAAM,yBAAyB,CAAC;AAC7D,YAAY,EAAE,gBAAgB,EAAE,YAAY,EAAE,iBAAiB,EAAE,kBAAkB,EAAE,MAAM,yBAAyB,CAAC;AACrH,YAAY,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC"}
package/dist/client.js CHANGED
@@ -3,6 +3,8 @@ export { Image } from "./components/Image.js";
3
3
  export { Link } from "./components/Link.js";
4
4
  export { NavLink } from "./components/NavLink.js";
5
5
  export { useRouter } from "./hooks/useRouter.js";
6
+ export { useFetch } from "./hooks/useFetch.js";
7
+ export { revineFetch } from "./runtime/fetch.js";
6
8
  export { defineConfig } from "./runtime/defineConfig.js";
7
9
  export { env, envAll } from "./runtime/env.js";
8
10
  export { middlewareResponse } from "./runtime/middleware.js";
@@ -0,0 +1,14 @@
1
+ import { RevineFetchOptions } from "../runtime/fetch.js";
2
+ export interface UseFetchResult<T> {
3
+ data: T | null;
4
+ loading: boolean;
5
+ error: Error | null;
6
+ revalidate: () => Promise<void>;
7
+ }
8
+ /**
9
+ * A React hook for making cached API calls.
10
+ * @param url The URL to fetch
11
+ * @param options Fetch and cache options
12
+ */
13
+ export declare function useFetch<T = any>(url: string, options?: RevineFetchOptions): UseFetchResult<T>;
14
+ //# sourceMappingURL=useFetch.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useFetch.d.ts","sourceRoot":"","sources":["../../src/hooks/useFetch.ts"],"names":[],"mappings":"AACA,OAAO,EAAe,kBAAkB,EAAE,MAAM,qBAAqB,CAAC;AAEtE,MAAM,WAAW,cAAc,CAAC,CAAC;IAC/B,IAAI,EAAE,CAAC,GAAG,IAAI,CAAC;IACf,OAAO,EAAE,OAAO,CAAC;IACjB,KAAK,EAAE,KAAK,GAAG,IAAI,CAAC;IACpB,UAAU,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;CACjC;AAED;;;;GAIG;AACH,wBAAgB,QAAQ,CAAC,CAAC,GAAG,GAAG,EAC9B,GAAG,EAAE,MAAM,EACX,OAAO,GAAE,kBAAuB,GAC/B,cAAc,CAAC,CAAC,CAAC,CA+BnB"}
@@ -0,0 +1,38 @@
1
+ import { useState, useEffect } from "react";
2
+ import { revineFetch } from "../runtime/fetch.js";
3
+ /**
4
+ * A React hook for making cached API calls.
5
+ * @param url The URL to fetch
6
+ * @param options Fetch and cache options
7
+ */
8
+ export function useFetch(url, options = {}) {
9
+ const [data, setData] = useState(null);
10
+ const [loading, setLoading] = useState(true);
11
+ const [error, setError] = useState(null);
12
+ const fetchData = async (isRevalidating = false) => {
13
+ try {
14
+ setLoading(true);
15
+ const result = await revineFetch(url, {
16
+ ...options,
17
+ revalidate: isRevalidating || options.revalidate,
18
+ });
19
+ setData(result);
20
+ setError(null);
21
+ }
22
+ catch (err) {
23
+ setError(err instanceof Error ? err : new Error(String(err)));
24
+ }
25
+ finally {
26
+ setLoading(false);
27
+ }
28
+ };
29
+ useEffect(() => {
30
+ fetchData();
31
+ }, [url, JSON.stringify(options)]);
32
+ return {
33
+ data,
34
+ loading,
35
+ error,
36
+ revalidate: () => fetchData(true),
37
+ };
38
+ }
@@ -0,0 +1,10 @@
1
+ declare class RevineCache {
2
+ private memoryCache;
3
+ set<T>(key: string, data: T, ttl: number, persist?: boolean): void;
4
+ get<T>(key: string): T | null;
5
+ delete(key: string): void;
6
+ clear(): void;
7
+ }
8
+ export declare const revineCache: RevineCache;
9
+ export {};
10
+ //# sourceMappingURL=cache.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cache.d.ts","sourceRoot":"","sources":["../../src/runtime/cache.ts"],"names":[],"mappings":"AAKA,cAAM,WAAW;IACf,OAAO,CAAC,WAAW,CAAsC;IAEzD,GAAG,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,EAAE,GAAG,EAAE,MAAM,EAAE,OAAO,GAAE,OAAe;IAiBlE,GAAG,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,GAAG,CAAC,GAAG,IAAI;IA4B7B,MAAM,CAAC,GAAG,EAAE,MAAM;IAOlB,KAAK;CAQN;AAED,eAAO,MAAM,WAAW,aAAoB,CAAC"}
@@ -0,0 +1,61 @@
1
+ class RevineCache {
2
+ constructor() {
3
+ this.memoryCache = new Map();
4
+ }
5
+ set(key, data, ttl, persist = false) {
6
+ const entry = {
7
+ data,
8
+ expiry: Date.now() + ttl,
9
+ };
10
+ this.memoryCache.set(key, entry);
11
+ if (persist && typeof window !== "undefined") {
12
+ try {
13
+ localStorage.setItem(`revine_cache_${key}`, JSON.stringify(entry));
14
+ }
15
+ catch (e) {
16
+ console.warn("Revine Cache: Failed to persist to localStorage", e);
17
+ }
18
+ }
19
+ }
20
+ get(key) {
21
+ // Check memory cache first
22
+ let entry = this.memoryCache.get(key);
23
+ // If not in memory, check localStorage
24
+ if (!entry && typeof window !== "undefined") {
25
+ try {
26
+ const persisted = localStorage.getItem(`revine_cache_${key}`);
27
+ if (persisted) {
28
+ entry = JSON.parse(persisted);
29
+ // Sync back to memory cache
30
+ if (entry)
31
+ this.memoryCache.set(key, entry);
32
+ }
33
+ }
34
+ catch (e) {
35
+ console.warn("Revine Cache: Failed to read from localStorage", e);
36
+ }
37
+ }
38
+ if (!entry)
39
+ return null;
40
+ if (Date.now() > entry.expiry) {
41
+ this.delete(key);
42
+ return null;
43
+ }
44
+ return entry.data;
45
+ }
46
+ delete(key) {
47
+ this.memoryCache.delete(key);
48
+ if (typeof window !== "undefined") {
49
+ localStorage.removeItem(`revine_cache_${key}`);
50
+ }
51
+ }
52
+ clear() {
53
+ this.memoryCache.clear();
54
+ if (typeof window !== "undefined") {
55
+ Object.keys(localStorage)
56
+ .filter((key) => key.startsWith("revine_cache_"))
57
+ .forEach((key) => localStorage.removeItem(key));
58
+ }
59
+ }
60
+ }
61
+ export const revineCache = new RevineCache();
@@ -0,0 +1,12 @@
1
+ export interface RevineFetchOptions extends RequestInit {
2
+ cacheTTL?: number;
3
+ persist?: boolean;
4
+ revalidate?: boolean;
5
+ }
6
+ /**
7
+ * Enhanced fetch with caching capabilities.
8
+ * @param url The URL to fetch
9
+ * @param options Fetch options plus cache configuration
10
+ */
11
+ export declare function revineFetch<T = any>(url: string, options?: RevineFetchOptions): Promise<T>;
12
+ //# sourceMappingURL=fetch.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fetch.d.ts","sourceRoot":"","sources":["../../src/runtime/fetch.ts"],"names":[],"mappings":"AAEA,MAAM,WAAW,kBAAmB,SAAQ,WAAW;IACrD,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,UAAU,CAAC,EAAE,OAAO,CAAC;CACtB;AAED;;;;GAIG;AACH,wBAAsB,WAAW,CAAC,CAAC,GAAG,GAAG,EACvC,GAAG,EAAE,MAAM,EACX,OAAO,GAAE,kBAAuB,GAC/B,OAAO,CAAC,CAAC,CAAC,CAiCZ"}
@@ -0,0 +1,32 @@
1
+ import { revineCache } from "./cache.js";
2
+ /**
3
+ * Enhanced fetch with caching capabilities.
4
+ * @param url The URL to fetch
5
+ * @param options Fetch options plus cache configuration
6
+ */
7
+ export async function revineFetch(url, options = {}) {
8
+ const { cacheTTL = 0, persist = false, revalidate = false, ...fetchOptions } = options;
9
+ // Cache works if cacheTTL > 0. Usually only for GET, but GraphQL uses POST.
10
+ const isCacheable = cacheTTL > 0;
11
+ // Create a more robust cache key that includes the method and body for POST requests
12
+ const method = (fetchOptions.method || "GET").toUpperCase();
13
+ const bodyKey = fetchOptions.body ? `_body:${fetchOptions.body}` : "";
14
+ const cacheKey = `fetch_${method}_${url}_${JSON.stringify(fetchOptions.headers || {})}${bodyKey}`;
15
+ if (isCacheable && !revalidate) {
16
+ const cachedData = revineCache.get(cacheKey);
17
+ if (cachedData !== null) {
18
+ return cachedData;
19
+ }
20
+ }
21
+ const response = await fetch(url, fetchOptions);
22
+ if (!response.ok) {
23
+ const error = new Error(`Revine Fetch Error: ${response.status} ${response.statusText}`);
24
+ error.status = response.status;
25
+ throw error;
26
+ }
27
+ const data = await response.json();
28
+ if (isCacheable) {
29
+ revineCache.set(cacheKey, data, cacheTTL, persist);
30
+ }
31
+ return data;
32
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "revine",
3
- "version": "1.4.1",
3
+ "version": "1.5.0",
4
4
  "description": "A react framework, but better.",
5
5
  "license": "MIT",
6
6
  "author": "Rachit Bharadwaj",
package/roadmap.md CHANGED
@@ -22,4 +22,7 @@
22
22
  ## Styling
23
23
  - add shadcn support
24
24
  - add font support ( google fonts)
25
- - add theme support with tailwind
25
+ - add theme support with tailwind
26
+
27
+ ## API calling
28
+ - caching the server response
package/src/client.ts CHANGED
@@ -13,6 +13,9 @@ export type { LinkProps } from "./components/Link.js";
13
13
  export { NavLink } from "./components/NavLink.js";
14
14
  export type { NavLinkProps } from "./components/NavLink.js";
15
15
  export { useRouter } from "./hooks/useRouter.js";
16
+ export { useFetch } from "./hooks/useFetch.js";
17
+ export { revineFetch } from "./runtime/fetch.js";
18
+ export type { RevineFetchOptions } from "./runtime/fetch.js";
16
19
  export { defineConfig } from "./runtime/defineConfig.js";
17
20
  export { env, envAll } from "./runtime/env.js";
18
21
  export { middlewareResponse } from "./runtime/middleware.js";
@@ -0,0 +1,50 @@
1
+ import { useState, useEffect } from "react";
2
+ import { revineFetch, RevineFetchOptions } from "../runtime/fetch.js";
3
+
4
+ export interface UseFetchResult<T> {
5
+ data: T | null;
6
+ loading: boolean;
7
+ error: Error | null;
8
+ revalidate: () => Promise<void>;
9
+ }
10
+
11
+ /**
12
+ * A React hook for making cached API calls.
13
+ * @param url The URL to fetch
14
+ * @param options Fetch and cache options
15
+ */
16
+ export function useFetch<T = any>(
17
+ url: string,
18
+ options: RevineFetchOptions = {}
19
+ ): UseFetchResult<T> {
20
+ const [data, setData] = useState<T | null>(null);
21
+ const [loading, setLoading] = useState<boolean>(true);
22
+ const [error, setError] = useState<Error | null>(null);
23
+
24
+ const fetchData = async (isRevalidating: boolean = false) => {
25
+ try {
26
+ setLoading(true);
27
+ const result = await revineFetch<T>(url, {
28
+ ...options,
29
+ revalidate: isRevalidating || options.revalidate,
30
+ });
31
+ setData(result);
32
+ setError(null);
33
+ } catch (err) {
34
+ setError(err instanceof Error ? err : new Error(String(err)));
35
+ } finally {
36
+ setLoading(false);
37
+ }
38
+ };
39
+
40
+ useEffect(() => {
41
+ fetchData();
42
+ }, [url, JSON.stringify(options)]);
43
+
44
+ return {
45
+ data,
46
+ loading,
47
+ error,
48
+ revalidate: () => fetchData(true),
49
+ };
50
+ }
@@ -0,0 +1,71 @@
1
+ type CacheEntry<T> = {
2
+ data: T;
3
+ expiry: number;
4
+ };
5
+
6
+ class RevineCache {
7
+ private memoryCache = new Map<string, CacheEntry<any>>();
8
+
9
+ set<T>(key: string, data: T, ttl: number, persist: boolean = false) {
10
+ const entry: CacheEntry<T> = {
11
+ data,
12
+ expiry: Date.now() + ttl,
13
+ };
14
+
15
+ this.memoryCache.set(key, entry);
16
+
17
+ if (persist && typeof window !== "undefined") {
18
+ try {
19
+ localStorage.setItem(`revine_cache_${key}`, JSON.stringify(entry));
20
+ } catch (e) {
21
+ console.warn("Revine Cache: Failed to persist to localStorage", e);
22
+ }
23
+ }
24
+ }
25
+
26
+ get<T>(key: string): T | null {
27
+ // Check memory cache first
28
+ let entry = this.memoryCache.get(key);
29
+
30
+ // If not in memory, check localStorage
31
+ if (!entry && typeof window !== "undefined") {
32
+ try {
33
+ const persisted = localStorage.getItem(`revine_cache_${key}`);
34
+ if (persisted) {
35
+ entry = JSON.parse(persisted);
36
+ // Sync back to memory cache
37
+ if (entry) this.memoryCache.set(key, entry);
38
+ }
39
+ } catch (e) {
40
+ console.warn("Revine Cache: Failed to read from localStorage", e);
41
+ }
42
+ }
43
+
44
+ if (!entry) return null;
45
+
46
+ if (Date.now() > entry.expiry) {
47
+ this.delete(key);
48
+ return null;
49
+ }
50
+
51
+ return entry.data;
52
+ }
53
+
54
+ delete(key: string) {
55
+ this.memoryCache.delete(key);
56
+ if (typeof window !== "undefined") {
57
+ localStorage.removeItem(`revine_cache_${key}`);
58
+ }
59
+ }
60
+
61
+ clear() {
62
+ this.memoryCache.clear();
63
+ if (typeof window !== "undefined") {
64
+ Object.keys(localStorage)
65
+ .filter((key) => key.startsWith("revine_cache_"))
66
+ .forEach((key) => localStorage.removeItem(key));
67
+ }
68
+ }
69
+ }
70
+
71
+ export const revineCache = new RevineCache();
@@ -0,0 +1,50 @@
1
+ import { revineCache } from "./cache.js";
2
+
3
+ export interface RevineFetchOptions extends RequestInit {
4
+ cacheTTL?: number; // TTL in milliseconds
5
+ persist?: boolean; // Whether to persist cache to localStorage
6
+ revalidate?: boolean; // If true, force fetch and update cache
7
+ }
8
+
9
+ /**
10
+ * Enhanced fetch with caching capabilities.
11
+ * @param url The URL to fetch
12
+ * @param options Fetch options plus cache configuration
13
+ */
14
+ export async function revineFetch<T = any>(
15
+ url: string,
16
+ options: RevineFetchOptions = {}
17
+ ): Promise<T> {
18
+ const { cacheTTL = 0, persist = false, revalidate = false, ...fetchOptions } = options;
19
+
20
+ // Cache works if cacheTTL > 0. Usually only for GET, but GraphQL uses POST.
21
+ const isCacheable = cacheTTL > 0;
22
+
23
+ // Create a more robust cache key that includes the method and body for POST requests
24
+ const method = (fetchOptions.method || "GET").toUpperCase();
25
+ const bodyKey = fetchOptions.body ? `_body:${fetchOptions.body}` : "";
26
+ const cacheKey = `fetch_${method}_${url}_${JSON.stringify(fetchOptions.headers || {})}${bodyKey}`;
27
+
28
+ if (isCacheable && !revalidate) {
29
+ const cachedData = revineCache.get<T>(cacheKey);
30
+ if (cachedData !== null) {
31
+ return cachedData;
32
+ }
33
+ }
34
+
35
+ const response = await fetch(url, fetchOptions);
36
+
37
+ if (!response.ok) {
38
+ const error: any = new Error(`Revine Fetch Error: ${response.status} ${response.statusText}`);
39
+ error.status = response.status;
40
+ throw error;
41
+ }
42
+
43
+ const data = await response.json();
44
+
45
+ if (isCacheable) {
46
+ revineCache.set(cacheKey, data, cacheTTL, persist);
47
+ }
48
+
49
+ return data;
50
+ }