@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.
- 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 +32 -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 +151 -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 +361 -453
- package/bunfig.toml +2 -1
- package/dist/bundle.js +1313 -639
- package/dist/index.d.ts +313 -6
- package/dist/index.js +328 -24
- package/dist/schema.d.ts +290 -1
- package/dist/schema.js +54 -1
- package/fake-snippets-api/lib/db/autoload-dev-packages.ts +31 -20
- package/fake-snippets-api/lib/db/db-client.ts +219 -4
- package/fake-snippets-api/lib/db/schema.ts +63 -1
- package/fake-snippets-api/lib/db/seed.ts +100 -0
- package/fake-snippets-api/lib/middleware/with-session-auth.ts +60 -8
- 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 +33 -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 +48 -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 +60 -0
- package/fake-snippets-api/routes/api/orgs/remove_member.ts +46 -0
- package/fake-snippets-api/routes/api/orgs/update.ts +118 -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 +57 -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 +25 -19
- package/renovate.json +1 -1
- package/scripts/generate-sitemap.ts +1 -1
- package/src/App.tsx +27 -8
- 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 +17 -5
- package/src/components/FileSidebar.tsx +11 -17
- package/src/components/Footer.tsx +8 -9
- 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 +43 -15
- package/src/components/NotFound.tsx +5 -5
- package/src/components/PackageBreadcrumb.tsx +6 -12
- 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/SentryNotFoundReporter.tsx +44 -0
- package/src/components/UserCard.tsx +80 -0
- package/src/components/ViewPackagePage/components/build-status.tsx +1 -1
- package/src/components/ViewPackagePage/components/important-files-view.tsx +105 -34
- package/src/components/ViewPackagePage/components/main-content-header.tsx +10 -6
- package/src/components/ViewPackagePage/components/main-content-view-selector.tsx +1 -1
- package/src/components/ViewPackagePage/components/mobile-sidebar.tsx +54 -19
- package/src/components/ViewPackagePage/components/package-header.tsx +25 -33
- package/src/components/ViewPackagePage/components/preview-image-squares.tsx +11 -18
- package/src/components/ViewPackagePage/components/repo-page-content.tsx +12 -5
- 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/tab-views/pcb-view.tsx +1 -2
- package/src/components/ViewPackagePage/components/tab-views/schematic-view.tsx +2 -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 +206 -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 +15 -12
- package/src/components/package-port/CodeEditor.tsx +4 -30
- package/src/components/package-port/CodeEditorHeader.tsx +123 -61
- 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 +23 -11
- 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-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-hydration.ts +30 -0
- 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 +51 -22
- package/src/hooks/useOptimizedPackageFilesLoader.ts +11 -24
- package/src/hooks/usePackageFilesLoader.ts +2 -2
- package/src/hooks/useUpdatePackageFilesMutation.ts +13 -1
- package/src/lib/download-fns/download-gltf-from-circuit-json.ts +1 -1
- package/src/lib/download-fns/download-kicad-files.ts +22 -11
- package/src/lib/download-fns/download-step.ts +12 -0
- 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 +2 -1
- package/src/main.tsx +2 -1
- package/src/pages/create-organization.tsx +169 -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 -6
- package/src/pages/latest.tsx +3 -0
- package/src/pages/organization-profile.tsx +199 -0
- package/src/pages/organization-settings.tsx +569 -0
- package/src/pages/package-editor.tsx +21 -21
- package/src/pages/preview-release.tsx +75 -145
- package/src/pages/quickstart.tsx +159 -123
- package/src/pages/release-detail.tsx +119 -31
- package/src/pages/search.tsx +197 -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/user-settings.tsx +161 -0
- package/src/pages/view-package.tsx +30 -16
- package/vite.config.ts +9 -0
- package/fake-snippets-api/routes/api/autocomplete/create_autocomplete.ts +0 -133
- package/src/components/JLCPCBImportDialog.tsx +0 -280
- package/src/components/PackageBuildsPage/LogContent.tsx +0 -72
- package/src/components/PackageBuildsPage/PackageBuildDetailsPage.tsx +0 -113
- package/src/components/PackageBuildsPage/build-preview-content.tsx +0 -56
- 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/pages/package-builds.tsx +0 -33
|
@@ -125,8 +125,8 @@ export const getPackageFileIdFromFileDescriptor = async (
|
|
|
125
125
|
|
|
126
126
|
if ("package_name_with_version" in descriptor) {
|
|
127
127
|
const { package_name_with_version, file_path } = descriptor
|
|
128
|
-
const packageName = package_name_with_version.split("@")[
|
|
129
|
-
const version = package_name_with_version.split("@")[
|
|
128
|
+
const packageName = package_name_with_version.split("@")[0]
|
|
129
|
+
const version = package_name_with_version.split("@")[1]
|
|
130
130
|
|
|
131
131
|
// Find the package
|
|
132
132
|
const pkg = ctx.db.packages.find((p: ZT.Package) => p.name === packageName)
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { z } from "zod"
|
|
2
|
+
import * as ZT from "fake-snippets-api/lib/db/schema"
|
|
3
|
+
|
|
4
|
+
export const publicMapOrg = (
|
|
5
|
+
internal_org: ZT.Organization & {
|
|
6
|
+
member_count: number
|
|
7
|
+
package_count: number
|
|
8
|
+
can_manage_org: boolean
|
|
9
|
+
},
|
|
10
|
+
): z.infer<typeof ZT.publicOrgSchema> => {
|
|
11
|
+
const {
|
|
12
|
+
can_manage_org,
|
|
13
|
+
github_handle,
|
|
14
|
+
member_count,
|
|
15
|
+
package_count,
|
|
16
|
+
created_at,
|
|
17
|
+
is_personal_org,
|
|
18
|
+
org_display_name,
|
|
19
|
+
org_name,
|
|
20
|
+
...org
|
|
21
|
+
} = internal_org
|
|
22
|
+
return {
|
|
23
|
+
org_id: org.org_id,
|
|
24
|
+
display_name: org_display_name ?? org_name,
|
|
25
|
+
owner_account_id: org.owner_account_id,
|
|
26
|
+
name: org_name,
|
|
27
|
+
member_count: Number(member_count) || 0,
|
|
28
|
+
package_count: Number(package_count) || 0,
|
|
29
|
+
is_personal_org: Boolean(is_personal_org),
|
|
30
|
+
created_at: String(created_at),
|
|
31
|
+
...(can_manage_org ? { user_permissions: { can_manage_org: true } } : {}),
|
|
32
|
+
}
|
|
33
|
+
}
|
|
@@ -25,6 +25,16 @@ export const publicMapPackageBuild = (
|
|
|
25
25
|
? internalPackageBuild.circuit_json_build_logs
|
|
26
26
|
: [],
|
|
27
27
|
circuit_json_build_error: internalPackageBuild.circuit_json_build_error,
|
|
28
|
+
image_generation_in_progress:
|
|
29
|
+
internalPackageBuild.image_generation_in_progress,
|
|
30
|
+
image_generation_started_at:
|
|
31
|
+
internalPackageBuild.image_generation_started_at,
|
|
32
|
+
image_generation_completed_at:
|
|
33
|
+
internalPackageBuild.image_generation_completed_at,
|
|
34
|
+
image_generation_logs: options.include_logs
|
|
35
|
+
? internalPackageBuild.image_generation_logs
|
|
36
|
+
: [],
|
|
37
|
+
image_generation_error: internalPackageBuild.image_generation_error,
|
|
28
38
|
build_started_at: internalPackageBuild.build_started_at,
|
|
29
39
|
build_completed_at: internalPackageBuild.build_completed_at,
|
|
30
40
|
build_error: internalPackageBuild.build_error,
|
|
@@ -24,6 +24,23 @@ export const publicMapPackageRelease = (
|
|
|
24
24
|
circuit_json_build_logs: options.include_logs
|
|
25
25
|
? internal_package_release.circuit_json_build_logs
|
|
26
26
|
: [],
|
|
27
|
+
image_generation_logs: options.include_logs
|
|
28
|
+
? internal_package_release.image_generation_logs
|
|
29
|
+
: null,
|
|
30
|
+
image_generation_in_progress:
|
|
31
|
+
internal_package_release.image_generation_in_progress,
|
|
32
|
+
image_generation_started_at:
|
|
33
|
+
internal_package_release.image_generation_started_at ?? null,
|
|
34
|
+
image_generation_completed_at:
|
|
35
|
+
internal_package_release.image_generation_completed_at ?? null,
|
|
36
|
+
image_generation_is_stale:
|
|
37
|
+
internal_package_release.image_generation_is_stale,
|
|
38
|
+
image_generation_error:
|
|
39
|
+
internal_package_release.image_generation_error ?? null,
|
|
40
|
+
image_generation_error_last_updated_at:
|
|
41
|
+
internal_package_release.image_generation_error_last_updated_at ?? null,
|
|
42
|
+
image_generation_display_status:
|
|
43
|
+
internal_package_release.image_generation_display_status,
|
|
27
44
|
is_pr_preview: Boolean(internal_package_release.is_pr_preview),
|
|
28
45
|
github_pr_number: internal_package_release.github_pr_number,
|
|
29
46
|
branch_name: internal_package_release.branch_name,
|
|
@@ -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,48 @@
|
|
|
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
|
+
github_handle: z.string().optional(),
|
|
11
|
+
}),
|
|
12
|
+
auth: "session",
|
|
13
|
+
jsonResponse: z.object({
|
|
14
|
+
org: publicOrgSchema,
|
|
15
|
+
}),
|
|
16
|
+
})(async (req, ctx) => {
|
|
17
|
+
const { name, github_handle } = req.commonParams
|
|
18
|
+
|
|
19
|
+
const existing = ctx.db.getOrg({ org_name: name })
|
|
20
|
+
|
|
21
|
+
if (existing) {
|
|
22
|
+
return ctx.error(400, {
|
|
23
|
+
error_code: "org_already_exists",
|
|
24
|
+
message: "An organization with this name already exists",
|
|
25
|
+
})
|
|
26
|
+
}
|
|
27
|
+
const newOrg = {
|
|
28
|
+
owner_account_id: ctx.auth.account_id,
|
|
29
|
+
name: name,
|
|
30
|
+
created_at: new Date(),
|
|
31
|
+
can_manage_org: true,
|
|
32
|
+
...(github_handle ? { github_handle } : {}),
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
const org = ctx.db.addOrganization(newOrg)
|
|
36
|
+
|
|
37
|
+
// Add the creator as a member of the organization
|
|
38
|
+
ctx.db.addOrganizationAccount({
|
|
39
|
+
org_id: org.org_id,
|
|
40
|
+
account_id: ctx.auth.account_id,
|
|
41
|
+
is_owner: true,
|
|
42
|
+
})
|
|
43
|
+
|
|
44
|
+
const fullOrg = ctx.db.getOrg({ org_id: org.org_id }, ctx.auth)
|
|
45
|
+
return ctx.json({
|
|
46
|
+
org: publicMapOrg(fullOrg!),
|
|
47
|
+
})
|
|
48
|
+
})
|
|
@@ -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: "optional_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,60 @@
|
|
|
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: "optional_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
|
+
const members = ctx.db.orgAccounts
|
|
39
|
+
.map((m) => {
|
|
40
|
+
if (m.org_id == org.org_id) return ctx.db.getAccount(m.account_id)
|
|
41
|
+
return undefined
|
|
42
|
+
})
|
|
43
|
+
.filter(
|
|
44
|
+
(member): member is NonNullable<typeof member> => member !== undefined,
|
|
45
|
+
)
|
|
46
|
+
|
|
47
|
+
const hasOwner = members.some((m) => m?.account_id === org.owner_account_id)
|
|
48
|
+
let fullMembers = members
|
|
49
|
+
|
|
50
|
+
if (!hasOwner) {
|
|
51
|
+
const owner = ctx.db.accounts.find(
|
|
52
|
+
(acc) => acc.account_id === org.owner_account_id,
|
|
53
|
+
)
|
|
54
|
+
if (owner) {
|
|
55
|
+
fullMembers = [...members, owner]
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
return ctx.json({ members: fullMembers })
|
|
60
|
+
})
|
|
@@ -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,118 @@
|
|
|
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
|
+
github_handle: z.string().trim().min(1).nullable().optional(),
|
|
17
|
+
}),
|
|
18
|
+
),
|
|
19
|
+
auth: "session",
|
|
20
|
+
jsonResponse: z.object({
|
|
21
|
+
org: publicOrgSchema,
|
|
22
|
+
}),
|
|
23
|
+
})(async (req, ctx) => {
|
|
24
|
+
const { org_id, name, display_name, github_handle } = req.commonParams as {
|
|
25
|
+
org_id: string
|
|
26
|
+
name?: string
|
|
27
|
+
display_name?: string
|
|
28
|
+
github_handle?: string
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
const org = ctx.db.getOrg({ org_id }, ctx.auth)
|
|
32
|
+
|
|
33
|
+
if (!org) {
|
|
34
|
+
return ctx.error(404, {
|
|
35
|
+
error_code: "org_not_found",
|
|
36
|
+
message: "Organization not found",
|
|
37
|
+
})
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
if (!org.can_manage_org) {
|
|
41
|
+
return ctx.error(403, {
|
|
42
|
+
error_code: "not_authorized",
|
|
43
|
+
message: "You do not have permission to manage this organization",
|
|
44
|
+
})
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// No changes provided
|
|
48
|
+
if (!name && display_name === undefined && github_handle === null) {
|
|
49
|
+
return ctx.json({ org: publicMapOrg(org) })
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
if (name && name !== org.org_name) {
|
|
53
|
+
// Validate duplicate name
|
|
54
|
+
const duplicate = ctx.db.getOrg({ org_name: name })
|
|
55
|
+
|
|
56
|
+
if (duplicate && duplicate.org_id !== org_id) {
|
|
57
|
+
return ctx.error(400, {
|
|
58
|
+
error_code: "org_already_exists",
|
|
59
|
+
message: "An organization with this name already exists",
|
|
60
|
+
})
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
if (
|
|
64
|
+
github_handle !== undefined &&
|
|
65
|
+
github_handle !== org.github_handle &&
|
|
66
|
+
github_handle !== null
|
|
67
|
+
) {
|
|
68
|
+
const duplicateHandle = ctx.db.getOrg({ github_handle })
|
|
69
|
+
? ctx.db.getOrg({ github_handle })?.org_id != org_id
|
|
70
|
+
: false
|
|
71
|
+
|
|
72
|
+
if (duplicateHandle) {
|
|
73
|
+
return ctx.error(400, {
|
|
74
|
+
error_code: "org_github_handle_already_exists",
|
|
75
|
+
message: "An organization with this GitHub handle already exists",
|
|
76
|
+
})
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
const updates: {
|
|
80
|
+
org_name?: string
|
|
81
|
+
org_display_name?: string
|
|
82
|
+
github_handle?: string
|
|
83
|
+
} = {}
|
|
84
|
+
|
|
85
|
+
if (name) {
|
|
86
|
+
updates.github_handle = name
|
|
87
|
+
updates.org_name = name
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
if (github_handle !== undefined) {
|
|
91
|
+
updates.github_handle = github_handle
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
if (display_name !== undefined) {
|
|
95
|
+
const trimmedDisplayName = display_name.trim()
|
|
96
|
+
const handleForFallback =
|
|
97
|
+
github_handle !== undefined ? github_handle : org.github_handle
|
|
98
|
+
const fallbackDisplayName =
|
|
99
|
+
name ?? org.org_display_name ?? org.org_name ?? handleForFallback ?? ""
|
|
100
|
+
updates.org_display_name =
|
|
101
|
+
trimmedDisplayName.length > 0 ? trimmedDisplayName : fallbackDisplayName
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
const updated = ctx.db.updateOrganization(org_id, updates)
|
|
105
|
+
|
|
106
|
+
if (!updated) {
|
|
107
|
+
return ctx.error(500, {
|
|
108
|
+
error_code: "update_failed",
|
|
109
|
+
message: "Failed to update organization",
|
|
110
|
+
})
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
const updatedOrgWithPermissions = ctx.db.getOrg({ org_id }, ctx.auth)
|
|
114
|
+
|
|
115
|
+
return ctx.json({
|
|
116
|
+
org: publicMapOrg(updatedOrgWithPermissions!),
|
|
117
|
+
})
|
|
118
|
+
})
|
|
@@ -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,60 @@ 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.org_name?.toLowerCase() === requested_owner_lower ||
|
|
70
|
+
o.github_handle?.toLowerCase() === requested_owner_lower,
|
|
71
|
+
)
|
|
72
|
+
|
|
73
|
+
if (!memberOrg) {
|
|
74
|
+
throw ctx.error(403, {
|
|
75
|
+
error_code: "forbidden",
|
|
76
|
+
message:
|
|
77
|
+
"You must be a member of the organization to create a package under it",
|
|
78
|
+
})
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
owner_org_id = memberOrg.org_id
|
|
82
|
+
owner_github_username = memberOrg.github_handle || memberOrg.org_name
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
const existingPackage = ctx.db
|
|
86
|
+
.getState()
|
|
87
|
+
.packages.find((pkg) => pkg.name === final_name)
|
|
88
|
+
|
|
89
|
+
if (existingPackage) {
|
|
90
|
+
throw ctx.error(400, {
|
|
91
|
+
error_code: "package_already_exists",
|
|
92
|
+
message: "A package with this name already exists",
|
|
93
|
+
})
|
|
94
|
+
}
|
|
95
|
+
|
|
47
96
|
const newPackage = ctx.db.addPackage({
|
|
48
|
-
name:
|
|
49
|
-
? name
|
|
50
|
-
: `${ctx.auth.github_username}/${String(unscoped_name)}`,
|
|
97
|
+
name: final_name,
|
|
51
98
|
description: description ?? null,
|
|
52
99
|
creator_account_id: ctx.auth.account_id,
|
|
53
|
-
owner_org_id
|
|
54
|
-
owner_github_username
|
|
100
|
+
owner_org_id,
|
|
101
|
+
owner_github_username,
|
|
55
102
|
latest_package_release_id: null,
|
|
56
103
|
latest_package_release_fs_sha: null,
|
|
57
104
|
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
|
})
|