@tscircuit/fake-snippets 0.0.108 → 0.0.110

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 (203) 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 +31 -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 +99 -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 +389 -450
  34. package/bunfig.toml +2 -1
  35. package/dist/bundle.js +1255 -625
  36. package/dist/index.d.ts +296 -4
  37. package/dist/index.js +325 -24
  38. package/dist/schema.d.ts +282 -1
  39. package/dist/schema.js +54 -2
  40. package/fake-snippets-api/lib/db/autoload-dev-packages.ts +31 -20
  41. package/fake-snippets-api/lib/db/db-client.ts +214 -3
  42. package/fake-snippets-api/lib/db/schema.ts +62 -0
  43. package/fake-snippets-api/lib/db/seed.ts +100 -0
  44. package/fake-snippets-api/lib/middleware/with-session-auth.ts +1 -1
  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 +32 -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 +46 -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 +67 -0
  54. package/fake-snippets-api/routes/api/orgs/remove_member.ts +46 -0
  55. package/fake-snippets-api/routes/api/orgs/update.ts +93 -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 +54 -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 +27 -24
  64. package/renovate.json +1 -1
  65. package/scripts/generate-sitemap.ts +1 -1
  66. package/src/App.tsx +29 -10
  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 +133 -36
  71. package/src/components/FileSidebar.tsx +41 -50
  72. package/src/components/Footer.tsx +8 -10
  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 +44 -16
  77. package/src/components/HiddenFilesDropdown.tsx +0 -2
  78. package/src/components/NotFound.tsx +5 -5
  79. package/src/components/PackageBreadcrumb.tsx +6 -12
  80. package/src/components/PackageCard.tsx +0 -1
  81. package/src/components/PackageSearchResults.tsx +1 -1
  82. package/src/components/PrefetchPageLink.tsx +7 -1
  83. package/src/components/ProfileRouter.tsx +32 -0
  84. package/src/components/SearchComponent.tsx +12 -8
  85. package/src/components/UserCard.tsx +80 -0
  86. package/src/components/ViewPackagePage/components/ShikiCodeViewer.tsx +20 -11
  87. package/src/components/ViewPackagePage/components/build-status.tsx +1 -1
  88. package/src/components/ViewPackagePage/components/important-files-view.tsx +174 -87
  89. package/src/components/ViewPackagePage/components/main-content-header.tsx +8 -4
  90. package/src/components/ViewPackagePage/components/main-content-view-selector.tsx +1 -2
  91. package/src/components/ViewPackagePage/components/mobile-sidebar.tsx +54 -20
  92. package/src/components/ViewPackagePage/components/package-header.tsx +26 -37
  93. package/src/components/ViewPackagePage/components/preview-image-squares.tsx +11 -19
  94. package/src/components/ViewPackagePage/components/repo-page-content.tsx +33 -25
  95. package/src/components/ViewPackagePage/components/sidebar-about-section.tsx +16 -10
  96. package/src/components/ViewPackagePage/components/sidebar-releases-section.tsx +11 -11
  97. package/src/components/ViewPackagePage/components/sidebar.tsx +0 -2
  98. package/src/components/ViewPackagePage/components/tab-views/files-view.tsx +18 -17
  99. package/src/components/ViewPackagePage/components/tab-views/pcb-view.tsx +1 -2
  100. package/src/components/ViewPackagePage/components/tab-views/schematic-view.tsx +2 -1
  101. package/src/components/ViewPackagePage/components/theme-toggle.tsx +0 -2
  102. package/src/components/ViewPackagePage/hooks/use-toast.tsx +0 -1
  103. package/src/components/dialogs/GitHubRepositorySelector.tsx +56 -49
  104. package/src/components/dialogs/edit-package-details-dialog.tsx +5 -6
  105. package/src/components/dialogs/import-component-dialog.tsx +16 -9
  106. package/src/components/dialogs/import-package-dialog.tsx +3 -2
  107. package/src/components/dialogs/new-package-save-prompt-dialog.tsx +190 -0
  108. package/src/components/organization/OrganizationCard.tsx +204 -0
  109. package/src/components/organization/OrganizationCardSkeleton.tsx +55 -0
  110. package/src/components/organization/OrganizationHeader.tsx +154 -0
  111. package/src/components/organization/OrganizationMembers.tsx +146 -0
  112. package/src/components/package-port/CodeAndPreview.tsx +32 -46
  113. package/src/components/package-port/CodeEditor.tsx +28 -31
  114. package/src/components/package-port/CodeEditorHeader.tsx +128 -63
  115. package/src/components/package-port/EditorNav.tsx +32 -49
  116. package/src/components/preview/ConnectedPackagesList.tsx +8 -8
  117. package/src/components/preview/ConnectedRepoOverview.tsx +102 -2
  118. package/src/components/preview/PackageReleasesDashboard.tsx +53 -36
  119. package/src/components/ui/tree-view.tsx +6 -3
  120. package/src/hooks/use-add-org-member-mutation.ts +51 -0
  121. package/src/hooks/use-create-org-mutation.ts +38 -0
  122. package/src/hooks/use-create-package-mutation.ts +3 -0
  123. package/src/hooks/use-current-package-id.ts +5 -30
  124. package/src/hooks/use-current-package-info.ts +29 -5
  125. package/src/hooks/use-current-package-release.ts +4 -3
  126. package/src/hooks/use-download-zip.ts +2 -2
  127. package/src/hooks/use-global-store.ts +6 -4
  128. package/src/hooks/use-jlcpcb-component-import.tsx +164 -0
  129. package/src/hooks/use-list-org-members.ts +27 -0
  130. package/src/hooks/use-list-user-orgs.ts +25 -0
  131. package/src/hooks/use-org-by-github-handle.ts +26 -0
  132. package/src/hooks/use-org.ts +24 -0
  133. package/src/hooks/use-organization.ts +42 -0
  134. package/src/hooks/use-package-as-snippet.ts +4 -2
  135. package/src/hooks/use-package-builds.ts +6 -2
  136. package/src/hooks/use-package-files.ts +5 -3
  137. package/src/hooks/use-package-release-by-id-or-version.ts +29 -20
  138. package/src/hooks/use-package-release-images.ts +105 -0
  139. package/src/hooks/use-package-release.ts +2 -2
  140. package/src/hooks/use-package-stars.ts +80 -4
  141. package/src/hooks/use-preview-images.ts +6 -3
  142. package/src/hooks/use-remove-org-member-mutation.ts +32 -0
  143. package/src/hooks/use-update-ai-description-mutation.ts +42 -0
  144. package/src/hooks/use-update-org-mutation.ts +41 -0
  145. package/src/hooks/use-warn-user-on-page-change.ts +71 -4
  146. package/src/hooks/useFileManagement.ts +183 -35
  147. package/src/hooks/useOptimizedPackageFilesLoader.ts +136 -0
  148. package/src/hooks/usePackageFilesLoader.ts +2 -2
  149. package/src/hooks/useUpdatePackageFilesMutation.ts +15 -1
  150. package/src/lib/download-fns/download-circuit-png.ts +11 -3
  151. package/src/lib/download-fns/download-gltf-from-circuit-json.ts +44 -0
  152. package/src/lib/download-fns/download-kicad-files.ts +12 -11
  153. package/src/lib/normalize-svg-for-tile.ts +50 -0
  154. package/src/lib/posthog.ts +11 -9
  155. package/src/lib/react-query-api-failure-tracking.ts +148 -0
  156. package/src/lib/sentry.ts +14 -0
  157. package/src/lib/templates/blank-circuit-board-template.ts +0 -4
  158. package/src/lib/ts-lib-cache.ts +122 -7
  159. package/src/lib/utils/checkIfManualEditsImported.ts +4 -4
  160. package/src/lib/utils/findTargetFile.ts +45 -10
  161. package/src/lib/utils/isComponentExported.ts +10 -0
  162. package/src/main.tsx +2 -1
  163. package/src/pages/authorize.tsx +0 -2
  164. package/src/pages/create-organization.tsx +168 -0
  165. package/src/pages/dashboard.tsx +38 -6
  166. package/src/pages/datasheet.tsx +1 -1
  167. package/src/pages/datasheets.tsx +3 -3
  168. package/src/pages/editor.tsx +4 -6
  169. package/src/pages/landing.tsx +6 -7
  170. package/src/pages/latest.tsx +3 -0
  171. package/src/pages/organization-profile.tsx +199 -0
  172. package/src/pages/organization-settings.tsx +566 -0
  173. package/src/pages/package-editor.tsx +21 -21
  174. package/src/pages/preview-release.tsx +76 -136
  175. package/src/pages/quickstart.tsx +159 -123
  176. package/src/pages/release-detail.tsx +119 -31
  177. package/src/pages/search.tsx +192 -57
  178. package/src/pages/settings-redirect.tsx +44 -0
  179. package/src/pages/trending.tsx +29 -20
  180. package/src/pages/user-profile.tsx +58 -7
  181. package/src/pages/view-package.tsx +21 -26
  182. package/vite.config.ts +9 -0
  183. package/fake-snippets-api/routes/api/autocomplete/create_autocomplete.ts +0 -133
  184. package/src/components/Footer2.tsx +0 -100
  185. package/src/components/JLCPCBImportDialog.tsx +0 -280
  186. package/src/components/PackageBuildsPage/LogContent.tsx +0 -72
  187. package/src/components/PackageBuildsPage/PackageBuildDetailsPage.tsx +0 -115
  188. package/src/components/PackageBuildsPage/build-preview-content.tsx +0 -27
  189. package/src/components/PackageBuildsPage/collapsible-section.tsx +0 -63
  190. package/src/components/PackageBuildsPage/package-build-details-panel.tsx +0 -166
  191. package/src/components/PackageBuildsPage/package-build-header.tsx +0 -79
  192. package/src/components/PageSearchComponent.tsx +0 -148
  193. package/src/components/ShippingInformationForm.tsx +0 -423
  194. package/src/components/StaticViewSnippetHeader.tsx +0 -70
  195. package/src/components/ViewPackagePage/components/file-explorer.tsx +0 -67
  196. package/src/components/ViewPackagePage/components/readme-view.tsx +0 -58
  197. package/src/components/ViewPackagePage/components/repo-header-button.tsx +0 -36
  198. package/src/components/ViewPackagePage/components/repo-header.tsx +0 -4
  199. package/src/components/ViewPackagePage/components/sidebar-contributors-section.tsx +0 -31
  200. package/src/components/ViewSnippetHeader.tsx +0 -181
  201. package/src/components/ui/input-otp.tsx +0 -69
  202. package/src/pages/package-builds.tsx +0 -33
  203. package/src/pages/settings.tsx +0 -25
@@ -12,10 +12,10 @@ jobs:
12
12
  runs-on: ubuntu-latest
13
13
 
14
14
  steps:
15
- - uses: actions/checkout@v3
15
+ - uses: actions/checkout@v4
16
16
 
17
17
  - name: Setup bun
18
- uses: oven-sh/setup-bun@v1
18
+ uses: oven-sh/setup-bun@v2
19
19
  with:
20
20
  bun-version: latest
21
21
 
@@ -11,12 +11,12 @@ jobs:
11
11
  publish:
12
12
  runs-on: ubuntu-latest
13
13
  steps:
14
- - uses: actions/checkout@v3
14
+ - uses: actions/checkout@v4
15
15
  - name: Setup bun
16
- uses: oven-sh/setup-bun@v1
16
+ uses: oven-sh/setup-bun@v2
17
17
  with:
18
18
  bun-version: latest
19
- - uses: actions/setup-node@v3
19
+ - uses: actions/setup-node@v5
20
20
  with:
21
21
  node-version: 20
22
22
  registry-url: https://registry.npmjs.org/
@@ -17,7 +17,7 @@ jobs:
17
17
  uses: actions/checkout@v4
18
18
 
19
19
  - name: Setup bun
20
- uses: oven-sh/setup-bun@v1
20
+ uses: oven-sh/setup-bun@v2
21
21
  with:
22
22
  bun-version: latest
23
23
 
@@ -12,10 +12,10 @@ jobs:
12
12
  runs-on: ubuntu-latest
13
13
 
14
14
  steps:
15
- - uses: actions/checkout@v3
15
+ - uses: actions/checkout@v4
16
16
 
17
17
  - name: Setup bun
18
- uses: oven-sh/setup-bun@v1
18
+ uses: oven-sh/setup-bun@v2
19
19
  with:
20
20
  bun-version: latest
21
21
 
@@ -26,7 +26,7 @@ jobs:
26
26
  GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
27
27
 
28
28
  - name: Setup bun
29
- uses: oven-sh/setup-bun@v1
29
+ uses: oven-sh/setup-bun@v2
30
30
  with:
31
31
  bun-version: latest
32
32
 
package/README.md CHANGED
@@ -62,6 +62,10 @@ VITE_USE_DIRECT_AI_REQUESTS=true
62
62
  VITE_ANTHROPIC_API_KEY=<your-key-here>
63
63
  ```
64
64
 
65
+ ### Error Reporting
66
+
67
+ To enable Sentry error monitoring, set the `SENTRY_DSN` environment variable before running the app. The value will be exposed to the client as `VITE_SENTRY_DSN` and used to initialize Sentry.
68
+
65
69
  ### Building for Production
66
70
 
67
71
  To build the project for production:
@@ -36,7 +36,7 @@ const PREFETCHABLE_PAGES = new Set([
36
36
 
37
37
  const pageDescriptions = {
38
38
  landing:
39
- "Build electronics with code, AI, and drag'n'drop tools. Render code into schematics, PCBs, 3D, fabrication files, and more. Open-source MIT licensed electronic design automation tool.",
39
+ "Build electronics with code and AI tools. Render code into schematics, PCBs, 3D, fabrication files, and more. Open-source MIT licensed electronic design automation tool.",
40
40
  dashboard:
41
41
  "Your tscircuit dashboard - manage your electronic circuit packages, view trending and latest packages, and access your recent designs.",
42
42
  search:
@@ -182,6 +182,9 @@ async function handleCustomPackageHtml(req, res) {
182
182
  const [_, author, unscopedPackageName, other] = req.url
183
183
  .split("?")[0]
184
184
  .split("/")
185
+ if (unscopedPackageName === "settings") {
186
+ throw new Error("Organization settings route")
187
+ }
185
188
  if (other == "releases" || other == "release") {
186
189
  throw new Error("Release route")
187
190
  }
@@ -240,8 +243,8 @@ async function handleCustomPackageHtml(req, res) {
240
243
  if (packageRelease?.package_release_id) {
241
244
  try {
242
245
  const filesResponse = await ky
243
- .post(`${REGISTRY_URL}/package_files/list`, {
244
- json: {
246
+ .get(`${REGISTRY_URL}/package_files/list`, {
247
+ searchParams: {
245
248
  package_release_id: packageRelease.package_release_id,
246
249
  },
247
250
  })
@@ -289,6 +292,30 @@ async function handleCustomPackageHtml(req, res) {
289
292
  res.setHeader("Vary", "Accept-Encoding")
290
293
  res.status(200).send(html)
291
294
  }
295
+
296
+ async function handleOrganizationSettings(req, res) {
297
+ const [_, orgSlug, settings] = req.url.split("?")[0].split("/")
298
+ if (!orgSlug || settings !== "settings") {
299
+ throw new Error("Not an organization settings route")
300
+ }
301
+
302
+ const encodedSlugForUrl = encodeURIComponent(orgSlug)
303
+ const title = he.encode(`${orgSlug} Settings - tscircuit`)
304
+ const description = he.encode(
305
+ `Manage the ${orgSlug} organization settings on tscircuit.`,
306
+ )
307
+
308
+ const html = getHtmlWithModifiedSeoTags({
309
+ title,
310
+ description,
311
+ canonicalUrl: he.encode(`${BASE_URL}/${encodedSlugForUrl}/settings`),
312
+ })
313
+
314
+ res.setHeader("Content-Type", "text/html; charset=utf-8")
315
+ res.setHeader("Cache-Control", cacheControlHeader)
316
+ res.setHeader("Vary", "Accept-Encoding")
317
+ res.status(200).send(html)
318
+ }
292
319
  async function handleCustomPage(req, res) {
293
320
  const [_, page] = req.url.split("?")[0].split("/")
294
321
 
@@ -400,6 +427,13 @@ export default async function handler(req, res) {
400
427
  }
401
428
  }
402
429
 
430
+ try {
431
+ await handleOrganizationSettings(req, res)
432
+ return
433
+ } catch (e) {
434
+ console.warn(e)
435
+ }
436
+
403
437
  try {
404
438
  await handleCustomPage(req, res)
405
439
  return
package/biome.json CHANGED
@@ -13,7 +13,8 @@
13
13
  "dist",
14
14
  "package.json",
15
15
  ".vercel",
16
- "src/index.css"
16
+ "src/index.css",
17
+ "renovate.json"
17
18
  ]
18
19
  },
19
20
  "javascript": {
@@ -1,4 +1,3 @@
1
- import { afterEach } from "bun:test"
2
1
  import defaultAxios from "redaxios"
3
2
  import { startServer } from "./start-server"
4
3
  import { DbClient } from "fake-snippets-api/lib/db/db-client"
@@ -40,7 +39,7 @@ export const getTestServer = async (): Promise<TestFixture> => {
40
39
  baseURL: url,
41
40
  })
42
41
 
43
- afterEach(async () => {
42
+ globalThis.deferredCleanupFns.push(async () => {
44
43
  if (server && typeof server.stop === "function") {
45
44
  await server.stop()
46
45
  }
@@ -73,6 +72,22 @@ const seedDatabase = (db: DbClient) => {
73
72
  phone: "555-123-4567",
74
73
  },
75
74
  })
75
+
76
+ // Create personal organization for the main test account
77
+ const personalOrg = db.addOrganization({
78
+ org_id: "personal-org-1",
79
+ name: "testuser",
80
+ owner_account_id: account.account_id,
81
+ is_personal_org: true,
82
+ })
83
+
84
+ // Add the account as a member of their personal org
85
+ db.addOrganizationAccount({
86
+ org_id: personalOrg.org_id,
87
+ account_id: account.account_id,
88
+ is_owner: true,
89
+ })
90
+
76
91
  const account2 = db.addAccount({
77
92
  github_username: "jane",
78
93
  })
@@ -108,5 +123,18 @@ const seedDatabase = (db: DbClient) => {
108
123
  transpilation_error: null,
109
124
  })
110
125
 
111
- return { account, account2, order, packageRelease }
126
+ // Seed a organization for account2
127
+ const organization = db.addOrganization({
128
+ name: "jane",
129
+ owner_account_id: account2.account_id,
130
+ })
131
+
132
+ // Add account2 as a member of their org
133
+ db.addOrganizationAccount({
134
+ org_id: organization.org_id,
135
+ account_id: account2.account_id,
136
+ is_owner: true,
137
+ })
138
+
139
+ return { account, account2, order, packageRelease, organization }
112
140
  }
@@ -0,0 +1,18 @@
1
+ import { afterEach } from "bun:test"
2
+
3
+ declare global {
4
+ // Add the property to the globalThis type
5
+ // eslint-disable-next-line no-var
6
+ var deferredCleanupFns: Array<() => Promise<void>>
7
+ }
8
+
9
+ if (!globalThis.deferredCleanupFns) {
10
+ globalThis.deferredCleanupFns = []
11
+ }
12
+
13
+ afterEach(async () => {
14
+ for (const fn of globalThis.deferredCleanupFns) {
15
+ await fn()
16
+ }
17
+ globalThis.deferredCleanupFns = []
18
+ })
@@ -0,0 +1,26 @@
1
+ import { getTestServer } from "bun-tests/fake-snippets-api/fixtures/get-test-server"
2
+ import { expect, test } from "bun:test"
3
+
4
+ test("POST /api/orgs/add_member - should add a user to an org (owner authorized)", async () => {
5
+ const { jane_axios, db, seed } = await getTestServer()
6
+
7
+ const originalMember = db.getAccount(seed.account.account_id)
8
+
9
+ const addResponse = await jane_axios.post("/api/orgs/add_member", {
10
+ org_id: seed.organization.org_id,
11
+ account_id: seed.account.account_id,
12
+ })
13
+
14
+ expect(addResponse.status).toBe(200)
15
+ expect(addResponse.data).toEqual({})
16
+
17
+ expect(db.getAccount(seed.account.account_id)?.personal_org_id).toEqual(
18
+ String(originalMember?.personal_org_id),
19
+ )
20
+
21
+ const membership = db.getOrganizationAccount({
22
+ account_id: seed.account.account_id,
23
+ org_id: seed.organization.org_id,
24
+ })
25
+ expect(membership).toBeDefined()
26
+ })
@@ -0,0 +1,37 @@
1
+ import { getTestServer } from "bun-tests/fake-snippets-api/fixtures/get-test-server"
2
+ import { expect, test } from "bun:test"
3
+
4
+ test("POST /api/orgs/create - should create a new org for the user", async () => {
5
+ const { axios } = await getTestServer()
6
+ const orgName = "acme-corp"
7
+ const createResponse = await axios.post("/api/orgs/create", {
8
+ name: orgName,
9
+ })
10
+
11
+ expect(createResponse.status).toBe(200)
12
+ const responseBody = createResponse.data
13
+ expect(responseBody.org).toBeDefined()
14
+ expect(responseBody.org.name).toBe(orgName)
15
+ expect(responseBody.org.owner_account_id).toBe(
16
+ String((axios.defaults.headers as any)?.["Authorization"]?.split(" ")[1]),
17
+ )
18
+ expect(responseBody.org.member_count).toBe(1)
19
+ expect(responseBody.org.package_count).toBe(0)
20
+ expect(responseBody.org.user_permissions?.can_manage_org).toBe(true)
21
+ })
22
+
23
+ test("POST /api/orgs/create - should reject duplicate org names", async () => {
24
+ const { axios, seed } = await getTestServer()
25
+ try {
26
+ const orgResponse = await axios.post("/api/orgs/create", {
27
+ name: seed.organization.github_handle,
28
+ })
29
+ throw new Error("Expected request to fail")
30
+ } catch (error: any) {
31
+ expect(error.status).toBe(400)
32
+ expect(error.data.error.error_code).toBe("org_already_exists")
33
+ expect(error.data.error.message).toBe(
34
+ "An organization with this name already exists",
35
+ )
36
+ }
37
+ })
@@ -0,0 +1,52 @@
1
+ import { getTestServer } from "bun-tests/fake-snippets-api/fixtures/get-test-server"
2
+ import { expect, test } from "bun:test"
3
+
4
+ test("GET /api/orgs/get - should return org by org_id", async () => {
5
+ const { axios, jane_axios, seed } = await getTestServer()
6
+
7
+ const getResponse = await jane_axios.get("/api/orgs/get", {
8
+ params: { org_id: seed.organization.org_id },
9
+ })
10
+
11
+ const getNotOwnerResponse = await axios.get("/api/orgs/get", {
12
+ params: { org_id: seed.organization.org_id },
13
+ })
14
+
15
+ expect(getResponse.status).toBe(200)
16
+ const responseBody = getResponse.data
17
+ const responseBody2 = getNotOwnerResponse.data
18
+ expect(responseBody.org).toBeDefined()
19
+ expect(responseBody.org.org_id).toBe(seed.organization.org_id)
20
+ expect(responseBody.org.name).toBe(seed.organization.github_handle)
21
+ expect(responseBody.org.user_permissions?.can_manage_org).toBe(true)
22
+ expect(responseBody2.org.user_permissions?.can_manage_org).not.toBe(true)
23
+ })
24
+
25
+ test("GET /api/orgs/get - should return org by github_handle", async () => {
26
+ const { axios } = await getTestServer()
27
+
28
+ const getResponse = await axios.get("/api/orgs/get", {
29
+ params: { github_handle: "jane" },
30
+ })
31
+
32
+ expect(getResponse.status).toBe(200)
33
+ const responseBody = getResponse.data
34
+ expect(responseBody.org).toBeDefined()
35
+ expect(responseBody.org.name).toBe("jane")
36
+ expect(responseBody.org.user_permissions?.can_manage_org).not.toBe(true)
37
+ })
38
+
39
+ test("GET /api/orgs/get - should return 404 if org not found", async () => {
40
+ const { axios } = await getTestServer()
41
+
42
+ try {
43
+ await axios.get("/api/orgs/get", {
44
+ params: { org_id: "non_existent_org_id" },
45
+ })
46
+ throw new Error("Expected request to fail")
47
+ } catch (error: any) {
48
+ expect(error.status).toBe(404)
49
+ expect(error.data.error.error_code).toBe("org_not_found")
50
+ expect(error.data.error.message).toBe("Organization not found")
51
+ }
52
+ })
@@ -0,0 +1,17 @@
1
+ import { getTestServer } from "bun-tests/fake-snippets-api/fixtures/get-test-server"
2
+ import { expect, test } from "bun:test"
3
+
4
+ test("GET /api/orgs/list - should return user's organizations when authenticated", async () => {
5
+ const { jane_axios } = await getTestServer()
6
+
7
+ const listResponse = await jane_axios.get("/api/orgs/list")
8
+
9
+ expect(listResponse.status).toBe(200)
10
+ const responseBody = listResponse.data
11
+ expect(responseBody.ok).toBe(true)
12
+ expect(Array.isArray(responseBody.orgs)).toBe(true)
13
+ const personalOrg = responseBody.orgs[0]
14
+ expect(personalOrg).toBeDefined()
15
+ expect(personalOrg.name).toBe("jane")
16
+ expect(personalOrg.user_permissions?.can_manage_org).toBe(true)
17
+ })
@@ -0,0 +1,23 @@
1
+ import { getTestServer } from "bun-tests/fake-snippets-api/fixtures/get-test-server"
2
+ import { expect, test } from "bun:test"
3
+
4
+ test("GET /orgs/list_members returns members for an org when owner", async () => {
5
+ const { jane_axios, seed } = await getTestServer()
6
+
7
+ await jane_axios.post("/api/orgs/add_member", {
8
+ org_id: seed.organization.org_id,
9
+ account_id: seed.account.account_id,
10
+ })
11
+
12
+ const {
13
+ data: { members },
14
+ } = await jane_axios.get(
15
+ `/api/orgs/list_members?org_id=${seed.organization.org_id}`,
16
+ )
17
+
18
+ expect(Array.isArray(members)).toBe(true)
19
+ const membership = members.find(
20
+ (m: any) => m.account_id === seed.account.account_id,
21
+ )
22
+ expect(membership).toBeDefined()
23
+ })
@@ -0,0 +1,81 @@
1
+ import { getTestServer } from "bun-tests/fake-snippets-api/fixtures/get-test-server"
2
+ import { expect, test } from "bun:test"
3
+
4
+ test("POST /api/orgs/remove_member - should remove a user from an org (resets to personal org)", async () => {
5
+ const { jane_axios, seed } = await getTestServer()
6
+
7
+ const createResponse = await jane_axios.post("/api/orgs/create", {
8
+ name: "globex",
9
+ })
10
+ const org = createResponse.data.org
11
+
12
+ await jane_axios.post("/api/orgs/add_member", {
13
+ org_id: org.org_id,
14
+ account_id: seed.account.account_id,
15
+ })
16
+
17
+ const removeResponse = await jane_axios.post("/api/orgs/remove_member", {
18
+ org_id: org.org_id,
19
+ account_id: seed.account.account_id,
20
+ })
21
+
22
+ expect(removeResponse.status).toBe(200)
23
+ expect(removeResponse.data).toEqual({})
24
+ })
25
+
26
+ test("POST /api/orgs/remove_member - should fail for non-owner (403)", async () => {
27
+ const { jane_axios, axios, seed } = await getTestServer()
28
+
29
+ const createResponse = await jane_axios.post("/api/orgs/create", {
30
+ name: "initech",
31
+ })
32
+ const org = createResponse.data.org
33
+
34
+ await jane_axios.post("/api/orgs/add_member", {
35
+ org_id: org.org_id,
36
+ account_id: seed.account.account_id,
37
+ })
38
+
39
+ try {
40
+ await axios.post("/api/orgs/remove_member", {
41
+ org_id: org.org_id,
42
+ account_id: seed.account2.account_id,
43
+ })
44
+ throw new Error("Expected request to fail")
45
+ } catch (error: any) {
46
+ expect(error.status).toBe(403)
47
+ expect(error.data.error.error_code).toBe("not_authorized")
48
+ }
49
+ })
50
+
51
+ test("POST /api/orgs/remove_member - should remove a user from an org and reset to personal org", async () => {
52
+ const { jane_axios, db, seed } = await getTestServer()
53
+ const originalMember = db.getAccount(seed.account.account_id)
54
+
55
+ const addResponse = await jane_axios.post("/api/orgs/add_member", {
56
+ org_id: seed.organization.org_id,
57
+ account_id: seed.account.account_id,
58
+ })
59
+
60
+ expect(addResponse.status).toBe(200)
61
+ const membership = db.getOrganizationAccount({
62
+ org_id: seed.organization.org_id,
63
+ account_id: seed.account.account_id,
64
+ })
65
+ expect(membership).toBeDefined()
66
+
67
+ await jane_axios.post("/api/orgs/remove_member", {
68
+ org_id: seed.organization.org_id,
69
+ account_id: seed.account.account_id,
70
+ })
71
+
72
+ expect(db.getAccount(seed.account.account_id)?.personal_org_id).toEqual(
73
+ String(originalMember?.personal_org_id),
74
+ )
75
+
76
+ const membershipAfter = db.getOrganizationAccount({
77
+ org_id: seed.organization.org_id,
78
+ account_id: seed.account.account_id,
79
+ })
80
+ expect(membershipAfter).toBeUndefined()
81
+ })
@@ -0,0 +1,99 @@
1
+ import { getTestServer } from "bun-tests/fake-snippets-api/fixtures/get-test-server"
2
+ import { expect, test } from "bun:test"
3
+
4
+ test("POST /api/orgs/update - should update org name when owner", async () => {
5
+ const { axios } = await getTestServer()
6
+
7
+ const createResponse = await axios.post("/api/orgs/create", {
8
+ name: "old-name",
9
+ })
10
+ const org = createResponse.data.org
11
+
12
+ const updateResponse = await axios.post("/api/orgs/update", {
13
+ org_id: org.org_id,
14
+ name: "new-name",
15
+ })
16
+
17
+ expect(updateResponse.status).toBe(200)
18
+ const responseBody = updateResponse.data
19
+ expect(responseBody.org).toBeDefined()
20
+ expect(responseBody.org.name).toBe("new-name")
21
+ expect(responseBody.org.user_permissions?.can_manage_org).toBe(true)
22
+ })
23
+
24
+ test("PATCH /api/orgs/update - should update org name using PATCH method", async () => {
25
+ const { axios } = await getTestServer()
26
+
27
+ const createResponse = await axios.post("/api/orgs/create", {
28
+ name: "patch-test",
29
+ })
30
+ const org = createResponse.data.org
31
+
32
+ const updateResponse = await axios.patch("/api/orgs/update", {
33
+ org_id: org.org_id,
34
+ name: "patch-updated",
35
+ })
36
+
37
+ expect(updateResponse.status).toBe(200)
38
+ expect(updateResponse.data.org.name).toBe("patch-updated")
39
+ })
40
+
41
+ test("POST /api/orgs/update - should return current org when no changes provided", async () => {
42
+ const { axios } = await getTestServer()
43
+
44
+ const createResponse = await axios.post("/api/orgs/create", {
45
+ name: "no-change",
46
+ })
47
+ const org = createResponse.data.org
48
+
49
+ const updateResponse = await axios.post("/api/orgs/update", {
50
+ org_id: org.org_id,
51
+ })
52
+
53
+ expect(updateResponse.status).toBe(200)
54
+ expect(updateResponse.data.org.name).toBe("no-change")
55
+ })
56
+
57
+ test("POST /api/orgs/update - should fail when user lacks management permissions", async () => {
58
+ const { axios, seed } = await getTestServer()
59
+
60
+ try {
61
+ await axios.post("/api/orgs/update", {
62
+ org_id: seed.organization.org_id,
63
+ name: "unauthorized-change",
64
+ })
65
+ throw new Error("Expected request to fail")
66
+ } catch (error: any) {
67
+ expect(error.status).toBe(403)
68
+ expect(error.data.error.error_code).toBe("not_authorized")
69
+ expect(error.data.error.message).toBe(
70
+ "You do not have permission to manage this organization",
71
+ )
72
+ }
73
+ })
74
+
75
+ test("POST /api/orgs/update - should reject duplicate name", async () => {
76
+ const { axios } = await getTestServer()
77
+
78
+ await axios.post("/api/orgs/create", {
79
+ name: "dup-a",
80
+ })
81
+ const org2Response = await axios.post("/api/orgs/create", {
82
+ name: "dup-b",
83
+ })
84
+
85
+ try {
86
+ const updateResponse = await axios.post("/api/orgs/update", {
87
+ org_id: org2Response.data.org.org_id,
88
+ name: "dup-a",
89
+ })
90
+ console.log(2, updateResponse.data)
91
+ throw new Error("Expected request to fail")
92
+ } catch (error: any) {
93
+ expect(error.status).toBe(400)
94
+ expect(error.data.error.error_code).toBe("org_already_exists")
95
+ expect(error.data.error.message).toBe(
96
+ "An organization with this name already exists",
97
+ )
98
+ }
99
+ })
@@ -24,7 +24,7 @@ export const createTestPackageRelease = async (
24
24
  return releaseRes.data
25
25
  }
26
26
 
27
- test("GET /api/package_builds/get - requires package_build_id 69", async () => {
27
+ test("GET /api/package_builds/get - requires package_build_id", async () => {
28
28
  const { jane_axios } = await getTestServer()
29
29
  const res = await jane_axios.get("/api/package_builds/get", {
30
30
  validateStatus: () => true,