Compare commits

...

5 Commits

28 changed files with 120 additions and 176 deletions

View File

@ -11,9 +11,6 @@
}, },
{ {
"path": "packages/cli" "path": "packages/cli"
},
{
"path": "packages/shared"
} }
], ],
"settings": { "settings": {

View File

@ -261,7 +261,7 @@ dependencies = [
[[package]] [[package]]
name = "cryptgeon" name = "cryptgeon"
version = "2.8.0" version = "2.8.1"
dependencies = [ dependencies = [
"axum", "axum",
"bs62", "bs62",

View File

@ -1,6 +1,6 @@
[package] [package]
name = "cryptgeon" name = "cryptgeon"
version = "2.8.0" version = "2.8.1"
authors = ["cupcakearmy <hi@nicco.io>"] authors = ["cupcakearmy <hi@nicco.io>"]
edition = "2021" edition = "2021"
rust-version = "1.80" rust-version = "1.80"

View File

@ -1,13 +1,14 @@
import pkg from './package.json' with { type: 'json' }
import { build } from 'tsup' import { build } from 'tsup'
import pkg from './package.json' with { type: 'json' }
const watch = process.argv.slice(2)[0] === '--watch' const watch = process.argv.slice(2)[0] === '--watch'
await build({ await build({
entry: ['src/index.ts', 'src/cli.ts'], entry: ['src/index.ts', 'src/cli.ts', 'src/shared/shared.ts'],
dts: true, dts: true,
minify: true, minify: true,
format: ['esm', 'cjs'], format: ['esm', 'cjs'],
target: 'es2020',
clean: true, clean: true,
define: { VERSION: `"${pkg.version}"` }, define: { VERSION: `"${pkg.version}"` },
watch, watch,

View File

@ -1,6 +1,6 @@
{ {
"name": "cryptgeon", "name": "cryptgeon",
"version": "2.8.0", "version": "2.8.1",
"homepage": "https://github.com/cupcakearmy/cryptgeon", "homepage": "https://github.com/cupcakearmy/cryptgeon",
"repository": { "repository": {
"type": "git", "type": "git",
@ -9,7 +9,11 @@
}, },
"type": "module", "type": "module",
"exports": { "exports": {
".": "./dist/index.js" ".": "./dist/index.js",
"./shared": {
"import": "./dist/shared/shared.js",
"types": "./dist/shared/shared.d.ts"
}
}, },
"types": "./dist/index.d.ts", "types": "./dist/index.d.ts",
"bin": { "bin": {
@ -25,15 +29,14 @@
"prepublishOnly": "run-s build" "prepublishOnly": "run-s build"
}, },
"devDependencies": { "devDependencies": {
"@commander-js/extra-typings": "^12.0.1", "@commander-js/extra-typings": "^12.1.0",
"@cryptgeon/shared": "workspace:*",
"@types/inquirer": "^9.0.7", "@types/inquirer": "^9.0.7",
"@types/mime": "^3.0.4", "@types/mime": "^4.0.0",
"@types/node": "^20.11.24", "@types/node": "^20.11.24",
"commander": "^12.0.0", "commander": "^12.1.0",
"inquirer": "^9.2.15", "inquirer": "^9.2.15",
"mime": "^4.0.1", "mime": "^4.0.1",
"occulto": "^2.0.3", "occulto": "^2.0.6",
"pretty-bytes": "^6.1.1", "pretty-bytes": "^6.1.1",
"tsup": "^8.2.4", "tsup": "^8.2.4",
"typescript": "^5.3.3" "typescript": "^5.3.3"

View File

@ -1,14 +1,15 @@
import { Adapters, get, info, setOptions } from '@cryptgeon/shared'
import inquirer from 'inquirer' import inquirer from 'inquirer'
import { access, constants, writeFile } from 'node:fs/promises' import { access, constants, writeFile } from 'node:fs/promises'
import { basename, resolve } from 'node:path' import { basename, resolve } from 'node:path'
import { AES, Hex } from 'occulto' import { AES, Hex } from 'occulto'
import pretty from 'pretty-bytes' import pretty from 'pretty-bytes'
import { Adapters } from '../shared/adapters.js'
import { API } from '../shared/api.js'
export async function download(url: URL, all: boolean, suggestedPassword?: string) { export async function download(url: URL, all: boolean, suggestedPassword?: string) {
setOptions({ server: url.origin }) API.setOptions({ server: url.origin })
const id = url.pathname.split('/')[2] const id = url.pathname.split('/')[2]
const preview = await info(id).catch(() => { const preview = await API.info(id).catch(() => {
throw new Error('Note does not exist or is expired') throw new Error('Note does not exist or is expired')
}) })
@ -33,7 +34,7 @@ export async function download(url: URL, all: boolean, suggestedPassword?: strin
} }
const key = derivation ? (await AES.derive(password, derivation))[0] : Hex.decode(password) const key = derivation ? (await AES.derive(password, derivation))[0] : Hex.decode(password)
const note = await get(id) const note = await API.get(id)
const couldNotDecrypt = new Error('Could not decrypt note. Probably an invalid password') const couldNotDecrypt = new Error('Could not decrypt note. Probably an invalid password')
switch (note.meta.type) { switch (note.meta.type) {

View File

@ -1,9 +1,10 @@
import { readFile, stat } from 'node:fs/promises' import { readFile, stat } from 'node:fs/promises'
import { basename } from 'node:path' import { basename } from 'node:path'
import { Adapters, create, getOptions, FileDTO, Note, NoteMeta } from '@cryptgeon/shared'
import mime from 'mime' import mime from 'mime'
import { AES, Hex } from 'occulto' import { AES, Hex } from 'occulto'
import { Adapters } from '../shared/adapters.js'
import { API, FileDTO, Note, NoteMeta } from '../shared/api.js'
export type UploadOptions = Pick<Note, 'views' | 'expiration'> & { password?: string } export type UploadOptions = Pick<Note, 'views' | 'expiration'> & { password?: string }
@ -38,8 +39,8 @@ export async function upload(input: string | string[], options: UploadOptions):
// Create the actual note and upload it. // Create the actual note and upload it.
const note: Note = { ...noteOptions, contents, meta: { type, derivation: derived?.[1] } } const note: Note = { ...noteOptions, contents, meta: { type, derivation: derived?.[1] } }
const result = await create(note) const result = await API.create(note)
let url = `${getOptions().server}/note/${result.id}` let url = `${API.getOptions().server}/note/${result.id}`
if (!derived) url += `#${Hex.encode(key)}` if (!derived) url += `#${Hex.encode(key)}`
return url return url
} }

View File

@ -1,14 +1,14 @@
#!/usr/bin/env node #!/usr/bin/env node
import { Argument, Option, program } from '@commander-js/extra-typings' import { Argument, Option, program } from '@commander-js/extra-typings'
import { setOptions, status } from '@cryptgeon/shared'
import prettyBytes from 'pretty-bytes' import prettyBytes from 'pretty-bytes'
import { download } from './download.js' import { download } from './actions/download.js'
import { parseFile, parseNumber } from './parsers.js' import { upload } from './actions/upload.js'
import { getStdin } from './stdin.js' import { API } from './shared/api.js'
import { upload } from './upload.js' import { parseFile, parseNumber } from './utils/parsers.js'
import { checkConstrains, exit } from './utils.js' import { getStdin } from './utils/stdin.js'
import { checkConstrains, exit } from './utils/utils.js'
const defaultServer = process.env['CRYPTGEON_SERVER'] || 'https://cryptgeon.org' const defaultServer = process.env['CRYPTGEON_SERVER'] || 'https://cryptgeon.org'
const server = new Option('-s --server <url>', 'the cryptgeon server to use').default(defaultServer) const server = new Option('-s --server <url>', 'the cryptgeon server to use').default(defaultServer)
@ -33,8 +33,8 @@ program
.description('show information about the server') .description('show information about the server')
.addOption(server) .addOption(server)
.action(async (options) => { .action(async (options) => {
setOptions({ server: options.server }) API.setOptions({ server: options.server })
const response = await status() const response = await API.status()
const formatted = { const formatted = {
...response, ...response,
max_size: prettyBytes(response.max_size), max_size: prettyBytes(response.max_size),
@ -54,7 +54,7 @@ send
.addOption(minutes) .addOption(minutes)
.addOption(password) .addOption(password)
.action(async (files, options) => { .action(async (files, options) => {
setOptions({ server: options.server }) API.setOptions({ server: options.server })
await checkConstrains(options) await checkConstrains(options)
options.password ||= await getStdin() options.password ||= await getStdin()
try { try {
@ -72,7 +72,7 @@ send
.addOption(minutes) .addOption(minutes)
.addOption(password) .addOption(password)
.action(async (text, options) => { .action(async (text, options) => {
setOptions({ server: options.server }) API.setOptions({ server: options.server })
await checkConstrains(options) await checkConstrains(options)
options.password ||= await getStdin() options.password ||= await getStdin()
try { try {

View File

@ -1,4 +1,4 @@
export * from '@cryptgeon/shared' export * from './actions/download.js'
export * from './download.js' export * from './actions/upload.js'
export * from './upload.js' export * from './shared/adapters.js'
export * from './utils.js' export * from './shared/api.js'

View File

@ -39,15 +39,15 @@ export let client: ClientOptions = {
server: '', server: '',
} }
export function setOptions(options: Partial<ClientOptions>) { function setOptions(options: Partial<ClientOptions>) {
client = { ...client, ...options } client = { ...client, ...options }
} }
export function getOptions(): ClientOptions { function getOptions(): ClientOptions {
return client return client
} }
export async function call(options: CallOptions) { async function call(options: CallOptions) {
const url = client.server + '/api/' + options.url const url = client.server + '/api/' + options.url
const response = await fetch(url, { const response = await fetch(url, {
method: options.method, method: options.method,
@ -65,7 +65,7 @@ export async function call(options: CallOptions) {
return response.json() return response.json()
} }
export async function create(note: Note) { async function create(note: Note) {
const { meta, ...rest } = note const { meta, ...rest } = note
const body: NoteCreate = { const body: NoteCreate = {
...rest, ...rest,
@ -79,7 +79,7 @@ export async function create(note: Note) {
return data as { id: string } return data as { id: string }
} }
export async function get(id: string): Promise<NotePublic> { async function get(id: string): Promise<NotePublic> {
const data = await call({ const data = await call({
url: `notes/${id}`, url: `notes/${id}`,
method: 'delete', method: 'delete',
@ -93,7 +93,7 @@ export async function get(id: string): Promise<NotePublic> {
return note return note
} }
export async function info(id: string): Promise<NoteInfo> { async function info(id: string): Promise<NoteInfo> {
const data = await call({ const data = await call({
url: `notes/${id}`, url: `notes/${id}`,
method: 'get', method: 'get',
@ -112,6 +112,7 @@ export type Status = {
max_views: number max_views: number
max_expiration: number max_expiration: number
allow_advanced: boolean allow_advanced: boolean
allow_files: boolean
theme_image: string theme_image: string
theme_text: string theme_text: string
theme_favicon: string theme_favicon: string
@ -119,10 +120,19 @@ export type Status = {
theme_new_note_notice: boolean theme_new_note_notice: boolean
} }
export async function status() { async function status() {
const data = await call({ const data = await call({
url: 'status/', url: 'status/',
method: 'get', method: 'get',
}) })
return data as Status return data as Status
} }
export const API = {
setOptions,
getOptions,
create,
get,
info,
status,
}

View File

@ -21,7 +21,7 @@ export function parseURL(value: string, _: URL): URL {
} }
export function parseNumber(value: string, _: number): number { export function parseNumber(value: string, _: number): number {
const n = parseInt(value, 10) const n = Number.parseInt(value, 10)
if (isNaN(n)) throw new InvalidOptionArgumentError('invalid number') if (Number.isNaN(n)) throw new InvalidOptionArgumentError('invalid number')
return n return n
} }

View File

@ -18,6 +18,7 @@ export function getStdin(timeout: number = 10): Promise<string> {
resolve('') resolve('')
}, timeout) }, timeout)
process.stdin.on('error', reject)
process.stdin.on('data', dataHandler) process.stdin.on('data', dataHandler)
process.stdin.on('end', endHandler) process.stdin.on('end', endHandler)
}) })

View File

@ -1,5 +1,5 @@
import { status } from '@cryptgeon/shared'
import { exit as exitNode } from 'node:process' import { exit as exitNode } from 'node:process'
import { API } from '../shared/api.js'
export function exit(message: string) { export function exit(message: string) {
console.error(message) console.error(message)
@ -11,7 +11,7 @@ export async function checkConstrains(constrains: { views?: number; minutes?: nu
if (views && minutes) exit('cannot set view and minutes constrains simultaneously') if (views && minutes) exit('cannot set view and minutes constrains simultaneously')
if (!views && !minutes) constrains.views = 1 if (!views && !minutes) constrains.views = 1
const response = await status() const response = await API.status()
if (views && views > response.max_views) if (views && views > response.max_views)
exit(`Only a maximum of ${response.max_views} views allowed. ${views} given.`) exit(`Only a maximum of ${response.max_views} views allowed. ${views} given.`)
if (minutes && minutes > response.max_expiration) if (minutes && minutes > response.max_expiration)

View File

@ -2,7 +2,7 @@
"compilerOptions": { "compilerOptions": {
"target": "es2022", "target": "es2022",
"module": "es2022", "module": "es2022",
"moduleResolution": "node", "moduleResolution": "Bundler",
"declaration": true, "declaration": true,
"emitDeclarationOnly": true, "emitDeclarationOnly": true,
"strict": true, "strict": true,

View File

@ -17,7 +17,6 @@
"@sveltejs/adapter-static": "^3.0.1", "@sveltejs/adapter-static": "^3.0.1",
"@sveltejs/kit": "^2.5.2", "@sveltejs/kit": "^2.5.2",
"@sveltejs/vite-plugin-svelte": "^3.0.2", "@sveltejs/vite-plugin-svelte": "^3.0.2",
"@types/file-saver": "^2.0.7",
"@zerodevx/svelte-toast": "^0.9.5", "@zerodevx/svelte-toast": "^0.9.5",
"adm-zip": "^0.5.10", "adm-zip": "^0.5.10",
"dotenv": "^16.4.5", "dotenv": "^16.4.5",
@ -29,11 +28,9 @@
"vite": "^5.1.7" "vite": "^5.1.7"
}, },
"dependencies": { "dependencies": {
"@cryptgeon/shared": "workspace:*", "cryptgeon": "workspace:*",
"@fontsource/fira-mono": "^5.0.8", "@fontsource/fira-mono": "^5.0.8",
"copy-to-clipboard": "^3.3.3", "occulto": "^2.0.6",
"file-saver": "^2.0.5",
"occulto": "^2.0.3",
"pretty-bytes": "^6.1.1", "pretty-bytes": "^6.1.1",
"qrious": "^4.0.2" "qrious": "^4.0.2"
} }

View File

@ -1,8 +1,8 @@
import { status as getStatus, type Status } from '@cryptgeon/shared' import { API, type Status } from 'cryptgeon/shared'
import { writable } from 'svelte/store' import { writable } from 'svelte/store'
export const status = writable<null | Status>(null) export const status = writable<null | Status>(null)
export async function init() { export async function init() {
status.set(await getStatus()) status.set(await API.status())
} }

View File

@ -1,11 +1,11 @@
import { toast, type SvelteToastOptions } from '@zerodevx/svelte-toast' import { toast } from '@zerodevx/svelte-toast'
export enum NotifyType { export enum NotifyType {
Success = 'success', Success = 'success',
Error = 'error', Error = 'error',
} }
const themeMapping: Record<NotifyType, SvelteToastOptions['theme']> = { const themeMapping: Record<NotifyType, Record<string, string>> = {
[NotifyType.Success]: { [NotifyType.Success]: {
'--toastBackground': 'var(--ui-clr-primary)', '--toastBackground': 'var(--ui-clr-primary)',
'--toastBarBackground': 'var(--ui-clr-primary-alt)', '--toastBarBackground': 'var(--ui-clr-primary-alt)',
@ -17,7 +17,7 @@ const themeMapping: Record<NotifyType, SvelteToastOptions['theme']> = {
} }
function notifyFN(message: string, type: NotifyType = NotifyType.Success) { function notifyFN(message: string, type: NotifyType = NotifyType.Success) {
const options: SvelteToastOptions = { const options = {
duration: 5_000, duration: 5_000,
theme: { theme: {
...themeMapping[type], ...themeMapping[type],

View File

@ -4,7 +4,7 @@
import { status } from '$lib/stores/status' import { status } from '$lib/stores/status'
import Switch from '$lib/ui/Switch.svelte' import Switch from '$lib/ui/Switch.svelte'
import TextInput from '$lib/ui/TextInput.svelte' import TextInput from '$lib/ui/TextInput.svelte'
import type { Note } from '@cryptgeon/shared' import type { Note } from 'cryptgeon/shared'
export let note: Note export let note: Note
export let timeExpiration = false export let timeExpiration = false

View File

@ -3,7 +3,7 @@
import Button from '$lib/ui/Button.svelte' import Button from '$lib/ui/Button.svelte'
import MaxSize from '$lib/ui/MaxSize.svelte' import MaxSize from '$lib/ui/MaxSize.svelte'
import type { FileDTO } from '@cryptgeon/shared' import type { FileDTO } from 'cryptgeon/shared'
export let label: string = '' export let label: string = ''
export let files: FileDTO[] = [] export let files: FileDTO[] = []

View File

@ -1,16 +1,26 @@
<script lang="ts" context="module"> <script lang="ts" context="module">
export type DecryptedNote = Omit<NotePublic, 'contents'> & { contents: any } export type DecryptedNote = Omit<NotePublic, 'contents'> & { contents: any }
function saveAs(file: File) {
const url = window.URL.createObjectURL(file)
const a = document.createElement('a')
a.style.display = 'none'
a.href = url
a.download = file.name
document.body.appendChild(a)
a.click()
window.URL.revokeObjectURL(url)
a.remove()
}
</script> </script>
<script lang="ts"> <script lang="ts">
import pkg from 'file-saver'
const { saveAs } = pkg
import prettyBytes from 'pretty-bytes' import prettyBytes from 'pretty-bytes'
import { t } from 'svelte-intl-precompile' import { t } from 'svelte-intl-precompile'
import Button from '$lib/ui/Button.svelte' import Button from '$lib/ui/Button.svelte'
import { copy } from '$lib/utils' import { copy } from '$lib/utils'
import type { FileDTO, NotePublic } from '@cryptgeon/shared' import type { FileDTO, NotePublic } from 'cryptgeon/shared'
export let note: DecryptedNote export let note: DecryptedNote

View File

@ -1,11 +1,10 @@
import copyToClipboard from 'copy-to-clipboard'
import { t } from 'svelte-intl-precompile' import { t } from 'svelte-intl-precompile'
import { get } from 'svelte/store' import { get } from 'svelte/store'
import { notify } from './toast' import { notify } from './toast'
export function copy(value: string) { export function copy(value: string) {
copyToClipboard(value) window.navigator.clipboard.writeText(value)
const msg = get(t)('common.copied_to_clipboard') const msg = get(t)('common.copied_to_clipboard')
notify.success(msg) notify.success(msg)
} }

View File

@ -13,8 +13,7 @@
import Result, { type NoteResult } from '$lib/ui/NoteResult.svelte' import Result, { type NoteResult } from '$lib/ui/NoteResult.svelte'
import Switch from '$lib/ui/Switch.svelte' import Switch from '$lib/ui/Switch.svelte'
import TextArea from '$lib/ui/TextArea.svelte' import TextArea from '$lib/ui/TextArea.svelte'
import type { FileDTO, Note } from '@cryptgeon/shared' import { Adapters, API, PayloadToLargeError, type FileDTO, type Note } from 'cryptgeon/shared'
import { Adapters, PayloadToLargeError, create } from '@cryptgeon/shared'
let note: Note = { let note: Note = {
contents: '', contents: '',
@ -77,7 +76,7 @@
else data.views = parseInt(note.views as any) else data.views = parseInt(note.views as any)
loading = $t('common.uploading') loading = $t('common.uploading')
const response = await create(data) const response = await API.create(data)
result = { result = {
id: response.id, id: response.id,
password: customPassword ? undefined : Hex.encode(key), password: customPassword ? undefined : Hex.encode(key),

View File

@ -7,7 +7,7 @@
import Loader from '$lib/ui/Loader.svelte' import Loader from '$lib/ui/Loader.svelte'
import ShowNote, { type DecryptedNote } from '$lib/ui/ShowNote.svelte' import ShowNote, { type DecryptedNote } from '$lib/ui/ShowNote.svelte'
import TextInput from '$lib/ui/TextInput.svelte' import TextInput from '$lib/ui/TextInput.svelte'
import { Adapters, get, info, type NoteMeta } from '@cryptgeon/shared' import { Adapters, API, type NoteMeta } from 'cryptgeon/shared'
import type { PageData } from './$types' import type { PageData } from './$types'
export let data: PageData export let data: PageData
@ -28,7 +28,7 @@
try { try {
loading = $t('common.loading') loading = $t('common.loading')
password = window.location.hash.slice(1) password = window.location.hash.slice(1)
const note = await info(id) const note = await API.info(id)
meta = note.meta meta = note.meta
exists = true exists = true
} catch { } catch {
@ -51,7 +51,7 @@
// Load note // Load note
error = null error = null
loading = $t('common.downloading') loading = $t('common.downloading')
const data = await get(id) const data = await API.get(id)
loading = $t('common.decrypting') loading = $t('common.decrypting')
const derived = meta?.derivation && (await AES.derive(password!, meta.derivation)) const derived = meta?.derivation && (await AES.derive(password!, meta.derivation))
const key = derived ? derived[0] : Hex.decode(password!) const key = derived ? derived[0] : Hex.decode(password!)

View File

@ -1,22 +0,0 @@
{
"private": true,
"name": "@cryptgeon/shared",
"type": "module",
"types": "./dist/index.d.ts",
"exports": {
".": {
"types": "./dist/index.d.ts",
"import": "./dist/index.js"
}
},
"scripts": {
"dev": "tsc -w",
"build": "tsc"
},
"devDependencies": {
"typescript": "^5.3.3"
},
"dependencies": {
"occulto": "^2.0.3"
}
}

View File

@ -1,12 +0,0 @@
{
"compilerOptions": {
"incremental": true,
"composite": true,
"target": "es2022",
"module": "es2022",
"rootDir": "./src",
"moduleResolution": "node",
"outDir": "./dist",
"strict": true
}
}

103
pnpm-lock.yaml generated
View File

@ -26,23 +26,20 @@ importers:
packages/cli: packages/cli:
devDependencies: devDependencies:
'@commander-js/extra-typings': '@commander-js/extra-typings':
specifier: ^12.0.1 specifier: ^12.1.0
version: 12.0.1(commander@12.0.0) version: 12.1.0(commander@12.1.0)
'@cryptgeon/shared':
specifier: workspace:*
version: link:../shared
'@types/inquirer': '@types/inquirer':
specifier: ^9.0.7 specifier: ^9.0.7
version: 9.0.7 version: 9.0.7
'@types/mime': '@types/mime':
specifier: ^3.0.4 specifier: ^4.0.0
version: 3.0.4 version: 4.0.0
'@types/node': '@types/node':
specifier: ^20.11.24 specifier: ^20.11.24
version: 20.11.24 version: 20.11.24
commander: commander:
specifier: ^12.0.0 specifier: ^12.1.0
version: 12.0.0 version: 12.1.0
inquirer: inquirer:
specifier: ^9.2.15 specifier: ^9.2.15
version: 9.2.15 version: 9.2.15
@ -50,8 +47,8 @@ importers:
specifier: ^4.0.1 specifier: ^4.0.1
version: 4.0.1 version: 4.0.1
occulto: occulto:
specifier: ^2.0.3 specifier: ^2.0.6
version: 2.0.3 version: 2.0.6
pretty-bytes: pretty-bytes:
specifier: ^6.1.1 specifier: ^6.1.1
version: 6.1.1 version: 6.1.1
@ -64,21 +61,15 @@ importers:
packages/frontend: packages/frontend:
dependencies: dependencies:
'@cryptgeon/shared':
specifier: workspace:*
version: link:../shared
'@fontsource/fira-mono': '@fontsource/fira-mono':
specifier: ^5.0.8 specifier: ^5.0.8
version: 5.0.8 version: 5.0.8
copy-to-clipboard: cryptgeon:
specifier: ^3.3.3 specifier: workspace:*
version: 3.3.3 version: link:../cli
file-saver:
specifier: ^2.0.5
version: 2.0.5
occulto: occulto:
specifier: ^2.0.3 specifier: ^2.0.6
version: 2.0.3 version: 2.0.6
pretty-bytes: pretty-bytes:
specifier: ^6.1.1 specifier: ^6.1.1
version: 6.1.1 version: 6.1.1
@ -98,9 +89,6 @@ importers:
'@sveltejs/vite-plugin-svelte': '@sveltejs/vite-plugin-svelte':
specifier: ^3.0.2 specifier: ^3.0.2
version: 3.0.2(svelte@4.2.12)(vite@5.1.7(@types/node@22.5.0)) version: 3.0.2(svelte@4.2.12)(vite@5.1.7(@types/node@22.5.0))
'@types/file-saver':
specifier: ^2.0.7
version: 2.0.7
'@zerodevx/svelte-toast': '@zerodevx/svelte-toast':
specifier: ^0.9.5 specifier: ^0.9.5
version: 0.9.5(svelte@4.2.12) version: 0.9.5(svelte@4.2.12)
@ -135,16 +123,6 @@ importers:
specifier: ^1.18.1 specifier: ^1.18.1
version: 1.18.1 version: 1.18.1
packages/shared:
dependencies:
occulto:
specifier: ^2.0.3
version: 2.0.3
devDependencies:
typescript:
specifier: ^5.3.3
version: 5.3.3
packages: packages:
'@ampproject/remapping@2.3.0': '@ampproject/remapping@2.3.0':
@ -242,10 +220,10 @@ packages:
resolution: {integrity: sha512-+j7a5c253RfKh8iABBhywc8NSfP5LURe7Uh4qpsh6jc+aLJguvmIUBdjSdEMQv2bENrCR5MfRdjGo7vzS/ob7w==} resolution: {integrity: sha512-+j7a5c253RfKh8iABBhywc8NSfP5LURe7Uh4qpsh6jc+aLJguvmIUBdjSdEMQv2bENrCR5MfRdjGo7vzS/ob7w==}
engines: {node: '>=6.9.0'} engines: {node: '>=6.9.0'}
'@commander-js/extra-typings@12.0.1': '@commander-js/extra-typings@12.1.0':
resolution: {integrity: sha512-OvkMobb1eMqOCuJdbuSin/KJkkZr7n24/UNV+Lcz/0Dhepf3r2p9PaGwpRpAWej7A+gQnny4h8mGhpFl4giKkg==} resolution: {integrity: sha512-wf/lwQvWAA0goIghcb91dQYpkLBcyhOhQNqG/VgWhnKzgt+UOMvra7EX/2fv70arm5RW+PUHoQHHDa6/p77Eqg==}
peerDependencies: peerDependencies:
commander: ~12.0.0 commander: ~12.1.0
'@esbuild/aix-ppc64@0.19.12': '@esbuild/aix-ppc64@0.19.12':
resolution: {integrity: sha512-bmoCYyWdEL3wDQIVbcyzRyeKLgk2WtWLTWz1ZIAZF/EGbNOwSA6ew3PftJ1PqMiOOGu0OyFMzG53L0zqIpPeNA==} resolution: {integrity: sha512-bmoCYyWdEL3wDQIVbcyzRyeKLgk2WtWLTWz1ZIAZF/EGbNOwSA6ew3PftJ1PqMiOOGu0OyFMzG53L0zqIpPeNA==}
@ -778,14 +756,12 @@ packages:
'@types/estree@1.0.5': '@types/estree@1.0.5':
resolution: {integrity: sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==} resolution: {integrity: sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==}
'@types/file-saver@2.0.7':
resolution: {integrity: sha512-dNKVfHd/jk0SkR/exKGj2ggkB45MAkzvWCaqLUUgkyjITkGNzH8H+yUwr+BLJUBjZOe9w8X3wgmXhZDRg1ED6A==}
'@types/inquirer@9.0.7': '@types/inquirer@9.0.7':
resolution: {integrity: sha512-Q0zyBupO6NxGRZut/JdmqYKOnN95Eg5V8Csg3PGKkP+FnvsUZx1jAyK7fztIszxxMuoBA6E3KXWvdZVXIpx60g==} resolution: {integrity: sha512-Q0zyBupO6NxGRZut/JdmqYKOnN95Eg5V8Csg3PGKkP+FnvsUZx1jAyK7fztIszxxMuoBA6E3KXWvdZVXIpx60g==}
'@types/mime@3.0.4': '@types/mime@4.0.0':
resolution: {integrity: sha512-iJt33IQnVRkqeqC7PzBHPTC6fDlRNRW8vjrgqtScAhrmMwe8c4Eo7+fUGTa+XdWrpEgpyKWMYmi2dIwMAYRzPw==} resolution: {integrity: sha512-5eEkJZ/BLvTE3vXGKkWlyTSUVZuzj23Wj8PoyOq2lt5I3CYbiLBOPb3XmCW6QcuOibIUE6emHXHt9E/F/rCa6w==}
deprecated: This is a stub types definition. mime provides its own type definitions, so you do not need this installed.
'@types/node@20.11.24': '@types/node@20.11.24':
resolution: {integrity: sha512-Kza43ewS3xoLgCEpQrsT+xRo/EJej1y0kVYGiLFE1NEODXGzTfwiC6tXTLMQskn1X4/Rjlh0MQUvx9W+L9long==} resolution: {integrity: sha512-Kza43ewS3xoLgCEpQrsT+xRo/EJej1y0kVYGiLFE1NEODXGzTfwiC6tXTLMQskn1X4/Rjlh0MQUvx9W+L9long==}
@ -980,8 +956,8 @@ packages:
color-name@1.1.4: color-name@1.1.4:
resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==}
commander@12.0.0: commander@12.1.0:
resolution: {integrity: sha512-MwVNWlYjDTtOjX5PiD7o5pK0UrFU/OYgcJfjjK4RaHZETNtjJqrZa9Y9ds88+A+f+d5lv+561eZ+yCKoS3gbAA==} resolution: {integrity: sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA==}
engines: {node: '>=18'} engines: {node: '>=18'}
commander@4.1.1: commander@4.1.1:
@ -1002,9 +978,6 @@ packages:
resolution: {integrity: sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw==} resolution: {integrity: sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw==}
engines: {node: '>= 0.6'} engines: {node: '>= 0.6'}
copy-to-clipboard@3.3.3:
resolution: {integrity: sha512-2KV8NhB5JqC3ky0r9PMCAZKbUHSwtEo4CwCs0KXgruG43gX5PMqDEBbVU4OUzw2MuAWUfsuFmWvEKG5QRfSnJA==}
cross-spawn@6.0.5: cross-spawn@6.0.5:
resolution: {integrity: sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==} resolution: {integrity: sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==}
engines: {node: '>=4.8'} engines: {node: '>=4.8'}
@ -1153,9 +1126,6 @@ packages:
resolution: {integrity: sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg==} resolution: {integrity: sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg==}
engines: {node: '>=8'} engines: {node: '>=8'}
file-saver@2.0.5:
resolution: {integrity: sha512-P9bmyZ3h/PRG+Nzga+rbdI4OEpNDzAVyy74uVO9ATgzLK6VtAsYybF/+TOCvrc0MO793d6+42lLyZTw7/ArVzA==}
fill-range@7.0.1: fill-range@7.0.1:
resolution: {integrity: sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==} resolution: {integrity: sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==}
engines: {node: '>=8'} engines: {node: '>=8'}
@ -1618,9 +1588,9 @@ packages:
resolution: {integrity: sha512-byy+U7gp+FVwmyzKPYhW2h5l3crpmGsxl7X2s8y43IgxvG4g3QZ6CffDtsNQy1WsmZpQbO+ybo0AlW7TY6DcBQ==} resolution: {integrity: sha512-byy+U7gp+FVwmyzKPYhW2h5l3crpmGsxl7X2s8y43IgxvG4g3QZ6CffDtsNQy1WsmZpQbO+ybo0AlW7TY6DcBQ==}
engines: {node: '>= 0.4'} engines: {node: '>= 0.4'}
occulto@2.0.3: occulto@2.0.6:
resolution: {integrity: sha512-rLXDNqsxM3Gusp4cn8QsveeKsmX36SbnIUcUc3mSeM88pGsNY5Tn6VnWItw/7zST01z9gtaZWQFk0F1L53jMCQ==} resolution: {integrity: sha512-oaCwtnQjr+fTfFVfSPEDC5rh+L13OcEJ6uQzhmG8PlLxxYn2MxTDPmoCUVHKD7rUxwxCpH7/N4hbTu5U4mqZag==}
engines: {node: '>=16', npm: please-use-pnpm, pnpm: '>=8', yarn: please-use-pnpm} engines: {node: '>=18'}
once@1.4.0: once@1.4.0:
resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==} resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==}
@ -2079,9 +2049,6 @@ packages:
resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==}
engines: {node: '>=8.0'} engines: {node: '>=8.0'}
toggle-selection@1.0.6:
resolution: {integrity: sha512-BiZS+C1OS8g/q2RRbJmy59xpyghNBqrr6k5L/uKBGRsTfxmu3ffiRnd8mlGPUVayg8pvfi5urfnu8TU7DVOkLQ==}
totalist@3.0.1: totalist@3.0.1:
resolution: {integrity: sha512-sf4i37nQ2LBx4m3wB74y+ubopq6W/dIzXg0FDGjsYnZHVa1Da8FH853wlL2gtUhg+xJXjfk3kUZS3BRoQeoQBQ==} resolution: {integrity: sha512-sf4i37nQ2LBx4m3wB74y+ubopq6W/dIzXg0FDGjsYnZHVa1Da8FH853wlL2gtUhg+xJXjfk3kUZS3BRoQeoQBQ==}
engines: {node: '>=6'} engines: {node: '>=6'}
@ -2377,9 +2344,9 @@ snapshots:
'@babel/helper-validator-identifier': 7.22.20 '@babel/helper-validator-identifier': 7.22.20
to-fast-properties: 2.0.0 to-fast-properties: 2.0.0
'@commander-js/extra-typings@12.0.1(commander@12.0.0)': '@commander-js/extra-typings@12.1.0(commander@12.1.0)':
dependencies: dependencies:
commander: 12.0.0 commander: 12.1.0
'@esbuild/aix-ppc64@0.19.12': '@esbuild/aix-ppc64@0.19.12':
optional: true optional: true
@ -2733,14 +2700,14 @@ snapshots:
'@types/estree@1.0.5': {} '@types/estree@1.0.5': {}
'@types/file-saver@2.0.7': {}
'@types/inquirer@9.0.7': '@types/inquirer@9.0.7':
dependencies: dependencies:
'@types/through': 0.0.33 '@types/through': 0.0.33
rxjs: 7.8.1 rxjs: 7.8.1
'@types/mime@3.0.4': {} '@types/mime@4.0.0':
dependencies:
mime: 4.0.1
'@types/node@20.11.24': '@types/node@20.11.24':
dependencies: dependencies:
@ -2942,7 +2909,7 @@ snapshots:
color-name@1.1.4: {} color-name@1.1.4: {}
commander@12.0.0: {} commander@12.1.0: {}
commander@4.1.1: {} commander@4.1.1: {}
@ -2954,10 +2921,6 @@ snapshots:
cookie@0.6.0: {} cookie@0.6.0: {}
copy-to-clipboard@3.3.3:
dependencies:
toggle-selection: 1.0.6
cross-spawn@6.0.5: cross-spawn@6.0.5:
dependencies: dependencies:
nice-try: 1.0.5 nice-try: 1.0.5
@ -3190,8 +3153,6 @@ snapshots:
dependencies: dependencies:
escape-string-regexp: 1.0.5 escape-string-regexp: 1.0.5
file-saver@2.0.5: {}
fill-range@7.0.1: fill-range@7.0.1:
dependencies: dependencies:
to-regex-range: 5.0.1 to-regex-range: 5.0.1
@ -3623,7 +3584,7 @@ snapshots:
has-symbols: 1.0.3 has-symbols: 1.0.3
object-keys: 1.1.1 object-keys: 1.1.1
occulto@2.0.3: {} occulto@2.0.6: {}
once@1.4.0: once@1.4.0:
dependencies: dependencies:
@ -4117,8 +4078,6 @@ snapshots:
dependencies: dependencies:
is-number: 7.0.0 is-number: 7.0.0
toggle-selection@1.0.6: {}
totalist@3.0.1: {} totalist@3.0.1: {}
tr46@1.0.1: tr46@1.0.1: