@tscircuit/fake-snippets 0.0.22 → 0.0.24

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.
@@ -0,0 +1,121 @@
1
+ import ky from "ky"
2
+ import { readFileSync } from "fs"
3
+ import { join, dirname } from "path"
4
+ import { fileURLToPath } from "url"
5
+ import he from "he"
6
+
7
+ const __filename = fileURLToPath(import.meta.url)
8
+ const __dirname = dirname(__filename)
9
+
10
+ const normalIndexFile = join(__dirname, "../dist/index.html")
11
+ const htmlContent = readFileSync(normalIndexFile, "utf-8")
12
+
13
+ const cacheControlHeader = "public, max-age=120, s-maxage=120"
14
+
15
+ function getHtmlWithModifiedSeoTags({
16
+ title,
17
+ description,
18
+ canonicalUrl,
19
+ imageUrl,
20
+ }) {
21
+ const seoStartTag = "<!-- SEO_START -->"
22
+ const seoEndTag = "<!-- SEO_END -->"
23
+ const startIndex = htmlContent.indexOf(seoStartTag)
24
+ const endIndex = htmlContent.indexOf(seoEndTag) + seoEndTag.length
25
+
26
+ const seoTags = `
27
+ <title>${title}</title>
28
+ <meta name="description"
29
+ content="${description}" />
30
+ <meta name="keywords"
31
+ content="electronic design, PCB design, schematic capture, React components, circuit design, electronics CAD, open source EDA, ${title}" />
32
+ <meta property="og:title" content="${title}" />
33
+ <meta property="og:description"
34
+ content="${description}" />
35
+ <meta property="og:type" content="website" />
36
+ <meta name="twitter:card" content="summary_large_image" />
37
+ <meta name="twitter:title" content="${title}" />
38
+ <meta name="twitter:description" content="${description}" />
39
+ ${imageUrl ? `<meta property="og:image" content="${imageUrl}" />` : ""}
40
+ ${imageUrl ? `<meta name="twitter:image" content="${imageUrl}" />` : ""}
41
+ <link rel="canonical" href="${canonicalUrl}" />
42
+ `
43
+
44
+ return `${htmlContent.substring(0, startIndex)}${seoTags}${htmlContent.substring(endIndex)}`
45
+ }
46
+
47
+ async function handleCustomPackageHtml(req, res) {
48
+ // Get the author and package name
49
+ const [_, author, unscopedPackageName] = req.url.split("?")[0].split("/")
50
+ if (!author || !unscopedPackageName) {
51
+ throw new Error("Invalid author/package URL")
52
+ }
53
+
54
+ const { package: packageInfo } = await ky
55
+ .get(`https://registry-api.tscircuit.com/packages/get`, {
56
+ searchParams: {
57
+ name: `${author}/${unscopedPackageName}`,
58
+ },
59
+ })
60
+ .json()
61
+
62
+ if (!packageInfo) {
63
+ throw new Error("Package not found")
64
+ }
65
+
66
+ const description = he.encode(
67
+ `${packageInfo.description || packageInfo.ai_description || "A tscircuit component created by " + author} ${packageInfo.ai_usage_instructions ?? ""}`,
68
+ )
69
+ const title = he.encode(`${packageInfo.name} - tscircuit`)
70
+
71
+ const html = getHtmlWithModifiedSeoTags({
72
+ title,
73
+ description,
74
+ canonicalUrl: `https://tscircuit.com/${he.encode(author)}/${he.encode(unscopedPackageName)}`,
75
+ imageUrl: `https://registry-api.tscircuit.com/snippets/images/${he.encode(author)}/${he.encode(unscopedPackageName)}/pcb.png`,
76
+ })
77
+
78
+ res.setHeader("Content-Type", "text/html; charset=utf-8")
79
+ res.setHeader("Cache-Control", cacheControlHeader)
80
+ res.status(200).send(html)
81
+ }
82
+
83
+ async function handleCustomPage(req, res) {
84
+ const [_, page] = req.url.split("?")[0].split("/")
85
+
86
+ if (page === "landing" || !page) {
87
+ throw new Error("Use landing.html content")
88
+ }
89
+
90
+ // TODO handle usernames
91
+
92
+ const html = getHtmlWithModifiedSeoTags({
93
+ title: `${page} - tscircuit`,
94
+ description: ``,
95
+ canonicalUrl: `https://tscircuit.com/${page}`,
96
+ })
97
+
98
+ res.setHeader("Content-Type", "text/html; charset=utf-8")
99
+ res.setHeader("Cache-Control", cacheControlHeader)
100
+ res.status(200).send(html)
101
+ }
102
+
103
+ export default async function handler(req, res) {
104
+ try {
105
+ await handleCustomPackageHtml(req, res)
106
+ return
107
+ } catch (e) {
108
+ console.warn(e)
109
+ }
110
+
111
+ try {
112
+ await handleCustomPage(req, res)
113
+ return
114
+ } catch (e) {
115
+ console.warn(e)
116
+ }
117
+
118
+ res.setHeader("Content-Type", "text/html; charset=utf-8")
119
+ res.setHeader("Cache-Control", cacheControlHeader)
120
+ res.status(200).send(htmlContent)
121
+ }
@@ -67,16 +67,16 @@ test("get order", async () => {
67
67
  expect(getResponse.data.orderState).toBeDefined()
68
68
  expect(getResponse.data.orderState.order_id).toBe(orderId)
69
69
 
70
- expect(getResponse.data.orderState.are_gerbers_uploaded).toBe(true)
71
- expect(getResponse.data.orderState.is_gerber_analyzed).toBe(true)
72
- expect(getResponse.data.orderState.are_initial_costs_calculated).toBe(true)
73
- expect(getResponse.data.orderState.is_pcb_added_to_cart).toBe(true)
74
- expect(getResponse.data.orderState.is_bom_uploaded).toBe(true)
75
- expect(getResponse.data.orderState.is_pnp_uploaded).toBe(true)
76
- expect(getResponse.data.orderState.is_bom_pnp_analyzed).toBe(true)
77
- expect(getResponse.data.orderState.is_bom_parsing_complete).toBe(true)
78
- expect(getResponse.data.orderState.are_components_available).toBe(true)
79
- expect(getResponse.data.orderState.is_patch_map_generated).toBe(true)
70
+ expect(getResponse.data.orderState.are_gerbers_uploaded).toBe(false)
71
+ expect(getResponse.data.orderState.is_gerber_analyzed).toBe(false)
72
+ expect(getResponse.data.orderState.are_initial_costs_calculated).toBe(false)
73
+ expect(getResponse.data.orderState.is_pcb_added_to_cart).toBe(false)
74
+ expect(getResponse.data.orderState.is_bom_uploaded).toBe(false)
75
+ expect(getResponse.data.orderState.is_pnp_uploaded).toBe(false)
76
+ expect(getResponse.data.orderState.is_bom_pnp_analyzed).toBe(false)
77
+ expect(getResponse.data.orderState.is_bom_parsing_complete).toBe(false)
78
+ expect(getResponse.data.orderState.are_components_available).toBe(false)
79
+ expect(getResponse.data.orderState.is_patch_map_generated).toBe(false)
80
80
  })
81
81
 
82
82
  test("get order with simulate scenario", async () => {
@@ -156,3 +156,47 @@ test("get order with simulate scenario [is_bom_uploaded ]", async () => {
156
156
 
157
157
  expect(getResponse.data.orderState.is_pnp_uploaded).toBe(false)
158
158
  })
159
+
160
+ test("get order after polling /move_orders_forward", async () => {
161
+ const {
162
+ axios,
163
+ seed: { order },
164
+ } = await getTestServer()
165
+
166
+ const createResponse = await axios.post("/api/orders/create", {
167
+ circuit_json: order.circuit_json,
168
+ })
169
+
170
+ expect(createResponse.status).toBe(200)
171
+ expect(createResponse.data.order).toBeDefined()
172
+ expect(createResponse.data.order.order_id).toBeDefined()
173
+
174
+ const getResponse = await axios.get("/api/orders/get", {
175
+ params: { order_id: createResponse.data.order.order_id },
176
+ })
177
+
178
+ expect(getResponse.status).toBe(200)
179
+ expect(getResponse.data.order).toBeDefined()
180
+ expect(getResponse.data.order.order_id).toBe(
181
+ createResponse.data.order.order_id,
182
+ )
183
+ expect(getResponse.data.orderState).toBeDefined()
184
+
185
+ expect(getResponse.data.orderState.are_gerbers_uploaded).toBe(false)
186
+
187
+ const moveOrdersForwardResponse = await axios.post(
188
+ "/api/_fake/move_orders_forward",
189
+ {
190
+ order_id: createResponse.data.order.order_id,
191
+ },
192
+ )
193
+
194
+ expect(moveOrdersForwardResponse.status).toBe(200)
195
+
196
+ const getResponseAfterMove = await axios.get("/api/orders/get", {
197
+ params: { order_id: createResponse.data.order.order_id },
198
+ })
199
+
200
+ expect(getResponseAfterMove.status).toBe(200)
201
+ expect(getResponseAfterMove.data.orderState.are_gerbers_generated).toBe(true)
202
+ })
package/bun.lock CHANGED
@@ -88,6 +88,7 @@
88
88
  "react-day-picker": "8.10.1",
89
89
  "react-dom": "^18.3.1",
90
90
  "react-helmet": "^6.1.0",
91
+ "react-helmet-async": "^2.0.5",
91
92
  "react-hook-form": "^7.53.0",
92
93
  "react-intersection-observer": "^9.14.1",
93
94
  "react-query": "^3.39.3",
@@ -128,6 +129,8 @@
128
129
  "circuit-to-svg": "^0.0.101",
129
130
  "get-port": "^7.1.0",
130
131
  "globals": "^15.9.0",
132
+ "he": "^1.2.0",
133
+ "ky": "^1.7.5",
131
134
  "postcss": "^8.4.47",
132
135
  "prismjs": "^1.29.0",
133
136
  "prompts": "^2.4.2",
@@ -1330,6 +1333,8 @@
1330
1333
 
1331
1334
  "hastscript": ["hastscript@6.0.0", "", { "dependencies": { "@types/hast": "^2.0.0", "comma-separated-tokens": "^1.0.0", "hast-util-parse-selector": "^2.0.0", "property-information": "^5.0.0", "space-separated-tokens": "^1.0.0" } }, "sha512-nDM6bvd7lIqDUiYEiu5Sl/+6ReP0BMk/2f4U/Rooccxkj0P5nm+acM5PrGJ/t5I8qPGiqZSE6hVAwZEdZIvP4w=="],
1332
1335
 
1336
+ "he": ["he@1.2.0", "", { "bin": { "he": "bin/he" } }, "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw=="],
1337
+
1333
1338
  "heap": ["heap@0.2.5", "", {}, "sha512-G7HLD+WKcrOyJP5VQwYZNC3Z6FcQ7YYjEFiFoIj8PfEr73mu421o8B1N5DKUcc8K37EsJ2XXWA8DtrDz/2dReg=="],
1334
1339
 
1335
1340
  "highlight.js": ["highlight.js@10.7.3", "", {}, "sha512-tzcUFauisWKNHaRkN4Wjl/ZA07gENAjFl3J/c480dprkGTg5EQstgaNFqBfUqCq54kZRIEcreTsAgF/m2quD7A=="],
@@ -1366,6 +1371,8 @@
1366
1371
 
1367
1372
  "internmap": ["internmap@2.0.3", "", {}, "sha512-5Hh7Y1wQbvY5ooGgPbDaL5iYLAPzMTUrjMulskHLH6wnv/A+1q5rgEaiuqEjB+oxGXIVZs1FF+R/KPN3ZSQYYg=="],
1368
1373
 
1374
+ "invariant": ["invariant@2.2.4", "", { "dependencies": { "loose-envify": "^1.0.0" } }, "sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA=="],
1375
+
1369
1376
  "ipaddr.js": ["ipaddr.js@2.2.0", "", {}, "sha512-Ag3wB2o37wslZS19hZqorUnrnzSkpOVy+IiiDEiTqNubEYpYuHWIf6K4psgN2ZWKExS4xhVCrRVfb/wfW8fWJA=="],
1370
1377
 
1371
1378
  "is-alphabetical": ["is-alphabetical@1.0.4", "", {}, "sha512-DwzsA04LQ10FHTZuL0/grVDk4rFoVH1pjAToYwBrHSxcrBIGQuXrQMtD5U1b0U2XVgKZCTLLP8u2Qxqhy3l2Vg=="],
@@ -1464,6 +1471,8 @@
1464
1471
 
1465
1472
  "kleur": ["kleur@3.0.3", "", {}, "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w=="],
1466
1473
 
1474
+ "ky": ["ky@1.7.5", "", {}, "sha512-HzhziW6sc5m0pwi5M196+7cEBtbt0lCYi67wNsiwMUmz833wloE0gbzJPWKs1gliFKQb34huItDQX97LyOdPdA=="],
1475
+
1467
1476
  "lie": ["lie@3.3.0", "", { "dependencies": { "immediate": "~3.0.5" } }, "sha512-UaiMJzeWRlEujzAuw5LokY1L5ecNQYZKfmyZ9L7wDHb/p5etKaxXhohBcrw0EYby+G/NA52vRSN4N39dxHAIwQ=="],
1468
1477
 
1469
1478
  "light-my-request": ["light-my-request@6.6.0", "", { "dependencies": { "cookie": "^1.0.1", "process-warning": "^4.0.0", "set-cookie-parser": "^2.6.0" } }, "sha512-CHYbu8RtboSIoVsHZ6Ye4cj4Aw/yg2oAFimlF7mNvfDV192LR7nDiKtSIfCuLT7KokPSTn/9kfVLm5OGN0A28A=="],
@@ -1828,6 +1837,8 @@
1828
1837
 
1829
1838
  "react-helmet": ["react-helmet@6.1.0", "", { "dependencies": { "object-assign": "^4.1.1", "prop-types": "^15.7.2", "react-fast-compare": "^3.1.1", "react-side-effect": "^2.1.0" }, "peerDependencies": { "react": ">=16.3.0" } }, "sha512-4uMzEY9nlDlgxr61NL3XbKRy1hEkXmKNXhjbAIOVw5vcFrsdYbH2FEwcNyWvWinl103nXgzYNlns9ca+8kFiWw=="],
1830
1839
 
1840
+ "react-helmet-async": ["react-helmet-async@2.0.5", "", { "dependencies": { "invariant": "^2.2.4", "react-fast-compare": "^3.2.2", "shallowequal": "^1.1.0" }, "peerDependencies": { "react": "^16.6.0 || ^17.0.0 || ^18.0.0" } }, "sha512-rYUYHeus+i27MvFE+Jaa4WsyBKGkL6qVgbJvSBoX8mbsWoABJXdEO0bZyi0F6i+4f0NuIb8AvqPMj3iXFHkMwg=="],
1841
+
1831
1842
  "react-hook-form": ["react-hook-form@7.54.2", "", { "peerDependencies": { "react": "^16.8.0 || ^17 || ^18 || ^19" } }, "sha512-eHpAUgUjWbZocoQYUHposymRb4ZP6d0uwUnooL2uOybA9/3tPUvoAKqEWK1WaSiTxxOfTpffNZP7QwlnM3/gEg=="],
1832
1843
 
1833
1844
  "react-intersection-observer": ["react-intersection-observer@9.16.0", "", { "peerDependencies": { "react": "^17.0.0 || ^18.0.0 || ^19.0.0", "react-dom": "^17.0.0 || ^18.0.0 || ^19.0.0" }, "optionalPeers": ["react-dom"] }, "sha512-w9nJSEp+DrW9KmQmeWHQyfaP6b03v+TdXynaoA964Wxt7mdR3An11z4NNCQgL4gKSK7y1ver2Fq+JKH6CWEzUA=="],