create-tsrouter-app 0.2.0 → 0.3.0-alpha.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/dist/add-ons.js +53 -0
- package/dist/cli.js +51 -0
- package/dist/constants.js +2 -0
- package/dist/create-app.js +285 -0
- package/dist/index.js +2 -347
- package/dist/options.js +231 -0
- package/dist/types.js +1 -0
- package/package.json +4 -3
- package/src/add-ons.ts +141 -0
- package/src/cli.ts +74 -0
- package/src/constants.ts +2 -0
- package/src/create-app.ts +445 -0
- package/src/index.ts +2 -507
- package/src/options.ts +264 -0
- package/src/types.ts +24 -0
- package/templates/add-on/clerk/README.md +3 -0
- package/templates/add-on/clerk/assets/.env.local.append +2 -0
- package/templates/add-on/clerk/assets/src/routes/demo.clerk.tsx +20 -0
- package/templates/add-on/clerk/info.json +28 -0
- package/templates/add-on/clerk/package.json +5 -0
- package/templates/add-on/convex/README.md +4 -0
- package/templates/add-on/convex/assets/.cursorrules.append +93 -0
- package/templates/add-on/convex/assets/.env.local.append +3 -0
- package/templates/add-on/convex/assets/convex/products.ts +8 -0
- package/templates/add-on/convex/assets/convex/schema.ts +10 -0
- package/templates/add-on/convex/assets/src/routes/demo.convex.tsx +33 -0
- package/templates/add-on/convex/info.json +27 -0
- package/templates/add-on/convex/package.json +6 -0
- package/templates/add-on/form/assets/src/routes/demo.form.tsx +50 -0
- package/templates/add-on/form/info.json +12 -0
- package/templates/add-on/form/package.json +5 -0
- package/templates/add-on/netlify/README.md +11 -0
- package/templates/add-on/netlify/info.json +6 -0
- package/templates/add-on/react-query/assets/src/routes/demo.react-query.tsx +30 -0
- package/templates/add-on/react-query/info.json +30 -0
- package/templates/add-on/react-query/package.json +6 -0
- package/templates/add-on/sentry/assets/.cursorrules +22 -0
- package/templates/add-on/sentry/assets/.env.local.append +2 -0
- package/templates/add-on/sentry/assets/src/routes/demo.sentry.bad-server-func.tsx +29 -0
- package/templates/add-on/sentry/info.json +13 -0
- package/templates/add-on/sentry/package.json +5 -0
- package/templates/add-on/shadcn/README.md +7 -0
- package/templates/add-on/shadcn/info.json +10 -0
- package/templates/add-on/start/assets/app.config.ts +16 -0
- package/templates/add-on/start/assets/postcss.config.ts +5 -0
- package/templates/add-on/start/assets/src/api.ts +6 -0
- package/templates/add-on/start/assets/src/client.tsx +10 -0
- package/templates/add-on/start/assets/src/router.tsx.ejs +34 -0
- package/templates/add-on/start/assets/src/routes/api.demo-names.ts +11 -0
- package/templates/add-on/start/assets/src/routes/demo.start.api-request.tsx.ejs +33 -0
- package/templates/add-on/start/assets/src/routes/demo.start.server-funcs.tsx +49 -0
- package/templates/add-on/start/assets/src/ssr.tsx +12 -0
- package/templates/add-on/start/info.json +18 -0
- package/templates/add-on/start/package.json +14 -0
- package/templates/add-on/store/assets/src/lib/demo-store.ts +5 -0
- package/templates/add-on/store/assets/src/routes/demo.store.page1.tsx +30 -0
- package/templates/add-on/store/assets/src/routes/demo.store.page2.tsx +30 -0
- package/templates/add-on/store/info.json +16 -0
- package/templates/add-on/store/package.json +6 -0
- package/templates/base/README.md.ejs +9 -0
- package/templates/base/package.json +1 -0
- package/templates/base/{tsconfig.json → tsconfig.json.ejs} +5 -1
- package/templates/base/vite.config.js.ejs +8 -0
- package/templates/example/ai-chat/assets/.env.local.append +2 -0
- package/templates/example/ai-chat/assets/src/routes/example.ai-chat.tsx.ejs +81 -0
- package/templates/example/ai-chat/info.json +27 -0
- package/templates/example/ai-chat/package.json +1 -0
- package/templates/file-router/src/components/Header.tsx.ejs +27 -0
- package/templates/file-router/src/routes/__root.tsx.ejs +80 -0
- package/templates/file-router/src/routes/__root.tsx +0 -11
- /package/dist/{utils/getPackageManager.js → package-manager.js} +0 -0
- /package/src/{utils/getPackageManager.ts → package-manager.ts} +0 -0
package/src/options.ts
ADDED
|
@@ -0,0 +1,264 @@
|
|
|
1
|
+
import {
|
|
2
|
+
cancel,
|
|
3
|
+
confirm,
|
|
4
|
+
isCancel,
|
|
5
|
+
multiselect,
|
|
6
|
+
select,
|
|
7
|
+
text,
|
|
8
|
+
} from '@clack/prompts'
|
|
9
|
+
|
|
10
|
+
import {
|
|
11
|
+
DEFAULT_PACKAGE_MANAGER,
|
|
12
|
+
SUPPORTED_PACKAGE_MANAGERS,
|
|
13
|
+
getPackageManager,
|
|
14
|
+
} from './package-manager.js'
|
|
15
|
+
import { CODE_ROUTER, FILE_ROUTER } from './constants.js'
|
|
16
|
+
import { finalizeAddOns, getAllAddOns } from './add-ons.js'
|
|
17
|
+
import type { Variable } from './add-ons.js'
|
|
18
|
+
|
|
19
|
+
import type { CliOptions, Options } from './types.js'
|
|
20
|
+
|
|
21
|
+
// If all CLI options are provided, use them directly
|
|
22
|
+
export function normalizeOptions(
|
|
23
|
+
cliOptions: CliOptions,
|
|
24
|
+
): Required<Options> | undefined {
|
|
25
|
+
if (cliOptions.projectName) {
|
|
26
|
+
const typescript =
|
|
27
|
+
cliOptions.template === 'typescript' ||
|
|
28
|
+
cliOptions.template === 'file-router'
|
|
29
|
+
|
|
30
|
+
return {
|
|
31
|
+
projectName: cliOptions.projectName,
|
|
32
|
+
typescript,
|
|
33
|
+
tailwind: !!cliOptions.tailwind,
|
|
34
|
+
packageManager: cliOptions.packageManager || DEFAULT_PACKAGE_MANAGER,
|
|
35
|
+
mode: cliOptions.template === 'file-router' ? FILE_ROUTER : CODE_ROUTER,
|
|
36
|
+
git: !!cliOptions.git,
|
|
37
|
+
addOns: !!cliOptions.addOns,
|
|
38
|
+
chosenAddOns: [],
|
|
39
|
+
variableValues: {},
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
async function collectVariables(
|
|
45
|
+
variables: Array<Variable>,
|
|
46
|
+
): Promise<Record<string, string | number | boolean>> {
|
|
47
|
+
const responses: Record<string, string | number | boolean> = {}
|
|
48
|
+
for (const variable of variables) {
|
|
49
|
+
if (variable.type === 'string') {
|
|
50
|
+
const response = await text({
|
|
51
|
+
message: variable.description,
|
|
52
|
+
initialValue: variable.default,
|
|
53
|
+
})
|
|
54
|
+
if (isCancel(response)) {
|
|
55
|
+
cancel('Operation cancelled.')
|
|
56
|
+
process.exit(0)
|
|
57
|
+
}
|
|
58
|
+
responses[variable.name] = response
|
|
59
|
+
} else if (variable.type === 'number') {
|
|
60
|
+
const response = await text({
|
|
61
|
+
message: variable.description,
|
|
62
|
+
initialValue: variable.default.toString(),
|
|
63
|
+
})
|
|
64
|
+
if (isCancel(response)) {
|
|
65
|
+
cancel('Operation cancelled.')
|
|
66
|
+
process.exit(0)
|
|
67
|
+
}
|
|
68
|
+
responses[variable.name] = Number(response)
|
|
69
|
+
} else {
|
|
70
|
+
const response = await confirm({
|
|
71
|
+
message: variable.description,
|
|
72
|
+
initialValue: variable.default === true,
|
|
73
|
+
})
|
|
74
|
+
if (isCancel(response)) {
|
|
75
|
+
cancel('Operation cancelled.')
|
|
76
|
+
process.exit(0)
|
|
77
|
+
}
|
|
78
|
+
responses[variable.name] = response
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
return responses
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
export async function promptForOptions(
|
|
85
|
+
cliOptions: CliOptions,
|
|
86
|
+
): Promise<Required<Options>> {
|
|
87
|
+
const options = {} as Required<Options>
|
|
88
|
+
|
|
89
|
+
if (!cliOptions.projectName) {
|
|
90
|
+
const value = await text({
|
|
91
|
+
message: 'What would you like to name your project?',
|
|
92
|
+
defaultValue: 'my-app',
|
|
93
|
+
validate(value) {
|
|
94
|
+
if (!value) {
|
|
95
|
+
return 'Please enter a name'
|
|
96
|
+
}
|
|
97
|
+
},
|
|
98
|
+
})
|
|
99
|
+
if (isCancel(value)) {
|
|
100
|
+
cancel('Operation cancelled.')
|
|
101
|
+
process.exit(0)
|
|
102
|
+
}
|
|
103
|
+
options.projectName = value
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
// Router type selection
|
|
107
|
+
if (!cliOptions.template) {
|
|
108
|
+
const routerType = await select({
|
|
109
|
+
message: 'Select the router type:',
|
|
110
|
+
options: [
|
|
111
|
+
{
|
|
112
|
+
value: FILE_ROUTER,
|
|
113
|
+
label: 'File Router - File-based routing structure',
|
|
114
|
+
},
|
|
115
|
+
{
|
|
116
|
+
value: CODE_ROUTER,
|
|
117
|
+
label: 'Code Router - Traditional code-based routing',
|
|
118
|
+
},
|
|
119
|
+
],
|
|
120
|
+
initialValue: FILE_ROUTER,
|
|
121
|
+
})
|
|
122
|
+
if (isCancel(routerType)) {
|
|
123
|
+
cancel('Operation cancelled.')
|
|
124
|
+
process.exit(0)
|
|
125
|
+
}
|
|
126
|
+
options.mode = routerType as typeof CODE_ROUTER | typeof FILE_ROUTER
|
|
127
|
+
} else {
|
|
128
|
+
options.mode = cliOptions.template as
|
|
129
|
+
| typeof CODE_ROUTER
|
|
130
|
+
| typeof FILE_ROUTER
|
|
131
|
+
if (options.mode === FILE_ROUTER) {
|
|
132
|
+
options.typescript = true
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
// TypeScript selection (if using Code Router)
|
|
137
|
+
if (!options.typescript) {
|
|
138
|
+
if (options.mode === CODE_ROUTER) {
|
|
139
|
+
const typescriptEnable = await confirm({
|
|
140
|
+
message: 'Would you like to use TypeScript?',
|
|
141
|
+
initialValue: true,
|
|
142
|
+
})
|
|
143
|
+
if (isCancel(typescriptEnable)) {
|
|
144
|
+
cancel('Operation cancelled.')
|
|
145
|
+
process.exit(0)
|
|
146
|
+
}
|
|
147
|
+
options.typescript = typescriptEnable
|
|
148
|
+
} else {
|
|
149
|
+
options.typescript = true
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
// Tailwind selection
|
|
154
|
+
if (cliOptions.tailwind === undefined) {
|
|
155
|
+
const tailwind = await confirm({
|
|
156
|
+
message: 'Would you like to use Tailwind CSS?',
|
|
157
|
+
initialValue: true,
|
|
158
|
+
})
|
|
159
|
+
if (isCancel(tailwind)) {
|
|
160
|
+
cancel('Operation cancelled.')
|
|
161
|
+
process.exit(0)
|
|
162
|
+
}
|
|
163
|
+
options.tailwind = tailwind
|
|
164
|
+
} else {
|
|
165
|
+
options.tailwind = cliOptions.tailwind
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
// Package manager selection
|
|
169
|
+
if (cliOptions.packageManager === undefined) {
|
|
170
|
+
const detectedPackageManager = getPackageManager()
|
|
171
|
+
if (!detectedPackageManager) {
|
|
172
|
+
const pm = await select({
|
|
173
|
+
message: 'Select package manager:',
|
|
174
|
+
options: SUPPORTED_PACKAGE_MANAGERS.map((pm) => ({
|
|
175
|
+
value: pm,
|
|
176
|
+
label: pm,
|
|
177
|
+
})),
|
|
178
|
+
initialValue: DEFAULT_PACKAGE_MANAGER,
|
|
179
|
+
})
|
|
180
|
+
if (isCancel(pm)) {
|
|
181
|
+
cancel('Operation cancelled.')
|
|
182
|
+
process.exit(0)
|
|
183
|
+
}
|
|
184
|
+
options.packageManager = pm
|
|
185
|
+
} else {
|
|
186
|
+
options.packageManager = detectedPackageManager
|
|
187
|
+
}
|
|
188
|
+
} else {
|
|
189
|
+
options.packageManager = cliOptions.packageManager
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
// Select any add-ons
|
|
193
|
+
if (options.mode === FILE_ROUTER && cliOptions.addOns) {
|
|
194
|
+
const addOns = await getAllAddOns()
|
|
195
|
+
|
|
196
|
+
const selectedAddOns = await multiselect({
|
|
197
|
+
message: 'What add-ons would you like for your project:',
|
|
198
|
+
options: addOns
|
|
199
|
+
.filter((addOn) => addOn.type === 'add-on')
|
|
200
|
+
.map((addOn) => ({
|
|
201
|
+
value: addOn.id,
|
|
202
|
+
label: addOn.name,
|
|
203
|
+
hint: addOn.description,
|
|
204
|
+
})),
|
|
205
|
+
required: false,
|
|
206
|
+
})
|
|
207
|
+
|
|
208
|
+
if (isCancel(selectedAddOns)) {
|
|
209
|
+
cancel('Operation cancelled.')
|
|
210
|
+
process.exit(0)
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
const selectedExamples = await multiselect({
|
|
214
|
+
message: 'Would you like any examples?',
|
|
215
|
+
options: addOns
|
|
216
|
+
.filter((addOn) => addOn.type === 'example')
|
|
217
|
+
.map((addOn) => ({
|
|
218
|
+
value: addOn.id,
|
|
219
|
+
label: addOn.name,
|
|
220
|
+
hint: addOn.description,
|
|
221
|
+
})),
|
|
222
|
+
required: false,
|
|
223
|
+
})
|
|
224
|
+
|
|
225
|
+
if (isCancel(selectedExamples)) {
|
|
226
|
+
cancel('Operation cancelled.')
|
|
227
|
+
process.exit(0)
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
options.chosenAddOns = await finalizeAddOns([
|
|
231
|
+
...selectedAddOns,
|
|
232
|
+
...selectedExamples,
|
|
233
|
+
])
|
|
234
|
+
options.tailwind = true
|
|
235
|
+
} else {
|
|
236
|
+
options.chosenAddOns = []
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
// Collect variables
|
|
240
|
+
const variables: Array<Variable> = []
|
|
241
|
+
for (const addOn of options.chosenAddOns) {
|
|
242
|
+
for (const variable of addOn.variables ?? []) {
|
|
243
|
+
variables.push(variable)
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
options.variableValues = await collectVariables(variables)
|
|
247
|
+
|
|
248
|
+
// Git selection
|
|
249
|
+
if (cliOptions.git === undefined) {
|
|
250
|
+
const git = await confirm({
|
|
251
|
+
message: 'Would you like to initialize a new git repository?',
|
|
252
|
+
initialValue: true,
|
|
253
|
+
})
|
|
254
|
+
if (isCancel(git)) {
|
|
255
|
+
cancel('Operation cancelled.')
|
|
256
|
+
process.exit(0)
|
|
257
|
+
}
|
|
258
|
+
options.git = git
|
|
259
|
+
} else {
|
|
260
|
+
options.git = !!cliOptions.git
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
return options
|
|
264
|
+
}
|
package/src/types.ts
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import type { AddOn } from './add-ons.js'
|
|
2
|
+
import type { CODE_ROUTER, FILE_ROUTER } from './constants.js'
|
|
3
|
+
import type { PackageManager } from './package-manager.js'
|
|
4
|
+
|
|
5
|
+
export interface Options {
|
|
6
|
+
projectName: string
|
|
7
|
+
typescript: boolean
|
|
8
|
+
tailwind: boolean
|
|
9
|
+
packageManager: PackageManager
|
|
10
|
+
mode: typeof CODE_ROUTER | typeof FILE_ROUTER
|
|
11
|
+
addOns: boolean
|
|
12
|
+
chosenAddOns: Array<AddOn>
|
|
13
|
+
git: boolean
|
|
14
|
+
variableValues: Record<string, string | number | boolean>
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export interface CliOptions {
|
|
18
|
+
template?: 'typescript' | 'javascript' | 'file-router'
|
|
19
|
+
tailwind?: boolean
|
|
20
|
+
packageManager?: PackageManager
|
|
21
|
+
projectName?: string
|
|
22
|
+
git?: boolean
|
|
23
|
+
addOns?: boolean
|
|
24
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { createFileRoute } from '@tanstack/react-router'
|
|
2
|
+
import { useUser } from '@clerk/clerk-react'
|
|
3
|
+
|
|
4
|
+
export const Route = createFileRoute('/demo/clerk')({
|
|
5
|
+
component: App,
|
|
6
|
+
})
|
|
7
|
+
|
|
8
|
+
function App() {
|
|
9
|
+
const { isSignedIn, user, isLoaded } = useUser()
|
|
10
|
+
|
|
11
|
+
if (!isLoaded) {
|
|
12
|
+
return <div className="p-4">Loading...</div>
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
if (!isSignedIn) {
|
|
16
|
+
return <div className="p-4">Sign in to view this page</div>
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
return <div className="p-4">Hello {user.firstName}!</div>
|
|
20
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "Clerk",
|
|
3
|
+
"description": "Add Clerk authentication to your application.",
|
|
4
|
+
"phase": "add-on",
|
|
5
|
+
"link": "https://clerk.com",
|
|
6
|
+
"main": {
|
|
7
|
+
"imports": ["import { ClerkProvider } from '@clerk/clerk-react'"],
|
|
8
|
+
"initialize": [
|
|
9
|
+
"const PUBLISHABLE_KEY = import.meta.env.VITE_CLERK_PUBLISHABLE_KEY\nif (!PUBLISHABLE_KEY) {\n throw new Error('Add your Clerk Publishable Key to the .env.local file');\n}"
|
|
10
|
+
],
|
|
11
|
+
"providers": [
|
|
12
|
+
{
|
|
13
|
+
"open": "<ClerkProvider publishableKey={PUBLISHABLE_KEY} afterSignOutUrl=\"/\">",
|
|
14
|
+
"close": "</ClerkProvider>"
|
|
15
|
+
}
|
|
16
|
+
]
|
|
17
|
+
},
|
|
18
|
+
"userUi": {
|
|
19
|
+
"import": "import { SignedIn, SignInButton, SignedOut, UserButton } from '@clerk/clerk-react'",
|
|
20
|
+
"jsx": "<SignedIn><UserButton /></SignedIn><SignedOut><SignInButton /></SignedOut>"
|
|
21
|
+
},
|
|
22
|
+
"routes": [
|
|
23
|
+
{
|
|
24
|
+
"url": "/demo/clerk",
|
|
25
|
+
"name": "Clerk"
|
|
26
|
+
}
|
|
27
|
+
]
|
|
28
|
+
}
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
This document serves as some special instructions when working with Convex.
|
|
2
|
+
|
|
3
|
+
# Schemas
|
|
4
|
+
|
|
5
|
+
When designing the schema please see this page on built in System fields and data types available: https://docs.convex.dev/database/types
|
|
6
|
+
|
|
7
|
+
Here are some specifics that are often mishandled:
|
|
8
|
+
|
|
9
|
+
## v (https://docs.convex.dev/api/modules/values#v)
|
|
10
|
+
|
|
11
|
+
The validator builder.
|
|
12
|
+
|
|
13
|
+
This builder allows you to build validators for Convex values.
|
|
14
|
+
|
|
15
|
+
Validators can be used in schema definitions and as input validators for Convex functions.
|
|
16
|
+
|
|
17
|
+
Type declaration
|
|
18
|
+
Name Type
|
|
19
|
+
id <TableName>(tableName: TableName) => VId<GenericId<TableName>, "required">
|
|
20
|
+
null () => VNull<null, "required">
|
|
21
|
+
number () => VFloat64<number, "required">
|
|
22
|
+
float64 () => VFloat64<number, "required">
|
|
23
|
+
bigint () => VInt64<bigint, "required">
|
|
24
|
+
int64 () => VInt64<bigint, "required">
|
|
25
|
+
boolean () => VBoolean<boolean, "required">
|
|
26
|
+
string () => VString<string, "required">
|
|
27
|
+
bytes () => VBytes<ArrayBuffer, "required">
|
|
28
|
+
literal <T>(literal: T) => VLiteral<T, "required">
|
|
29
|
+
array <T>(element: T) => VArray<T["type"][], T, "required">
|
|
30
|
+
object <T>(fields: T) => VObject<Expand<{ [Property in string | number | symbol]?: Exclude<Infer<T[Property]>, undefined> } & { [Property in string | number | symbol]: Infer<T[Property]> }>, T, "required", { [Property in string | number | symbol]: Property | `${Property & string}.${T[Property]["fieldPaths"]}` }[keyof T] & string>
|
|
31
|
+
record <Key, Value>(keys: Key, values: Value) => VRecord<Record<Infer<Key>, Value["type"]>, Key, Value, "required", string>
|
|
32
|
+
union <T>(...members: T) => VUnion<T[number]["type"], T, "required", T[number]["fieldPaths"]>
|
|
33
|
+
any () => VAny<any, "required", string>
|
|
34
|
+
optional <T>(value: T) => VOptional<T>
|
|
35
|
+
|
|
36
|
+
## System fields (https://docs.convex.dev/database/types#system-fields)
|
|
37
|
+
|
|
38
|
+
Every document in Convex has two automatically-generated system fields:
|
|
39
|
+
|
|
40
|
+
_id: The document ID of the document.
|
|
41
|
+
_creationTime: The time this document was created, in milliseconds since the Unix epoch.
|
|
42
|
+
|
|
43
|
+
You do not need to add indices as these are added automatically.
|
|
44
|
+
|
|
45
|
+
## Example Schema
|
|
46
|
+
|
|
47
|
+
This is an example of a well crafted schema.
|
|
48
|
+
|
|
49
|
+
```ts
|
|
50
|
+
import { defineSchema, defineTable } from "convex/server";
|
|
51
|
+
import { v } from "convex/values";
|
|
52
|
+
|
|
53
|
+
export default defineSchema(
|
|
54
|
+
{
|
|
55
|
+
users: defineTable({
|
|
56
|
+
name: v.string(),
|
|
57
|
+
}),
|
|
58
|
+
|
|
59
|
+
sessions: defineTable({
|
|
60
|
+
userId: v.id("users"),
|
|
61
|
+
sessionId: v.string(),
|
|
62
|
+
}).index("sessionId", ["sessionId"]),
|
|
63
|
+
|
|
64
|
+
threads: defineTable({
|
|
65
|
+
uuid: v.string(),
|
|
66
|
+
summary: v.optional(v.string()),
|
|
67
|
+
summarizer: v.optional(v.id("_scheduled_functions")),
|
|
68
|
+
}).index("uuid", ["uuid"]),
|
|
69
|
+
|
|
70
|
+
messages: defineTable({
|
|
71
|
+
message: v.string(),
|
|
72
|
+
threadId: v.id("threads"),
|
|
73
|
+
author: v.union(
|
|
74
|
+
v.object({
|
|
75
|
+
role: v.literal("system"),
|
|
76
|
+
}),
|
|
77
|
+
v.object({
|
|
78
|
+
role: v.literal("assistant"),
|
|
79
|
+
context: v.array(v.id("messages")),
|
|
80
|
+
model: v.optional(v.string()),
|
|
81
|
+
}),
|
|
82
|
+
v.object({
|
|
83
|
+
role: v.literal("user"),
|
|
84
|
+
userId: v.id("users"),
|
|
85
|
+
}),
|
|
86
|
+
),
|
|
87
|
+
})
|
|
88
|
+
.index("threadId", ["threadId"]),
|
|
89
|
+
},
|
|
90
|
+
);
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
Sourced from: https://github.com/PatrickJS/awesome-cursorrules/blob/main/rules/convex-cursorrules-prompt-file/.cursorrules
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { Suspense } from 'react'
|
|
2
|
+
import { createFileRoute } from '@tanstack/react-router'
|
|
3
|
+
import { useQuery } from 'convex/react'
|
|
4
|
+
|
|
5
|
+
import { api } from '../../convex/_generated/api'
|
|
6
|
+
|
|
7
|
+
export const Route = createFileRoute('/demo/convex')({
|
|
8
|
+
component: App,
|
|
9
|
+
})
|
|
10
|
+
|
|
11
|
+
function Products() {
|
|
12
|
+
const products = useQuery(api.products.get)
|
|
13
|
+
|
|
14
|
+
return (
|
|
15
|
+
<ul>
|
|
16
|
+
{(products || []).map((p) => (
|
|
17
|
+
<li key={p._id}>
|
|
18
|
+
{p.title} - {p.price}
|
|
19
|
+
</li>
|
|
20
|
+
))}
|
|
21
|
+
</ul>
|
|
22
|
+
)
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function App() {
|
|
26
|
+
return (
|
|
27
|
+
<div className="p-4">
|
|
28
|
+
<Suspense fallback={<div>Loading...</div>}>
|
|
29
|
+
<Products />
|
|
30
|
+
</Suspense>
|
|
31
|
+
</div>
|
|
32
|
+
)
|
|
33
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "Convex",
|
|
3
|
+
"description": "Add the Convex database to your application.",
|
|
4
|
+
"link": "https://convex.dev",
|
|
5
|
+
"phase": "add-on",
|
|
6
|
+
"main": {
|
|
7
|
+
"imports": [
|
|
8
|
+
"import { ConvexProvider } from 'convex/react'",
|
|
9
|
+
"import { ConvexQueryClient } from '@convex-dev/react-query'"
|
|
10
|
+
],
|
|
11
|
+
"initialize": [
|
|
12
|
+
"const CONVEX_URL = (import.meta as any).env.VITE_CONVEX_URL!\nif (!CONVEX_URL) {\n console.error(\"missing envar CONVEX_URL\");\n}\nconst convexQueryClient = new ConvexQueryClient(CONVEX_URL);"
|
|
13
|
+
],
|
|
14
|
+
"providers": [
|
|
15
|
+
{
|
|
16
|
+
"open": "<ConvexProvider client={convexQueryClient.convexClient}>",
|
|
17
|
+
"close": "</ConvexProvider>"
|
|
18
|
+
}
|
|
19
|
+
]
|
|
20
|
+
},
|
|
21
|
+
"routes": [
|
|
22
|
+
{
|
|
23
|
+
"url": "/demo/convex",
|
|
24
|
+
"name": "Convex"
|
|
25
|
+
}
|
|
26
|
+
]
|
|
27
|
+
}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { createFileRoute } from '@tanstack/react-router'
|
|
2
|
+
import { useForm } from '@tanstack/react-form'
|
|
3
|
+
|
|
4
|
+
export const Route = createFileRoute('/demo/form')({
|
|
5
|
+
component: App,
|
|
6
|
+
})
|
|
7
|
+
|
|
8
|
+
export default function App() {
|
|
9
|
+
const form = useForm({
|
|
10
|
+
defaultValues: {
|
|
11
|
+
fullName: '',
|
|
12
|
+
},
|
|
13
|
+
onSubmit: async ({ value }) => {
|
|
14
|
+
console.log(value)
|
|
15
|
+
},
|
|
16
|
+
})
|
|
17
|
+
|
|
18
|
+
return (
|
|
19
|
+
<div className="p-4">
|
|
20
|
+
<form
|
|
21
|
+
onSubmit={(e) => {
|
|
22
|
+
e.preventDefault()
|
|
23
|
+
e.stopPropagation()
|
|
24
|
+
form.handleSubmit()
|
|
25
|
+
}}
|
|
26
|
+
>
|
|
27
|
+
<div>
|
|
28
|
+
<form.Field
|
|
29
|
+
name="fullName"
|
|
30
|
+
children={(field) => (
|
|
31
|
+
<input
|
|
32
|
+
name={field.name}
|
|
33
|
+
value={field.state.value}
|
|
34
|
+
onBlur={field.handleBlur}
|
|
35
|
+
onChange={(e) => field.handleChange(e.target.value)}
|
|
36
|
+
className="block w-full px-4 py-2 text-gray-700 bg-white border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-primary-500 focus:border-primary-500"
|
|
37
|
+
/>
|
|
38
|
+
)}
|
|
39
|
+
/>
|
|
40
|
+
</div>
|
|
41
|
+
<button
|
|
42
|
+
type="submit"
|
|
43
|
+
className="mt-4 inline-flex items-center px-6 py-3 border border-transparent shadow-sm text-base font-medium rounded-md text-white bg-indigo-600 hover:bg-indigo-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500"
|
|
44
|
+
>
|
|
45
|
+
Submit
|
|
46
|
+
</button>
|
|
47
|
+
</form>
|
|
48
|
+
</div>
|
|
49
|
+
)
|
|
50
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { createFileRoute } from '@tanstack/react-router'
|
|
2
|
+
import { useQuery } from '@tanstack/react-query'
|
|
3
|
+
|
|
4
|
+
export const Route = createFileRoute('/demo/react-query')({
|
|
5
|
+
component: App,
|
|
6
|
+
})
|
|
7
|
+
|
|
8
|
+
function App() {
|
|
9
|
+
const { data } = useQuery({
|
|
10
|
+
queryKey: ['people'],
|
|
11
|
+
queryFn: () =>
|
|
12
|
+
fetch('https://swapi.dev/api/people')
|
|
13
|
+
.then((res) => res.json())
|
|
14
|
+
.then((data) => data.results as { name: string }[]),
|
|
15
|
+
initialData: [],
|
|
16
|
+
})
|
|
17
|
+
|
|
18
|
+
return (
|
|
19
|
+
<div className="p-4">
|
|
20
|
+
<h1 className="text-2xl mb-4">People list from Swapi</h1>
|
|
21
|
+
<ul>
|
|
22
|
+
{data.map((person) => (
|
|
23
|
+
<li key={person.name}>{person.name}</li>
|
|
24
|
+
))}
|
|
25
|
+
</ul>
|
|
26
|
+
</div>
|
|
27
|
+
)
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export default App
|