1
- import { onBeforeUnmount , Ref , ref , toRaw , watch } from "vue" ;
1
+ import { inject , onBeforeUnmount , Ref , ref , toRaw , watch } from "vue" ;
2
2
import FetchError from "../core/fetch_error.ts" ;
3
- import { DataContext } from "./context.ts" ;
3
+ import type { SSRContext } from "../../server/renderer.ts" ;
4
+ import { HttpMethod , UpdateStrategy } from "./context.ts" ;
4
5
5
- type HttpMethod = "get" | "post" | "put" | "patch" | "delete" ;
6
+ export type RouteData = {
7
+ data ?: unknown ;
8
+ dataCacheTtl ?: number ;
9
+ dataExpires ?: number ;
10
+ } ;
6
11
7
- type UpdateStrategy < T > = "none" | "replace" | {
8
- optimisticUpdate ?: ( data : T ) => T ;
9
- onFailure ?: ( error : Error ) => void ;
10
- replace ?: boolean ;
12
+ export type DataProviderProps = {
13
+ dataUrl : string ;
14
+ dataCache : Map < string , RouteData > ;
11
15
} ;
12
16
13
- export const useData = < T = unknown > ( ) : {
14
- data : Ref < T > ;
15
- isMutating : Ref < HttpMethod | boolean > ;
16
- mutation : typeof mutation ;
17
- reload : ( signal ?: AbortSignal ) => Promise < void > ;
18
- } => {
19
- const { dataUrl , dataCache } = DataContext ;
20
- const cached = dataCache . get ( dataUrl ) ;
17
+ const createDataProvider = ( ) => {
18
+ const dataCache : Map < string , RouteData > = inject ( "dataCache" ) || new Map ( ) ;
19
+ const ssrContext : SSRContext | undefined = inject ( "ssrContext" ) ;
20
+ const url = ssrContext ?. url || new URL ( window . location ?. href ) ;
21
+ const defaultDataUrl = url . pathname + url . search ;
22
+ const dataUrl : string = inject ( "dataUrl" ) || defaultDataUrl ;
23
+
24
+ const cached = dataCache ? .get ( dataUrl ) ;
21
25
22
26
if ( cached ) {
23
27
if ( cached . data instanceof Error ) {
@@ -26,7 +30,7 @@ export const useData = <T = unknown>(): {
26
30
if ( typeof cached . data === "function" ) {
27
31
const data = cached . data ( ) ;
28
32
if ( data instanceof Promise ) {
29
- throw data . then ( ( data ) => {
33
+ data . then ( ( data ) => {
30
34
cached . data = data ;
31
35
} ) . catch ( ( error ) => {
32
36
cached . data = error ;
@@ -38,25 +42,24 @@ export const useData = <T = unknown>(): {
38
42
throw new Error ( `Data for ${ dataUrl } is not found` ) ;
39
43
}
40
44
41
- const _data : Ref < T > = ref ( cached . data ) ;
45
+ const _data : Ref < unknown > = ref ( cached ? .data ) ;
42
46
const isMutating = ref < HttpMethod | boolean > ( false ) ;
43
47
44
- const action = async ( method : HttpMethod , fetcher : Promise < Response > , update : UpdateStrategy < T > ) => {
48
+ const action = async ( method : HttpMethod , fetcher : Promise < Response > , update : UpdateStrategy ) => {
45
49
const updateIsObject = update && typeof update === "object" && update !== null ;
46
50
const optimistic = updateIsObject && typeof update . optimisticUpdate === "function" ;
47
51
const replace = update === "replace" || ( updateIsObject && ! ! update . replace ) ;
48
52
49
- isMutating . value = method ;
50
-
51
- let rollbackData : T | undefined = undefined ;
53
+ let rollbackData : unknown = undefined ;
52
54
if ( optimistic ) {
53
55
const optimisticUpdate = update . optimisticUpdate ! ;
54
56
if ( _data . value !== undefined ) {
55
57
rollbackData = toRaw ( _data . value ) ;
56
- _data . value = optimisticUpdate ( clone ( toRaw ( _data . value ) ) ) ;
58
+ _data . value = optimisticUpdate ( shallowClone ( toRaw ( _data . value ) ) ) ;
57
59
}
58
60
}
59
61
62
+ isMutating . value = method ;
60
63
const res = await fetcher ;
61
64
if ( res . status >= 400 ) {
62
65
if ( optimistic ) {
@@ -132,16 +135,16 @@ export const useData = <T = unknown>(): {
132
135
} ;
133
136
134
137
const mutation = {
135
- post : ( data ?: unknown , update ?: UpdateStrategy < T > ) => {
138
+ post : ( data ?: unknown , update ?: UpdateStrategy ) => {
136
139
return action ( "post" , send ( "post" , dataUrl , data ) , update ?? "none" ) ;
137
140
} ,
138
- put : ( data ?: unknown , update ?: UpdateStrategy < T > ) => {
141
+ put : ( data ?: unknown , update ?: UpdateStrategy ) => {
139
142
return action ( "put" , send ( "put" , dataUrl , data ) , update ?? "none" ) ;
140
143
} ,
141
- patch : ( data ?: unknown , update ?: UpdateStrategy < T > ) => {
144
+ patch : ( data ?: unknown , update ?: UpdateStrategy ) => {
142
145
return action ( "patch" , send ( "patch" , dataUrl , data ) , update ?? "none" ) ;
143
146
} ,
144
- delete : ( data ?: unknown , update ?: UpdateStrategy < T > ) => {
147
+ delete : ( data ?: unknown , update ?: UpdateStrategy ) => {
145
148
return action ( "delete" , send ( "delete" , dataUrl , data ) , update ?? "none" ) ;
146
149
} ,
147
150
} ;
@@ -165,6 +168,10 @@ export const useData = <T = unknown>(): {
165
168
return { data : _data , isMutating, mutation, reload } ;
166
169
} ;
167
170
171
+ export const useData = ( ) => {
172
+ return createDataProvider ( ) ;
173
+ } ;
174
+
168
175
function send ( method : HttpMethod , href : string , data : unknown ) {
169
176
let body : BodyInit | undefined ;
170
177
const headers = new Headers ( ) ;
@@ -190,8 +197,12 @@ function send(method: HttpMethod, href: string, data: unknown) {
190
197
return fetch ( href , { method, body, headers, redirect : "manual" } ) ;
191
198
}
192
199
193
- function clone < T > ( obj : T ) : T {
194
- // deno-lint-ignore ban-ts-comment
195
- // @ts -ignore
196
- return typeof structuredClone === "function" ? structuredClone ( obj ) : JSON . parse ( JSON . stringify ( obj ) ) ;
200
+ function shallowClone < T > ( obj : T ) : T {
201
+ if ( obj === null || typeof obj !== "object" ) {
202
+ return obj ;
203
+ }
204
+ if ( Array . isArray ( obj ) ) {
205
+ return [ ...obj ] as unknown as T ;
206
+ }
207
+ return { ...obj } ;
197
208
}
0 commit comments