@rlabs-inc/fsdb 1.0.0
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 +358 -0
- package/dist/core/collection.d.ts +66 -0
- package/dist/core/collection.d.ts.map +1 -0
- package/dist/core/columns.d.ts +47 -0
- package/dist/core/columns.d.ts.map +1 -0
- package/dist/core/constants.d.ts +22 -0
- package/dist/core/constants.d.ts.map +1 -0
- package/dist/core/database.d.ts +55 -0
- package/dist/core/database.d.ts.map +1 -0
- package/dist/core/registry.d.ts +52 -0
- package/dist/core/registry.d.ts.map +1 -0
- package/dist/core/types.d.ts +107 -0
- package/dist/core/types.d.ts.map +1 -0
- package/dist/index.d.ts +20 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +2682 -0
- package/dist/index.mjs +2656 -0
- package/dist/persistence/markdown.d.ts +57 -0
- package/dist/persistence/markdown.d.ts.map +1 -0
- package/dist/persistence/watcher.d.ts +38 -0
- package/dist/persistence/watcher.d.ts.map +1 -0
- package/dist/query/filters.d.ts +65 -0
- package/dist/query/filters.d.ts.map +1 -0
- package/dist/query/reactive.d.ts +58 -0
- package/dist/query/reactive.d.ts.map +1 -0
- package/dist/vector/search.d.ts +59 -0
- package/dist/vector/search.d.ts.map +1 -0
- package/package.json +53 -0
package/README.md
ADDED
|
@@ -0,0 +1,358 @@
|
|
|
1
|
+
# fsDB - Fractal State Database
|
|
2
|
+
|
|
3
|
+
A reactive database with parallel arrays and fine-grained reactivity. Built on the **Father State Pattern** at every level.
|
|
4
|
+
|
|
5
|
+
**F**ather **S**tate **DB** = **F**ractal **S**tate **DB**
|
|
6
|
+
|
|
7
|
+
## Features
|
|
8
|
+
|
|
9
|
+
- **Parallel Arrays** - One reactive array per field, not array of objects
|
|
10
|
+
- **Fine-Grained Reactivity** - Update one field, only that field triggers
|
|
11
|
+
- **Reactive Queries** - Queries auto-update when data changes
|
|
12
|
+
- **Vector Search** - Cosine similarity with stale detection
|
|
13
|
+
- **Markdown Persistence** - YAML frontmatter + body, human-readable
|
|
14
|
+
- **File Watching** - External changes sync automatically
|
|
15
|
+
- **Zero Global State** - Each database instance is isolated
|
|
16
|
+
|
|
17
|
+
## Installation
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
bun add @rlabs-inc/fsdb
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
## Quick Start
|
|
24
|
+
|
|
25
|
+
```typescript
|
|
26
|
+
import { createDatabase, eq, gt, and, query, effect } from '@rlabs-inc/fsdb'
|
|
27
|
+
|
|
28
|
+
// Create a database
|
|
29
|
+
const db = createDatabase({ name: 'myapp' })
|
|
30
|
+
|
|
31
|
+
// Create a collection with schema
|
|
32
|
+
const users = db.collection('users', {
|
|
33
|
+
schema: {
|
|
34
|
+
name: 'string',
|
|
35
|
+
age: 'number',
|
|
36
|
+
email: 'string',
|
|
37
|
+
active: 'boolean',
|
|
38
|
+
}
|
|
39
|
+
})
|
|
40
|
+
|
|
41
|
+
// Insert records
|
|
42
|
+
const id = users.insert({
|
|
43
|
+
name: 'Alice',
|
|
44
|
+
age: 30,
|
|
45
|
+
email: 'alice@example.com',
|
|
46
|
+
active: true,
|
|
47
|
+
})
|
|
48
|
+
|
|
49
|
+
// Query with filters
|
|
50
|
+
const activeUsers = users.find(eq('active', true))
|
|
51
|
+
const seniors = users.find(and(gt('age', 50), eq('active', true)))
|
|
52
|
+
|
|
53
|
+
// Reactive queries - auto-update on changes
|
|
54
|
+
const activeQuery = query(users, r => r.active)
|
|
55
|
+
|
|
56
|
+
effect(() => {
|
|
57
|
+
console.log('Active count:', activeQuery.value.count)
|
|
58
|
+
})
|
|
59
|
+
|
|
60
|
+
// Updates trigger the effect above
|
|
61
|
+
users.update(id, { active: false })
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
## The Father State Pattern
|
|
65
|
+
|
|
66
|
+
Instead of storing records as objects (slow):
|
|
67
|
+
|
|
68
|
+
```typescript
|
|
69
|
+
// Traditional - array of objects
|
|
70
|
+
const records = [
|
|
71
|
+
{ id: '1', name: 'Alice', age: 30 },
|
|
72
|
+
{ id: '2', name: 'Bob', age: 25 },
|
|
73
|
+
]
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
We use parallel arrays indexed by a central registry (fast):
|
|
77
|
+
|
|
78
|
+
```typescript
|
|
79
|
+
// Father State Pattern - parallel arrays
|
|
80
|
+
registry = { idToIndex: { '1': 0, '2': 1 } }
|
|
81
|
+
names = ['Alice', 'Bob']
|
|
82
|
+
ages = [30, 25]
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
**Benefits:**
|
|
86
|
+
- O(1) array access vs object property lookup
|
|
87
|
+
- Better CPU cache locality
|
|
88
|
+
- Fine-grained reactivity at field level
|
|
89
|
+
- Minimal garbage collection
|
|
90
|
+
|
|
91
|
+
## API Reference
|
|
92
|
+
|
|
93
|
+
### Database
|
|
94
|
+
|
|
95
|
+
```typescript
|
|
96
|
+
import { createDatabase } from '@rlabs-inc/fsdb'
|
|
97
|
+
|
|
98
|
+
// Global storage (default): ~/.fsdb/myapp/
|
|
99
|
+
const db = createDatabase({ name: 'myapp' })
|
|
100
|
+
|
|
101
|
+
// Project-local storage: ./.fsdb/myapp/
|
|
102
|
+
const localDb = createDatabase({ name: 'myapp', local: true })
|
|
103
|
+
|
|
104
|
+
// Custom path: /custom/path/
|
|
105
|
+
const customDb = createDatabase({ basePath: '/custom/path' })
|
|
106
|
+
|
|
107
|
+
// Create/get collections
|
|
108
|
+
const users = db.collection('users', { schema: {...} })
|
|
109
|
+
|
|
110
|
+
// List collections
|
|
111
|
+
db.listCollections()
|
|
112
|
+
|
|
113
|
+
// Cleanup
|
|
114
|
+
db.close()
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
### Collections
|
|
118
|
+
|
|
119
|
+
```typescript
|
|
120
|
+
const users = db.collection('users', {
|
|
121
|
+
schema: {
|
|
122
|
+
name: 'string',
|
|
123
|
+
age: 'number',
|
|
124
|
+
score: 'number',
|
|
125
|
+
tags: 'string[]',
|
|
126
|
+
embedding: 'vector:384', // 384-dimensional vector
|
|
127
|
+
},
|
|
128
|
+
contentColumn: 'bio', // Stored as markdown body
|
|
129
|
+
autoSave: true, // Auto-persist changes
|
|
130
|
+
watchFiles: true, // Watch for external changes
|
|
131
|
+
})
|
|
132
|
+
|
|
133
|
+
// CRUD
|
|
134
|
+
const id = users.insert({ name: 'Alice', age: 30 })
|
|
135
|
+
const user = users.get(id)
|
|
136
|
+
users.update(id, { age: 31 })
|
|
137
|
+
users.updateField(id, 'score', 100)
|
|
138
|
+
users.delete(id)
|
|
139
|
+
|
|
140
|
+
// Querying
|
|
141
|
+
const all = users.all()
|
|
142
|
+
const found = users.find(filter)
|
|
143
|
+
const first = users.findOne(filter)
|
|
144
|
+
const count = users.count(filter)
|
|
145
|
+
|
|
146
|
+
// Persistence (if path configured)
|
|
147
|
+
await users.load()
|
|
148
|
+
await users.save()
|
|
149
|
+
|
|
150
|
+
// File watching
|
|
151
|
+
users.startWatching()
|
|
152
|
+
users.stopWatching()
|
|
153
|
+
users.onFileChange((event) => {
|
|
154
|
+
console.log(event.type, event.id, event.stale)
|
|
155
|
+
})
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
### Schema Types
|
|
159
|
+
|
|
160
|
+
| Type | TypeScript | Example |
|
|
161
|
+
|------|------------|---------|
|
|
162
|
+
| `'string'` | `string` | `'hello'` |
|
|
163
|
+
| `'number'` | `number` | `42` |
|
|
164
|
+
| `'boolean'` | `boolean` | `true` |
|
|
165
|
+
| `'timestamp'` | `number` | `Date.now()` |
|
|
166
|
+
| `'string[]'` | `string[]` | `['a', 'b']` |
|
|
167
|
+
| `'number[]'` | `number[]` | `[1, 2, 3]` |
|
|
168
|
+
| `'vector:N'` | `Float32Array` | 384-dim embedding |
|
|
169
|
+
|
|
170
|
+
### Filter Helpers
|
|
171
|
+
|
|
172
|
+
```typescript
|
|
173
|
+
import {
|
|
174
|
+
// Comparison
|
|
175
|
+
eq, neq, gt, gte, lt, lte, between, oneOf,
|
|
176
|
+
|
|
177
|
+
// Text
|
|
178
|
+
fullText, matches, startsWith, endsWith,
|
|
179
|
+
|
|
180
|
+
// Arrays
|
|
181
|
+
contains, containsAny, containsAll, isEmpty, isNotEmpty,
|
|
182
|
+
|
|
183
|
+
// Logic
|
|
184
|
+
and, or, not,
|
|
185
|
+
|
|
186
|
+
// Existence
|
|
187
|
+
exists, isNull,
|
|
188
|
+
|
|
189
|
+
// Time
|
|
190
|
+
after, before, withinLast,
|
|
191
|
+
} from '@rlabs-inc/fsdb'
|
|
192
|
+
|
|
193
|
+
// Examples
|
|
194
|
+
users.find(eq('name', 'Alice'))
|
|
195
|
+
users.find(gt('age', 30))
|
|
196
|
+
users.find(contains('tags', 'admin'))
|
|
197
|
+
users.find(and(gt('age', 18), eq('active', true)))
|
|
198
|
+
users.find(or(eq('role', 'admin'), eq('role', 'moderator')))
|
|
199
|
+
users.find(fullText('search term'))
|
|
200
|
+
users.find(withinLast('created', 24 * 60 * 60 * 1000)) // Last 24h
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
### Reactive Queries
|
|
204
|
+
|
|
205
|
+
```typescript
|
|
206
|
+
import { query, querySorted, queryCount, queryFirst, effect } from '@rlabs-inc/fsdb'
|
|
207
|
+
|
|
208
|
+
// Basic query - returns { indices, records, count }
|
|
209
|
+
const activeUsers = query(users, r => r.active === true)
|
|
210
|
+
|
|
211
|
+
// Sorted query
|
|
212
|
+
const topScorers = querySorted(users, r => r.score > 0, 'score', true)
|
|
213
|
+
|
|
214
|
+
// Count query
|
|
215
|
+
const activeCount = queryCount(users, r => r.active)
|
|
216
|
+
|
|
217
|
+
// First match
|
|
218
|
+
const admin = queryFirst(users, r => r.role === 'admin')
|
|
219
|
+
|
|
220
|
+
// Use in effects - auto-updates!
|
|
221
|
+
effect(() => {
|
|
222
|
+
console.log('Active users:', activeUsers.value.count)
|
|
223
|
+
})
|
|
224
|
+
```
|
|
225
|
+
|
|
226
|
+
### Vector Search
|
|
227
|
+
|
|
228
|
+
```typescript
|
|
229
|
+
import { vectorSearch, cosineSimilarity } from '@rlabs-inc/fsdb'
|
|
230
|
+
|
|
231
|
+
// Schema with vector column
|
|
232
|
+
const docs = db.collection('docs', {
|
|
233
|
+
schema: {
|
|
234
|
+
content: 'string',
|
|
235
|
+
embedding: 'vector:384',
|
|
236
|
+
}
|
|
237
|
+
})
|
|
238
|
+
|
|
239
|
+
// Store embeddings with stale detection
|
|
240
|
+
docs.setEmbedding(id, 'embedding', vector, sourceContent)
|
|
241
|
+
|
|
242
|
+
// Search
|
|
243
|
+
const results = docs.search('embedding', queryVector, {
|
|
244
|
+
topK: 10,
|
|
245
|
+
minSimilarity: 0.5,
|
|
246
|
+
filter: r => r.category === 'tech',
|
|
247
|
+
})
|
|
248
|
+
|
|
249
|
+
// Results include stale flag
|
|
250
|
+
results.forEach(({ record, similarity, stale }) => {
|
|
251
|
+
console.log(record.content, similarity, stale ? '(stale)' : '')
|
|
252
|
+
})
|
|
253
|
+
|
|
254
|
+
// Manual similarity calculation
|
|
255
|
+
const sim = cosineSimilarity(vecA, vecB)
|
|
256
|
+
```
|
|
257
|
+
|
|
258
|
+
### Sorting & Pagination
|
|
259
|
+
|
|
260
|
+
```typescript
|
|
261
|
+
import { sortBy, paginate } from '@rlabs-inc/fsdb'
|
|
262
|
+
|
|
263
|
+
const records = users.all()
|
|
264
|
+
|
|
265
|
+
// Sort
|
|
266
|
+
const sorted = sortBy(records, { column: 'score', direction: 'desc' })
|
|
267
|
+
|
|
268
|
+
// Multi-column sort
|
|
269
|
+
const sorted2 = sortBy(records,
|
|
270
|
+
{ column: 'age', direction: 'asc' },
|
|
271
|
+
{ column: 'name', direction: 'asc' }
|
|
272
|
+
)
|
|
273
|
+
|
|
274
|
+
// Paginate
|
|
275
|
+
const page = paginate(records, 1, 20)
|
|
276
|
+
// { records, total, page, pageSize, totalPages, hasNext, hasPrev }
|
|
277
|
+
```
|
|
278
|
+
|
|
279
|
+
### File Persistence
|
|
280
|
+
|
|
281
|
+
Records are stored as markdown files with YAML frontmatter:
|
|
282
|
+
|
|
283
|
+
```markdown
|
|
284
|
+
---
|
|
285
|
+
id: user-1703289600000-abc123
|
|
286
|
+
created: 1703289600000
|
|
287
|
+
updated: 1703289600000
|
|
288
|
+
name: Alice
|
|
289
|
+
age: 30
|
|
290
|
+
embedding: [0.1, 0.2, 0.3, ...]
|
|
291
|
+
---
|
|
292
|
+
|
|
293
|
+
This is the bio content (contentColumn)
|
|
294
|
+
```
|
|
295
|
+
|
|
296
|
+
### Stale Detection
|
|
297
|
+
|
|
298
|
+
When content changes but embedding isn't regenerated:
|
|
299
|
+
|
|
300
|
+
```typescript
|
|
301
|
+
// Store embedding with content hash
|
|
302
|
+
users.setEmbedding(id, 'embedding', vector, originalContent)
|
|
303
|
+
|
|
304
|
+
// Later, if content changes externally...
|
|
305
|
+
users.isStale(id) // true
|
|
306
|
+
users.getStaleIds() // ['id1', 'id2', ...]
|
|
307
|
+
|
|
308
|
+
// After regenerating embedding
|
|
309
|
+
users.setEmbedding(id, 'embedding', newVector, newContent)
|
|
310
|
+
users.isStale(id) // false
|
|
311
|
+
```
|
|
312
|
+
|
|
313
|
+
## Benchmarks
|
|
314
|
+
|
|
315
|
+
On Apple Silicon (M1/M2/M3):
|
|
316
|
+
|
|
317
|
+
| Operation | Time |
|
|
318
|
+
|-----------|------|
|
|
319
|
+
| Insert 1000 records | 2.28ms |
|
|
320
|
+
| Get by ID | 0.13μs |
|
|
321
|
+
| Update field | 0.11μs |
|
|
322
|
+
| Filter 1000 records | 107μs |
|
|
323
|
+
| fullText search | 169μs |
|
|
324
|
+
| Sort 1000 records | 110μs |
|
|
325
|
+
| Cosine similarity (384d) | 0.17μs |
|
|
326
|
+
| Vector search top-10 | 534μs |
|
|
327
|
+
| Vector search with filter | 194μs |
|
|
328
|
+
| Bun.hash (fingerprint) | 0.28μs |
|
|
329
|
+
|
|
330
|
+
**Test Coverage:** 77 tests passing across all features.
|
|
331
|
+
|
|
332
|
+
## Architecture
|
|
333
|
+
|
|
334
|
+
```
|
|
335
|
+
Database (FatherState)
|
|
336
|
+
└── Collections (ReactiveMap)
|
|
337
|
+
└── Collection (FatherState)
|
|
338
|
+
├── Registry (index management)
|
|
339
|
+
└── Columns (parallel arrays)
|
|
340
|
+
├── names: ['Alice', 'Bob', ...]
|
|
341
|
+
├── ages: [30, 25, ...]
|
|
342
|
+
└── scores: [100, 85, ...]
|
|
343
|
+
```
|
|
344
|
+
|
|
345
|
+
The same pattern repeats at every level - **fractal architecture**.
|
|
346
|
+
|
|
347
|
+
## Requirements
|
|
348
|
+
|
|
349
|
+
- Bun 1.0+
|
|
350
|
+
- @rlabs-inc/signals
|
|
351
|
+
|
|
352
|
+
## License
|
|
353
|
+
|
|
354
|
+
MIT
|
|
355
|
+
|
|
356
|
+
---
|
|
357
|
+
|
|
358
|
+
Built with ❤️ by RLabs Inc.
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Collection
|
|
3
|
+
*
|
|
4
|
+
* A reactive collection using the Father State Pattern.
|
|
5
|
+
* Combines the registry (index management) with columns (parallel arrays).
|
|
6
|
+
*
|
|
7
|
+
* This is where the fractal pattern lives - each collection is a FatherState.
|
|
8
|
+
*/
|
|
9
|
+
import { type DerivedSignal } from '@rlabs-inc/signals';
|
|
10
|
+
import { type Registry } from './registry';
|
|
11
|
+
import { type ColumnsManager } from './columns';
|
|
12
|
+
import type { SchemaDefinition, SchemaToRecord, RecordWithMeta, PartialRecord, CollectionOptions, FilterFn } from './types';
|
|
13
|
+
export interface Collection<S extends SchemaDefinition> {
|
|
14
|
+
/** Collection name */
|
|
15
|
+
readonly name: string;
|
|
16
|
+
/** The schema definition */
|
|
17
|
+
readonly schema: S;
|
|
18
|
+
/** Content column (stored as markdown body) */
|
|
19
|
+
readonly contentColumn: keyof S | undefined;
|
|
20
|
+
/** Access to the registry */
|
|
21
|
+
readonly registry: Registry;
|
|
22
|
+
/** Access to the columns manager */
|
|
23
|
+
readonly columns: ColumnsManager<S>;
|
|
24
|
+
/** Insert a record, returns the ID */
|
|
25
|
+
insert(data: PartialRecord<S>): string;
|
|
26
|
+
/** Insert multiple records, returns array of IDs */
|
|
27
|
+
insertMany(records: PartialRecord<S>[]): string[];
|
|
28
|
+
/** Get a record by ID */
|
|
29
|
+
get(id: string): RecordWithMeta<S> | undefined;
|
|
30
|
+
/** Get all records */
|
|
31
|
+
all(): RecordWithMeta<S>[];
|
|
32
|
+
/** Find records matching a filter */
|
|
33
|
+
find(filter: FilterFn<S>): RecordWithMeta<S>[];
|
|
34
|
+
/** Find first record matching a filter */
|
|
35
|
+
findOne(filter: FilterFn<S>): RecordWithMeta<S> | undefined;
|
|
36
|
+
/** Update a record by ID */
|
|
37
|
+
update(id: string, data: Partial<SchemaToRecord<S>>): boolean;
|
|
38
|
+
/** Update a specific field of a record */
|
|
39
|
+
updateField<K extends keyof S>(id: string, field: K, value: SchemaToRecord<S>[K]): boolean;
|
|
40
|
+
/** Update multiple records matching a filter */
|
|
41
|
+
updateMany(filter: FilterFn<S>, data: Partial<SchemaToRecord<S>>): number;
|
|
42
|
+
/** Delete a record by ID */
|
|
43
|
+
delete(id: string): boolean;
|
|
44
|
+
/** Delete multiple records matching a filter */
|
|
45
|
+
deleteMany(filter: FilterFn<S>): number;
|
|
46
|
+
/** Count records (optionally filtered) */
|
|
47
|
+
count(filter?: FilterFn<S>): number;
|
|
48
|
+
/** Get a reactive count */
|
|
49
|
+
readonly reactiveCount: DerivedSignal<number>;
|
|
50
|
+
/** Get record by array index (not ID) */
|
|
51
|
+
getByIndex(index: number): RecordWithMeta<S> | undefined;
|
|
52
|
+
/** Get all allocated indices */
|
|
53
|
+
getIndices(): number[];
|
|
54
|
+
/** Check if record exists */
|
|
55
|
+
has(id: string): boolean;
|
|
56
|
+
/** Check if a record's embedding is stale */
|
|
57
|
+
isStale(id: string): boolean;
|
|
58
|
+
/** Get all IDs with stale embeddings */
|
|
59
|
+
getStaleIds(): string[];
|
|
60
|
+
/** Manually set stale flag */
|
|
61
|
+
setStale(id: string, stale: boolean): void;
|
|
62
|
+
/** Clear all data */
|
|
63
|
+
clear(): void;
|
|
64
|
+
}
|
|
65
|
+
export declare function createCollection<S extends SchemaDefinition>(name: string, options: CollectionOptions<S>): Collection<S>;
|
|
66
|
+
//# sourceMappingURL=collection.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"collection.d.ts","sourceRoot":"","sources":["../../src/core/collection.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Columns Manager
|
|
3
|
+
*
|
|
4
|
+
* Manages parallel reactive arrays for each schema column.
|
|
5
|
+
* This is the heart of the Father State Pattern - each field
|
|
6
|
+
* gets its own array, indexed by the registry.
|
|
7
|
+
*/
|
|
8
|
+
import { type WritableSignal } from '@rlabs-inc/signals';
|
|
9
|
+
import type { SchemaDefinition, SchemaToRecord, ParsedColumnType, ParsedSchema } from './types';
|
|
10
|
+
/**
|
|
11
|
+
* Parse a column type string into structured info
|
|
12
|
+
*/
|
|
13
|
+
export declare function parseColumnType(type: string): ParsedColumnType;
|
|
14
|
+
/**
|
|
15
|
+
* Get default value for a column type
|
|
16
|
+
*/
|
|
17
|
+
export declare function getDefaultValue(type: string): unknown;
|
|
18
|
+
/**
|
|
19
|
+
* Parse a schema definition into structured info
|
|
20
|
+
*/
|
|
21
|
+
export declare function parseSchema<S extends SchemaDefinition>(definition: S): ParsedSchema<S>;
|
|
22
|
+
export interface ColumnsManager<S extends SchemaDefinition> {
|
|
23
|
+
/** Get the parsed schema */
|
|
24
|
+
readonly schema: ParsedSchema<S>;
|
|
25
|
+
/** Get a raw column array (reactive signal) */
|
|
26
|
+
getColumn<K extends keyof S>(name: K): WritableSignal<unknown[]>;
|
|
27
|
+
/** Get a value from a column at an index */
|
|
28
|
+
get<K extends keyof S>(column: K, index: number): SchemaToRecord<S>[K];
|
|
29
|
+
/** Set a value in a column at an index */
|
|
30
|
+
set<K extends keyof S>(column: K, index: number, value: SchemaToRecord<S>[K]): void;
|
|
31
|
+
/** Get all column values for an index as a record */
|
|
32
|
+
getRecord(index: number): SchemaToRecord<S>;
|
|
33
|
+
/** Set all column values for an index from a record */
|
|
34
|
+
setRecord(index: number, record: Partial<SchemaToRecord<S>>): void;
|
|
35
|
+
/** Clear all values at an index (reset to defaults) */
|
|
36
|
+
clearAt(index: number): void;
|
|
37
|
+
/** Reset all columns (for testing/cleanup) */
|
|
38
|
+
reset(): void;
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Create a columns manager for a schema
|
|
42
|
+
*
|
|
43
|
+
* Each column is a reactive signal containing an array.
|
|
44
|
+
* Fine-grained updates: modifying arr[i] triggers only effects reading that index.
|
|
45
|
+
*/
|
|
46
|
+
export declare function createColumns<S extends SchemaDefinition>(definition: S): ColumnsManager<S>;
|
|
47
|
+
//# sourceMappingURL=columns.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"columns.d.ts","sourceRoot":"","sources":["../../src/core/columns.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAU,KAAK,cAAc,EAAE,MAAM,oBAAoB,CAAA;AAChE,OAAO,KAAK,EAAE,gBAAgB,EAAE,cAAc,EAAE,gBAAgB,EAAE,YAAY,EAAE,MAAM,SAAS,CAAA;AAO/F;;GAEG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,gBAAgB,CAkB9D;AAED;;GAEG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAoBrD;AAED;;GAEG;AACH,wBAAgB,WAAW,CAAC,CAAC,SAAS,gBAAgB,EAAE,UAAU,EAAE,CAAC,GAAG,YAAY,CAAC,CAAC,CAAC,CAoBtF;AAMD,MAAM,WAAW,cAAc,CAAC,CAAC,SAAS,gBAAgB;IACxD,4BAA4B;IAC5B,QAAQ,CAAC,MAAM,EAAE,YAAY,CAAC,CAAC,CAAC,CAAA;IAEhC,+CAA+C;IAC/C,SAAS,CAAC,CAAC,SAAS,MAAM,CAAC,EAAE,IAAI,EAAE,CAAC,GAAG,cAAc,CAAC,OAAO,EAAE,CAAC,CAAA;IAEhE,4CAA4C;IAC5C,GAAG,CAAC,CAAC,SAAS,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,KAAK,EAAE,MAAM,GAAG,cAAc,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAA;IAEtE,0CAA0C;IAC1C,GAAG,CAAC,CAAC,SAAS,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,cAAc,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,IAAI,CAAA;IAEnF,qDAAqD;IACrD,SAAS,CAAC,KAAK,EAAE,MAAM,GAAG,cAAc,CAAC,CAAC,CAAC,CAAA;IAE3C,uDAAuD;IACvD,SAAS,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,CAAC,cAAc,CAAC,CAAC,CAAC,CAAC,GAAG,IAAI,CAAA;IAElE,uDAAuD;IACvD,OAAO,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAAA;IAE5B,8CAA8C;IAC9C,KAAK,IAAI,IAAI,CAAA;CACd;AAED;;;;;GAKG;AACH,wBAAgB,aAAa,CAAC,CAAC,SAAS,gBAAgB,EACtD,UAAU,EAAE,CAAC,GACZ,cAAc,CAAC,CAAC,CAAC,CA6FnB"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* fsDB Constants
|
|
3
|
+
*/
|
|
4
|
+
/** Default values for column types */
|
|
5
|
+
export declare const DEFAULT_VALUES: {
|
|
6
|
+
readonly string: "";
|
|
7
|
+
readonly number: 0;
|
|
8
|
+
readonly boolean: false;
|
|
9
|
+
readonly timestamp: 0;
|
|
10
|
+
readonly 'string[]': string[];
|
|
11
|
+
readonly 'number[]': number[];
|
|
12
|
+
readonly vector: Float32Array | null;
|
|
13
|
+
};
|
|
14
|
+
/** File watcher debounce time in ms */
|
|
15
|
+
export declare const WATCHER_DEBOUNCE_MS = 100;
|
|
16
|
+
/** Time to wait after save before allowing file watcher to process (prevents loops) */
|
|
17
|
+
export declare const SAVE_GRACE_PERIOD_MS = 200;
|
|
18
|
+
/** Default page size for pagination */
|
|
19
|
+
export declare const DEFAULT_PAGE_SIZE = 20;
|
|
20
|
+
/** Maximum iterations for stale detection loop */
|
|
21
|
+
export declare const MAX_STALE_CHECK_ITERATIONS = 10000;
|
|
22
|
+
//# sourceMappingURL=constants.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../../src/core/constants.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,sCAAsC;AACtC,eAAO,MAAM,cAAc;;;;;yBAKP,MAAM,EAAE;yBACR,MAAM,EAAE;qBACV,YAAY,GAAG,IAAI;CAC3B,CAAA;AAEV,uCAAuC;AACvC,eAAO,MAAM,mBAAmB,MAAM,CAAA;AAEtC,uFAAuF;AACvF,eAAO,MAAM,oBAAoB,MAAM,CAAA;AAEvC,uCAAuC;AACvC,eAAO,MAAM,iBAAiB,KAAK,CAAA;AAEnC,kDAAkD;AAClD,eAAO,MAAM,0BAA0B,QAAQ,CAAA"}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Database
|
|
3
|
+
*
|
|
4
|
+
* The top level of the fractal - manages collections.
|
|
5
|
+
* Each database is a FatherState at the macro level.
|
|
6
|
+
*/
|
|
7
|
+
import { type Collection } from './collection';
|
|
8
|
+
import type { SchemaDefinition, CollectionOptions, DatabaseOptions, FileChangeEvent } from './types';
|
|
9
|
+
import type { VectorSearchOptions, VectorSearchResult } from './types';
|
|
10
|
+
export interface PersistentCollection<S extends SchemaDefinition> extends Collection<S> {
|
|
11
|
+
/** Load all records from disk */
|
|
12
|
+
load(): Promise<number>;
|
|
13
|
+
/** Save all records to disk */
|
|
14
|
+
save(): Promise<number>;
|
|
15
|
+
/** Start file watching */
|
|
16
|
+
startWatching(): void;
|
|
17
|
+
/** Stop file watching */
|
|
18
|
+
stopWatching(): void;
|
|
19
|
+
/** Is currently watching */
|
|
20
|
+
readonly isWatching: boolean;
|
|
21
|
+
/** Register callback for file changes */
|
|
22
|
+
onFileChange(callback: (event: FileChangeEvent<S>) => void | Promise<void>): () => void;
|
|
23
|
+
/** Set embedding with content hash for stale detection */
|
|
24
|
+
setEmbedding(id: string, column: keyof S, embedding: Float32Array | number[], sourceContent: string): void;
|
|
25
|
+
/** Vector similarity search */
|
|
26
|
+
search(column: keyof S, queryVector: Float32Array | number[], options?: VectorSearchOptions<S>): VectorSearchResult<S>[];
|
|
27
|
+
/** Close the collection (stop watching, cleanup) */
|
|
28
|
+
close(): void;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Create a persistent collection with file I/O and watching
|
|
32
|
+
*/
|
|
33
|
+
export declare function createPersistentCollection<S extends SchemaDefinition>(name: string, options: CollectionOptions<S> & {
|
|
34
|
+
path: string;
|
|
35
|
+
}): PersistentCollection<S>;
|
|
36
|
+
export interface Database {
|
|
37
|
+
/** Database name */
|
|
38
|
+
readonly name: string;
|
|
39
|
+
/** Base path for collections */
|
|
40
|
+
readonly basePath: string;
|
|
41
|
+
/** Get or create a collection */
|
|
42
|
+
collection<S extends SchemaDefinition>(name: string, options: Omit<CollectionOptions<S>, 'path'>): PersistentCollection<S>;
|
|
43
|
+
/** Get an existing collection */
|
|
44
|
+
getCollection<S extends SchemaDefinition>(name: string): PersistentCollection<S> | undefined;
|
|
45
|
+
/** List all collection names */
|
|
46
|
+
listCollections(): string[];
|
|
47
|
+
/** Close all collections */
|
|
48
|
+
close(): void;
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Create a database instance
|
|
52
|
+
*/
|
|
53
|
+
export declare function createDatabase(options?: DatabaseOptions): Database;
|
|
54
|
+
export { createDatabase as fsDB };
|
|
55
|
+
//# sourceMappingURL=database.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"database.d.ts","sourceRoot":"","sources":["../../src/core/database.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAGH,OAAO,EAAoB,KAAK,UAAU,EAAE,MAAM,cAAc,CAAA;AAChE,OAAO,KAAK,EAAE,gBAAgB,EAAE,iBAAiB,EAAE,eAAe,EAAkB,eAAe,EAAE,MAAM,SAAS,CAAA;AAUpH,OAAO,KAAK,EAAE,mBAAmB,EAAE,kBAAkB,EAAE,MAAM,SAAS,CAAA;AAMtE,MAAM,WAAW,oBAAoB,CAAC,CAAC,SAAS,gBAAgB,CAAE,SAAQ,UAAU,CAAC,CAAC,CAAC;IACrF,iCAAiC;IACjC,IAAI,IAAI,OAAO,CAAC,MAAM,CAAC,CAAA;IAEvB,+BAA+B;IAC/B,IAAI,IAAI,OAAO,CAAC,MAAM,CAAC,CAAA;IAEvB,0BAA0B;IAC1B,aAAa,IAAI,IAAI,CAAA;IAErB,yBAAyB;IACzB,YAAY,IAAI,IAAI,CAAA;IAEpB,4BAA4B;IAC5B,QAAQ,CAAC,UAAU,EAAE,OAAO,CAAA;IAE5B,yCAAyC;IACzC,YAAY,CAAC,QAAQ,EAAE,CAAC,KAAK,EAAE,eAAe,CAAC,CAAC,CAAC,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,GAAG,MAAM,IAAI,CAAA;IAEvF,0DAA0D;IAC1D,YAAY,CAAC,EAAE,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,SAAS,EAAE,YAAY,GAAG,MAAM,EAAE,EAAE,aAAa,EAAE,MAAM,GAAG,IAAI,CAAA;IAE1G,+BAA+B;IAC/B,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,EAAE,WAAW,EAAE,YAAY,GAAG,MAAM,EAAE,EAAE,OAAO,CAAC,EAAE,mBAAmB,CAAC,CAAC,CAAC,GAAG,kBAAkB,CAAC,CAAC,CAAC,EAAE,CAAA;IAExH,oDAAoD;IACpD,KAAK,IAAI,IAAI,CAAA;CACd;AAED;;GAEG;AACH,wBAAgB,0BAA0B,CAAC,CAAC,SAAS,gBAAgB,EACnE,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE,iBAAiB,CAAC,CAAC,CAAC,GAAG;IAAE,IAAI,EAAE,MAAM,CAAA;CAAE,GAC/C,oBAAoB,CAAC,CAAC,CAAC,CA8QzB;AAMD,MAAM,WAAW,QAAQ;IACvB,oBAAoB;IACpB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IAErB,gCAAgC;IAChC,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAA;IAEzB,iCAAiC;IACjC,UAAU,CAAC,CAAC,SAAS,gBAAgB,EACnC,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE,IAAI,CAAC,iBAAiB,CAAC,CAAC,CAAC,EAAE,MAAM,CAAC,GAC1C,oBAAoB,CAAC,CAAC,CAAC,CAAA;IAE1B,iCAAiC;IACjC,aAAa,CAAC,CAAC,SAAS,gBAAgB,EAAE,IAAI,EAAE,MAAM,GAAG,oBAAoB,CAAC,CAAC,CAAC,GAAG,SAAS,CAAA;IAE5F,gCAAgC;IAChC,eAAe,IAAI,MAAM,EAAE,CAAA;IAE3B,4BAA4B;IAC5B,KAAK,IAAI,IAAI,CAAA;CACd;AAED;;GAEG;AACH,wBAAgB,cAAc,CAAC,OAAO,GAAE,eAAoB,GAAG,QAAQ,CA0DtE;AAGD,OAAO,EAAE,cAAc,IAAI,IAAI,EAAE,CAAA"}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* FatherState Registry
|
|
3
|
+
*
|
|
4
|
+
* The central index manager for the Father State Pattern.
|
|
5
|
+
* Manages ID-to-index mappings with O(1) lookups and index reuse.
|
|
6
|
+
*
|
|
7
|
+
* Uses reactive primitives from @rlabs-inc/signals for fine-grained reactivity.
|
|
8
|
+
*/
|
|
9
|
+
import { ReactiveMap, ReactiveSet } from '@rlabs-inc/signals';
|
|
10
|
+
export interface Registry {
|
|
11
|
+
/** Map from record ID to array index */
|
|
12
|
+
readonly idToIndex: ReactiveMap<string, number>;
|
|
13
|
+
/** Map from array index to record ID */
|
|
14
|
+
readonly indexToId: ReactiveMap<number, string>;
|
|
15
|
+
/** Set of currently allocated indices */
|
|
16
|
+
readonly allocatedIndices: ReactiveSet<number>;
|
|
17
|
+
/** Array of free indices available for reuse */
|
|
18
|
+
readonly freeIndices: number[];
|
|
19
|
+
/** Next index to allocate if no free indices */
|
|
20
|
+
nextIndex: number;
|
|
21
|
+
/** Allocate an index for an ID (reuses free indices) */
|
|
22
|
+
allocate(id: string): number;
|
|
23
|
+
/** Release an index, returning it to the free pool */
|
|
24
|
+
release(id: string): boolean;
|
|
25
|
+
/** Get index for an ID (returns -1 if not found) */
|
|
26
|
+
getIndex(id: string): number;
|
|
27
|
+
/** Get ID for an index (returns undefined if not found) */
|
|
28
|
+
getId(index: number): string | undefined;
|
|
29
|
+
/** Check if a record exists */
|
|
30
|
+
has(id: string): boolean;
|
|
31
|
+
/** Get all record IDs */
|
|
32
|
+
getAllIds(): string[];
|
|
33
|
+
/** Get all allocated indices */
|
|
34
|
+
getAllIndices(): number[];
|
|
35
|
+
/** Get count of records */
|
|
36
|
+
readonly count: number;
|
|
37
|
+
/** Reset the registry (for testing/cleanup) */
|
|
38
|
+
reset(): void;
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Create a new FatherState registry instance
|
|
42
|
+
*
|
|
43
|
+
* Each registry is independent - no global state.
|
|
44
|
+
*/
|
|
45
|
+
export declare function createRegistry(): Registry;
|
|
46
|
+
/**
|
|
47
|
+
* Generate a unique ID
|
|
48
|
+
*
|
|
49
|
+
* Format: timestamp-random (e.g., "1703289600000-a1b2c3")
|
|
50
|
+
*/
|
|
51
|
+
export declare function generateId(): string;
|
|
52
|
+
//# sourceMappingURL=registry.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"registry.d.ts","sourceRoot":"","sources":["../../src/core/registry.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EAAE,WAAW,EAAE,WAAW,EAAU,MAAM,oBAAoB,CAAA;AAErE,MAAM,WAAW,QAAQ;IACvB,wCAAwC;IACxC,QAAQ,CAAC,SAAS,EAAE,WAAW,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;IAE/C,wCAAwC;IACxC,QAAQ,CAAC,SAAS,EAAE,WAAW,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;IAE/C,yCAAyC;IACzC,QAAQ,CAAC,gBAAgB,EAAE,WAAW,CAAC,MAAM,CAAC,CAAA;IAE9C,gDAAgD;IAChD,QAAQ,CAAC,WAAW,EAAE,MAAM,EAAE,CAAA;IAE9B,gDAAgD;IAChD,SAAS,EAAE,MAAM,CAAA;IAEjB,wDAAwD;IACxD,QAAQ,CAAC,EAAE,EAAE,MAAM,GAAG,MAAM,CAAA;IAE5B,sDAAsD;IACtD,OAAO,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAA;IAE5B,oDAAoD;IACpD,QAAQ,CAAC,EAAE,EAAE,MAAM,GAAG,MAAM,CAAA;IAE5B,2DAA2D;IAC3D,KAAK,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAAA;IAExC,+BAA+B;IAC/B,GAAG,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAA;IAExB,yBAAyB;IACzB,SAAS,IAAI,MAAM,EAAE,CAAA;IAErB,gCAAgC;IAChC,aAAa,IAAI,MAAM,EAAE,CAAA;IAEzB,2BAA2B;IAC3B,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;IAEtB,+CAA+C;IAC/C,KAAK,IAAI,IAAI,CAAA;CACd;AAED;;;;GAIG;AACH,wBAAgB,cAAc,IAAI,QAAQ,CAkGzC;AAED;;;;GAIG;AACH,wBAAgB,UAAU,IAAI,MAAM,CAInC"}
|