diff options
36 files changed, 386 insertions, 311 deletions
diff --git a/.prettierrc.json b/.prettierrc.json new file mode 100644 index 0000000..b2095be --- /dev/null +++ b/.prettierrc.json @@ -0,0 +1,4 @@ +{ + "semi": false, + "singleQuote": true +} diff --git a/client/jest.config.js b/client/jest.config.js index 3f878c4..758fa13 100644 --- a/client/jest.config.js +++ b/client/jest.config.js @@ -1,4 +1,4 @@ module.exports = { - preset: "ts-jest", - testEnvironment: "node" -}; + preset: 'ts-jest', + testEnvironment: 'node' +} diff --git a/client/package.json b/client/package.json index c199e46..b7489f0 100644 --- a/client/package.json +++ b/client/package.json @@ -1,6 +1,6 @@ { "name": "naive-client", - "version": "2.2.1", + "version": "2.3.4", "description": "A naive NoSQL database client", "main": "dist/client/src/index.js", "types": "dist/client/src/index.d.ts", @@ -18,5 +18,9 @@ "ts-node": "^7.0.1", "typedoc": "^0.14.2", "typescript": "^3.2.4" + }, + "dependencies": { + "@types/node-fetch": "^2.1.4", + "node-fetch": "^2.3.0" } } diff --git a/client/readme.md b/client/readme.md index 79f4637..92753d4 100644 --- a/client/readme.md +++ b/client/readme.md @@ -1,6 +1,6 @@ # naive -![build status](https://api.travis-ci.org/nvandoorn/naive.svg?branch=master "Build Status") +![build status](https://api.travis-ci.org/nvandoorn/naive.svg?branch=master 'Build Status') [Read the docs](https://nvandoorn.github.io/naive/) diff --git a/client/src/config.model.ts b/client/src/config.model.ts index 9c3b70a..278f7f7 100644 --- a/client/src/config.model.ts +++ b/client/src/config.model.ts @@ -1,5 +1,4 @@ export interface Config { - wsPort: number; - httpPort: number; - url: string; + wsPort: number + httpUrl: string } diff --git a/client/src/database-connection.model.ts b/client/src/database-connection.model.ts index b89014b..0523e22 100644 --- a/client/src/database-connection.model.ts +++ b/client/src/database-connection.model.ts @@ -1,7 +1,7 @@ export interface DatabaseConnection { - init(): Promise<any>; - close(): Promise<any>; - subscribe(path: string, callback: (e: any) => any): Promise<() => any>; - write(path: string, toWrite: Object): Promise<any>; - remove(path: string): Promise<any>; + init(): Promise<any> + close(): Promise<any> + subscribe(path: string, callback: (e: any) => any): Promise<() => any> + write(path: string, toWrite: Object): Promise<any> + remove(path: string): Promise<any> } diff --git a/client/src/database-connection.test.ts b/client/src/database-connection.test.ts new file mode 100644 index 0000000..e6353e8 --- /dev/null +++ b/client/src/database-connection.test.ts @@ -0,0 +1,3 @@ +describe('Client module', () => { + test('it should work', () => {}) +}) diff --git a/client/src/database-connection.ts b/client/src/database-connection.ts index d870f71..3cf733c 100644 --- a/client/src/database-connection.ts +++ b/client/src/database-connection.ts @@ -1,5 +1,7 @@ -import { Config } from "./config.model"; -import { DatabaseConnection } from "./database-connection.model"; +import { Config } from './config.model' +import { DatabaseConnection } from './database-connection.model' + +const IS_NODE = true /** * Client side implementation is implemented using @@ -7,25 +9,34 @@ import { DatabaseConnection } from "./database-connection.model"; * size */ export const dbFactory = (config: Config): DatabaseConnection => { + // TODO figure out how to patch + // in native fetch instead + // when this is running in the browser + const send = require('node-fetch') const write = (path: string, toWrite: Object | null) => { - const { url, httpPort } = config; - return fetch(`${url}:${httpPort}/write`, { - method: "POST", - body: JSON.stringify({ - path, - toWrite - }) - }); - }; + const { httpUrl } = config + const body = JSON.stringify({ + path, + toWrite + }) + return send(`${httpUrl}/write`, { + method: 'POST', + body, + headers: { + Accept: 'application/json', + 'Content-Type': 'application/json' + } + }) + } return { async init() {}, async close() {}, async subscribe(path: string, callback: (e: any) => Promise<any>) { - return () => {}; + return () => {} }, write, async remove(path: string) { - write(path, null); + write(path, null) } - }; -}; + } +} diff --git a/client/src/index.ts b/client/src/index.ts index c16688d..f11e59c 100644 --- a/client/src/index.ts +++ b/client/src/index.ts @@ -1,3 +1,4 @@ -export { dbFactory } from "./database-connection"; -export { DatabaseConnection } from "./database-connection.model"; -export { Config } from "./config.model"; +export { dbFactory } from './database-connection' +export { DatabaseConnection } from './database-connection.model' +export { Config } from './config.model' +declare var fetch: any diff --git a/client/yarn.lock b/client/yarn.lock index 7bbbb85..97a7de5 100644 --- a/client/yarn.lock +++ b/client/yarn.lock @@ -69,6 +69,13 @@ resolved "https://registry.yarnpkg.com/@types/minimatch/-/minimatch-3.0.3.tgz#3dca0e3f33b200fc7d1139c0cd96c1268cadfd9d" integrity sha512-tHq6qdbT9U1IRSGf14CL0pUlULksvY9OZ+5eEgl1N7t+OA3tGvNpxJCzuKQlsNgCVwbAs670L1vcVQi8j9HjnA== +"@types/node-fetch@^2.1.4": + version "2.1.4" + resolved "https://registry.yarnpkg.com/@types/node-fetch/-/node-fetch-2.1.4.tgz#093d1beae11541aef25999d70aa09286fd025b1a" + integrity sha512-tR1ekaXUGpmzOcDXWU9BW73YfA2/VW1DF1FH+wlJ82BbCSnWTbdX+JkqWQXWKIGsFPnPsYadbXfNgz28g+ccWg== + dependencies: + "@types/node" "*" + "@types/node@*", "@types/node@^10.12.18": version "10.12.18" resolved "https://registry.yarnpkg.com/@types/node/-/node-10.12.18.tgz#1d3ca764718915584fcd9f6344621b7672665c67" @@ -2475,6 +2482,11 @@ needle@^2.2.1: iconv-lite "^0.4.4" sax "^1.2.4" +node-fetch@^2.3.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.3.0.tgz#1a1d940bbfb916a1d3e0219f037e89e71f8c5fa5" + integrity sha512-MOd8pV3fxENbryESLgVIeaGKrdl+uaYhCSSVkjeOb/31/njTpcis5aWfdqgNlHIrKOLRbMnfPINPOML2CIFeXA== + node-int64@^0.4.0: version "0.4.0" resolved "https://registry.yarnpkg.com/node-int64/-/node-int64-0.4.0.tgz#87a9065cdb355d3182d8f94ce11188b825c68a3b" diff --git a/core/jest.config.js b/core/jest.config.js index 3f878c4..758fa13 100644 --- a/core/jest.config.js +++ b/core/jest.config.js @@ -1,4 +1,4 @@ module.exports = { - preset: "ts-jest", - testEnvironment: "node" -}; + preset: 'ts-jest', + testEnvironment: 'node' +} diff --git a/core/package.json b/core/package.json index c5c40f7..8d9f6ee 100644 --- a/core/package.json +++ b/core/package.json @@ -1,6 +1,6 @@ { "name": "naive-core", - "version": "2.2.1", + "version": "2.3.4", "description": "A naive NoSQL database", "main": "dist/core/src/index.js", "types": "dist/core/src/index.d.ts", diff --git a/core/readme.md b/core/readme.md index 79f4637..92753d4 100644 --- a/core/readme.md +++ b/core/readme.md @@ -1,6 +1,6 @@ # naive -![build status](https://api.travis-ci.org/nvandoorn/naive.svg?branch=master "Build Status") +![build status](https://api.travis-ci.org/nvandoorn/naive.svg?branch=master 'Build Status') [Read the docs](https://nvandoorn.github.io/naive/) diff --git a/core/src/change-handlers.model.ts b/core/src/change-handlers.model.ts index 0bddecf..0ef499a 100644 --- a/core/src/change-handlers.model.ts +++ b/core/src/change-handlers.model.ts @@ -1,6 +1,6 @@ export interface ChangeHandlers { [handlerKey: string]: { - path: string; - callback: (e: any) => Promise<any>; - }; + path: string + callback: (e: any) => Promise<any> + } } diff --git a/core/src/context.model.ts b/core/src/context.model.ts index 193f152..3551fc8 100644 --- a/core/src/context.model.ts +++ b/core/src/context.model.ts @@ -6,13 +6,13 @@ export interface Context { * but it may be handy to pipe it elsewhere * later */ - logger: (e: any) => void; + logger: (e: any) => void /** * Location of the database cache file (JSON file) */ - cachePath: string; + cachePath: string /** * Maximum database size in megabytes */ - maxDbSizeMB: number; + maxDbSizeMB: number } diff --git a/core/src/database.model.ts b/core/src/database.model.ts index a0cd3a5..0ceed6e 100644 --- a/core/src/database.model.ts +++ b/core/src/database.model.ts @@ -7,7 +7,7 @@ export interface DatabaseInterface { * but is required for any form * of non-volatile storage */ - init(): Promise<void>; + init(): Promise<void> /** * Read data from the "node" described by path @@ -16,12 +16,12 @@ export interface DatabaseInterface { * null is returned if there is no data * at path */ - read(path: string): Promise<Object>; + read(path: string): Promise<Object> /** * Subscribe to all data changes at path */ - subscribe(path: string, callback: (e: any) => any): () => any; + subscribe(path: string, callback: (e: any) => any): () => any /** * Write data to the node described by path @@ -32,20 +32,20 @@ export interface DatabaseInterface { * * An exception is thrown if the write fails */ - write(path: string, toWrite: Object): Promise<void>; + write(path: string, toWrite: Object): Promise<void> /** * Remove data at path */ - remove(path: string): Promise<void>; + remove(path: string): Promise<void> /** * Empty the database */ - flush(): Promise<void>; + flush(): Promise<void> /** * String representation (JSON) of the database */ - toString(): Object; + toString(): Object } diff --git a/core/src/database.test.ts b/core/src/database.test.ts index 7f81308..7b2be8d 100644 --- a/core/src/database.test.ts +++ b/core/src/database.test.ts @@ -1,130 +1,131 @@ -import { Database } from "./database"; -import { NaiveErrorCode } from "../../lib/error.model"; +import { Database } from './database' +import { NaiveErrorCode } from '../../lib/error.model' -const generateString = (sizeBytes: number) => - Array(Math.ceil(sizeBytes)).fill("a"); +const generateString = (sizeBytes: number): string => + Array(Math.ceil(sizeBytes)) + .fill('a') + .join('') -const maxDbSizeMB = 0.1; +const maxDbSizeMB = 0.1 -describe("Database module", () => { - let db: Database; +describe('Database module', () => { + let db: Database beforeAll(async () => { db = new Database({ logger: console.log, maxDbSizeMB, cachePath: `${__dirname}/db.json` - }); - await db.init(); - }); + }) + await db.init() + }) beforeEach(async () => { - await db.flush(); - }); - - // Next 2 tests simply look - // for expections on operations that - // should be harmless - test("it should write data", () => { - return db.write("/hello/world", { - formula: "hello world" - }); - }); - - test("it should read data", () => { - return db.read("/hello/world"); - }); - - test("it should have a string representation", () => { - expect(db.toString()).not.toHaveLength(0); - }); - - test("it should write and then read data", async () => { - const path = "/hello/world"; + await db.flush() + }) + + // Next 2 tests run simple operations + // that should _not_ throw exceptions + test('it should write data', () => { + return db.write('/hello/world', { + formula: 'hello world' + }) + }) + + test('it should read data', () => { + return db.read('/hello/world') + }) + + test('it should have a string representation', () => { + expect(db.toString()).not.toHaveLength(0) + }) + + test('it should write and then read data', async () => { + const path = '/hello/world' const toWrite = { - secret: "stuff" - }; - await db.write(path, toWrite); - const s = await db.read(path); - expect(s).toBe(toWrite); - }); - - test("it should write and read data from the root", async () => { + secret: 'stuff' + } + await db.write(path, toWrite) + const s = await db.read(path) + expect(s).toBe(toWrite) + }) + + test('it should write and read data from the root', async () => { const toWrite = { my: { big: { - fun: "data" + fun: 'data' } } - }; - await db.write("/", toWrite); - const s = await db.read("/"); - expect(s).toEqual(toWrite); - }); - - test("it should remove data", async () => { - const path = "/this/is/fun"; + } + await db.write('/', toWrite) + const s = await db.read('/') + expect(s).toEqual(toWrite) + }) + + test('it should remove data', async () => { + const path = '/this/is/fun' await db.write(path, { foxNews: { - stories: ["AOC", "aoc", "aOC", "aOc"] + stories: ['AOC', 'aoc', 'aOC', 'aOc'] } - }); - await db.remove(path); - const s = await db.read(path); - expect(s).toBeNull(); - }); + }) + await db.remove(path) + const s = await db.read(path) + expect(s).toBeNull() + }) - test("it should remove data at path & leave other data", async () => { - const path = "/this/is/fun"; + test('it should remove data at path & leave other data', async () => { + const path = '/this/is/fun' const buzzFeed = { - stories: ["hack your dogs brain in 4 easy steps"] - }; + stories: ['hack your dogs brain in 4 easy steps'] + } await db.write(path, { foxNews: { - stories: ["AOC", "aoc", "aOC", "aOc"] + stories: ['AOC', 'aoc', 'aOC', 'aOc'] }, buzzFeed - }); - const appendedPath = `${path}/foxNews`; - await db.remove(appendedPath); - const buzzFeedOut = await db.read(`${path}/buzzFeed`); - const foxOut = await db.read(appendedPath); - expect(buzzFeedOut).toEqual(buzzFeed); - expect(foxOut).toBeNull(); - }); - - test("it should be empty after a flush", async () => { - await db.write("/hello/world", { - my: "object" - }); - await db.flush(); - expect(db.toString()).toHaveLength(2); // empty object - }); - - test("it should read null on an empty node", async () => { - const s = await db.read("/any/path/should/work"); - expect(s).toBeNull(); - }); - - test("it should not change on read", async () => { - await db.write("/my/data/lives/here", { + }) + const appendedPath = `${path}/foxNews` + await db.remove(appendedPath) + const buzzFeedOut = await db.read(`${path}/buzzFeed`) + const foxOut = await db.read(appendedPath) + expect(buzzFeedOut).toEqual(buzzFeed) + expect(foxOut).toBeNull() + }) + + test('it should be empty after a flush', async () => { + await db.write('/hello/world', { + my: 'object' + }) + await db.flush() + expect(db.toString()).toHaveLength(2) // empty object + }) + + test('it should read null on an empty node', async () => { + const s = await db.read('/any/path/should/work') + expect(s).toBeNull() + }) + + test('it should not change on read', async () => { + await db.write('/my/data/lives/here', { whiteHouseDinner: `America's Finest McDouble's` - }); - const before = db.toString(); - const s = await db.read("/no/data/here/silly"); - expect(s).toBeNull(); - expect(before).toEqual(db.toString()); - }); - - test("it should throw an error if max size is exceeded", async () => { + }) + const before = db.toString() + const s = await db.read('/no/data/here/silly') + expect(s).toBeNull() + expect(before).toEqual(db.toString()) + }) + + test('it should throw an error if max size is exceeded', async () => { try { - await db.write("/big/data", { + await db.write('/big/data', { bigObj: generateString(maxDbSizeMB * 1024 ** 2) - }); - throw new Error("Did not throw exception"); + }) + throw new Error('Did not throw exception') } catch (e) { - expect(e.code).toBe(NaiveErrorCode.OUT_OF_SPACE); + expect(e.code).toBe(NaiveErrorCode.OUT_OF_SPACE) } - }); -}); + }) +}) diff --git a/core/src/database.ts b/core/src/database.ts index 3b19f8e..e4083b1 100644 --- a/core/src/database.ts +++ b/core/src/database.ts @@ -1,22 +1,22 @@ -import { writeFile, readFile } from "fs"; -import { promisify } from "util"; +import { writeFile, readFile } from 'fs' +import { promisify } from 'util' -import { DatabaseInterface } from "./database.model"; -import { Context } from "./context.model"; -import { NaiveError, NaiveErrorCode as e } from "../../lib/error.model"; -import { ChangeHandlers } from "./change-handlers.model"; +import { DatabaseInterface } from './database.model' +import { Context } from './context.model' +import { NaiveError, NaiveErrorCode as e } from '../../lib/error.model' +import { ChangeHandlers } from './change-handlers.model' -import { last, getKey } from "../../lib/util"; +import { last, getKey } from '../../lib/util' /** * Split path using "/" as a delimiter */ -const splitPath = (path: string): string[] => path.split("/").filter(k => k); +const splitPath = (path: string): string[] => path.split('/').filter(k => k) /** * Identify if a path is a root node */ -const isRootNode = (path: string): boolean => path === "/" || path === ""; +const isRootNode = (path: string): boolean => path === '/' || path === '' /** * Check if path1 matches path2, @@ -26,19 +26,19 @@ const isRootNode = (path: string): boolean => path === "/" || path === ""; */ const isChildOrMatch = (child: string, parent: string) => { // console.log(`child: ${child}, parent ${parent}`); - if (child === parent || parent === "/") return true; - const parentTokens = parent.split("/").filter((i: string) => i.length); - return parentTokens.every((t, i) => child.split("/")[i] === t); -}; + if (child === parent || parent === '/') return true + const parentTokens = parent.split('/').filter((i: string) => i.length) + return parentTokens.every((t, i) => child.split('/')[i] === t) +} -const write = promisify(writeFile); -const read = promisify(readFile); +const write = promisify(writeFile) +const read = promisify(readFile) export const DEFAULT_CTX = { logger: console.log, cachePath: `${__dirname}/db.json`, maxDbSizeMB: 6 -}; +} /** * Implementation of NoSQL DB that uses paths and objects. @@ -54,22 +54,22 @@ export class Database implements DatabaseInterface { /** * In memory buffer to read/write data */ - private buff: any = {}; + private buff: any = {} /** * An array of callback functions that are alerted * when the database changes (mostly want to use this * for some type of pubsub functionality on-top) */ - private changeHandlers: ChangeHandlers = {}; + private changeHandlers: ChangeHandlers = {} constructor(private ctx: Context = DEFAULT_CTX) {} async init(): Promise<void> { try { - const buff = await read(this.ctx.cachePath); - this.buff = JSON.parse(buff.toString()); + const buff = await read(this.ctx.cachePath) + this.buff = JSON.parse(buff.toString()) } catch (e) { - this.ctx.logger("Failed to init database, using empty object"); - this.ctx.logger(e); + this.ctx.logger('Failed to init database, using empty object') + this.ctx.logger(e) } } @@ -78,57 +78,57 @@ export class Database implements DatabaseInterface { // (which obviously becomes a bad idea at some point) async read(path: string): Promise<Object> { // root node case - if (isRootNode(path)) return this.buff; - const pathParts = splitPath(path); - return this.resolve(pathParts); + if (isRootNode(path)) return this.buff + const pathParts = splitPath(path) + return this.resolve(pathParts) } async write(path: string, toWrite: any): Promise<void> { if (isRootNode(path)) { - this.buff = toWrite; + this.buff = toWrite } else { - const pathParts = splitPath(path); - const writeTo = this.resolve(pathParts, false, 1); - writeTo[last(pathParts)] = toWrite; + const pathParts = splitPath(path) + const writeTo = this.resolve(pathParts, false, 1) + writeTo[last(pathParts)] = toWrite } - await this.serialize(); + await this.serialize() // alert everyone of our new change - await this.runChangeHandlers(path, toWrite); + await this.runChangeHandlers(path, toWrite) } private async runChangeHandlers(path: string, change: any): Promise<void> { - const handlers = Object.values(this.changeHandlers); + const handlers = Object.values(this.changeHandlers) for (let handler of handlers) { if (isChildOrMatch(path, handler.path)) { - await handler.callback(change); + await handler.callback(change) } } } subscribe(path: string, callback: (e: any) => Promise<any>): () => any { - const key = getKey("subscriber"); + const key = getKey('subscriber') this.changeHandlers[key] = { path, callback - }; - return () => this.unsubscribe(key); + } + return () => this.unsubscribe(key) } private unsubscribe(key: string) { - delete this.changeHandlers[key]; + delete this.changeHandlers[key] } remove(path: string): Promise<void> { - return this.write(path, null); + return this.write(path, null) } async flush(): Promise<void> { - this.buff = {}; - await this.serialize(); + this.buff = {} + await this.serialize() } toString() { - return JSON.stringify(this.buff); + return JSON.stringify(this.buff) } /** @@ -148,41 +148,41 @@ export class Database implements DatabaseInterface { isRead: boolean = true, level: number = 0 ): any { - const [firstPart] = pathParts; - if (isRootNode(firstPart)) return this.buff; - const n = pathParts.length - level; + const [firstPart] = pathParts + if (isRootNode(firstPart)) return this.buff + const n = pathParts.length - level // TODO avoid having to pull // this specific case out if (n === 0) { if (!this.buff[firstPart]) { - this.buff[firstPart] = {}; - return this.buff[firstPart]; + this.buff[firstPart] = {} + return this.buff[firstPart] } } // start at the root of our buffer - let lastNode = this.buff; - let node; + let lastNode = this.buff + let node for (let i = 0; i < n; i++) { - const part: string = pathParts[i]; + const part: string = pathParts[i] // handle null node if (!lastNode[part]) { // if we're reading from the object // we want to stop as soon // as we hit a null node if (isRead) { - return null; + return null } // but if we're writing and the node is missing, // we should make it and continue else { - lastNode[part] = {}; + lastNode[part] = {} } } // traverse to the next node - node = lastNode[part]; - lastNode = node; + node = lastNode[part] + lastNode = node } - return node; + return node } /** @@ -195,12 +195,12 @@ export class Database implements DatabaseInterface { * Throws OUT_OF_SPACE */ private serialize(): Promise<void> { - if (!this.hasSpace()) throw new NaiveError(e.OUT_OF_SPACE); - return write(this.ctx.cachePath, this.toString()); + if (!this.hasSpace()) throw new NaiveError(e.OUT_OF_SPACE) + return write(this.ctx.cachePath, this.toString()) } private hasSpace(): boolean { // convert from MB to B - return this.toString().length <= this.ctx.maxDbSizeMB * 1024 ** 2; + return this.toString().length <= this.ctx.maxDbSizeMB * 1024 ** 2 } } diff --git a/core/src/index.ts b/core/src/index.ts index 0874475..ff4b500 100644 --- a/core/src/index.ts +++ b/core/src/index.ts @@ -1,3 +1,3 @@ -export { Database } from "./database"; -export { Context } from "./context.model"; -export { DatabaseInterface } from "./database.model"; +export { Database } from './database' +export { Context } from './context.model' +export { DatabaseInterface } from './database.model' diff --git a/lib/database-change.model.ts b/lib/database-change.model.ts index c3a04f5..8e8db39 100644 --- a/lib/database-change.model.ts +++ b/lib/database-change.model.ts @@ -1,4 +1,4 @@ export interface DatabaseChange { - path: string; - change: Object; + path: string + change: Object } diff --git a/lib/error.model.ts b/lib/error.model.ts index 5cf9916..c79a2b2 100644 --- a/lib/error.model.ts +++ b/lib/error.model.ts @@ -3,20 +3,20 @@ export enum NaiveErrorCode { OUT_OF_SPACE } -const e = NaiveErrorCode; +const e = NaiveErrorCode export const lookupMsg = (c: NaiveErrorCode) => { switch (c) { case e.OUT_OF_SPACE: - return `Out of storage space (too big for in memory buffer)`; + return `Out of storage space (too big for in memory buffer)` case e.UNCAUGHT: default: - return `Uncaught error. Please add message/code in src/error.model.ts`; + return `Uncaught error. Please add message/code in src/error.model.ts` } -}; +} export class NaiveError extends Error { constructor(public code: NaiveErrorCode) { - super(lookupMsg(code)); + super(lookupMsg(code)) } } diff --git a/lib/subscription-req.model.ts b/lib/subscription-req.model.ts index a788767..b6c70fd 100644 --- a/lib/subscription-req.model.ts +++ b/lib/subscription-req.model.ts @@ -1,3 +1,3 @@ export interface SubscriptionRequest { - path: string; + path: string } diff --git a/lib/util.ts b/lib/util.ts index 7e7b965..1235792 100644 --- a/lib/util.ts +++ b/lib/util.ts @@ -1,10 +1,10 @@ /** * Get the last item in t */ -export const last = <T>(t: T[]): T => t[t.length - 1]; +export const last = <T>(t: T[]): T => t[t.length - 1] /** * Generate a random key with a seed */ export const getKey = (seed: string) => - seed + Date.now() + Math.floor(Math.random() * 1000); + seed + Date.now() + Math.floor(Math.random() * 1000) diff --git a/lib/write-req.model.ts b/lib/write-req.model.ts index ff2f361..26e2982 100644 --- a/lib/write-req.model.ts +++ b/lib/write-req.model.ts @@ -1,4 +1,4 @@ export interface WriteRequest { - path: string; - toWrite: Object; + path: string + toWrite: Object } diff --git a/scripts/build.sh b/scripts/build.sh new file mode 100755 index 0000000..7db02bd --- /dev/null +++ b/scripts/build.sh @@ -0,0 +1,10 @@ +#!/usr/bin/env bash + +declare -a arr=("core" "client" "server") + +for i in "${arr[@]}" +do + cd $i + yarn run build + cd .. +done diff --git a/scripts/format.sh b/scripts/format.sh new file mode 100755 index 0000000..5bebb80 --- /dev/null +++ b/scripts/format.sh @@ -0,0 +1,3 @@ +#!/usr/bin/env bash + +npx prettier --write "**/*.ts" diff --git a/scripts/release.sh b/scripts/release.sh index 11e9ac8..d931ec6 100755 --- a/scripts/release.sh +++ b/scripts/release.sh @@ -1,20 +1,45 @@ -sed -i.bak s/__REPLACE_VERSION__/$1/g package.json -yarn test || exit 1 -branchName=$(git symbolic-ref HEAD | sed -e 's,.*/\(.*\),\1,') -echo $branchName -# TODO find a better way to exlcude -# docs from the stash -git stash push -- src || exit 1 -git checkout master -yarn run build || exit 1 -# this is a hack required -# to serve paths that start -# with '_' +#!/usr/bin/env bash + +declare -a arr=("core" "client" "server") + +# TODO re-enable git stuff + +# git stash || exit 1 +# git checkout master + +rm -rf docs +mkdir docs touch docs/.nojekyll -git add docs package.json -git commit -m "Update docs for release $1" -git tag $1 -git push --tags origin master -git checkout $branchName -git stash pop -# sed -i.bak s/$1/__REPLACE_VERSION__/g package.json + +branchName=$(git symbolic-ref HEAD | sed -e 's,.*/\(.*\),\1,') + +for i in "${arr[@]}" +do + echo "*** Building naive-$i ***" + cd $i + # update the version in the package file + yarn run build || exit 1 + mv docs ../docs/$i + cd .. +done + +for i in "${arr[@]}" +do + echo "*** Testing and publishing naive-$i ***" + cd $i + # make sure the tests pass + yarn test || exit 1 + # update the version in the package file + # for some reason yarn fucks + # with the git repo? use npm for now + # I guess + npm version $1 + npm publish --access public || exit 1 + cd .. +done + +# git commit -m "Update for release $1" +# git tag $1 +# git push --tags origin master +# git checkout $branchName +# git stash pop diff --git a/server/bin/index.ts b/server/bin/index.ts index fdf1352..fb276c6 100644 --- a/server/bin/index.ts +++ b/server/bin/index.ts @@ -1,10 +1,10 @@ -import { runServer } from "../src/server"; -import process from "process"; +import { runServer } from '../src/server' +import process from 'process' -const port = +(process.env.PORT || 5000); +const port = +(process.env.PORT || 5000) runServer({ httpPort: port, wsPort: port + 1, logger: console.log -}); +}) diff --git a/server/jest.config.js b/server/jest.config.js index 3f878c4..758fa13 100644 --- a/server/jest.config.js +++ b/server/jest.config.js @@ -1,4 +1,4 @@ module.exports = { - preset: "ts-jest", - testEnvironment: "node" -}; + preset: 'ts-jest', + testEnvironment: 'node' +} diff --git a/server/package.json b/server/package.json index 68c6b62..ad3976e 100644 --- a/server/package.json +++ b/server/package.json @@ -4,7 +4,7 @@ "description": "A naive NoSQL database server", "scripts": { "build": "tsc && typedoc --out docs src", - "test": "jest src", + "test": "jest .!(dist)", "start": "ts-node bin/index.ts" }, "bin": { @@ -22,8 +22,10 @@ "typescript": "^3.2.4" }, "dependencies": { + "@types/body-parser": "^1.17.0", "@types/express": "^4.16.0", "@types/ws": "^6.0.1", + "body-parser": "^1.18.3", "express": "^4.16.4", "ws": "^6.1.2" } diff --git a/server/readme.md b/server/readme.md index 79f4637..92753d4 100644 --- a/server/readme.md +++ b/server/readme.md @@ -1,6 +1,6 @@ # naive -![build status](https://api.travis-ci.org/nvandoorn/naive.svg?branch=master "Build Status") +![build status](https://api.travis-ci.org/nvandoorn/naive.svg?branch=master 'Build Status') [Read the docs](https://nvandoorn.github.io/naive/) diff --git a/server/src/context.model.ts b/server/src/context.model.ts index d8419d4..cedd675 100644 --- a/server/src/context.model.ts +++ b/server/src/context.model.ts @@ -1,5 +1,5 @@ export interface Context { - httpPort: number; - wsPort: number; - logger: (e: any) => any; + httpPort: number + wsPort: number + logger: (e: any) => any } diff --git a/server/src/operations.ts b/server/src/operations.ts index 6802ee7..c0fec8d 100644 --- a/server/src/operations.ts +++ b/server/src/operations.ts @@ -1,56 +1,56 @@ -import { Context } from "./context.model"; -import { SubscriptionRequest } from "../../lib/subscription-req.model"; -import { WriteRequest } from "../../lib/write-req.model"; -import { DatabaseChange } from "../../lib/database-change.model"; +import { Context } from './context.model' +import { SubscriptionRequest } from '../../lib/subscription-req.model' +import { WriteRequest } from '../../lib/write-req.model' +import { DatabaseChange } from '../../lib/database-change.model' -import { DatabaseInterface } from "naive-core"; +import { DatabaseInterface } from 'naive-core' -import express, { RequestHandler } from "express"; -import bodyParser from "body-parser"; +import express, { RequestHandler } from 'express' +import bodyParser from 'body-parser' export const bindOperations = ( ctx: Context, db: DatabaseInterface, send: (e: DatabaseChange) => Promise<any> ) => { - const unsubs: { [key: string]: () => any } = {}; + const unsubs: { [key: string]: () => any } = {} const writeHandler: RequestHandler = async (req, res) => { - const { path, toWrite } = req.body as WriteRequest; - await db.write(path, toWrite); - res.status(200).end(); - }; + const { path, toWrite } = req.body as WriteRequest + await db.write(path, toWrite) + res.status(200).end() + } const addSubHandler: RequestHandler = async (req, res) => { - const { path } = req.body as SubscriptionRequest; + const { path } = req.body as SubscriptionRequest // only allow users to subscribe // to a given path once // TODO return a better error if (unsubs[path]) { - res.status(500).end(); - return; + res.status(500).end() + return } unsubs[path] = db.subscribe(path, async (change: Object) => { await send({ path, change - }); - }); - res.status(200).end(); - }; + }) + }) + res.status(200).end() + } const removeSubHandler: RequestHandler = async (req, res) => { - const { path } = req.body as SubscriptionRequest; - const unsub = unsubs[path]; - if (unsub) unsub(); - }; + const { path } = req.body as SubscriptionRequest + const unsub = unsubs[path] + if (unsub) unsub() + } - const router = express(); - router.use(bodyParser.json()); - router.post("/write", writeHandler); + const router = express() + router.use(bodyParser.json()) + router.post('/write', writeHandler) router - .route("/subscribe") + .route('/subscribe') .post(addSubHandler) - .delete(removeSubHandler); - router.listen(ctx.httpPort, () => ctx.logger("HTTPS server started")); -}; + .delete(removeSubHandler) + router.listen(ctx.httpPort, () => ctx.logger('HTTPS server started')) +} diff --git a/server/src/server.test.ts b/server/src/server.test.ts index 2bd2e45..b4a308f 100644 --- a/server/src/server.test.ts +++ b/server/src/server.test.ts @@ -1,31 +1,31 @@ -import { runServer } from "./server"; -import process from "process"; +import { runServer } from './server' +import process from 'process' // We test the server using the client. // It would be better to import this // from a linked npm package, // but that requires rebuilding each time // so fuck it for now -import { dbFactory, DatabaseConnection } from "../../client/src"; +import { dbFactory, DatabaseConnection } from '../../client/src' -const port = +(process.env.PORT || 5005); -const httpPort = port; -const wsPort = port + 1; +const port = +(process.env.PORT || 5005) +const httpPort = port +const wsPort = port + 1 -describe("Server module", async () => { - let db: DatabaseConnection; +describe('Server module', async () => { + let db: DatabaseConnection beforeAll(async () => { await runServer({ httpPort, wsPort, logger: console.log - }); - db = dbFactory({ wsPort, httpUrl: `http://localhost:${httpPort}` }); - await db.init(); - }); - test("it should work", async () => { - await db.write("/hello/world", { - my: "data" - }); - }); -}); + }) + db = dbFactory({ wsPort, httpUrl: `http://localhost:${httpPort}` }) + await db.init() + }) + test('it should work', async () => { + await db.write('/hello/world', { + my: 'data' + }) + }) +}) diff --git a/server/src/server.ts b/server/src/server.ts index f80bd2b..459bc69 100644 --- a/server/src/server.ts +++ b/server/src/server.ts @@ -1,18 +1,18 @@ -import { Context } from "./context.model"; -import { bindOperations } from "./operations"; -import { DatabaseChange } from "../../lib/database-change.model"; -import WebSocket from "ws"; +import { Context } from './context.model' +import { bindOperations } from './operations' +import { DatabaseChange } from '../../lib/database-change.model' +import WebSocket from 'ws' -import { Database, Context as CoreContext } from "naive-core"; +import { Database, Context as CoreContext } from 'naive-core' export const runServer = async (ctx: Context) => { - const db = new Database(); - await db.init(); - const wss = new WebSocket.Server({ port: ctx.wsPort }); + const db = new Database() + await db.init() + const wss = new WebSocket.Server({ port: ctx.wsPort }) bindOperations(ctx, db, async (dbChange: DatabaseChange) => { for (let client of wss.clients) { - client.send(dbChange); + client.send(dbChange) } - }); -}; + }) +} diff --git a/server/yarn.lock b/server/yarn.lock index 9eb810c..5d36ad8 100644 --- a/server/yarn.lock +++ b/server/yarn.lock @@ -18,7 +18,7 @@ esutils "^2.0.2" js-tokens "^4.0.0" -"@types/body-parser@*": +"@types/body-parser@*", "@types/body-parser@^1.17.0": version "1.17.0" resolved "https://registry.yarnpkg.com/@types/body-parser/-/body-parser-1.17.0.tgz#9f5c9d9bd04bb54be32d5eb9fc0d8c974e6cf58c" integrity sha512-a2+YeUjPkztKJu5aIF2yArYFQQp8d51wZ7DavSHjFuY1mqVgidGyzEQ41JIVNy82fXj8yPgy2vJmfIywgESW6w== @@ -541,7 +541,7 @@ bcrypt-pbkdf@^1.0.0: dependencies: tweetnacl "^0.14.3" -body-parser@1.18.3: +body-parser@1.18.3, body-parser@^1.18.3: version "1.18.3" resolved "https://registry.yarnpkg.com/body-parser/-/body-parser-1.18.3.tgz#5b292198ffdd553b3a0f20ded0592b956955c8b4" integrity sha1-WykhmP/dVTs6DyDe0FkrlWlVyLQ= |