asasvirtuais 0.1.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 +78 -0
- package/actions/draw.ts +110 -0
- package/components/OAuthCard.tsx +346 -0
- package/components/icons.tsx +11 -0
- package/components/markdown.tsx +18 -0
- package/components/stack/list.tsx +21 -0
- package/components/stack/menu.tsx +40 -0
- package/components/stack/nav.tsx +39 -0
- package/components/table/fixed.tsx +59 -0
- package/components/table/key-value.tsx +19 -0
- package/components/ui/color-mode.tsx +108 -0
- package/components/ui/provider.tsx +15 -0
- package/components/ui/toaster.tsx +43 -0
- package/components/ui/tooltip.tsx +46 -0
- package/hooks/useBoolean.tsx +11 -0
- package/hooks/useForwardAs.tsx +29 -0
- package/hooks/useHash copy.tsx +27 -0
- package/hooks/useHash.tsx +27 -0
- package/hooks/useIsMobile.tsx +6 -0
- package/hooks/useOAuthTokens.ts +97 -0
- package/hooks/useOpenRouterModels.ts +80 -0
- package/lib/auth0.ts +11 -0
- package/lib/blob.ts +3 -0
- package/lib/client-token-storage.ts +216 -0
- package/lib/oauth-tokens.ts +85 -0
- package/lib/react/context.tsx +20 -0
- package/lib/react/index.ts +1 -0
- package/lib/tools.ts +375 -0
- package/next-env.d.ts +5 -0
- package/next.config.ts +23 -0
- package/package.json +72 -0
- package/packages/blob.ts +97 -0
- package/packages/chat/components/chat/feed/index.tsx +76 -0
- package/packages/chat/components/chat/feed/story.tsx +18 -0
- package/packages/chat/components/chat/index.tsx +16 -0
- package/packages/chat/components/chat/story.tsx +74 -0
- package/packages/chat/components/debug/index.tsx +54 -0
- package/packages/chat/components/header/index.tsx +14 -0
- package/packages/chat/components/header/menu/index.tsx +63 -0
- package/packages/chat/components/header/story.tsx +33 -0
- package/packages/chat/components/header/title/index.tsx +35 -0
- package/packages/chat/components/index.ts +13 -0
- package/packages/chat/components/input/index.tsx +17 -0
- package/packages/chat/components/input/menu/index.tsx +35 -0
- package/packages/chat/components/input/send.tsx +21 -0
- package/packages/chat/components/input/story.tsx +35 -0
- package/packages/chat/components/input/textarea/index.tsx +20 -0
- package/packages/chat/components/message/file.tsx +103 -0
- package/packages/chat/components/message/menu/index.tsx +26 -0
- package/packages/chat/components/message/story.tsx +49 -0
- package/packages/chat/components/messages/index.tsx +23 -0
- package/packages/chat/components/messages/story.tsx +11 -0
- package/packages/chat/components/ui/prose.tsx +263 -0
- package/packages/chat/edit-message.tsx +49 -0
- package/packages/chat/header.tsx +118 -0
- package/packages/chat/index.ts +14 -0
- package/packages/chat/input.tsx +89 -0
- package/packages/chat/message-menu.tsx +57 -0
- package/packages/chat/message.tsx +44 -0
- package/packages/chat/messages.tsx +44 -0
- package/packages/chat/model-selector.tsx +172 -0
- package/packages/chat/scenarios.tsx +68 -0
- package/packages/chat/settings.tsx +98 -0
- package/packages/chat/temperature-slider.tsx +67 -0
- package/packages/chat/tool-results.tsx +32 -0
- package/packages/crud/core.ts +75 -0
- package/packages/crud/fetcher.ts +64 -0
- package/packages/crud/index.ts +2 -0
- package/packages/crud/next.ts +128 -0
- package/packages/crud/react.tsx +365 -0
- package/packages/env.ts +8 -0
- package/packages/fields.tsx +157 -0
- package/packages/firebase.ts +13 -0
- package/packages/firestore.ts +51 -0
- package/packages/form.tsx +66 -0
- package/packages/next.ts +64 -0
- package/packages/openrouter.ts +4 -0
- package/packages/react/context.tsx +21 -0
- package/packages/react/crud.tsx +372 -0
- package/packages/react/hooks.ts +90 -0
- package/packages/react/store.tsx +20 -0
- package/packages/replit-db.ts +219 -0
- package/packages/wretch.ts +22 -0
- package/packages/yaml.ts +163 -0
- package/pnpm-workspace.yaml +4 -0
- package/server/db.ts +15 -0
package/packages/next.ts
ADDED
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { ReactNode } from 'react'
|
|
2
|
+
import { ImageResponse } from 'next/og'
|
|
3
|
+
import { NextRequest, NextResponse } from 'next/server'
|
|
4
|
+
import { parse } from 'search-params'
|
|
5
|
+
|
|
6
|
+
type NextRouteProps<Params = {}> = {request: NextRequest} & Params
|
|
7
|
+
type NextRouteHandler<Params = {}> = (props: NextRouteProps<Params>) => Promise<NextResponse<any> | Response | ImageResponse | object | unknown>
|
|
8
|
+
type NextLayoutHandler<SP = {}> = (props: SP) => Promise<ReactNode>
|
|
9
|
+
type NextPageHandler<Params = {}> = (params: Params) => Promise<ReactNode>
|
|
10
|
+
|
|
11
|
+
export const route = <P = {}>(handler: NextRouteHandler<P>) : any => (
|
|
12
|
+
async (request: NextRequest, { params: promise }: { params: Promise<P> } ) => {
|
|
13
|
+
const params = await promise
|
|
14
|
+
const query = parse(request.nextUrl.search)
|
|
15
|
+
return handler({request, ...params, ...query})
|
|
16
|
+
.then(
|
|
17
|
+
response => {
|
|
18
|
+
if (response instanceof NextResponse)
|
|
19
|
+
return response
|
|
20
|
+
else if (response instanceof Response)
|
|
21
|
+
return response
|
|
22
|
+
else if (response instanceof ImageResponse)
|
|
23
|
+
return response
|
|
24
|
+
return NextResponse.json(response)
|
|
25
|
+
}
|
|
26
|
+
)
|
|
27
|
+
.catch(
|
|
28
|
+
error => {
|
|
29
|
+
console.error(error, request, params)
|
|
30
|
+
return NextResponse.json({
|
|
31
|
+
request: JSON.stringify(request),
|
|
32
|
+
params,
|
|
33
|
+
error: {
|
|
34
|
+
name: error.name,
|
|
35
|
+
message: error.message,
|
|
36
|
+
cause: error.cause,
|
|
37
|
+
stack: error.stack,
|
|
38
|
+
},
|
|
39
|
+
}, { url: request.url, status: 500 })
|
|
40
|
+
}
|
|
41
|
+
)
|
|
42
|
+
}
|
|
43
|
+
)
|
|
44
|
+
|
|
45
|
+
export const layout = <SlotsAndParams = {}>( handler: NextLayoutHandler<SlotsAndParams & { children: React.ReactNode }> ): any => (
|
|
46
|
+
async ({params, ...slots}: { params: Promise<any>, children: ReactNode }) => (
|
|
47
|
+
handler({ ...slots, ...(params ? await params : {}) } as SlotsAndParams & { children: React.ReactNode })
|
|
48
|
+
)
|
|
49
|
+
)
|
|
50
|
+
export const page = <Params = {}>( handler: NextPageHandler<Params>): any => (
|
|
51
|
+
async ({params, searchParams}: { params: Promise<any>, searchParams: Promise<any> }) => handler({
|
|
52
|
+
...(await params), ...(await searchParams)
|
|
53
|
+
})
|
|
54
|
+
)
|
|
55
|
+
export const redirect = (url: string, auth?: string) => (
|
|
56
|
+
new NextResponse(null, {
|
|
57
|
+
status: 302,
|
|
58
|
+
headers: {
|
|
59
|
+
Location: url,
|
|
60
|
+
...(auth ? { Authorization: auth } : {})
|
|
61
|
+
}
|
|
62
|
+
})
|
|
63
|
+
)
|
|
64
|
+
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
'use client'
|
|
2
|
+
|
|
3
|
+
import React from 'react'
|
|
4
|
+
|
|
5
|
+
export function createContextFromHook<Props, Result>(useHook: (props: Props) => Result) {
|
|
6
|
+
|
|
7
|
+
const Context = React.createContext<Result | undefined>(undefined)
|
|
8
|
+
|
|
9
|
+
function Provider( { children, ...props}: React.PropsWithChildren<Props> ) {
|
|
10
|
+
|
|
11
|
+
const value = useHook(props as Props) as Result
|
|
12
|
+
|
|
13
|
+
return <Context.Provider value={value}>{ children }</Context.Provider>
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
function useContext() {
|
|
17
|
+
return React.useContext(Context) as Result
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
return [Provider, useContext] as const
|
|
21
|
+
}
|
|
@@ -0,0 +1,372 @@
|
|
|
1
|
+
"use client"
|
|
2
|
+
import { z } from "zod"
|
|
3
|
+
import {
|
|
4
|
+
useState,
|
|
5
|
+
useCallback,
|
|
6
|
+
useEffect,
|
|
7
|
+
useMemo,
|
|
8
|
+
createContext,
|
|
9
|
+
useContext,
|
|
10
|
+
} from "react"
|
|
11
|
+
import { TableInterface, ListProps } from "@/packages/crud"
|
|
12
|
+
import { createContextFromHook } from "./context"
|
|
13
|
+
import { useIndex } from "./hooks"
|
|
14
|
+
import { FieldsProvider, useFields } from "@/packages/fields"
|
|
15
|
+
import { FormProvider, useForm, useFormProvider } from "@/packages/form"
|
|
16
|
+
|
|
17
|
+
export function database<
|
|
18
|
+
Database extends Record<
|
|
19
|
+
string,
|
|
20
|
+
{ readable: z.SomeZodObject; writable: z.SomeZodObject }
|
|
21
|
+
>
|
|
22
|
+
>(
|
|
23
|
+
database: Database,
|
|
24
|
+
{
|
|
25
|
+
find,
|
|
26
|
+
create,
|
|
27
|
+
update,
|
|
28
|
+
remove,
|
|
29
|
+
list,
|
|
30
|
+
}: TableInterface<
|
|
31
|
+
z.infer<Database[keyof Database]["readable"]>,
|
|
32
|
+
z.infer<Database[keyof Database]["writable"]>
|
|
33
|
+
>
|
|
34
|
+
) {
|
|
35
|
+
type TableKey = keyof Database & string
|
|
36
|
+
|
|
37
|
+
function useTableProvider<Table extends TableKey>({
|
|
38
|
+
table,
|
|
39
|
+
asAbove,
|
|
40
|
+
}: {
|
|
41
|
+
table: Table
|
|
42
|
+
asAbove?: Record<string, z.infer<Database[Table]["readable"]>>
|
|
43
|
+
}) {
|
|
44
|
+
type Readable = z.infer<Database[Table]["readable"]>
|
|
45
|
+
type Writable = z.infer<Database[Table]["writable"]>
|
|
46
|
+
|
|
47
|
+
const index = useIndex<Readable>({ ...(asAbove ?? {}) })
|
|
48
|
+
|
|
49
|
+
const array = useMemo(
|
|
50
|
+
() => Object.values(index.index) as Readable[],
|
|
51
|
+
[index.index]
|
|
52
|
+
)
|
|
53
|
+
|
|
54
|
+
useEffect(function soBelow() {
|
|
55
|
+
index.setIndex((prev) => ({ ...prev, ...asAbove }))
|
|
56
|
+
}, [])
|
|
57
|
+
|
|
58
|
+
const methods = { find, create, update, remove, list } as TableInterface<
|
|
59
|
+
Readable,
|
|
60
|
+
Writable
|
|
61
|
+
>
|
|
62
|
+
|
|
63
|
+
// Create wrapped method that updates the index after successful operations
|
|
64
|
+
const createWithIndex = useCallback(
|
|
65
|
+
(async (props) => {
|
|
66
|
+
const result = await methods.create({ ...props, table })
|
|
67
|
+
if (result && (result).id) {
|
|
68
|
+
index.set(result as Readable)
|
|
69
|
+
}
|
|
70
|
+
return result
|
|
71
|
+
}) as typeof create,
|
|
72
|
+
[methods, table, index]
|
|
73
|
+
)
|
|
74
|
+
|
|
75
|
+
const updateWithIndex = useCallback(
|
|
76
|
+
(async (props) => {
|
|
77
|
+
const result = await methods.update({ ...props, table })
|
|
78
|
+
if (result && (result).id) {
|
|
79
|
+
index.set(result as Readable)
|
|
80
|
+
}
|
|
81
|
+
return result
|
|
82
|
+
}) as typeof update,
|
|
83
|
+
[methods, table, index]
|
|
84
|
+
)
|
|
85
|
+
|
|
86
|
+
const removeWithIndex = useCallback(
|
|
87
|
+
(async (props) => {
|
|
88
|
+
const result = await methods.remove({ ...props, table })
|
|
89
|
+
if (result && (result).id) {
|
|
90
|
+
index.remove(result as Readable)
|
|
91
|
+
}
|
|
92
|
+
return result
|
|
93
|
+
}) as typeof remove,
|
|
94
|
+
[methods, table, index]
|
|
95
|
+
)
|
|
96
|
+
|
|
97
|
+
const listWithIndex = useCallback(
|
|
98
|
+
(async (props) => {
|
|
99
|
+
const result = await methods.list({ ...props, table })
|
|
100
|
+
if (Array.isArray(result)) {
|
|
101
|
+
index.setIndex(
|
|
102
|
+
Object.fromEntries(result.map((item) => [(item).id, item]))
|
|
103
|
+
)
|
|
104
|
+
}
|
|
105
|
+
return result
|
|
106
|
+
}) as typeof list,
|
|
107
|
+
[methods, table, index]
|
|
108
|
+
)
|
|
109
|
+
|
|
110
|
+
return {
|
|
111
|
+
...index,
|
|
112
|
+
array,
|
|
113
|
+
find: useMethod((props) => methods.find({ ...props, table })),
|
|
114
|
+
create: useMethod(createWithIndex),
|
|
115
|
+
update: useMethod(updateWithIndex),
|
|
116
|
+
remove: useMethod(removeWithIndex),
|
|
117
|
+
list: useMethod(listWithIndex),
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
function useDatabaseProvider(tables: {
|
|
122
|
+
[T in TableKey]: ReturnType<typeof useTableProvider<T>>
|
|
123
|
+
}) {
|
|
124
|
+
return tables
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
const [DatabaseProvider, useDatabase] =
|
|
128
|
+
createContextFromHook(useDatabaseProvider)
|
|
129
|
+
|
|
130
|
+
function useTable<T extends TableKey>(name: T) {
|
|
131
|
+
return useDatabase()[name]
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
function useSingleProvider<Table extends TableKey>({
|
|
135
|
+
id,
|
|
136
|
+
table,
|
|
137
|
+
}: {
|
|
138
|
+
id: string
|
|
139
|
+
table: Table
|
|
140
|
+
}) {
|
|
141
|
+
const { index, find } = useTable(table)
|
|
142
|
+
const [single, setSingle] = useState<z.infer<Database[Table]["readable"]>>(
|
|
143
|
+
() => index[id]
|
|
144
|
+
)
|
|
145
|
+
useEffect(() => {
|
|
146
|
+
if (!single) find.trigger({ id }).then(setSingle)
|
|
147
|
+
}, [])
|
|
148
|
+
useEffect(() => {
|
|
149
|
+
setSingle(index[id])
|
|
150
|
+
}, [index[id]])
|
|
151
|
+
return {
|
|
152
|
+
id,
|
|
153
|
+
single,
|
|
154
|
+
setSingle,
|
|
155
|
+
loading: find.loading,
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
const SingleContext = createContext<
|
|
160
|
+
ReturnType<typeof useSingleProvider<any>> | undefined
|
|
161
|
+
>(undefined)
|
|
162
|
+
|
|
163
|
+
function SingleProvider<Table extends TableKey>({
|
|
164
|
+
children,
|
|
165
|
+
...props
|
|
166
|
+
}: {
|
|
167
|
+
id: string
|
|
168
|
+
table: Table
|
|
169
|
+
children: React.ReactNode
|
|
170
|
+
}) {
|
|
171
|
+
const value = useSingleProvider(props)
|
|
172
|
+
if (!value.single) return null
|
|
173
|
+
return (
|
|
174
|
+
<SingleContext.Provider value={value}>{children}</SingleContext.Provider>
|
|
175
|
+
)
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
const useSingle = <Table extends TableKey>() =>
|
|
179
|
+
useContext(SingleContext) as ReturnType<typeof useSingleProvider<Table>>
|
|
180
|
+
|
|
181
|
+
function CreateForm<
|
|
182
|
+
T extends TableKey
|
|
183
|
+
>({
|
|
184
|
+
table,
|
|
185
|
+
defaults,
|
|
186
|
+
onSuccess,
|
|
187
|
+
children,
|
|
188
|
+
}: {
|
|
189
|
+
table: T
|
|
190
|
+
defaults?: Partial<z.infer<Database[T]["writable"]>>
|
|
191
|
+
onSuccess?: (result: z.infer<Database[T]["readable"]>) => void
|
|
192
|
+
children: (props: ReturnType<typeof useFormProvider<z.infer<Database[T]["writable"]>, z.infer<Database[T]["readable"]>>>) => React.ReactNode
|
|
193
|
+
}) {
|
|
194
|
+
type Readable = z.infer<Database[T]["readable"]>
|
|
195
|
+
type Writable = z.infer<Database[T]["writable"]>
|
|
196
|
+
|
|
197
|
+
const { create } = useTable(table)
|
|
198
|
+
|
|
199
|
+
const callback = useCallback(
|
|
200
|
+
async (fields: Writable) => {
|
|
201
|
+
const result = await create.trigger({ data: fields })
|
|
202
|
+
if (onSuccess) onSuccess(result as Readable)
|
|
203
|
+
return result
|
|
204
|
+
},
|
|
205
|
+
[create, onSuccess]
|
|
206
|
+
)
|
|
207
|
+
|
|
208
|
+
return (
|
|
209
|
+
<FieldsProvider<Writable> defaults={defaults || ({} as Writable)}>
|
|
210
|
+
{fields => (
|
|
211
|
+
<FormProvider<Writable, Readable> callback={callback} data={fields.fields}>
|
|
212
|
+
{form => children({...form, ...fields})}
|
|
213
|
+
</FormProvider>
|
|
214
|
+
)}
|
|
215
|
+
</FieldsProvider>
|
|
216
|
+
)
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
function UpdateForm<T extends TableKey>({
|
|
220
|
+
table,
|
|
221
|
+
id,
|
|
222
|
+
defaults,
|
|
223
|
+
onSuccess,
|
|
224
|
+
children,
|
|
225
|
+
}: {
|
|
226
|
+
table: TableKey
|
|
227
|
+
id: string
|
|
228
|
+
defaults?: Partial<z.infer<Database[TableKey]["writable"]>>
|
|
229
|
+
onSuccess?: (result: z.infer<Database[TableKey]["readable"]>) => void
|
|
230
|
+
children: (props: ReturnType<typeof useFormProvider<z.infer<Database[T]["writable"]>, z.infer<Database[T]["readable"]>>>) => React.ReactNode
|
|
231
|
+
}) {
|
|
232
|
+
type Readable = z.infer<Database[T]["readable"]>
|
|
233
|
+
type Writable = z.infer<Database[T]["writable"]>
|
|
234
|
+
|
|
235
|
+
const { update } = useTable(table)
|
|
236
|
+
|
|
237
|
+
const callback = useCallback(
|
|
238
|
+
async (fields: Partial<Writable>) => {
|
|
239
|
+
const result = await update.trigger({ id, data: fields })
|
|
240
|
+
if (onSuccess) onSuccess(result as Readable)
|
|
241
|
+
return result
|
|
242
|
+
},
|
|
243
|
+
[update, id, onSuccess]
|
|
244
|
+
)
|
|
245
|
+
|
|
246
|
+
return (
|
|
247
|
+
<FieldsProvider<Partial<Writable>>
|
|
248
|
+
defaults={defaults || ({} as Partial<Writable>)}
|
|
249
|
+
>
|
|
250
|
+
{fields => (
|
|
251
|
+
<FormProvider<Partial<Writable>, Readable> callback={callback} data={fields.fields}>
|
|
252
|
+
{form => children({...form, ...fields})}
|
|
253
|
+
</FormProvider>
|
|
254
|
+
)}
|
|
255
|
+
</FieldsProvider>
|
|
256
|
+
)
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
function FilterForm<T extends TableKey>({
|
|
260
|
+
table,
|
|
261
|
+
defaults,
|
|
262
|
+
onSuccess,
|
|
263
|
+
children,
|
|
264
|
+
}: {
|
|
265
|
+
table: T
|
|
266
|
+
defaults?: Partial<ListProps<z.infer<Database[T]["readable"]>>>
|
|
267
|
+
onSuccess?: (result: z.infer<Database[T]["readable"]>[]) => void
|
|
268
|
+
children: (props: ReturnType<typeof useFormProvider<z.infer<Database[T]["writable"]>, z.infer<Database[T]["readable"]>>>) => React.ReactNode
|
|
269
|
+
}) {
|
|
270
|
+
type Readable = z.infer<Database[T]["readable"]>
|
|
271
|
+
type Writable = z.infer<Database[T]["writable"]>
|
|
272
|
+
|
|
273
|
+
const { list } = useTable(table)
|
|
274
|
+
|
|
275
|
+
const callback = useCallback(
|
|
276
|
+
async (fields: Omit<ListProps<Readable>, "table">) => {
|
|
277
|
+
const result = await list.trigger(fields)
|
|
278
|
+
if (onSuccess) onSuccess(result)
|
|
279
|
+
return result
|
|
280
|
+
},
|
|
281
|
+
[list, onSuccess]
|
|
282
|
+
)
|
|
283
|
+
|
|
284
|
+
return (
|
|
285
|
+
<FieldsProvider<ListProps<Readable>>
|
|
286
|
+
defaults={(defaults || {}) as ListProps<Readable>}
|
|
287
|
+
>
|
|
288
|
+
{fields => (
|
|
289
|
+
<FormProvider<ListProps<Readable>, Readable[]> callback={callback} data={fields.fields}>
|
|
290
|
+
{form => children({...form, ...fields})}
|
|
291
|
+
</FormProvider>
|
|
292
|
+
)}
|
|
293
|
+
</FieldsProvider>
|
|
294
|
+
)
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
const useCreateForm = <T extends TableKey>(table: T) => {
|
|
298
|
+
return {
|
|
299
|
+
...useFields<z.infer<Database[T]["writable"]>>(),
|
|
300
|
+
...useForm<
|
|
301
|
+
z.infer<Database[T]["writable"]>,
|
|
302
|
+
z.infer<Database[T]["readable"]>
|
|
303
|
+
>(),
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
const useUpdateForm = <T extends TableKey>(table: T) => {
|
|
307
|
+
return {
|
|
308
|
+
...useFields<Partial<z.infer<Database[T]["writable"]>>>(),
|
|
309
|
+
...useForm<
|
|
310
|
+
Partial<z.infer<Database[T]["writable"]>>,
|
|
311
|
+
z.infer<Database[T]["readable"]>
|
|
312
|
+
>(),
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
const useFiltersForm = <T extends TableKey>(table: T) => {
|
|
316
|
+
return {
|
|
317
|
+
...useFields<ListProps<z.infer<Database[T]["readable"]>>>(),
|
|
318
|
+
...useForm<
|
|
319
|
+
ListProps<z.infer<Database[T]["readable"]>>,
|
|
320
|
+
z.infer<Database[T]["readable"]>[]
|
|
321
|
+
>(),
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
return {
|
|
326
|
+
DatabaseProvider,
|
|
327
|
+
useDatabase,
|
|
328
|
+
useTable,
|
|
329
|
+
useTableProvider,
|
|
330
|
+
SingleProvider,
|
|
331
|
+
useSingle,
|
|
332
|
+
CreateForm,
|
|
333
|
+
UpdateForm,
|
|
334
|
+
FilterForm,
|
|
335
|
+
useCreateForm,
|
|
336
|
+
useUpdateForm,
|
|
337
|
+
useFiltersForm,
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
function useMethod<Fn extends (props: any) => Promise<any>>(method: Fn) {
|
|
342
|
+
const [loading, setLoading] = useState(false)
|
|
343
|
+
const [error, setError] = useState<Error | null>(null)
|
|
344
|
+
const [result, setResult] = useState<Awaited<ReturnType<Fn>>>()
|
|
345
|
+
|
|
346
|
+
const trigger = useCallback(
|
|
347
|
+
(async (props) => {
|
|
348
|
+
if (loading) return
|
|
349
|
+
setLoading(true)
|
|
350
|
+
setError(null)
|
|
351
|
+
try {
|
|
352
|
+
const data = await method(props)
|
|
353
|
+
setResult(data)
|
|
354
|
+
return data
|
|
355
|
+
} catch (err) {
|
|
356
|
+
console.error("CRUD operation failed:", err)
|
|
357
|
+
setError(err as Error)
|
|
358
|
+
throw err
|
|
359
|
+
} finally {
|
|
360
|
+
setLoading(false)
|
|
361
|
+
}
|
|
362
|
+
}) as typeof method,
|
|
363
|
+
[method, loading]
|
|
364
|
+
)
|
|
365
|
+
|
|
366
|
+
return {
|
|
367
|
+
trigger,
|
|
368
|
+
loading,
|
|
369
|
+
error,
|
|
370
|
+
result,
|
|
371
|
+
}
|
|
372
|
+
}
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
'use client'
|
|
2
|
+
|
|
3
|
+
import { useCallback, useEffect, useMemo, useState } from 'react'
|
|
4
|
+
|
|
5
|
+
export function useAction<Props, Result, Defaults = Partial<Props>>(action: (props: Props) => Promise<Result>, {
|
|
6
|
+
onSuccess,
|
|
7
|
+
autoTrigger,
|
|
8
|
+
...props
|
|
9
|
+
} : {
|
|
10
|
+
defaults?: Defaults
|
|
11
|
+
onSuccess?: (result: Result, props?: Props) => void
|
|
12
|
+
autoTrigger?: boolean
|
|
13
|
+
} = {}) {
|
|
14
|
+
const [loading, setLoading] = useState<boolean>(false)
|
|
15
|
+
const [error, setError] = useState()
|
|
16
|
+
const [result, setResult] = useState<Result>()
|
|
17
|
+
const [defaults, setDefaults] = useState<Defaults>(props.defaults ?? {} as Defaults)
|
|
18
|
+
|
|
19
|
+
const trigger = useCallback(async (props: Omit<Props, keyof Defaults>): Promise<Result> => {
|
|
20
|
+
try {
|
|
21
|
+
setLoading(true)
|
|
22
|
+
const result = await action({
|
|
23
|
+
...props,
|
|
24
|
+
...defaults,
|
|
25
|
+
} as Props)
|
|
26
|
+
|
|
27
|
+
setResult(result)
|
|
28
|
+
if (onSuccess)
|
|
29
|
+
onSuccess(result)
|
|
30
|
+
return result
|
|
31
|
+
|
|
32
|
+
} catch (error) {
|
|
33
|
+
// @ts-expect-error
|
|
34
|
+
setError(error)
|
|
35
|
+
throw error
|
|
36
|
+
} finally {
|
|
37
|
+
setLoading(false)
|
|
38
|
+
}
|
|
39
|
+
}, [defaults, onSuccess, loading, setLoading])
|
|
40
|
+
|
|
41
|
+
useEffect(() => {
|
|
42
|
+
if (autoTrigger)
|
|
43
|
+
trigger(props as Omit<Props, keyof Defaults>)
|
|
44
|
+
}, [])
|
|
45
|
+
|
|
46
|
+
return {
|
|
47
|
+
trigger,
|
|
48
|
+
loading,
|
|
49
|
+
error,
|
|
50
|
+
result,
|
|
51
|
+
defaults,
|
|
52
|
+
setDefaults,
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export function useIndex<T>(value: Record<string, any>) {
|
|
57
|
+
|
|
58
|
+
type readable = T
|
|
59
|
+
|
|
60
|
+
const [index, setIndex] = useState<Record<string, T>>(() => value)
|
|
61
|
+
|
|
62
|
+
const array = useMemo(() => Object.values(index) as readable[], [index])
|
|
63
|
+
|
|
64
|
+
const set = useCallback((...params: readable[]) => {
|
|
65
|
+
setIndex(prev => ({
|
|
66
|
+
...prev,
|
|
67
|
+
...Object.fromEntries(params.map(data => ([(data as readable & { id: string }).id, data])))
|
|
68
|
+
}))
|
|
69
|
+
}, [])
|
|
70
|
+
|
|
71
|
+
const remove = useCallback((...params: readable[]) => {
|
|
72
|
+
setIndex(prev => {
|
|
73
|
+
const newState = { ...prev }
|
|
74
|
+
for ( const data of params ) {
|
|
75
|
+
const id = (data as readable & { id: string }).id
|
|
76
|
+
if (newState[id])
|
|
77
|
+
delete newState[id]
|
|
78
|
+
}
|
|
79
|
+
return newState
|
|
80
|
+
})
|
|
81
|
+
}, [])
|
|
82
|
+
|
|
83
|
+
return {
|
|
84
|
+
index,
|
|
85
|
+
array,
|
|
86
|
+
set,
|
|
87
|
+
setIndex,
|
|
88
|
+
remove,
|
|
89
|
+
}
|
|
90
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
'use client'
|
|
2
|
+
|
|
3
|
+
import { useIndex } from './hooks'
|
|
4
|
+
import { createContextFromHook } from './context'
|
|
5
|
+
|
|
6
|
+
type StoreProps<T> = {
|
|
7
|
+
[table: string]: (T & { id: string })[]
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
function useStoreProvider<T>(props : StoreProps<T>) {
|
|
11
|
+
return Object.fromEntries(
|
|
12
|
+
Object.entries(props).map(
|
|
13
|
+
([table, initial]) => [table, useIndex<T>({ initial: initial as T & { id: string }[] })]
|
|
14
|
+
)
|
|
15
|
+
) as {
|
|
16
|
+
[table: string]: ReturnType<typeof useIndex<T>>
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export const [StoreProvider, useStore] = createContextFromHook(useStoreProvider<any>)
|