@tscircuit/fake-snippets 0.0.69 → 0.0.71
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/api/generated-index.js +77 -3
- package/bun-tests/fake-snippets-api/routes/accounts/get.test.ts +53 -0
- package/dist/bundle.js +22 -3
- package/fake-snippets-api/routes/api/accounts/get.ts +16 -3
- package/fake-snippets-api/routes/api/packages/get.ts +12 -0
- package/index.html +3 -0
- package/package.json +1 -1
- package/src/ContextProviders.tsx +2 -0
- package/src/components/CircuitJsonImportDialog.tsx +113 -52
- package/src/components/DownloadButtonAndMenu.tsx +1 -1
- package/src/components/HeaderLogin.tsx +3 -3
- package/src/components/ViewPackagePage/components/important-files-view.tsx +1 -1
- package/src/components/ViewPackagePage/components/repo-page-content.tsx +1 -0
- package/src/components/package-port/CodeAndPreview.tsx +1 -1
- package/src/components/package-port/CodeEditorHeader.tsx +12 -4
- package/src/components/package-port/EditorNav.tsx +33 -8
- package/src/hooks/use-create-package-mutation.ts +0 -2
- package/src/lib/populate-query-cache-with-ssr-data.ts +52 -0
- package/src/pages/dashboard.tsx +2 -2
- package/src/pages/landing.tsx +14 -3
- package/src/pages/package-editor.tsx +2 -1
- package/src/pages/quickstart.tsx +1 -1
- package/src/pages/view-package.tsx +13 -11
package/api/generated-index.js
CHANGED
|
@@ -18,11 +18,12 @@ function getHtmlWithModifiedSeoTags({
|
|
|
18
18
|
description,
|
|
19
19
|
canonicalUrl,
|
|
20
20
|
imageUrl,
|
|
21
|
+
ssrPackageData,
|
|
21
22
|
}) {
|
|
22
23
|
const seoStartTag = "<!-- SEO_START -->"
|
|
23
24
|
const seoEndTag = "<!-- SEO_END -->"
|
|
24
|
-
const
|
|
25
|
-
const
|
|
25
|
+
const seoStartIndex = htmlContent.indexOf(seoStartTag)
|
|
26
|
+
const seoEndIndex = htmlContent.indexOf(seoEndTag) + seoEndTag.length
|
|
26
27
|
|
|
27
28
|
const seoTags = `
|
|
28
29
|
<title>${title}</title>
|
|
@@ -42,7 +43,47 @@ function getHtmlWithModifiedSeoTags({
|
|
|
42
43
|
<link rel="canonical" href="${canonicalUrl}" />
|
|
43
44
|
`
|
|
44
45
|
|
|
45
|
-
|
|
46
|
+
// First replace SEO tags
|
|
47
|
+
let modifiedHtml = `${htmlContent.substring(0, seoStartIndex)}${seoTags}${htmlContent.substring(seoEndIndex)}`
|
|
48
|
+
|
|
49
|
+
// Then handle SSR data injection
|
|
50
|
+
if (ssrPackageData) {
|
|
51
|
+
const ssrStartTag = "<!-- SSR_START -->"
|
|
52
|
+
const ssrEndTag = "<!-- SSR_END -->"
|
|
53
|
+
const {
|
|
54
|
+
package: packageData,
|
|
55
|
+
packageRelease,
|
|
56
|
+
packageFiles,
|
|
57
|
+
} = ssrPackageData
|
|
58
|
+
|
|
59
|
+
const assignments = []
|
|
60
|
+
if (packageData) {
|
|
61
|
+
assignments.push(`window.SSR_PACKAGE = ${JSON.stringify(packageData)};`)
|
|
62
|
+
}
|
|
63
|
+
if (packageRelease) {
|
|
64
|
+
assignments.push(
|
|
65
|
+
`window.SSR_PACKAGE_RELEASE = ${JSON.stringify(packageRelease)};`,
|
|
66
|
+
)
|
|
67
|
+
}
|
|
68
|
+
if (packageFiles) {
|
|
69
|
+
assignments.push(
|
|
70
|
+
`window.SSR_PACKAGE_FILES = ${JSON.stringify(packageFiles)};`,
|
|
71
|
+
)
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
const ssrScripts =
|
|
75
|
+
assignments.length > 0 ? `<script>${assignments.join(" ")}</script>` : ""
|
|
76
|
+
|
|
77
|
+
if (ssrScripts) {
|
|
78
|
+
const ssrContent = `\n ${ssrScripts}\n `
|
|
79
|
+
modifiedHtml = modifiedHtml.replace(
|
|
80
|
+
`${ssrStartTag}\n ${ssrEndTag}`,
|
|
81
|
+
`${ssrStartTag}${ssrContent}${ssrEndTag}`,
|
|
82
|
+
)
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
return modifiedHtml
|
|
46
87
|
}
|
|
47
88
|
|
|
48
89
|
async function handleCustomPackageHtml(req, res) {
|
|
@@ -64,6 +105,38 @@ async function handleCustomPackageHtml(req, res) {
|
|
|
64
105
|
throw new Error("Package not found")
|
|
65
106
|
}
|
|
66
107
|
|
|
108
|
+
let packageRelease = null
|
|
109
|
+
let packageFiles = null
|
|
110
|
+
try {
|
|
111
|
+
const releaseResponse = await ky
|
|
112
|
+
.post(`https://registry-api.tscircuit.com/package_releases/get`, {
|
|
113
|
+
json: {
|
|
114
|
+
package_id: packageInfo.package_id,
|
|
115
|
+
is_latest: true,
|
|
116
|
+
},
|
|
117
|
+
})
|
|
118
|
+
.json()
|
|
119
|
+
packageRelease = releaseResponse.package_release
|
|
120
|
+
|
|
121
|
+
// Get package files for the latest release
|
|
122
|
+
if (packageRelease?.package_release_id) {
|
|
123
|
+
try {
|
|
124
|
+
const filesResponse = await ky
|
|
125
|
+
.post(`https://registry-api.tscircuit.com/package_files/list`, {
|
|
126
|
+
json: {
|
|
127
|
+
package_release_id: packageRelease.package_release_id,
|
|
128
|
+
},
|
|
129
|
+
})
|
|
130
|
+
.json()
|
|
131
|
+
packageFiles = filesResponse.package_files || []
|
|
132
|
+
} catch (e) {
|
|
133
|
+
console.warn("Failed to fetch package files:", e)
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
} catch (e) {
|
|
137
|
+
console.warn("Failed to fetch package release:", e)
|
|
138
|
+
}
|
|
139
|
+
|
|
67
140
|
const description = he.encode(
|
|
68
141
|
`${packageInfo.description || packageInfo.ai_description || "A tscircuit component created by " + author} ${packageInfo.ai_usage_instructions ?? ""}`,
|
|
69
142
|
)
|
|
@@ -74,6 +147,7 @@ async function handleCustomPackageHtml(req, res) {
|
|
|
74
147
|
description,
|
|
75
148
|
canonicalUrl: `https://tscircuit.com/${he.encode(author)}/${he.encode(unscopedPackageName)}`,
|
|
76
149
|
imageUrl: `https://registry-api.tscircuit.com/snippets/images/${he.encode(author)}/${he.encode(unscopedPackageName)}/pcb.png`,
|
|
150
|
+
ssrPackageData: { package: packageInfo, packageRelease, packageFiles },
|
|
77
151
|
})
|
|
78
152
|
|
|
79
153
|
res.setHeader("Content-Type", "text/html; charset=utf-8")
|
|
@@ -0,0 +1,53 @@
|
|
|
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/accounts/get - should return account when authenticated", async () => {
|
|
5
|
+
const { axios } = await getTestServer()
|
|
6
|
+
|
|
7
|
+
// The test server should automatically create a test account and set up authentication
|
|
8
|
+
const response = await axios.get("/api/accounts/get")
|
|
9
|
+
|
|
10
|
+
expect(response.status).toBe(200)
|
|
11
|
+
expect(response.data.account).toBeDefined()
|
|
12
|
+
expect(response.data.account.account_id).toBeDefined()
|
|
13
|
+
})
|
|
14
|
+
|
|
15
|
+
test("GET /api/accounts/get - should return 404 if account not found", async () => {
|
|
16
|
+
const { unauthenticatedAxios } = await getTestServer()
|
|
17
|
+
|
|
18
|
+
try {
|
|
19
|
+
await unauthenticatedAxios.get("/api/accounts/get")
|
|
20
|
+
throw new Error("Expected request to fail")
|
|
21
|
+
} catch (error: any) {
|
|
22
|
+
expect(error.status).toBe(404)
|
|
23
|
+
expect(error.data.error.error_code).toBe("account_not_found")
|
|
24
|
+
expect(error.data.error.message).toBe("Account not found")
|
|
25
|
+
}
|
|
26
|
+
})
|
|
27
|
+
|
|
28
|
+
test("POST /api/accounts/get - should return account when authenticated", async () => {
|
|
29
|
+
const { axios } = await getTestServer()
|
|
30
|
+
|
|
31
|
+
const response = await axios.post("/api/accounts/get", {
|
|
32
|
+
github_username: "testuser",
|
|
33
|
+
})
|
|
34
|
+
|
|
35
|
+
expect(response.status).toBe(200)
|
|
36
|
+
expect(response.data.account).toBeDefined()
|
|
37
|
+
expect(response.data.account.account_id).toBeDefined()
|
|
38
|
+
})
|
|
39
|
+
|
|
40
|
+
test("POST /api/accounts/get - should return 404 if account not found", async () => {
|
|
41
|
+
const { axios } = await getTestServer()
|
|
42
|
+
|
|
43
|
+
try {
|
|
44
|
+
await axios.post("/api/accounts/get", {
|
|
45
|
+
github_username: "nonexistentuser",
|
|
46
|
+
})
|
|
47
|
+
throw new Error("Expected request to fail")
|
|
48
|
+
} catch (error: any) {
|
|
49
|
+
expect(error.status).toBe(404)
|
|
50
|
+
expect(error.data.error.error_code).toBe("account_not_found")
|
|
51
|
+
expect(error.data.error.message).toBe("Account not found")
|
|
52
|
+
}
|
|
53
|
+
})
|