feat: add carbone-sdk dependency and create initial CarboneClient implementation
Supa-backup / run_db_backup (push) Successful in 38s

- Added carbone-sdk@^1.6.0 to package.json and deno.lock
- Created a new notebook (carbone.ipynb) with CarboneClient class for interacting with the Carbone API
- Implemented methods for listing templates, categories, tags, uploading templates, and rendering documents
This commit is contained in:
2026-03-13 11:09:22 -06:00
parent 55fd3f3886
commit 357c244f2a
3 changed files with 598 additions and 1 deletions
Generated
+51 -1
View File
@@ -23,6 +23,7 @@
"npm:@supabase/supabase-js@^2.90.1": "2.90.1",
"npm:@toon-format/toon@^2.1.0": "2.1.0",
"npm:@types/bun@^1.3.5": "1.3.5",
"npm:carbone-sdk@^1.6.0": "1.6.0",
"npm:citeproc@^2.4.63": "2.4.63",
"npm:deno@^2.6.4": "2.6.4",
"npm:jwt-decode@4": "4.0.0",
@@ -202,7 +203,7 @@
"integrity": "sha512-QGpRVpzSaUs30JBSGPjOg4Uveu384erbHBoT1zeONvyCfwQxIkUshLAOqN/k9EjGviPRmWTTe6aH2qySWKTVSw==",
"dependencies": [
"@types/node",
"form-data"
"form-data@4.0.5"
]
},
"@types/node@18.19.130": {
@@ -261,6 +262,14 @@
"function-bind"
]
},
"carbone-sdk@1.6.0": {
"integrity": "sha512-fo/dPMwWnaCDK8sqwpkQbm7IiRpVKwRJu5gkVZj2gOQe6XyY6U6/N43c5bnA13GD9YxeI3WrWmj+rTbYlw8lEg==",
"dependencies": [
"form-data@4.0.0",
"kitten-cache",
"simple-get"
]
},
"chownr@3.0.0": {
"integrity": "sha512-+IxzY9BZOQd/XuYPRmrvEVjF/nqj5kgT4kEq7VofrDoM1MxoRjEWkrCC3EtLi59TVawxTAn+orJwFQcrqEN1+g=="
},
@@ -285,6 +294,12 @@
"ms"
]
},
"decompress-response@6.0.0": {
"integrity": "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==",
"dependencies": [
"mimic-response"
]
},
"delayed-stream@1.0.0": {
"integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ=="
},
@@ -343,6 +358,14 @@
"form-data-encoder@1.7.2": {
"integrity": "sha512-qfqtYan3rxrnCk1VYaA4H+Ms9xdpPqvLZa6xmMgFvhO32x7/3J/ExcTd6qpxM0vH2GdMI+poehyBZvqfMTto8A=="
},
"form-data@4.0.0": {
"integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==",
"dependencies": [
"asynckit",
"combined-stream",
"mime-types"
]
},
"form-data@4.0.5": {
"integrity": "sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w==",
"dependencies": [
@@ -431,6 +454,9 @@
"jwt-decode@4.0.0": {
"integrity": "sha512-+KJGIyHgkGuIq3IEBNftfhW/LfWhXUIY6OmyVWjliu5KH1y0fw7VQ8YndE2O4qZdMSd9SqbnC8GOcZEy0Om7sA=="
},
"kitten-cache@0.3.0": {
"integrity": "sha512-PFu7PEmbDSFehdsGXz7JtE1L5fFktV+apTcmyamWPo5IuDnFaTc5dT24JlrgB2RzYclLunimWUX5i3vBkPfv9w=="
},
"math-intrinsics@1.1.0": {
"integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g=="
},
@@ -443,6 +469,9 @@
"mime-db"
]
},
"mimic-response@3.1.0": {
"integrity": "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ=="
},
"minipass@7.1.2": {
"integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw=="
},
@@ -476,6 +505,12 @@
"npm-normalize-package-bin@5.0.0": {
"integrity": "sha512-CJi3OS4JLsNMmr2u07OJlhcrPxCeOeP/4xq67aWNai6TNWWbTrlNDgl8NcFKVlcBKp18GPj+EzbNIgrBfZhsag=="
},
"once@1.4.0": {
"integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==",
"dependencies": [
"wrappy"
]
},
"openai@4.104.0_zod@3.25.76": {
"integrity": "sha512-p99EFNsA/yX6UhVO93f5kJsDRLAg+CTA2RBqdHK4RtK8u5IJw32Hyb2dTGKbnnFmnuoBv5r7Z2CURI9sGZpSuA==",
"dependencies": [
@@ -512,6 +547,17 @@
"signal-exit@4.1.0": {
"integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw=="
},
"simple-concat@1.0.1": {
"integrity": "sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q=="
},
"simple-get@4.0.1": {
"integrity": "sha512-brv7p5WgH0jmQJr1ZDDfKDOSeWWg+OVypG99A/5vYGPqJ6pxiaHLy8nxtFjBA7oMa01ebA9gfh1uMCFqOuXxvA==",
"dependencies": [
"decompress-response",
"once",
"simple-concat"
]
},
"supabase@2.75.0": {
"integrity": "sha512-Xaai8Wp7F03OMkOWSyS1OMMdk5DxkauJxcMMesj4mO6jDGEZpWBwgo/gIUb4hsckexmr/RXYdpDpTbUIMXahbw==",
"dependencies": [
@@ -559,6 +605,9 @@
"webidl-conversions"
]
},
"wrappy@1.0.2": {
"integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ=="
},
"write-file-atomic@7.0.0": {
"integrity": "sha512-YnlPC6JqnZl6aO4uRc+dx5PHguiR9S6WeoLtpxNT9wIG+BDya7ZNE1q7KOjVgaA73hKhKLpVPgJ5QA9THQ5BRg==",
"dependencies": [
@@ -608,6 +657,7 @@
"npm:@supabase/supabase-js@^2.90.1",
"npm:@toon-format/toon@^2.1.0",
"npm:@types/bun@^1.3.5",
"npm:carbone-sdk@^1.6.0",
"npm:citeproc@^2.4.63",
"npm:deno@^2.6.4",
"npm:jwt-decode@4",
+546
View File
@@ -0,0 +1,546 @@
{
"cells": [
{
"cell_type": "code",
"execution_count": 8,
"id": "76919cba",
"metadata": {},
"outputs": [],
"source": [
"import { load } from \"jsr:@std/dotenv\";\n",
"\n",
"const env = await load({\n",
" export: true,\n",
"});"
]
},
{
"cell_type": "code",
"execution_count": 1,
"id": "20231839",
"metadata": {},
"outputs": [],
"source": [
"type ListTemplatesParams = {\n",
" id?: string;\n",
" versionId?: string;\n",
" category?: string;\n",
" origin?: 0 | 1;\n",
" includeVersions?: boolean;\n",
" search?: string;\n",
" limit?: number;\n",
" cursor?: string;\n",
"};\n",
"\n",
"class CarboneClient {\n",
" listTemplates(params?: ListTemplatesParams) {}\n",
" listCategories() {}\n",
" listTags() {}\n",
" uploadTemplate(input: {\n",
" file?: Buffer;\n",
" filename?: string;\n",
" template?: string; // base64\n",
" versioning?: boolean;\n",
" id?: string;\n",
" name?: string;\n",
" comment?: string;\n",
" tags?: string[];\n",
" category?: string;\n",
" sample?: any[];\n",
" deployedAt?: number;\n",
" expireAt?: number;\n",
" }) {}\n",
" patchTemplate(id: string, body: {\n",
" name?: string;\n",
" comment?: string;\n",
" tags?: string[];\n",
" category?: string;\n",
" deployedAt?: number;\n",
" expireAt?: number;\n",
" }) {}\n",
" deleteTemplate(id: string) {}\n",
" downloadTemplate(id: string) {}\n",
" render(templateId: string, body: any, opts?: { download?: boolean }) {}\n",
" renderFromTemplate(body: any, opts?: { download?: boolean }) {}\n",
" downloadRender(renderId: string) {}\n",
"}"
]
},
{
"cell_type": "code",
"execution_count": 2,
"id": "9950c1f7",
"metadata": {},
"outputs": [],
"source": [
"const CARBONE_VERSION = \"5\";\n",
"\n",
"type CarboneSuccess<T> = {\n",
" success: true;\n",
" data: T;\n",
" hasMore?: boolean;\n",
" nextCursor?: string;\n",
"};\n",
"\n",
"type CarboneError = {\n",
" success: false;\n",
" error: string;\n",
"};\n",
"\n",
"type CarboneResponse<T> = CarboneSuccess<T> | CarboneError;\n",
"\n",
"export class CarboneClient {\n",
" constructor(\n",
" private baseUrl: string,\n",
" private apiToken: string\n",
" ) {}\n",
"\n",
" private buildHeaders(extra?: Record<string, string>) {\n",
" return {\n",
" Authorization: `Bearer ${this.apiToken}`,\n",
" \"carbone-version\": CARBONE_VERSION,\n",
" ...extra,\n",
" };\n",
" }\n",
"\n",
" private async parseResponse<T>(res: Response): Promise<T> {\n",
" const contentType = res.headers.get(\"content-type\") || \"\";\n",
"\n",
" if (!res.ok) {\n",
" let message = `Carbone request failed: ${res.status}`;\n",
" try {\n",
" const err = await res.json();\n",
" message = err?.error || err?.message || message;\n",
" } catch {}\n",
" throw new Error(message);\n",
" }\n",
"\n",
" if (contentType.includes(\"application/json\")) {\n",
" return res.json() as Promise<T>;\n",
" }\n",
"\n",
" return res as unknown as T;\n",
" }\n",
"\n",
" private toQuery(params?: Record<string, unknown>) {\n",
" const qs = new URLSearchParams();\n",
" if (!params) return \"\";\n",
" for (const [key, value] of Object.entries(params)) {\n",
" if (value === undefined || value === null) continue;\n",
" qs.set(key, String(value));\n",
" }\n",
" const s = qs.toString();\n",
" return s ? `?${s}` : \"\";\n",
" }\n",
"\n",
" async listTemplates(params?: {\n",
" id?: string;\n",
" versionId?: string;\n",
" category?: string;\n",
" origin?: 0 | 1;\n",
" includeVersions?: boolean;\n",
" search?: string;\n",
" limit?: number;\n",
" cursor?: string;\n",
" }) {\n",
" const res = await fetch(\n",
" `${this.baseUrl}/templates${this.toQuery(params)}`,\n",
" {\n",
" method: \"GET\",\n",
" headers: this.buildHeaders(),\n",
" }\n",
" );\n",
" return this.parseResponse<\n",
" CarboneResponse<\n",
" Array<{\n",
" id: string;\n",
" versionId: string;\n",
" deployedAt: number;\n",
" createdAt: number;\n",
" expireAt?: number;\n",
" size: number;\n",
" type: string;\n",
" name?: string;\n",
" category?: string;\n",
" comment?: string;\n",
" tags?: string[];\n",
" origin?: number;\n",
" }>\n",
" >\n",
" >(res);\n",
" }\n",
"\n",
" async listCategories() {\n",
" const res = await fetch(`${this.baseUrl}/templates/categories`, {\n",
" method: \"GET\",\n",
" headers: this.buildHeaders(),\n",
" });\n",
" return this.parseResponse<CarboneResponse<Array<{ name: string }>>>(res);\n",
" }\n",
"\n",
" async listTags() {\n",
" const res = await fetch(`${this.baseUrl}/templates/tags`, {\n",
" method: \"GET\",\n",
" headers: this.buildHeaders(),\n",
" });\n",
" return this.parseResponse<CarboneResponse<Array<{ name: string }>>>(res);\n",
" }\n",
"\n",
" async uploadTemplateJson(body: {\n",
" template: string;\n",
" versioning?: boolean;\n",
" id?: string;\n",
" name?: string;\n",
" comment?: string;\n",
" tags?: string[];\n",
" category?: string;\n",
" sample?: any[];\n",
" deployedAt?: number;\n",
" expireAt?: number;\n",
" }) {\n",
" const res = await fetch(`${this.baseUrl}/template`, {\n",
" method: \"POST\",\n",
" headers: this.buildHeaders({\n",
" \"Content-Type\": \"application/json\",\n",
" }),\n",
" body: JSON.stringify(body),\n",
" });\n",
" return this.parseResponse<\n",
" CarboneResponse<{\n",
" id?: string;\n",
" versionId?: string;\n",
" templateId?: string;\n",
" type?: string;\n",
" size?: number;\n",
" createdAt?: number;\n",
" }>\n",
" >(res);\n",
" }\n",
"\n",
" async uploadTemplateFile(input: {\n",
" file: Blob;\n",
" filename: string;\n",
" versioning?: boolean;\n",
" id?: string;\n",
" name?: string;\n",
" comment?: string;\n",
" tags?: string[];\n",
" category?: string;\n",
" sample?: any[];\n",
" deployedAt?: number;\n",
" expireAt?: number;\n",
" }) {\n",
" const form = new FormData();\n",
"\n",
" if (input.versioning !== undefined) {\n",
" form.append(\"versioning\", String(input.versioning));\n",
" }\n",
" if (input.id) form.append(\"id\", input.id);\n",
" if (input.name) form.append(\"name\", input.name);\n",
" if (input.comment) form.append(\"comment\", input.comment);\n",
" if (input.category) form.append(\"category\", input.category);\n",
" if (input.tags) {\n",
" for (const tag of input.tags) form.append(\"tags[]\", tag);\n",
" }\n",
" if (input.sample) form.append(\"sample\", JSON.stringify(input.sample));\n",
" if (input.deployedAt !== undefined) {\n",
" form.append(\"deployedAt\", String(input.deployedAt));\n",
" }\n",
" if (input.expireAt !== undefined) {\n",
" form.append(\"expireAt\", String(input.expireAt));\n",
" }\n",
"\n",
" // template should be appended last\n",
" form.append(\"template\", input.file, input.filename);\n",
"\n",
" const res = await fetch(`${this.baseUrl}/template`, {\n",
" method: \"POST\",\n",
" headers: this.buildHeaders(),\n",
" body: form,\n",
" });\n",
" return this.parseResponse<\n",
" CarboneResponse<{\n",
" id?: string;\n",
" versionId?: string;\n",
" templateId?: string;\n",
" type?: string;\n",
" size?: number;\n",
" createdAt?: number;\n",
" }>\n",
" >(res);\n",
" }\n",
"\n",
" async patchTemplate(\n",
" id: string,\n",
" body: {\n",
" name?: string;\n",
" comment?: string;\n",
" tags?: string[];\n",
" category?: string;\n",
" deployedAt?: number;\n",
" expireAt?: number;\n",
" }\n",
" ) {\n",
" const res = await fetch(`${this.baseUrl}/template/${id}`, {\n",
" method: \"PATCH\",\n",
" headers: this.buildHeaders({\n",
" \"Content-Type\": \"application/json\",\n",
" }),\n",
" body: JSON.stringify(body),\n",
" });\n",
" return this.parseResponse<CarboneResponse<true>>(res);\n",
" }\n",
"\n",
" async deleteTemplate(id: string) {\n",
" const res = await fetch(`${this.baseUrl}/template/${id}`, {\n",
" method: \"DELETE\",\n",
" headers: this.buildHeaders(),\n",
" });\n",
" return this.parseResponse<CarboneResponse<true>>(res);\n",
" }\n",
"\n",
" async downloadTemplate(id: string) {\n",
" const res = await fetch(`${this.baseUrl}/template/${id}`, {\n",
" method: \"GET\",\n",
" headers: this.buildHeaders(),\n",
" });\n",
"\n",
" if (!res.ok) {\n",
" const err = await res.json().catch(() => null);\n",
" throw new Error(err?.error || `Failed to download template: ${res.status}`);\n",
" }\n",
"\n",
" return {\n",
" buffer: Buffer.from(await res.arrayBuffer()),\n",
" contentType: res.headers.get(\"content-type\"),\n",
" contentDisposition: res.headers.get(\"content-disposition\"),\n",
" };\n",
" }\n",
"\n",
" async render(\n",
" templateIdOrVersionId: string,\n",
" body: Record<string, unknown>,\n",
" opts?: { download?: boolean }\n",
" ) {\n",
" const query = opts?.download ? \"?download=true\" : \"\";\n",
" const res = await fetch(\n",
" `${this.baseUrl}/render/${templateIdOrVersionId}${query}`,\n",
" {\n",
" method: \"POST\",\n",
" headers: this.buildHeaders({\n",
" \"Content-Type\": \"application/json\",\n",
" }),\n",
" body: JSON.stringify(body),\n",
" }\n",
" );\n",
"\n",
" if (opts?.download) {\n",
" if (!res.ok) {\n",
" const err = await res.json().catch(() => null);\n",
" throw new Error(err?.error || `Render failed: ${res.status}`);\n",
" }\n",
" return {\n",
" buffer: Buffer.from(await res.arrayBuffer()),\n",
" contentType: res.headers.get(\"content-type\"),\n",
" contentDisposition: res.headers.get(\"content-disposition\"),\n",
" };\n",
" }\n",
"\n",
" return this.parseResponse<CarboneResponse<{ renderId: string }>>(res);\n",
" }\n",
"\n",
" async renderFromTemplate(\n",
" body: Record<string, unknown>,\n",
" opts?: { download?: boolean }\n",
" ) {\n",
" const query = opts?.download ? \"?download=true\" : \"\";\n",
" const res = await fetch(`${this.baseUrl}/render/template${query}`, {\n",
" method: \"POST\",\n",
" headers: this.buildHeaders({\n",
" \"Content-Type\": \"application/json\",\n",
" }),\n",
" body: JSON.stringify(body),\n",
" });\n",
"\n",
" if (opts?.download) {\n",
" if (!res.ok) {\n",
" const err = await res.json().catch(() => null);\n",
" throw new Error(err?.error || `Render failed: ${res.status}`);\n",
" }\n",
" return {\n",
" buffer: Buffer.from(await res.arrayBuffer()),\n",
" contentType: res.headers.get(\"content-type\"),\n",
" contentDisposition: res.headers.get(\"content-disposition\"),\n",
" };\n",
" }\n",
"\n",
" return this.parseResponse<CarboneResponse<{ renderId: string }>>(res);\n",
" }\n",
"\n",
" async downloadRender(renderId: string) {\n",
" const res = await fetch(`${this.baseUrl}/render/${renderId}`, {\n",
" method: \"GET\",\n",
" });\n",
"\n",
" if (!res.ok) {\n",
" const err = await res.json().catch(() => null);\n",
" throw new Error(err?.error || `Failed to download render: ${res.status}`);\n",
" }\n",
"\n",
" return {\n",
" buffer: Buffer.from(await res.arrayBuffer()),\n",
" contentType: res.headers.get(\"content-type\"),\n",
" contentDisposition: res.headers.get(\"content-disposition\"),\n",
" };\n",
" }\n",
"}"
]
},
{
"cell_type": "code",
"execution_count": 17,
"id": "95b24436",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"{\n",
" success: true,\n",
" data: [\n",
" {\n",
" versionId: \"1990f3351b4313779ae3ef3caeaea14dea51c3a98a691fb90a347dec53f875e6\",\n",
" id: \"1373945625988937236\",\n",
" deployedAt: 1773246290,\n",
" createdAt: 1773246316,\n",
" expireAt: 0,\n",
" size: 29008,\n",
" type: \"docx\",\n",
" name: \"Anexo 1\",\n",
" category: \"Planes de estudio\",\n",
" comment: \"Plantilla de anexo 1 SEP RVOE Acuerdo 17-11-17\",\n",
" tags: [],\n",
" origin: 1\n",
" },\n",
" {\n",
" versionId: \"caee5a7804647382f6df62ef92cc8f04c822b6a297479c5068e22d08def685d7\",\n",
" id: \"1373945108764607184\",\n",
" deployedAt: 1773246225,\n",
" createdAt: 1773246251,\n",
" expireAt: 0,\n",
" size: 29642,\n",
" type: \"xlsx\",\n",
" name: \"Anexo 2\",\n",
" category: \"\",\n",
" comment: \"\",\n",
" tags: [],\n",
" origin: 1\n",
" },\n",
" {\n",
" versionId: \"b0ab3c6fe90c73151fcae64d8509e406803381f3966aa88d30d8d30e2b1ffe8f\",\n",
" id: \"1373944894291796699\",\n",
" deployedAt: 1773246204,\n",
" createdAt: 1773246230,\n",
" expireAt: 0,\n",
" size: 23806,\n",
" type: \"docx\",\n",
" name: \"Anexo 3\",\n",
" category: \"\",\n",
" comment: \"\",\n",
" tags: [],\n",
" origin: 1\n",
" }\n",
" ],\n",
" hasMore: false\n",
"}\n",
"{ success: true, data: [ { name: \"Planes de estudio\" } ] }\n",
"{ success: true, data: [] }\n"
]
}
],
"source": [
"const carbone = new CarboneClient(\n",
" \"https://carbone.lci.ulsa.mx\",\n",
" Deno.env.get(\"CARBONE_API_TOKEN\")!\n",
");\n",
"\n",
"const templates = await carbone.listTemplates({ limit: 20 });\n",
"console.log(templates);\n",
"\n",
"const categories = await carbone.listCategories();\n",
"console.log(categories);\n",
"\n",
"const tags = await carbone.listTags();\n",
"console.log(tags);"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "8196fac4",
"metadata": {},
"outputs": [],
"source": [
"const templateID = \"1373945625988937236\";\n"
]
},
{
"cell_type": "code",
"execution_count": 16,
"id": "caf8bc20",
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"{\n",
" success: \u001b[33mtrue\u001b[39m,\n",
" data: {\n",
" category: \u001b[32m\"Planes de estudio\"\u001b[39m,\n",
" comment: \u001b[32m\"Plantilla de anexo 1 SEP RVOE Acuerdo 17-11-17\"\u001b[39m,\n",
" versionId: \u001b[32m\"1990f3351b4313779ae3ef3caeaea14dea51c3a98a691fb90a347dec53f875e6\"\u001b[39m\n",
" }\n",
"}"
]
},
"execution_count": 16,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"await carbone.patchTemplate(\"1373945625988937236\", {\n",
" category: \"Planes de estudio\",\n",
" comment: \"Plantilla de anexo 1 SEP RVOE Acuerdo 17-11-17\",\n",
"});\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "929883b9",
"metadata": {},
"outputs": [],
"source": []
}
],
"metadata": {
"kernelspec": {
"display_name": "Deno",
"language": "typescript",
"name": "deno"
},
"language_info": {
"codemirror_mode": "typescript",
"file_extension": ".ts",
"mimetype": "text/x.typescript",
"name": "typescript",
"nbconvert_exporter": "script",
"pygments_lexer": "typescript",
"version": "5.9.2"
}
},
"nbformat": 4,
"nbformat_minor": 5
}
+1
View File
@@ -2,6 +2,7 @@
"dependencies": {
"@supabase/supabase-js": "^2.90.1",
"@toon-format/toon": "^2.1.0",
"carbone-sdk": "^1.6.0",
"citeproc": "^2.4.63",
"deno": "^2.6.4",
"jwt-decode": "^4.0.0",