@tscircuit/fake-snippets 0.0.114 → 0.0.116

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/index.js CHANGED
@@ -593,7 +593,7 @@ var seed = (db) => {
593
593
  phone: "555-123-4567"
594
594
  }
595
595
  });
596
- db.addAccount({
596
+ const seveibarAcc = db.addAccount({
597
597
  github_username: "seveibar"
598
598
  });
599
599
  if (process.env.AUTOLOAD_PACKAGES === "true") {
@@ -2370,6 +2370,10 @@ exports.TestComponent = TestComponent;
2370
2370
  github_handle: "tscircuit",
2371
2371
  is_personal_org: true
2372
2372
  });
2373
+ db.addOrganizationAccount({
2374
+ org_id: testOrg.org_id,
2375
+ account_id: seveibarAcc.account_id
2376
+ });
2373
2377
  };
2374
2378
 
2375
2379
  // fake-snippets-api/lib/package_file/generate-fs-sha.ts
@@ -18,7 +18,7 @@ export const seed = (db: DbClient) => {
18
18
  phone: "555-123-4567",
19
19
  },
20
20
  })
21
- db.addAccount({
21
+ const seveibarAcc = db.addAccount({
22
22
  github_username: "seveibar",
23
23
  })
24
24
 
@@ -1835,4 +1835,9 @@ exports.TestComponent = TestComponent;
1835
1835
  github_handle: "tscircuit",
1836
1836
  is_personal_org: true,
1837
1837
  })
1838
+
1839
+ db.addOrganizationAccount({
1840
+ org_id: testOrg.org_id,
1841
+ account_id: seveibarAcc.account_id,
1842
+ })
1838
1843
  }
@@ -0,0 +1,16 @@
1
+ import { withRouteSpec } from "fake-snippets-api/lib/middleware/with-winter-spec"
2
+ import { z } from "zod"
3
+
4
+ export default withRouteSpec({
5
+ methods: ["POST"],
6
+ auth: "session",
7
+ jsonBody: z.object({
8
+ prefix: z.string(),
9
+ suffix: z.string(),
10
+ }),
11
+ jsonResponse: z.object({
12
+ prediction: z.string(),
13
+ }),
14
+ })(async (req, ctx) => {
15
+ return ctx.json({ prediction: "{/* mock autocomplete prediction */}" })
16
+ })
@@ -18,35 +18,44 @@ export default withRouteSpec({
18
18
  description: z.string().optional(),
19
19
  is_private: z.boolean().optional().default(false),
20
20
  is_unlisted: z.boolean().optional().default(false),
21
+ org_id: z.string().optional(),
21
22
  }),
22
23
  jsonResponse: z.object({
23
24
  package: packageSchema.optional(),
24
25
  }),
25
26
  })(async (req, ctx) => {
26
- const { name, description, is_private, is_unlisted } = req.jsonBody
27
-
27
+ const { name, description, is_private, is_unlisted, org_id } = req.jsonBody
28
28
  let owner_segment = name?.split("/")[0]
29
29
  let unscoped_name = name?.split("/")[1]
30
30
 
31
31
  if (name && !unscoped_name) {
32
- throw ctx.error(400, {
32
+ return ctx.error(400, {
33
33
  error_code: "invalid_package_name",
34
34
  message:
35
35
  "Package name must include an author segment (e.g. author/package_name)",
36
36
  })
37
37
  }
38
+ const org = ctx.db.getOrg({ org_id })
39
+ if (Boolean(org_id) && !Boolean(org))
40
+ return ctx.error(404, {
41
+ error_code: "org_not_found",
42
+ message: "Organization not found",
43
+ })
38
44
 
39
45
  if (!unscoped_name) {
40
46
  const state = ctx.db.getState()
41
- const count = state.packages.filter(
42
- (pkg) => pkg.creator_account_id === ctx.auth.account_id,
43
- ).length
47
+
48
+ const count = org_id
49
+ ? state.packages.filter((pkg) => pkg.owner_org_id === org_id).length
50
+ : state.packages.filter(
51
+ (pkg) => pkg.creator_account_id === ctx.auth.account_id,
52
+ ).length
44
53
 
45
54
  unscoped_name = `untitled-package-${count}`
46
55
  }
47
56
 
48
57
  if (!owner_segment) {
49
- owner_segment = ctx.auth.github_username
58
+ owner_segment = org_id ? org!.org_name : ctx.auth.github_username
50
59
  }
51
60
 
52
61
  const final_name = name ?? `${owner_segment}/${unscoped_name}`
@@ -71,7 +80,7 @@ export default withRouteSpec({
71
80
  )
72
81
 
73
82
  if (!memberOrg) {
74
- throw ctx.error(403, {
83
+ return ctx.error(403, {
75
84
  error_code: "forbidden",
76
85
  message:
77
86
  "You must be a member of the organization to create a package under it",
@@ -87,7 +96,7 @@ export default withRouteSpec({
87
96
  .packages.find((pkg) => pkg.name === final_name)
88
97
 
89
98
  if (existingPackage) {
90
- throw ctx.error(400, {
99
+ return ctx.error(400, {
91
100
  error_code: "package_already_exists",
92
101
  message: "A package with this name already exists",
93
102
  })
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tscircuit/fake-snippets",
3
- "version": "0.0.114",
3
+ "version": "0.0.116",
4
4
  "type": "module",
5
5
  "repository": {
6
6
  "type": "git",
@@ -81,7 +81,7 @@
81
81
  "@tscircuit/3d-viewer": "^0.0.407",
82
82
  "@tscircuit/assembly-viewer": "^0.0.5",
83
83
  "@tscircuit/create-snippet-url": "^0.0.8",
84
- "@tscircuit/eval": "^0.0.325",
84
+ "@tscircuit/eval": "^0.0.406",
85
85
  "@tscircuit/layout": "^0.0.29",
86
86
  "@tscircuit/mm": "^0.0.8",
87
87
  "@tscircuit/pcb-viewer": "^1.11.218",
@@ -14,7 +14,7 @@ export const GithubAvatarWithFallback = ({
14
14
  size?: number
15
15
  }) => {
16
16
  return (
17
- <Avatar className={`border-4 border-gray-100 ${className}`}>
17
+ <Avatar className={className}>
18
18
  <AvatarImage
19
19
  src={`https://github.com/${username}.png${size ? `?size=${size}` : ""}`}
20
20
  alt={`${username || fallback} avatar`}
@@ -19,6 +19,7 @@ import {
19
19
  import { createUseDialog } from "./create-use-dialog"
20
20
  import { useListUserOrgs } from "@/hooks/use-list-user-orgs"
21
21
  import { useGlobalStore } from "@/hooks/use-global-store"
22
+ import { ICreatePackageProps } from "@/hooks/useFileManagement"
22
23
 
23
24
  export const NewPackageSavePromptDialog = ({
24
25
  open,
@@ -31,27 +32,31 @@ export const NewPackageSavePromptDialog = ({
31
32
  onOpenChange: (open: boolean) => void
32
33
  initialIsPrivate?: boolean
33
34
  initialName?: string
34
- onSave: ({
35
- name,
36
- isPrivate,
37
- orgId,
38
- }: {
39
- name?: string
40
- isPrivate: boolean
41
- orgId: string
42
- }) => void
35
+ onSave: (props: ICreatePackageProps) => void
43
36
  }) => {
44
37
  const [packageName, setPackageName] = useState(initialName)
45
38
  const session = useGlobalStore((s) => s.session)
46
39
  const [isPrivate, setIsPrivate] = useState(initialIsPrivate)
47
40
  const [selectedOrgId, setSelectedOrgId] = useState<string>("")
48
41
  const { data: organizations, isLoading: orgsLoading } = useListUserOrgs()
42
+
43
+ const isOwnerPersonalOrg = useMemo(() => {
44
+ if (!selectedOrgId) return false
45
+ const selectedOrg = organizations?.find((x) => x.org_id === selectedOrgId)
46
+ return (
47
+ selectedOrg?.is_personal_org &&
48
+ selectedOrg?.owner_account_id == session?.account_id
49
+ )
50
+ }, [selectedOrgId, organizations, session?.github_username])
51
+
49
52
  const fullPackageName = useMemo(() => {
53
+ if (!Boolean(packageName)) return
50
54
  if (selectedOrgId) {
51
55
  return `${organizations?.find((x) => x.org_id === selectedOrgId)?.name}/${packageName}`
52
56
  }
53
57
  return `${session?.github_username}/${packageName}`
54
58
  }, [selectedOrgId, packageName, organizations, session?.github_username])
59
+
55
60
  useEffect(() => {
56
61
  if (organizations && organizations.length > 0 && !selectedOrgId) {
57
62
  setSelectedOrgId(
@@ -169,9 +174,13 @@ export const NewPackageSavePromptDialog = ({
169
174
  <Button
170
175
  onClick={() => {
171
176
  onSave({
172
- name: fullPackageName.trim(),
177
+ name: fullPackageName,
173
178
  isPrivate,
174
- orgId: selectedOrgId,
179
+ ...(isOwnerPersonalOrg
180
+ ? {}
181
+ : {
182
+ org_id: selectedOrgId,
183
+ }),
175
184
  })
176
185
  onOpenChange(false)
177
186
  }}
@@ -4,7 +4,7 @@ import { Building2, Users, Package, Lock, Globe2, Settings } from "lucide-react"
4
4
  import { cn } from "@/lib/utils"
5
5
  import { PublicOrgSchema } from "fake-snippets-api/lib/db/schema"
6
6
  import { useGlobalStore } from "@/hooks/use-global-store"
7
- import { useLocation } from "wouter"
7
+ import { Link, useLocation } from "wouter"
8
8
  import { useOrganization } from "@/hooks/use-organization"
9
9
  import { GithubAvatarWithFallback } from "../GithubAvatarWithFallback"
10
10
 
@@ -97,9 +97,12 @@ export const OrganizationHeader: React.FC<OrganizationHeaderProps> = ({
97
97
 
98
98
  <div className="flex-1 min-w-0">
99
99
  <div className="flex items-center justify-between mb-3">
100
- <h1 className="font-bold text-gray-900 text-2xl md:text-3xl truncate">
100
+ <Link
101
+ href={`/${organization.name}`}
102
+ className="font-bold text-gray-900 text-2xl md:text-3xl truncate"
103
+ >
101
104
  {organization.name}
102
- </h1>
105
+ </Link>
103
106
  {canManageOrg && showActions && (
104
107
  <Button variant="outline" onClick={handleSettingsClick}>
105
108
  <Settings className="mr-2 h-4 w-4" />
@@ -1,48 +1,26 @@
1
1
  import React from "react"
2
2
  import { Link } from "wouter"
3
3
  import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar"
4
- import { Badge } from "@/components/ui/badge"
5
- import { Users, Crown, Shield, User, Loader2 } from "lucide-react"
4
+ import { Users, Loader2 } from "lucide-react"
6
5
  import { timeAgo } from "@/lib/utils/timeAgo"
7
6
  import { cn } from "@/lib/utils"
8
7
  import { useListOrgMembers } from "@/hooks/use-list-org-members"
8
+ import { getMemberRole } from "@/lib/utils/member-role"
9
+ import { RoleBadge } from "@/components/ui/role-badge"
10
+ import { PublicOrgSchema } from "fake-snippets-api/lib/db/schema"
9
11
 
10
12
  interface OrganizationMembersProps {
11
- orgId: string
13
+ organization: PublicOrgSchema
12
14
  className?: string
13
15
  }
14
- type MemberRole = "owner" | "admin" | "member" //todo
15
- const getRoleIcon = (role: MemberRole) => {
16
- switch (role) {
17
- case "owner":
18
- return <Crown className="h-3 w-3" />
19
- case "admin":
20
- return <Shield className="h-3 w-3" />
21
- case "member":
22
- return <User className="h-3 w-3" />
23
- default:
24
- return <User className="h-3 w-3" />
25
- }
26
- }
27
-
28
- const getRoleColor = (role: MemberRole) => {
29
- switch (role) {
30
- case "owner":
31
- return "bg-yellow-100 text-yellow-800 border-yellow-200"
32
- case "admin":
33
- return "bg-purple-100 text-purple-800 border-purple-200"
34
- case "member":
35
- return "bg-gray-100 text-gray-800 border-gray-200"
36
- default:
37
- return "bg-gray-100 text-gray-800 border-gray-200"
38
- }
39
- }
40
16
 
41
17
  export const OrganizationMembers: React.FC<OrganizationMembersProps> = ({
42
- orgId,
18
+ organization,
43
19
  className,
44
20
  }) => {
45
- const { data: members = [], isLoading } = useListOrgMembers({ orgId })
21
+ const { data: members = [], isLoading } = useListOrgMembers({
22
+ orgId: organization.org_id,
23
+ })
46
24
 
47
25
  if (isLoading) {
48
26
  return (
@@ -78,60 +56,54 @@ export const OrganizationMembers: React.FC<OrganizationMembersProps> = ({
78
56
  </div>
79
57
 
80
58
  <div className="space-y-2 sm:space-y-3">
81
- {members.map((member) => (
82
- <Link
83
- key={member.account_id || member.github_username}
84
- href={`/${member.github_username}`}
85
- className="block"
86
- >
87
- <div className="flex items-center justify-between p-3 rounded-lg hover:bg-gray-50 transition-colors cursor-pointer">
88
- <div className="flex items-center gap-3 min-w-0 flex-1">
89
- <Avatar className="h-10 w-10 sm:h-12 sm:w-12 flex-shrink-0">
90
- <AvatarImage
91
- src={`https://github.com/${member.github_username}.png`}
92
- alt={`${member.github_username} avatar`}
93
- />
94
- <AvatarFallback className="text-sm font-medium">
95
- {member.github_username
96
- .split(" ")
97
- .map((word) => word[0])
98
- .join("")
99
- .toUpperCase()
100
- .slice(0, 2)}
101
- </AvatarFallback>
102
- </Avatar>
59
+ {members.map((member) => {
60
+ const role = getMemberRole(organization, member.account_id)
61
+ return (
62
+ <Link
63
+ key={member.account_id || member.github_username}
64
+ href={`/${member.github_username}`}
65
+ className="block"
66
+ >
67
+ <div className="flex items-center justify-between p-3 rounded-lg hover:bg-gray-50 transition-colors cursor-pointer">
68
+ <div className="flex items-center gap-3 min-w-0 flex-1">
69
+ <Avatar className="h-10 w-10 sm:h-12 sm:w-12 flex-shrink-0">
70
+ <AvatarImage
71
+ src={`https://github.com/${member.github_username}.png`}
72
+ alt={`${member.github_username} avatar`}
73
+ />
74
+ <AvatarFallback className="text-sm font-medium">
75
+ {member.github_username
76
+ .split(" ")
77
+ .map((word) => word[0])
78
+ .join("")
79
+ .toUpperCase()
80
+ .slice(0, 2)}
81
+ </AvatarFallback>
82
+ </Avatar>
103
83
 
104
- <div className="min-w-0 flex-1">
105
- <div className="flex items-center gap-2 mb-1">
106
- <h3 className="font-medium text-gray-900 truncate">
107
- {member.github_username}
108
- </h3>
109
- <Badge
110
- variant="outline"
111
- className={cn(
112
- "text-xs flex items-center gap-1 flex-shrink-0",
113
- getRoleColor("admin"),
114
- )}
115
- >
116
- {getRoleIcon("admin")}
117
- {"admin"}
118
- </Badge>
84
+ <div className="min-w-0 flex-1">
85
+ <div className="flex items-center gap-2 mb-1">
86
+ <h3 className="font-medium text-gray-900 truncate">
87
+ {member.github_username}
88
+ </h3>
89
+ <RoleBadge role={role} />
90
+ </div>
91
+ <p className="text-sm text-gray-500 truncate">
92
+ @{member.github_username}
93
+ </p>
119
94
  </div>
120
- <p className="text-sm text-gray-500 truncate">
121
- @{member.github_username}
122
- </p>
123
95
  </div>
96
+ {member.joined_at && (
97
+ <div className="text-right flex-shrink-0 hidden sm:block">
98
+ <p className="text-xs text-gray-500">
99
+ Joined {timeAgo(new Date(member.joined_at))}
100
+ </p>
101
+ </div>
102
+ )}
124
103
  </div>
125
- {member.joined_at && (
126
- <div className="text-right flex-shrink-0 hidden sm:block">
127
- <p className="text-xs text-gray-500">
128
- Joined {timeAgo(new Date(member.joined_at))}
129
- </p>
130
- </div>
131
- )}
132
- </div>
133
- </Link>
134
- ))}
104
+ </Link>
105
+ )
106
+ })}
135
107
  </div>
136
108
 
137
109
  {members.length === 0 && (
@@ -51,6 +51,7 @@ import { isHiddenFile } from "../ViewPackagePage/utils/is-hidden-file"
51
51
  import { inlineCopilot } from "codemirror-copilot"
52
52
  import { useViewTsFilesDialog } from "@/components/dialogs/view-ts-files-dialog"
53
53
  import { Loader2 } from "lucide-react"
54
+ import { useAxios } from "@/hooks/use-axios"
54
55
 
55
56
  const defaultImports = `
56
57
  import React from "@types/react/jsx-runtime"
@@ -111,7 +112,7 @@ export const CodeEditor = ({
111
112
  const highlightTimeoutRef = useRef<number | null>(null)
112
113
 
113
114
  const { highlighter } = useShikiHighlighter()
114
-
115
+ const axios = useAxios()
115
116
  // Get URL search params for file_path
116
117
  const urlParams = new URLSearchParams(window.location.search)
117
118
  const filePathFromUrl = urlParams.get("file_path")
@@ -358,23 +359,12 @@ export const CodeEditor = ({
358
359
  if (aiAutocompleteEnabled) {
359
360
  baseExtensions.push(
360
361
  inlineCopilot(async (prefix, suffix) => {
361
- const res = await fetch(
362
- `${apiUrl}/autocomplete/create_autocomplete`,
363
- {
364
- method: "POST",
365
- headers: {
366
- "Content-Type": "application/json",
367
- },
368
- body: JSON.stringify({
369
- prefix,
370
- suffix,
371
- language: "typescript",
372
- }),
373
- },
374
- )
375
-
376
- const { prediction } = await res.json()
377
- return prediction
362
+ const res = await axios.post("/autocomplete/create_autocomplete", {
363
+ prefix,
364
+ suffix,
365
+ language: "typescript",
366
+ })
367
+ return res.data?.prediction
378
368
  }),
379
369
  EditorView.theme({
380
370
  ".cm-ghostText, .cm-ghostText *": {
@@ -390,12 +390,12 @@ export const CodeEditorHeader: React.FC<CodeEditorHeaderProps> = ({
390
390
  onClick={() => {
391
391
  setAiAutocompleteEnabled((prev) => !prev)
392
392
  }}
393
- className={`relative group bg-transparent ${aiAutocompleteEnabled ? "text-gray-600 bg-gray-50" : "text-gray-400"}`}
393
+ className={`relative group bg-transparent ${aiAutocompleteEnabled ? "text-black bg-white" : "text-gray-500 group-hover:text-black"}`}
394
394
  >
395
395
  <Bot className="h-4 w-4" />
396
396
  {!aiAutocompleteEnabled && (
397
397
  <div className="absolute inset-0 flex items-center justify-center">
398
- <div className="w-5 h-0.5 group-hover:bg-slate-900 bg-gray-400 rotate-45 rounded-full" />
398
+ <div className="w-5 h-0.5 group-hover:bg-black bg-gray-500 rotate-45 rounded-full" />
399
399
  </div>
400
400
  )}
401
401
  </Button>
@@ -0,0 +1,27 @@
1
+ import { cn } from "@/lib/utils"
2
+ import {
3
+ type MemberRole,
4
+ getRoleIcon,
5
+ getRoleColor,
6
+ getRoleName,
7
+ } from "@/lib/utils/member-role"
8
+
9
+ interface RoleBadgeProps {
10
+ role: MemberRole
11
+ className?: string
12
+ }
13
+
14
+ export const RoleBadge = ({ role, className }: RoleBadgeProps) => {
15
+ return (
16
+ <div
17
+ className={cn(
18
+ "select-none inline-flex items-center gap-1 py-0.5 px-1.5 rounded text-xs font-medium flex-shrink-0",
19
+ getRoleColor(role),
20
+ className,
21
+ )}
22
+ >
23
+ {getRoleIcon(role)}
24
+ <span className="leading-none">{getRoleName(role)}</span>
25
+ </div>
26
+ )
27
+ }
@@ -25,6 +25,7 @@ export const useListOrgMembers = ({
25
25
  enabled: Boolean(orgId || orgName),
26
26
  retry: false,
27
27
  refetchOnWindowFocus: false,
28
+ refetchOnMount: false,
28
29
  keepPreviousData: true,
29
30
  },
30
31
  )
@@ -1,8 +1,11 @@
1
- import { useQuery } from "react-query"
1
+ import { useQuery, UseQueryOptions } from "react-query"
2
2
  import { useAxios } from "@/hooks/use-axios"
3
3
  import type { Package } from "fake-snippets-api/lib/db/schema"
4
4
 
5
- export const usePackageByName = (packageName: string | null) => {
5
+ export const usePackageByName = (
6
+ packageName: string | null,
7
+ options?: UseQueryOptions<Package, Error & { status: number }>,
8
+ ) => {
6
9
  const axios = useAxios()
7
10
  return useQuery<Package, Error & { status: number }>(
8
11
  ["package", packageName],
@@ -19,6 +22,8 @@ export const usePackageByName = (packageName: string | null) => {
19
22
  retry: false,
20
23
  enabled: Boolean(packageName),
21
24
  refetchOnWindowFocus: false,
25
+ staleTime: 5 * 60 * 1000,
26
+ ...options,
22
27
  },
23
28
  )
24
29
  }
@@ -1,5 +1,7 @@
1
1
  import { useMutation, useQuery, useQueryClient } from "react-query"
2
2
  import { useAxios } from "./use-axios"
3
+ import { usePackageByName } from "./use-package-by-package-name"
4
+ import { usePackageById } from "./use-package-by-package-id"
3
5
 
4
6
  type PackageStarQuery = { package_id: string } | { name: string }
5
7
 
@@ -9,7 +11,11 @@ interface PackageStarResponse {
9
11
  }
10
12
 
11
13
  export const usePackageStars = (query: PackageStarQuery | null) => {
12
- const axios = useAxios()
14
+ const packageName = query && "name" in query ? query.name : null
15
+ const packageId = query && "package_id" in query ? query.package_id : null
16
+ const packageQuery = packageName
17
+ ? usePackageByName(packageName)
18
+ : usePackageById(packageId)
13
19
 
14
20
  return useQuery<PackageStarResponse, Error & { status: number }>(
15
21
  ["packageStars", query],
@@ -17,19 +23,14 @@ export const usePackageStars = (query: PackageStarQuery | null) => {
17
23
  if (!query) {
18
24
  throw new Error("Query is required")
19
25
  }
20
-
21
- const { data } = await axios.get("/packages/get", {
22
- params: query,
23
- })
24
-
25
26
  return {
26
- is_starred: data.package.is_starred ?? false,
27
- star_count: data.package.star_count ?? 0,
27
+ is_starred: packageQuery.data?.is_starred ?? false,
28
+ star_count: packageQuery.data?.star_count ?? 0,
28
29
  }
29
30
  },
30
31
  {
31
32
  retry: false,
32
- enabled: Boolean(query),
33
+ enabled: Boolean(query) && packageQuery.isSuccess,
33
34
  },
34
35
  )
35
36
  }
@@ -45,6 +45,12 @@ export interface IRenameFileResult {
45
45
  fileRenamed: boolean
46
46
  }
47
47
 
48
+ export interface ICreatePackageProps {
49
+ isPrivate?: boolean
50
+ name?: string
51
+ org_id?: string
52
+ }
53
+
48
54
  export function useFileManagement({
49
55
  templateCode,
50
56
  currentPackage,
@@ -428,12 +434,8 @@ export function useFileManagement({
428
434
  const savePackage = async ({
429
435
  name,
430
436
  isPrivate,
431
- orgId,
432
- }: {
433
- name?: string
434
- isPrivate: boolean
435
- orgId: string
436
- }) => {
437
+ org_id,
438
+ }: ICreatePackageProps) => {
437
439
  if (!isLoggedIn) {
438
440
  toast({
439
441
  title: "Not Logged In",
@@ -443,9 +445,9 @@ export function useFileManagement({
443
445
  }
444
446
 
445
447
  await createPackageMutation.mutateAsync({
446
- is_private: isPrivate,
447
- org_id: orgId,
448
- ...(name ? { name } : {}),
448
+ is_private: Boolean(isPrivate),
449
+ ...(org_id ? { org_id: org_id } : {}),
450
+ ...(name ? { name: name?.trim() } : {}),
449
451
  })
450
452
  }
451
453