create-z3 0.0.40 → 0.0.41

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-z3",
3
- "version": "0.0.40",
3
+ "version": "0.0.41",
4
4
  "type": "module",
5
5
  "description": "CLI for scaffolding Z3 Stack applications (TanStack/Next.js + Convex + Better Auth)",
6
6
  "bin": {
@@ -189,15 +189,26 @@ export const convexAdapter = <DataModel extends GenericDataModel>(
189
189
  if (data && fieldAttributes.type === "date") {
190
190
  return new Date(data).getTime();
191
191
  }
192
- // Handle array fields - Better Auth serializes them as JSON strings
192
+ // Handle array fields - Better Auth may send single values or arrays
193
193
  if ((fieldAttributes.type as string)?.endsWith("[]")) {
194
+ // If already an array, return as-is
195
+ if (Array.isArray(data)) {
196
+ return data
197
+ }
198
+ // If it's a string that looks like JSON array, parse it
194
199
  if (typeof data === "string") {
195
200
  try {
196
- return JSON.parse(data);
201
+ const parsed = JSON.parse(data)
202
+ return Array.isArray(parsed) ? parsed : [data]
197
203
  } catch {
198
- return data;
204
+ // If parsing fails, wrap the string in an array
205
+ return [data]
199
206
  }
200
207
  }
208
+ // For any other value type, wrap in array
209
+ if (data !== null && data !== undefined) {
210
+ return [data]
211
+ }
201
212
  }
202
213
  return data;
203
214
  },
@@ -6,6 +6,9 @@ import { TABLE_SLUG_ACCOUNTS, TABLE_SLUG_JWKS, TABLE_SLUG_SESSIONS, TABLE_SLUG_U
6
6
  export default defineSchema({
7
7
  // Better Auth component tables (type definitions only - actual tables are in component)
8
8
  [TABLE_SLUG_USERS]: defineTable({
9
+ banExpires: v.optional(v.number()), // admin plugin
10
+ banned: v.optional(v.boolean()), // admin plugin
11
+ banReason: v.optional(v.string()), // admin plugin
9
12
  displayUsername: v.optional(v.union(v.null(), v.string())),
10
13
  name: v.string(),
11
14
  username: v.optional(v.union(v.null(), v.string())),
@@ -16,7 +19,7 @@ export default defineSchema({
16
19
  isAnonymous: v.optional(v.union(v.null(), v.boolean())),
17
20
  phoneNumber: v.optional(v.union(v.null(), v.string())),
18
21
  phoneNumberVerified: v.optional(v.union(v.null(), v.boolean())),
19
- role: v.array(v.string()),
22
+ role: v.array(v.string()), // admin plugin
20
23
  twoFactorEnabled: v.optional(v.union(v.null(), v.boolean())),
21
24
  updatedAt: v.number(),
22
25
  userId: v.optional(v.union(v.null(), v.string()))
@@ -43,6 +46,7 @@ export default defineSchema({
43
46
  [TABLE_SLUG_SESSIONS]: defineTable({
44
47
  createdAt: v.number(),
45
48
  expiresAt: v.number(),
49
+ impersonatedBy: v.optional(v.id(TABLE_SLUG_USERS)), // admin plugin
46
50
  ipAddress: v.optional(v.string()),
47
51
  token: v.string(),
48
52
  updatedAt: v.number(),
@@ -185,15 +185,26 @@ export const convexAdapter = <DataModel extends GenericDataModel>(
185
185
  if (data && fieldAttributes.type === "date") {
186
186
  return new Date(data).getTime();
187
187
  }
188
- // Handle array fields - Better Auth serializes them as JSON strings
189
- if (fieldAttributes.type && fieldAttributes.type.endsWith("[]")) {
188
+ // Handle array fields - Better Auth may send single values or arrays
189
+ if ((fieldAttributes.type as string)?.endsWith("[]")) {
190
+ // If already an array, return as-is
191
+ if (Array.isArray(data)) {
192
+ return data
193
+ }
194
+ // If it's a string that looks like JSON array, parse it
190
195
  if (typeof data === "string") {
191
196
  try {
192
- return JSON.parse(data);
197
+ const parsed = JSON.parse(data)
198
+ return Array.isArray(parsed) ? parsed : [data]
193
199
  } catch {
194
- return data;
200
+ // If parsing fails, wrap the string in an array
201
+ return [data]
195
202
  }
196
203
  }
204
+ // For any other value type, wrap in array
205
+ if (data !== null && data !== undefined) {
206
+ return [data]
207
+ }
197
208
  }
198
209
  return data;
199
210
  },
@@ -1,6 +1,11 @@
1
- import { apiKey } from "better-auth/plugins"
1
+ import { admin, apiKey } from "better-auth/plugins"
2
+ import { USER_ROLES } from "~/db/constants"
2
3
 
3
4
  const plugins = [
5
+ admin({
6
+ adminRoles: [USER_ROLES.admin],
7
+ defaultRole: USER_ROLES.user
8
+ }),
4
9
  apiKey(),
5
10
  ]
6
11
 
@@ -16,10 +16,13 @@ export default defineSchema({
16
16
  isAnonymous: v.optional(v.union(v.null(), v.boolean())),
17
17
  phoneNumber: v.optional(v.union(v.null(), v.string())),
18
18
  phoneNumberVerified: v.optional(v.union(v.null(), v.boolean())),
19
- role: v.array(v.string()),
20
19
  twoFactorEnabled: v.optional(v.union(v.null(), v.boolean())),
21
20
  updatedAt: v.number(),
22
- userId: v.optional(v.union(v.null(), v.string()))
21
+ userId: v.optional(v.union(v.null(), v.string())),
22
+ banExpires: v.optional(v.number()), // admin plugin
23
+ banned: v.optional(v.boolean()), // admin plugin
24
+ banReason: v.optional(v.string()), // admin plugin
25
+ role: v.array(v.string()), // admin plugin
23
26
  })
24
27
  .index("by_email", ["email"]),
25
28
 
@@ -48,6 +51,7 @@ export default defineSchema({
48
51
  updatedAt: v.number(),
49
52
  userAgent: v.optional(v.string()),
50
53
  userId: v.id(TABLE_SLUG_USERS),
54
+ impersonatedBy: v.optional(v.id(TABLE_SLUG_USERS)), // admin plugin
51
55
  })
52
56
  .index("by_token", ["token"]),
53
57
 
@@ -1,11 +1,12 @@
1
1
  import { createAuthClient } from "better-auth/react"
2
- import { apiKeyClient } from "better-auth/client/plugins"
2
+ import { adminClient, apiKeyClient } from "better-auth/client/plugins"
3
3
  import { env } from '~/env';
4
4
 
5
5
  export const authClient = createAuthClient({
6
6
  basePath: "/api/auth",
7
7
  baseURL: env.VITE_SITE_URL, // Point to TanStack Start, which proxies to Convex
8
8
  plugins: [
9
+ adminClient(),
9
10
  apiKeyClient()
10
11
  ]
11
12
  })