roccodb-iaaryan 1.0.0 → 1.0.2
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 +270 -0
- package/index.js +5 -5
- package/package.json +11 -5
- package/roccodb-site/index.html +265 -0
- package/roccodb-site/script.js +83 -0
- package/roccodb-site/style.css +205 -0
- package/src/Collection.h +4 -4
- package/src/binding.cpp +2 -2
- package/src/main.cpp +5 -5
- package/public/index.html +0 -132
- package/roccodb-1.0.0.tgz +0 -0
package/README.md
ADDED
|
@@ -0,0 +1,270 @@
|
|
|
1
|
+
# roccoDB
|
|
2
|
+
|
|
3
|
+
A local, folder-based NoSQL document database built from scratch in C++20 and exposed to Node.js through a native Node-API binding.
|
|
4
|
+
|
|
5
|
+
> **Status:** Experimental / educational release. roccoDB is not production-ready yet.
|
|
6
|
+
|
|
7
|
+
## What is roccoDB?
|
|
8
|
+
|
|
9
|
+
roccoDB is an embedded database that runs directly inside your Node.js application.
|
|
10
|
+
|
|
11
|
+
It does not require MySQL, MongoDB, SQLite, Firebase, or a separate database server.
|
|
12
|
+
|
|
13
|
+
Your data stays locally on the machine or server where your application is running.
|
|
14
|
+
|
|
15
|
+
```text
|
|
16
|
+
Node.js Application
|
|
17
|
+
↓
|
|
18
|
+
roccoDB JavaScript API
|
|
19
|
+
↓
|
|
20
|
+
Node-API Native Binding
|
|
21
|
+
↓
|
|
22
|
+
C++20 Database Engine
|
|
23
|
+
↓
|
|
24
|
+
Local Database Folder
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
## Features
|
|
28
|
+
|
|
29
|
+
- Local and embedded database
|
|
30
|
+
- Custom C++20 storage engine
|
|
31
|
+
- Persistent folder-based storage
|
|
32
|
+
- Append-only data storage
|
|
33
|
+
- In-memory ID index for fast lookups
|
|
34
|
+
- Basic Write-Ahead Logging (WAL)
|
|
35
|
+
- JavaScript object storage
|
|
36
|
+
- Insert and get documents by ID
|
|
37
|
+
- Local collection change events
|
|
38
|
+
- Separate image and file storage
|
|
39
|
+
- No external database server required
|
|
40
|
+
|
|
41
|
+
## Installation
|
|
42
|
+
|
|
43
|
+
```bash
|
|
44
|
+
npm install roccodb-iaaryan
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
roccoDB contains a native C++ addon, so native build tools may be required on your system.
|
|
48
|
+
|
|
49
|
+
## Quick Start
|
|
50
|
+
|
|
51
|
+
```javascript
|
|
52
|
+
const { RoccoDB } = require("roccodb-iaaryan");
|
|
53
|
+
|
|
54
|
+
const db = new RoccoDB("./my_database");
|
|
55
|
+
|
|
56
|
+
const users = db.collection("users");
|
|
57
|
+
|
|
58
|
+
const id = users.insert({
|
|
59
|
+
name: "Aaryan",
|
|
60
|
+
age: 20,
|
|
61
|
+
active: true
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
console.log("Inserted ID:", id);
|
|
65
|
+
|
|
66
|
+
const user = users.get(id);
|
|
67
|
+
|
|
68
|
+
console.log("User:", user);
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
roccoDB automatically creates and manages the local database folder.
|
|
72
|
+
|
|
73
|
+
## Collections
|
|
74
|
+
|
|
75
|
+
Organize documents into separate collections:
|
|
76
|
+
|
|
77
|
+
```javascript
|
|
78
|
+
const users = db.collection("users");
|
|
79
|
+
const posts = db.collection("posts");
|
|
80
|
+
const messages = db.collection("messages");
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
Insert a document:
|
|
84
|
+
|
|
85
|
+
```javascript
|
|
86
|
+
const postId = posts.insert({
|
|
87
|
+
title: "Hello roccoDB",
|
|
88
|
+
content: "My first local database post",
|
|
89
|
+
published: true
|
|
90
|
+
});
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
Read it back using its ID:
|
|
94
|
+
|
|
95
|
+
```javascript
|
|
96
|
+
const post = posts.get(postId);
|
|
97
|
+
|
|
98
|
+
console.log(post);
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
## Local Change Events
|
|
102
|
+
|
|
103
|
+
Listen for changes inside a collection:
|
|
104
|
+
|
|
105
|
+
```javascript
|
|
106
|
+
const messages = db.collection("messages");
|
|
107
|
+
|
|
108
|
+
messages.onChange((event) => {
|
|
109
|
+
console.log("Database changed:", event);
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
messages.insert({
|
|
113
|
+
text: "Hello from roccoDB!"
|
|
114
|
+
});
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
`onChange()` provides local change notifications inside the running application.
|
|
118
|
+
|
|
119
|
+
It is not cloud synchronization and does not automatically synchronize data between different machines or remote clients.
|
|
120
|
+
|
|
121
|
+
## Image and File Storage
|
|
122
|
+
|
|
123
|
+
roccoDB keeps larger files separate from normal document records.
|
|
124
|
+
|
|
125
|
+
```javascript
|
|
126
|
+
const imageRef = db.storage.upload("./images/profile.jpg");
|
|
127
|
+
|
|
128
|
+
const users = db.collection("users");
|
|
129
|
+
|
|
130
|
+
users.insert({
|
|
131
|
+
name: "Aaryan",
|
|
132
|
+
profileImage: imageRef
|
|
133
|
+
});
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
The document stores a lightweight reference while the file is managed separately by roccoDB storage.
|
|
137
|
+
|
|
138
|
+
## Example: Social Media Post
|
|
139
|
+
|
|
140
|
+
```javascript
|
|
141
|
+
const { RoccoDB } = require("roccodb-iaaryan");
|
|
142
|
+
|
|
143
|
+
const db = new RoccoDB("./social_database");
|
|
144
|
+
|
|
145
|
+
const posts = db.collection("posts");
|
|
146
|
+
|
|
147
|
+
const imageRef = db.storage.upload("./images/post.jpg");
|
|
148
|
+
|
|
149
|
+
const postId = posts.insert({
|
|
150
|
+
author: "Aaryan",
|
|
151
|
+
caption: "Building my own database from scratch 🚀",
|
|
152
|
+
image: imageRef,
|
|
153
|
+
likes: 0
|
|
154
|
+
});
|
|
155
|
+
|
|
156
|
+
const post = posts.get(postId);
|
|
157
|
+
|
|
158
|
+
console.log(post);
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
## Current JavaScript API
|
|
162
|
+
|
|
163
|
+
### Create a Database
|
|
164
|
+
|
|
165
|
+
```javascript
|
|
166
|
+
const db = new RoccoDB("./data");
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
### Open a Collection
|
|
170
|
+
|
|
171
|
+
```javascript
|
|
172
|
+
const users = db.collection("users");
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
### Insert a Document
|
|
176
|
+
|
|
177
|
+
```javascript
|
|
178
|
+
const id = users.insert({
|
|
179
|
+
name: "Aaryan"
|
|
180
|
+
});
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
### Get a Document
|
|
184
|
+
|
|
185
|
+
```javascript
|
|
186
|
+
const user = users.get(id);
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
### Watch Collection Changes
|
|
190
|
+
|
|
191
|
+
```javascript
|
|
192
|
+
users.onChange((event) => {
|
|
193
|
+
console.log(event);
|
|
194
|
+
});
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
### Store a File
|
|
198
|
+
|
|
199
|
+
```javascript
|
|
200
|
+
const fileRef = db.storage.upload("./photo.jpg");
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
## How Data Is Stored
|
|
204
|
+
|
|
205
|
+
roccoDB stores data locally inside the database path you provide:
|
|
206
|
+
|
|
207
|
+
```javascript
|
|
208
|
+
const db = new RoccoDB("./my_database");
|
|
209
|
+
```
|
|
210
|
+
|
|
211
|
+
The database engine manages its own local collections, storage files, indexes, and system data inside that directory.
|
|
212
|
+
|
|
213
|
+
The exact internal storage structure may evolve between experimental versions.
|
|
214
|
+
|
|
215
|
+
## Current Limitations
|
|
216
|
+
|
|
217
|
+
roccoDB v1.0.0 is an experimental release.
|
|
218
|
+
|
|
219
|
+
Currently:
|
|
220
|
+
|
|
221
|
+
- JavaScript API exposes `insert()` and `get()`
|
|
222
|
+
- Advanced queries and filtering are not available yet
|
|
223
|
+
- JavaScript-level update and delete APIs are not available yet
|
|
224
|
+
- Change events are local, not network-based realtime sync
|
|
225
|
+
- Advanced concurrent access is not yet supported
|
|
226
|
+
- Cross-platform compatibility is still evolving
|
|
227
|
+
- It should not be used for sensitive or critical production data
|
|
228
|
+
|
|
229
|
+
## Good Use Cases
|
|
230
|
+
|
|
231
|
+
roccoDB is currently suitable for:
|
|
232
|
+
|
|
233
|
+
- Learning database internals
|
|
234
|
+
- Local Node.js applications
|
|
235
|
+
- Prototypes
|
|
236
|
+
- Experimental backend projects
|
|
237
|
+
- Small developer tools
|
|
238
|
+
- Exploring C++ and JavaScript native integration
|
|
239
|
+
|
|
240
|
+
## Project Goal
|
|
241
|
+
|
|
242
|
+
The goal of roccoDB is to explore how a database works from the storage layer upward:
|
|
243
|
+
|
|
244
|
+
```text
|
|
245
|
+
Binary Storage
|
|
246
|
+
↓
|
|
247
|
+
Indexing
|
|
248
|
+
↓
|
|
249
|
+
Write-Ahead Logging
|
|
250
|
+
↓
|
|
251
|
+
C++ Database Engine
|
|
252
|
+
↓
|
|
253
|
+
Node.js Native Binding
|
|
254
|
+
↓
|
|
255
|
+
JavaScript Developer API
|
|
256
|
+
```
|
|
257
|
+
|
|
258
|
+
Future versions may expand the JavaScript API with richer CRUD operations, querying, indexing, storage features, and improved reliability.
|
|
259
|
+
|
|
260
|
+
## License
|
|
261
|
+
|
|
262
|
+
ISC
|
|
263
|
+
|
|
264
|
+
## Author
|
|
265
|
+
|
|
266
|
+
Built by Aaryan.
|
|
267
|
+
|
|
268
|
+
---
|
|
269
|
+
|
|
270
|
+
If you find a bug or want to contribute, contributions and feedback are welcome.
|
package/index.js
CHANGED
|
@@ -4,26 +4,26 @@ class Collection {
|
|
|
4
4
|
constructor(dbNative, name) {
|
|
5
5
|
this.dbNative = dbNative;
|
|
6
6
|
this.name = name;
|
|
7
|
-
this.listeners = []; //
|
|
7
|
+
this.listeners = []; // Keep track of active listeners
|
|
8
8
|
}
|
|
9
9
|
|
|
10
|
-
//
|
|
10
|
+
// Register a listener for collection changes
|
|
11
11
|
onChange(callback) {
|
|
12
12
|
this.listeners.push(callback);
|
|
13
|
-
//
|
|
13
|
+
// Return a cleanup function for this listener
|
|
14
14
|
return () => {
|
|
15
15
|
this.listeners = this.listeners.filter(cb => cb !== callback);
|
|
16
16
|
};
|
|
17
17
|
}
|
|
18
18
|
|
|
19
|
-
//
|
|
19
|
+
// Notify every active listener
|
|
20
20
|
_notify(eventType, data) {
|
|
21
21
|
this.listeners.forEach(cb => cb({ type: eventType, data: data }));
|
|
22
22
|
}
|
|
23
23
|
|
|
24
24
|
insert(data) {
|
|
25
25
|
const id = this.dbNative.insert(this.name, JSON.stringify(data));
|
|
26
|
-
//
|
|
26
|
+
// Notify listeners after the record is saved
|
|
27
27
|
this._notify("INSERT", { id: id, ...data });
|
|
28
28
|
return id;
|
|
29
29
|
}
|
package/package.json
CHANGED
|
@@ -1,17 +1,23 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "roccodb-iaaryan",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.2",
|
|
4
4
|
"description": "A high-performance folder-based NoSQL database engine written in C++",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"scripts": {
|
|
7
7
|
"install": "npx cmake-js compile",
|
|
8
8
|
"test": "node test.js"
|
|
9
9
|
},
|
|
10
|
-
"keywords": [
|
|
10
|
+
"keywords": [
|
|
11
|
+
"database",
|
|
12
|
+
"cpp",
|
|
13
|
+
"nosql",
|
|
14
|
+
"local",
|
|
15
|
+
"embedded"
|
|
16
|
+
],
|
|
11
17
|
"author": "Aaryan",
|
|
12
18
|
"license": "ISC",
|
|
13
19
|
"dependencies": {
|
|
14
|
-
"cmake-js": "^7.3.0",
|
|
15
|
-
"node-addon-api": "^8.0.0"
|
|
20
|
+
"cmake-js": "^7.3.0",
|
|
21
|
+
"node-addon-api": "^8.0.0"
|
|
16
22
|
}
|
|
17
|
-
}
|
|
23
|
+
}
|
|
@@ -0,0 +1,265 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8">
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
6
|
+
<meta name="description" content="roccoDB is an experimental local, folder-based NoSQL document database with a C++20 core and a Node.js API.">
|
|
7
|
+
<meta name="theme-color" content="#000000">
|
|
8
|
+
<meta property="og:title" content="roccoDB — The Local C++ Database for Node.js">
|
|
9
|
+
<meta property="og:description" content="A local, folder-based NoSQL document database built from scratch in C++20 and exposed to Node.js.">
|
|
10
|
+
<meta property="og:type" content="website">
|
|
11
|
+
<title>roccoDB — The Local C++ Database for Node.js</title>
|
|
12
|
+
<link rel="preconnect" href="https://fonts.googleapis.com">
|
|
13
|
+
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
|
14
|
+
<link href="https://fonts.googleapis.com/css2?family=Geist:wght@300;400;500;600&family=JetBrains+Mono:wght@400;500&display=swap" rel="stylesheet">
|
|
15
|
+
<link rel="stylesheet" href="style.css">
|
|
16
|
+
</head>
|
|
17
|
+
<body>
|
|
18
|
+
<div class="page-noise" aria-hidden="true"></div>
|
|
19
|
+
<div class="ambient-glow top-glow" aria-hidden="true"></div>
|
|
20
|
+
|
|
21
|
+
<header class="site-header">
|
|
22
|
+
<nav class="nav shell" aria-label="Main navigation">
|
|
23
|
+
<a class="brand" href="#top" aria-label="roccoDB home">
|
|
24
|
+
<span>roccoDB</span>
|
|
25
|
+
</a>
|
|
26
|
+
|
|
27
|
+
<button class="nav-toggle" type="button" aria-label="Open navigation" aria-expanded="false">
|
|
28
|
+
<span></span><span></span>
|
|
29
|
+
</button>
|
|
30
|
+
|
|
31
|
+
<div class="nav-links">
|
|
32
|
+
<a href="#features">Features</a>
|
|
33
|
+
<a href="#architecture">Architecture</a>
|
|
34
|
+
<a href="#quick-start">Quick Start</a>
|
|
35
|
+
<a href="#status">Status</a>
|
|
36
|
+
<a class="nav-pill" href="https://www.npmjs.com/package/roccodb-iaaryan" target="_blank" rel="noreferrer">npm ↗</a>
|
|
37
|
+
</div>
|
|
38
|
+
</nav>
|
|
39
|
+
</header>
|
|
40
|
+
|
|
41
|
+
<main id="top">
|
|
42
|
+
<section class="hero shell">
|
|
43
|
+
<h1 class="reveal">the local <span class="gradient-text">database</span><br>for node.js</h1>
|
|
44
|
+
|
|
45
|
+
<p class="hero-copy reveal">
|
|
46
|
+
A folder-based NoSQL document database built from scratch in C++20,
|
|
47
|
+
connected to Node.js through a native Node-API bridge. No separate database server required.
|
|
48
|
+
</p>
|
|
49
|
+
|
|
50
|
+
<div class="hero-actions reveal">
|
|
51
|
+
<a class="button primary" href="#quick-start">Get Started</a>
|
|
52
|
+
<a class="button secondary" href="https://www.npmjs.com/package/roccodb-iaaryan" target="_blank" rel="noreferrer">View on npm</a>
|
|
53
|
+
</div>
|
|
54
|
+
|
|
55
|
+
<div class="install-card reveal glass-panel">
|
|
56
|
+
<div class="window-bar">
|
|
57
|
+
<div class="traffic-lights" aria-hidden="true"><i></i><i></i><i></i></div>
|
|
58
|
+
<span class="window-title">terminal</span>
|
|
59
|
+
</div>
|
|
60
|
+
<div class="install-line">
|
|
61
|
+
<span class="prompt">~</span>
|
|
62
|
+
<code>npm install roccodb-iaaryan</code>
|
|
63
|
+
<button class="copy-button" type="button" data-copy="npm install roccodb-iaaryan" aria-label="Copy install command">Copy</button>
|
|
64
|
+
</div>
|
|
65
|
+
</div>
|
|
66
|
+
|
|
67
|
+
<div class="hero-meta reveal">
|
|
68
|
+
<span>C++20 Core</span>
|
|
69
|
+
<span class="separator">·</span>
|
|
70
|
+
<span>Node-API Bridge</span>
|
|
71
|
+
<span class="separator">·</span>
|
|
72
|
+
<span>Zero Config</span>
|
|
73
|
+
</div>
|
|
74
|
+
</section>
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
<section id="features" class="section shell">
|
|
79
|
+
<div class="section-heading reveal text-center">
|
|
80
|
+
<h2>Small stack. Low-level core.</h2>
|
|
81
|
+
<p>roccoDB keeps the developer API in clean JavaScript while the raw performance and persistence live in C++.</p>
|
|
82
|
+
</div>
|
|
83
|
+
|
|
84
|
+
<div class="feature-grid">
|
|
85
|
+
<article class="feature-card featured reveal glass-panel">
|
|
86
|
+
<div class="feature-icon"></></div>
|
|
87
|
+
<h3>C++20 Core</h3>
|
|
88
|
+
<p>The storage engine is built from scratch in modern C++, handling all persistence and memory management directly beneath the JavaScript API.</p>
|
|
89
|
+
<div class="mini-code">
|
|
90
|
+
<span class="muted">Engine</span><span class="value">rocco::database</span>
|
|
91
|
+
<span class="muted">Bridge</span><span class="value">Node-API</span>
|
|
92
|
+
</div>
|
|
93
|
+
</article>
|
|
94
|
+
|
|
95
|
+
<article class="feature-card reveal glass-panel">
|
|
96
|
+
<div class="feature-icon">↳</div>
|
|
97
|
+
<h3>Embedded & Local</h3>
|
|
98
|
+
<p>Run the database with your Node.js application. Your data stays locally on your machine or server.</p>
|
|
99
|
+
</article>
|
|
100
|
+
|
|
101
|
+
<article class="feature-card reveal glass-panel">
|
|
102
|
+
<div class="feature-icon">⌁</div>
|
|
103
|
+
<h3>O(1) Indexing</h3>
|
|
104
|
+
<p>An in-memory <code>unordered_map</code> maps document IDs directly to byte offsets for lightning-fast lookups.</p>
|
|
105
|
+
</article>
|
|
106
|
+
|
|
107
|
+
<article class="feature-card reveal glass-panel">
|
|
108
|
+
<div class="feature-icon">◫</div>
|
|
109
|
+
<h3>Media Isolation</h3>
|
|
110
|
+
<p>Files and images are stored efficiently outside normal document records via OS-level copies.</p>
|
|
111
|
+
</article>
|
|
112
|
+
|
|
113
|
+
<article class="feature-card reveal glass-panel">
|
|
114
|
+
<div class="feature-icon">◉</div>
|
|
115
|
+
<h3>Real-Time Watchers</h3>
|
|
116
|
+
<p>Observe collection changes inside the running application instantly with the <code>onChange()</code> API.</p>
|
|
117
|
+
</article>
|
|
118
|
+
</div>
|
|
119
|
+
</section>
|
|
120
|
+
|
|
121
|
+
<section id="architecture" class="section architecture-section">
|
|
122
|
+
<div class="ambient-glow center-glow" aria-hidden="true"></div>
|
|
123
|
+
<div class="shell">
|
|
124
|
+
<div class="section-heading text-center reveal">
|
|
125
|
+
<h2>JavaScript on top. <br> C++ underneath.</h2>
|
|
126
|
+
<p>Objects move seamlessly through a native bridge into the custom engine, persisting safely inside a local directory.</p>
|
|
127
|
+
</div>
|
|
128
|
+
|
|
129
|
+
<div class="architecture-flow reveal glass-panel">
|
|
130
|
+
<div class="arch-node">
|
|
131
|
+
<strong>Node.js</strong>
|
|
132
|
+
<small>Developer Code</small>
|
|
133
|
+
</div>
|
|
134
|
+
<div class="arch-arrow">→</div>
|
|
135
|
+
<div class="arch-node">
|
|
136
|
+
<strong>roccoDB SDK</strong>
|
|
137
|
+
<small>Public API</small>
|
|
138
|
+
</div>
|
|
139
|
+
<div class="arch-arrow">→</div>
|
|
140
|
+
<div class="arch-node active">
|
|
141
|
+
<strong>Node-API</strong>
|
|
142
|
+
<small>Native Boundary</small>
|
|
143
|
+
</div>
|
|
144
|
+
<div class="arch-arrow">→</div>
|
|
145
|
+
<div class="arch-node">
|
|
146
|
+
<strong>C++20</strong>
|
|
147
|
+
<small>Storage Engine</small>
|
|
148
|
+
</div>
|
|
149
|
+
<div class="arch-arrow">→</div>
|
|
150
|
+
<div class="arch-node">
|
|
151
|
+
<strong>Local Drive</strong>
|
|
152
|
+
<small>Binary Persistence</small>
|
|
153
|
+
</div>
|
|
154
|
+
</div>
|
|
155
|
+
</div>
|
|
156
|
+
</section>
|
|
157
|
+
|
|
158
|
+
<section id="quick-start" class="section shell">
|
|
159
|
+
<div class="section-heading split reveal">
|
|
160
|
+
<div>
|
|
161
|
+
<h2>From install to data<br>in seconds.</h2>
|
|
162
|
+
</div>
|
|
163
|
+
<p>The SDK focuses on developer ergonomics. Create a database, open a collection, insert a document, and retrieve it without touching a single configuration file.</p>
|
|
164
|
+
</div>
|
|
165
|
+
|
|
166
|
+
<div class="code-window reveal glass-panel">
|
|
167
|
+
<div class="window-bar">
|
|
168
|
+
<div class="traffic-lights" aria-hidden="true"><i></i><i></i><i></i></div>
|
|
169
|
+
<div class="code-tabs">
|
|
170
|
+
<button class="code-tab active" type="button" data-tab="basic">quick-start.js</button>
|
|
171
|
+
<button class="code-tab" type="button" data-tab="events">events.js</button>
|
|
172
|
+
<button class="code-tab" type="button" data-tab="storage">storage.js</button>
|
|
173
|
+
</div>
|
|
174
|
+
<button class="copy-button code-copy" type="button" aria-label="Copy current code">Copy</button>
|
|
175
|
+
</div>
|
|
176
|
+
|
|
177
|
+
<pre class="code-panel active" data-panel="basic"><code><span class="token-keyword">const</span> { RoccoDB } = <span class="token-fn">require</span>(<span class="token-string">"roccodb-iaaryan"</span>);
|
|
178
|
+
|
|
179
|
+
<span class="token-comment">// Initializes the C++ engine & creates local folders</span>
|
|
180
|
+
<span class="token-keyword">const</span> db = <span class="token-keyword">new</span> <span class="token-class">RoccoDB</span>(<span class="token-string">"./my_database"</span>);
|
|
181
|
+
<span class="token-keyword">const</span> users = db.<span class="token-fn">collection</span>(<span class="token-string">"users"</span>);
|
|
182
|
+
|
|
183
|
+
<span class="token-comment">// Insert a standard JS object</span>
|
|
184
|
+
<span class="token-keyword">const</span> id = users.<span class="token-fn">insert</span>({
|
|
185
|
+
name: <span class="token-string">"Aaryan"</span>,
|
|
186
|
+
role: <span class="token-string">"Systems Engineer"</span>,
|
|
187
|
+
active: <span class="token-bool">true</span>
|
|
188
|
+
});
|
|
189
|
+
|
|
190
|
+
<span class="token-comment">// Fetch it back via O(1) byte-offset lookup</span>
|
|
191
|
+
<span class="token-keyword">const</span> user = users.<span class="token-fn">get</span>(id);
|
|
192
|
+
console.<span class="token-fn">log</span>(user);</code></pre>
|
|
193
|
+
|
|
194
|
+
<pre class="code-panel" data-panel="events"><code><span class="token-keyword">const</span> messages = db.<span class="token-fn">collection</span>(<span class="token-string">"messages"</span>);
|
|
195
|
+
|
|
196
|
+
<span class="token-comment">// Set up a local event emitter</span>
|
|
197
|
+
messages.<span class="token-fn">onChange</span>((event) => {
|
|
198
|
+
console.<span class="token-fn">log</span>(<span class="token-string">"Database changed:"</span>, event.type);
|
|
199
|
+
console.<span class="token-fn">log</span>(<span class="token-string">"New Data payload:"</span>, event.data);
|
|
200
|
+
});
|
|
201
|
+
|
|
202
|
+
<span class="token-comment">// Trigger the watcher instantly</span>
|
|
203
|
+
messages.<span class="token-fn">insert</span>({
|
|
204
|
+
text: <span class="token-string">"Hello from roccoDB!"</span>,
|
|
205
|
+
timestamp: Date.<span class="token-fn">now</span>()
|
|
206
|
+
});</code></pre>
|
|
207
|
+
|
|
208
|
+
<pre class="code-panel" data-panel="storage"><code><span class="token-comment">// Fast OS-level file copy to the /storage directory</span>
|
|
209
|
+
<span class="token-keyword">const</span> imageRef = db.storage.<span class="token-fn">upload</span>(
|
|
210
|
+
<span class="token-string">"./local_files/profile.jpg"</span>
|
|
211
|
+
);
|
|
212
|
+
|
|
213
|
+
<span class="token-keyword">const</span> users = db.<span class="token-fn">collection</span>(<span class="token-string">"users"</span>);
|
|
214
|
+
|
|
215
|
+
<span class="token-comment">// Keep your JSON lightweight by storing the reference ID</span>
|
|
216
|
+
users.<span class="token-fn">insert</span>({
|
|
217
|
+
name: <span class="token-string">"Aaryan"</span>,
|
|
218
|
+
profileImage: imageRef
|
|
219
|
+
});</code></pre>
|
|
220
|
+
</div>
|
|
221
|
+
</section>
|
|
222
|
+
|
|
223
|
+
<section id="status" class="section shell">
|
|
224
|
+
<div class="status-card reveal glass-panel">
|
|
225
|
+
<div class="status-top">
|
|
226
|
+
<h2>Experimental by design.</h2>
|
|
227
|
+
<span class="status-badge"><i></i> v1.0.0</span>
|
|
228
|
+
</div>
|
|
229
|
+
<p>roccoDB is an educational release. It is built for learning database internals, prototyping, and local embedded tools. It is not designed for critical production workloads.</p>
|
|
230
|
+
<div class="limitation-grid">
|
|
231
|
+
<div><p>No query/filter API yet.</p></div>
|
|
232
|
+
<div><p>JS update/delete not exposed.</p></div>
|
|
233
|
+
<div><p>Events are local, not cloud sync.</p></div>
|
|
234
|
+
<div><p>No multi-threading locks.</p></div>
|
|
235
|
+
</div>
|
|
236
|
+
</div>
|
|
237
|
+
</section>
|
|
238
|
+
|
|
239
|
+
<section class="section shell">
|
|
240
|
+
<div class="final-cta reveal glass-panel">
|
|
241
|
+
<div class="ambient-glow cta-glow" aria-hidden="true"></div>
|
|
242
|
+
<h2>Build locally. Understand deeply.</h2>
|
|
243
|
+
<p>Install the experimental release and explore a database stack from Node.js down to binary storage.</p>
|
|
244
|
+
<div class="cta-command">
|
|
245
|
+
<code>npm install roccodb-iaaryan</code>
|
|
246
|
+
<button class="copy-button" type="button" data-copy="npm install roccodb-iaaryan">Copy</button>
|
|
247
|
+
</div>
|
|
248
|
+
</div>
|
|
249
|
+
</section>
|
|
250
|
+
</main>
|
|
251
|
+
|
|
252
|
+
<footer class="footer">
|
|
253
|
+
<div class="shell footer-inner">
|
|
254
|
+
<a class="brand" href="#top"><span>roccoDB</span></a>
|
|
255
|
+
<p>Engineered from scratch by Aaryan.</p>
|
|
256
|
+
<div class="footer-links">
|
|
257
|
+
<a href="https://www.npmjs.com/package/roccodb-iaaryan" target="_blank" rel="noreferrer">npm Package</a>
|
|
258
|
+
<a href="https://github.com/iaaryans/roccoDB" target="_blank" rel="noreferrer">GitHub</a>
|
|
259
|
+
</div>
|
|
260
|
+
</div>
|
|
261
|
+
</footer>
|
|
262
|
+
|
|
263
|
+
<script src="script.js"></script>
|
|
264
|
+
</body>
|
|
265
|
+
</html>
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
// Nav Scroll Effect
|
|
2
|
+
const header = document.querySelector(".site-header");
|
|
3
|
+
const navToggle = document.querySelector(".nav-toggle");
|
|
4
|
+
const navLinks = document.querySelector(".nav-links");
|
|
5
|
+
|
|
6
|
+
window.addEventListener("scroll", () => {
|
|
7
|
+
header.classList.toggle("scrolled", window.scrollY > 20);
|
|
8
|
+
}, { passive: true });
|
|
9
|
+
|
|
10
|
+
// Mobile Nav Toggle
|
|
11
|
+
navToggle.addEventListener("click", () => {
|
|
12
|
+
const open = navLinks.classList.toggle("open");
|
|
13
|
+
navToggle.setAttribute("aria-expanded", String(open));
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
navLinks.querySelectorAll("a").forEach((link) => {
|
|
17
|
+
link.addEventListener("click", () => {
|
|
18
|
+
navLinks.classList.remove("open");
|
|
19
|
+
navToggle.setAttribute("aria-expanded", "false");
|
|
20
|
+
});
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
// Copy to Clipboard Utility
|
|
24
|
+
const setCopied = (button) => {
|
|
25
|
+
const original = button.textContent;
|
|
26
|
+
button.textContent = "Copied";
|
|
27
|
+
button.classList.add("copied");
|
|
28
|
+
window.setTimeout(() => {
|
|
29
|
+
button.textContent = original;
|
|
30
|
+
button.classList.remove("copied");
|
|
31
|
+
}, 2000); // Extended slightly for a more relaxed feel
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
document.querySelectorAll("[data-copy]").forEach((button) => {
|
|
35
|
+
button.addEventListener("click", async () => {
|
|
36
|
+
try {
|
|
37
|
+
await navigator.clipboard.writeText(button.dataset.copy);
|
|
38
|
+
setCopied(button);
|
|
39
|
+
} catch {
|
|
40
|
+
button.textContent = "Failed";
|
|
41
|
+
}
|
|
42
|
+
});
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
// Code Window Tabs
|
|
46
|
+
const tabs = document.querySelectorAll(".code-tab");
|
|
47
|
+
const panels = document.querySelectorAll(".code-panel");
|
|
48
|
+
const codeCopy = document.querySelector(".code-copy");
|
|
49
|
+
|
|
50
|
+
tabs.forEach((tab) => {
|
|
51
|
+
tab.addEventListener("click", () => {
|
|
52
|
+
tabs.forEach((item) => item.classList.remove("active"));
|
|
53
|
+
panels.forEach((panel) => panel.classList.remove("active"));
|
|
54
|
+
|
|
55
|
+
tab.classList.add("active");
|
|
56
|
+
document.querySelector(`[data-panel="${tab.dataset.tab}"]`).classList.add("active");
|
|
57
|
+
});
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
codeCopy.addEventListener("click", async () => {
|
|
61
|
+
const activeCode = document.querySelector(".code-panel.active code").innerText;
|
|
62
|
+
try {
|
|
63
|
+
await navigator.clipboard.writeText(activeCode);
|
|
64
|
+
setCopied(codeCopy);
|
|
65
|
+
} catch {
|
|
66
|
+
codeCopy.textContent = "Failed";
|
|
67
|
+
}
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
// Smooth Reveal Animations
|
|
71
|
+
const observer = new IntersectionObserver((entries) => {
|
|
72
|
+
entries.forEach((entry) => {
|
|
73
|
+
if (entry.isIntersecting) {
|
|
74
|
+
entry.target.classList.add("visible");
|
|
75
|
+
observer.unobserve(entry.target); // Only animate once
|
|
76
|
+
}
|
|
77
|
+
});
|
|
78
|
+
}, {
|
|
79
|
+
threshold: 0.15,
|
|
80
|
+
rootMargin: "0px 0px -50px 0px" // Triggers slightly before element enters view
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
document.querySelectorAll(".reveal").forEach((element) => observer.observe(element));
|
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
:root {
|
|
2
|
+
--bg: #000000;
|
|
3
|
+
--panel: rgba(20, 20, 22, 0.6);
|
|
4
|
+
--line: rgba(255, 255, 255, 0.08);
|
|
5
|
+
--line-strong: rgba(255, 255, 255, 0.15);
|
|
6
|
+
--text: #ffffff;
|
|
7
|
+
--muted: #88888b;
|
|
8
|
+
--accent: #22d3ee; /* Cyan/Emerald vibe */
|
|
9
|
+
--accent-glow: rgba(34, 211, 238, 0.15);
|
|
10
|
+
--mono: "JetBrains Mono", monospace;
|
|
11
|
+
--sans: "Geist", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
|
|
12
|
+
--radius: 16px;
|
|
13
|
+
--shell: min(1080px, calc(100% - 48px));
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
* { box-sizing: border-box; scrollbar-color: #1a1a1a transparent; scrollbar-width: thin; }
|
|
17
|
+
html { scroll-behavior: smooth; }
|
|
18
|
+
body {
|
|
19
|
+
margin: 0; background: var(--bg); color: var(--text);
|
|
20
|
+
font-family: var(--sans); line-height: 1.6; overflow-x: hidden;
|
|
21
|
+
-webkit-font-smoothing: antialiased;
|
|
22
|
+
}
|
|
23
|
+
button, a { font: inherit; }
|
|
24
|
+
a { color: inherit; text-decoration: none; transition: color 0.2s; }
|
|
25
|
+
code, pre { font-family: var(--mono); font-size: 0.9em; }
|
|
26
|
+
button { color: inherit; cursor: pointer; border: none; background: transparent; }
|
|
27
|
+
.shell { width: var(--shell); margin-inline: auto; }
|
|
28
|
+
|
|
29
|
+
/* Premium Ambient Lighting */
|
|
30
|
+
.page-noise {
|
|
31
|
+
position: fixed; inset: 0; pointer-events: none; z-index: 99; opacity: 0.03;
|
|
32
|
+
background-image: url("data:image/svg+xml,%3Csvg viewBox='0 0 200 200' xmlns='http://www.w3.org/2000/svg'%3E%3Cfilter id='noiseFilter'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.85' numOctaves='3' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23noiseFilter)'/%3E%3C/svg%3E");
|
|
33
|
+
}
|
|
34
|
+
.ambient-glow {
|
|
35
|
+
position: absolute; border-radius: 50%; filter: blur(100px); pointer-events: none; z-index: 0;
|
|
36
|
+
}
|
|
37
|
+
.top-glow { width: 600px; height: 400px; top: -100px; left: 50%; transform: translateX(-50%); background: radial-gradient(circle, var(--accent-glow) 0%, transparent 70%); }
|
|
38
|
+
.center-glow { width: 800px; height: 600px; top: 50%; left: 50%; transform: translate(-50%, -50%); background: radial-gradient(circle, rgba(34, 211, 238, 0.05) 0%, transparent 60%); }
|
|
39
|
+
|
|
40
|
+
/* Glassmorphism Utilities */
|
|
41
|
+
.glass-panel {
|
|
42
|
+
background: var(--panel);
|
|
43
|
+
backdrop-filter: blur(24px) saturate(150%);
|
|
44
|
+
-webkit-backdrop-filter: blur(24px) saturate(150%);
|
|
45
|
+
border: 1px solid var(--line);
|
|
46
|
+
box-shadow: 0 4px 30px rgba(0, 0, 0, 0.1);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/* Header */
|
|
50
|
+
.site-header {
|
|
51
|
+
position: sticky; top: 0; z-index: 50; border-bottom: 1px solid transparent;
|
|
52
|
+
background: rgba(0, 0, 0, 0.4); backdrop-filter: blur(20px) saturate(180%);
|
|
53
|
+
-webkit-backdrop-filter: blur(20px) saturate(180%); transition: border-color 0.3s;
|
|
54
|
+
}
|
|
55
|
+
.site-header.scrolled { border-color: var(--line); }
|
|
56
|
+
.nav { height: 64px; display: flex; align-items: center; justify-content: space-between; }
|
|
57
|
+
.brand { display: inline-flex; align-items: center; gap: 12px; font-weight: 500; letter-spacing: -0.02em; font-size: 15px; }
|
|
58
|
+
.brand-mark {
|
|
59
|
+
width: 26px; height: 26px; display: grid; place-items: center;
|
|
60
|
+
border: 1px solid var(--line-strong); border-radius: 6px;
|
|
61
|
+
background: linear-gradient(180deg, #1f1f1f, #000000);
|
|
62
|
+
color: var(--text); font-family: var(--mono); font-size: 13px;
|
|
63
|
+
}
|
|
64
|
+
.nav-links { display: flex; align-items: center; gap: 32px; font-size: 13px; color: var(--muted); font-weight: 400; }
|
|
65
|
+
.nav-links a:hover { color: var(--text); }
|
|
66
|
+
.nav-pill { padding: 6px 14px; border: 1px solid var(--line); border-radius: 99px; color: var(--text) !important; background: rgba(255,255,255,0.03); transition: background 0.2s; }
|
|
67
|
+
.nav-pill:hover { background: rgba(255,255,255,0.1); }
|
|
68
|
+
.nav-toggle { display: none; padding: 8px; }
|
|
69
|
+
.nav-toggle span { display: block; width: 20px; height: 1.5px; background: var(--text); margin: 5px 0; transition: 0.3s; }
|
|
70
|
+
|
|
71
|
+
/* Hero */
|
|
72
|
+
.hero { padding: 140px 0 100px; text-align: center; position: relative; z-index: 1; }
|
|
73
|
+
.eyebrow {
|
|
74
|
+
display: inline-flex; align-items: center; gap: 8px; padding: 6px 12px;
|
|
75
|
+
border: 1px solid var(--line); border-radius: 99px; background: rgba(255,255,255,0.03);
|
|
76
|
+
color: var(--muted); font-family: var(--mono); font-size: 11px; letter-spacing: 0.05em; text-transform: uppercase;
|
|
77
|
+
}
|
|
78
|
+
.status-dot { width: 6px; height: 6px; border-radius: 50%; background: var(--accent); box-shadow: 0 0 10px var(--accent); }
|
|
79
|
+
.hero h1 { margin: 32px auto 24px; font-size: clamp(48px, 7vw, 96px); line-height: 1; letter-spacing: -0.04em; font-weight: 500; }
|
|
80
|
+
.gradient-text { background: linear-gradient(135deg, #ffffff 0%, var(--accent) 100%); -webkit-background-clip: text; -webkit-text-fill-color: transparent; }
|
|
81
|
+
.hero-copy { max-width: 640px; margin: 0 auto; color: var(--muted); font-size: 18px; font-weight: 300; }
|
|
82
|
+
.hero-actions { display: flex; justify-content: center; gap: 16px; margin-top: 40px; }
|
|
83
|
+
.button { padding: 12px 24px; border-radius: 99px; font-size: 14px; font-weight: 500; transition: all 0.2s ease; border: 1px solid transparent; }
|
|
84
|
+
.button.primary { background: var(--text); color: #000000; }
|
|
85
|
+
.button.primary:hover { background: #e0e0e0; transform: scale(1.02); }
|
|
86
|
+
.button.secondary { background: rgba(255,255,255,0.05); color: var(--text); border-color: var(--line); }
|
|
87
|
+
.button.secondary:hover { background: rgba(255,255,255,0.1); }
|
|
88
|
+
.hero-meta { display: flex; justify-content: center; align-items: center; gap: 16px; margin-top: 40px; color: #555; font-family: var(--mono); font-size: 11px; text-transform: uppercase; letter-spacing: 0.05em; }
|
|
89
|
+
.hero-meta .separator { color: #333; font-size: 16px; }
|
|
90
|
+
|
|
91
|
+
/* Windows / Terminals (macOS style) */
|
|
92
|
+
.window-bar { display: flex; align-items: center; height: 48px; padding: 0 16px; border-bottom: 1px solid var(--line); background: rgba(255,255,255,0.02); }
|
|
93
|
+
.traffic-lights { display: flex; gap: 8px; margin-right: 16px; }
|
|
94
|
+
.traffic-lights i { width: 12px; height: 12px; border-radius: 50%; }
|
|
95
|
+
.traffic-lights i:nth-child(1) { background: #ff5f56; border: 1px solid #e0443e; }
|
|
96
|
+
.traffic-lights i:nth-child(2) { background: #ffbd2e; border: 1px solid #dea123; }
|
|
97
|
+
.traffic-lights i:nth-child(3) { background: #27c93f; border: 1px solid #1aab29; }
|
|
98
|
+
.window-title { margin-left: auto; margin-right: auto; color: var(--muted); font-size: 12px; font-family: var(--mono); padding-right: 48px; }
|
|
99
|
+
|
|
100
|
+
/* Install Card */
|
|
101
|
+
.install-card { max-width: 600px; margin: 64px auto 0; border-radius: 12px; overflow: hidden; text-align: left; }
|
|
102
|
+
.install-line { display: flex; align-items: center; padding: 24px; gap: 16px; }
|
|
103
|
+
.install-line code { flex: 1; font-size: 15px; color: var(--text); }
|
|
104
|
+
.prompt { color: var(--accent); font-family: var(--mono); }
|
|
105
|
+
.copy-button { padding: 6px 12px; border: 1px solid var(--line); border-radius: 6px; background: rgba(255,255,255,0.05); font-family: var(--mono); font-size: 11px; color: var(--muted); transition: 0.2s; }
|
|
106
|
+
.copy-button:hover, .copy-button.copied { color: var(--text); background: rgba(255,255,255,0.1); border-color: var(--line-strong); }
|
|
107
|
+
|
|
108
|
+
/* Ticker */
|
|
109
|
+
.ticker { overflow: hidden; border-block: 1px solid var(--line); padding: 16px 0; background: rgba(255,255,255,0.01); color: #444; font-family: var(--mono); font-size: 12px; letter-spacing: 0.1em; }
|
|
110
|
+
.ticker-track { width: max-content; display: flex; gap: 40px; animation: ticker 40s linear infinite; }
|
|
111
|
+
.ticker b { color: #222; font-size: 10px; }
|
|
112
|
+
@keyframes ticker { to { transform: translateX(-50%); } }
|
|
113
|
+
|
|
114
|
+
/* Sections */
|
|
115
|
+
.section { padding: 140px 0; position: relative; z-index: 1; }
|
|
116
|
+
.section-heading { margin-bottom: 64px; }
|
|
117
|
+
.text-center { text-align: center; max-width: 700px; margin-inline: auto; }
|
|
118
|
+
.section-heading.split { display: grid; grid-template-columns: 1fr 1fr; align-items: center; gap: 40px; }
|
|
119
|
+
.section-heading h2 { margin: 0; font-size: clamp(32px, 4vw, 56px); line-height: 1.1; letter-spacing: -0.03em; font-weight: 500; }
|
|
120
|
+
.section-heading p { color: var(--muted); margin: 24px 0 0; font-size: 18px; font-weight: 300; }
|
|
121
|
+
|
|
122
|
+
/* Features */
|
|
123
|
+
.feature-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 24px; }
|
|
124
|
+
.feature-card { padding: 32px; border-radius: var(--radius); display: flex; flex-direction: column; }
|
|
125
|
+
.feature-card.featured { grid-column: span 2; }
|
|
126
|
+
.feature-icon { width: 48px; height: 48px; display: grid; place-items: center; border: 1px solid var(--line); border-radius: 12px; font-family: var(--mono); font-size: 18px; color: var(--text); background: rgba(255,255,255,0.03); margin-bottom: 32px; box-shadow: inset 0 2px 10px rgba(255,255,255,0.05); }
|
|
127
|
+
.feature-card h3 { margin: 0 0 12px; font-size: 20px; font-weight: 500; letter-spacing: -0.01em; }
|
|
128
|
+
.feature-card p { color: var(--muted); margin: 0; font-size: 15px; font-weight: 300; line-height: 1.5; flex: 1; }
|
|
129
|
+
.mini-code { margin-top: 32px; padding: 16px; border: 1px solid var(--line); border-radius: 8px; display: grid; grid-template-columns: 80px 1fr; gap: 10px; font-family: var(--mono); font-size: 12px; background: rgba(0,0,0,0.4); }
|
|
130
|
+
.mini-code .value { color: var(--text); }
|
|
131
|
+
|
|
132
|
+
/* Architecture */
|
|
133
|
+
.architecture-section { border-block: 1px solid var(--line); }
|
|
134
|
+
.architecture-flow { display: grid; grid-template-columns: 1fr auto 1fr auto 1fr auto 1fr auto 1fr; align-items: center; gap: 16px; padding: 32px; border-radius: 24px; }
|
|
135
|
+
.arch-node { text-align: center; padding: 24px 16px; }
|
|
136
|
+
.arch-node.active strong { color: var(--accent); }
|
|
137
|
+
.arch-node strong { display: block; font-size: 15px; font-weight: 500; margin-bottom: 4px; }
|
|
138
|
+
.arch-node small { color: var(--muted); font-size: 12px; font-family: var(--mono); }
|
|
139
|
+
.arch-arrow { color: #333; font-family: var(--mono); }
|
|
140
|
+
|
|
141
|
+
/* Code Window */
|
|
142
|
+
.code-window { border-radius: 12px; }
|
|
143
|
+
.code-window .window-bar { justify-content: flex-start; }
|
|
144
|
+
.code-tabs { display: flex; gap: 8px; margin-left: 24px; height: 100%; }
|
|
145
|
+
.code-tab { border: none; border-bottom: 2px solid transparent; background: transparent; padding: 0 12px; color: #666; font-family: var(--mono); font-size: 12px; transition: 0.2s; }
|
|
146
|
+
.code-tab.active { color: var(--text); border-bottom-color: var(--text); }
|
|
147
|
+
.code-copy { margin-left: auto; }
|
|
148
|
+
.code-panel { display: none; margin: 0; padding: 40px; min-height: 400px; overflow: auto; font-size: 14px; line-height: 1.7; background: rgba(0,0,0,0.3); }
|
|
149
|
+
.code-panel.active { display: block; }
|
|
150
|
+
.token-keyword { color: #ff7b72; } .token-fn { color: #d2a8ff; } .token-string { color: #a5d6ff; } .token-class { color: #79c0ff; } .token-bool { color: #79c0ff; } .token-comment { color: #8b949e; font-style: italic; }
|
|
151
|
+
|
|
152
|
+
/* Status */
|
|
153
|
+
.status-card { padding: 64px; border-radius: 24px; }
|
|
154
|
+
.status-top { display: flex; justify-content: space-between; align-items: flex-start; margin-bottom: 24px; }
|
|
155
|
+
.status-top h2 { margin: 0; font-size: 32px; font-weight: 500; }
|
|
156
|
+
.status-badge { display: flex; align-items: center; gap: 8px; border: 1px solid var(--line); background: rgba(255,255,255,0.05); color: var(--text); padding: 6px 14px; border-radius: 99px; font-family: var(--mono); font-size: 12px; }
|
|
157
|
+
.status-card > p { max-width: 600px; color: var(--muted); font-size: 16px; margin: 0 0 48px; font-weight: 300; }
|
|
158
|
+
.limitation-grid { display: grid; grid-template-columns: repeat(4, 1fr); gap: 24px; border-top: 1px solid var(--line); padding-top: 32px; }
|
|
159
|
+
.limitation-grid p { color: #999; font-size: 14px; margin: 0; }
|
|
160
|
+
|
|
161
|
+
/* CTA */
|
|
162
|
+
.final-cta { text-align: center; padding: 100px 32px; border-radius: 24px; position: relative; overflow: hidden; }
|
|
163
|
+
.cta-glow { width: 400px; height: 400px; top: 50%; left: 50%; transform: translate(-50%, -50%); background: radial-gradient(circle, var(--accent-glow) 0%, transparent 70%); }
|
|
164
|
+
.final-cta h2 { margin: 0; font-size: 48px; font-weight: 500; position: relative; z-index: 1; }
|
|
165
|
+
.final-cta p { color: var(--muted); margin: 24px auto 40px; font-size: 18px; max-width: 500px; font-weight: 300; position: relative; z-index: 1; }
|
|
166
|
+
.cta-command { max-width: 400px; margin: 0 auto; display: flex; align-items: center; justify-content: space-between; padding: 8px 8px 8px 24px; border: 1px solid var(--line); border-radius: 12px; background: rgba(0,0,0,0.5); position: relative; z-index: 1; }
|
|
167
|
+
.cta-command code { font-size: 13px; }
|
|
168
|
+
|
|
169
|
+
/* Footer */
|
|
170
|
+
.footer { border-top: 1px solid var(--line); background: rgba(0,0,0,0.5); padding: 48px 0; }
|
|
171
|
+
.footer-inner { display: flex; justify-content: space-between; align-items: center; font-size: 13px; color: var(--muted); }
|
|
172
|
+
.footer-inner > p { margin: 0; }
|
|
173
|
+
.footer-links { display: flex; gap: 24px; }
|
|
174
|
+
.footer-links a:hover { color: var(--text); }
|
|
175
|
+
|
|
176
|
+
/* Animations */
|
|
177
|
+
.reveal { opacity: 0; transform: translateY(20px); transition: opacity 0.8s cubic-bezier(0.16, 1, 0.3, 1), transform 0.8s cubic-bezier(0.16, 1, 0.3, 1); }
|
|
178
|
+
.reveal.visible { opacity: 1; transform: none; }
|
|
179
|
+
|
|
180
|
+
/* Responsive */
|
|
181
|
+
@media (max-width: 900px) {
|
|
182
|
+
.nav-toggle { display: block; }
|
|
183
|
+
.nav-links { position: absolute; top: 64px; left: 16px; right: 16px; display: none; flex-direction: column; background: rgba(20,20,22,0.95); backdrop-filter: blur(20px); border: 1px solid var(--line); border-radius: 16px; padding: 16px; text-align: center; }
|
|
184
|
+
.nav-links.open { display: flex; }
|
|
185
|
+
.section-heading.split { grid-template-columns: 1fr; text-align: center; gap: 16px; }
|
|
186
|
+
.section-heading.split p { margin-inline: auto; }
|
|
187
|
+
.feature-grid { grid-template-columns: 1fr; }
|
|
188
|
+
.feature-card.featured { grid-column: 1; }
|
|
189
|
+
.architecture-flow { grid-template-columns: 1fr; padding: 24px; }
|
|
190
|
+
.arch-arrow { transform: rotate(90deg); padding: 16px 0; }
|
|
191
|
+
.limitation-grid { grid-template-columns: 1fr 1fr; }
|
|
192
|
+
.footer-inner { flex-direction: column; gap: 24px; text-align: center; }
|
|
193
|
+
}
|
|
194
|
+
@media (max-width: 600px) {
|
|
195
|
+
.hero { padding: 100px 0 64px; }
|
|
196
|
+
.hero h1 { font-size: 40px; }
|
|
197
|
+
.hero-actions { flex-direction: column; }
|
|
198
|
+
.install-line { padding: 20px; flex-direction: column; text-align: center; }
|
|
199
|
+
.install-line code { font-size: 13px; }
|
|
200
|
+
.status-card { padding: 32px; }
|
|
201
|
+
.status-top { flex-direction: column; gap: 16px; }
|
|
202
|
+
.limitation-grid { grid-template-columns: 1fr; }
|
|
203
|
+
.code-tabs { overflow-x: auto; padding-bottom: 4px; }
|
|
204
|
+
.code-panel { padding: 24px; font-size: 12px; }
|
|
205
|
+
}
|
package/src/Collection.h
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
#include <fstream>
|
|
5
5
|
#include <iostream>
|
|
6
6
|
#include <unordered_map>
|
|
7
|
-
#include <random> //
|
|
7
|
+
#include <random> // Used to generate record IDs
|
|
8
8
|
|
|
9
9
|
namespace fs = std::filesystem;
|
|
10
10
|
|
|
@@ -15,7 +15,7 @@ private:
|
|
|
15
15
|
fs::path wal_file;
|
|
16
16
|
std::unordered_map<std::string, std::streampos> index;
|
|
17
17
|
|
|
18
|
-
//
|
|
18
|
+
// Generate a short hex ID for a new record
|
|
19
19
|
std::string generate_id() {
|
|
20
20
|
const char chars[] = "0123456789abcdef";
|
|
21
21
|
std::string id;
|
|
@@ -90,12 +90,12 @@ public:
|
|
|
90
90
|
build_index();
|
|
91
91
|
}
|
|
92
92
|
|
|
93
|
-
//
|
|
93
|
+
// Keep the same ID when writing an updated record
|
|
94
94
|
void insert(const std::string& id, const std::string& data) {
|
|
95
95
|
append_record(id, data, false);
|
|
96
96
|
}
|
|
97
97
|
|
|
98
|
-
//
|
|
98
|
+
// New records get an ID automatically
|
|
99
99
|
std::string insert(const std::string& data) {
|
|
100
100
|
std::string new_id = generate_id();
|
|
101
101
|
append_record(new_id, data, false);
|
package/src/binding.cpp
CHANGED
|
@@ -10,7 +10,7 @@ public:
|
|
|
10
10
|
Napi::Function func = DefineClass(env, "RoccoDBNative", {
|
|
11
11
|
InstanceMethod("insert", &RoccoDBNative::Insert),
|
|
12
12
|
InstanceMethod("get", &RoccoDBNative::Get),
|
|
13
|
-
InstanceMethod("upload", &RoccoDBNative::Upload) //
|
|
13
|
+
InstanceMethod("upload", &RoccoDBNative::Upload) // Expose file uploads to JavaScript
|
|
14
14
|
});
|
|
15
15
|
|
|
16
16
|
Napi::FunctionReference* constructor = new Napi::FunctionReference();
|
|
@@ -48,7 +48,7 @@ public:
|
|
|
48
48
|
return Napi::String::New(env, data);
|
|
49
49
|
}
|
|
50
50
|
|
|
51
|
-
//
|
|
51
|
+
// Pass the file to roccoDB storage and return its generated ID
|
|
52
52
|
Napi::Value Upload(const Napi::CallbackInfo& info) {
|
|
53
53
|
Napi::Env env = info.Env();
|
|
54
54
|
std::string source_file = info[0].As<Napi::String>().Utf8Value();
|
package/src/main.cpp
CHANGED
|
@@ -8,17 +8,17 @@ int main() {
|
|
|
8
8
|
auto users = db.collection("users");
|
|
9
9
|
httplib::Server svr;
|
|
10
10
|
|
|
11
|
-
//
|
|
11
|
+
// Serve the frontend from the public directory
|
|
12
12
|
svr.set_mount_point("/", "../public");
|
|
13
13
|
|
|
14
|
-
//
|
|
14
|
+
// Create a user
|
|
15
15
|
svr.Post("/users", [&](const httplib::Request& req, httplib::Response& res) {
|
|
16
16
|
std::string id = users.insert(req.body);
|
|
17
17
|
std::cout << "API Call: Created " << id << "\n";
|
|
18
18
|
res.set_content("{\"id\": \"" + id + "\"}", "application/json");
|
|
19
19
|
});
|
|
20
20
|
|
|
21
|
-
//
|
|
21
|
+
// Fetch a user by ID
|
|
22
22
|
svr.Get(R"(/users/(.+))", [&](const httplib::Request& req, httplib::Response& res) {
|
|
23
23
|
std::string id = req.matches[1];
|
|
24
24
|
std::string data = users.get(id);
|
|
@@ -31,7 +31,7 @@ int main() {
|
|
|
31
31
|
}
|
|
32
32
|
});
|
|
33
33
|
|
|
34
|
-
//
|
|
34
|
+
// Update an existing user
|
|
35
35
|
svr.Put(R"(/users/(.+))", [&](const httplib::Request& req, httplib::Response& res) {
|
|
36
36
|
std::string id = req.matches[1];
|
|
37
37
|
users.update(id, req.body);
|
|
@@ -39,7 +39,7 @@ int main() {
|
|
|
39
39
|
res.set_content("{\"status\": \"Updated Successfully\"}", "application/json");
|
|
40
40
|
});
|
|
41
41
|
|
|
42
|
-
//
|
|
42
|
+
// Delete a user
|
|
43
43
|
svr.Delete(R"(/users/(.+))", [&](const httplib::Request& req, httplib::Response& res) {
|
|
44
44
|
std::string id = req.matches[1];
|
|
45
45
|
users.remove(id);
|
package/public/index.html
DELETED
|
@@ -1,132 +0,0 @@
|
|
|
1
|
-
<!DOCTYPE html>
|
|
2
|
-
<html lang="en">
|
|
3
|
-
<head>
|
|
4
|
-
<meta charset="UTF-8">
|
|
5
|
-
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
6
|
-
<title>roccoDB | Playground</title>
|
|
7
|
-
<script src="https://cdn.tailwindcss.com"></script>
|
|
8
|
-
<style>
|
|
9
|
-
/* Custom scrollbar for the terminal output */
|
|
10
|
-
::-webkit-scrollbar { width: 8px; height: 8px; }
|
|
11
|
-
::-webkit-scrollbar-track { background: #1f2937; }
|
|
12
|
-
::-webkit-scrollbar-thumb { background: #4b5563; border-radius: 4px; }
|
|
13
|
-
::-webkit-scrollbar-thumb:hover { background: #6b7280; }
|
|
14
|
-
</style>
|
|
15
|
-
</head>
|
|
16
|
-
<body class="bg-gray-950 text-gray-100 font-sans antialiased min-h-screen flex flex-col">
|
|
17
|
-
|
|
18
|
-
<nav class="bg-gray-900 border-b border-gray-800 px-6 py-4 flex items-center justify-between shadow-sm">
|
|
19
|
-
<div class="flex items-center gap-3">
|
|
20
|
-
<svg class="w-6 h-6 text-blue-500" fill="none" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 7v10c0 2.21 3.582 4 8 4s8-1.79 8-4V7M4 7c0 2.21 3.582 4 8 4s8-1.79 8-4M4 7c0-2.21 3.582-4 8-4s8 1.79 8 4m0 5c0 2.21-3.582 4-8 4s-8-1.79-8-4"></path></svg>
|
|
21
|
-
<h1 class="text-xl font-bold tracking-tight text-white">rocco<span class="text-blue-500">DB</span></h1>
|
|
22
|
-
</div>
|
|
23
|
-
<div class="text-xs font-mono text-gray-500 bg-gray-800 px-3 py-1 rounded-full border border-gray-700">
|
|
24
|
-
C++ Engine v1.0 • Connected
|
|
25
|
-
</div>
|
|
26
|
-
</nav>
|
|
27
|
-
|
|
28
|
-
<main class="flex-grow p-6 grid grid-cols-1 lg:grid-cols-2 gap-6 max-w-7xl mx-auto w-full">
|
|
29
|
-
|
|
30
|
-
<div class="space-y-6">
|
|
31
|
-
|
|
32
|
-
<div class="bg-gray-900 border border-gray-800 rounded-xl p-5 shadow-lg">
|
|
33
|
-
<h3 class="text-sm font-semibold text-gray-400 uppercase tracking-wider mb-4">1. Insert New Document</h3>
|
|
34
|
-
<div class="space-y-3">
|
|
35
|
-
<label class="block text-xs font-medium text-gray-500">JSON DATA</label>
|
|
36
|
-
<textarea id="newData" rows="3" class="w-full bg-gray-950 border border-gray-700 rounded-lg px-4 py-3 text-sm text-green-400 font-mono focus:outline-none focus:border-blue-500 focus:ring-1 focus:ring-blue-500 transition-colors" placeholder='{"name": "Bruce", "skill": "Martial Arts"}'>{"name": "Bruce", "skill": "Martial Arts"}</textarea>
|
|
37
|
-
<button onclick="createRecord()" class="w-full bg-blue-600 hover:bg-blue-500 text-white text-sm font-semibold py-2.5 rounded-lg transition-colors flex justify-center items-center gap-2">
|
|
38
|
-
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 4v16m8-8H4"></path></svg>
|
|
39
|
-
Run Insert
|
|
40
|
-
</button>
|
|
41
|
-
</div>
|
|
42
|
-
</div>
|
|
43
|
-
|
|
44
|
-
<div class="bg-gray-900 border border-gray-800 rounded-xl p-5 shadow-lg">
|
|
45
|
-
<h3 class="text-sm font-semibold text-gray-400 uppercase tracking-wider mb-4">2. Manage Existing Document</h3>
|
|
46
|
-
<div class="space-y-4">
|
|
47
|
-
<div>
|
|
48
|
-
<label class="block text-xs font-medium text-gray-500 mb-1">TARGET RECORD ID</label>
|
|
49
|
-
<input type="text" id="recordId" class="w-full bg-gray-950 border border-gray-700 rounded-lg px-4 py-2.5 text-sm text-gray-200 font-mono focus:outline-none focus:border-blue-500 transition-colors" placeholder="e.g., a3f9b2c1d4e5">
|
|
50
|
-
</div>
|
|
51
|
-
<div>
|
|
52
|
-
<label class="block text-xs font-medium text-gray-500 mb-1">UPDATE DATA (JSON)</label>
|
|
53
|
-
<input type="text" id="updateData" class="w-full bg-gray-950 border border-gray-700 rounded-lg px-4 py-2.5 text-sm text-green-400 font-mono focus:outline-none focus:border-blue-500 transition-colors" placeholder='{"name": "Updated Name"}'>
|
|
54
|
-
</div>
|
|
55
|
-
|
|
56
|
-
<div class="grid grid-cols-3 gap-3 pt-2">
|
|
57
|
-
<button onclick="getRecord()" class="bg-gray-700 hover:bg-gray-600 text-white text-sm font-medium py-2 rounded-lg transition-colors">GET</button>
|
|
58
|
-
<button onclick="updateRecord()" class="bg-emerald-600 hover:bg-emerald-500 text-white text-sm font-medium py-2 rounded-lg transition-colors">PUT (Update)</button>
|
|
59
|
-
<button onclick="deleteRecord()" class="bg-rose-600 hover:bg-rose-500 text-white text-sm font-medium py-2 rounded-lg transition-colors">DELETE</button>
|
|
60
|
-
</div>
|
|
61
|
-
</div>
|
|
62
|
-
</div>
|
|
63
|
-
</div>
|
|
64
|
-
|
|
65
|
-
<div class="bg-[#0D1117] border border-gray-800 rounded-xl flex flex-col shadow-2xl overflow-hidden h-full min-h-[400px]">
|
|
66
|
-
<div class="bg-gray-900 border-b border-gray-800 px-4 py-2 flex items-center gap-2">
|
|
67
|
-
<div class="flex gap-1.5">
|
|
68
|
-
<div class="w-3 h-3 rounded-full bg-red-500"></div>
|
|
69
|
-
<div class="w-3 h-3 rounded-full bg-yellow-500"></div>
|
|
70
|
-
<div class="w-3 h-3 rounded-full bg-green-500"></div>
|
|
71
|
-
</div>
|
|
72
|
-
<span class="text-xs font-mono text-gray-500 ml-2">roccoDB API Response</span>
|
|
73
|
-
</div>
|
|
74
|
-
<div class="p-4 flex-grow overflow-auto relative">
|
|
75
|
-
<pre id="output" class="text-green-400 font-mono text-sm leading-relaxed break-all whitespace-pre-wrap">> Engine standby... ready for commands.</pre>
|
|
76
|
-
</div>
|
|
77
|
-
</div>
|
|
78
|
-
</main>
|
|
79
|
-
|
|
80
|
-
<script>
|
|
81
|
-
const outputEl = document.getElementById('output');
|
|
82
|
-
|
|
83
|
-
// Helper to format and display JSON nicely in our terminal
|
|
84
|
-
const print = (data, method) => {
|
|
85
|
-
const time = new Date().toLocaleTimeString();
|
|
86
|
-
const formattedJson = JSON.stringify(data, null, 2);
|
|
87
|
-
outputEl.innerHTML = `<span class="text-gray-500">[${time}] ${method} 200 OK</span>\n${formattedJson}`;
|
|
88
|
-
};
|
|
89
|
-
|
|
90
|
-
async function createRecord() {
|
|
91
|
-
outputEl.innerHTML = `<span class="text-yellow-500">Executing POST /users...</span>`;
|
|
92
|
-
try {
|
|
93
|
-
const res = await fetch('/users', { method: 'POST', body: document.getElementById('newData').value });
|
|
94
|
-
const data = await res.json();
|
|
95
|
-
document.getElementById('recordId').value = data.id; // Auto-fill
|
|
96
|
-
print(data, 'POST');
|
|
97
|
-
} catch (e) { outputEl.innerHTML = `<span class="text-red-500">Error: ${e.message}</span>`; }
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
async function getRecord() {
|
|
101
|
-
const id = document.getElementById('recordId').value;
|
|
102
|
-
if(!id) return outputEl.innerHTML = `<span class="text-red-500">Error: Provide an ID first!</span>`;
|
|
103
|
-
outputEl.innerHTML = `<span class="text-yellow-500">Executing GET /users/${id}...</span>`;
|
|
104
|
-
try {
|
|
105
|
-
const res = await fetch('/users/' + id);
|
|
106
|
-
print(await res.json(), 'GET');
|
|
107
|
-
} catch (e) { outputEl.innerHTML = `<span class="text-red-500">Error: ${e.message}</span>`; }
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
async function updateRecord() {
|
|
111
|
-
const id = document.getElementById('recordId').value;
|
|
112
|
-
if(!id) return outputEl.innerHTML = `<span class="text-red-500">Error: Provide an ID first!</span>`;
|
|
113
|
-
outputEl.innerHTML = `<span class="text-yellow-500">Executing PUT /users/${id}...</span>`;
|
|
114
|
-
try {
|
|
115
|
-
const res = await fetch('/users/' + id, { method: 'PUT', body: document.getElementById('updateData').value });
|
|
116
|
-
print(await res.json(), 'PUT');
|
|
117
|
-
} catch (e) { outputEl.innerHTML = `<span class="text-red-500">Error: ${e.message}</span>`; }
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
async function deleteRecord() {
|
|
121
|
-
const id = document.getElementById('recordId').value;
|
|
122
|
-
if(!id) return outputEl.innerHTML = `<span class="text-red-500">Error: Provide an ID first!</span>`;
|
|
123
|
-
outputEl.innerHTML = `<span class="text-yellow-500">Executing DELETE /users/${id}...</span>`;
|
|
124
|
-
try {
|
|
125
|
-
const res = await fetch('/users/' + id, { method: 'DELETE' });
|
|
126
|
-
print(await res.json(), 'DELETE');
|
|
127
|
-
document.getElementById('recordId').value = ''; // Clear ID
|
|
128
|
-
} catch (e) { outputEl.innerHTML = `<span class="text-red-500">Error: ${e.message}</span>`; }
|
|
129
|
-
}
|
|
130
|
-
</script>
|
|
131
|
-
</body>
|
|
132
|
-
</html>
|
package/roccodb-1.0.0.tgz
DELETED
|
Binary file
|