@delma/fylo 2.0.0 → 2.1.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.
Files changed (43) hide show
  1. package/README.md +185 -267
  2. package/package.json +2 -5
  3. package/src/core/directory.ts +22 -354
  4. package/src/engines/s3-files/documents.ts +65 -0
  5. package/src/engines/s3-files/filesystem.ts +172 -0
  6. package/src/engines/s3-files/query.ts +291 -0
  7. package/src/engines/s3-files/types.ts +42 -0
  8. package/src/engines/s3-files.ts +391 -510
  9. package/src/engines/types.ts +1 -1
  10. package/src/index.ts +142 -1237
  11. package/src/sync.ts +58 -0
  12. package/src/types/fylo.d.ts +66 -161
  13. package/src/types/node-runtime.d.ts +1 -0
  14. package/tests/collection/truncate.test.js +11 -10
  15. package/tests/helpers/root.js +7 -0
  16. package/tests/integration/create.test.js +9 -9
  17. package/tests/integration/delete.test.js +16 -14
  18. package/tests/integration/edge-cases.test.js +29 -25
  19. package/tests/integration/encryption.test.js +47 -30
  20. package/tests/integration/export.test.js +11 -11
  21. package/tests/integration/join-modes.test.js +16 -16
  22. package/tests/integration/nested.test.js +26 -24
  23. package/tests/integration/operators.test.js +43 -29
  24. package/tests/integration/read.test.js +25 -21
  25. package/tests/integration/rollback.test.js +21 -51
  26. package/tests/integration/s3-files.performance.test.js +75 -0
  27. package/tests/integration/s3-files.test.js +115 -18
  28. package/tests/integration/sync.test.js +154 -0
  29. package/tests/integration/update.test.js +24 -18
  30. package/src/adapters/redis.ts +0 -487
  31. package/src/adapters/s3.ts +0 -61
  32. package/src/core/walker.ts +0 -174
  33. package/src/core/write-queue.ts +0 -59
  34. package/src/migrate-cli.ts +0 -22
  35. package/src/migrate.ts +0 -74
  36. package/src/types/write-queue.ts +0 -42
  37. package/src/worker.ts +0 -18
  38. package/src/workers/write-worker.ts +0 -120
  39. package/tests/index.js +0 -14
  40. package/tests/integration/migration.test.js +0 -38
  41. package/tests/integration/queue.test.js +0 -83
  42. package/tests/mocks/redis.js +0 -123
  43. package/tests/mocks/s3.js +0 -80
package/README.md CHANGED
@@ -1,368 +1,286 @@
1
- # Fylo
1
+ # FYLO
2
2
 
3
- NoSQL document store with SQL parsing, real-time listeners, and Bun-first workflows.
3
+ FYLO is a Bun-native document store that keeps **one canonical file per document** and builds a **collection index file** to make queries fast.
4
4
 
5
- Fylo `2.0.0` supports two storage engines:
5
+ The important mental model is simple:
6
6
 
7
- - `legacy-s3`: the existing S3 + Redis architecture with queued writes, bucket-per-collection storage, and Redis-backed pub/sub/locks.
8
- - `s3-files`: a new AWS S3 Files mode that stores canonical documents on a mounted S3 Files filesystem, keeps query indexes in a collection-level SQLite database under `.fylo/index.db`, and uses filesystem locks plus an append-only event journal instead of Redis.
7
+ - document files are the source of truth
8
+ - the index file is just an accelerator
9
+ - if the index ever gets out of date, FYLO can rebuild it from the documents
9
10
 
10
- The legacy engine still stores documents as **S3 key paths** — not file contents. Each document produces two keys per field: a **data key** (`{ttid}/{field}/{value}`) for full-doc retrieval and an **index key** (`{field}/{value}/{ttid}`) for query lookups. This enables fast reads and filtered queries without a traditional database engine.
11
+ FYLO now ships with **one engine**: a filesystem-first storage model designed to work well with AWS S3 Files and other synced filesystem setups.
11
12
 
12
- Built for **serverless** runtimes (AWS Lambda, Cloudflare Workers) — no persistent in-memory state, lazy connections, minimal cold-start overhead.
13
+ ## Why this design?
13
14
 
14
- In `legacy-s3`, writes are coordinated through Redis before they are flushed to S3. By default the high-level CRUD methods wait for the queued write to be processed so existing code can continue to behave synchronously. If you want fire-and-forget semantics, pass `{ wait: false }` and process queued jobs with a worker or `processQueuedWrites()`.
15
+ We wanted three things:
15
16
 
16
- In `s3-files`, writes are immediate and synchronous. Queue APIs, worker APIs, and Redis-backed job tracking are intentionally unsupported.
17
+ - low durable storage overhead
18
+ - fast application queries
19
+ - a system that is still understandable by normal engineers
17
20
 
18
- ## Install
21
+ That is why FYLO does **not** create one tiny durable file per indexed field and does **not** depend on Redis-backed queued writes anymore.
19
22
 
20
- ```bash
21
- bun add @delma/fylo
22
- ```
23
+ Instead, each collection looks like this:
23
24
 
24
- ## Engine Selection
25
-
26
- ```typescript
27
- import Fylo from '@delma/fylo'
28
-
29
- const legacy = new Fylo()
30
-
31
- const s3Files = new Fylo({
32
- engine: 's3-files',
33
- s3FilesRoot: '/mnt/fylo'
34
- })
25
+ ```text
26
+ <root>/<collection>/
27
+ .fylo/
28
+ docs/
29
+ 4U/
30
+ 4UUB32VGUDW.json
31
+ indexes/
32
+ <collection>.idx.json
33
+ events/
34
+ <collection>.ndjson
35
35
  ```
36
36
 
37
- Static helpers such as `Fylo.createCollection()` and `Fylo.findDocs()` use environment defaults:
37
+ ## Installation
38
38
 
39
39
  ```bash
40
- export FYLO_STORAGE_ENGINE=s3-files
41
- export FYLO_S3FILES_ROOT=/mnt/fylo
40
+ bun add @delma/fylo
42
41
  ```
43
42
 
44
- ## Environment Variables
45
-
46
- | Variable | Purpose |
47
- | ------------------------------------------------ | ------------------------------------------------------------------------------------ |
48
- | `FYLO_STORAGE_ENGINE` | `legacy-s3` (default) or `s3-files` |
49
- | `FYLO_S3FILES_ROOT` | Mounted S3 Files root directory used by the `s3-files` engine |
50
- | `BUCKET_PREFIX` | S3 bucket name prefix |
51
- | `S3_ACCESS_KEY_ID` / `AWS_ACCESS_KEY_ID` | S3 credentials |
52
- | `S3_SECRET_ACCESS_KEY` / `AWS_SECRET_ACCESS_KEY` | S3 credentials |
53
- | `S3_REGION` / `AWS_REGION` | S3 region |
54
- | `S3_ENDPOINT` / `AWS_ENDPOINT` | S3 endpoint (for LocalStack, MinIO, etc.) |
55
- | `REDIS_URL` | Redis connection URL used for pub/sub, document locks, and queued write coordination |
56
- | `FYLO_WRITE_MAX_ATTEMPTS` | Maximum retry attempts before a queued job is dead-lettered |
57
- | `FYLO_WRITE_RETRY_BASE_MS` | Base retry delay used for exponential backoff between recovery attempts |
58
- | `FYLO_WORKER_ID` | Optional stable identifier for a write worker process |
59
- | `FYLO_WORKER_BATCH_SIZE` | Number of queued jobs a worker pulls per read loop |
60
- | `FYLO_WORKER_BLOCK_MS` | Redis stream block time for waiting on new jobs |
61
- | `FYLO_WORKER_RECOVER_ON_START` | Whether the worker reclaims stale pending jobs on startup |
62
- | `FYLO_WORKER_RECOVER_IDLE_MS` | Minimum idle time before a pending job is reclaimed |
63
- | `FYLO_WORKER_STOP_WHEN_IDLE` | Exit the worker loop when no jobs are available |
64
- | `LOGGING` | Enable debug logging |
65
- | `STRICT` | Enable schema validation via CHEX |
66
-
67
- ### S3 Files requirements
68
-
69
- When `FYLO_STORAGE_ENGINE=s3-files`, FYLO expects:
70
-
71
- - an already provisioned AWS S3 Files file system
72
- - the mounted root directory to be available to the Bun process
73
- - bucket versioning enabled on the underlying S3 bucket
74
- - Linux/AWS compute assumptions that match AWS S3 Files mounting requirements
75
-
76
- FYLO no longer talks to the S3 API directly in this mode, but S3 remains the underlying source of truth because that is how S3 Files works.
77
-
78
- ## Usage
79
-
80
- ### CRUD — NoSQL API
81
-
82
- ```typescript
83
- import Fylo from '@delma/fylo'
84
-
85
- const fylo = new Fylo()
86
-
87
- // Collections
88
- await Fylo.createCollection('users')
89
-
90
- // Create
91
- const _id = await fylo.putData<_user>('users', { name: 'John Doe', age: 30 })
92
-
93
- // Read one
94
- const user = await Fylo.getDoc<_user>('users', _id).once()
95
-
96
- // Read many
97
- for await (const doc of Fylo.findDocs<_user>('users', { $limit: 10 }).collect()) {
98
- console.log(doc)
99
- }
43
+ ## Basic usage
100
44
 
101
- // Update one
102
- await fylo.patchDoc<_user>('users', { [_id]: { age: 31 } })
45
+ ```ts
46
+ import Fylo from '@delma/fylo'
103
47
 
104
- // Update many
105
- const updated = await fylo.patchDocs<_user>('users', {
106
- $where: { $ops: [{ age: { $gte: 30 } }] },
107
- $set: { age: 31 }
48
+ const fylo = new Fylo({
49
+ root: '/mnt/fylo'
108
50
  })
109
51
 
110
- // Delete one
111
- await fylo.delDoc('users', _id)
52
+ await fylo.createCollection('users')
112
53
 
113
- // Delete many
114
- const deleted = await fylo.delDocs<_user>('users', {
115
- $ops: [{ name: { $like: '%Doe%' } }]
54
+ const id = await fylo.putData('users', {
55
+ name: 'Ada',
56
+ role: 'admin',
57
+ tags: ['engineering', 'platform']
116
58
  })
117
59
 
118
- // Drop
119
- await Fylo.dropCollection('users')
60
+ const doc = await fylo.getDoc('users', id).once()
61
+ console.log(doc[id])
120
62
  ```
121
63
 
122
- ### Queued Writes
123
-
124
- `legacy-s3` only.
64
+ ## Configuration
125
65
 
126
- ```typescript
127
- const fylo = new Fylo()
66
+ FYLO is filesystem-first now.
128
67
 
129
- // Default behavior waits for the queued write to finish.
130
- const _id = await fylo.putData('users', { name: 'John Doe' })
68
+ You can configure the root in one of two ways:
131
69
 
132
- // Async mode returns the queued job immediately.
133
- const queued = await fylo.putData('users', { name: 'Jane Doe' }, { wait: false })
70
+ ```bash
71
+ export FYLO_ROOT=/mnt/fylo
72
+ ```
134
73
 
135
- // Poll status if you need to track progress.
136
- const status = await fylo.getJobStatus(queued.jobId)
74
+ Or:
137
75
 
138
- // Process pending writes in-process when you are not running a separate worker.
139
- await fylo.processQueuedWrites()
76
+ ```ts
77
+ const fylo = new Fylo({ root: '/mnt/fylo' })
140
78
  ```
141
79
 
142
- When `wait: false` is used, the job is durable in Redis but the document is not visible in S3 until a worker commits it.
80
+ If you do not configure a root, FYLO uses a project-local default:
143
81
 
144
- Queued jobs that fail are left pending for recovery. Recovered jobs retry up to `FYLO_WRITE_MAX_ATTEMPTS` times before being moved to a dead-letter stream. You can inspect dead letters with `getDeadLetters()` and reclaim stale pending jobs with `processQueuedWrites(count, true)`.
82
+ ```text
83
+ <current working directory>/.fylo-data
84
+ ```
145
85
 
146
- Operational helpers:
86
+ For compatibility with older `s3-files` experiments, FYLO still accepts `s3FilesRoot` and still reads `FYLO_S3FILES_ROOT` as a fallback.
147
87
 
148
- - `getQueueStats()` returns current queue, pending, and dead-letter counts
149
- - `getDeadLetters()` lists exhausted jobs
150
- - `replayDeadLetter(streamId)` moves a dead-lettered job back into the main queue
88
+ ### Environment variables
151
89
 
152
- ### Worker
90
+ | Variable | Purpose |
91
+ | ------------------- | ---------------------------------------------------------------- |
92
+ | `FYLO_ROOT` | Preferred filesystem root for collections |
93
+ | `FYLO_S3FILES_ROOT` | Backward-compatible alias for `FYLO_ROOT` |
94
+ | `SCHEMA_DIR` | Directory containing JSON validation schemas |
95
+ | `STRICT` | When truthy, validate documents with `@delma/chex` before writes |
96
+ | `ENCRYPTION_KEY` | Required when schemas declare `$encrypted` fields |
153
97
 
154
- `legacy-s3` only.
98
+ ## Syncing to S3-compatible storage
155
99
 
156
- Run a dedicated write worker when you want queued writes to be flushed outside the request path:
100
+ FYLO does **not** ship its own cloud sync engine.
157
101
 
158
- ```bash
159
- bun run worker
160
- ```
102
+ That is intentional.
161
103
 
162
- The worker entrypoint lives at [worker.ts](/Users/iyor/Library/CloudStorage/Dropbox/myProjects/FYLO/src/worker.ts) and continuously drains the Redis stream, recovers stale pending jobs on startup, and respects the retry/dead-letter settings above.
104
+ The package owns:
163
105
 
164
- If `FYLO_STORAGE_ENGINE=s3-files`, `fylo.worker` exits with an explicit unsupported-engine error.
106
+ - document storage behavior
107
+ - query behavior
108
+ - index maintenance
165
109
 
166
- ### Migration
110
+ You own:
167
111
 
168
- Move legacy collections into an S3 Files-backed root with:
112
+ - how that root directory gets synced to AWS S3 Files, S3-compatible storage, or any other file-backed replication layer you trust
169
113
 
170
- ```bash
171
- fylo.migrate users posts
172
- ```
114
+ That means you can choose the sync tool that matches your infrastructure:
173
115
 
174
- Programmatic usage:
116
+ - AWS S3 Files
117
+ - `aws s3 sync`
118
+ - `rclone`
119
+ - storage vendor tooling
120
+ - platform-specific replication
175
121
 
176
- ```typescript
177
- import { migrateLegacyS3ToS3Files } from '@delma/fylo'
122
+ If you want FYLO to notify your own S3 client on document writes, you can plug in sync hooks:
178
123
 
179
- await migrateLegacyS3ToS3Files({
180
- collections: ['users', 'posts'],
181
- s3FilesRoot: '/mnt/fylo',
182
- verify: true
124
+ ```ts
125
+ import Fylo from '@delma/fylo'
126
+
127
+ const fylo = new Fylo({
128
+ root: '/mnt/fylo',
129
+ syncMode: 'await-sync',
130
+ sync: {
131
+ async onWrite(event) {
132
+ const file = Bun.file(event.path)
133
+ await myS3Client.putObject({
134
+ key: `${event.collection}/${event.docId}.json`,
135
+ body: await file.arrayBuffer()
136
+ })
137
+ },
138
+ async onDelete(event) {
139
+ await myS3Client.deleteObject({
140
+ key: `${event.collection}/${event.docId}.json`
141
+ })
142
+ }
143
+ }
183
144
  })
184
145
  ```
185
146
 
186
- ### CRUD SQL API
147
+ There are two sync modes:
187
148
 
188
- ```typescript
189
- const fylo = new Fylo()
149
+ - `await-sync`: FYLO waits for your hook and throws if the remote sync fails
150
+ - `fire-and-forget`: FYLO commits locally first and runs your hook in the background
190
151
 
191
- await fylo.executeSQL(`CREATE TABLE users`)
152
+ Important detail for junior engineers:
192
153
 
193
- const _id = await fylo.executeSQL<_user>(`INSERT INTO users (name, age) VALUES ('John Doe', 30)`)
154
+ - the filesystem write is still the source of truth
155
+ - a sync hook is a replication helper, not the database itself
194
156
 
195
- const docs = await fylo.executeSQL<_user>(`SELECT * FROM users LIMIT 10`)
157
+ ## CRUD examples
196
158
 
197
- await fylo.executeSQL<_user>(`UPDATE users SET age = 31 WHERE name = 'John Doe'`)
159
+ ### Create
198
160
 
199
- await fylo.executeSQL<_user>(`DELETE FROM users WHERE name LIKE '%Doe%'`)
200
-
201
- await fylo.executeSQL(`DROP TABLE users`)
161
+ ```ts
162
+ const userId = await fylo.putData('users', {
163
+ name: 'Jane Doe',
164
+ age: 29,
165
+ team: 'platform'
166
+ })
202
167
  ```
203
168
 
204
- ### Query Operators
169
+ ### Read one
205
170
 
206
- ```typescript
207
- // Equality
208
- {
209
- $ops: [{ status: { $eq: 'active' } }]
210
- }
171
+ ```ts
172
+ const user = await fylo.getDoc('users', userId).once()
173
+ ```
211
174
 
212
- // Not equal
213
- {
214
- $ops: [{ status: { $ne: 'archived' } }]
215
- }
175
+ ### Find many
216
176
 
217
- // Numeric range
218
- {
219
- $ops: [{ age: { $gte: 18, $lt: 65 } }]
220
- }
177
+ ```ts
178
+ const results = {}
221
179
 
222
- // Pattern matching
223
- {
224
- $ops: [{ email: { $like: '%@gmail.com' } }]
180
+ for await (const doc of fylo
181
+ .findDocs('users', {
182
+ $ops: [{ age: { $gte: 18 } }]
183
+ })
184
+ .collect()) {
185
+ Object.assign(results, doc)
225
186
  }
187
+ ```
226
188
 
227
- // Array contains
228
- {
229
- $ops: [{ tags: { $contains: 'urgent' } }]
230
- }
189
+ ### Update one
231
190
 
232
- // Multiple ops use OR semantics — matches if any op is satisfied
233
- {
234
- $ops: [{ status: { $eq: 'active' } }, { priority: { $gte: 5 } }]
235
- }
191
+ ```ts
192
+ const nextId = await fylo.patchDoc('users', {
193
+ [userId]: {
194
+ team: 'core-platform'
195
+ }
196
+ })
236
197
  ```
237
198
 
238
- ### Joins
199
+ ### Delete one
239
200
 
240
- ```typescript
241
- const results = await Fylo.joinDocs<_post, _user>({
242
- $leftCollection: 'posts',
243
- $rightCollection: 'users',
244
- $mode: 'inner', // "inner" | "left" | "right" | "outer"
245
- $on: { userId: { $eq: 'id' } },
246
- $select: ['title', 'name'],
247
- $limit: 50
248
- })
201
+ ```ts
202
+ await fylo.delDoc('users', nextId)
249
203
  ```
250
204
 
251
- ### Real-Time Streaming
205
+ ## SQL support
252
206
 
253
- ```typescript
254
- // Stream new/updated documents
255
- for await (const doc of Fylo.findDocs<_user>('users')) {
256
- console.log(doc)
257
- }
207
+ FYLO also supports SQL-like commands for app-facing document work:
258
208
 
259
- // Stream deletions
260
- for await (const _id of Fylo.findDocs<_user>('users').onDelete()) {
261
- console.log('deleted:', _id)
262
- }
209
+ ```ts
210
+ await fylo.executeSQL(`
211
+ CREATE TABLE posts
212
+ `)
263
213
 
264
- // Watch a single document
265
- for await (const doc of Fylo.getDoc<_user>('users', _id)) {
266
- console.log(doc)
267
- }
214
+ await fylo.executeSQL(`
215
+ INSERT INTO posts VALUES { "title": "Hello", "published": true }
216
+ `)
217
+
218
+ const posts = await fylo.executeSQL(`
219
+ SELECT * FROM posts WHERE published = true
220
+ `)
268
221
  ```
269
222
 
270
- ### Bulk Import / Export
223
+ ## Query behavior
271
224
 
272
- ```typescript
273
- const fylo = new Fylo()
225
+ FYLO queries use the collection index file first when they can, then hydrate only the matching documents.
274
226
 
275
- // Import from JSON array or NDJSON URL
276
- const count = await fylo.importBulkData<_user>(
277
- 'users',
278
- new URL('https://example.com/users.json'),
279
- 1000
280
- )
227
+ That means:
281
228
 
282
- // Export all documents
283
- for await (const doc of Fylo.exportBulkData<_user>('users')) {
284
- console.log(doc)
285
- }
286
- ```
229
+ - exact matches are fast
230
+ - range queries are narrowed before document reads
231
+ - contains-style queries can use indexed candidates
232
+ - final document validation still happens before returning results
287
233
 
288
- ### Rollback
234
+ This is why FYLO behaves more like an application document store than a data warehouse.
289
235
 
290
- `rollback()` is now a legacy escape hatch.
236
+ ## Realtime behavior
291
237
 
292
- Fylo still keeps best-effort rollback data for writes performed by the current instance. This is mainly useful for in-process failures and test workflows:
238
+ FYLO keeps a filesystem event journal per collection.
293
239
 
294
- ```typescript
295
- const fylo = new Fylo()
296
- await fylo.putData('users', { name: 'test' })
297
- await fylo.rollback() // undoes all writes in this instance
298
- ```
240
+ That is what powers listeners such as:
299
241
 
300
- For queued writes, prefer:
242
+ ```ts
243
+ for await (const doc of fylo.findDocs('users', {
244
+ $ops: [{ role: { $eq: 'admin' } }]
245
+ })) {
246
+ console.log(doc)
247
+ }
248
+ ```
301
249
 
302
- - `getJobStatus()` to inspect an individual write
303
- - `processQueuedWrites(count, true)` to recover stale pending jobs
304
- - `getDeadLetters()` to inspect exhausted jobs
305
- - compensating writes instead of `rollback()` after a commit
250
+ ## What FYLO no longer does
306
251
 
307
- `rollback()` may be removed from the main queued-write path in a future major release.
252
+ FYLO no longer centers:
308
253
 
309
- ### CLI
254
+ - Redis-backed queued writes
255
+ - worker-based write draining
256
+ - legacy bucket-per-collection S3 storage
257
+ - built-in migration commands between old and new engines
310
258
 
311
- ```bash
312
- fylo.query "SELECT * FROM users WHERE age > 25 LIMIT 10"
313
- ```
259
+ If you see older references to those ideas in historic discussions, treat them as previous design stages, not the current product direction.
314
260
 
315
- ### Schema Validation
261
+ ## Recovery story
316
262
 
317
- When `STRICT` is set, documents are validated against CHEX schemas before writes:
263
+ This part is important:
318
264
 
319
- ```bash
320
- STRICT=true bun run start
321
- ```
265
+ - document files are the truth
266
+ - index files can be rebuilt
322
267
 
323
- Schemas are `.d.ts` interface declarations generated by [`@delma/chex`](https://github.com/Chidelma/CHEX).
268
+ That means FYLO is designed so that the system can recover from index drift without treating the index as a sacred durable database.
324
269
 
325
270
  ## Development
326
271
 
327
272
  ```bash
328
- bun test # Run all tests
329
- bun run build # Compile TypeScript
330
- bun run typecheck # Type-check without emitting
331
- bun run lint # ESLint
273
+ bun run typecheck
274
+ bun run build
275
+ bun test
332
276
  ```
333
277
 
334
- ### Local S3 (LocalStack)
278
+ ## Performance testing
279
+
280
+ FYLO includes an opt-in scale test for the filesystem engine:
335
281
 
336
282
  ```bash
337
- docker compose up aws
283
+ FYLO_RUN_PERF_TESTS=true bun test tests/integration/s3-files.performance.test.js
338
284
  ```
339
285
 
340
- This starts LocalStack on `localhost:4566`. Set `S3_ENDPOINT=http://localhost:4566` to route S3 calls locally.
341
-
342
- ## Security
343
-
344
- ### What Fylo does NOT provide
345
-
346
- Fylo is a low-level storage abstraction. The following must be implemented by the integrating application:
347
-
348
- - **Authentication** — Fylo has no concept of users or sessions. Any caller with access to the Fylo instance can read and write any collection.
349
- - **Authorization** — `executeSQL` and all document operations accept any collection name with no permission check. In multi-tenant applications, a caller can access any collection unless the integrator enforces a boundary above Fylo.
350
- - **Rate limiting** — There is no built-in request throttling. An attacker with access to the instance can flood S3 with requests or trigger expensive operations without restriction. Add rate limiting and document-size limits in your service layer.
351
-
352
- ### Secure configuration
353
-
354
- | Concern | Guidance |
355
- | ---------------- | -------------------------------------------------------------------------------------------------------------------------------------------- |
356
- | AWS credentials | Never commit credentials to version control. Use IAM instance roles or inject via CI secrets. Rotate any credentials that have been exposed. |
357
- | `ENCRYPTION_KEY` | Must be at least 32 characters. Use a high-entropy random value. |
358
- | `CIPHER_SALT` | Set a unique random value per deployment to prevent cross-instance precomputation attacks. |
359
- | `REDIS_URL` | Always set explicitly. Use `rediss://` (TLS) in production with authentication credentials in the URL. |
360
- | Collection names | Must match `^[a-z0-9][a-z0-9\-]*[a-z0-9]$`. Names are validated before any shell or S3 operation. |
361
-
362
- ### Encrypted fields
363
-
364
- Fields listed in `$encrypted` in a collection schema are encrypted with AES-256-CBC. By default a random IV is used per write (non-deterministic). Pass `deterministic: true` to `Cipher.encrypt()` only for fields that require `$eq`/`$ne` queries — deterministic encryption leaks value equality to observers of stored ciphertext.
365
-
366
- ## License
367
-
368
- MIT
286
+ This is useful when you want to see how index size and query latency behave as collections grow.
package/package.json CHANGED
@@ -1,18 +1,15 @@
1
1
  {
2
2
  "name": "@delma/fylo",
3
- "version": "2.0.0",
3
+ "version": "2.1.0",
4
4
  "main": "./dist/index.js",
5
5
  "types": "./dist/types/index.d.ts",
6
6
  "bin": {
7
- "fylo.query": "./dist/cli/index.js",
8
- "fylo.worker": "./dist/worker.js",
9
- "fylo.migrate": "./dist/migrate-cli.js"
7
+ "fylo.query": "./dist/cli/index.js"
10
8
  },
11
9
  "scripts": {
12
10
  "build": "tsc",
13
11
  "test": "bun test",
14
12
  "typecheck": "tsc -p tsconfig.typecheck.json",
15
- "worker": "bun run ./src/worker.ts",
16
13
  "lint": "prettier --check \"src/**/*.{ts,d.ts}\" \"tests/**/*.js\" README.md",
17
14
  "format": "prettier --write src tests"
18
15
  },