volter 0.0.0 → 0.0.1

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/README.md CHANGED
@@ -1 +1,15 @@
1
- # VolterJS
1
+ # volter
2
+
3
+ To install dependencies:
4
+
5
+ ```bash
6
+ bun install
7
+ ```
8
+
9
+ To run:
10
+
11
+ ```bash
12
+ bun run index.ts
13
+ ```
14
+
15
+ This project was created using `bun init` in bun v1.2.21. [Bun](https://bun.com) is a fast all-in-one JavaScript runtime.
package/biome.json ADDED
@@ -0,0 +1,40 @@
1
+ {
2
+ "$schema": "https://biomejs.dev/schemas/2.1.2/schema.json",
3
+ "vcs": {
4
+ "enabled": false,
5
+ "clientKind": "git",
6
+ "useIgnoreFile": false
7
+ },
8
+ "files": {
9
+ "ignoreUnknown": false
10
+ },
11
+ "formatter": {
12
+ "enabled": true,
13
+ "indentStyle": "tab"
14
+ },
15
+ "linter": {
16
+ "enabled": true,
17
+ "rules": {
18
+ "recommended": true
19
+ }
20
+ },
21
+ "javascript": {
22
+ "formatter": {
23
+ "quoteStyle": "double",
24
+ "semicolons": "asNeeded",
25
+ "lineWidth": 160,
26
+ "arrowParentheses": "asNeeded"
27
+ },
28
+ "linter": {
29
+ "enabled": true
30
+ }
31
+ },
32
+ "assist": {
33
+ "enabled": true,
34
+ "actions": {
35
+ "source": {
36
+ "organizeImports": "off"
37
+ }
38
+ }
39
+ }
40
+ }
package/index.ts ADDED
@@ -0,0 +1,182 @@
1
+ import type { RedisClient, SocketAddress } from "bun"
2
+ import { createID } from "./utils"
3
+ import type { BunSQLDatabase } from "drizzle-orm/bun-sql"
4
+
5
+ declare global {
6
+ interface Request {
7
+ ip: SocketAddress
8
+ }
9
+ }
10
+
11
+ export namespace Volter {
12
+ export namespace Cache {
13
+ export interface Options {
14
+ expiry?: number
15
+ limit?: number
16
+ /** FIFO */
17
+ max?: number
18
+ }
19
+ export interface KV {
20
+ value: string
21
+ getch: number
22
+ expiry?: NodeJS.Timeout
23
+ limit?: number
24
+ }
25
+ }
26
+ export class Cache {
27
+ constructor(options?: Cache.Options) {
28
+ this.expiry = options?.expiry
29
+ this.limit = options?.limit
30
+ this.max = options?.max
31
+ }
32
+ expiry?: number
33
+ limit?: number
34
+ max?: number
35
+ hasher = new Bun.CryptoHasher("sha256")
36
+ store: { [x: string]: Cache.KV } = {}
37
+ hash(password: string): string {
38
+ return this.hasher.update(password).digest("base64")
39
+ }
40
+ pop(): void {
41
+ const keys = Object.keys(this.store)
42
+ delete this.store[keys[keys.length - 1]]
43
+ }
44
+ has(key: string): boolean {
45
+ return this.store[key] !== undefined
46
+ }
47
+ get(key: string): string | null {
48
+ if (this.store[key] !== undefined) {
49
+ const record = this.store[key]
50
+ this.store[key].getch += 1
51
+ if (record.limit !== undefined) {
52
+ if (record.getch >= record.limit) {
53
+ delete this.store[key]
54
+ return null
55
+ }
56
+ }
57
+ return record.value
58
+ }
59
+ return null
60
+ }
61
+ set(key: string, value: string, expiry?: number, limit?: number): boolean {
62
+ if (this.max !== undefined && this.size() >= this.max) this.pop()
63
+ this.store[key] = {
64
+ value,
65
+ getch: 0,
66
+ limit: limit ?? this.limit,
67
+ expiry: expiry
68
+ ? setTimeout(() => delete this.store[key], expiry * 1000)
69
+ : this.expiry
70
+ ? setTimeout(() => delete this.store[key], this.expiry * 1000)
71
+ : undefined,
72
+ }
73
+ return true
74
+ }
75
+ del(key: string): boolean {
76
+ if (this.store[key]) {
77
+ if (this.store[key].expiry !== undefined) {
78
+ clearTimeout(this.store[key].expiry)
79
+ } else {
80
+ delete this.store[key]
81
+ }
82
+ return true
83
+ }
84
+ return false
85
+ }
86
+ clear(): void {
87
+ this.store = {}
88
+ }
89
+ size(): number {
90
+ return Object.keys(this.store).length
91
+ }
92
+ }
93
+
94
+ export interface RatelimitOptions {
95
+ redis: RedisClient
96
+ limit: number
97
+ duration: number
98
+ max: number
99
+ }
100
+ export interface RatelimitResult {
101
+ success: boolean
102
+ remaining: number
103
+ }
104
+
105
+ export class Ratelimit {
106
+ private redis: RedisClient
107
+ private limiter: number
108
+ private duration: number
109
+ private max: number
110
+
111
+ constructor(options: RatelimitOptions) {
112
+ this.redis = options.redis
113
+ this.limiter = options.limit
114
+ this.duration = options.duration
115
+ this.max = options.max
116
+ }
117
+
118
+ async limit(identifier: string): Promise<RatelimitResult> {
119
+ const blocked = await this.redis.exists(`ratelimited:${identifier}`)
120
+ if (blocked) {
121
+ return {
122
+ success: false,
123
+ remaining: 0,
124
+ }
125
+ }
126
+
127
+ const key = `ratelimit:${identifier}`
128
+
129
+ const count = await this.redis.incr(key)
130
+ if (count === 1) {
131
+ await this.redis.expire(key, this.duration)
132
+ }
133
+
134
+ if (count >= this.max) {
135
+ await this.redis.set(`ratelimited:${identifier}`, "1")
136
+ await this.redis.expire(`ratelimited:${identifier}`, this.limiter)
137
+ }
138
+
139
+ return {
140
+ success: true,
141
+ remaining: this.max - count,
142
+ }
143
+ }
144
+ }
145
+
146
+ export interface SessionsOptions {
147
+ redis: RedisClient
148
+ db: BunSQLDatabase
149
+ }
150
+ export class Sessions {
151
+ constructor(options: SessionsOptions) {
152
+ this.redis = options.redis
153
+ this.db = options.db
154
+ }
155
+ redis: RedisClient
156
+ db: BunSQLDatabase
157
+ async create(id: string) {
158
+ const session = createID()
159
+ const lookup = hash(session)
160
+ await this.redis.set(`session:${lookup}`, id, "EX", 2592000) // 30 days
161
+ return session
162
+ } // create
163
+ async validate(token: string) {
164
+ const lookup = hash(token)
165
+ // todo: login
166
+ return await this.redis.get(`session:${lookup}`)
167
+ } // read
168
+ async rotate(token: string) {
169
+ const isValid = await this.validate(token)
170
+ if (!isValid) return undefined
171
+ await this.revoke(token)
172
+ return await this.create(token)
173
+ }
174
+ async revoke(token: string) {
175
+ const lookup = hash(token)
176
+ await this.redis.del(`session:${lookup}`)
177
+ return true
178
+ } // delete
179
+ }
180
+ }
181
+
182
+ export default Volter
package/package.json CHANGED
@@ -1,53 +1,21 @@
1
1
  {
2
- "name": "volter",
3
- "main": "./src/volter.ts",
4
- "module": "./src/volter.ts",
5
- "types": "./volter.d.ts",
6
- "type": "module",
7
- "files": [
8
- "*.d.ts"
9
- ],
10
- "devDependencies": {
11
- "@biomejs/biome": "1.5.3",
12
- "@types/bun": "latest",
13
- "@types/showdown": "^2.0.6"
14
- },
15
- "description": "⚡ Volter a React Meta Framework",
16
- "version": "0.0.0",
17
- "dependencies": {
18
- "@elysiajs/cors": "^0.8.0",
19
- "@elysiajs/static": "^0.8.1",
20
- "bun-types": "^1.0.26",
21
- "csstype": "^3.1.3",
22
- "elysia": "latest",
23
- "showdown": "^2.1.0",
24
- "typescript": "latest",
25
- "undici-types": "^5.26.5"
26
- },
27
- "exports": {
28
- ".": {
29
- "import": "./src/volter.ts",
30
- "require": "./src/volter.ts",
31
- "types": "./volter.d.ts"
32
- }
33
- },
34
- "scripts": {
35
- "udp": "bun src/udp.ts",
36
- "ftp": "bun src/ftp.ts",
37
- "test": "bun tests/index.test.ts",
38
- "test:j": "bun tests/json.test.ts"
39
- },
40
- "repository": {
41
- "type": "git",
42
- "url": "invalid"
43
- },
44
- "keywords": [
45
- "JavaScript",
46
- "Framework"
47
- ],
48
- "author": "Modlin Inc",
49
- "license": "MIT",
50
- "directories": {
51
- "test": "tests"
52
- }
2
+ "name": "volter",
3
+ "version": "0.0.1",
4
+ "description": "Secure, lightweight, and user-friendly modern JavaScript toolchain optimized for performance and minimalism.",
5
+ "main": "index.ts",
6
+ "module": "index.ts",
7
+ "type": "module",
8
+ "devDependencies": {
9
+ "@types/bun": "latest"
10
+ },
11
+ "peerDependencies": {
12
+ "typescript": "^5"
13
+ },
14
+ "dependencies": {
15
+ "@paralleldrive/cuid2": "^2.2.2",
16
+ "@signalapp/libsignal-client": "^0.79.1",
17
+ "chalk": "^5.6.0",
18
+ "drizzle-orm": "^0.44.5",
19
+ "zod": "^4.1.7"
20
+ }
53
21
  }
package/session.ts ADDED
@@ -0,0 +1,37 @@
1
+ import { getRandomValues, randomBytes } from "node:crypto"
2
+
3
+ function pad(input: string) {
4
+ const result: string[] = []
5
+ for (let i = 0; i < input.length; i++) {
6
+ result.push(input[i] as string)
7
+ if ((i + 1) % 4 === 0 && i !== input.length - 1) {
8
+ result.push("-")
9
+ }
10
+ }
11
+ return result.join("")
12
+ }
13
+
14
+ export function randomID() {
15
+ const session = randomBytes(16).toHex()
16
+
17
+ return pad(session)
18
+ }
19
+ export function randomPN(): string {
20
+ const MAX = 100_000_000
21
+ const LIMIT = Math.floor(0x1_0000_0000 / MAX) * MAX
22
+ const buf = new Uint32Array(1)
23
+
24
+ let v: number
25
+ while (true) {
26
+ getRandomValues(buf)
27
+ v = buf[0] as number
28
+ if (v < LIMIT) break
29
+ }
30
+
31
+ const padded = (v % MAX).toString().padStart(8, "0")
32
+ return `${padded.slice(0, 4)} ${padded.slice(4)}`
33
+ }
34
+
35
+ do {
36
+ console.log(randomID())
37
+ } while (true)
package/tsconfig.json ADDED
@@ -0,0 +1,29 @@
1
+ {
2
+ "compilerOptions": {
3
+ // Environment setup & latest features
4
+ "lib": ["ESNext"],
5
+ "target": "ESNext",
6
+ "module": "Preserve",
7
+ "moduleDetection": "force",
8
+ "jsx": "react-jsx",
9
+ "allowJs": true,
10
+
11
+ // Bundler mode
12
+ "moduleResolution": "bundler",
13
+ "allowImportingTsExtensions": true,
14
+ "verbatimModuleSyntax": true,
15
+ "noEmit": true,
16
+
17
+ // Best practices
18
+ "strict": true,
19
+ "skipLibCheck": true,
20
+ "noFallthroughCasesInSwitch": true,
21
+ "noUncheckedIndexedAccess": true,
22
+ "noImplicitOverride": true,
23
+
24
+ // Some stricter flags (disabled by default)
25
+ "noUnusedLocals": false,
26
+ "noUnusedParameters": false,
27
+ "noPropertyAccessFromIndexSignature": false
28
+ }
29
+ }
package/utils.ts ADDED
@@ -0,0 +1,142 @@
1
+ import { privateDecrypt, publicEncrypt, randomInt } from "node:crypto"
2
+ import os from "node:os"
3
+ import chalk from "chalk"
4
+ import cuid2 from "@paralleldrive/cuid2"
5
+
6
+ export const pin = z.string().min(6).max(6).regex(/\d{6}/)
7
+
8
+ export function createPIN(): string {
9
+ return randomInt(0, 1000000).toString().padStart(6, "0")
10
+ }
11
+ export const createID = cuid2.init({ length: 32 })
12
+
13
+ const hasher = new Bun.CryptoHasher("sha256")
14
+ export function hash(password: string): string {
15
+ return hasher.update(password).digest("hex")
16
+ }
17
+
18
+ export function timestamp(): string
19
+ export function timestamp(date: Date): string
20
+ export function timestamp(date?: Date): string {
21
+ if (!date) date = new Date()
22
+ return date.toISOString().slice(0, 19).replace("T", " ")
23
+ }
24
+
25
+ export function log(...message: unknown[]) {
26
+ console.log(chalk.gray(timestamp()), ...message)
27
+ }
28
+ export function error(...message: unknown[]) {
29
+ console.error(chalk.red(timestamp()), ...message)
30
+ }
31
+
32
+ export async function input(prompt: string = chalk.black("> ")) {
33
+ await Bun.stdout.write(prompt)
34
+ for await (const line of console) {
35
+ return line
36
+ }
37
+ }
38
+
39
+ export type HTTPLog = {
40
+ timestamp: string
41
+ method: string
42
+ url: string
43
+ IP: string
44
+ request_id?: string
45
+ service?: string
46
+ level: "info" | "warn" | "error"
47
+ }
48
+
49
+ /**
50
+ * Returns a structured JSON log of the request
51
+ */
52
+ export function formatRequest(req: Request): HTTPLog {
53
+ return {
54
+ timestamp: new Date().toISOString(),
55
+ method: req.method,
56
+ url: new URL(req.url).pathname,
57
+ IP: req.headers.get("x-forwarded-for") ?? "unknown",
58
+ request_id: req.headers.get("x-request-id") ?? "none",
59
+ service: "app",
60
+ level: "info",
61
+ }
62
+ }
63
+
64
+ /**
65
+ * Returns a one-line pretty string with colors using chalk
66
+ */
67
+ export function prettifyRequest(req: Request): string {
68
+ const url = chalk.white(new URL(req.url).pathname)
69
+ return `${chalk.gray(timestamp())} ${chalk.red(req.ip.address)} ${chalk.green(req.method)} ${url}`
70
+ }
71
+
72
+ interface Adapter {
73
+ adapter: string
74
+ networks: Bun.SocketAddress[]
75
+ }
76
+
77
+ export function listAdapters(): Adapter[] {
78
+ const interfaces = os.networkInterfaces()
79
+ const results: Adapter[] = []
80
+
81
+ for (const [name, infos] of Object.entries(interfaces)) {
82
+ if (!infos) continue
83
+
84
+ const networks: Bun.SocketAddress[] = []
85
+
86
+ for (const info of infos) {
87
+ if (!info.address) continue
88
+
89
+ networks.push({
90
+ address: info.address,
91
+ family: info.family,
92
+ port: 0,
93
+ })
94
+ }
95
+
96
+ results.push({
97
+ adapter: name,
98
+ networks,
99
+ })
100
+ }
101
+
102
+ return results
103
+ }
104
+
105
+ import { generateKeyPairSync } from "node:crypto"
106
+ import z from "zod"
107
+
108
+ type KeyPair = {
109
+ public: string
110
+ private: string
111
+ }
112
+
113
+ export async function key(): Promise<KeyPair> {
114
+ const { publicKey, privateKey } = generateKeyPairSync("rsa", {
115
+ modulusLength: 2048,
116
+ publicKeyEncoding: {
117
+ type: "spki",
118
+ format: "pem",
119
+ },
120
+ privateKeyEncoding: {
121
+ type: "pkcs8",
122
+ format: "pem",
123
+ },
124
+ })
125
+
126
+ return {
127
+ public: publicKey,
128
+ private: privateKey,
129
+ }
130
+ }
131
+
132
+ export function encrypt(key: string, data: string) {
133
+ const buffer = Buffer.from(data, "utf-8")
134
+ const encrypted = publicEncrypt(key, buffer)
135
+ return encrypted.toString("hex")
136
+ }
137
+
138
+ export function decrypt(key: string, encrypted: string) {
139
+ const buffer = Buffer.from(encrypted, "hex")
140
+ const decrypted = privateDecrypt(key, buffer)
141
+ return decrypted.toString("utf-8")
142
+ }
package/global.d.ts DELETED
@@ -1,3 +0,0 @@
1
- declare global {
2
- var Volter: typeof import("volter");
3
- }
package/volter.d.ts DELETED
@@ -1,80 +0,0 @@
1
- declare module 'volter' {
2
- import type { Elysia } from 'elysia'
3
- import type { ConverterOptions } from 'showdown'
4
-
5
- //! Properties //!
6
- export const HTTPMethods: [
7
- 'ACL',
8
- 'BIND',
9
- 'CHECKOUT',
10
- 'CONNECT',
11
- 'COPY',
12
- 'DELETE',
13
- 'GET',
14
- 'HEAD',
15
- 'LINK',
16
- 'LOCK',
17
- 'M-SEARCH',
18
- 'MERGE',
19
- 'MKACTIVITY',
20
- 'MKCALENDAR',
21
- 'MKCOL',
22
- 'MOVE',
23
- 'NOTIFY',
24
- 'OPTIONS',
25
- 'PATCH',
26
- 'POST',
27
- 'PROPFIND',
28
- 'PROPPATCH',
29
- 'PURGE',
30
- 'PUT',
31
- 'REBIND',
32
- 'REPORT',
33
- 'SEARCH',
34
- 'SOURCE',
35
- 'SUBSCRIBE',
36
- 'TRACE',
37
- 'UNBIND',
38
- 'UNLINK',
39
- 'UNLOCK',
40
- 'UNSUBSCRIBE',
41
- 'ALL'
42
- ]
43
-
44
- //! Tools //!
45
- export function readFilesSync(dir: string): Generator<string>
46
-
47
- /**
48
- * Markdown to HTML converter using [Showdown](https://github.com/showdownjs/showdown).
49
- *
50
- * [MDN Guide](https://developer.mozilla.org/en-US/docs/MDN/Writing_guidelines/Howto/Markdown_in_MDN)
51
- *
52
- * @param markdown as a String
53
- * @param options to customize the converter
54
- */
55
- export function markdown(markdown: string, options?: ConverterOptions): string
56
-
57
- //! Interfaces & Types //!
58
- export interface StaticOptions {
59
- path?: string
60
- prefix?: string
61
- }
62
-
63
- export type jsExt = '.js' | '.jsx' | '.ts' | '.tsx' | '.mjs' | '.cjs'
64
-
65
- //! Plugins //!
66
- /**
67
- * ElysiaJS plugin for File System Routing
68
- * @param options The options to use when creating the router
69
- * @param options.dir The root directory containing the files to route
70
- */
71
- export function FileSystemRouter<D extends string>(options: {
72
- dir: D,
73
- fileExtensions?: jsExt[] | string[]
74
- }): Elysia
75
-
76
- export function staticFiles(options?: StaticOptions): Elysia
77
-
78
- //! 3rd Party Modules //!
79
- export * from '@elysiajs/cors'
80
- }