@delma/fylo 1.1.2 → 2.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.
Files changed (66) hide show
  1. package/README.md +141 -62
  2. package/eslint.config.js +8 -4
  3. package/package.json +9 -7
  4. package/src/CLI +16 -14
  5. package/src/adapters/cipher.ts +12 -6
  6. package/src/adapters/redis.ts +193 -123
  7. package/src/adapters/s3.ts +6 -12
  8. package/src/core/collection.ts +5 -0
  9. package/src/core/directory.ts +120 -151
  10. package/src/core/extensions.ts +4 -2
  11. package/src/core/format.ts +390 -419
  12. package/src/core/parser.ts +167 -142
  13. package/src/core/query.ts +31 -26
  14. package/src/core/walker.ts +68 -61
  15. package/src/core/write-queue.ts +7 -4
  16. package/src/engines/s3-files.ts +888 -0
  17. package/src/engines/types.ts +21 -0
  18. package/src/index.ts +754 -378
  19. package/src/migrate-cli.ts +22 -0
  20. package/src/migrate.ts +74 -0
  21. package/src/types/bun-runtime.d.ts +73 -0
  22. package/src/types/fylo.d.ts +115 -27
  23. package/src/types/node-runtime.d.ts +61 -0
  24. package/src/types/query.d.ts +6 -2
  25. package/src/types/vendor-modules.d.ts +8 -7
  26. package/src/worker.ts +7 -1
  27. package/src/workers/write-worker.ts +25 -24
  28. package/tests/collection/truncate.test.js +35 -0
  29. package/tests/{data.ts → data.js} +8 -21
  30. package/tests/{index.ts → index.js} +4 -9
  31. package/tests/integration/aws-s3-files.canary.test.js +22 -0
  32. package/tests/integration/{create.test.ts → create.test.js} +13 -31
  33. package/tests/integration/delete.test.js +95 -0
  34. package/tests/integration/{edge-cases.test.ts → edge-cases.test.js} +50 -124
  35. package/tests/integration/{encryption.test.ts → encryption.test.js} +20 -65
  36. package/tests/integration/{export.test.ts → export.test.js} +8 -23
  37. package/tests/integration/{join-modes.test.ts → join-modes.test.js} +37 -104
  38. package/tests/integration/migration.test.js +38 -0
  39. package/tests/integration/nested.test.js +142 -0
  40. package/tests/integration/operators.test.js +122 -0
  41. package/tests/integration/{queue.test.ts → queue.test.js} +24 -40
  42. package/tests/integration/read.test.js +119 -0
  43. package/tests/integration/rollback.test.js +60 -0
  44. package/tests/integration/s3-files.test.js +108 -0
  45. package/tests/integration/update.test.js +99 -0
  46. package/tests/mocks/{cipher.ts → cipher.js} +11 -26
  47. package/tests/mocks/redis.js +123 -0
  48. package/tests/mocks/{s3.ts → s3.js} +24 -58
  49. package/tests/schemas/album.json +1 -1
  50. package/tests/schemas/comment.json +1 -1
  51. package/tests/schemas/photo.json +1 -1
  52. package/tests/schemas/post.json +1 -1
  53. package/tests/schemas/tip.json +1 -1
  54. package/tests/schemas/todo.json +1 -1
  55. package/tests/schemas/user.d.ts +12 -12
  56. package/tests/schemas/user.json +1 -1
  57. package/tsconfig.json +4 -2
  58. package/tsconfig.typecheck.json +31 -0
  59. package/tests/collection/truncate.test.ts +0 -56
  60. package/tests/integration/delete.test.ts +0 -147
  61. package/tests/integration/nested.test.ts +0 -212
  62. package/tests/integration/operators.test.ts +0 -167
  63. package/tests/integration/read.test.ts +0 -203
  64. package/tests/integration/rollback.test.ts +0 -105
  65. package/tests/integration/update.test.ts +0 -130
  66. package/tests/mocks/redis.ts +0 -169
@@ -0,0 +1,99 @@
1
+ import { test, expect, describe, beforeAll, afterAll, mock } from 'bun:test'
2
+ import Fylo from '../../src'
3
+ import { photosURL, todosURL } from '../data'
4
+ import S3Mock from '../mocks/s3'
5
+ import RedisMock from '../mocks/redis'
6
+ const PHOTOS = `photo`
7
+ const TODOS = `todo`
8
+ const fylo = new Fylo()
9
+ mock.module('../../src/adapters/s3', () => ({ S3: S3Mock }))
10
+ mock.module('../../src/adapters/redis', () => ({ Redis: RedisMock }))
11
+ beforeAll(async () => {
12
+ await Promise.all([Fylo.createCollection(PHOTOS), fylo.executeSQL(`CREATE TABLE ${TODOS}`)])
13
+ try {
14
+ await fylo.importBulkData(PHOTOS, new URL(photosURL), 100)
15
+ await fylo.importBulkData(TODOS, new URL(todosURL), 100)
16
+ } catch {
17
+ await fylo.rollback()
18
+ }
19
+ })
20
+ afterAll(async () => {
21
+ await Promise.all([Fylo.dropCollection(PHOTOS), fylo.executeSQL(`DROP TABLE ${TODOS}`)])
22
+ })
23
+ describe('NO-SQL', async () => {
24
+ test('UPDATE ONE', async () => {
25
+ const ids = []
26
+ for await (const data of Fylo.findDocs(PHOTOS, { $limit: 1, $onlyIds: true }).collect()) {
27
+ ids.push(data)
28
+ }
29
+ try {
30
+ await fylo.patchDoc(PHOTOS, { [ids.shift()]: { title: 'All Mighty' } })
31
+ } catch {
32
+ await fylo.rollback()
33
+ }
34
+ let results = {}
35
+ for await (const data of Fylo.findDocs(PHOTOS, {
36
+ $ops: [{ title: { $eq: 'All Mighty' } }]
37
+ }).collect()) {
38
+ results = { ...results, ...data }
39
+ }
40
+ expect(Object.keys(results).length).toBe(1)
41
+ })
42
+ test('UPDATE CLAUSE', async () => {
43
+ let count = -1
44
+ try {
45
+ count = await fylo.patchDocs(PHOTOS, {
46
+ $set: { title: 'All Mighti' },
47
+ $where: { $ops: [{ title: { $like: '%est%' } }] }
48
+ })
49
+ } catch {
50
+ await fylo.rollback()
51
+ }
52
+ let results = {}
53
+ for await (const data of Fylo.findDocs(PHOTOS, {
54
+ $ops: [{ title: { $eq: 'All Mighti' } }]
55
+ }).collect()) {
56
+ results = { ...results, ...data }
57
+ }
58
+ expect(Object.keys(results).length).toBe(count)
59
+ })
60
+ test('UPDATE ALL', async () => {
61
+ let count = -1
62
+ try {
63
+ count = await fylo.patchDocs(PHOTOS, { $set: { title: 'All Mighter' } })
64
+ } catch {
65
+ await fylo.rollback()
66
+ }
67
+ let results = {}
68
+ for await (const data of Fylo.findDocs(PHOTOS, {
69
+ $ops: [{ title: { $eq: 'All Mighter' } }]
70
+ }).collect()) {
71
+ results = { ...results, ...data }
72
+ }
73
+ expect(Object.keys(results).length).toBe(count)
74
+ }, 20000)
75
+ })
76
+ describe('SQL', async () => {
77
+ test('UPDATE CLAUSE', async () => {
78
+ let count = -1
79
+ try {
80
+ count = await fylo.executeSQL(
81
+ `UPDATE ${TODOS} SET title = 'All Mighty' WHERE title LIKE '%est%'`
82
+ )
83
+ } catch {
84
+ await fylo.rollback()
85
+ }
86
+ const results = await fylo.executeSQL(`SELECT * FROM ${TODOS} WHERE title = 'All Mighty'`)
87
+ expect(Object.keys(results).length).toBe(count)
88
+ })
89
+ test('UPDATE ALL', async () => {
90
+ let count = -1
91
+ try {
92
+ count = await fylo.executeSQL(`UPDATE ${TODOS} SET title = 'All Mightier'`)
93
+ } catch {
94
+ await fylo.rollback()
95
+ }
96
+ const results = await fylo.executeSQL(`SELECT * FROM ${TODOS} WHERE title = 'All Mightier'`)
97
+ expect(Object.keys(results).length).toBe(count)
98
+ }, 20000)
99
+ })
@@ -1,55 +1,40 @@
1
- /**
2
- * Pass-through Cipher mock for tests that don't need real encryption.
3
- * Returns values as-is (base64-encoded to match real adapter interface shape).
4
- */
5
1
  export class CipherMock {
6
-
7
- private static _configured = false
8
- private static collections: Map<string, Set<string>> = new Map()
9
-
10
- static isConfigured(): boolean {
2
+ static _configured = false
3
+ static collections = new Map()
4
+ static isConfigured() {
11
5
  return CipherMock._configured
12
6
  }
13
-
14
- static hasEncryptedFields(collection: string): boolean {
7
+ static hasEncryptedFields(collection) {
15
8
  const fields = CipherMock.collections.get(collection)
16
9
  return !!fields && fields.size > 0
17
10
  }
18
-
19
- static isEncryptedField(collection: string, field: string): boolean {
11
+ static isEncryptedField(collection, field) {
20
12
  const fields = CipherMock.collections.get(collection)
21
13
  if (!fields || fields.size === 0) return false
22
-
23
14
  for (const pattern of fields) {
24
15
  if (field === pattern) return true
25
16
  if (field.startsWith(`${pattern}/`)) return true
26
17
  }
27
-
28
18
  return false
29
19
  }
30
-
31
- static registerFields(collection: string, fields: string[]): void {
20
+ static registerFields(collection, fields) {
32
21
  if (fields.length > 0) {
33
22
  CipherMock.collections.set(collection, new Set(fields))
34
23
  }
35
24
  }
36
-
37
- static async configure(_secret: string): Promise<void> {
25
+ static async configure(_secret) {
38
26
  CipherMock._configured = true
39
27
  }
40
-
41
- static reset(): void {
28
+ static reset() {
42
29
  CipherMock._configured = false
43
30
  CipherMock.collections = new Map()
44
31
  }
45
-
46
- static async encrypt(value: string): Promise<string> {
32
+ static async encrypt(value) {
47
33
  return btoa(value).replace(/\+/g, '-').replace(/\//g, '_').replace(/=+$/, '')
48
34
  }
49
-
50
- static async decrypt(encoded: string): Promise<string> {
35
+ static async decrypt(encoded) {
51
36
  const b64 = encoded.replace(/-/g, '+').replace(/_/g, '/')
52
- const padded = b64 + '='.repeat((4 - b64.length % 4) % 4)
37
+ const padded = b64 + '='.repeat((4 - (b64.length % 4)) % 4)
53
38
  return atob(padded)
54
39
  }
55
40
  }
@@ -0,0 +1,123 @@
1
+ export default class RedisMock {
2
+ static stream = []
3
+ static jobs = new Map()
4
+ static docs = new Map()
5
+ static locks = new Map()
6
+ static deadLetters = []
7
+ static nextId = 0
8
+ async publish(_collection, _action, _keyId) {}
9
+ async claimTTID(_id, _ttlSeconds = 10) {
10
+ return true
11
+ }
12
+ async enqueueWrite(job) {
13
+ RedisMock.jobs.set(job.jobId, { ...job, nextAttemptAt: job.nextAttemptAt ?? Date.now() })
14
+ RedisMock.docs.set(`fylo:doc:${job.collection}:${job.docId}`, {
15
+ status: 'queued',
16
+ lastJobId: job.jobId,
17
+ updatedAt: String(Date.now())
18
+ })
19
+ const streamId = String(++RedisMock.nextId)
20
+ RedisMock.stream.push({
21
+ streamId,
22
+ jobId: job.jobId,
23
+ collection: job.collection,
24
+ docId: job.docId,
25
+ operation: job.operation
26
+ })
27
+ return streamId
28
+ }
29
+ async readWriteJobs(workerId, count = 1) {
30
+ const available = RedisMock.stream.filter((entry) => !entry.claimedBy).slice(0, count)
31
+ for (const entry of available) entry.claimedBy = workerId
32
+ return available.map((entry) => ({
33
+ streamId: entry.streamId,
34
+ job: { ...RedisMock.jobs.get(entry.jobId) }
35
+ }))
36
+ }
37
+ async ackWriteJob(streamId) {
38
+ RedisMock.stream = RedisMock.stream.filter((item) => item.streamId !== streamId)
39
+ }
40
+ async deadLetterWriteJob(streamId, job, reason) {
41
+ RedisMock.deadLetters.push({
42
+ streamId: String(RedisMock.deadLetters.length + 1),
43
+ jobId: job.jobId,
44
+ reason,
45
+ failedAt: Date.now()
46
+ })
47
+ await this.ackWriteJob(streamId)
48
+ }
49
+ async claimPendingJobs(workerId, _minIdleMs = 30000, count = 10) {
50
+ const pending = RedisMock.stream.filter((entry) => entry.claimedBy).slice(0, count)
51
+ for (const entry of pending) entry.claimedBy = workerId
52
+ return pending.map((entry) => ({
53
+ streamId: entry.streamId,
54
+ job: { ...RedisMock.jobs.get(entry.jobId) }
55
+ }))
56
+ }
57
+ async setJobStatus(jobId, status, extra = {}) {
58
+ const job = RedisMock.jobs.get(jobId)
59
+ if (job) Object.assign(job, extra, { status, updatedAt: Date.now() })
60
+ }
61
+ async setDocStatus(collection, docId, status, jobId) {
62
+ const key = `fylo:doc:${collection}:${docId}`
63
+ const curr = RedisMock.docs.get(key) ?? {}
64
+ RedisMock.docs.set(key, {
65
+ ...curr,
66
+ status,
67
+ updatedAt: String(Date.now()),
68
+ ...(jobId ? { lastJobId: jobId } : {})
69
+ })
70
+ }
71
+ async getJob(jobId) {
72
+ const job = RedisMock.jobs.get(jobId)
73
+ return job ? { ...job } : null
74
+ }
75
+ async getDocStatus(collection, docId) {
76
+ return RedisMock.docs.get(`fylo:doc:${collection}:${docId}`) ?? null
77
+ }
78
+ async readDeadLetters(count = 10) {
79
+ return RedisMock.deadLetters.slice(0, count).map((item) => ({
80
+ streamId: item.streamId,
81
+ job: { ...RedisMock.jobs.get(item.jobId) },
82
+ reason: item.reason,
83
+ failedAt: item.failedAt
84
+ }))
85
+ }
86
+ async replayDeadLetter(streamId) {
87
+ const item = RedisMock.deadLetters.find((entry) => entry.streamId === streamId)
88
+ if (!item) return null
89
+ const job = RedisMock.jobs.get(item.jobId)
90
+ if (!job) return null
91
+ const replayed = {
92
+ ...job,
93
+ status: 'queued',
94
+ error: undefined,
95
+ workerId: undefined,
96
+ attempts: 0,
97
+ updatedAt: Date.now(),
98
+ nextAttemptAt: Date.now()
99
+ }
100
+ RedisMock.jobs.set(item.jobId, replayed)
101
+ await this.enqueueWrite(replayed)
102
+ RedisMock.deadLetters = RedisMock.deadLetters.filter((entry) => entry.streamId !== streamId)
103
+ return { ...replayed }
104
+ }
105
+ async getQueueStats() {
106
+ return {
107
+ queued: RedisMock.stream.length,
108
+ pending: RedisMock.stream.filter((entry) => entry.claimedBy).length,
109
+ deadLetters: RedisMock.deadLetters.length
110
+ }
111
+ }
112
+ async acquireDocLock(collection, docId, jobId) {
113
+ const key = `fylo:lock:${collection}:${docId}`
114
+ if (RedisMock.locks.has(key)) return false
115
+ RedisMock.locks.set(key, jobId)
116
+ return true
117
+ }
118
+ async releaseDocLock(collection, docId, jobId) {
119
+ const key = `fylo:lock:${collection}:${docId}`
120
+ if (RedisMock.locks.get(key) === jobId) RedisMock.locks.delete(key)
121
+ }
122
+ async *subscribe(_collection) {}
123
+ }
@@ -1,66 +1,43 @@
1
- /**
2
- * In-memory S3 mock. Replaces src/adapters/s3 so tests never touch real S3
3
- * or the AWS CLI. Each test file gets a fresh store because mock.module is
4
- * hoisted before imports, and module-level state is isolated per test file
5
- * in Bun's test runner.
6
- *
7
- * createBucket / deleteBucket are no-ops (bucket creation/deletion is
8
- * handled implicitly by the in-memory store).
9
- */
10
-
11
- const store = new Map<string, Map<string, string>>()
12
-
13
- function getBucket(name: string): Map<string, string> {
1
+ const store = new Map()
2
+ function getBucket(name) {
14
3
  if (!store.has(name)) store.set(name, new Map())
15
- return store.get(name)!
4
+ return store.get(name)
16
5
  }
17
-
18
6
  export default class S3Mock {
19
-
20
- static readonly BUCKET_ENV = process.env.BUCKET_PREFIX
21
-
22
- static readonly CREDS = {
7
+ static BUCKET_ENV = process.env.BUCKET_PREFIX
8
+ static CREDS = {
23
9
  accessKeyId: 'mock',
24
10
  secretAccessKey: 'mock',
25
11
  region: 'mock',
26
- endpoint: undefined as string | undefined
12
+ endpoint: undefined
27
13
  }
28
-
29
- static getBucketFormat(collection: string): string {
14
+ static getBucketFormat(collection) {
30
15
  return S3Mock.BUCKET_ENV ? `${S3Mock.BUCKET_ENV}-${collection}` : collection
31
16
  }
32
-
33
- static file(collection: string, path: string) {
17
+ static file(collection, path) {
34
18
  const bucket = getBucket(S3Mock.getBucketFormat(collection))
35
19
  return {
36
20
  get size() {
37
21
  const val = bucket.get(path)
38
22
  return val !== undefined ? val.length : 0
39
23
  },
40
- async text(): Promise<string> {
24
+ async text() {
41
25
  return bucket.get(path) ?? ''
42
26
  }
43
27
  }
44
28
  }
45
-
46
- static async list(collection: string, options: {
47
- prefix?: string
48
- delimiter?: string
49
- maxKeys?: number
50
- continuationToken?: string
51
- } = {}) {
29
+ static async list(collection, options = {}) {
52
30
  const bucket = getBucket(S3Mock.getBucketFormat(collection))
53
31
  const prefix = options.prefix ?? ''
54
32
  const delimiter = options.delimiter
55
33
  const maxKeys = options.maxKeys ?? 1000
56
34
  const token = options.continuationToken
57
-
58
- const allKeys = Array.from(bucket.keys()).filter(k => k.startsWith(prefix)).sort()
59
-
35
+ const allKeys = Array.from(bucket.keys())
36
+ .filter((k) => k.startsWith(prefix))
37
+ .sort()
60
38
  if (delimiter) {
61
- const prefixSet = new Set<string>()
62
- const contents: Array<{ key: string }> = []
63
-
39
+ const prefixSet = new Set()
40
+ const contents = []
64
41
  for (const key of allKeys) {
65
42
  const rest = key.slice(prefix.length)
66
43
  const idx = rest.indexOf(delimiter)
@@ -70,45 +47,34 @@ export default class S3Mock {
70
47
  contents.push({ key })
71
48
  }
72
49
  }
73
-
74
- const allPrefixes = Array.from(prefixSet).map(p => ({ prefix: p }))
50
+ const allPrefixes = Array.from(prefixSet).map((p) => ({ prefix: p }))
75
51
  const limitedPrefixes = allPrefixes.slice(0, maxKeys)
76
-
77
52
  return {
78
53
  contents: contents.length ? contents : undefined,
79
- commonPrefixes: limitedPrefixes.length
80
- ? limitedPrefixes
81
- : undefined,
54
+ commonPrefixes: limitedPrefixes.length ? limitedPrefixes : undefined,
82
55
  isTruncated: allPrefixes.length > maxKeys,
83
56
  nextContinuationToken: undefined
84
57
  }
85
58
  }
86
-
87
59
  const startIdx = token ? parseInt(token) : 0
88
60
  const page = allKeys.slice(startIdx, startIdx + maxKeys)
89
- const nextToken = startIdx + maxKeys < allKeys.length
90
- ? String(startIdx + maxKeys)
91
- : undefined
92
-
61
+ const nextToken =
62
+ startIdx + maxKeys < allKeys.length ? String(startIdx + maxKeys) : undefined
93
63
  return {
94
- contents: page.length ? page.map(k => ({ key: k })) : undefined,
64
+ contents: page.length ? page.map((k) => ({ key: k })) : undefined,
95
65
  isTruncated: !!nextToken,
96
66
  nextContinuationToken: nextToken,
97
67
  commonPrefixes: undefined
98
68
  }
99
69
  }
100
-
101
- static async put(collection: string, path: string, data: string): Promise<void> {
70
+ static async put(collection, path, data) {
102
71
  getBucket(S3Mock.getBucketFormat(collection)).set(path, data)
103
72
  }
104
-
105
- static async delete(collection: string, path: string): Promise<void> {
73
+ static async delete(collection, path) {
106
74
  getBucket(S3Mock.getBucketFormat(collection)).delete(path)
107
75
  }
108
-
109
- static async createBucket(_collection: string): Promise<void> {}
110
-
111
- static async deleteBucket(collection: string): Promise<void> {
76
+ static async createBucket(_collection) {}
77
+ static async deleteBucket(collection) {
112
78
  store.delete(S3Mock.getBucketFormat(collection))
113
79
  }
114
80
  }
@@ -2,4 +2,4 @@
2
2
  "id": -0,
3
3
  "userId": -0,
4
4
  "title": ""
5
- }
5
+ }
@@ -4,4 +4,4 @@
4
4
  "email": "",
5
5
  "body": "",
6
6
  "postId": -0
7
- }
7
+ }
@@ -4,4 +4,4 @@
4
4
  "title": "",
5
5
  "url": "",
6
6
  "thumbnailUrl": ""
7
- }
7
+ }
@@ -3,4 +3,4 @@
3
3
  "userId": -0,
4
4
  "title": "",
5
5
  "body": ""
6
- }
6
+ }
@@ -4,4 +4,4 @@
4
4
  "text": "",
5
5
  "date": "",
6
6
  "compliment_count": -0
7
- }
7
+ }
@@ -3,4 +3,4 @@
3
3
  "userId": -0,
4
4
  "title": "",
5
5
  "completed": false
6
- }
6
+ }
@@ -4,20 +4,20 @@ interface _user {
4
4
  username: string
5
5
  email: string
6
6
  address: {
7
- street: string
8
- suite: string
9
- city: string
10
- zipcode: string
11
- geo: {
12
- lat: string
13
- lng: string
7
+ street: string
8
+ suite: string
9
+ city: string
10
+ zipcode: string
11
+ geo: {
12
+ lat: string
13
+ lng: string
14
+ }
14
15
  }
15
- }
16
16
  phone: string
17
17
  website: string
18
18
  company: {
19
- name: string
20
- catchPhrase: string
21
- bs: string
22
- }
19
+ name: string
20
+ catchPhrase: string
21
+ bs: string
22
+ }
23
23
  }
@@ -20,4 +20,4 @@
20
20
  "catchPhrase": "",
21
21
  "bs": ""
22
22
  }
23
- }
23
+ }
package/tsconfig.json CHANGED
@@ -5,7 +5,7 @@
5
5
  "target": "ESNext",
6
6
  "lib": ["ESNext"],
7
7
  "module": "ES2022",
8
- "moduleResolution": "node",
8
+ "moduleResolution": "bundler",
9
9
  "sourceMap": true,
10
10
  "experimentalDecorators": true,
11
11
  "pretty": true,
@@ -15,5 +15,7 @@
15
15
  "types": ["bun-types", "node"],
16
16
  "isolatedModules": true,
17
17
  "skipLibCheck": true
18
- }
18
+ },
19
+ "include": ["src/**/*"],
20
+ "exclude": ["dist", "tests", "node_modules"]
19
21
  }
@@ -0,0 +1,31 @@
1
+ {
2
+ "compilerOptions": {
3
+ "strict": true,
4
+ "target": "ESNext",
5
+ "lib": ["ESNext", "DOM"],
6
+ "module": "ES2022",
7
+ "moduleResolution": "bundler",
8
+ "experimentalDecorators": true,
9
+ "pretty": true,
10
+ "noFallthroughCasesInSwitch": true,
11
+ "noImplicitReturns": true,
12
+ "forceConsistentCasingInFileNames": true,
13
+ "isolatedModules": true,
14
+ "skipLibCheck": true,
15
+ "noEmit": true
16
+ },
17
+ "include": [
18
+ "src/**/*.ts",
19
+ "src/types/node-runtime.d.ts",
20
+ "src/types/vendor-modules.d.ts",
21
+ "src/types/fylo.d.ts",
22
+ "src/types/query.d.ts",
23
+ "src/types/bun-runtime.d.ts"
24
+ ],
25
+ "exclude": [
26
+ "dist",
27
+ "tests",
28
+ "node_modules",
29
+ "src/types/index.d.ts"
30
+ ]
31
+ }
@@ -1,56 +0,0 @@
1
- import { test, expect, describe, afterAll, mock } from 'bun:test'
2
- import Fylo from '../../src'
3
- import { postsURL, albumURL } from '../data'
4
- import S3Mock from '../mocks/s3'
5
- import RedisMock from '../mocks/redis'
6
-
7
- const POSTS = `post`
8
- const ALBUMS = `album`
9
-
10
- afterAll(async () => {
11
- await Promise.all([Fylo.dropCollection(ALBUMS), Fylo.dropCollection(POSTS)])
12
- })
13
-
14
- mock.module('../../src/adapters/s3', () => ({ S3: S3Mock }))
15
- mock.module('../../src/adapters/redis', () => ({ Redis: RedisMock }))
16
-
17
- describe("NO-SQL", () => {
18
-
19
- test("TRUNCATE", async () => {
20
-
21
- const fylo = new Fylo()
22
-
23
- await Fylo.createCollection(POSTS)
24
-
25
- await fylo.importBulkData<_post>(POSTS, new URL(postsURL))
26
-
27
- await fylo.delDocs<_post>(POSTS)
28
-
29
- const ids: _ttid[] = []
30
-
31
- for await (const data of Fylo.findDocs<_post>(POSTS, { $limit: 1, $onlyIds: true }).collect()) {
32
-
33
- ids.push(data as _ttid)
34
- }
35
-
36
- expect(ids.length).toBe(0)
37
- })
38
- })
39
-
40
- describe("SQL", () => {
41
-
42
- test("TRUNCATE", async () => {
43
-
44
- const fylo = new Fylo()
45
-
46
- await fylo.executeSQL<_album>(`CREATE TABLE ${ALBUMS}`)
47
-
48
- await fylo.importBulkData<_album>(ALBUMS, new URL(albumURL))
49
-
50
- await fylo.executeSQL<_album>(`DELETE FROM ${ALBUMS}`)
51
-
52
- const ids = await fylo.executeSQL<_album>(`SELECT _id FROM ${ALBUMS} LIMIT 1`) as _ttid[]
53
-
54
- expect(ids.length).toBe(0)
55
- })
56
- })