Skip to content

Commit 73287d9

Browse files
author
Eder Lima
committed
feat: add CRUD features
1 parent 72e9c20 commit 73287d9

File tree

7 files changed

+223
-126
lines changed

7 files changed

+223
-126
lines changed

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,3 @@
11
**/node_modules
2+
3+
database.json

database.json

Lines changed: 0 additions & 5 deletions
This file was deleted.

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
"dev": "ts-node-dev --transpile-only --ignore-watch node_modules src/index.ts"
88
},
99
"devDependencies": {
10+
"@types/express": "^4.17.13",
1011
"@types/node": "^18.0.0",
1112
"@types/uuid": "^8.3.4",
1213
"ts-node-dev": "^2.0.0",

src/fsdb/index.ts

Lines changed: 152 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,152 @@
1+
import fsl from 'fs'
2+
import fsp from 'fs/promises'
3+
import { v4 } from 'uuid'
4+
5+
const kData = Symbol('kData')
6+
const kDatabase = Symbol('kDatabase')
7+
8+
export type GenericDatabaseType = {
9+
[k: string]: any[]
10+
}
11+
12+
export type DatabaseRecord<T> = { _id: string } & T
13+
14+
type ArrayElement<T> = T extends readonly (infer ElementType)[]
15+
? ElementType
16+
: never
17+
18+
type CollectionRef<T extends GenericDatabaseType, K extends keyof T> = {
19+
[kData]: Array<ArrayElement<T[K]>>
20+
[kDatabase]: FileSystemDatabase<T>
21+
}
22+
23+
export default class FileSystemDatabase<T extends GenericDatabaseType> {
24+
public [kData]: T | null
25+
26+
private constructor(private filename: string) {
27+
this[kData] = null
28+
}
29+
30+
static fromRef<T extends GenericDatabaseType>(
31+
filename: string,
32+
ref: T
33+
): FileSystemDatabase<T> {
34+
const database = new FileSystemDatabase<T>(filename)
35+
database[kData] = ref
36+
return database
37+
}
38+
39+
static async fromFile<T extends GenericDatabaseType>(
40+
filename: string
41+
): Promise<FileSystemDatabase<T>> {
42+
const database = new FileSystemDatabase<T>(filename)
43+
await database.load()
44+
return database
45+
}
46+
47+
public async load(): Promise<void> {
48+
if (!fsl.existsSync(this.filename)) {
49+
this[kData] = {} as T
50+
51+
await fsp.writeFile(this.filename, JSON.stringify(this[kData]))
52+
}
53+
54+
this[kData] = JSON.parse(await fsp.readFile(this.filename, 'utf8'))
55+
}
56+
57+
public async save(): Promise<void> {
58+
await fsp.writeFile(this.filename, JSON.stringify(this[kData]))
59+
}
60+
61+
public async sync(): Promise<void> {
62+
await this.load()
63+
}
64+
}
65+
66+
export const collection = <T extends GenericDatabaseType, K extends keyof T>(
67+
database: FileSystemDatabase<T>,
68+
key: K
69+
): CollectionRef<T, K> => {
70+
if (!database[kData]![key]) {
71+
;(database[kData]![key] as any) = []
72+
}
73+
74+
return {
75+
[kData]: database[kData]![key],
76+
[kDatabase]: database,
77+
}
78+
}
79+
80+
export const createDoc = async <
81+
T extends GenericDatabaseType,
82+
R extends keyof T
83+
>(
84+
collection: CollectionRef<T, R>,
85+
doc: Omit<ArrayElement<T[R]>, '_id'>
86+
) => {
87+
const docDto: ArrayElement<T[R]> = {
88+
_id: v4(),
89+
...doc,
90+
} as any
91+
92+
collection[kData].push(docDto)
93+
await collection[kDatabase].save()
94+
95+
return docDto
96+
}
97+
98+
export const updateDoc = async <
99+
T extends GenericDatabaseType,
100+
R extends keyof T
101+
>(
102+
collection: CollectionRef<T, R>,
103+
id: string,
104+
doc: Partial<ArrayElement<T[R]>>
105+
) => {
106+
const index = collection[kData].findIndex((item) => item._id === id)
107+
108+
if (index === -1) {
109+
throw new Error('Record not found')
110+
}
111+
112+
Object.assign(collection[kData][index], doc)
113+
114+
await collection[kDatabase].save()
115+
return collection[kData][index]
116+
}
117+
118+
export const getDoc = async <T extends GenericDatabaseType, R extends keyof T>(
119+
collection: CollectionRef<T, R>,
120+
id: string
121+
) => {
122+
const index = collection[kData].findIndex((item) => item._id === id)
123+
124+
if (index === -1) {
125+
throw new Error('Record not found')
126+
}
127+
128+
return collection[kData][index]
129+
}
130+
131+
export const getDocs = async <T extends GenericDatabaseType, R extends keyof T>(
132+
collection: CollectionRef<T, R>
133+
) => {
134+
return collection[kData]
135+
}
136+
137+
export const deleteDoc = async <
138+
T extends GenericDatabaseType,
139+
R extends keyof T
140+
>(
141+
collection: CollectionRef<T, R>,
142+
id: string
143+
) => {
144+
const index = collection[kData].findIndex((item) => item._id === id)
145+
146+
if (index === -1) {
147+
throw new Error('Record not found')
148+
}
149+
150+
collection[kData].splice(index, 1)
151+
await collection[kDatabase].save()
152+
}

src/index.ts

Lines changed: 10 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
import FileSystemDatabase, { collection } from './lib/index'
2-
import type { DatabaseRecord } from './lib/index'
1+
import FileSystemDatabase, { collection, getDocs } from './fsdb'
2+
import type { DatabaseRecord } from './fsdb'
33

44
type Person = {
55
name: string
@@ -10,15 +10,14 @@ type DatabaseType = {
1010
people: Array<DatabaseRecord<Person>>
1111
}
1212

13-
FileSystemDatabase.fromFile<DatabaseType>('database.json').then(async (db) => {
14-
const peopleRef = collection(db, 'people')
13+
const main = async () => {
14+
const database = await FileSystemDatabase.fromFile<DatabaseType>(
15+
'database.json'
16+
)
1517

16-
peopleRef.createDoc({
17-
name: 'Eder',
18-
age: 20,
19-
})
20-
21-
const people = peopleRef.getDocs()
18+
const people = await getDocs(collection(database, 'people'))
2219

2320
console.log(people)
24-
})
21+
}
22+
23+
main()

src/lib/index.ts

Lines changed: 0 additions & 109 deletions
This file was deleted.

yarn.lock

Lines changed: 58 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,11 +47,68 @@
4747
resolved "https://registry.yarnpkg.com/@tsconfig/node16/-/node16-1.0.3.tgz#472eaab5f15c1ffdd7f8628bd4c4f753995ec79e"
4848
integrity sha512-yOlFc+7UtL/89t2ZhjPvvB/DeAr3r+Dq58IgzsFkOAvVC6NMJXmCGjbptdXdR9qsX7pKcTL+s87FtYREi2dEEQ==
4949

50-
"@types/node@^18.0.0":
50+
"@types/body-parser@*":
51+
version "1.19.2"
52+
resolved "https://registry.yarnpkg.com/@types/body-parser/-/body-parser-1.19.2.tgz#aea2059e28b7658639081347ac4fab3de166e6f0"
53+
integrity sha512-ALYone6pm6QmwZoAgeyNksccT9Q4AWZQ6PvfwR37GT6r6FWUPguq6sUmNGSMV2Wr761oQoBxwGGa6DR5o1DC9g==
54+
dependencies:
55+
"@types/connect" "*"
56+
"@types/node" "*"
57+
58+
"@types/connect@*":
59+
version "3.4.35"
60+
resolved "https://registry.yarnpkg.com/@types/connect/-/connect-3.4.35.tgz#5fcf6ae445e4021d1fc2219a4873cc73a3bb2ad1"
61+
integrity sha512-cdeYyv4KWoEgpBISTxWvqYsVy444DOqehiF3fM3ne10AmJ62RSyNkUnxMJXHQWRQQX2eR94m5y1IZyDwBjV9FQ==
62+
dependencies:
63+
"@types/node" "*"
64+
65+
"@types/express-serve-static-core@^4.17.18":
66+
version "4.17.29"
67+
resolved "https://registry.yarnpkg.com/@types/express-serve-static-core/-/express-serve-static-core-4.17.29.tgz#2a1795ea8e9e9c91b4a4bbe475034b20c1ec711c"
68+
integrity sha512-uMd++6dMKS32EOuw1Uli3e3BPgdLIXmezcfHv7N4c1s3gkhikBplORPpMq3fuWkxncZN1reb16d5n8yhQ80x7Q==
69+
dependencies:
70+
"@types/node" "*"
71+
"@types/qs" "*"
72+
"@types/range-parser" "*"
73+
74+
"@types/express@^4.17.13":
75+
version "4.17.13"
76+
resolved "https://registry.yarnpkg.com/@types/express/-/express-4.17.13.tgz#a76e2995728999bab51a33fabce1d705a3709034"
77+
integrity sha512-6bSZTPaTIACxn48l50SR+axgrqm6qXFIxrdAKaG6PaJk3+zuUr35hBlgT7vOmJcum+OEaIBLtHV/qloEAFITeA==
78+
dependencies:
79+
"@types/body-parser" "*"
80+
"@types/express-serve-static-core" "^4.17.18"
81+
"@types/qs" "*"
82+
"@types/serve-static" "*"
83+
84+
"@types/mime@^1":
85+
version "1.3.2"
86+
resolved "https://registry.yarnpkg.com/@types/mime/-/mime-1.3.2.tgz#93e25bf9ee75fe0fd80b594bc4feb0e862111b5a"
87+
integrity sha512-YATxVxgRqNH6nHEIsvg6k2Boc1JHI9ZbH5iWFFv/MTkchz3b1ieGDa5T0a9RznNdI0KhVbdbWSN+KWWrQZRxTw==
88+
89+
"@types/node@*", "@types/node@^18.0.0":
5190
version "18.0.0"
5291
resolved "https://registry.yarnpkg.com/@types/node/-/node-18.0.0.tgz#67c7b724e1bcdd7a8821ce0d5ee184d3b4dd525a"
5392
integrity sha512-cHlGmko4gWLVI27cGJntjs/Sj8th9aYwplmZFwmmgYQQvL5NUsgVJG7OddLvNfLqYS31KFN0s3qlaD9qCaxACA==
5493

94+
"@types/qs@*":
95+
version "6.9.7"
96+
resolved "https://registry.yarnpkg.com/@types/qs/-/qs-6.9.7.tgz#63bb7d067db107cc1e457c303bc25d511febf6cb"
97+
integrity sha512-FGa1F62FT09qcrueBA6qYTrJPVDzah9a+493+o2PCXsesWHIn27G98TsSMs3WPNbZIEj4+VJf6saSFpvD+3Zsw==
98+
99+
"@types/range-parser@*":
100+
version "1.2.4"
101+
resolved "https://registry.yarnpkg.com/@types/range-parser/-/range-parser-1.2.4.tgz#cd667bcfdd025213aafb7ca5915a932590acdcdc"
102+
integrity sha512-EEhsLsD6UsDM1yFhAvy0Cjr6VwmpMWqFBCb9w07wVugF7w9nfajxLuVmngTIpgS6svCnm6Vaw+MZhoDCKnOfsw==
103+
104+
"@types/serve-static@*":
105+
version "1.13.10"
106+
resolved "https://registry.yarnpkg.com/@types/serve-static/-/serve-static-1.13.10.tgz#f5e0ce8797d2d7cc5ebeda48a52c96c4fa47a8d9"
107+
integrity sha512-nCkHGI4w7ZgAdNkrEu0bv+4xNV/XDqW+DydknebMOQwkpDGx8G+HTlj7R7ABI8i8nKxVw0wtKPi1D+lPOkh4YQ==
108+
dependencies:
109+
"@types/mime" "^1"
110+
"@types/node" "*"
111+
55112
"@types/strip-bom@^3.0.0":
56113
version "3.0.0"
57114
resolved "https://registry.yarnpkg.com/@types/strip-bom/-/strip-bom-3.0.0.tgz#14a8ec3956c2e81edb7520790aecf21c290aebd2"

0 commit comments

Comments
 (0)