Skip to content

Commit

Permalink
Hello, cruel world!
Browse files Browse the repository at this point in the history
  • Loading branch information
kossnocorp committed Jul 11, 2019
0 parents commit e2284a9
Show file tree
Hide file tree
Showing 25 changed files with 6,658 additions and 0 deletions.
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
node_modules
lib
/secrets
4 changes: 4 additions & 0 deletions .prettierrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
"singleQuote": true,
"semi": false
}
3 changes: 3 additions & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"typescript.tsdk": "node_modules/typescript/lib"
}
12 changes: 12 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
# Changelog

All notable changes to this project will be documented in this file.
This project adheres to [Semantic Versioning].
This change log follows the format documented in [Keep a CHANGELOG].

[semantic versioning]: http://semver.org/
[keep a changelog]: http://keepachangelog.com/

## 0.1.0 - 2019-07-08

Initial version.
22 changes: 22 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
.DEFAULT_GOAL := build
.PHONY: build

SHELL := /bin/bash
PATH := $(shell yarn bin):$(PATH)

test:
jest
.PHONY: test

test-watch:
jest --watch

build:
@rm -rf lib
@tsc
@prettier "lib/**/*.[jt]s" --write --loglevel silent
@cp {package.json,*.md} lib
@rsync --archive --prune-empty-dirs --exclude '*.ts' --relative src/./ lib

publish: build
cd lib && npm publish --access public
77 changes: 77 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
# Storytype

TypeScript-first ORM for Firestore.

## Installation

TODO

## Get started

### Add data

```ts
import store from 'storetype'

type User = { name: string }
const users = collection<User>('users')
store.add(users, { name: 'Sasha' })
// await addDoc(users, { name: 'Sasha' })
```

### Read data

```ts
import { collection, getDoc } from 'storetype'
type User = { name: string }
const users = collection<User>('users')
await addDoc(users, { name: 'Sasha' })
```

### API

## Usage

### Query data from collections

```ts
import { collection, ref, getRef } from 'storetype'

// 1. Define your models using interfaces and types

interface User {
name: string
}

interface Order {
username: string
order: string
time: number
}

// 2. Define collections

const schema = {
users: collection<User>('users'),
orders: collection<Order>('orders')
}

// 3. Add data

getRef(ref(schema.users, 'kossnocorp'))

const newUsername = 'newuser1234'

setRef(ref(schema.users, newUsername), { name: 'New User 1234' })

pushItem(schema.orders, { name: 'Sasha' })

queryItems(schema.orders, ({ where, order }) => [
where('username', 'newuser1234'),
order('time', 'desc')
])
```

## License

[MIT © Sasha Koss](https://kossnocorp.mit-license.org/)
6 changes: 6 additions & 0 deletions babel.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
module.exports = {
presets: [
['@babel/preset-env', { targets: { node: 'current' } }],
'@babel/preset-typescript'
]
}
4 changes: 4 additions & 0 deletions jest.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
module.exports = {
roots: ['<rootDir>/src/'],
setupFiles: ['<rootDir>/test/setupJest.ts']
}
25 changes: 25 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
{
"name": "storetype",
"version": "0.1.0",
"description": "Firestore ORM, definitely typed",
"main": "index.js",
"repository": "https://github.com/kossnocorp/storetype",
"author": "Sasha Koss <[email protected]>",
"license": "MIT",
"devDependencies": {
"@babel/core": "^7.4.5",
"@babel/preset-env": "^7.4.5",
"@babel/preset-typescript": "^7.3.3",
"@types/jest": "^24.0.13",
"@types/node": "^12.0.4",
"jest": "^24.8.0",
"nanoid": "^2.0.3",
"prettier": "^1.17.1",
"typescript": "^3.6.0-dev.20190709"
},
"dependencies": {
"@google-cloud/firestore": "^2.2.3",
"firebase": "^6.2.4",
"firebase-admin": "^8.2.0"
}
}
19 changes: 19 additions & 0 deletions src/adaptor/browser.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
/**
* Browser Firestore adaptor.
*/

import firebase from 'firebase/app'
import 'firebase/firestore'

const store = firebase.firestore()
export default store

export type FirestoreQuery = firebase.firestore.Query
export type FirestoreDocumentReference = firebase.firestore.DocumentReference
export const FirestoreDocumentReference = firebase.firestore.DocumentReference
export type FirestoreCollectionReference = firebase.firestore.CollectionReference
export type FirestoreOrderByDirection = firebase.firestore.OrderByDirection
export type FirestoreWhereFilterOp = firebase.firestore.WhereFilterOp
export type FirestoreTransaction = firebase.firestore.Transaction
export const FirestoreFieldValue = firebase.firestore.FieldValue
export type FirebaseWriteBatch = firebase.firestore.WriteBatch
20 changes: 20 additions & 0 deletions src/adaptor/node.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
/**
* Node.js Firestore adaptor.
*/

import * as firestore from '@google-cloud/firestore'
import * as admin from 'firebase-admin'

const store = admin.firestore()
export default store

export type FirestoreQuery = admin.firestore.Query
export type FirestoreDocumentReference = admin.firestore.DocumentReference
export const FirestoreDocumentReference = admin.firestore.DocumentReference
export const FirestoreFieldValue = admin.firestore.FieldValue
export type FirebaseWriteBatch = admin.firestore.WriteBatch
export type FirestoreCollectionReference = admin.firestore.CollectionReference
export type FirestoreTransaction = admin.firestore.Transaction
// TODO: Use admin reference after they added to firebase-admin
export type FirestoreOrderByDirection = firestore.OrderByDirection
export type FirestoreWhereFilterOp = firestore.WhereFilterOp
4 changes: 4 additions & 0 deletions src/adaptor/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
"browser": "./browser.ts",
"main": "./node.ts"
}
18 changes: 18 additions & 0 deletions src/collection/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
export interface Collection<_Model, Nested = undefined> {
__type__: 'collection'
path: string
nested?: Nested
}

function collection<Model, Nested = undefined>(
path: string,
nested?: Nested
): Collection<Model, Nested> {
if (nested) {
return { __type__: 'collection', path, nested }
} else {
return { __type__: 'collection', path }
}
}

export { collection }
15 changes: 15 additions & 0 deletions src/collection/test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import assert from 'assert'
import { collection } from '.'

describe('Collection', () => {
describe('collection', () => {
it('creates collection object', () => {
assert.deepEqual(collection<User>('users'), {
__type__: 'collection',
path: 'users'
})
})
})
})

interface User {}
84 changes: 84 additions & 0 deletions src/data/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
import store, {
FirestoreDocumentReference,
FirestoreFieldValue
} from '../adaptor/node'
import { Ref, pathToRef } from '../ref'

export type ValueIncrement = {
__type__: 'value'
operation: 'increment'
number: number
}

export type ValueClear = {
__type__: 'value'
operation: 'clear'
}

export type AnyValue = ValueClear
export type Value<T> = T extends number ? AnyValue | ValueIncrement : AnyValue

export function increment(number: number): ValueIncrement {
return { __type__: 'value', operation: 'increment', number }
}

export function clear(): ValueClear {
return { __type__: 'value', operation: 'clear' }
}

export function unwrapValue(value: any) {
if (value instanceof Document) {
return refToDoc(value.ref)
} else if (value && typeof value === 'object') {
if (value.__type__ === 'ref') {
return refToDoc(value)
} else if (value.__type__ === 'value') {
const fieldValue = value as Value<any>
switch (fieldValue.operation) {
case 'clear':
return FirestoreFieldValue.delete()

case 'increment':
return FirestoreFieldValue.increment(fieldValue.number)
}
}

const unwrappedObject: { [key: string]: any } = Object.assign(
isArray(value) ? [] : {},
value
)
Object.keys(unwrappedObject).forEach(key => {
unwrappedObject[key] = unwrapValue(unwrappedObject[key])
})
return unwrappedObject
} else if (value === undefined) {
return null
} else {
return value
}
}

export function wrapData(data: unknown) {
if (data instanceof FirestoreDocumentReference) {
return pathToRef(data.path)
} else if (data && typeof data === 'object') {
const wrappedData: { [key: string]: any } = Object.assign(
isArray(data) ? [] : {},
data
)
Object.keys(wrappedData).forEach(key => {
wrappedData[key] = wrapData(wrappedData[key])
})
return wrappedData
} else {
return data
}
}

export function isArray(object: any) {
return Object.prototype.toString.call(object) === '[object Array]'
}

export function refToDoc<Model>(ref: Ref<Model>) {
return store.doc(ref.path)
}
11 changes: 11 additions & 0 deletions src/doc/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { Ref } from '../ref'

export interface Doc<Model> {
__type__: 'doc'
data: Model
ref: Ref<Model>
}

export function doc<Model>(ref: Ref<Model>, data: Model): Doc<Model> {
return { __type__: 'doc', ref, data }
}
24 changes: 24 additions & 0 deletions src/doc/test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import assert from 'assert'
import nanoid from 'nanoid'
import { doc } from '.'
import { collection } from '../collection'
import { ref } from '../ref'

describe('Doc', () => {
const users = collection<User>('users')

describe('doc', () => {
it('creates doc object', () => {
const userRef = ref(users, nanoid())
assert.deepEqual(doc(userRef, { name: 'Sasha' }), {
__type__: 'doc',
ref: userRef,
data: { name: 'Sasha' }
})
})
})
})

interface User {
name: string
}
Loading

0 comments on commit e2284a9

Please sign in to comment.