@tscircuit/fake-snippets 0.0.7 → 0.0.8
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/bun-tests/fake-snippets-api/fixtures/get-test-server.ts +2 -5
- package/bun-tests/fake-snippets-api/routes/packages/{list.test.ts → list-1.test.ts} +3 -55
- package/bun-tests/fake-snippets-api/routes/packages/list-2.test.ts +59 -0
- package/bun-tests/fake-snippets-api/routes/snippets/create.test.ts +34 -1
- package/bun.lock +3 -0
- package/dist/bundle.js +146 -62
- package/fake-snippets-api/lib/db/db-client.ts +46 -51
- package/fake-snippets-api/lib/db/schema.ts +15 -6
- package/fake-snippets-api/lib/public-mapping/public-map-package.ts +9 -2
- package/fake-snippets-api/routes/api/snippets/create.ts +123 -29
- package/package.json +2 -1
|
@@ -1,26 +1,24 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import { z } from "zod"
|
|
1
|
+
import type { z } from "zod"
|
|
2
|
+
import { hoist } from "zustand-hoist"
|
|
3
|
+
import { createStore } from "zustand/vanilla"
|
|
5
4
|
|
|
5
|
+
import { combine } from "zustand/middleware"
|
|
6
6
|
import {
|
|
7
|
+
type Account,
|
|
8
|
+
type AccountSnippet,
|
|
9
|
+
type LoginPage,
|
|
10
|
+
type Order,
|
|
11
|
+
type OrderFile,
|
|
12
|
+
type Package,
|
|
13
|
+
type PackageFile,
|
|
14
|
+
type PackageRelease,
|
|
15
|
+
type Session,
|
|
16
|
+
type Snippet,
|
|
7
17
|
databaseSchema,
|
|
8
|
-
Snippet,
|
|
9
|
-
Session,
|
|
10
|
-
LoginPage,
|
|
11
|
-
Account,
|
|
12
|
-
type DatabaseSchema,
|
|
13
|
-
snippetSchema,
|
|
14
|
-
Order,
|
|
15
|
-
OrderFile,
|
|
16
|
-
AccountSnippet,
|
|
17
18
|
packageReleaseSchema,
|
|
18
|
-
packageSchema,
|
|
19
|
-
|
|
20
|
-
PackageRelease,
|
|
21
|
-
PackageFile,
|
|
19
|
+
type packageSchema,
|
|
20
|
+
snippetSchema,
|
|
22
21
|
} from "./schema.ts"
|
|
23
|
-
import { combine } from "zustand/middleware"
|
|
24
22
|
import { seed as seedFn } from "./seed"
|
|
25
23
|
|
|
26
24
|
export const createDatabase = ({ seed }: { seed?: boolean } = {}) => {
|
|
@@ -35,7 +33,7 @@ export type DbClient = ReturnType<typeof createDatabase>
|
|
|
35
33
|
|
|
36
34
|
const initializer = combine(databaseSchema.parse({}), (set, get) => ({
|
|
37
35
|
addOrder: (order: Omit<Order, "order_id">): Order => {
|
|
38
|
-
|
|
36
|
+
const newOrder = { order_id: `order_${get().idCounter + 1}`, ...order }
|
|
39
37
|
set((state) => {
|
|
40
38
|
return {
|
|
41
39
|
orders: [...state.orders, newOrder],
|
|
@@ -180,13 +178,13 @@ const initializer = combine(databaseSchema.parse({}), (set, get) => ({
|
|
|
180
178
|
)
|
|
181
179
|
},
|
|
182
180
|
updateSnippet: (
|
|
183
|
-
|
|
181
|
+
snippetId: string,
|
|
184
182
|
updates: Partial<Snippet>,
|
|
185
183
|
): Snippet | undefined => {
|
|
186
184
|
let updatedSnippet: Snippet | undefined
|
|
187
185
|
set((state) => {
|
|
188
186
|
const snippetIndex = state.snippets.findIndex(
|
|
189
|
-
(snippet) => snippet.snippet_id ===
|
|
187
|
+
(snippet) => snippet.snippet_id === snippetId,
|
|
190
188
|
)
|
|
191
189
|
if (snippetIndex === -1) {
|
|
192
190
|
return state
|
|
@@ -202,16 +200,16 @@ const initializer = combine(databaseSchema.parse({}), (set, get) => ({
|
|
|
202
200
|
})
|
|
203
201
|
return updatedSnippet
|
|
204
202
|
},
|
|
205
|
-
getSnippetById: (
|
|
203
|
+
getSnippetById: (snippetId: string): Snippet | undefined => {
|
|
206
204
|
const state = get()
|
|
207
205
|
const snippet = state.snippets.find(
|
|
208
|
-
(snippet) => snippet.snippet_id ===
|
|
206
|
+
(snippet) => snippet.snippet_id === snippetId,
|
|
209
207
|
)
|
|
210
208
|
if (!snippet) return undefined
|
|
211
209
|
return {
|
|
212
210
|
...snippet,
|
|
213
211
|
star_count: state.accountSnippets.filter(
|
|
214
|
-
(as) => as.snippet_id ===
|
|
212
|
+
(as) => as.snippet_id === snippetId && as.has_starred,
|
|
215
213
|
).length,
|
|
216
214
|
}
|
|
217
215
|
},
|
|
@@ -232,10 +230,10 @@ const initializer = combine(databaseSchema.parse({}), (set, get) => ({
|
|
|
232
230
|
).length,
|
|
233
231
|
}))
|
|
234
232
|
},
|
|
235
|
-
deleteSnippet: (
|
|
233
|
+
deleteSnippet: (snippetId: string): boolean => {
|
|
236
234
|
let deleted = false
|
|
237
235
|
set((state) => {
|
|
238
|
-
const index = state.snippets.findIndex((s) => s.snippet_id ===
|
|
236
|
+
const index = state.snippets.findIndex((s) => s.snippet_id === snippetId)
|
|
239
237
|
if (index !== -1) {
|
|
240
238
|
state.snippets.splice(index, 1)
|
|
241
239
|
deleted = true
|
|
@@ -277,32 +275,29 @@ const initializer = combine(databaseSchema.parse({}), (set, get) => ({
|
|
|
277
275
|
}))
|
|
278
276
|
return newLoginPage
|
|
279
277
|
},
|
|
280
|
-
getLoginPage: (
|
|
278
|
+
getLoginPage: (loginPageId: string): LoginPage | undefined => {
|
|
281
279
|
const state = get()
|
|
282
|
-
return state.loginPages.find((lp) => lp.login_page_id ===
|
|
280
|
+
return state.loginPages.find((lp) => lp.login_page_id === loginPageId)
|
|
283
281
|
},
|
|
284
|
-
updateLoginPage: (
|
|
285
|
-
login_page_id: string,
|
|
286
|
-
updates: Partial<LoginPage>,
|
|
287
|
-
): void => {
|
|
282
|
+
updateLoginPage: (loginPageId: string, updates: Partial<LoginPage>): void => {
|
|
288
283
|
set((state) => ({
|
|
289
284
|
loginPages: state.loginPages.map((lp) =>
|
|
290
|
-
lp.login_page_id ===
|
|
285
|
+
lp.login_page_id === loginPageId ? { ...lp, ...updates } : lp,
|
|
291
286
|
),
|
|
292
287
|
}))
|
|
293
288
|
},
|
|
294
|
-
getAccount: (
|
|
289
|
+
getAccount: (accountId: string): Account | undefined => {
|
|
295
290
|
const state = get()
|
|
296
|
-
return state.accounts.find((account) => account.account_id ===
|
|
291
|
+
return state.accounts.find((account) => account.account_id === accountId)
|
|
297
292
|
},
|
|
298
293
|
updateAccount: (
|
|
299
|
-
|
|
294
|
+
accountId: string,
|
|
300
295
|
updates: Partial<Account>,
|
|
301
296
|
): Account | undefined => {
|
|
302
297
|
let updatedAccount: Account | undefined
|
|
303
298
|
set((state) => {
|
|
304
299
|
const accountIndex = state.accounts.findIndex(
|
|
305
|
-
(account) => account.account_id ===
|
|
300
|
+
(account) => account.account_id === accountId,
|
|
306
301
|
)
|
|
307
302
|
if (accountIndex !== -1) {
|
|
308
303
|
updatedAccount = { ...state.accounts[accountIndex] }
|
|
@@ -329,11 +324,11 @@ const initializer = combine(databaseSchema.parse({}), (set, get) => ({
|
|
|
329
324
|
}))
|
|
330
325
|
return newSession
|
|
331
326
|
},
|
|
332
|
-
addStar: (
|
|
327
|
+
addStar: (accountId: string, snippetId: string): AccountSnippet => {
|
|
333
328
|
const now = new Date().toISOString()
|
|
334
329
|
const accountSnippet = {
|
|
335
|
-
account_id,
|
|
336
|
-
snippet_id,
|
|
330
|
+
account_id: accountId,
|
|
331
|
+
snippet_id: snippetId,
|
|
337
332
|
has_starred: true,
|
|
338
333
|
created_at: now,
|
|
339
334
|
updated_at: now,
|
|
@@ -343,19 +338,19 @@ const initializer = combine(databaseSchema.parse({}), (set, get) => ({
|
|
|
343
338
|
}))
|
|
344
339
|
return accountSnippet
|
|
345
340
|
},
|
|
346
|
-
removeStar: (
|
|
341
|
+
removeStar: (accountId: string, snippetId: string): void => {
|
|
347
342
|
set((state) => ({
|
|
348
343
|
accountSnippets: state.accountSnippets.filter(
|
|
349
|
-
(as) => !(as.account_id ===
|
|
344
|
+
(as) => !(as.account_id === accountId && as.snippet_id === snippetId),
|
|
350
345
|
),
|
|
351
346
|
}))
|
|
352
347
|
},
|
|
353
|
-
hasStarred: (
|
|
348
|
+
hasStarred: (accountId: string, snippetId: string): boolean => {
|
|
354
349
|
const state = get()
|
|
355
350
|
return state.accountSnippets.some(
|
|
356
351
|
(as) =>
|
|
357
|
-
as.account_id ===
|
|
358
|
-
as.snippet_id ===
|
|
352
|
+
as.account_id === accountId &&
|
|
353
|
+
as.snippet_id === snippetId &&
|
|
359
354
|
as.has_starred,
|
|
360
355
|
)
|
|
361
356
|
},
|
|
@@ -377,24 +372,24 @@ const initializer = combine(databaseSchema.parse({}), (set, get) => ({
|
|
|
377
372
|
})
|
|
378
373
|
newPackage.latest_package_release_id = packageRelease.package_release_id
|
|
379
374
|
set((state) => ({
|
|
380
|
-
packages: [...state.packages, newPackage],
|
|
375
|
+
packages: [...state.packages, newPackage as Package],
|
|
381
376
|
}))
|
|
382
|
-
return newPackage
|
|
377
|
+
return newPackage as Package
|
|
383
378
|
},
|
|
384
|
-
getPackageById: (
|
|
379
|
+
getPackageById: (packageId: string): Package | undefined => {
|
|
385
380
|
const state = get()
|
|
386
|
-
const pkg = state.packages.find((pkg) => pkg.package_id ===
|
|
381
|
+
const pkg = state.packages.find((pkg) => pkg.package_id === packageId)
|
|
387
382
|
if (!pkg) return undefined
|
|
388
383
|
return {
|
|
389
384
|
...pkg,
|
|
390
385
|
}
|
|
391
386
|
},
|
|
392
387
|
getPackageReleaseById: (
|
|
393
|
-
|
|
388
|
+
packageReleaseId: string,
|
|
394
389
|
): PackageRelease | undefined => {
|
|
395
390
|
const state = get()
|
|
396
391
|
return state.packageReleases.find(
|
|
397
|
-
(pr) => pr.package_release_id ===
|
|
392
|
+
(pr) => pr.package_release_id === packageReleaseId,
|
|
398
393
|
)
|
|
399
394
|
},
|
|
400
395
|
addPackageRelease: (
|
|
@@ -124,18 +124,27 @@ export type PackageFile = z.infer<typeof packageFileSchema>
|
|
|
124
124
|
export const packageSchema = z.object({
|
|
125
125
|
package_id: z.string(),
|
|
126
126
|
creator_account_id: z.string(),
|
|
127
|
-
latest_package_release_id: z.string().nullable(),
|
|
128
|
-
latest_version: z.string().nullable(),
|
|
129
|
-
license: z.string().nullable(),
|
|
130
127
|
owner_org_id: z.string(),
|
|
131
128
|
owner_github_username: z.string().nullable(),
|
|
132
|
-
is_source_from_github: z.boolean(),
|
|
133
|
-
description: z.string().nullable(),
|
|
134
129
|
name: z.string(),
|
|
135
130
|
unscoped_name: z.string(),
|
|
136
|
-
|
|
131
|
+
description: z.string().nullable(),
|
|
137
132
|
created_at: z.string().datetime(),
|
|
138
133
|
updated_at: z.string().datetime(),
|
|
134
|
+
is_snippet: z.boolean().default(false),
|
|
135
|
+
is_board: z.boolean().default(false),
|
|
136
|
+
is_package: z.boolean().default(false),
|
|
137
|
+
is_model: z.boolean().default(false),
|
|
138
|
+
is_footprint: z.boolean().default(false),
|
|
139
|
+
is_private: z.boolean().nullable().default(false),
|
|
140
|
+
is_public: z.boolean().nullable().default(true),
|
|
141
|
+
is_unlisted: z.boolean().nullable().default(false),
|
|
142
|
+
is_source_from_github: z.boolean().default(false),
|
|
143
|
+
snippet_type: z.enum(["board", "package", "model", "footprint"]).optional(),
|
|
144
|
+
latest_package_release_id: z.string().nullable(),
|
|
145
|
+
latest_version: z.string().nullable(),
|
|
146
|
+
license: z.string().nullable(),
|
|
147
|
+
star_count: z.number().default(0),
|
|
139
148
|
ai_description: z.string().nullable(),
|
|
140
149
|
})
|
|
141
150
|
export type Package = z.infer<typeof packageSchema>
|
|
@@ -17,15 +17,22 @@ export const publicMapPackage = (internal_package: {
|
|
|
17
17
|
updated_at: string
|
|
18
18
|
created_at: string
|
|
19
19
|
ai_description: string | null
|
|
20
|
+
is_snippet: boolean
|
|
21
|
+
is_board: boolean
|
|
22
|
+
is_package: boolean
|
|
23
|
+
is_model: boolean
|
|
24
|
+
is_footprint: boolean
|
|
20
25
|
}): ZT.Package => {
|
|
21
26
|
return {
|
|
22
27
|
...internal_package,
|
|
23
|
-
latest_package_release_id:
|
|
24
|
-
internal_package.latest_package_release_id ?? null,
|
|
28
|
+
latest_package_release_id: internal_package.latest_package_release_id ?? "",
|
|
25
29
|
latest_version: internal_package.latest_version ?? null,
|
|
26
30
|
license: internal_package.latest_license ?? null,
|
|
27
31
|
star_count: internal_package.star_count ?? 0,
|
|
28
32
|
created_at: internal_package.created_at,
|
|
29
33
|
updated_at: internal_package.updated_at,
|
|
34
|
+
is_private: false,
|
|
35
|
+
is_public: true,
|
|
36
|
+
is_unlisted: false,
|
|
30
37
|
}
|
|
31
38
|
}
|
|
@@ -30,50 +30,144 @@ export default withRouteSpec({
|
|
|
30
30
|
} = req.jsonBody
|
|
31
31
|
|
|
32
32
|
if (!unscoped_name) {
|
|
33
|
-
|
|
33
|
+
// Count snippets of this type for this user
|
|
34
|
+
const userSnippets = ctx.db.packages.filter(
|
|
35
|
+
(p) =>
|
|
36
|
+
p.owner_github_username === ctx.auth.github_username &&
|
|
37
|
+
p.is_snippet === true &&
|
|
38
|
+
((p.is_board && snippet_type === "board") ||
|
|
39
|
+
(p.is_package && snippet_type === "package") ||
|
|
40
|
+
(p.is_model && snippet_type === "model") ||
|
|
41
|
+
(p.is_footprint && snippet_type === "footprint")),
|
|
42
|
+
)
|
|
43
|
+
unscoped_name = `untitled-${snippet_type}-${userSnippets.length + 1}`
|
|
34
44
|
}
|
|
35
45
|
|
|
36
|
-
const
|
|
37
|
-
(
|
|
38
|
-
|
|
39
|
-
|
|
46
|
+
const existingPackage = ctx.db.packages.find(
|
|
47
|
+
(pkg) =>
|
|
48
|
+
pkg.unscoped_name === unscoped_name &&
|
|
49
|
+
pkg.owner_github_username === ctx.auth.github_username,
|
|
40
50
|
)
|
|
41
51
|
|
|
42
|
-
if (
|
|
52
|
+
if (existingPackage) {
|
|
43
53
|
return ctx.error(400, {
|
|
44
54
|
error_code: "snippet_already_exists",
|
|
45
55
|
message: "You have already forked this snippet in your account.",
|
|
46
56
|
})
|
|
47
57
|
}
|
|
48
58
|
|
|
49
|
-
let newSnippet: Omit<
|
|
50
|
-
z.input<typeof snippetSchema>,
|
|
51
|
-
"snippet_id" | "package_release_id"
|
|
52
|
-
> = {
|
|
53
|
-
name: `${ctx.auth.github_username}/${unscoped_name}`,
|
|
54
|
-
unscoped_name,
|
|
55
|
-
owner_name: ctx.auth.github_username,
|
|
56
|
-
code,
|
|
57
|
-
created_at: new Date().toISOString(),
|
|
58
|
-
updated_at: new Date().toISOString(),
|
|
59
|
-
snippet_type,
|
|
60
|
-
description,
|
|
61
|
-
compiled_js,
|
|
62
|
-
circuit_json,
|
|
63
|
-
dts,
|
|
64
|
-
}
|
|
65
|
-
|
|
66
59
|
try {
|
|
67
|
-
|
|
60
|
+
// Create the package directly (which will serve as our snippet)
|
|
61
|
+
const newPackage = {
|
|
62
|
+
package_id: `pkg_${Date.now()}`,
|
|
63
|
+
creator_account_id: ctx.auth.account_id,
|
|
64
|
+
owner_org_id: ctx.auth.personal_org_id,
|
|
65
|
+
owner_github_username: ctx.auth.github_username,
|
|
66
|
+
is_source_from_github: false,
|
|
67
|
+
description: description,
|
|
68
|
+
name: `${ctx.auth.github_username}/${unscoped_name}`,
|
|
69
|
+
unscoped_name: unscoped_name,
|
|
70
|
+
latest_version: "0.0.1",
|
|
71
|
+
license: null,
|
|
72
|
+
star_count: 0,
|
|
73
|
+
created_at: new Date().toISOString(),
|
|
74
|
+
updated_at: new Date().toISOString(),
|
|
75
|
+
ai_description: null,
|
|
76
|
+
is_snippet: true,
|
|
77
|
+
is_board: snippet_type === "board",
|
|
78
|
+
is_package: snippet_type === "package",
|
|
79
|
+
is_model: snippet_type === "model",
|
|
80
|
+
is_footprint: snippet_type === "footprint",
|
|
81
|
+
snippet_type: snippet_type,
|
|
82
|
+
is_private: false,
|
|
83
|
+
is_public: true,
|
|
84
|
+
is_unlisted: false,
|
|
85
|
+
latest_package_release_id: "",
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
ctx.db.addPackage(newPackage)
|
|
89
|
+
|
|
90
|
+
const newPackageRelease = {
|
|
91
|
+
package_release_id: `package_release_${Date.now()}`,
|
|
92
|
+
package_id: newPackage.package_id,
|
|
93
|
+
version: "0.0.1",
|
|
94
|
+
is_latest: true,
|
|
95
|
+
is_locked: false,
|
|
96
|
+
created_at: newPackage.created_at,
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
ctx.db.addPackageRelease(newPackageRelease)
|
|
100
|
+
|
|
101
|
+
// Update the package with the release ID
|
|
102
|
+
newPackage.latest_package_release_id = newPackageRelease.package_release_id
|
|
103
|
+
|
|
104
|
+
// Add package files
|
|
105
|
+
// Add index.tsx file with the code content
|
|
106
|
+
ctx.db.addPackageFile({
|
|
107
|
+
package_release_id: newPackageRelease.package_release_id,
|
|
108
|
+
file_path: "index.tsx",
|
|
109
|
+
content_text: code,
|
|
110
|
+
created_at: new Date().toISOString(),
|
|
111
|
+
})
|
|
112
|
+
|
|
113
|
+
// Add DTS file if provided
|
|
114
|
+
if (dts) {
|
|
115
|
+
ctx.db.addPackageFile({
|
|
116
|
+
package_release_id: newPackageRelease.package_release_id,
|
|
117
|
+
file_path: "/dist/index.d.ts",
|
|
118
|
+
content_text: dts,
|
|
119
|
+
created_at: new Date().toISOString(),
|
|
120
|
+
})
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
// Add compiled JS if provided
|
|
124
|
+
if (compiled_js) {
|
|
125
|
+
ctx.db.addPackageFile({
|
|
126
|
+
package_release_id: newPackageRelease.package_release_id,
|
|
127
|
+
file_path: "/dist/index.js",
|
|
128
|
+
content_text: compiled_js,
|
|
129
|
+
created_at: new Date().toISOString(),
|
|
130
|
+
})
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
// Add circuit JSON if provided
|
|
134
|
+
if (circuit_json) {
|
|
135
|
+
ctx.db.addPackageFile({
|
|
136
|
+
package_release_id: newPackageRelease.package_release_id,
|
|
137
|
+
file_path: "/dist/circuit.json",
|
|
138
|
+
content_text: JSON.stringify(circuit_json),
|
|
139
|
+
created_at: new Date().toISOString(),
|
|
140
|
+
})
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
// Create the snippet response object
|
|
144
|
+
const snippetResponse = {
|
|
145
|
+
snippet_id: newPackage.package_id,
|
|
146
|
+
package_release_id: newPackageRelease.package_release_id,
|
|
147
|
+
name: newPackage.name,
|
|
148
|
+
unscoped_name: newPackage.unscoped_name,
|
|
149
|
+
owner_name: ctx.auth.github_username,
|
|
150
|
+
code,
|
|
151
|
+
dts,
|
|
152
|
+
compiled_js,
|
|
153
|
+
star_count: 0,
|
|
154
|
+
created_at: newPackage.created_at,
|
|
155
|
+
updated_at: newPackage.updated_at,
|
|
156
|
+
snippet_type: snippet_type,
|
|
157
|
+
circuit_json: circuit_json,
|
|
158
|
+
description: description,
|
|
159
|
+
is_starred: false,
|
|
160
|
+
version: "0.0.1",
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
return ctx.json({
|
|
164
|
+
ok: true,
|
|
165
|
+
snippet: snippetResponse,
|
|
166
|
+
})
|
|
68
167
|
} catch (error) {
|
|
69
168
|
return ctx.error(500, {
|
|
70
169
|
error_code: "snippet_creation_failed",
|
|
71
170
|
message: `Failed to create snippet: ${error}`,
|
|
72
171
|
})
|
|
73
172
|
}
|
|
74
|
-
|
|
75
|
-
return ctx.json({
|
|
76
|
-
ok: true,
|
|
77
|
-
snippet: newSnippet as any,
|
|
78
|
-
})
|
|
79
173
|
})
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tscircuit/fake-snippets",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.8",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -146,6 +146,7 @@
|
|
|
146
146
|
"@vitejs/plugin-react": "^4.3.1",
|
|
147
147
|
"autoprefixer": "^10.4.20",
|
|
148
148
|
"circuit-to-svg": "^0.0.101",
|
|
149
|
+
"get-port": "^7.1.0",
|
|
149
150
|
"globals": "^15.9.0",
|
|
150
151
|
"postcss": "^8.4.47",
|
|
151
152
|
"prismjs": "^1.29.0",
|