@delma/fylo 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/.env.example +16 -0
- package/.github/copilot-instructions.md +113 -0
- package/.github/prompts/issue.prompt.md +19 -0
- package/.github/prompts/pr.prompt.md +18 -0
- package/.github/prompts/release.prompt.md +49 -0
- package/.github/prompts/review-pr.prompt.md +19 -0
- package/.github/prompts/sync-main.prompt.md +14 -0
- package/.github/workflows/ci.yml +37 -0
- package/.github/workflows/publish.yml +101 -0
- package/.prettierrc +7 -0
- package/LICENSE +21 -0
- package/README.md +230 -0
- package/eslint.config.js +28 -0
- package/package.json +51 -0
- package/src/CLI +37 -0
- package/src/adapters/cipher.ts +174 -0
- package/src/adapters/redis.ts +71 -0
- package/src/adapters/s3.ts +67 -0
- package/src/core/directory.ts +418 -0
- package/src/core/extensions.ts +19 -0
- package/src/core/format.ts +486 -0
- package/src/core/parser.ts +876 -0
- package/src/core/query.ts +48 -0
- package/src/core/walker.ts +167 -0
- package/src/index.ts +1088 -0
- package/src/types/fylo.d.ts +139 -0
- package/src/types/index.d.ts +3 -0
- package/src/types/query.d.ts +73 -0
- package/tests/collection/truncate.test.ts +56 -0
- package/tests/data.ts +110 -0
- package/tests/index.ts +19 -0
- package/tests/integration/create.test.ts +57 -0
- package/tests/integration/delete.test.ts +147 -0
- package/tests/integration/edge-cases.test.ts +232 -0
- package/tests/integration/encryption.test.ts +176 -0
- package/tests/integration/export.test.ts +61 -0
- package/tests/integration/join-modes.test.ts +221 -0
- package/tests/integration/nested.test.ts +212 -0
- package/tests/integration/operators.test.ts +167 -0
- package/tests/integration/read.test.ts +203 -0
- package/tests/integration/rollback.test.ts +105 -0
- package/tests/integration/update.test.ts +130 -0
- package/tests/mocks/cipher.ts +55 -0
- package/tests/mocks/redis.ts +13 -0
- package/tests/mocks/s3.ts +114 -0
- package/tests/schemas/album.d.ts +5 -0
- package/tests/schemas/album.json +5 -0
- package/tests/schemas/comment.d.ts +7 -0
- package/tests/schemas/comment.json +7 -0
- package/tests/schemas/photo.d.ts +7 -0
- package/tests/schemas/photo.json +7 -0
- package/tests/schemas/post.d.ts +6 -0
- package/tests/schemas/post.json +6 -0
- package/tests/schemas/tip.d.ts +7 -0
- package/tests/schemas/tip.json +7 -0
- package/tests/schemas/todo.d.ts +6 -0
- package/tests/schemas/todo.json +6 -0
- package/tests/schemas/user.d.ts +23 -0
- package/tests/schemas/user.json +23 -0
- package/tsconfig.json +19 -0
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
interface _getDoc {
|
|
2
|
+
[Symbol.asyncIterator]<T>(): AsyncGenerator<_ttid | Record<_ttid, T>, void, unknown>;
|
|
3
|
+
once<T>(): Promise<Record<_ttid, T>>
|
|
4
|
+
onDelete(): AsyncGenerator<_ttid, void, unknown>
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
interface _findDocs {
|
|
8
|
+
[Symbol.asyncIterator]<T>(): AsyncGenerator<_ttid | Record<_ttid, T> | Record<string, _ttid[]> | Record<_ttid, Partial<T>> | undefined, void, unknown>
|
|
9
|
+
once<T>(): Promise<Record<_ttid, T>>
|
|
10
|
+
onDelete(): AsyncGenerator<_ttid, void, unknown>
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
interface ObjectConstructor {
|
|
14
|
+
appendGroup: (target: Record<string, any>, source: Record<string, any>) => Record<string, any>;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
interface Console {
|
|
18
|
+
format: (docs: Record<string, any>) => void
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
type _joinDocs<T, U> = _ttid[] | Record<string, _ttid[]> | Record<string, Record<_ttid, Partial<T | U>>> | Record<`${_ttid}, ${_ttid}`, T | U | (T & U) | (Partial<T> & Partial<U>)>
|
|
22
|
+
|
|
23
|
+
declare module "@vyckr/fylo" {
|
|
24
|
+
|
|
25
|
+
export default class {
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Rolls back all transcations in current instance
|
|
29
|
+
*/
|
|
30
|
+
rollback(): Promise<void>
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Executes a SQL query and returns the results.
|
|
34
|
+
* @param SQL The SQL query to execute.
|
|
35
|
+
* @returns The results of the query.
|
|
36
|
+
*/
|
|
37
|
+
executeSQL<T extends Record<string, any>, U extends Record<string, any> = {}>(SQL: string): Promise<number | void | any[] | _ttid | Record<any, any>>
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Creates a new schema for a collection.
|
|
41
|
+
* @param collection The name of the collection.
|
|
42
|
+
*/
|
|
43
|
+
static createCollection(collection: string): Promise<void>
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Drops an existing schema for a collection.
|
|
47
|
+
* @param collection The name of the collection.
|
|
48
|
+
*/
|
|
49
|
+
static dropCollection(collection: string): Promise<void>
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Imports data from a URL into a collection.
|
|
53
|
+
* @param collection The name of the collection.
|
|
54
|
+
* @param url The URL of the data to import.
|
|
55
|
+
* @param limit The maximum number of documents to import.
|
|
56
|
+
*/
|
|
57
|
+
importBulkData(collection: string, url: URL, limit?: number): Promise<number>
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Exports data from a collection to a URL.
|
|
61
|
+
* @param collection The name of the collection.
|
|
62
|
+
* @returns The current data exported from the collection.
|
|
63
|
+
*/
|
|
64
|
+
exportBulkData<T extends Record<string, any>>(collection: string): AsyncGenerator<T, void, unknown>
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Gets a document from a collection.
|
|
68
|
+
* @param collection The name of the collection.
|
|
69
|
+
* @param _id The ID of the document.
|
|
70
|
+
* @param onlyId Whether to only return the ID of the document.
|
|
71
|
+
* @returns The document or the ID of the document.
|
|
72
|
+
*/
|
|
73
|
+
static getDoc(collection: string, _id: _ttid, onlyId: boolean): _getDoc
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Puts multiple documents into a collection.
|
|
77
|
+
* @param collection The name of the collection.
|
|
78
|
+
* @param batch The documents to put.
|
|
79
|
+
* @returns The IDs of the documents.
|
|
80
|
+
*/
|
|
81
|
+
batchPutData<T extends Record<string, any>>(collection: string, batch: Array<T>): Promise<_ttid[]>
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Puts a document into a collection.
|
|
85
|
+
* @param collection The name of the collection.
|
|
86
|
+
* @param data The document to put.
|
|
87
|
+
* @returns The ID of the document.
|
|
88
|
+
*/
|
|
89
|
+
putData<T extends Record<string, any>>(collection: string, data: Record<_ttid, T> | T): Promise<_ttid>
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* Patches a document in a collection.
|
|
93
|
+
* @param collection The name of the collection.
|
|
94
|
+
* @param newDoc The new document data.
|
|
95
|
+
* @param oldDoc The old document data.
|
|
96
|
+
* @returns The number of documents patched.
|
|
97
|
+
*/
|
|
98
|
+
patchDoc<T extends Record<string, any>>(collection: string, newDoc: Record<_ttid, Partial<T>>, oldDoc: Record<_ttid, T>): Promise<_ttid>
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* Patches documents in a collection.
|
|
102
|
+
* @param collection The name of the collection.
|
|
103
|
+
* @param updateSchema The update schema.
|
|
104
|
+
* @returns The number of documents patched.
|
|
105
|
+
*/
|
|
106
|
+
patchDocs<T extends Record<string, any>>(collection: string, updateSchema: _storeUpdate<T>): Promise<number>
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* Deletes a document from a collection.
|
|
110
|
+
* @param collection The name of the collection.
|
|
111
|
+
* @param _id The ID of the document.
|
|
112
|
+
* @returns The number of documents deleted.
|
|
113
|
+
*/
|
|
114
|
+
delDoc(collection: string, _id: _ttid): Promise<void>
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* Deletes documents from a collection.
|
|
118
|
+
* @param collection The name of the collection.
|
|
119
|
+
* @param deleteSchema The delete schema.
|
|
120
|
+
* @returns The number of documents deleted.
|
|
121
|
+
*/
|
|
122
|
+
delDocs<T extends Record<string, any>>(collection: string, deleteSchema?: _storeDelete<T>): Promise<number>
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* Joins documents from two collections.
|
|
126
|
+
* @param join The join schema.
|
|
127
|
+
* @returns The joined documents.
|
|
128
|
+
*/
|
|
129
|
+
static joinDocs<T extends Record<string, any>, U extends Record<string, any>>(join: _join<T, U>): Promise<_joinDocs<T, U>>
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
* Finds documents in a collection.
|
|
133
|
+
* @param collection The name of the collection.
|
|
134
|
+
* @param query The query schema.
|
|
135
|
+
* @returns The found documents.
|
|
136
|
+
*/
|
|
137
|
+
static findDocs<T extends Record<string, any>>(collection: string, query?: _storeQuery<T>): _findDocs
|
|
138
|
+
}
|
|
139
|
+
}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
interface _joinOperand<U> {
|
|
2
|
+
$eq?: keyof U
|
|
3
|
+
$ne?: keyof U
|
|
4
|
+
$gt?: keyof U
|
|
5
|
+
$lt?: keyof U
|
|
6
|
+
$gte?: keyof U
|
|
7
|
+
$lte?: keyof U
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
interface _timestamp {
|
|
11
|
+
$gt?: number
|
|
12
|
+
$lt?: number
|
|
13
|
+
$gte?: number
|
|
14
|
+
$lte?: number
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
interface _operand {
|
|
18
|
+
$eq?: any
|
|
19
|
+
$ne?: any
|
|
20
|
+
$gt?: number
|
|
21
|
+
$lt?: number
|
|
22
|
+
$gte?: number
|
|
23
|
+
$lte?: number
|
|
24
|
+
$like?: string
|
|
25
|
+
$contains?: string | number | boolean
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
type _op<T> = Partial<Record<keyof T, _operand>>
|
|
29
|
+
|
|
30
|
+
type _on<T, U> = Partial<Record<keyof T, _joinOperand<U>>>
|
|
31
|
+
|
|
32
|
+
type _join<T, U> = {
|
|
33
|
+
$select?: Array<keyof T | keyof U>
|
|
34
|
+
$leftCollection: string
|
|
35
|
+
$rightCollection: string
|
|
36
|
+
$mode: "inner" | "left" | "right" | "outer"
|
|
37
|
+
$on: _on<T, U>
|
|
38
|
+
$limit?: number
|
|
39
|
+
$onlyIds?: boolean
|
|
40
|
+
$groupby?: keyof T | keyof U
|
|
41
|
+
$rename?: Record<keyof Partial<T> | keyof Partial<U>, string>
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
interface _storeQuery<T extends Record<string, any>> {
|
|
45
|
+
$select?: Array<keyof T>
|
|
46
|
+
$rename?: Record<keyof Partial<T>, string>
|
|
47
|
+
$collection?: string
|
|
48
|
+
$ops?: Array<_op<T>>
|
|
49
|
+
$limit?: number
|
|
50
|
+
$onlyIds?: boolean
|
|
51
|
+
$groupby?: keyof T
|
|
52
|
+
$updated?: _timestamp
|
|
53
|
+
$created?: _timestamp
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
interface _condition { column: string, operator: string, value: string | number| boolean | null }
|
|
57
|
+
|
|
58
|
+
type _storeUpdate<T extends Record<string, any>> = {
|
|
59
|
+
$collection?: string
|
|
60
|
+
$where?: _storeQuery<T>
|
|
61
|
+
$set: {
|
|
62
|
+
[K in keyof Partial<T>]: T[K]
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
type _storeDelete<T extends Record<string, any>> = _storeQuery<T>
|
|
67
|
+
|
|
68
|
+
type _storeInsert<T extends Record<string, any>> = {
|
|
69
|
+
$collection?: string
|
|
70
|
+
$values: {
|
|
71
|
+
[K in keyof T]: T[K]
|
|
72
|
+
}
|
|
73
|
+
}
|
|
@@ -0,0 +1,56 @@
|
|
|
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
|
+
})
|
package/tests/data.ts
ADDED
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
function makeDataUrl(data: unknown): string {
|
|
2
|
+
return `data:application/json,${encodeURIComponent(JSON.stringify(data))}`
|
|
3
|
+
}
|
|
4
|
+
|
|
5
|
+
function generateAlbums(): _album[] {
|
|
6
|
+
return Array.from({ length: 100 }, (_, index) => {
|
|
7
|
+
const id = index + 1
|
|
8
|
+
const userId = Math.ceil(id / 10)
|
|
9
|
+
const prefix = id <= 15 ? 'omnis' : id % 4 === 0 ? 'quidem' : 'album'
|
|
10
|
+
|
|
11
|
+
return {
|
|
12
|
+
id,
|
|
13
|
+
userId,
|
|
14
|
+
title: `${prefix} album ${id}`
|
|
15
|
+
}
|
|
16
|
+
})
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
function generatePosts(): _post[] {
|
|
20
|
+
return Array.from({ length: 100 }, (_, index) => {
|
|
21
|
+
const id = index + 1
|
|
22
|
+
const userId = Math.ceil(id / 10)
|
|
23
|
+
|
|
24
|
+
return {
|
|
25
|
+
id,
|
|
26
|
+
userId,
|
|
27
|
+
title: `post title ${id}`,
|
|
28
|
+
body: `post body ${id} for user ${userId}`
|
|
29
|
+
}
|
|
30
|
+
})
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
function generateComments(): _comment[] {
|
|
34
|
+
return Array.from({ length: 100 }, (_, index) => {
|
|
35
|
+
const id = index + 1
|
|
36
|
+
|
|
37
|
+
return {
|
|
38
|
+
id,
|
|
39
|
+
postId: id,
|
|
40
|
+
name: `comment ${id}`,
|
|
41
|
+
email: `comment${id}@example.com`,
|
|
42
|
+
body: `comment body ${id}`
|
|
43
|
+
}
|
|
44
|
+
})
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function generatePhotos(): _photo[] {
|
|
48
|
+
return Array.from({ length: 100 }, (_, index) => {
|
|
49
|
+
const id = index + 1
|
|
50
|
+
const title = id % 3 === 0 ? `test photo ${id}` : `photo ${id}`
|
|
51
|
+
|
|
52
|
+
return {
|
|
53
|
+
id,
|
|
54
|
+
albumId: Math.ceil(id / 10),
|
|
55
|
+
title,
|
|
56
|
+
url: `https://example.com/photos/${id}.jpg`,
|
|
57
|
+
thumbnailUrl: `https://example.com/photos/${id}-thumb.jpg`
|
|
58
|
+
}
|
|
59
|
+
})
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
function generateTodos(): _todo[] {
|
|
63
|
+
return Array.from({ length: 100 }, (_, index) => {
|
|
64
|
+
const id = index + 1
|
|
65
|
+
|
|
66
|
+
return {
|
|
67
|
+
id,
|
|
68
|
+
userId: Math.ceil(id / 10),
|
|
69
|
+
title: id % 4 === 0 ? `test todo ${id}` : `todo ${id}`,
|
|
70
|
+
completed: id % 2 === 0
|
|
71
|
+
}
|
|
72
|
+
})
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
function generateUsers(): _user[] {
|
|
76
|
+
return Array.from({ length: 10 }, (_, index) => {
|
|
77
|
+
const id = index + 1
|
|
78
|
+
|
|
79
|
+
return {
|
|
80
|
+
id,
|
|
81
|
+
name: `User ${id}`,
|
|
82
|
+
username: `user${id}`,
|
|
83
|
+
email: `user${id}@example.com`,
|
|
84
|
+
address: {
|
|
85
|
+
street: `Main Street ${id}`,
|
|
86
|
+
suite: `Suite ${id}`,
|
|
87
|
+
city: id <= 5 ? 'South Christy' : 'North Christy',
|
|
88
|
+
zipcode: `0000${id}`,
|
|
89
|
+
geo: {
|
|
90
|
+
lat: 10 + id,
|
|
91
|
+
lng: -20 - id
|
|
92
|
+
}
|
|
93
|
+
},
|
|
94
|
+
phone: `555-000-${String(id).padStart(4, '0')}`,
|
|
95
|
+
website: `user${id}.example.com`,
|
|
96
|
+
company: {
|
|
97
|
+
name: id <= 5 ? 'Acme Labs' : 'Northwind Labs',
|
|
98
|
+
catchPhrase: `Catch phrase ${id}`,
|
|
99
|
+
bs: `business ${id}`
|
|
100
|
+
}
|
|
101
|
+
} as unknown as _user
|
|
102
|
+
})
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
export const albumURL = makeDataUrl(generateAlbums())
|
|
106
|
+
export const postsURL = makeDataUrl(generatePosts())
|
|
107
|
+
export const commentsURL = makeDataUrl(generateComments())
|
|
108
|
+
export const photosURL = makeDataUrl(generatePhotos())
|
|
109
|
+
export const todosURL = makeDataUrl(generateTodos())
|
|
110
|
+
export const usersURL = makeDataUrl(generateUsers())
|
package/tests/index.ts
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { Redis } from '../src/adapters/redis'
|
|
2
|
+
import ttid from '@vyckr/ttid'
|
|
3
|
+
|
|
4
|
+
const redisPub = new Redis()
|
|
5
|
+
const redisSub = new Redis()
|
|
6
|
+
|
|
7
|
+
setTimeout(async () => {
|
|
8
|
+
await redisPub.publish("bun", "insert", ttid.generate())
|
|
9
|
+
}, 2000)
|
|
10
|
+
|
|
11
|
+
setTimeout(async () => {
|
|
12
|
+
await redisPub.publish("bun", "insert", ttid.generate())
|
|
13
|
+
}, 3000)
|
|
14
|
+
|
|
15
|
+
await Bun.sleep(1000)
|
|
16
|
+
|
|
17
|
+
for await (const data of redisSub.subscribe("bun")) {
|
|
18
|
+
console.log("Received:", data)
|
|
19
|
+
}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { test, expect, describe, beforeAll, afterAll, mock } from 'bun:test'
|
|
2
|
+
import Fylo from '../../src'
|
|
3
|
+
import { albumURL, postsURL } 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
|
+
let postsCount = 0
|
|
11
|
+
let albumsCount = 0
|
|
12
|
+
|
|
13
|
+
const fylo = new Fylo()
|
|
14
|
+
|
|
15
|
+
mock.module('../../src/adapters/s3', () => ({ S3: S3Mock }))
|
|
16
|
+
mock.module('../../src/adapters/redis', () => ({ Redis: RedisMock }))
|
|
17
|
+
|
|
18
|
+
beforeAll(async () => {
|
|
19
|
+
|
|
20
|
+
await Promise.all([Fylo.createCollection(POSTS), fylo.executeSQL<_user>(`CREATE TABLE ${ALBUMS}`)])
|
|
21
|
+
|
|
22
|
+
try {
|
|
23
|
+
albumsCount = await fylo.importBulkData<_album>(ALBUMS, new URL(albumURL), 100)
|
|
24
|
+
postsCount = await fylo.importBulkData<_post>(POSTS, new URL(postsURL), 100)
|
|
25
|
+
} catch {
|
|
26
|
+
await fylo.rollback()
|
|
27
|
+
}
|
|
28
|
+
})
|
|
29
|
+
|
|
30
|
+
afterAll(async () => {
|
|
31
|
+
await Promise.all([Fylo.dropCollection(POSTS), fylo.executeSQL<_album>(`DROP TABLE ${ALBUMS}`)])
|
|
32
|
+
})
|
|
33
|
+
|
|
34
|
+
describe("NO-SQL", async () => {
|
|
35
|
+
|
|
36
|
+
test("PUT", async () => {
|
|
37
|
+
|
|
38
|
+
let results: Record<_ttid, _post> = {}
|
|
39
|
+
|
|
40
|
+
for await (const data of Fylo.findDocs<_post>(POSTS).collect()) {
|
|
41
|
+
|
|
42
|
+
results = { ...results, ...data as Record<_ttid, _post> }
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
expect(Object.keys(results).length).toEqual(postsCount)
|
|
46
|
+
})
|
|
47
|
+
})
|
|
48
|
+
|
|
49
|
+
describe("SQL", () => {
|
|
50
|
+
|
|
51
|
+
test("INSERT", async () => {
|
|
52
|
+
|
|
53
|
+
const results = await fylo.executeSQL<_album>(`SELECT * FROM ${ALBUMS}`) as Record<_ttid, _album>
|
|
54
|
+
|
|
55
|
+
expect(Object.keys(results).length).toEqual(albumsCount)
|
|
56
|
+
})
|
|
57
|
+
})
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
import { test, expect, describe, beforeAll, afterAll, mock } from 'bun:test'
|
|
2
|
+
import Fylo from '../../src'
|
|
3
|
+
import { commentsURL, usersURL } from '../data'
|
|
4
|
+
import S3Mock from '../mocks/s3'
|
|
5
|
+
import RedisMock from '../mocks/redis'
|
|
6
|
+
|
|
7
|
+
const COMMENTS = `comment`
|
|
8
|
+
const USERS = `user`
|
|
9
|
+
|
|
10
|
+
let commentsResults: Record<_ttid, _comment> = {}
|
|
11
|
+
let usersResults: Record<_ttid, _user> = {}
|
|
12
|
+
|
|
13
|
+
const fylo = new Fylo()
|
|
14
|
+
|
|
15
|
+
mock.module('../../src/adapters/s3', () => ({ S3: S3Mock }))
|
|
16
|
+
mock.module('../../src/adapters/redis', () => ({ Redis: RedisMock }))
|
|
17
|
+
|
|
18
|
+
beforeAll(async () => {
|
|
19
|
+
|
|
20
|
+
await Promise.all([
|
|
21
|
+
Fylo.createCollection(COMMENTS),
|
|
22
|
+
fylo.executeSQL<_post>(`CREATE TABLE ${USERS}`)
|
|
23
|
+
])
|
|
24
|
+
|
|
25
|
+
try {
|
|
26
|
+
|
|
27
|
+
await Promise.all([
|
|
28
|
+
fylo.importBulkData<_comment>(COMMENTS, new URL(commentsURL), 100),
|
|
29
|
+
fylo.importBulkData<_user>(USERS, new URL(usersURL), 100)
|
|
30
|
+
])
|
|
31
|
+
|
|
32
|
+
} catch {
|
|
33
|
+
await fylo.rollback()
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
for await (const data of Fylo.findDocs<_comment>(COMMENTS, { $limit: 1 }).collect()) {
|
|
37
|
+
|
|
38
|
+
commentsResults = { ...commentsResults, ...data as Record<_ttid, _comment> }
|
|
39
|
+
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
usersResults = await fylo.executeSQL<_user>(`SELECT * FROM ${USERS} LIMIT 1`) as Record<_ttid, _user>
|
|
43
|
+
})
|
|
44
|
+
|
|
45
|
+
afterAll(async () => {
|
|
46
|
+
await Promise.all([Fylo.dropCollection(COMMENTS), fylo.executeSQL<_user>(`DROP TABLE ${USERS}`)])
|
|
47
|
+
})
|
|
48
|
+
|
|
49
|
+
describe("NO-SQL", async () => {
|
|
50
|
+
|
|
51
|
+
test("DELETE ONE", async () => {
|
|
52
|
+
|
|
53
|
+
const id = Object.keys(commentsResults).shift()!
|
|
54
|
+
|
|
55
|
+
try {
|
|
56
|
+
await fylo.delDoc(COMMENTS, id)
|
|
57
|
+
} catch {
|
|
58
|
+
await fylo.rollback()
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
commentsResults = {}
|
|
62
|
+
|
|
63
|
+
for await (const data of Fylo.findDocs<_comment>(COMMENTS).collect()) {
|
|
64
|
+
|
|
65
|
+
commentsResults = { ...commentsResults, ...data as Record<_ttid, _comment> }
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
const idx = Object.keys(commentsResults).findIndex(_id => _id === id)
|
|
69
|
+
|
|
70
|
+
expect(idx).toEqual(-1)
|
|
71
|
+
|
|
72
|
+
})
|
|
73
|
+
|
|
74
|
+
test("DELETE CLAUSE", async () => {
|
|
75
|
+
|
|
76
|
+
try {
|
|
77
|
+
await fylo.delDocs<_comment>(COMMENTS, { $ops: [ { name: { $like: "%et%" } } ] })
|
|
78
|
+
//console.log
|
|
79
|
+
} catch(e) {
|
|
80
|
+
console.error(e)
|
|
81
|
+
await fylo.rollback()
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
commentsResults = {}
|
|
85
|
+
|
|
86
|
+
for await (const data of Fylo.findDocs<_comment>(COMMENTS, { $ops: [ { name: { $like: "%et%" } } ] }).collect()) {
|
|
87
|
+
|
|
88
|
+
// console.log(data)
|
|
89
|
+
|
|
90
|
+
commentsResults = { ...commentsResults, ...data as Record<_ttid, _comment> }
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
expect(Object.keys(commentsResults).length).toEqual(0)
|
|
94
|
+
})
|
|
95
|
+
|
|
96
|
+
test("DELETE ALL", async () => {
|
|
97
|
+
|
|
98
|
+
try {
|
|
99
|
+
await fylo.delDocs<_comment>(COMMENTS)
|
|
100
|
+
} catch {
|
|
101
|
+
await fylo.rollback()
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
commentsResults = {}
|
|
105
|
+
|
|
106
|
+
for await (const data of Fylo.findDocs<_comment>(COMMENTS).collect()) {
|
|
107
|
+
|
|
108
|
+
commentsResults = { ...commentsResults, ...data as Record<_ttid, _comment> }
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
expect(Object.keys(commentsResults).length).toEqual(0)
|
|
112
|
+
})
|
|
113
|
+
})
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
describe("SQL", async () => {
|
|
117
|
+
|
|
118
|
+
test("DELETE CLAUSE", async () => {
|
|
119
|
+
|
|
120
|
+
const name = Object.values(usersResults).shift()!.name
|
|
121
|
+
|
|
122
|
+
try {
|
|
123
|
+
await fylo.executeSQL<_user>(`DELETE FROM ${USERS} WHERE name = '${name}'`)
|
|
124
|
+
} catch {
|
|
125
|
+
await fylo.rollback()
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
usersResults = await fylo.executeSQL<_user>(`SELECT * FROM ${USERS} WHERE name = '${name}'`) as Record<_ttid, _user>
|
|
129
|
+
|
|
130
|
+
const idx = Object.values(usersResults).findIndex(com => com.name === name)
|
|
131
|
+
|
|
132
|
+
expect(idx).toBe(-1)
|
|
133
|
+
})
|
|
134
|
+
|
|
135
|
+
test("DELETE ALL", async () => {
|
|
136
|
+
|
|
137
|
+
try {
|
|
138
|
+
await fylo.executeSQL<_user>(`DELETE FROM ${USERS}`)
|
|
139
|
+
} catch {
|
|
140
|
+
await fylo.rollback()
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
usersResults = await fylo.executeSQL<_user>(`SELECT * FROM ${USERS}`) as Record<_ttid, _user>
|
|
144
|
+
|
|
145
|
+
expect(Object.keys(usersResults).length).toBe(0)
|
|
146
|
+
})
|
|
147
|
+
})
|