core-nails 1.0.1 → 1.0.2
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/dist/helpers/InitializeModel.d.ts +31 -0
- package/dist/helpers/InitializeModel.js +95 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.js +2 -0
- package/package.json +6 -2
- package/src/helpers/InitializeModel.ts +161 -0
- package/src/index.ts +3 -0
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { z, ZodObject, ZodRawShape } from "zod";
|
|
2
|
+
type WhereOperator = "==" | "!=" | "<" | "<=" | ">" | ">=" | "array-contains" | "in" | "not-in" | "array-contains-any";
|
|
3
|
+
type WhereCondition<T> = {
|
|
4
|
+
field: keyof T;
|
|
5
|
+
operator: WhereOperator;
|
|
6
|
+
value: any;
|
|
7
|
+
};
|
|
8
|
+
declare function InitializeModel<TSchema extends ZodObject<ZodRawShape>>(opts: {
|
|
9
|
+
collection: string;
|
|
10
|
+
schema: TSchema;
|
|
11
|
+
FirebaseAdmin: any;
|
|
12
|
+
}): {
|
|
13
|
+
all(): Promise<(z.core.output<TSchema> & {
|
|
14
|
+
id: string;
|
|
15
|
+
})[]>;
|
|
16
|
+
find(id: string): Promise<(z.core.output<TSchema> & {
|
|
17
|
+
id: string;
|
|
18
|
+
}) | null>;
|
|
19
|
+
create(data: z.core.output<TSchema>, id?: string): Promise<(z.core.output<TSchema> & {
|
|
20
|
+
id: string;
|
|
21
|
+
}) | null>;
|
|
22
|
+
update(data: Partial<z.core.output<TSchema>>, id: string): Promise<void>;
|
|
23
|
+
destroy(id: string): Promise<void>;
|
|
24
|
+
where(conditions: WhereCondition<z.core.output<TSchema>>[]): Promise<(z.core.output<TSchema> & {
|
|
25
|
+
id: string;
|
|
26
|
+
})[]>;
|
|
27
|
+
find_by(conditions: WhereCondition<z.core.output<TSchema>>[]): Promise<(z.core.output<TSchema> & {
|
|
28
|
+
id: string;
|
|
29
|
+
}) | null>;
|
|
30
|
+
};
|
|
31
|
+
export default InitializeModel;
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
function InitializeModel(opts) {
|
|
2
|
+
const { collection: collectionName, schema, FirebaseAdmin } = opts;
|
|
3
|
+
const db = FirebaseAdmin.firestore();
|
|
4
|
+
const collection = db.collection(collectionName);
|
|
5
|
+
function parseDoc(doc) {
|
|
6
|
+
if (!doc.exists)
|
|
7
|
+
return null;
|
|
8
|
+
const result = schema.safeParse(doc.data());
|
|
9
|
+
if (!result.success || typeof result.data !== "object" || result.data === null)
|
|
10
|
+
return null;
|
|
11
|
+
return {
|
|
12
|
+
id: doc.id,
|
|
13
|
+
...result.data,
|
|
14
|
+
};
|
|
15
|
+
}
|
|
16
|
+
function parseDocs(snapshot) {
|
|
17
|
+
return snapshot.docs
|
|
18
|
+
.map(parseDoc)
|
|
19
|
+
.filter((doc) => doc !== null);
|
|
20
|
+
}
|
|
21
|
+
return {
|
|
22
|
+
async all() {
|
|
23
|
+
const snapshot = await collection.get();
|
|
24
|
+
return parseDocs(snapshot);
|
|
25
|
+
},
|
|
26
|
+
async find(id) {
|
|
27
|
+
const snapshot = await collection.doc(id).get();
|
|
28
|
+
return parseDoc(snapshot);
|
|
29
|
+
},
|
|
30
|
+
async create(data, id) {
|
|
31
|
+
const dataWithTimestamp = {
|
|
32
|
+
...data,
|
|
33
|
+
createdAt: FirebaseAdmin.firestore.FieldValue.serverTimestamp(),
|
|
34
|
+
updatedAt: FirebaseAdmin.firestore.FieldValue.serverTimestamp(),
|
|
35
|
+
};
|
|
36
|
+
const parsed = schema.parse(dataWithTimestamp);
|
|
37
|
+
let docRef;
|
|
38
|
+
if (id) {
|
|
39
|
+
docRef = collection.doc(id);
|
|
40
|
+
const existingDoc = await docRef.get();
|
|
41
|
+
if (existingDoc.exists) {
|
|
42
|
+
throw new Error(`Document with ID ${id} already exists!`);
|
|
43
|
+
}
|
|
44
|
+
await docRef.set(parsed);
|
|
45
|
+
}
|
|
46
|
+
else {
|
|
47
|
+
docRef = await collection.add(parsed);
|
|
48
|
+
}
|
|
49
|
+
const docSnap = await docRef.get();
|
|
50
|
+
if (!docSnap.exists)
|
|
51
|
+
return null;
|
|
52
|
+
const result = schema.safeParse(docSnap.data());
|
|
53
|
+
if (!result.success || typeof result.data !== "object" || result.data === null)
|
|
54
|
+
return null;
|
|
55
|
+
return {
|
|
56
|
+
id: docSnap.id,
|
|
57
|
+
...result.data,
|
|
58
|
+
};
|
|
59
|
+
},
|
|
60
|
+
async update(data, id) {
|
|
61
|
+
if ("createdAt" in data) {
|
|
62
|
+
throw new Error("Cannot modify createdAt field");
|
|
63
|
+
}
|
|
64
|
+
const dataWithTimestamp = {
|
|
65
|
+
...data,
|
|
66
|
+
updatedAt: FirebaseAdmin.firestore.FieldValue.serverTimestamp(),
|
|
67
|
+
};
|
|
68
|
+
const partialSchema = schema.partial();
|
|
69
|
+
const parsed = partialSchema.parse(dataWithTimestamp);
|
|
70
|
+
await collection.doc(id).update(parsed);
|
|
71
|
+
},
|
|
72
|
+
async destroy(id) {
|
|
73
|
+
await collection.doc(id).delete();
|
|
74
|
+
},
|
|
75
|
+
async where(conditions) {
|
|
76
|
+
let query = collection;
|
|
77
|
+
for (const cond of conditions) {
|
|
78
|
+
query = query.where(cond.field, cond.operator, cond.value);
|
|
79
|
+
}
|
|
80
|
+
const snapshot = await query.get();
|
|
81
|
+
return parseDocs(snapshot);
|
|
82
|
+
},
|
|
83
|
+
async find_by(conditions) {
|
|
84
|
+
let query = collection;
|
|
85
|
+
for (const cond of conditions) {
|
|
86
|
+
query = query.where(cond.field, cond.operator, cond.value);
|
|
87
|
+
}
|
|
88
|
+
const snapshot = await query.limit(1).get();
|
|
89
|
+
if (snapshot.empty)
|
|
90
|
+
return null;
|
|
91
|
+
return parseDoc(snapshot.docs[0]);
|
|
92
|
+
},
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
export default InitializeModel;
|
package/dist/index.d.ts
ADDED
package/dist/index.js
ADDED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "core-nails",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.2",
|
|
4
4
|
"scripts": {
|
|
5
5
|
"build": "tsc"
|
|
6
6
|
},
|
|
@@ -13,6 +13,10 @@
|
|
|
13
13
|
"typescript": "^6.0.2"
|
|
14
14
|
},
|
|
15
15
|
"dependencies": {
|
|
16
|
-
"pluralize": "^8.0.0"
|
|
16
|
+
"pluralize": "^8.0.0",
|
|
17
|
+
"zod": "^4.3.6"
|
|
18
|
+
},
|
|
19
|
+
"exports": {
|
|
20
|
+
".": "./dist/index.js"
|
|
17
21
|
}
|
|
18
22
|
}
|
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
import { z, ZodObject, ZodRawShape } from "zod";
|
|
2
|
+
|
|
3
|
+
type WhereOperator =
|
|
4
|
+
| "=="
|
|
5
|
+
| "!="
|
|
6
|
+
| "<"
|
|
7
|
+
| "<="
|
|
8
|
+
| ">"
|
|
9
|
+
| ">="
|
|
10
|
+
| "array-contains"
|
|
11
|
+
| "in"
|
|
12
|
+
| "not-in"
|
|
13
|
+
| "array-contains-any";
|
|
14
|
+
|
|
15
|
+
type WhereCondition<T> = {
|
|
16
|
+
field: keyof T;
|
|
17
|
+
operator: WhereOperator;
|
|
18
|
+
value: any;
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
function InitializeModel<TSchema extends ZodObject<ZodRawShape>>(opts: {
|
|
22
|
+
collection: string;
|
|
23
|
+
schema: TSchema;
|
|
24
|
+
FirebaseAdmin: any;
|
|
25
|
+
}) {
|
|
26
|
+
const { collection: collectionName, schema, FirebaseAdmin } = opts;
|
|
27
|
+
|
|
28
|
+
type T = z.infer<TSchema>;
|
|
29
|
+
type WithId = T & { id: string };
|
|
30
|
+
|
|
31
|
+
const db = FirebaseAdmin.firestore();
|
|
32
|
+
const collection = db.collection(collectionName);
|
|
33
|
+
|
|
34
|
+
function parseDoc(doc: any): WithId | null {
|
|
35
|
+
if (!doc.exists) return null;
|
|
36
|
+
|
|
37
|
+
const result = schema.safeParse(doc.data());
|
|
38
|
+
|
|
39
|
+
if (!result.success || typeof result.data !== "object" || result.data === null) return null;
|
|
40
|
+
|
|
41
|
+
return {
|
|
42
|
+
id: doc.id,
|
|
43
|
+
...result.data,
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function parseDocs(
|
|
48
|
+
snapshot: any
|
|
49
|
+
): WithId[] {
|
|
50
|
+
return snapshot.docs
|
|
51
|
+
.map(parseDoc)
|
|
52
|
+
.filter((doc: WithId | null): doc is WithId => doc !== null);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
return {
|
|
56
|
+
async all(): Promise<WithId[]> {
|
|
57
|
+
const snapshot = await collection.get();
|
|
58
|
+
return parseDocs(snapshot);
|
|
59
|
+
},
|
|
60
|
+
|
|
61
|
+
async find(id: string): Promise<WithId | null> {
|
|
62
|
+
const snapshot = await collection.doc(id).get();
|
|
63
|
+
return parseDoc(snapshot);
|
|
64
|
+
},
|
|
65
|
+
|
|
66
|
+
async create(data: T, id?: string): Promise<WithId | null> {
|
|
67
|
+
const dataWithTimestamp = {
|
|
68
|
+
...data,
|
|
69
|
+
createdAt: FirebaseAdmin.firestore.FieldValue.serverTimestamp(),
|
|
70
|
+
updatedAt: FirebaseAdmin.firestore.FieldValue.serverTimestamp(),
|
|
71
|
+
};
|
|
72
|
+
|
|
73
|
+
const parsed = schema.parse(dataWithTimestamp) as any;
|
|
74
|
+
|
|
75
|
+
let docRef: any;
|
|
76
|
+
|
|
77
|
+
if (id) {
|
|
78
|
+
docRef = collection.doc(id);
|
|
79
|
+
|
|
80
|
+
const existingDoc = await docRef.get();
|
|
81
|
+
if (existingDoc.exists) {
|
|
82
|
+
throw new Error(`Document with ID ${id} already exists!`);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
await docRef.set(parsed);
|
|
86
|
+
} else {
|
|
87
|
+
docRef = await collection.add(parsed);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
const docSnap = await docRef.get();
|
|
91
|
+
if (!docSnap.exists) return null;
|
|
92
|
+
|
|
93
|
+
const result = schema.safeParse(docSnap.data());
|
|
94
|
+
if (!result.success || typeof result.data !== "object" || result.data === null) return null;
|
|
95
|
+
|
|
96
|
+
return {
|
|
97
|
+
id: docSnap.id,
|
|
98
|
+
...result.data,
|
|
99
|
+
};
|
|
100
|
+
},
|
|
101
|
+
|
|
102
|
+
async update(data: Partial<T>, id: string): Promise<void> {
|
|
103
|
+
if ("createdAt" in data) {
|
|
104
|
+
throw new Error("Cannot modify createdAt field");
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
const dataWithTimestamp = {
|
|
108
|
+
...data,
|
|
109
|
+
updatedAt: FirebaseAdmin.firestore.FieldValue.serverTimestamp(),
|
|
110
|
+
};
|
|
111
|
+
|
|
112
|
+
const partialSchema = schema.partial();
|
|
113
|
+
const parsed = partialSchema.parse(dataWithTimestamp);
|
|
114
|
+
|
|
115
|
+
await collection.doc(id).update(parsed);
|
|
116
|
+
},
|
|
117
|
+
|
|
118
|
+
async destroy(id: string): Promise<void> {
|
|
119
|
+
await collection.doc(id).delete();
|
|
120
|
+
},
|
|
121
|
+
|
|
122
|
+
async where(
|
|
123
|
+
conditions: WhereCondition<T>[]
|
|
124
|
+
): Promise<WithId[]> {
|
|
125
|
+
let query: any = collection;
|
|
126
|
+
|
|
127
|
+
for (const cond of conditions) {
|
|
128
|
+
query = query.where(
|
|
129
|
+
cond.field as string,
|
|
130
|
+
cond.operator,
|
|
131
|
+
cond.value
|
|
132
|
+
);
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
const snapshot = await query.get();
|
|
136
|
+
return parseDocs(snapshot);
|
|
137
|
+
},
|
|
138
|
+
|
|
139
|
+
async find_by(
|
|
140
|
+
conditions: WhereCondition<T>[]
|
|
141
|
+
): Promise<WithId | null> {
|
|
142
|
+
let query: any = collection;
|
|
143
|
+
|
|
144
|
+
for (const cond of conditions) {
|
|
145
|
+
query = query.where(
|
|
146
|
+
cond.field as string,
|
|
147
|
+
cond.operator,
|
|
148
|
+
cond.value
|
|
149
|
+
);
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
const snapshot = await query.limit(1).get();
|
|
153
|
+
|
|
154
|
+
if (snapshot.empty) return null;
|
|
155
|
+
|
|
156
|
+
return parseDoc(snapshot.docs[0]);
|
|
157
|
+
},
|
|
158
|
+
};
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
export default InitializeModel;
|
package/src/index.ts
ADDED