flora is a fast and secure runtime that lets you write discord bots for your servers, with a rich TypeScript SDK, without worrying about running infrastructure. [mirror]
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

feat(cli): prebundle deploy uploads with flora config

+562 -24
+2
apps/cli/package.json
··· 24 24 }, 25 25 "dependencies": { 26 26 "@clack/prompts": "^1.0.1", 27 + "c12": "^3.3.2", 27 28 "citty": "^0.2.1", 28 29 "consola": "^3.4.2", 29 30 "conf": "^15.1.0", 30 31 "ignore": "^7.0.5", 31 32 "openapi-fetch": "^0.17.0", 33 + "rolldown": "^1.0.0-beta.43", 32 34 "tinyglobby": "^0.2.15" 33 35 }, 34 36 "devDependencies": {
+22 -13
apps/cli/src/commands/deployments.ts
··· 1 - import path from 'node:path' 2 - 3 - import { collectFiles, toRelative } from '../lib/files' 1 + import { loadProjectConfig } from '../lib/config' 2 + import { bundleDeploymentSource } from '../lib/deploy_bundle' 4 3 import { authHeaders, createApiClient, expectOk } from '../lib/http' 5 4 import { logger } from '../lib/logger' 6 5 import { promptIfMissing } from '../lib/prompts' 7 - import type { CliConfig } from '../lib/types' 6 + import type { CliConfig, DeploySourceMapMode } from '../lib/types' 7 + 8 + export type DeployOverrides = { 9 + root?: string 10 + sourcemap?: DeploySourceMapMode 11 + minify?: boolean 12 + external?: string[] 13 + } 8 14 9 15 export async function deploy( 10 16 config: CliConfig, 11 17 guildArg: string | undefined, 12 18 entryArg: string | undefined, 13 - rootArg?: string 19 + overrides: DeployOverrides = {} 14 20 ): Promise<void> { 15 21 const guild = await promptIfMissing(guildArg, 'Guild ID') 16 - const entryPath = await promptIfMissing(entryArg, 'Entry file path') 22 + const projectConfig = await loadProjectConfig() 17 23 18 - const entry = path.resolve(entryPath) 19 - const root = rootArg ? path.resolve(rootArg) : path.dirname(entry) 20 - 21 - const files = await collectFiles(root) 22 - const entryRel = toRelative(entry, root) 24 + const bundled = await bundleDeploymentSource({ 25 + root: overrides.root ?? projectConfig.root, 26 + entry: entryArg ?? projectConfig.entry, 27 + sourcemap: overrides.sourcemap ?? projectConfig.sourcemap, 28 + minify: overrides.minify ?? projectConfig.minify, 29 + external: overrides.external ?? projectConfig.external 30 + }) 23 31 24 32 const client = createApiClient(config) 25 33 const response = await expectOk( ··· 27 35 params: { path: { guild_id: guild } }, 28 36 headers: authHeaders(config), 29 37 body: { 30 - entry: entryRel, 31 - files 38 + entry: bundled.entry, 39 + bundle: bundled.bundle, 40 + source_map: bundled.sourceMap 32 41 } 33 42 }) 34 43 )
+8 -7
apps/cli/src/generated/openapi-schema.ts
··· 391 391 guild_id: string 392 392 store_name: string 393 393 } 394 - DeploymentFile: { 394 + DeploymentSourceMapFile: { 395 395 contents: string 396 396 path: string 397 397 } 398 398 /** @description Body for creating or replacing a deployment. */ 399 399 DeploymentRequest: { 400 + /** @description Prebuilt JavaScript bundle source. */ 401 + bundle: string 400 402 /** @description Entry point path for the bundle (e.g. src/main.ts). */ 401 403 entry: string 402 - /** @description Files included in this deployment. */ 403 - files: components['schemas']['DeploymentFile'][] 404 + source_map?: components['schemas']['DeploymentSourceMapFile'] | null 404 405 } 405 406 /** @description API representation of a deployment. */ 406 407 DeploymentResponse: { 407 408 bundle?: string | null 408 409 created_at: string 409 410 entry: string 410 - files?: components['schemas']['DeploymentFile'][] | null 411 411 guild_id: string 412 + source_map?: components['schemas']['DeploymentSourceMapFile'] | null 412 413 updated_at: string 413 414 } 414 415 /** @description Canonical error envelope returned by the HTTP API. */ ··· 829 830 bundle?: string | null 830 831 created_at: string 831 832 entry: string 832 - files?: components['schemas']['DeploymentFile'][] | null 833 833 guild_id: string 834 + source_map?: components['schemas']['DeploymentSourceMapFile'] | null 834 835 updated_at: string 835 836 }[] 836 837 } ··· 922 923 bundle?: string | null 923 924 created_at: string 924 925 entry: string 925 - files?: components['schemas']['DeploymentFile'][] | null 926 926 guild_id: string 927 + source_map?: components['schemas']['DeploymentSourceMapFile'] | null 927 928 updated_at: string 928 929 } 929 930 } ··· 1016 1017 bundle?: string | null 1017 1018 created_at: string 1018 1019 entry: string 1019 - files?: components['schemas']['DeploymentFile'][] | null 1020 1020 guild_id: string 1021 + source_map?: components['schemas']['DeploymentSourceMapFile'] | null 1021 1022 updated_at: string 1022 1023 } 1023 1024 }
+36 -3
apps/cli/src/index.ts
··· 18 18 import { logs, streamLogs } from './commands/logs' 19 19 import { loadConfig } from './lib/config' 20 20 import { logger } from './lib/logger' 21 - import type { CliConfig } from './lib/types' 21 + import type { CliConfig, DeploySourceMapMode } from './lib/types' 22 22 23 23 function positional(args: Record<string, unknown>, index: number): string | undefined { 24 24 const values = Array.isArray(args._) ? args._ : [] ··· 54 54 config.apiUrl = apiUrl 55 55 } 56 56 return config 57 + } 58 + 59 + function parseSourcemap(value: unknown): DeploySourceMapMode | undefined { 60 + if (typeof value !== 'string' || value.length === 0) { 61 + return undefined 62 + } 63 + 64 + if (value === 'none' || value === 'inline' || value === 'external') { 65 + return value 66 + } 67 + 68 + throw new Error(`invalid --sourcemap value: ${value}`) 69 + } 70 + 71 + function parseExternal(value: unknown): string[] | undefined { 72 + if (typeof value !== 'string' || value.trim().length === 0) { 73 + return undefined 74 + } 75 + 76 + const values = value 77 + .split(',') 78 + .map((entry) => entry.trim()) 79 + .filter((entry) => entry.length > 0) 80 + 81 + return values.length > 0 ? values : undefined 57 82 } 58 83 59 84 const kvCommand = defineCommand({ ··· 193 218 deploy: defineCommand({ 194 219 args: { 195 220 guild: { type: 'string', required: false }, 196 - root: { type: 'string', required: false } 221 + root: { type: 'string', required: false }, 222 + sourcemap: { type: 'string', required: false }, 223 + minify: { type: 'boolean', required: false }, 224 + external: { type: 'string', required: false } 197 225 }, 198 226 async run({ args }) { 199 227 const config = resolveConfig(args as Record<string, unknown>) ··· 202 230 config, 203 231 args.guild as string | undefined, 204 232 entry, 205 - args.root as string | undefined 233 + { 234 + root: args.root as string | undefined, 235 + sourcemap: parseSourcemap(args.sourcemap), 236 + minify: args.minify as boolean | undefined, 237 + external: parseExternal(args.external) 238 + } 206 239 ) 207 240 } 208 241 }),
+17 -1
apps/cli/src/lib/config.ts
··· 1 + import { loadConfig as loadC12 } from 'c12' 1 2 import Conf from 'conf' 2 3 3 - import { type CliConfig, DEFAULT_API_URL } from './types' 4 + import { type CliConfig, DEFAULT_API_URL, type DeployProjectConfig } from './types' 4 5 5 6 const store = new Conf<CliConfig>({ 6 7 projectName: 'flora', ··· 20 21 export function saveConfig(config: CliConfig): void { 21 22 store.set(config) 22 23 } 24 + 25 + export async function loadProjectConfig(cwd = process.cwd()): Promise<DeployProjectConfig> { 26 + const { config } = await loadC12<{ deploy?: DeployProjectConfig }>({ 27 + cwd, 28 + name: 'flora', 29 + configFile: 'flora.config', 30 + rcFile: false, 31 + packageJson: false, 32 + defaults: { 33 + deploy: {} 34 + } 35 + }) 36 + 37 + return config.deploy ?? {} 38 + }
+104
apps/cli/src/lib/deploy_bundle.ts
··· 1 + import path from 'node:path' 2 + 3 + import { build } from 'rolldown' 4 + import type { OutputAsset, OutputChunk } from 'rolldown' 5 + 6 + import type { DeploySourceMapMode } from './types' 7 + 8 + export const DEFAULT_DEPLOY_ENTRY = 'src/main.ts' 9 + export const DEFAULT_DEPLOY_SOURCEMAP: DeploySourceMapMode = 'none' 10 + 11 + export type DeploymentSourceMapFile = { 12 + path: string 13 + contents: string 14 + } 15 + 16 + export type BundleDeployOptions = { 17 + cwd?: string 18 + root?: string 19 + entry?: string 20 + sourcemap?: DeploySourceMapMode 21 + minify?: boolean 22 + external?: string[] 23 + } 24 + 25 + export type BundleDeployResult = { 26 + entry: string 27 + bundle: string 28 + sourceMap?: DeploymentSourceMapFile 29 + } 30 + 31 + export async function bundleDeploymentSource( 32 + options: BundleDeployOptions = {} 33 + ): Promise<BundleDeployResult> { 34 + const cwd = path.resolve(options.cwd ?? process.cwd()) 35 + const root = path.resolve(cwd, options.root ?? '.') 36 + const entryAbs = path.resolve(root, options.entry ?? DEFAULT_DEPLOY_ENTRY) 37 + const entry = toRelativePath(entryAbs, root) 38 + 39 + const sourcemap = options.sourcemap ?? DEFAULT_DEPLOY_SOURCEMAP 40 + const minify = options.minify ?? false 41 + 42 + const result = await build({ 43 + cwd: root, 44 + input: entryAbs, 45 + external: options.external, 46 + write: false, 47 + output: { 48 + format: 'esm', 49 + sourcemap: sourcemap === 'none' ? false : sourcemap === 'inline' ? 'inline' : true, 50 + minify, 51 + exports: 'named' 52 + } 53 + }) 54 + 55 + const chunk = result.output.find( 56 + (output): output is OutputChunk => output.type === 'chunk' && output.isEntry 57 + ) 58 + 59 + if (!chunk) { 60 + throw new Error(`Failed to bundle entry ${entry}`) 61 + } 62 + 63 + if (sourcemap !== 'external') { 64 + return { 65 + entry, 66 + bundle: chunk.code 67 + } 68 + } 69 + 70 + const sourceMapAsset = result.output.find( 71 + (output): output is OutputAsset => output.type === 'asset' && output.fileName.endsWith('.map') 72 + ) 73 + 74 + if (!sourceMapAsset) { 75 + throw new Error(`Expected external sourcemap for entry ${entry}`) 76 + } 77 + 78 + return { 79 + entry, 80 + bundle: chunk.code, 81 + sourceMap: { 82 + path: sourceMapAsset.fileName, 83 + contents: toText(sourceMapAsset.source) 84 + } 85 + } 86 + } 87 + 88 + function toRelativePath(filePath: string, root: string): string { 89 + const rel = path.relative(root, filePath).replace(/\\/g, '/') 90 + 91 + if (!rel || rel.startsWith('..')) { 92 + throw new Error(`Entry file is not inside ${root}`) 93 + } 94 + 95 + return rel 96 + } 97 + 98 + function toText(value: string | Uint8Array): string { 99 + if (typeof value === 'string') { 100 + return value 101 + } 102 + 103 + return Buffer.from(value).toString('utf8') 104 + }
+10
apps/cli/src/lib/types.ts
··· 3 3 token?: string 4 4 } 5 5 6 + export type DeploySourceMapMode = 'none' | 'inline' | 'external' 7 + 8 + export type DeployProjectConfig = { 9 + entry?: string 10 + root?: string 11 + sourcemap?: DeploySourceMapMode 12 + minify?: boolean 13 + external?: string[] 14 + } 15 + 6 16 export const DEFAULT_API_URL = 'http://localhost:3000/api'
+94
apps/cli/test/deploy_bundle.test.ts
··· 1 + import { mkdir, mkdtemp, rm, writeFile } from 'node:fs/promises' 2 + import { tmpdir } from 'node:os' 3 + import path from 'node:path' 4 + import { afterEach, describe, expect, it } from 'vitest' 5 + 6 + import { bundleDeploymentSource } from '../src/lib/deploy_bundle' 7 + 8 + async function createProjectFixture(): Promise<string> { 9 + const root = await mkdtemp(path.join(tmpdir(), 'flora-cli-bundle-')) 10 + 11 + await mkdir(path.join(root, 'src'), { recursive: true }) 12 + await mkdir(path.join(root, 'node_modules', 'tiny-dep'), { recursive: true }) 13 + 14 + await writeFile( 15 + path.join(root, 'src', 'main.ts'), 16 + [ 17 + "import { localValue } from './util'", 18 + "import dependencyValue from 'tiny-dep'", 19 + 'export const value = `${localValue}${dependencyValue}`' 20 + ].join('\n') 21 + ) 22 + await writeFile(path.join(root, 'src', 'util.ts'), "export const localValue = 'local-'\n") 23 + await writeFile( 24 + path.join(root, 'node_modules', 'tiny-dep', 'package.json'), 25 + JSON.stringify({ name: 'tiny-dep', version: '1.0.0', type: 'module', main: 'index.js' }) 26 + ) 27 + await writeFile(path.join(root, 'node_modules', 'tiny-dep', 'index.js'), "export default 'dep'\n") 28 + 29 + return root 30 + } 31 + 32 + describe('bundleDeploymentSource', () => { 33 + const roots: string[] = [] 34 + 35 + afterEach(async () => { 36 + await Promise.all(roots.map((root) => rm(root, { recursive: true, force: true }))) 37 + roots.length = 0 38 + }) 39 + 40 + it('bundles multi-file entry and node_modules by default', async () => { 41 + const root = await createProjectFixture() 42 + roots.push(root) 43 + 44 + const result = await bundleDeploymentSource({ cwd: root }) 45 + 46 + expect(result.entry).toBe('src/main.ts') 47 + expect(result.bundle).toContain('local-') 48 + expect(result.bundle).toContain('dep') 49 + expect(result.bundle).not.toMatch(/from ['"]tiny-dep['"]/) 50 + }) 51 + 52 + it('supports sourcemap none mode', async () => { 53 + const root = await createProjectFixture() 54 + roots.push(root) 55 + 56 + const result = await bundleDeploymentSource({ cwd: root, sourcemap: 'none' }) 57 + 58 + expect(result.sourceMap).toBeUndefined() 59 + expect(result.bundle).not.toContain('sourceMappingURL=') 60 + }) 61 + 62 + it('supports sourcemap inline mode', async () => { 63 + const root = await createProjectFixture() 64 + roots.push(root) 65 + 66 + const result = await bundleDeploymentSource({ cwd: root, sourcemap: 'inline' }) 67 + 68 + expect(result.sourceMap).toBeUndefined() 69 + expect(result.bundle).toContain('sourceMappingURL=data:') 70 + }) 71 + 72 + it('supports sourcemap external mode', async () => { 73 + const root = await createProjectFixture() 74 + roots.push(root) 75 + 76 + const result = await bundleDeploymentSource({ cwd: root, sourcemap: 'external' }) 77 + 78 + expect(result.sourceMap).toBeDefined() 79 + expect(result.sourceMap?.path).toMatch(/\.map$/) 80 + expect(result.sourceMap?.contents).toContain('"version"') 81 + }) 82 + 83 + it('keeps explicit external imports in output', async () => { 84 + const root = await createProjectFixture() 85 + roots.push(root) 86 + 87 + const result = await bundleDeploymentSource({ 88 + cwd: root, 89 + external: ['tiny-dep'] 90 + }) 91 + 92 + expect(result.bundle).toMatch(/from ['"]tiny-dep['"]/) 93 + }) 94 + })
+111
apps/cli/test/deployments.test.ts
··· 1 + import { beforeEach, describe, expect, it, vi } from 'vitest' 2 + 3 + import { deploy } from '../src/commands/deployments' 4 + import type { CliConfig } from '../src/lib/types' 5 + 6 + const { 7 + postMock, 8 + expectOkMock, 9 + loadProjectConfigMock, 10 + bundleDeploymentSourceMock 11 + } = vi.hoisted(() => ({ 12 + postMock: vi.fn(), 13 + expectOkMock: vi.fn(), 14 + loadProjectConfigMock: vi.fn(), 15 + bundleDeploymentSourceMock: vi.fn() 16 + })) 17 + 18 + vi.mock('../src/lib/http', () => ({ 19 + authHeaders: vi.fn(() => ({ authorization: 'Bearer token' })), 20 + createApiClient: vi.fn(() => ({ 21 + POST: postMock 22 + })), 23 + expectOk: expectOkMock 24 + })) 25 + 26 + vi.mock('../src/lib/config', () => ({ 27 + loadProjectConfig: loadProjectConfigMock 28 + })) 29 + 30 + vi.mock('../src/lib/deploy_bundle', () => ({ 31 + bundleDeploymentSource: bundleDeploymentSourceMock 32 + })) 33 + 34 + vi.mock('../src/lib/logger', () => ({ 35 + logger: { 36 + log: vi.fn() 37 + } 38 + })) 39 + 40 + describe('deploy command', () => { 41 + const config: CliConfig = { 42 + apiUrl: 'http://localhost:3000/api', 43 + token: 'token' 44 + } 45 + 46 + beforeEach(() => { 47 + postMock.mockReset() 48 + expectOkMock.mockReset() 49 + loadProjectConfigMock.mockReset() 50 + bundleDeploymentSourceMock.mockReset() 51 + 52 + loadProjectConfigMock.mockResolvedValue({}) 53 + bundleDeploymentSourceMock.mockResolvedValue({ 54 + entry: 'src/main.ts', 55 + bundle: 'console.log(1)', 56 + sourceMap: { 57 + path: 'main.js.map', 58 + contents: '{"version":3}' 59 + } 60 + }) 61 + expectOkMock.mockResolvedValue({ 62 + guild_id: '123', 63 + entry: 'src/main.ts', 64 + updated_at: 'now' 65 + }) 66 + postMock.mockResolvedValue({}) 67 + }) 68 + 69 + it('posts prebundled deployment payload', async () => { 70 + await deploy(config, '123', undefined) 71 + 72 + expect(postMock).toHaveBeenCalledOnce() 73 + const request = postMock.mock.calls[0]?.[1] 74 + 75 + expect(request).toMatchObject({ 76 + body: { 77 + entry: 'src/main.ts', 78 + bundle: 'console.log(1)', 79 + source_map: { 80 + path: 'main.js.map', 81 + contents: '{"version":3}' 82 + } 83 + } 84 + }) 85 + }) 86 + 87 + it('merges cli overrides over project config', async () => { 88 + loadProjectConfigMock.mockResolvedValue({ 89 + entry: 'config/entry.ts', 90 + root: './bot', 91 + sourcemap: 'external', 92 + minify: false, 93 + external: ['discord.js'] 94 + }) 95 + 96 + await deploy(config, '123', 'src/cli-entry.ts', { 97 + root: './cli-root', 98 + sourcemap: 'inline', 99 + minify: true, 100 + external: ['node:fs'] 101 + }) 102 + 103 + expect(bundleDeploymentSourceMock).toHaveBeenCalledWith({ 104 + root: './cli-root', 105 + entry: 'src/cli-entry.ts', 106 + sourcemap: 'inline', 107 + minify: true, 108 + external: ['node:fs'] 109 + }) 110 + }) 111 + })
+48
apps/cli/test/project_config.test.ts
··· 1 + import { mkdir, mkdtemp, rm, writeFile } from 'node:fs/promises' 2 + import { tmpdir } from 'node:os' 3 + import path from 'node:path' 4 + import { describe, expect, it } from 'vitest' 5 + 6 + import { loadProjectConfig } from '../src/lib/config' 7 + 8 + describe('loadProjectConfig', () => { 9 + it('returns empty deploy config when flora.config.ts is missing', async () => { 10 + const root = await mkdtemp(path.join(tmpdir(), 'flora-cli-config-')) 11 + 12 + try { 13 + expect(await loadProjectConfig(root)).toEqual({}) 14 + } finally { 15 + await rm(root, { recursive: true, force: true }) 16 + } 17 + }) 18 + 19 + it('loads deploy options from flora.config.ts', async () => { 20 + const root = await mkdtemp(path.join(tmpdir(), 'flora-cli-config-')) 21 + 22 + try { 23 + await mkdir(path.join(root, 'bot'), { recursive: true }) 24 + await writeFile( 25 + path.join(root, 'flora.config.ts'), 26 + `export default { 27 + deploy: { 28 + entry: 'bot/main.ts', 29 + root: './bot', 30 + sourcemap: 'external', 31 + minify: true, 32 + external: ['discord.js'] 33 + } 34 + }\n` 35 + ) 36 + 37 + expect(await loadProjectConfig(root)).toEqual({ 38 + entry: 'bot/main.ts', 39 + root: './bot', 40 + sourcemap: 'external', 41 + minify: true, 42 + external: ['discord.js'] 43 + }) 44 + } finally { 45 + await rm(root, { recursive: true, force: true }) 46 + } 47 + }) 48 + })
+110
pnpm-lock.yaml
··· 38 38 '@clack/prompts': 39 39 specifier: ^1.0.1 40 40 version: 1.0.1 41 + c12: 42 + specifier: ^3.3.2 43 + version: 3.3.3 41 44 citty: 42 45 specifier: ^0.2.1 43 46 version: 0.2.1 ··· 53 56 openapi-fetch: 54 57 specifier: ^0.17.0 55 58 version: 0.17.0 59 + rolldown: 60 + specifier: ^1.0.0-beta.43 61 + version: 1.0.0-rc.3 56 62 tinyglobby: 57 63 specifier: ^0.2.15 58 64 version: 0.2.15 ··· 3852 3858 } 3853 3859 engines: { node: '>= 0.8' } 3854 3860 3861 + c12@3.3.3: 3862 + resolution: { 3863 + integrity: sha512-750hTRvgBy5kcMNPdh95Qo+XUBeGo8C7nsKSmedDmaQI+E0r82DwHeM6vBewDe4rGFbnxoa4V9pw+sPh5+Iz8Q==, 3864 + } 3865 + peerDependencies: 3866 + magicast: '*' 3867 + peerDependenciesMeta: 3868 + magicast: 3869 + optional: true 3870 + 3855 3871 cac@6.7.14: 3856 3872 resolution: { 3857 3873 integrity: sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==, ··· 3924 3940 integrity: sha512-TQMmc3w+5AxjpL8iIiwebF73dRDF4fBIieAqGn9RGCWaEVwQ6Fb2cGe31Yns0RRIzii5goJ1Y7xbMwo1TxMplw==, 3925 3941 } 3926 3942 engines: { node: '>= 20.19.0' } 3943 + 3944 + citty@0.1.6: 3945 + resolution: { 3946 + integrity: sha512-tskPPKEs8D2KPafUypv2gxwJP8h/OaJmC82QQGGDQcHvXX43xF2VDACcJVmZ0EuSxkpO9Kc4MlrA3q0+FG58AQ==, 3947 + } 3927 3948 3928 3949 citty@0.2.1: 3929 3950 resolution: { ··· 4022 4043 confbox@0.1.8: 4023 4044 resolution: { 4024 4045 integrity: sha512-RMtmw0iFkeR4YV+fUOSucriAQNb9g8zFR52MWCtl+cCZOFRNL6zeB395vPzFhEjjn4fMxXudmELnl/KF/WrK6w==, 4046 + } 4047 + 4048 + confbox@0.2.4: 4049 + resolution: { 4050 + integrity: sha512-ysOGlgTFbN2/Y6Cg3Iye8YKulHw+R2fNXHrgSmXISQdMnomY6eNDprVdW9R5xBguEqI954+S6709UyiO7B+6OQ==, 4025 4051 } 4026 4052 4027 4053 consola@3.4.2: ··· 4524 4550 integrity: sha512-hIS4idWWai69NezIdRt2xFVofaF4j+6INOpJlVOLDO8zXGpUVEVzIYk12UUi2JzjEzWL3IOAxcTubgz9Po0yXw==, 4525 4551 } 4526 4552 engines: { node: '>= 18' } 4553 + 4554 + exsolve@1.0.8: 4555 + resolution: { 4556 + integrity: sha512-LmDxfWXwcTArk8fUEnOfSZpHOJ6zOMUJKOtFLFqJLoKJetuQG874Uc7/Kki7zFLzYybmZhp1M7+98pfMqeX8yA==, 4557 + } 4527 4558 4528 4559 fast-deep-equal@3.1.3: 4529 4560 resolution: { ··· 4739 4770 integrity: sha512-shZT/QMiSHc/YBLxxOkMtgSid5HFoauqCE3/exfsEcwg1WkeqjG+V40yBbBrsD+jW2HDXcs28xOfcbm2jI8Ddw==, 4740 4771 } 4741 4772 4773 + giget@2.0.0: 4774 + resolution: { 4775 + integrity: sha512-L5bGsVkxJbJgdnwyuheIunkGatUF/zssUoxxjACCseZYAVbaqdh9Tsmmlkl8vYan09H7sbvKt4pS8GqKLBrEzA==, 4776 + } 4777 + hasBin: true 4778 + 4742 4779 glob-parent@5.1.2: 4743 4780 resolution: { 4744 4781 integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==, ··· 5733 5770 } 5734 5771 engines: { node: '>=18' } 5735 5772 5773 + nypm@0.6.5: 5774 + resolution: { 5775 + integrity: sha512-K6AJy1GMVyfyMXRVB88700BJqNUkByijGJM8kEHpLdcAt+vSQAVfkWWHYzuRXHSY6xA2sNc5RjTj0p9rE2izVQ==, 5776 + } 5777 + engines: { node: '>=18' } 5778 + hasBin: true 5779 + 5736 5780 object-assign@4.1.1: 5737 5781 resolution: { 5738 5782 integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==, ··· 5759 5803 ofetch@1.5.1: 5760 5804 resolution: { 5761 5805 integrity: sha512-2W4oUZlVaqAPAil6FUg/difl6YhqhUR7x2eZY4bQCko22UXg3hptq9KLQdqFClV+Wu85UX7hNtdGTngi/1BxcA==, 5806 + } 5807 + 5808 + ohash@2.0.11: 5809 + resolution: { 5810 + integrity: sha512-RdR9FQrFwNBNXAr4GixM8YaRZRJ5PUWbKYbE5eOsrwAjJW0q2REGcf79oYPsLyskQCZG1PLN+S/K1V00joZAoQ==, 5762 5811 } 5763 5812 5764 5813 on-finished@2.4.1: ··· 6017 6066 integrity: sha512-/Jm5M4RvtBFVkKWRu2BLUTNP8/M2a+UwuAX+ae4770q1qVGtfjG+WTCupoZixokjmHiry8uI+dlY8KXYV5HVVQ==, 6018 6067 } 6019 6068 6069 + pkg-types@2.3.0: 6070 + resolution: { 6071 + integrity: sha512-SIqCzDRg0s9npO5XQ3tNZioRY1uK06lA41ynBC1YmFTmnY6FjUjVt6s4LoADmwoig1qqD0oK8h1p/8mlMx8Oig==, 6072 + } 6073 + 6020 6074 pluralize@8.0.0: 6021 6075 resolution: { 6022 6076 integrity: sha512-Nc3IT5yHzflTfbjgqWcCPpo7DaKy4FnpB0l/zCAW0Tc7jxAiuqSxHasntB3D7887LSrA93kDJ9IXovxJYxyLCA==, ··· 6115 6169 } 6116 6170 engines: { node: '>= 0.10' } 6117 6171 6172 + rc9@2.1.2: 6173 + resolution: { 6174 + integrity: sha512-btXCnMmRIBINM2LDZoEmOogIZU7Qe7zn4BpomSKZ/ykbLObuBdvG+mFq11DL6fjH1DRwHhrlgtYWG96bJiC7Cg==, 6175 + } 6176 + 6118 6177 react-dom@19.2.4: 6119 6178 resolution: { 6120 6179 integrity: sha512-AXJdLo8kgMbimY95O2aKQqsz2iWi9jMgKJhRBAxECE4IFxfcazB2LmzloIoibJI3C12IlY20+KFaLv+71bUJeQ==, ··· 9613 9672 9614 9673 bytes@3.1.2: {} 9615 9674 9675 + c12@3.3.3: 9676 + dependencies: 9677 + chokidar: 5.0.0 9678 + confbox: 0.2.4 9679 + defu: 6.1.4 9680 + dotenv: 17.2.3 9681 + exsolve: 1.0.8 9682 + giget: 2.0.0 9683 + jiti: 2.6.1 9684 + ohash: 2.0.11 9685 + pathe: 2.0.3 9686 + perfect-debounce: 2.1.0 9687 + pkg-types: 2.3.0 9688 + rc9: 2.1.2 9689 + 9616 9690 cac@6.7.14: {} 9617 9691 9618 9692 call-bind-apply-helpers@1.0.2: ··· 9651 9725 chokidar@5.0.0: 9652 9726 dependencies: 9653 9727 readdirp: 5.0.0 9728 + 9729 + citty@0.1.6: 9730 + dependencies: 9731 + consola: 3.4.2 9654 9732 9655 9733 citty@0.2.1: {} 9656 9734 ··· 9707 9785 uint8array-extras: 1.5.0 9708 9786 9709 9787 confbox@0.1.8: {} 9788 + 9789 + confbox@0.2.4: {} 9710 9790 9711 9791 consola@3.4.2: {} 9712 9792 ··· 10105 10185 transitivePeerDependencies: 10106 10186 - supports-color 10107 10187 10188 + exsolve@1.0.8: {} 10189 + 10108 10190 fast-deep-equal@3.1.3: {} 10109 10191 10110 10192 fast-glob@3.3.3: ··· 10244 10326 dependencies: 10245 10327 resolve-pkg-maps: 1.0.0 10246 10328 10329 + giget@2.0.0: 10330 + dependencies: 10331 + citty: 0.1.6 10332 + consola: 3.4.2 10333 + defu: 6.1.4 10334 + node-fetch-native: 1.6.7 10335 + nypm: 0.6.5 10336 + pathe: 2.0.3 10337 + 10247 10338 glob-parent@5.1.2: 10248 10339 dependencies: 10249 10340 is-glob: 4.0.3 ··· 10791 10882 path-key: 4.0.0 10792 10883 unicorn-magic: 0.3.0 10793 10884 10885 + nypm@0.6.5: 10886 + dependencies: 10887 + citty: 0.2.1 10888 + pathe: 2.0.3 10889 + tinyexec: 1.0.2 10890 + 10794 10891 object-assign@4.1.1: {} 10795 10892 10796 10893 object-inspect@1.13.4: {} ··· 10804 10901 destr: 2.0.5 10805 10902 node-fetch-native: 1.6.7 10806 10903 ufo: 1.6.3 10904 + 10905 + ohash@2.0.11: {} 10807 10906 10808 10907 on-finished@2.4.1: 10809 10908 dependencies: ··· 11011 11110 mlly: 1.8.0 11012 11111 pathe: 2.0.3 11013 11112 11113 + pkg-types@2.3.0: 11114 + dependencies: 11115 + confbox: 0.2.4 11116 + exsolve: 1.0.8 11117 + pathe: 2.0.3 11118 + 11014 11119 pluralize@8.0.0: {} 11015 11120 11016 11121 pngjs@7.0.0: {} ··· 11066 11171 http-errors: 2.0.1 11067 11172 iconv-lite: 0.7.2 11068 11173 unpipe: 1.0.0 11174 + 11175 + rc9@2.1.2: 11176 + dependencies: 11177 + defu: 6.1.4 11178 + destr: 2.0.5 11069 11179 11070 11180 react-dom@19.2.4(react@19.2.4): 11071 11181 dependencies: