Select the types of activity you want to include in your feed.
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]
···4224224. `cargo test` (all tests pass)
4234235. `pnpm test` (SDK tests pass, if applicable)
4244246. Verify no secrets in changed files (`.env`, credentials)
425425+426426+<!--VITE PLUS START-->
427427+428428+# Using Vite+, the Unified Toolchain for the Web
429429+430430+This project is using Vite+, a unified toolchain built on top of Vite, Rolldown, Vitest, tsdown, Oxlint, Oxfmt, and Vite Task. Vite+ wraps runtime management, package management, and frontend tooling in a single global CLI called `vp`. Vite+ is distinct from Vite, but it invokes Vite through `vp dev` and `vp build`.
431431+432432+## Vite+ Workflow
433433+434434+`vp` is a global binary that handles the full development lifecycle. Run `vp help` to print a list of commands and `vp <command> --help` for information about a specific command.
435435+436436+### Start
437437+438438+- create - Create a new project from a template
439439+- migrate - Migrate an existing project to Vite+
440440+- config - Configure hooks and agent integration
441441+- staged - Run linters on staged files
442442+- install (`i`) - Install dependencies
443443+- env - Manage Node.js versions
444444+445445+### Develop
446446+447447+- dev - Run the development server
448448+- check - Run format, lint, and TypeScript type checks
449449+- lint - Lint code
450450+- fmt - Format code
451451+- test - Run tests
452452+453453+### Execute
454454+455455+- run - Run monorepo tasks
456456+- exec - Execute a command from local `node_modules/.bin`
457457+- dlx - Execute a package binary without installing it as a dependency
458458+- cache - Manage the task cache
459459+460460+### Build
461461+462462+- build - Build for production
463463+- pack - Build libraries
464464+- preview - Preview production build
465465+466466+### Manage Dependencies
467467+468468+Vite+ automatically detects and wraps the underlying package manager such as pnpm, npm, or Yarn through the `packageManager` field in `package.json` or package manager-specific lockfiles.
469469+470470+- add - Add packages to dependencies
471471+- remove (`rm`, `un`, `uninstall`) - Remove packages from dependencies
472472+- update (`up`) - Update packages to latest versions
473473+- dedupe - Deduplicate dependencies
474474+- outdated - Check for outdated packages
475475+- list (`ls`) - List installed packages
476476+- why (`explain`) - Show why a package is installed
477477+- info (`view`, `show`) - View package information from the registry
478478+- link (`ln`) / unlink - Manage local package links
479479+- pm - Forward a command to the package manager
480480+481481+### Maintain
482482+483483+- upgrade - Update `vp` itself to the latest version
484484+485485+These commands map to their corresponding tools. For example, `vp dev --port 3000` runs Vite's dev server and works the same as Vite. `vp test` runs JavaScript tests through the bundled Vitest. The version of all tools can be checked using `vp --version`. This is useful when researching documentation, features, and bugs.
486486+487487+## Common Pitfalls
488488+489489+- **Using the package manager directly:** Do not use pnpm, npm, or Yarn directly. Vite+ can handle all package manager operations.
490490+- **Always use Vite commands to run tools:** Don't attempt to run `vp vitest` or `vp oxlint`. They do not exist. Use `vp test` and `vp lint` instead.
491491+- **Running scripts:** Vite+ commands take precedence over `package.json` scripts. If there is a `test` script defined in `scripts` that conflicts with the built-in `vp test` command, run it using `vp run test`.
492492+- **Do not install Vitest, Oxlint, Oxfmt, or tsdown directly:** Vite+ wraps these tools. They must not be installed directly. You cannot upgrade these tools by installing their latest versions. Always use Vite+ commands.
493493+- **Use Vite+ wrappers for one-off binaries:** Use `vp dlx` instead of package-manager-specific `dlx`/`npx` commands.
494494+- **Import JavaScript modules from `vite-plus`:** Instead of importing from `vite` or `vitest`, all modules should be imported from the project's `vite-plus` dependency. For example, `import { defineConfig } from 'vite-plus';` or `import { expect, test, vi } from 'vite-plus/test';`. You must not install `vitest` to import test utilities.
495495+- **Type-Aware Linting:** There is no need to install `oxlint-tsgolint`, `vp lint --type-aware` works out of the box.
496496+497497+## Review Checklist for Agents
498498+499499+- [ ] Run `vp install` after pulling remote changes and before getting started.
500500+- [ ] Run `vp check` and `vp test` to validate changes.
501501+<!--VITE PLUS END-->
+32-5
Cargo.toml
···1818cookie = "0.18"
1919croner = "3.0"
2020dashmap = "6.1.0"
2121-deno_core = { git = "file:///home/tasky/flora/submodules/deno_core", branch = "flora-locker-compat", default-features = false, features = ["v8_use_custom_libcxx"] }
2121+deno_core = { git = "file:///home/tasky/flora/submodules/deno_core", branch = "flora-locker-compat", default-features = false, features = [
2222+ "v8_use_custom_libcxx"
2323+] }
2224deno_error = "0.7.1"
2325deno_fetch = "0.249.0"
2426deno_net = "0.217.0"
···3739http = "1"
3840num_cpus = "1.16"
3941openssl = { version = "0.10.75", features = ["vendored"] }
4040-oxc = { version = "0.101.0", features = ["full", "oxc_regular_expression", "semantic", "transformer"] }
4242+oxc = { version = "0.101.0", features = [
4343+ "full",
4444+ "oxc_regular_expression",
4545+ "semantic",
4646+ "transformer"
4747+] }
4148oxc_sourcemap = "6.0.2"
4249parking_lot = "0.12"
4350proc-macro2 = "1.0.106"
···4653reqwest = { version = "0.12.25", features = ["json", "multipart", "stream"] }
4754serde = { version = "1", features = ["derive"] }
4855serde_json = "1.0.149"
4949-serenity = { git = "https://github.com/serenity-rs/serenity", default-features = false, branch = "next", features = ["typesize", "temp_cache", "tokio_task_builder", "transport_compression_zstd", "rustls_backend"] }
5656+serenity = { git = "https://github.com/serenity-rs/serenity", default-features = false, branch = "next", features = [
5757+ "typesize",
5858+ "temp_cache",
5959+ "tokio_task_builder",
6060+ "transport_compression_zstd",
6161+ "rustls_backend"
6262+] }
5063sha2 = "0.10"
5164sled = "0.34"
5252-sqlx = { version = "0.8.6", features = ["chrono", "macros", "postgres", "runtime-tokio-rustls", "uuid"] }
6565+sqlx = { version = "0.8.6", features = [
6666+ "chrono",
6767+ "macros",
6868+ "postgres",
6969+ "runtime-tokio-rustls",
7070+ "uuid"
7171+] }
5372syn = { version = "2.0.117", features = ["full", "extra-traits", "parsing"] }
5473sys_traits = { version = "0.1.17", features = ["libc", "real"] }
5574t0x = { version = "0.1.8", features = ["serde-json-impl"] }
···6685tracing-subscriber = { version = "0.3.22", features = ["chrono", "env-filter", "serde", "time"] }
6786url = "2.5.8"
6887urlencoding = "2.1"
6969-utoipa = { version = "5.4.0", features = ["auto_into_responses", "axum_extras", "chrono", "config", "debug", "url", "uuid"] }
8888+utoipa = { version = "5.4.0", features = [
8989+ "auto_into_responses",
9090+ "axum_extras",
9191+ "chrono",
9292+ "config",
9393+ "debug",
9494+ "url",
9595+ "uuid"
9696+] }
7097utoipa-scalar = { version = "0.3.0", features = ["axum"] }
7198uuid = { version = "1.21.0", features = ["v4"] }
7299
···58585959 for (const [name, specifier] of entries) {
6060 if (typeof specifier !== 'string') {
6161- throw new Error(
6262- `Invalid specifier for dependency "${name}": must be a string`
6363- )
6161+ throw new Error(`Invalid specifier for dependency "${name}": must be a string`)
6462 }
65636664 for (const prefix of DISALLOWED_SPECIFIER_PREFIXES) {
···7169 }
7270 }
73717474- if (
7575- specifier.startsWith('/') ||
7676- specifier.startsWith('./') ||
7777- specifier.startsWith('../')
7878- ) {
7979- throw new Error(
8080- `Disallowed local path specifier for dependency "${name}": "${specifier}"`
8181- )
7272+ if (specifier.startsWith('/') || specifier.startsWith('./') || specifier.startsWith('../')) {
7373+ throw new Error(`Disallowed local path specifier for dependency "${name}": "${specifier}"`)
8274 }
83758476 // Block bare GitHub shorthands like "user/repo" or "user/repo#ref"
8577 // Scoped packages like "@org/pkg" are safe — they start with "@"
8678 if (!specifier.startsWith('@') && BARE_GIT_SHORTHAND_RE.test(specifier)) {
8787- throw new Error(
8888- `Disallowed bare Git shorthand for dependency "${name}": "${specifier}"`
8989- )
7979+ throw new Error(`Disallowed bare Git shorthand for dependency "${name}": "${specifier}"`)
9080 }
91819282 validDeps[name] = specifier
+2-4
apps/build-service/src/lib/zip.ts
···32323333 const data = entries[name]!
3434 if (data.byteLength > MAX_INDIVIDUAL_FILE_SIZE) {
3535- throw new Error(
3636- `File ${name} exceeds maximum size of ${MAX_INDIVIDUAL_FILE_SIZE} bytes`
3737- )
3535+ throw new Error(`File ${name} exceeds maximum size of ${MAX_INDIVIDUAL_FILE_SIZE} bytes`)
3836 }
39374038 totalSize += data.byteLength
···5856 }
59576058 return {
6161- fileCount: entryNames.filter(n => !n.endsWith('/')).length,
5959+ fileCount: entryNames.filter((n) => !n.endsWith('/')).length,
6260 totalSize
6361 }
6462}
···3434**Duplicate execution on crash:** If the runtime crashes while a cron job is executing, the `is_running` flag is lost. On restart, the job may run again if it's due. Use `skipIfRunning: true` and design handlers to be idempotent where possible.
35353636```ts
3737-cron('daily-cleanup', '0 0 * * *', async () => {
3838- // This handler should be safe to run twice
3939- await cleanupOldMessages()
4040-}, { skipIfRunning: true })
3737+cron(
3838+ 'daily-cleanup',
3939+ '0 0 * * *',
4040+ async () => {
4141+ // This handler should be safe to run twice
4242+ await cleanupOldMessages()
4343+ },
4444+ { skipIfRunning: true }
4545+)
4146```
42474348**Schedule drift:** If the runtime is down for an extended period, jobs won't "catch up" on missed executions. A job scheduled for midnight won't run at 2 AM if the bot was down at midnight — it will wait for the next midnight.
+9-6
apps/www/docs/sdk.md
···8080const echo = slash({
8181 name: 'echo',
8282 description: 'Echo your input',
8383- options: [
8484- { name: 'text', description: 'Text to echo', type: 'string', required: true }
8585- ],
8383+ options: [{ name: 'text', description: 'Text to echo', type: 'string', required: true }],
8684 run: async (ctx) => {
8785 const text = ctx.options.text as string
8886 await ctx.reply({ content: text, ephemeral: true })
···242240243241```ts
244242// Skip execution if previous run is still active
245245-cron('long-task', '*/5 * * * *', async (ctx) => {
246246- await someLongRunningTask()
247247-}, { skipIfRunning: true })
243243+cron(
244244+ 'long-task',
245245+ '*/5 * * * *',
246246+ async (ctx) => {
247247+ await someLongRunningTask()
248248+ },
249249+ { skipIfRunning: true }
250250+)
248251```
249252250253- `skipIfRunning`: If `true`, the job won't start a new execution if the previous one is still running. Default: `false`.
···11-import { beforeEach, describe, expect, it, vi } from 'vitest'
11+import { beforeEach, describe, expect, it, vi } from 'vite-plus/test'
22import { createBot, slash } from './sdk/commands'
33import type { InteractionContext } from './sdk/types'
44
···11-import { afterEach, beforeEach, describe, expect, it } from 'vitest'
11+import { afterEach, beforeEach, describe, expect, it } from 'vite-plus/test'
22import { createBot, prefix, slash } from '../sdk/commands'
33import { store } from '../sdk/kv'
44import { TestHarness } from './harness'
···2626 secrets?: Record<string, string>
2727}
28282929-export type DeepPartial<T> = T extends object ? { [K in keyof T]?: DeepPartial<T[K]> }
3030- : T
2929+export type DeepPartial<T> = T extends object ? { [K in keyof T]?: DeepPartial<T[K]> } : T
31303231export type MessagePartial = DeepPartial<EventMessage>
3332export type InteractionPartial = DeepPartial<EventInteractionCreate>