Skip to content

Commit 0d355dc

Browse files
committed
πŸ”– Release v0.4.0
1 parent e2137b5 commit 0d355dc

File tree

14 files changed

+1043
-706
lines changed

14 files changed

+1043
-706
lines changed

β€Žlib/Base.d.tsβ€Ž

Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
import DetaBase from 'deta/dist/types/base';
2+
import { OfflineDB } from './Offline';
3+
import { ParsedOptions, BaseSchema, BaseOptions, BaseDocument, Query, FullSchema } from './types';
4+
import { Schema } from './Schema';
5+
/**
6+
* Create and interact with a Deta Base
7+
*/
8+
export declare class Base<SchemaType> {
9+
_baseName: string;
10+
_baseSchema: Schema<SchemaType>;
11+
_db: DetaBase | OfflineDB;
12+
_opts: ParsedOptions;
13+
/**
14+
* Create a new Base with the provided name, schema and options
15+
* @param {string} name Name of the Base
16+
* @param {BaseOptions} opts Options object
17+
*/
18+
constructor(name: string, schema: BaseSchema | Schema<SchemaType>, opts?: BaseOptions);
19+
/**
20+
* Create a new document with the provided data based on the Base schema
21+
* @param {SchemaType} data Object representing the data of the new document
22+
* @returns {BaseDocument} Document
23+
*/
24+
create(rawData: FullSchema<SchemaType>): BaseDocument<SchemaType>;
25+
/**
26+
* Helper function to create and immediately save a new document
27+
* @param {SchemaType} data Object representing the data of the new document
28+
* @returns {BaseDocument} Document
29+
*/
30+
save(data: FullSchema<SchemaType>): Promise<BaseDocument<SchemaType>>;
31+
_parseQuery(queryObj: Query<SchemaType>): any;
32+
/**
33+
* Wrapper around the Deta Base SDK fetch method
34+
*
35+
* Automatically gets all items until the limit or since the last item
36+
* @internal
37+
*/
38+
_fetch(query?: any, limit?: number, last?: string): Promise<{
39+
items: any[];
40+
last?: string;
41+
}>;
42+
/**
43+
* Find all documents matching the query.
44+
*
45+
* Use limit and last to paginate the result.
46+
*
47+
* @param query A query object or array of query objects
48+
* @param limit Maximum number of items to return
49+
* @param last The key of the last item to start from
50+
* @returns Array of Documents
51+
*/
52+
find(query?: Query<SchemaType> | Query<SchemaType>[], limit?: number, last?: string): Promise<BaseDocument<SchemaType>[]>;
53+
/**
54+
* Find a single document matching the query.
55+
*
56+
* @param query A query object
57+
* @returns Document
58+
*/
59+
findOne(query?: Query<SchemaType> | Query<SchemaType>[]): Promise<BaseDocument<SchemaType> | undefined>;
60+
/**
61+
* Find a single document by its key
62+
*
63+
* @param key The key of the document
64+
* @returns Document
65+
*/
66+
findByKey(key: string): Promise<BaseDocument<SchemaType> | undefined>;
67+
/**
68+
* Find a single document matching the query and update it with the provided data.
69+
*
70+
* @param query A query object
71+
* @param data The data to update
72+
* @returns Document
73+
*/
74+
findOneAndUpdate(query: Query<SchemaType> | Query<SchemaType>[] | undefined, data: Partial<SchemaType>): Promise<BaseDocument<SchemaType>>;
75+
/**
76+
* Find a single document by its key and update it with the provided data.
77+
*
78+
* @param key The key of the document
79+
* @param data The data to update
80+
* @returns Document
81+
*/
82+
findByKeyAndUpdate(key: string, data: Partial<SchemaType>): Promise<BaseDocument<SchemaType>>;
83+
/**
84+
* Find a single document by its key and delete it.
85+
*
86+
* @param key The key of the document
87+
*/
88+
findByKeyAndDelete(key: string): Promise<void>;
89+
/**
90+
* Find a single document matching the query and delete it.
91+
*
92+
* @param query A query object
93+
*/
94+
findOneAndDelete(query?: Query<SchemaType> | Query<SchemaType>[]): Promise<void>;
95+
}

β€Žlib/Base.jsβ€Ž

Lines changed: 260 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,260 @@
1+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
2+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
3+
return new (P || (P = Promise))(function (resolve, reject) {
4+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
5+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
6+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
7+
step((generator = generator.apply(thisArg, _arguments || [])).next());
8+
});
9+
};
10+
/* eslint-disable valid-jsdoc */
11+
import { Deta } from 'deta';
12+
import { generateKey } from './random';
13+
import { OfflineDB } from './Offline';
14+
import { Schema } from './Schema';
15+
import { Document } from './Document';
16+
/**
17+
* Create and interact with a Deta Base
18+
*/
19+
export class Base {
20+
/**
21+
* Create a new Base with the provided name, schema and options
22+
* @param {string} name Name of the Base
23+
* @param {BaseOptions} opts Options object
24+
*/
25+
constructor(name, schema, opts) {
26+
this._baseName = name;
27+
if (schema instanceof Schema) {
28+
this._baseSchema = schema;
29+
}
30+
else {
31+
this._baseSchema = new Schema(schema);
32+
}
33+
// Parse options
34+
const ascending = (opts === null || opts === void 0 ? void 0 : opts.descending) !== true;
35+
const timestamp = (opts === null || opts === void 0 ? void 0 : opts.timestamp) || false;
36+
const offline = (opts === null || opts === void 0 ? void 0 : opts.offline) || false;
37+
const storagePath = (opts === null || opts === void 0 ? void 0 : opts.storagePath) || '.deta-base-orm';
38+
this._opts = { ascending, timestamp, offline, storagePath };
39+
if (this._opts.offline) {
40+
this._db = new OfflineDB(this._opts.storagePath, this._baseName);
41+
return;
42+
}
43+
// Reuse Deta Base
44+
const db = opts === null || opts === void 0 ? void 0 : opts.db;
45+
if (db !== undefined) {
46+
this._db = db;
47+
return;
48+
}
49+
// Create new Deta Base instance
50+
const deta = Deta();
51+
this._db = deta.Base(name);
52+
}
53+
/**
54+
* Create a new document with the provided data based on the Base schema
55+
* @param {SchemaType} data Object representing the data of the new document
56+
* @returns {BaseDocument} Document
57+
*/
58+
create(rawData) {
59+
// Set configs
60+
Document._baseName = this._baseName;
61+
Document._db = this._db;
62+
Document._opts = this._opts;
63+
// Auto generate a key if is missing
64+
if (!rawData.key) {
65+
rawData.key = generateKey(this._opts.ascending);
66+
}
67+
// Add timestamp to document
68+
if (this._opts.timestamp && rawData.createdAt === undefined) {
69+
rawData.createdAt = Date.now();
70+
}
71+
const validated = this._baseSchema.validate(rawData);
72+
// Log all errors and throw first one
73+
if (validated.errors && validated.errors.length > 0) {
74+
validated.errors.forEach((err) => console.error('Validation error: ' + err));
75+
throw new Error(validated.errors[0]);
76+
}
77+
const data = validated.result;
78+
// Create new document with data
79+
return new Document(data, this._baseSchema);
80+
}
81+
/**
82+
* Helper function to create and immediately save a new document
83+
* @param {SchemaType} data Object representing the data of the new document
84+
* @returns {BaseDocument} Document
85+
*/
86+
save(data) {
87+
return __awaiter(this, void 0, void 0, function* () {
88+
const doc = this.create(data);
89+
yield doc.save();
90+
return doc;
91+
});
92+
}
93+
_parseQuery(queryObj) {
94+
const queries = Object.entries(queryObj);
95+
const result = {};
96+
queries.forEach(([key, query]) => {
97+
if (typeof query !== 'object' || query === null) {
98+
return result[key] = query;
99+
}
100+
const properties = Object.entries(query);
101+
properties.forEach(([operator, value]) => {
102+
if (!operator.startsWith('$'))
103+
return;
104+
if (operator === '$con') {
105+
result[`${key}?contains`] = value;
106+
}
107+
else if (operator === '$ncon') {
108+
result[`${key}?!contains`] = value;
109+
}
110+
else if (operator === '$rg') {
111+
result[`${key}?r`] = value;
112+
}
113+
else if (operator === '$eq') {
114+
result[key] = value;
115+
}
116+
else {
117+
result[`${key}?${operator.slice(1)}`] = value;
118+
}
119+
});
120+
});
121+
return result;
122+
}
123+
/**
124+
* Wrapper around the Deta Base SDK fetch method
125+
*
126+
* Automatically gets all items until the limit or since the last item
127+
* @internal
128+
*/
129+
_fetch(query = {}, limit, last) {
130+
return __awaiter(this, void 0, void 0, function* () {
131+
const queries = Array.isArray(query) ? query : [query];
132+
const parsedQuery = queries.map(this._parseQuery);
133+
let res = yield this._db.fetch(parsedQuery, limit ? { limit, last } : undefined);
134+
let items = res.items;
135+
// We already have enough data
136+
if (limit && items.length === limit)
137+
return { items, last: res.last };
138+
// More data available
139+
while (res.last) {
140+
res = yield this._db.fetch(parsedQuery, Object.assign(Object.assign({}, (limit) && { limit: limit - items.length }), {
141+
// Since the last item
142+
last: res.last }));
143+
items = items.concat(items);
144+
}
145+
// We have everything
146+
return { items };
147+
});
148+
}
149+
/**
150+
* Find all documents matching the query.
151+
*
152+
* Use limit and last to paginate the result.
153+
*
154+
* @param query A query object or array of query objects
155+
* @param limit Maximum number of items to return
156+
* @param last The key of the last item to start from
157+
* @returns Array of Documents
158+
*/
159+
find(query = {}, limit, last) {
160+
return __awaiter(this, void 0, void 0, function* () {
161+
const res = yield this._fetch(query, limit, last);
162+
if (!res.items)
163+
return [];
164+
const result = res.items.map((item) => {
165+
return this.create(item);
166+
});
167+
return result;
168+
});
169+
}
170+
/**
171+
* Find a single document matching the query.
172+
*
173+
* @param query A query object
174+
* @returns Document
175+
*/
176+
findOne(query = {}) {
177+
return __awaiter(this, void 0, void 0, function* () {
178+
const res = yield this._fetch(query, 1);
179+
if (res.items.length < 1)
180+
return undefined;
181+
return this.create(res.items[0]);
182+
});
183+
}
184+
/**
185+
* Find a single document by its key
186+
*
187+
* @param key The key of the document
188+
* @returns Document
189+
*/
190+
findByKey(key) {
191+
return __awaiter(this, void 0, void 0, function* () {
192+
const res = yield this._db.get(key);
193+
if (!res)
194+
return undefined;
195+
return this.create(res);
196+
});
197+
}
198+
/**
199+
* Find a single document matching the query and update it with the provided data.
200+
*
201+
* @param query A query object
202+
* @param data The data to update
203+
* @returns Document
204+
*/
205+
findOneAndUpdate(query = {}, data) {
206+
return __awaiter(this, void 0, void 0, function* () {
207+
const item = yield this.findOne(query);
208+
if (item === undefined)
209+
throw new Error('No item with that key exists');
210+
// Prevent accidently changing immutable attributes
211+
const newItem = Object.assign(Object.assign({}, data), { key: undefined });
212+
yield this._db.update(newItem, item.key);
213+
return item;
214+
});
215+
}
216+
/**
217+
* Find a single document by its key and update it with the provided data.
218+
*
219+
* @param key The key of the document
220+
* @param data The data to update
221+
* @returns Document
222+
*/
223+
findByKeyAndUpdate(key, data) {
224+
return __awaiter(this, void 0, void 0, function* () {
225+
const item = yield this.findByKey(key);
226+
if (!item)
227+
throw new Error('No item with that key exists');
228+
// Prevent accidently changing immutable attributes
229+
const newItem = Object.assign(Object.assign({}, data), { key: undefined });
230+
yield this._db.update(newItem, item.key);
231+
return item;
232+
});
233+
}
234+
/**
235+
* Find a single document by its key and delete it.
236+
*
237+
* @param key The key of the document
238+
*/
239+
findByKeyAndDelete(key) {
240+
return __awaiter(this, void 0, void 0, function* () {
241+
const item = yield this.findByKey(key);
242+
if (!item)
243+
throw new Error('No item with that key exists');
244+
yield this._db.delete(item.key);
245+
});
246+
}
247+
/**
248+
* Find a single document matching the query and delete it.
249+
*
250+
* @param query A query object
251+
*/
252+
findOneAndDelete(query = {}) {
253+
return __awaiter(this, void 0, void 0, function* () {
254+
const item = yield this.findOne(query);
255+
if (!item)
256+
throw new Error('No item with that key exists');
257+
yield this._db.delete(item.key);
258+
});
259+
}
260+
}

β€Žlib/Document.d.tsβ€Ž

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
import DetaBase from 'deta/dist/types/base';
2+
import { OfflineDB } from './Offline';
3+
import { ParsedOptions } from './types';
4+
import { Schema } from './Schema';
5+
/**
6+
* Represents a Document with all of its data and methods
7+
* @internal
8+
*/
9+
export declare class Document<SchemaType> {
10+
[k: string]: any;
11+
static _baseName: string;
12+
static _db: DetaBase | OfflineDB;
13+
static _opts: ParsedOptions;
14+
_baseSchema?: Schema<SchemaType>;
15+
/**
16+
* Create a new Document instance with the provided data.
17+
*
18+
* Will auto generate a key if it is missing.
19+
* @internal
20+
*/
21+
constructor(data: SchemaType, _baseSchema: Schema<SchemaType>);
22+
/**
23+
* Update the document with the provided data
24+
*
25+
* @param data The data to update
26+
*/
27+
update(data: any): any;
28+
/**
29+
* Delete the document
30+
*/
31+
delete(): Promise<void>;
32+
/**
33+
* Populate a sub-document
34+
*/
35+
populate(path: string): Promise<any>;
36+
/**
37+
* Save the Document to the database
38+
*
39+
* @returns Document
40+
*/
41+
save(): Promise<this>;
42+
}

0 commit comments

Comments
Β (0)