@tscircuit/fake-snippets 0.0.109 → 0.0.111

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.
Files changed (185) hide show
  1. package/.github/workflows/bun-formatcheck.yml +2 -2
  2. package/.github/workflows/bun-pver-release.yml +3 -3
  3. package/.github/workflows/bun-test.yml +1 -1
  4. package/.github/workflows/bun-typecheck.yml +2 -2
  5. package/.github/workflows/update-snapshots.yml +1 -1
  6. package/README.md +4 -0
  7. package/api/generated-index.js +37 -3
  8. package/biome.json +2 -1
  9. package/bun-tests/fake-snippets-api/fixtures/get-test-server.ts +32 -3
  10. package/bun-tests/fake-snippets-api/fixtures/preload.ts +18 -0
  11. package/bun-tests/fake-snippets-api/routes/orgs/add_member.test.ts +26 -0
  12. package/bun-tests/fake-snippets-api/routes/orgs/create.test.ts +37 -0
  13. package/bun-tests/fake-snippets-api/routes/orgs/get.test.ts +52 -0
  14. package/bun-tests/fake-snippets-api/routes/orgs/list.test.ts +17 -0
  15. package/bun-tests/fake-snippets-api/routes/orgs/list_members.test.ts +23 -0
  16. package/bun-tests/fake-snippets-api/routes/orgs/remove_member.test.ts +81 -0
  17. package/bun-tests/fake-snippets-api/routes/orgs/update.test.ts +151 -0
  18. package/bun-tests/fake-snippets-api/routes/package_builds/get.test.ts +1 -1
  19. package/bun-tests/fake-snippets-api/routes/package_files/create.test.ts +15 -13
  20. package/bun-tests/fake-snippets-api/routes/package_files/create_or_update.test.ts +26 -24
  21. package/bun-tests/fake-snippets-api/routes/package_files/delete.test.ts +9 -9
  22. package/bun-tests/fake-snippets-api/routes/package_files/download.test.ts +4 -4
  23. package/bun-tests/fake-snippets-api/routes/package_files/get.test.ts +38 -28
  24. package/bun-tests/fake-snippets-api/routes/package_files/list.test.ts +23 -15
  25. package/bun-tests/fake-snippets-api/routes/package_releases/create.test.ts +33 -0
  26. package/bun-tests/fake-snippets-api/routes/package_releases/get.test.ts +4 -4
  27. package/bun-tests/fake-snippets-api/routes/package_releases/get_image_generation_fields.test.ts +38 -0
  28. package/bun-tests/fake-snippets-api/routes/packages/create.test.ts +19 -0
  29. package/bun-tests/fake-snippets-api/routes/packages/fork.test.ts +3 -4
  30. package/bun-tests/fake-snippets-api/routes/packages/get.test.ts +30 -0
  31. package/bun-tests/fake-snippets-api/routes/packages/images.test.ts +4 -2
  32. package/bun-tests/fake-snippets-api/routes/packages/list-1.test.ts +34 -0
  33. package/bun.lock +361 -453
  34. package/bunfig.toml +2 -1
  35. package/dist/bundle.js +1313 -639
  36. package/dist/index.d.ts +313 -6
  37. package/dist/index.js +328 -24
  38. package/dist/schema.d.ts +290 -1
  39. package/dist/schema.js +54 -1
  40. package/fake-snippets-api/lib/db/autoload-dev-packages.ts +31 -20
  41. package/fake-snippets-api/lib/db/db-client.ts +219 -4
  42. package/fake-snippets-api/lib/db/schema.ts +63 -1
  43. package/fake-snippets-api/lib/db/seed.ts +100 -0
  44. package/fake-snippets-api/lib/middleware/with-session-auth.ts +60 -8
  45. package/fake-snippets-api/lib/package_file/get-package-file-id-from-file-descriptor.ts +2 -2
  46. package/fake-snippets-api/lib/public-mapping/public-map-org.ts +33 -0
  47. package/fake-snippets-api/lib/public-mapping/public-map-package-build.ts +10 -0
  48. package/fake-snippets-api/lib/public-mapping/public-map-package-release.ts +17 -0
  49. package/fake-snippets-api/routes/api/orgs/add_member.ts +52 -0
  50. package/fake-snippets-api/routes/api/orgs/create.ts +48 -0
  51. package/fake-snippets-api/routes/api/orgs/get.ts +39 -0
  52. package/fake-snippets-api/routes/api/orgs/list.ts +31 -0
  53. package/fake-snippets-api/routes/api/orgs/list_members.ts +60 -0
  54. package/fake-snippets-api/routes/api/orgs/remove_member.ts +46 -0
  55. package/fake-snippets-api/routes/api/orgs/update.ts +118 -0
  56. package/fake-snippets-api/routes/api/package_files/get.ts +3 -6
  57. package/fake-snippets-api/routes/api/package_files/list.ts +7 -4
  58. package/fake-snippets-api/routes/api/packages/create.ts +57 -10
  59. package/fake-snippets-api/routes/api/packages/get.ts +23 -0
  60. package/fake-snippets-api/routes/api/packages/images/[owner_github_username]/[unscoped_name]/[view_format].ts +13 -11
  61. package/fake-snippets-api/routes/api/packages/list.ts +29 -2
  62. package/fake-snippets-api/routes/api/packages/update_ai_description.ts +37 -0
  63. package/package.json +25 -19
  64. package/renovate.json +1 -1
  65. package/scripts/generate-sitemap.ts +1 -1
  66. package/src/App.tsx +27 -8
  67. package/src/ContextProviders.tsx +25 -2
  68. package/src/components/CircuitJsonImportDialog.tsx +1 -1
  69. package/src/components/CmdKMenu.tsx +281 -247
  70. package/src/components/DownloadButtonAndMenu.tsx +17 -5
  71. package/src/components/FileSidebar.tsx +11 -17
  72. package/src/components/Footer.tsx +8 -9
  73. package/src/components/Header.tsx +19 -32
  74. package/src/components/Header2.tsx +16 -32
  75. package/src/components/HeaderDropdown.tsx +13 -8
  76. package/src/components/HeaderLogin.tsx +43 -15
  77. package/src/components/NotFound.tsx +5 -5
  78. package/src/components/PackageBreadcrumb.tsx +6 -12
  79. package/src/components/PackageSearchResults.tsx +1 -1
  80. package/src/components/PrefetchPageLink.tsx +7 -1
  81. package/src/components/ProfileRouter.tsx +32 -0
  82. package/src/components/SearchComponent.tsx +12 -8
  83. package/src/components/SentryNotFoundReporter.tsx +44 -0
  84. package/src/components/UserCard.tsx +80 -0
  85. package/src/components/ViewPackagePage/components/build-status.tsx +1 -1
  86. package/src/components/ViewPackagePage/components/important-files-view.tsx +105 -34
  87. package/src/components/ViewPackagePage/components/main-content-header.tsx +10 -6
  88. package/src/components/ViewPackagePage/components/main-content-view-selector.tsx +1 -1
  89. package/src/components/ViewPackagePage/components/mobile-sidebar.tsx +54 -19
  90. package/src/components/ViewPackagePage/components/package-header.tsx +25 -33
  91. package/src/components/ViewPackagePage/components/preview-image-squares.tsx +11 -18
  92. package/src/components/ViewPackagePage/components/repo-page-content.tsx +12 -5
  93. package/src/components/ViewPackagePage/components/sidebar-about-section.tsx +16 -10
  94. package/src/components/ViewPackagePage/components/sidebar-releases-section.tsx +11 -11
  95. package/src/components/ViewPackagePage/components/tab-views/pcb-view.tsx +1 -2
  96. package/src/components/ViewPackagePage/components/tab-views/schematic-view.tsx +2 -1
  97. package/src/components/dialogs/GitHubRepositorySelector.tsx +56 -49
  98. package/src/components/dialogs/edit-package-details-dialog.tsx +5 -6
  99. package/src/components/dialogs/import-component-dialog.tsx +16 -9
  100. package/src/components/dialogs/import-package-dialog.tsx +3 -2
  101. package/src/components/dialogs/new-package-save-prompt-dialog.tsx +190 -0
  102. package/src/components/organization/OrganizationCard.tsx +206 -0
  103. package/src/components/organization/OrganizationCardSkeleton.tsx +55 -0
  104. package/src/components/organization/OrganizationHeader.tsx +154 -0
  105. package/src/components/organization/OrganizationMembers.tsx +146 -0
  106. package/src/components/package-port/CodeAndPreview.tsx +15 -12
  107. package/src/components/package-port/CodeEditor.tsx +4 -30
  108. package/src/components/package-port/CodeEditorHeader.tsx +123 -61
  109. package/src/components/package-port/EditorNav.tsx +32 -49
  110. package/src/components/preview/ConnectedPackagesList.tsx +8 -8
  111. package/src/components/preview/ConnectedRepoOverview.tsx +102 -2
  112. package/src/components/preview/PackageReleasesDashboard.tsx +23 -11
  113. package/src/components/ui/tree-view.tsx +6 -3
  114. package/src/hooks/use-add-org-member-mutation.ts +51 -0
  115. package/src/hooks/use-create-org-mutation.ts +38 -0
  116. package/src/hooks/use-create-package-mutation.ts +3 -0
  117. package/src/hooks/use-current-package-release.ts +4 -3
  118. package/src/hooks/use-download-zip.ts +2 -2
  119. package/src/hooks/use-global-store.ts +6 -4
  120. package/src/hooks/use-hydration.ts +30 -0
  121. package/src/hooks/use-jlcpcb-component-import.tsx +164 -0
  122. package/src/hooks/use-list-org-members.ts +27 -0
  123. package/src/hooks/use-list-user-orgs.ts +25 -0
  124. package/src/hooks/use-org-by-github-handle.ts +26 -0
  125. package/src/hooks/use-org.ts +24 -0
  126. package/src/hooks/use-organization.ts +42 -0
  127. package/src/hooks/use-package-as-snippet.ts +4 -2
  128. package/src/hooks/use-package-builds.ts +6 -2
  129. package/src/hooks/use-package-files.ts +5 -3
  130. package/src/hooks/use-package-release-by-id-or-version.ts +29 -20
  131. package/src/hooks/use-package-release-images.ts +105 -0
  132. package/src/hooks/use-package-release.ts +2 -2
  133. package/src/hooks/use-package-stars.ts +80 -4
  134. package/src/hooks/use-preview-images.ts +6 -3
  135. package/src/hooks/use-remove-org-member-mutation.ts +32 -0
  136. package/src/hooks/use-update-ai-description-mutation.ts +42 -0
  137. package/src/hooks/use-update-org-mutation.ts +41 -0
  138. package/src/hooks/use-warn-user-on-page-change.ts +71 -4
  139. package/src/hooks/useFileManagement.ts +51 -22
  140. package/src/hooks/useOptimizedPackageFilesLoader.ts +11 -24
  141. package/src/hooks/usePackageFilesLoader.ts +2 -2
  142. package/src/hooks/useUpdatePackageFilesMutation.ts +13 -1
  143. package/src/lib/download-fns/download-gltf-from-circuit-json.ts +1 -1
  144. package/src/lib/download-fns/download-kicad-files.ts +22 -11
  145. package/src/lib/download-fns/download-step.ts +12 -0
  146. package/src/lib/normalize-svg-for-tile.ts +50 -0
  147. package/src/lib/posthog.ts +11 -9
  148. package/src/lib/react-query-api-failure-tracking.ts +148 -0
  149. package/src/lib/sentry.ts +14 -0
  150. package/src/lib/templates/blank-circuit-board-template.ts +0 -4
  151. package/src/lib/ts-lib-cache.ts +122 -7
  152. package/src/lib/utils/checkIfManualEditsImported.ts +4 -4
  153. package/src/lib/utils/findTargetFile.ts +45 -10
  154. package/src/lib/utils/isComponentExported.ts +2 -1
  155. package/src/main.tsx +2 -1
  156. package/src/pages/create-organization.tsx +169 -0
  157. package/src/pages/dashboard.tsx +38 -6
  158. package/src/pages/datasheet.tsx +1 -1
  159. package/src/pages/datasheets.tsx +3 -3
  160. package/src/pages/editor.tsx +4 -6
  161. package/src/pages/landing.tsx +6 -6
  162. package/src/pages/latest.tsx +3 -0
  163. package/src/pages/organization-profile.tsx +199 -0
  164. package/src/pages/organization-settings.tsx +569 -0
  165. package/src/pages/package-editor.tsx +21 -21
  166. package/src/pages/preview-release.tsx +75 -145
  167. package/src/pages/quickstart.tsx +159 -123
  168. package/src/pages/release-detail.tsx +119 -31
  169. package/src/pages/search.tsx +197 -57
  170. package/src/pages/settings-redirect.tsx +44 -0
  171. package/src/pages/trending.tsx +29 -20
  172. package/src/pages/user-profile.tsx +58 -7
  173. package/src/pages/user-settings.tsx +161 -0
  174. package/src/pages/view-package.tsx +30 -16
  175. package/vite.config.ts +9 -0
  176. package/fake-snippets-api/routes/api/autocomplete/create_autocomplete.ts +0 -133
  177. package/src/components/JLCPCBImportDialog.tsx +0 -280
  178. package/src/components/PackageBuildsPage/LogContent.tsx +0 -72
  179. package/src/components/PackageBuildsPage/PackageBuildDetailsPage.tsx +0 -113
  180. package/src/components/PackageBuildsPage/build-preview-content.tsx +0 -56
  181. package/src/components/PackageBuildsPage/collapsible-section.tsx +0 -63
  182. package/src/components/PackageBuildsPage/package-build-details-panel.tsx +0 -166
  183. package/src/components/PackageBuildsPage/package-build-header.tsx +0 -79
  184. package/src/components/PageSearchComponent.tsx +0 -148
  185. package/src/pages/package-builds.tsx +0 -33
@@ -16,7 +16,7 @@ const extractTsciDependencies = (
16
16
  }
17
17
 
18
18
  const registryApi = axios.create({
19
- baseURL: "https://registry-api.tscircuit.com",
19
+ baseURL: "https://api.tscircuit.com",
20
20
  headers: {
21
21
  Accept: "application/json",
22
22
  "Content-Type": "application/json",
@@ -25,7 +25,7 @@ const registryApi = axios.create({
25
25
 
26
26
  const fetchPackageFromRegistry = async (owner: string, name: string) => {
27
27
  const fullName = `${owner}/${name}`
28
- console.log(`Fetching package ${fullName}...`)
28
+ console.log(`[autoload-dev-pkgs] 📦 Fetching package ${fullName}...`)
29
29
 
30
30
  let packageData
31
31
  try {
@@ -46,35 +46,42 @@ const fetchPackageFromRegistry = async (owner: string, name: string) => {
46
46
  })
47
47
  releaseData = response.data
48
48
  } catch (e) {
49
- console.error(`Failed to fetch release data for ${fullName}:`, e)
49
+ console.error(
50
+ `[autoload-dev-pkgs] ❌ Failed to fetch release data for ${fullName}:`,
51
+ e,
52
+ )
50
53
  throw e
51
54
  }
52
55
 
53
56
  let filesData
54
57
  try {
55
- const response = await registryApi.post("/package_files/list", {
56
- package_release_id: releaseData.package_release.package_release_id,
57
- })
58
+ const response = await registryApi.post(
59
+ `package_files/list?package_release_id=${releaseData.package_release.package_release_id}`,
60
+ {},
61
+ )
58
62
  filesData = response.data
59
63
 
60
64
  // Fetch content_text for each file individually
61
65
  for (const file of filesData.package_files) {
62
66
  try {
63
- const fileResponse = await registryApi.post("/package_files/get", {
64
- package_release_id: releaseData.package_release.package_release_id,
65
- file_path: file.file_path,
66
- })
67
+ const fileResponse = await registryApi.post(
68
+ `/package_files/get?package_file_id=${file.package_file_id}`,
69
+ {},
70
+ )
67
71
  file.content_text = fileResponse.data.package_file.content_text
68
72
  } catch (e) {
69
73
  console.error(
70
- `Failed to fetch content for file ${file.file_path} in package ${fullName}:`,
74
+ `[autoload-dev-pkgs] ❌ Failed to fetch content for file ${file.file_path} in package ${fullName}:`,
71
75
  e,
72
76
  )
73
77
  throw e
74
78
  }
75
79
  }
76
80
  } catch (e) {
77
- console.error(`Failed to fetch files data for ${fullName}:`, e)
81
+ console.error(
82
+ `[autoload-dev-pkgs] ❌ Failed to fetch files data for ${fullName}:`,
83
+ e,
84
+ )
78
85
  throw e
79
86
  }
80
87
 
@@ -100,7 +107,7 @@ const loadPackageWithDependencies = async (
100
107
  try {
101
108
  result = await fetchPackageFromRegistry(owner, name)
102
109
  } catch (e) {
103
- console.error(`✗ Failed to load ${packageKey}`)
110
+ console.error(`[autoload-dev-pkgs] Failed to load ${packageKey}`)
104
111
  return false
105
112
  }
106
113
 
@@ -108,7 +115,7 @@ const loadPackageWithDependencies = async (
108
115
 
109
116
  // Check if package already exists
110
117
  if (db.getPackageById(pkg.package_id)) {
111
- console.log(`✓ Package ${packageKey} already exists`)
118
+ console.log(`[autoload-dev-pkgs] Package ${packageKey} already exists`)
112
119
  return true
113
120
  }
114
121
 
@@ -142,7 +149,7 @@ const loadPackageWithDependencies = async (
142
149
  }
143
150
 
144
151
  loadedPackages.add(packageKey)
145
- console.log(`✓ Loaded ${packageKey}`)
152
+ console.log(`[autoload-dev-pkgs] 📦 Loaded ${packageKey}`)
146
153
 
147
154
  // Load dependencies
148
155
  const mainFile = files.find(
@@ -165,7 +172,7 @@ const loadPackageWithDependencies = async (
165
172
  if (!depLoaded) {
166
173
  allDepsLoaded = false
167
174
  console.warn(
168
- `⚠️ Failed to load dependency ${dep.owner}/${dep.name} for ${packageKey}`,
175
+ `[autoload-dev-pkgs] ⚠️ Failed to load dependency ${dep.owner}/${dep.name} for ${packageKey}`,
169
176
  )
170
177
  }
171
178
  }
@@ -180,7 +187,7 @@ export const loadAutoloadPackages = async (db: DbClient) => {
180
187
  "autoload-packages.json",
181
188
  )
182
189
  if (!fs.existsSync(autoloadPath)) {
183
- console.error("No autoload-packages.json found")
190
+ console.error("[autoload-dev-pkgs] ❌ No autoload-packages.json found")
184
191
  return
185
192
  }
186
193
 
@@ -203,9 +210,13 @@ export const loadAutoloadPackages = async (db: DbClient) => {
203
210
  }
204
211
  }
205
212
 
206
- console.log(`\nPackage loading complete:`)
207
- console.log(`✓ Successfully loaded: ${successCount} packages`)
213
+ console.log(`\n[autoload-dev-pkgs] 📋 Package loading complete:`)
214
+ console.log(
215
+ `[autoload-dev-pkgs] ✅ Successfully loaded: ${successCount} packages`,
216
+ )
208
217
  if (failureCount > 0) {
209
- console.log(`✗ Failed to load: ${failureCount} packages`)
218
+ console.log(
219
+ `[autoload-dev-pkgs] ❌ Failed to load: ${failureCount} packages`,
220
+ )
210
221
  }
211
222
  }
@@ -17,6 +17,7 @@ import {
17
17
  type PackageRelease,
18
18
  packageReleaseSchema,
19
19
  type PackageBuild,
20
+ packageBuildSchema,
20
21
  type AiReview,
21
22
  aiReviewSchema,
22
23
  type Datasheet,
@@ -26,6 +27,8 @@ import {
26
27
  databaseSchema,
27
28
  type packageSchema,
28
29
  type snippetSchema,
30
+ Organization,
31
+ OrgAccount,
29
32
  } from "./schema.ts"
30
33
  import { seed as seedFn } from "./seed"
31
34
  import { generateFsSha } from "../package_file/generate-fs-sha"
@@ -175,11 +178,13 @@ const initializer = combine(databaseSchema.parse({}), (set, get) => ({
175
178
  return state.orderFiles.find((file) => file.order_file_id === orderFileId)
176
179
  },
177
180
  addAccount: (
178
- account: Omit<Account, "account_id"> & Partial<Pick<Account, "account_id">>,
181
+ account: Omit<Account, "account_id" | "is_tscircuit_staff"> &
182
+ Partial<Pick<Account, "account_id" | "is_tscircuit_staff">>,
179
183
  ) => {
180
184
  const newAccount = {
181
185
  account_id: account.account_id || `account_${get().idCounter + 1}`,
182
186
  ...account,
187
+ is_tscircuit_staff: Boolean(account.is_tscircuit_staff),
183
188
  }
184
189
 
185
190
  set((state) => {
@@ -1489,12 +1494,12 @@ const initializer = combine(databaseSchema.parse({}), (set, get) => ({
1489
1494
  return updated
1490
1495
  },
1491
1496
  addPackageBuild: (
1492
- packageBuild: Omit<PackageBuild, "package_build_id">,
1497
+ packageBuild: Omit<z.input<typeof packageBuildSchema>, "package_build_id">,
1493
1498
  ): PackageBuild => {
1494
- const newPackageBuild = {
1499
+ const newPackageBuild = packageBuildSchema.parse({
1495
1500
  package_build_id: crypto.randomUUID(),
1496
1501
  ...packageBuild,
1497
- }
1502
+ })
1498
1503
  set((state) => ({
1499
1504
  packageBuilds: [...state.packageBuilds, newPackageBuild],
1500
1505
  // Automatically update the package release to reference this as the latest build
@@ -1541,4 +1546,214 @@ const initializer = combine(databaseSchema.parse({}), (set, get) => ({
1541
1546
  })
1542
1547
  return updated
1543
1548
  },
1549
+ addOrganization: (organization: {
1550
+ name: string
1551
+ org_id?: string
1552
+ is_personal_org?: boolean
1553
+ owner_account_id: string
1554
+ github_handle?: string
1555
+ }) => {
1556
+ const newOrganization: Organization = {
1557
+ org_name: organization.name,
1558
+ org_id: organization.org_id || `org_${get().idCounter + 1}`,
1559
+ github_handle: organization.github_handle,
1560
+ is_personal_org: organization.is_personal_org || false,
1561
+ created_at: new Date().toISOString(),
1562
+ ...organization,
1563
+ }
1564
+ set((state) => ({
1565
+ idCounter: state.idCounter + 1,
1566
+ organizations: [...state.organizations, newOrganization as Organization],
1567
+ // Add the creator as a member of the new org by setting their personal_org_id
1568
+ accounts: state.accounts.map((account) =>
1569
+ account.account_id === organization.owner_account_id
1570
+ ? { ...account, personal_org_id: newOrganization.org_id }
1571
+ : account,
1572
+ ),
1573
+ }))
1574
+ return newOrganization
1575
+ },
1576
+ getOrgs: (
1577
+ filters?: {
1578
+ owner_account_id?: string
1579
+ github_handle?: string
1580
+ name?: string
1581
+ },
1582
+ auth?: { account_id?: string },
1583
+ ) => {
1584
+ let orgs = get().organizations
1585
+ if (filters?.owner_account_id) {
1586
+ orgs = orgs.filter(
1587
+ (org) => org.owner_account_id === filters.owner_account_id,
1588
+ )
1589
+ }
1590
+ if (filters?.github_handle) {
1591
+ orgs = orgs.filter((org) => {
1592
+ const account = get().accounts.find(
1593
+ (account) => account.account_id === org.owner_account_id,
1594
+ )
1595
+ return account?.github_username === filters.github_handle
1596
+ })
1597
+ }
1598
+ if (filters?.name) {
1599
+ orgs = orgs.filter((org) => org.github_handle === filters.name)
1600
+ }
1601
+
1602
+ return orgs.map((org) => {
1603
+ const member_count = get().accounts.filter(
1604
+ (account) => account.personal_org_id === org.org_id,
1605
+ ).length
1606
+
1607
+ const package_count = get().packages.filter(
1608
+ (pkg) => pkg.owner_org_id === org.org_id,
1609
+ ).length
1610
+
1611
+ const can_manage_org = auth
1612
+ ? org.owner_account_id === auth.account_id
1613
+ : false
1614
+
1615
+ return {
1616
+ ...org,
1617
+ member_count,
1618
+ package_count,
1619
+ can_manage_org,
1620
+ }
1621
+ })
1622
+ },
1623
+ getOrg: (
1624
+ filters: {
1625
+ org_id?: string
1626
+ org_name?: string
1627
+ github_handle?: string
1628
+ },
1629
+ auth?: { account_id: string },
1630
+ ) => {
1631
+ let orgs = get().organizations
1632
+
1633
+ if (filters?.org_id) {
1634
+ orgs = orgs.filter((org) => org.org_id === filters.org_id)
1635
+ }
1636
+ if (filters?.org_name) {
1637
+ orgs = orgs.filter((org) => org.org_name === filters.org_name)
1638
+ }
1639
+ // if (filters?.org_name && auth?.account_id) {
1640
+ // const account = get().accounts.find(x => x.account_id == auth?.account_id)
1641
+ // orgs = orgs.filter((org) => org.github_handle === account?.github_username)
1642
+ // }
1643
+ if (filters?.github_handle) {
1644
+ orgs = orgs.filter((org) => org.github_handle === filters.github_handle)
1645
+ }
1646
+
1647
+ if (orgs.length === 0) {
1648
+ return null
1649
+ }
1650
+
1651
+ const org = orgs[0]
1652
+
1653
+ const member_count = get().accounts.filter(
1654
+ (account) => account.personal_org_id === org.org_id,
1655
+ ).length
1656
+
1657
+ const package_count = get().packages.filter(
1658
+ (pkg) => pkg.owner_org_id === org.org_id,
1659
+ ).length
1660
+
1661
+ const can_manage_org = auth
1662
+ ? org.owner_account_id === auth.account_id
1663
+ : false
1664
+
1665
+ return {
1666
+ ...org,
1667
+ member_count,
1668
+ package_count,
1669
+ can_manage_org,
1670
+ }
1671
+ },
1672
+ addOrganizationAccount: (organizationAccount: {
1673
+ org_id: string
1674
+ account_id: string
1675
+ is_owner?: boolean
1676
+ }) => {
1677
+ const newOrgAccount: OrgAccount = {
1678
+ org_account_id: `org_account_${get().idCounter + 1}`,
1679
+ org_id: organizationAccount.org_id,
1680
+ account_id: organizationAccount.account_id,
1681
+ is_owner: organizationAccount.is_owner || false,
1682
+ created_at: new Date().toISOString(),
1683
+ }
1684
+ set((state) => ({
1685
+ orgAccounts: [...state.orgAccounts, newOrgAccount],
1686
+ idCounter: state.idCounter + 1,
1687
+ }))
1688
+ return newOrgAccount
1689
+ },
1690
+ getOrganizationAccount: (filters: {
1691
+ org_id?: string
1692
+ account_id?: string
1693
+ }): OrgAccount | undefined => {
1694
+ const state = get()
1695
+ return state.orgAccounts.find((orgAccount) => {
1696
+ if (filters.org_id && orgAccount.org_id !== filters.org_id) {
1697
+ return false
1698
+ }
1699
+ if (filters.account_id && orgAccount.account_id !== filters.account_id) {
1700
+ return false
1701
+ }
1702
+ return true
1703
+ })
1704
+ },
1705
+ getOrganizationAccounts: (filters?: {
1706
+ org_id?: string
1707
+ account_id?: string
1708
+ }): OrgAccount[] => {
1709
+ const state = get()
1710
+ return state.orgAccounts.filter((orgAccount) => {
1711
+ if (filters?.org_id && orgAccount.org_id !== filters.org_id) {
1712
+ return false
1713
+ }
1714
+ if (filters?.account_id && orgAccount.account_id !== filters.account_id) {
1715
+ return false
1716
+ }
1717
+ return true
1718
+ })
1719
+ },
1720
+ removeOrganizationAccount: (filters: {
1721
+ org_id: string
1722
+ account_id: string
1723
+ }): boolean => {
1724
+ let removed = false
1725
+ set((state) => {
1726
+ const index = state.orgAccounts.findIndex(
1727
+ (orgAccount) =>
1728
+ orgAccount.org_id === filters.org_id &&
1729
+ orgAccount.account_id === filters.account_id,
1730
+ )
1731
+ if (index !== -1) {
1732
+ state.orgAccounts.splice(index, 1)
1733
+ removed = true
1734
+ }
1735
+ return state
1736
+ })
1737
+ return removed
1738
+ },
1739
+ updateOrganization: (
1740
+ orgId: string,
1741
+ updates: Partial<Organization>,
1742
+ ): Organization | undefined => {
1743
+ let updatedOrg: Organization | undefined
1744
+ set((state) => {
1745
+ const orgIndex = state.organizations.findIndex(
1746
+ (org) => org.org_id === orgId,
1747
+ )
1748
+ if (orgIndex === -1) return state
1749
+ const updatedOrganizations = [...state.organizations]
1750
+ updatedOrganizations[orgIndex] = {
1751
+ ...updatedOrganizations[orgIndex],
1752
+ ...updates,
1753
+ }
1754
+ updatedOrg = updatedOrganizations[orgIndex]
1755
+ return { ...state, organizations: updatedOrganizations }
1756
+ })
1757
+ return updatedOrg
1758
+ },
1544
1759
  }))
@@ -67,11 +67,12 @@ export const shippingInfoSchema = z.object({
67
67
  country: z.string(),
68
68
  phone: z.string(),
69
69
  })
70
-
71
70
  export const accountSchema = z.object({
72
71
  account_id: z.string(),
73
72
  github_username: z.string(),
74
73
  shippingInfo: shippingInfoSchema.optional(),
74
+ personal_org_id: z.string().optional(),
75
+ is_tscircuit_staff: z.boolean().default(false),
75
76
  })
76
77
  export type Account = z.infer<typeof accountSchema>
77
78
 
@@ -244,6 +245,22 @@ export const packageReleaseSchema = z.object({
244
245
  circuit_json_build_logs: z.array(z.any()).default([]),
245
246
  circuit_json_build_is_stale: z.boolean().default(false),
246
247
 
248
+ // Image Generation Process
249
+ image_generation_display_status: z
250
+ .enum(["pending", "building", "complete", "error"])
251
+ .default("pending"),
252
+ image_generation_in_progress: z.boolean().default(false),
253
+ image_generation_started_at: z.string().datetime().nullable().optional(),
254
+ image_generation_completed_at: z.string().datetime().nullable().optional(),
255
+ image_generation_logs: z.array(z.any()).nullable().default(null),
256
+ image_generation_is_stale: z.boolean().default(false),
257
+ image_generation_error: z.string().nullable().optional(),
258
+ image_generation_error_last_updated_at: z
259
+ .string()
260
+ .datetime()
261
+ .nullable()
262
+ .optional(),
263
+
247
264
  // AI Review
248
265
  ai_review_text: z.string().nullable().default(null).optional(),
249
266
  ai_review_started_at: z.string().datetime().nullable().optional(),
@@ -367,6 +384,11 @@ export const packageBuildSchema = z.object({
367
384
  circuit_json_build_completed_at: z.string().datetime().nullable().optional(),
368
385
  circuit_json_build_logs: z.array(z.any()).default([]),
369
386
  circuit_json_build_error: z.string().nullable().optional(),
387
+ image_generation_in_progress: z.boolean().default(false),
388
+ image_generation_started_at: z.string().datetime().nullable().optional(),
389
+ image_generation_completed_at: z.string().datetime().nullable().optional(),
390
+ image_generation_logs: z.array(z.any()).default([]),
391
+ image_generation_error: z.string().nullable().optional(),
370
392
  build_in_progress: z.boolean().default(false),
371
393
  build_started_at: z.string().datetime().nullable().optional(),
372
394
  build_completed_at: z.string().datetime().nullable().optional(),
@@ -377,6 +399,44 @@ export const packageBuildSchema = z.object({
377
399
  })
378
400
  export type PackageBuild = z.infer<typeof packageBuildSchema>
379
401
 
402
+ export const orgSchema = z.object({
403
+ org_id: z.string(),
404
+ github_handle: z.string().optional(),
405
+ owner_account_id: z.string(),
406
+ is_personal_org: z.boolean().default(false),
407
+ created_at: z.string().datetime(),
408
+ org_display_name: z.string().optional(),
409
+ org_name: z.string(),
410
+ })
411
+ export type Organization = z.infer<typeof orgSchema>
412
+
413
+ export const orgAccountSchema = z.object({
414
+ org_account_id: z.string(),
415
+ org_id: z.string(),
416
+ account_id: z.string(),
417
+ is_owner: z.boolean().default(false),
418
+ created_at: z.string().datetime(),
419
+ })
420
+ export type OrgAccount = z.infer<typeof orgAccountSchema>
421
+
422
+ export const publicOrgSchema = z.object({
423
+ org_id: z.string(), //.uuid(),
424
+ owner_account_id: z.string(), //.uuid(),
425
+ name: z.string().nullable(),
426
+ member_count: z.number(),
427
+ is_personal_org: z.boolean(),
428
+ display_name: z.string().optional(),
429
+ package_count: z.number(),
430
+ created_at: z.string(),
431
+ user_permissions: z
432
+ .object({
433
+ can_manage_org: z.boolean().optional(),
434
+ can_manage_package: z.boolean().optional(),
435
+ })
436
+ .optional(),
437
+ })
438
+ export type PublicOrgSchema = z.infer<typeof publicOrgSchema>
439
+
380
440
  export const databaseSchema = z.object({
381
441
  idCounter: z.number().default(0),
382
442
  snippets: z.array(snippetSchema).default([]),
@@ -387,6 +447,8 @@ export const databaseSchema = z.object({
387
447
  accounts: z.array(accountSchema).default([]),
388
448
  packages: z.array(packageSchema).default([]),
389
449
  orders: z.array(orderSchema).default([]),
450
+ organizations: z.array(orgSchema).default([]),
451
+ orgAccounts: z.array(orgAccountSchema).default([]),
390
452
  orderFiles: z.array(orderFileSchema).default([]),
391
453
  accountSnippets: z.array(accountSnippetSchema).default([]),
392
454
  accountPackages: z.array(accountPackageSchema).default([]),
@@ -1733,4 +1733,104 @@ export const SquareWaveModule = () => (
1733
1733
  started_at: null,
1734
1734
  completed_at: null,
1735
1735
  })
1736
+
1737
+ const testOrg = db.addOrganization({
1738
+ name: "test-organization",
1739
+ owner_account_id: account_id,
1740
+ })
1741
+
1742
+ // Add org member
1743
+ db.addOrganizationAccount({
1744
+ org_id: testOrg.org_id,
1745
+ account_id: account_id,
1746
+ is_owner: true,
1747
+ })
1748
+
1749
+ const { package_release_id: orgPackageReleaseId } = db.addSnippet({
1750
+ name: "test-organization/test-package",
1751
+ unscoped_name: "test-package",
1752
+ owner_name: "test-organization",
1753
+ code: `
1754
+ export const TestComponent = ({ name }: { name: string }) => (
1755
+ <resistor name={name} resistance="10k" />
1756
+ )
1757
+ `.trim(),
1758
+ dts: `
1759
+ declare module "@tsci/test-organization.test-package" {
1760
+ export const TestComponent: ({ name }: {
1761
+ name: string;
1762
+ }) => any;
1763
+ }
1764
+ `.trim(),
1765
+ compiled_js: `
1766
+ "use strict";
1767
+
1768
+ Object.defineProperty(exports, "__esModule", {
1769
+ value: true
1770
+ });
1771
+ exports.TestComponent = void 0;
1772
+ const TestComponent = ({
1773
+ name
1774
+ }) => /*#__PURE__*/React.createElement("resistor", {
1775
+ name: name,
1776
+ resistance: "10k"
1777
+ });
1778
+ exports.TestComponent = TestComponent;
1779
+ `.trim(),
1780
+ created_at: new Date().toISOString(),
1781
+ updated_at: new Date().toISOString(),
1782
+ snippet_type: "package",
1783
+ description: "Test package for organization",
1784
+ circuit_json: [
1785
+ {
1786
+ type: "source_component",
1787
+ source_component_id: "source_component_0",
1788
+ ftype: "simple_resistor",
1789
+ name: "R1",
1790
+ resistance: "10k",
1791
+ },
1792
+ ],
1793
+ })
1794
+
1795
+ // Add successful build for org package
1796
+ const orgPackageBuild = db.addPackageBuild({
1797
+ package_release_id: orgPackageReleaseId,
1798
+ created_at: new Date().toISOString(),
1799
+ transpilation_in_progress: false,
1800
+ transpilation_started_at: new Date(Date.now() - 5000).toISOString(),
1801
+ transpilation_completed_at: new Date(Date.now() - 3000).toISOString(),
1802
+ transpilation_logs: [
1803
+ "[INFO] Starting transpilation...",
1804
+ "[SUCCESS] Transpilation completed successfully",
1805
+ ],
1806
+ transpilation_error: null,
1807
+ circuit_json_build_in_progress: false,
1808
+ circuit_json_build_started_at: new Date(Date.now() - 3000).toISOString(),
1809
+ circuit_json_build_completed_at: new Date(Date.now() - 1000).toISOString(),
1810
+ circuit_json_build_logs: [
1811
+ "[INFO] Starting circuit JSON build...",
1812
+ "[SUCCESS] Circuit JSON build completed",
1813
+ ],
1814
+ circuit_json_build_error: null,
1815
+ build_in_progress: false,
1816
+ build_started_at: new Date(Date.now() - 10000).toISOString(),
1817
+ build_completed_at: new Date().toISOString(),
1818
+ build_error: null,
1819
+ build_error_last_updated_at: new Date().toISOString(),
1820
+ preview_url: "http://localhost:3000/preview/org_package_build",
1821
+ build_logs: "Build completed successfully",
1822
+ })
1823
+
1824
+ // Update the org package release with the build ID
1825
+ const orgRelease = db.getPackageReleaseById(orgPackageReleaseId)!
1826
+ db.updatePackageRelease({
1827
+ ...orgRelease,
1828
+ latest_package_build_id: orgPackageBuild.package_build_id,
1829
+ })
1830
+
1831
+ db.addOrganization({
1832
+ name: "testuser",
1833
+ owner_account_id: account_id,
1834
+ is_personal_org: true,
1835
+ })
1736
1836
  }
@@ -1,10 +1,11 @@
1
1
  import type { Middleware } from "winterspec/middleware"
2
2
  import type { CtxErrorFn } from "./with-ctx-error"
3
+ import type { DbClient } from "../db/db-client"
3
4
 
4
5
  export const withSessionAuth: Middleware<
5
6
  {
6
7
  error: CtxErrorFn
7
- db: any
8
+ db: DbClient
8
9
  },
9
10
  {
10
11
  auth: {
@@ -13,6 +14,13 @@ export const withSessionAuth: Middleware<
13
14
  personal_org_id: string
14
15
  github_username: string
15
16
  session_id: string
17
+ orgs: Array<{
18
+ org_id: string
19
+ name: string
20
+ user_permissions: {
21
+ can_manage_packages: boolean
22
+ }
23
+ }>
16
24
  }
17
25
  },
18
26
  {}
@@ -22,28 +30,72 @@ export const withSessionAuth: Middleware<
22
30
  const token = req.headers.get("authorization")?.split("Bearer ")?.[1]
23
31
 
24
32
  // Only check database accounts when we're in a Bun test environment
25
- if (process.env.BUN_TEST === "true" && ctx.db?.accounts) {
26
- const account = ctx.db.accounts.find((acc: any) => acc.account_id === token)
27
-
33
+ if (process.env.BUN_TEST === "true" && ctx.db?.getState) {
34
+ const state = ctx.db.getState()
35
+ const account = state.accounts.find((acc: any) => acc.account_id === token)
28
36
  if (account) {
37
+ // Fetch orgs for this account
38
+ const orgAccounts = state.orgAccounts.filter(
39
+ (oa: any) => oa.account_id === account.account_id,
40
+ )
41
+
42
+ const orgs = orgAccounts.map((oa: any) => {
43
+ const org = state.organizations.find((o: any) => o.org_id === oa.org_id)
44
+ return {
45
+ org_id: oa.org_id,
46
+ name:
47
+ org?.org_display_name ||
48
+ org?.org_name ||
49
+ org?.github_handle ||
50
+ oa.org_id,
51
+ user_permissions: { can_manage_packages: true },
52
+ }
53
+ })
54
+
29
55
  ctx.auth = {
30
56
  type: "session",
31
57
  account_id: account.account_id,
32
- personal_org_id: `org-${account.account_id}`,
58
+ personal_org_id: account.personal_org_id || `org-${account.account_id}`,
33
59
  github_username: account.github_username,
34
60
  session_id: `session-${account.account_id}`,
61
+ orgs:
62
+ orgs.length > 0
63
+ ? orgs
64
+ : [
65
+ {
66
+ org_id:
67
+ account.personal_org_id || `org-${account.account_id}`,
68
+ name:
69
+ account.github_username ||
70
+ account.personal_org_id ||
71
+ `org-${account.account_id}`,
72
+ user_permissions: { can_manage_packages: true },
73
+ },
74
+ ],
35
75
  }
36
76
  return next(req, ctx)
37
77
  }
38
78
  }
39
79
 
40
- // For all other environments or if account not found in test, use hardcoded auth
80
+ // Fallback auth for non-test environments or when no token is found
81
+ const fallbackAccountId = "account-1234"
82
+ const fallbackOrgId = "org-1234"
83
+
84
+ const fallbackOrgs = [
85
+ {
86
+ org_id: fallbackOrgId,
87
+ name: "org-1234",
88
+ user_permissions: { can_manage_packages: true },
89
+ },
90
+ ]
91
+
41
92
  ctx.auth = {
42
93
  type: "session",
43
- account_id: "account-1234",
44
- personal_org_id: "org-1234",
94
+ account_id: fallbackAccountId,
95
+ personal_org_id: fallbackOrgId,
45
96
  github_username: "testuser",
46
97
  session_id: "session-1234",
98
+ orgs: fallbackOrgs,
47
99
  }
48
100
 
49
101
  return next(req, ctx)