@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.
- package/.github/workflows/bun-formatcheck.yml +2 -2
- package/.github/workflows/bun-pver-release.yml +3 -3
- package/.github/workflows/bun-test.yml +1 -1
- package/.github/workflows/bun-typecheck.yml +2 -2
- package/.github/workflows/update-snapshots.yml +1 -1
- package/README.md +4 -0
- package/api/generated-index.js +37 -3
- package/biome.json +2 -1
- package/bun-tests/fake-snippets-api/fixtures/get-test-server.ts +31 -3
- package/bun-tests/fake-snippets-api/fixtures/preload.ts +18 -0
- package/bun-tests/fake-snippets-api/routes/orgs/add_member.test.ts +26 -0
- package/bun-tests/fake-snippets-api/routes/orgs/create.test.ts +37 -0
- package/bun-tests/fake-snippets-api/routes/orgs/get.test.ts +52 -0
- package/bun-tests/fake-snippets-api/routes/orgs/list.test.ts +17 -0
- package/bun-tests/fake-snippets-api/routes/orgs/list_members.test.ts +23 -0
- package/bun-tests/fake-snippets-api/routes/orgs/remove_member.test.ts +81 -0
- package/bun-tests/fake-snippets-api/routes/orgs/update.test.ts +99 -0
- package/bun-tests/fake-snippets-api/routes/package_builds/get.test.ts +1 -1
- package/bun-tests/fake-snippets-api/routes/package_files/create.test.ts +15 -13
- package/bun-tests/fake-snippets-api/routes/package_files/create_or_update.test.ts +26 -24
- package/bun-tests/fake-snippets-api/routes/package_files/delete.test.ts +9 -9
- package/bun-tests/fake-snippets-api/routes/package_files/download.test.ts +4 -4
- package/bun-tests/fake-snippets-api/routes/package_files/get.test.ts +38 -28
- package/bun-tests/fake-snippets-api/routes/package_files/list.test.ts +23 -15
- package/bun-tests/fake-snippets-api/routes/package_releases/create.test.ts +33 -0
- package/bun-tests/fake-snippets-api/routes/package_releases/get.test.ts +4 -4
- package/bun-tests/fake-snippets-api/routes/package_releases/get_image_generation_fields.test.ts +38 -0
- package/bun-tests/fake-snippets-api/routes/packages/create.test.ts +19 -0
- package/bun-tests/fake-snippets-api/routes/packages/fork.test.ts +3 -4
- package/bun-tests/fake-snippets-api/routes/packages/get.test.ts +30 -0
- package/bun-tests/fake-snippets-api/routes/packages/images.test.ts +4 -2
- package/bun-tests/fake-snippets-api/routes/packages/list-1.test.ts +34 -0
- package/bun.lock +389 -450
- package/bunfig.toml +2 -1
- package/dist/bundle.js +1255 -625
- package/dist/index.d.ts +296 -4
- package/dist/index.js +325 -24
- package/dist/schema.d.ts +282 -1
- package/dist/schema.js +54 -2
- package/fake-snippets-api/lib/db/autoload-dev-packages.ts +31 -20
- package/fake-snippets-api/lib/db/db-client.ts +214 -3
- package/fake-snippets-api/lib/db/schema.ts +62 -0
- package/fake-snippets-api/lib/db/seed.ts +100 -0
- package/fake-snippets-api/lib/middleware/with-session-auth.ts +1 -1
- package/fake-snippets-api/lib/package_file/get-package-file-id-from-file-descriptor.ts +2 -2
- package/fake-snippets-api/lib/public-mapping/public-map-org.ts +32 -0
- package/fake-snippets-api/lib/public-mapping/public-map-package-build.ts +10 -0
- package/fake-snippets-api/lib/public-mapping/public-map-package-release.ts +17 -0
- package/fake-snippets-api/routes/api/orgs/add_member.ts +52 -0
- package/fake-snippets-api/routes/api/orgs/create.ts +46 -0
- package/fake-snippets-api/routes/api/orgs/get.ts +39 -0
- package/fake-snippets-api/routes/api/orgs/list.ts +31 -0
- package/fake-snippets-api/routes/api/orgs/list_members.ts +67 -0
- package/fake-snippets-api/routes/api/orgs/remove_member.ts +46 -0
- package/fake-snippets-api/routes/api/orgs/update.ts +93 -0
- package/fake-snippets-api/routes/api/package_files/get.ts +3 -6
- package/fake-snippets-api/routes/api/package_files/list.ts +7 -4
- package/fake-snippets-api/routes/api/packages/create.ts +54 -10
- package/fake-snippets-api/routes/api/packages/get.ts +23 -0
- package/fake-snippets-api/routes/api/packages/images/[owner_github_username]/[unscoped_name]/[view_format].ts +13 -11
- package/fake-snippets-api/routes/api/packages/list.ts +29 -2
- package/fake-snippets-api/routes/api/packages/update_ai_description.ts +37 -0
- package/package.json +27 -24
- package/renovate.json +1 -1
- package/scripts/generate-sitemap.ts +1 -1
- package/src/App.tsx +29 -10
- package/src/ContextProviders.tsx +25 -2
- package/src/components/CircuitJsonImportDialog.tsx +1 -1
- package/src/components/CmdKMenu.tsx +281 -247
- package/src/components/DownloadButtonAndMenu.tsx +133 -36
- package/src/components/FileSidebar.tsx +41 -50
- package/src/components/Footer.tsx +8 -10
- package/src/components/Header.tsx +19 -32
- package/src/components/Header2.tsx +16 -32
- package/src/components/HeaderDropdown.tsx +13 -8
- package/src/components/HeaderLogin.tsx +44 -16
- package/src/components/HiddenFilesDropdown.tsx +0 -2
- package/src/components/NotFound.tsx +5 -5
- package/src/components/PackageBreadcrumb.tsx +6 -12
- package/src/components/PackageCard.tsx +0 -1
- package/src/components/PackageSearchResults.tsx +1 -1
- package/src/components/PrefetchPageLink.tsx +7 -1
- package/src/components/ProfileRouter.tsx +32 -0
- package/src/components/SearchComponent.tsx +12 -8
- package/src/components/UserCard.tsx +80 -0
- package/src/components/ViewPackagePage/components/ShikiCodeViewer.tsx +20 -11
- package/src/components/ViewPackagePage/components/build-status.tsx +1 -1
- package/src/components/ViewPackagePage/components/important-files-view.tsx +174 -87
- package/src/components/ViewPackagePage/components/main-content-header.tsx +8 -4
- package/src/components/ViewPackagePage/components/main-content-view-selector.tsx +1 -2
- package/src/components/ViewPackagePage/components/mobile-sidebar.tsx +54 -20
- package/src/components/ViewPackagePage/components/package-header.tsx +26 -37
- package/src/components/ViewPackagePage/components/preview-image-squares.tsx +11 -19
- package/src/components/ViewPackagePage/components/repo-page-content.tsx +33 -25
- package/src/components/ViewPackagePage/components/sidebar-about-section.tsx +16 -10
- package/src/components/ViewPackagePage/components/sidebar-releases-section.tsx +11 -11
- package/src/components/ViewPackagePage/components/sidebar.tsx +0 -2
- package/src/components/ViewPackagePage/components/tab-views/files-view.tsx +18 -17
- package/src/components/ViewPackagePage/components/tab-views/pcb-view.tsx +1 -2
- package/src/components/ViewPackagePage/components/tab-views/schematic-view.tsx +2 -1
- package/src/components/ViewPackagePage/components/theme-toggle.tsx +0 -2
- package/src/components/ViewPackagePage/hooks/use-toast.tsx +0 -1
- package/src/components/dialogs/GitHubRepositorySelector.tsx +56 -49
- package/src/components/dialogs/edit-package-details-dialog.tsx +5 -6
- package/src/components/dialogs/import-component-dialog.tsx +16 -9
- package/src/components/dialogs/import-package-dialog.tsx +3 -2
- package/src/components/dialogs/new-package-save-prompt-dialog.tsx +190 -0
- package/src/components/organization/OrganizationCard.tsx +204 -0
- package/src/components/organization/OrganizationCardSkeleton.tsx +55 -0
- package/src/components/organization/OrganizationHeader.tsx +154 -0
- package/src/components/organization/OrganizationMembers.tsx +146 -0
- package/src/components/package-port/CodeAndPreview.tsx +32 -46
- package/src/components/package-port/CodeEditor.tsx +28 -31
- package/src/components/package-port/CodeEditorHeader.tsx +128 -63
- package/src/components/package-port/EditorNav.tsx +32 -49
- package/src/components/preview/ConnectedPackagesList.tsx +8 -8
- package/src/components/preview/ConnectedRepoOverview.tsx +102 -2
- package/src/components/preview/PackageReleasesDashboard.tsx +53 -36
- package/src/components/ui/tree-view.tsx +6 -3
- package/src/hooks/use-add-org-member-mutation.ts +51 -0
- package/src/hooks/use-create-org-mutation.ts +38 -0
- package/src/hooks/use-create-package-mutation.ts +3 -0
- package/src/hooks/use-current-package-id.ts +5 -30
- package/src/hooks/use-current-package-info.ts +29 -5
- package/src/hooks/use-current-package-release.ts +4 -3
- package/src/hooks/use-download-zip.ts +2 -2
- package/src/hooks/use-global-store.ts +6 -4
- package/src/hooks/use-jlcpcb-component-import.tsx +164 -0
- package/src/hooks/use-list-org-members.ts +27 -0
- package/src/hooks/use-list-user-orgs.ts +25 -0
- package/src/hooks/use-org-by-github-handle.ts +26 -0
- package/src/hooks/use-org.ts +24 -0
- package/src/hooks/use-organization.ts +42 -0
- package/src/hooks/use-package-as-snippet.ts +4 -2
- package/src/hooks/use-package-builds.ts +6 -2
- package/src/hooks/use-package-files.ts +5 -3
- package/src/hooks/use-package-release-by-id-or-version.ts +29 -20
- package/src/hooks/use-package-release-images.ts +105 -0
- package/src/hooks/use-package-release.ts +2 -2
- package/src/hooks/use-package-stars.ts +80 -4
- package/src/hooks/use-preview-images.ts +6 -3
- package/src/hooks/use-remove-org-member-mutation.ts +32 -0
- package/src/hooks/use-update-ai-description-mutation.ts +42 -0
- package/src/hooks/use-update-org-mutation.ts +41 -0
- package/src/hooks/use-warn-user-on-page-change.ts +71 -4
- package/src/hooks/useFileManagement.ts +183 -35
- package/src/hooks/useOptimizedPackageFilesLoader.ts +136 -0
- package/src/hooks/usePackageFilesLoader.ts +2 -2
- package/src/hooks/useUpdatePackageFilesMutation.ts +15 -1
- package/src/lib/download-fns/download-circuit-png.ts +11 -3
- package/src/lib/download-fns/download-gltf-from-circuit-json.ts +44 -0
- package/src/lib/download-fns/download-kicad-files.ts +12 -11
- package/src/lib/normalize-svg-for-tile.ts +50 -0
- package/src/lib/posthog.ts +11 -9
- package/src/lib/react-query-api-failure-tracking.ts +148 -0
- package/src/lib/sentry.ts +14 -0
- package/src/lib/templates/blank-circuit-board-template.ts +0 -4
- package/src/lib/ts-lib-cache.ts +122 -7
- package/src/lib/utils/checkIfManualEditsImported.ts +4 -4
- package/src/lib/utils/findTargetFile.ts +45 -10
- package/src/lib/utils/isComponentExported.ts +10 -0
- package/src/main.tsx +2 -1
- package/src/pages/authorize.tsx +0 -2
- package/src/pages/create-organization.tsx +168 -0
- package/src/pages/dashboard.tsx +38 -6
- package/src/pages/datasheet.tsx +1 -1
- package/src/pages/datasheets.tsx +3 -3
- package/src/pages/editor.tsx +4 -6
- package/src/pages/landing.tsx +6 -7
- package/src/pages/latest.tsx +3 -0
- package/src/pages/organization-profile.tsx +199 -0
- package/src/pages/organization-settings.tsx +566 -0
- package/src/pages/package-editor.tsx +21 -21
- package/src/pages/preview-release.tsx +76 -136
- package/src/pages/quickstart.tsx +159 -123
- package/src/pages/release-detail.tsx +119 -31
- package/src/pages/search.tsx +192 -57
- package/src/pages/settings-redirect.tsx +44 -0
- package/src/pages/trending.tsx +29 -20
- package/src/pages/user-profile.tsx +58 -7
- package/src/pages/view-package.tsx +21 -26
- package/vite.config.ts +9 -0
- package/fake-snippets-api/routes/api/autocomplete/create_autocomplete.ts +0 -133
- package/src/components/Footer2.tsx +0 -100
- package/src/components/JLCPCBImportDialog.tsx +0 -280
- package/src/components/PackageBuildsPage/LogContent.tsx +0 -72
- package/src/components/PackageBuildsPage/PackageBuildDetailsPage.tsx +0 -115
- package/src/components/PackageBuildsPage/build-preview-content.tsx +0 -27
- package/src/components/PackageBuildsPage/collapsible-section.tsx +0 -63
- package/src/components/PackageBuildsPage/package-build-details-panel.tsx +0 -166
- package/src/components/PackageBuildsPage/package-build-header.tsx +0 -79
- package/src/components/PageSearchComponent.tsx +0 -148
- package/src/components/ShippingInformationForm.tsx +0 -423
- package/src/components/StaticViewSnippetHeader.tsx +0 -70
- package/src/components/ViewPackagePage/components/file-explorer.tsx +0 -67
- package/src/components/ViewPackagePage/components/readme-view.tsx +0 -58
- package/src/components/ViewPackagePage/components/repo-header-button.tsx +0 -36
- package/src/components/ViewPackagePage/components/repo-header.tsx +0 -4
- package/src/components/ViewPackagePage/components/sidebar-contributors-section.tsx +0 -31
- package/src/components/ViewSnippetHeader.tsx +0 -181
- package/src/components/ui/input-otp.tsx +0 -69
- package/src/pages/package-builds.tsx +0 -33
- package/src/pages/settings.tsx +0 -25
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { withRouteSpec } from "fake-snippets-api/lib/middleware/with-winter-spec"
|
|
2
|
+
import { z } from "zod"
|
|
3
|
+
|
|
4
|
+
export default withRouteSpec({
|
|
5
|
+
methods: ["GET", "POST"],
|
|
6
|
+
commonParams: z.object({
|
|
7
|
+
org_id: z.string(),
|
|
8
|
+
account_id: z.string().optional(),
|
|
9
|
+
github_username: z.string().optional(),
|
|
10
|
+
}),
|
|
11
|
+
auth: "session",
|
|
12
|
+
jsonResponse: z.object({}),
|
|
13
|
+
})(async (req, ctx) => {
|
|
14
|
+
const { org_id, account_id, github_username } = req.commonParams
|
|
15
|
+
|
|
16
|
+
const org = ctx.db.getOrg({ org_id }, ctx.auth)
|
|
17
|
+
|
|
18
|
+
if (!org) {
|
|
19
|
+
return ctx.error(404, {
|
|
20
|
+
error_code: "org_not_found",
|
|
21
|
+
message: "Organization not found",
|
|
22
|
+
})
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
if (!org.can_manage_org) {
|
|
26
|
+
return ctx.error(403, {
|
|
27
|
+
error_code: "not_authorized",
|
|
28
|
+
message: "You do not have permission to manage this organization",
|
|
29
|
+
})
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
let account = ctx.db.accounts.find((acc) => acc.account_id === account_id)
|
|
33
|
+
if (!account) {
|
|
34
|
+
account = ctx.db.accounts.find(
|
|
35
|
+
(acc) => acc.github_username === github_username,
|
|
36
|
+
)
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
if (!account) {
|
|
40
|
+
return ctx.error(404, {
|
|
41
|
+
error_code: "account_not_found",
|
|
42
|
+
message: "Account not found",
|
|
43
|
+
})
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
ctx.db.addOrganizationAccount({
|
|
47
|
+
org_id,
|
|
48
|
+
account_id: account.account_id,
|
|
49
|
+
})
|
|
50
|
+
|
|
51
|
+
return ctx.json({})
|
|
52
|
+
})
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { withRouteSpec } from "fake-snippets-api/lib/middleware/with-winter-spec"
|
|
2
|
+
import { z } from "zod"
|
|
3
|
+
import { publicMapOrg } from "fake-snippets-api/lib/public-mapping/public-map-org"
|
|
4
|
+
import { publicOrgSchema } from "fake-snippets-api/lib/db/schema"
|
|
5
|
+
|
|
6
|
+
export default withRouteSpec({
|
|
7
|
+
methods: ["GET", "POST"],
|
|
8
|
+
commonParams: z.object({
|
|
9
|
+
name: z.string(),
|
|
10
|
+
}),
|
|
11
|
+
auth: "session",
|
|
12
|
+
jsonResponse: z.object({
|
|
13
|
+
org: publicOrgSchema,
|
|
14
|
+
}),
|
|
15
|
+
})(async (req, ctx) => {
|
|
16
|
+
const { name } = req.commonParams
|
|
17
|
+
|
|
18
|
+
const existing = ctx.db.getOrg({ github_handle: name })
|
|
19
|
+
|
|
20
|
+
if (existing) {
|
|
21
|
+
return ctx.error(400, {
|
|
22
|
+
error_code: "org_already_exists",
|
|
23
|
+
message: "An organization with this name already exists",
|
|
24
|
+
})
|
|
25
|
+
}
|
|
26
|
+
const newOrg = {
|
|
27
|
+
owner_account_id: ctx.auth.account_id,
|
|
28
|
+
name: name,
|
|
29
|
+
created_at: new Date(),
|
|
30
|
+
can_manage_org: true,
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
const org = ctx.db.addOrganization(newOrg)
|
|
34
|
+
|
|
35
|
+
// Add the creator as a member of the organization
|
|
36
|
+
ctx.db.addOrganizationAccount({
|
|
37
|
+
org_id: org.org_id,
|
|
38
|
+
account_id: ctx.auth.account_id,
|
|
39
|
+
is_owner: true,
|
|
40
|
+
})
|
|
41
|
+
|
|
42
|
+
const fullOrg = ctx.db.getOrg({ org_id: org.org_id }, ctx.auth)
|
|
43
|
+
return ctx.json({
|
|
44
|
+
org: publicMapOrg(fullOrg!),
|
|
45
|
+
})
|
|
46
|
+
})
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { withRouteSpec } from "fake-snippets-api/lib/middleware/with-winter-spec"
|
|
2
|
+
import { z } from "zod"
|
|
3
|
+
import { publicMapOrg } from "fake-snippets-api/lib/public-mapping/public-map-org"
|
|
4
|
+
import { publicOrgSchema } from "fake-snippets-api/lib/db/schema"
|
|
5
|
+
|
|
6
|
+
export default withRouteSpec({
|
|
7
|
+
methods: ["GET", "POST"],
|
|
8
|
+
commonParams: z
|
|
9
|
+
.object({ org_id: z.string() })
|
|
10
|
+
.or(z.object({ org_name: z.string() }))
|
|
11
|
+
.or(z.object({ github_handle: z.string() })),
|
|
12
|
+
auth: "session",
|
|
13
|
+
jsonResponse: z.object({
|
|
14
|
+
org: publicOrgSchema,
|
|
15
|
+
}),
|
|
16
|
+
})(async (req, ctx) => {
|
|
17
|
+
const params = req.commonParams as {
|
|
18
|
+
org_id?: string
|
|
19
|
+
org_name?: string
|
|
20
|
+
github_handle?: string
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
const org = ctx.db.getOrg(
|
|
24
|
+
{
|
|
25
|
+
org_id: params.org_id,
|
|
26
|
+
org_name: params.org_name,
|
|
27
|
+
github_handle: params.github_handle,
|
|
28
|
+
},
|
|
29
|
+
ctx.auth,
|
|
30
|
+
)
|
|
31
|
+
|
|
32
|
+
if (!org) {
|
|
33
|
+
return ctx.error(404, {
|
|
34
|
+
error_code: "org_not_found",
|
|
35
|
+
message: "Organization not found",
|
|
36
|
+
})
|
|
37
|
+
}
|
|
38
|
+
return ctx.json({ org: publicMapOrg(org) })
|
|
39
|
+
})
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { publicOrgSchema } from "fake-snippets-api/lib/db/schema"
|
|
2
|
+
import { withRouteSpec } from "fake-snippets-api/lib/middleware/with-winter-spec"
|
|
3
|
+
import { publicMapOrg } from "fake-snippets-api/lib/public-mapping/public-map-org"
|
|
4
|
+
import { z } from "zod"
|
|
5
|
+
|
|
6
|
+
export default withRouteSpec({
|
|
7
|
+
methods: ["GET", "POST"],
|
|
8
|
+
auth: "optional_session",
|
|
9
|
+
commonParams: z.object({
|
|
10
|
+
github_handle: z.string().optional(),
|
|
11
|
+
}),
|
|
12
|
+
jsonResponse: z.object({
|
|
13
|
+
ok: z.boolean(),
|
|
14
|
+
orgs: z.array(publicOrgSchema),
|
|
15
|
+
}),
|
|
16
|
+
})(async (req, ctx) => {
|
|
17
|
+
const { github_handle } = req.commonParams
|
|
18
|
+
const orgs = ctx.db.getOrgs(
|
|
19
|
+
{
|
|
20
|
+
owner_account_id: ctx.auth?.account_id,
|
|
21
|
+
github_handle,
|
|
22
|
+
},
|
|
23
|
+
{
|
|
24
|
+
account_id: ctx.auth?.account_id,
|
|
25
|
+
},
|
|
26
|
+
)
|
|
27
|
+
return ctx.json({
|
|
28
|
+
ok: true,
|
|
29
|
+
orgs: orgs.map((org) => publicMapOrg(org)),
|
|
30
|
+
})
|
|
31
|
+
})
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { withRouteSpec } from "fake-snippets-api/lib/middleware/with-winter-spec"
|
|
2
|
+
import { z } from "zod"
|
|
3
|
+
import { accountSchema } from "fake-snippets-api/lib/db/schema"
|
|
4
|
+
|
|
5
|
+
export default withRouteSpec({
|
|
6
|
+
methods: ["GET", "POST"],
|
|
7
|
+
commonParams: z
|
|
8
|
+
.object({
|
|
9
|
+
org_id: z.string(),
|
|
10
|
+
})
|
|
11
|
+
.or(
|
|
12
|
+
z.object({
|
|
13
|
+
name: z.string(),
|
|
14
|
+
}),
|
|
15
|
+
),
|
|
16
|
+
auth: "session",
|
|
17
|
+
jsonResponse: z.object({
|
|
18
|
+
members: z.array(accountSchema),
|
|
19
|
+
}),
|
|
20
|
+
})(async (req, ctx) => {
|
|
21
|
+
const params = req.commonParams as { org_id?: string; name?: string }
|
|
22
|
+
|
|
23
|
+
const org = ctx.db.getOrg(
|
|
24
|
+
{
|
|
25
|
+
org_id: params.org_id,
|
|
26
|
+
org_name: params.name,
|
|
27
|
+
},
|
|
28
|
+
ctx.auth,
|
|
29
|
+
)
|
|
30
|
+
|
|
31
|
+
if (!org) {
|
|
32
|
+
return ctx.error(404, {
|
|
33
|
+
error_code: "org_not_found",
|
|
34
|
+
message: "Organization not found",
|
|
35
|
+
})
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
if (!org.can_manage_org) {
|
|
39
|
+
return ctx.error(403, {
|
|
40
|
+
error_code: "not_authorized",
|
|
41
|
+
message: "You do not have permission to manage this organization",
|
|
42
|
+
})
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
const members = ctx.db.orgAccounts
|
|
46
|
+
.map((m) => {
|
|
47
|
+
if (m.org_id == org.org_id) return ctx.db.getAccount(m.account_id)
|
|
48
|
+
return undefined
|
|
49
|
+
})
|
|
50
|
+
.filter(
|
|
51
|
+
(member): member is NonNullable<typeof member> => member !== undefined,
|
|
52
|
+
)
|
|
53
|
+
|
|
54
|
+
const hasOwner = members.some((m) => m?.account_id === org.owner_account_id)
|
|
55
|
+
let fullMembers = members
|
|
56
|
+
|
|
57
|
+
if (!hasOwner) {
|
|
58
|
+
const owner = ctx.db.accounts.find(
|
|
59
|
+
(acc) => acc.account_id === org.owner_account_id,
|
|
60
|
+
)
|
|
61
|
+
if (owner) {
|
|
62
|
+
fullMembers = [...members, owner]
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
return ctx.json({ members: fullMembers })
|
|
67
|
+
})
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { withRouteSpec } from "fake-snippets-api/lib/middleware/with-winter-spec"
|
|
2
|
+
import { z } from "zod"
|
|
3
|
+
|
|
4
|
+
export default withRouteSpec({
|
|
5
|
+
methods: ["GET", "POST"],
|
|
6
|
+
commonParams: z.object({
|
|
7
|
+
org_id: z.string(),
|
|
8
|
+
account_id: z.string(),
|
|
9
|
+
}),
|
|
10
|
+
auth: "session",
|
|
11
|
+
jsonResponse: z.object({}),
|
|
12
|
+
})(async (req, ctx) => {
|
|
13
|
+
const { org_id, account_id } = req.commonParams
|
|
14
|
+
|
|
15
|
+
const org = ctx.db.getOrg({ org_id }, ctx.auth)
|
|
16
|
+
|
|
17
|
+
if (!org) {
|
|
18
|
+
return ctx.error(404, {
|
|
19
|
+
error_code: "org_not_found",
|
|
20
|
+
message: "Organization not found",
|
|
21
|
+
})
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
if (!org.can_manage_org) {
|
|
25
|
+
return ctx.error(403, {
|
|
26
|
+
error_code: "not_authorized",
|
|
27
|
+
message: "You do not have permission to manage this organization",
|
|
28
|
+
})
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
const account = ctx.db.accounts.find((acc) => acc.account_id === account_id)
|
|
32
|
+
|
|
33
|
+
if (!account) {
|
|
34
|
+
return ctx.error(404, {
|
|
35
|
+
error_code: "account_not_found",
|
|
36
|
+
message: "Account not found",
|
|
37
|
+
})
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
ctx.db.removeOrganizationAccount({
|
|
41
|
+
org_id,
|
|
42
|
+
account_id,
|
|
43
|
+
})
|
|
44
|
+
|
|
45
|
+
return ctx.json({})
|
|
46
|
+
})
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import { withRouteSpec } from "fake-snippets-api/lib/middleware/with-winter-spec"
|
|
2
|
+
import { z } from "zod"
|
|
3
|
+
import { publicOrgSchema } from "fake-snippets-api/lib/db/schema"
|
|
4
|
+
import { publicMapOrg } from "fake-snippets-api/lib/public-mapping/public-map-org"
|
|
5
|
+
|
|
6
|
+
export default withRouteSpec({
|
|
7
|
+
methods: ["POST", "PATCH"],
|
|
8
|
+
commonParams: z
|
|
9
|
+
.object({
|
|
10
|
+
org_id: z.string(),
|
|
11
|
+
})
|
|
12
|
+
.and(
|
|
13
|
+
z.object({
|
|
14
|
+
name: z.string().optional(),
|
|
15
|
+
display_name: z.string().optional(),
|
|
16
|
+
}),
|
|
17
|
+
),
|
|
18
|
+
auth: "session",
|
|
19
|
+
jsonResponse: z.object({
|
|
20
|
+
org: publicOrgSchema,
|
|
21
|
+
}),
|
|
22
|
+
})(async (req, ctx) => {
|
|
23
|
+
const { org_id, name, display_name } = req.commonParams as {
|
|
24
|
+
org_id: string
|
|
25
|
+
name?: string
|
|
26
|
+
display_name?: string
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
const org = ctx.db.getOrg({ org_id }, ctx.auth)
|
|
30
|
+
|
|
31
|
+
if (!org) {
|
|
32
|
+
return ctx.error(404, {
|
|
33
|
+
error_code: "org_not_found",
|
|
34
|
+
message: "Organization not found",
|
|
35
|
+
})
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
if (!org.can_manage_org) {
|
|
39
|
+
return ctx.error(403, {
|
|
40
|
+
error_code: "not_authorized",
|
|
41
|
+
message: "You do not have permission to manage this organization",
|
|
42
|
+
})
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// No changes provided
|
|
46
|
+
if (!name && display_name === undefined) {
|
|
47
|
+
return ctx.json({ org: publicMapOrg(org) })
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
if (name && name !== org.github_handle) {
|
|
51
|
+
// Validate duplicate name
|
|
52
|
+
const duplicate = ctx.db.getOrg({ github_handle: name })
|
|
53
|
+
|
|
54
|
+
if (duplicate && duplicate.org_id !== org_id) {
|
|
55
|
+
return ctx.error(400, {
|
|
56
|
+
error_code: "org_already_exists",
|
|
57
|
+
message: "An organization with this name already exists",
|
|
58
|
+
})
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
const updates: {
|
|
63
|
+
github_handle?: string
|
|
64
|
+
org_display_name?: string
|
|
65
|
+
} = {}
|
|
66
|
+
|
|
67
|
+
if (name) {
|
|
68
|
+
updates.github_handle = name
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
if (display_name !== undefined) {
|
|
72
|
+
const trimmedDisplayName = display_name.trim()
|
|
73
|
+
const fallbackDisplayName =
|
|
74
|
+
name ?? org.org_display_name ?? org.github_handle ?? ""
|
|
75
|
+
updates.org_display_name =
|
|
76
|
+
trimmedDisplayName.length > 0 ? trimmedDisplayName : fallbackDisplayName
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
const updated = ctx.db.updateOrganization(org_id, updates)
|
|
80
|
+
|
|
81
|
+
if (!updated) {
|
|
82
|
+
return ctx.error(500, {
|
|
83
|
+
error_code: "update_failed",
|
|
84
|
+
message: "Failed to update organization",
|
|
85
|
+
})
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
const updatedOrgWithPermissions = ctx.db.getOrg({ org_id }, ctx.auth)
|
|
89
|
+
|
|
90
|
+
return ctx.json({
|
|
91
|
+
org: publicMapOrg(updatedOrgWithPermissions!),
|
|
92
|
+
})
|
|
93
|
+
})
|
|
@@ -4,9 +4,9 @@ import * as ZT from "fake-snippets-api/lib/db/schema"
|
|
|
4
4
|
import { getPackageFileIdFromFileDescriptor } from "fake-snippets-api/lib/package_file/get-package-file-id-from-file-descriptor"
|
|
5
5
|
|
|
6
6
|
const routeSpec = {
|
|
7
|
-
methods: ["
|
|
7
|
+
methods: ["GET"],
|
|
8
8
|
auth: "none",
|
|
9
|
-
|
|
9
|
+
queryParams: z
|
|
10
10
|
.object({
|
|
11
11
|
package_file_id: z.string(),
|
|
12
12
|
})
|
|
@@ -45,10 +45,7 @@ const routeSpec = {
|
|
|
45
45
|
} as const
|
|
46
46
|
|
|
47
47
|
export default withRouteSpec(routeSpec)(async (req, ctx) => {
|
|
48
|
-
const packageFileId = await getPackageFileIdFromFileDescriptor(
|
|
49
|
-
req.jsonBody,
|
|
50
|
-
ctx,
|
|
51
|
-
)
|
|
48
|
+
const packageFileId = await getPackageFileIdFromFileDescriptor(req.query, ctx)
|
|
52
49
|
|
|
53
50
|
const packageFile = ctx.db.packageFiles.find(
|
|
54
51
|
(pf: ZT.PackageFile) => pf.package_file_id === packageFileId,
|
|
@@ -4,16 +4,19 @@ import * as ZT from "fake-snippets-api/lib/db/schema"
|
|
|
4
4
|
import { findPackageReleaseId } from "fake-snippets-api/lib/package_release/find-package-release-id"
|
|
5
5
|
|
|
6
6
|
const routeSpec = {
|
|
7
|
-
methods: ["
|
|
7
|
+
methods: ["GET"],
|
|
8
8
|
auth: "none",
|
|
9
|
-
|
|
9
|
+
queryParams: z
|
|
10
10
|
.object({
|
|
11
11
|
package_release_id: z.string(),
|
|
12
12
|
})
|
|
13
13
|
.or(
|
|
14
14
|
z.object({
|
|
15
15
|
package_name: z.string(),
|
|
16
|
-
use_latest_version: z.
|
|
16
|
+
use_latest_version: z.preprocess(
|
|
17
|
+
(val) => (val === "true" ? true : val),
|
|
18
|
+
z.literal(true),
|
|
19
|
+
),
|
|
17
20
|
}),
|
|
18
21
|
)
|
|
19
22
|
.or(
|
|
@@ -28,7 +31,7 @@ const routeSpec = {
|
|
|
28
31
|
} as const
|
|
29
32
|
|
|
30
33
|
export default withRouteSpec(routeSpec)(async (req, ctx) => {
|
|
31
|
-
const packageReleaseId = await findPackageReleaseId(req.
|
|
34
|
+
const packageReleaseId = await findPackageReleaseId(req.query, ctx)
|
|
32
35
|
|
|
33
36
|
if (!packageReleaseId) {
|
|
34
37
|
return ctx.error(404, {
|
|
@@ -25,16 +25,17 @@ export default withRouteSpec({
|
|
|
25
25
|
})(async (req, ctx) => {
|
|
26
26
|
const { name, description, is_private, is_unlisted } = req.jsonBody
|
|
27
27
|
|
|
28
|
-
|
|
28
|
+
let owner_segment = name?.split("/")[0]
|
|
29
|
+
let unscoped_name = name?.split("/")[1]
|
|
29
30
|
|
|
30
|
-
if (
|
|
31
|
+
if (name && !unscoped_name) {
|
|
31
32
|
throw ctx.error(400, {
|
|
32
|
-
error_code: "
|
|
33
|
-
message:
|
|
33
|
+
error_code: "invalid_package_name",
|
|
34
|
+
message:
|
|
35
|
+
"Package name must include an author segment (e.g. author/package_name)",
|
|
34
36
|
})
|
|
35
37
|
}
|
|
36
38
|
|
|
37
|
-
let unscoped_name = name?.includes("/") ? name?.split("/")[1] : name
|
|
38
39
|
if (!unscoped_name) {
|
|
39
40
|
const state = ctx.db.getState()
|
|
40
41
|
const count = state.packages.filter(
|
|
@@ -44,14 +45,57 @@ export default withRouteSpec({
|
|
|
44
45
|
unscoped_name = `untitled-package-${count}`
|
|
45
46
|
}
|
|
46
47
|
|
|
48
|
+
if (!owner_segment) {
|
|
49
|
+
owner_segment = ctx.auth.github_username
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
const final_name = name ?? `${owner_segment}/${unscoped_name}`
|
|
53
|
+
|
|
54
|
+
const requested_owner_lower = owner_segment.toLowerCase()
|
|
55
|
+
const personal_owner_lower = ctx.auth.github_username.toLowerCase()
|
|
56
|
+
|
|
57
|
+
let owner_org_id = ctx.auth.personal_org_id
|
|
58
|
+
let owner_github_username = ctx.auth.github_username
|
|
59
|
+
|
|
60
|
+
if (requested_owner_lower !== personal_owner_lower) {
|
|
61
|
+
const state = ctx.db.getState()
|
|
62
|
+
const memberOrg = state.orgAccounts
|
|
63
|
+
.filter((oa) => oa.account_id === ctx.auth.account_id)
|
|
64
|
+
.map((oa) => state.organizations.find((o) => o.org_id === oa.org_id))
|
|
65
|
+
.filter((o): o is NonNullable<typeof o> => o !== undefined)
|
|
66
|
+
.find(
|
|
67
|
+
(o) =>
|
|
68
|
+
o.org_display_name?.toLowerCase() === requested_owner_lower ||
|
|
69
|
+
o.github_handle?.toLowerCase() === requested_owner_lower,
|
|
70
|
+
)
|
|
71
|
+
|
|
72
|
+
if (!memberOrg) {
|
|
73
|
+
throw ctx.error(403, {
|
|
74
|
+
error_code: "forbidden",
|
|
75
|
+
message:
|
|
76
|
+
"You must be a member of the organization to create a package under it",
|
|
77
|
+
})
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
owner_org_id = memberOrg.org_id
|
|
81
|
+
owner_github_username = memberOrg.github_handle
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
const existingPackage = ctx.db.packages.find((pkg) => pkg.name === final_name)
|
|
85
|
+
|
|
86
|
+
if (existingPackage) {
|
|
87
|
+
throw ctx.error(400, {
|
|
88
|
+
error_code: "package_already_exists",
|
|
89
|
+
message: "A package with this name already exists",
|
|
90
|
+
})
|
|
91
|
+
}
|
|
92
|
+
|
|
47
93
|
const newPackage = ctx.db.addPackage({
|
|
48
|
-
name:
|
|
49
|
-
? name
|
|
50
|
-
: `${ctx.auth.github_username}/${String(unscoped_name)}`,
|
|
94
|
+
name: final_name,
|
|
51
95
|
description: description ?? null,
|
|
52
96
|
creator_account_id: ctx.auth.account_id,
|
|
53
|
-
owner_org_id
|
|
54
|
-
owner_github_username
|
|
97
|
+
owner_org_id,
|
|
98
|
+
owner_github_username,
|
|
55
99
|
latest_package_release_id: null,
|
|
56
100
|
latest_package_release_fs_sha: null,
|
|
57
101
|
latest_version: null,
|
|
@@ -16,6 +16,11 @@ export default withRouteSpec({
|
|
|
16
16
|
package: packageSchema
|
|
17
17
|
.extend({
|
|
18
18
|
is_starred: z.boolean(),
|
|
19
|
+
user_permissions: z
|
|
20
|
+
.object({
|
|
21
|
+
can_manage_packages: z.boolean(),
|
|
22
|
+
})
|
|
23
|
+
.optional(),
|
|
19
24
|
})
|
|
20
25
|
.optional(),
|
|
21
26
|
}),
|
|
@@ -46,6 +51,17 @@ export default withRouteSpec({
|
|
|
46
51
|
})
|
|
47
52
|
}
|
|
48
53
|
|
|
54
|
+
// Check if user can manage the package
|
|
55
|
+
const canManagePackage =
|
|
56
|
+
auth &&
|
|
57
|
+
ctx.db
|
|
58
|
+
.getState()
|
|
59
|
+
.orgAccounts.some(
|
|
60
|
+
(org_account) =>
|
|
61
|
+
org_account.account_id === auth.account_id &&
|
|
62
|
+
org_account.org_id === foundPackage.owner_org_id,
|
|
63
|
+
)
|
|
64
|
+
|
|
49
65
|
return ctx.json({
|
|
50
66
|
ok: true,
|
|
51
67
|
package: {
|
|
@@ -53,6 +69,13 @@ export default withRouteSpec({
|
|
|
53
69
|
is_starred: auth
|
|
54
70
|
? ctx.db.hasStarred(auth.account_id, foundPackage.package_id)
|
|
55
71
|
: false,
|
|
72
|
+
...(auth
|
|
73
|
+
? {
|
|
74
|
+
user_permissions: {
|
|
75
|
+
can_manage_packages: canManagePackage ?? false,
|
|
76
|
+
},
|
|
77
|
+
}
|
|
78
|
+
: {}),
|
|
56
79
|
},
|
|
57
80
|
})
|
|
58
81
|
})
|
|
@@ -4,8 +4,10 @@ import {
|
|
|
4
4
|
convertCircuitJsonToPcbSvg,
|
|
5
5
|
convertCircuitJsonToSchematicSvg,
|
|
6
6
|
} from "circuit-to-svg"
|
|
7
|
+
import { convertCircuitJsonToSimple3dSvg } from "circuit-json-to-simple-3d"
|
|
7
8
|
import { withRouteSpec } from "fake-snippets-api/lib/middleware/with-winter-spec"
|
|
8
9
|
import { z } from "zod"
|
|
10
|
+
import { renderAsync } from "@resvg/resvg-js"
|
|
9
11
|
|
|
10
12
|
// Define the view types and extensions
|
|
11
13
|
const VIEW_TYPES = ["schematic", "pcb", "assembly", "3d"] as const
|
|
@@ -68,7 +70,8 @@ export default withRouteSpec({
|
|
|
68
70
|
const circuit_json_file = ctx.db.packageFiles.find(
|
|
69
71
|
(pf) =>
|
|
70
72
|
pf.package_release_id === pkg_release.package_release_id &&
|
|
71
|
-
pf.file_path === "circuit.json"
|
|
73
|
+
(pf.file_path === "circuit.json" ||
|
|
74
|
+
pf.file_path === "/dist/circuit.json"),
|
|
72
75
|
)
|
|
73
76
|
|
|
74
77
|
if (!circuit_json_file?.content_text) {
|
|
@@ -82,7 +85,6 @@ export default withRouteSpec({
|
|
|
82
85
|
|
|
83
86
|
// Convert circuit json to svg
|
|
84
87
|
let svg = ""
|
|
85
|
-
|
|
86
88
|
if (outputType === "schematic") {
|
|
87
89
|
svg = convertCircuitJsonToSchematicSvg(circuit_json as AnyCircuitElement[])
|
|
88
90
|
} else if (outputType === "pcb") {
|
|
@@ -90,13 +92,14 @@ export default withRouteSpec({
|
|
|
90
92
|
} else if (outputType === "assembly") {
|
|
91
93
|
svg = convertCircuitJsonToAssemblySvg(circuit_json as AnyCircuitElement[])
|
|
92
94
|
} else if (outputType === "3d") {
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
95
|
+
svg = await convertCircuitJsonToSimple3dSvg(circuit_json, {
|
|
96
|
+
background: {
|
|
97
|
+
color: "#fff",
|
|
98
|
+
opacity: 0.0,
|
|
99
|
+
},
|
|
100
|
+
defaultZoomMultiplier: 1.1,
|
|
101
|
+
})
|
|
98
102
|
}
|
|
99
|
-
|
|
100
103
|
if (format === "svg") {
|
|
101
104
|
return new Response(svg, {
|
|
102
105
|
headers: {
|
|
@@ -106,9 +109,8 @@ export default withRouteSpec({
|
|
|
106
109
|
})
|
|
107
110
|
}
|
|
108
111
|
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
return new Response(svg, {
|
|
112
|
+
const pngBuffer = await renderAsync(svg)
|
|
113
|
+
return new Response(pngBuffer.asPng().buffer as ArrayBuffer, {
|
|
112
114
|
headers: {
|
|
113
115
|
"Content-Type": "image/png",
|
|
114
116
|
"Cache-Control": "public, max-age=86400, s-maxage=86400",
|
|
@@ -13,7 +13,16 @@ export default withRouteSpec({
|
|
|
13
13
|
}),
|
|
14
14
|
jsonResponse: z.object({
|
|
15
15
|
ok: z.boolean(),
|
|
16
|
-
packages: z.array(
|
|
16
|
+
packages: z.array(
|
|
17
|
+
packageSchema.extend({
|
|
18
|
+
starred_at: z.string().nullable(),
|
|
19
|
+
user_permissions: z
|
|
20
|
+
.object({
|
|
21
|
+
can_manage_packages: z.boolean(),
|
|
22
|
+
})
|
|
23
|
+
.optional(),
|
|
24
|
+
}),
|
|
25
|
+
),
|
|
17
26
|
}),
|
|
18
27
|
})(async (req, ctx) => {
|
|
19
28
|
const { creator_account_id, owner_github_username, name, is_writable } =
|
|
@@ -28,6 +37,17 @@ export default withRouteSpec({
|
|
|
28
37
|
})
|
|
29
38
|
}
|
|
30
39
|
|
|
40
|
+
// Helper to check if user can manage a package
|
|
41
|
+
const canManagePackage = (pkg: any) => {
|
|
42
|
+
if (!auth) return false
|
|
43
|
+
// Check if user is a member of the package's owner org
|
|
44
|
+
const state = ctx.db.getState()
|
|
45
|
+
return state.orgAccounts.some(
|
|
46
|
+
(oa) =>
|
|
47
|
+
oa.account_id === auth.account_id && oa.org_id === pkg.owner_org_id,
|
|
48
|
+
)
|
|
49
|
+
}
|
|
50
|
+
|
|
31
51
|
let packages = ctx.db.packages
|
|
32
52
|
|
|
33
53
|
// Apply filters
|
|
@@ -46,7 +66,7 @@ export default withRouteSpec({
|
|
|
46
66
|
}
|
|
47
67
|
|
|
48
68
|
if (is_writable && auth) {
|
|
49
|
-
packages = packages.filter(
|
|
69
|
+
packages = packages.filter(canManagePackage)
|
|
50
70
|
}
|
|
51
71
|
|
|
52
72
|
// Get star timestamps for authenticated user
|
|
@@ -65,6 +85,13 @@ export default withRouteSpec({
|
|
|
65
85
|
...p,
|
|
66
86
|
latest_package_release_id: p.latest_package_release_id || null,
|
|
67
87
|
starred_at: starTimestamps.get(p.package_id) || null,
|
|
88
|
+
...(auth
|
|
89
|
+
? {
|
|
90
|
+
user_permissions: {
|
|
91
|
+
can_manage_packages: canManagePackage(p),
|
|
92
|
+
},
|
|
93
|
+
}
|
|
94
|
+
: {}),
|
|
68
95
|
})),
|
|
69
96
|
})
|
|
70
97
|
})
|