Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion .babelrc
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
{
"presets": [
"@babel/preset-typescript",
[
"@babel/preset-env",
{
Expand All @@ -8,5 +9,9 @@
}
]
],
"plugins": ["babel-plugin-transform-async-to-promises"]
"env": {
"build": {
"plugins": ["babel-plugin-transform-async-to-promises"]
}
}
}
33 changes: 0 additions & 33 deletions .circleci/config.yml

This file was deleted.

34 changes: 34 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
name: CI

on:
pull_request:
push:
branches:
- main
- master
workflow_dispatch:

jobs:
test:
name: Test (Node ${{ matrix.node-version }})
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
node-version: [18, 20]

steps:
- name: Checkout
uses: actions/checkout@v4

- name: Setup Node
uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node-version }}
cache: yarn

- name: Install Dependencies
run: yarn install --frozen-lockfile

- name: Run Tests
run: yarn test
1 change: 0 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
# @clocklimited/schemata - Define, create, and validate objects from a simple schema.

[![NPM Details](https://nodei.co/npm/@clocklimited/schemata.png?stars&downloads)](https://npmjs.org/package/@clocklimited/schemata)
[![CircleCI](https://circleci.com/gh/clocklimited/schemata/tree/master.svg?style=svg)](https://circleci.com/gh/clocklimited/schemata/tree/master)
[![NPM version](https://img.shields.io/npm/v/@clocklimited/schemata.svg)](https://www.npmjs.com/package/@clocklimited/schemata)

schemata allows you to define schemas to ensure your objects are well formed.
Expand Down
8 changes: 0 additions & 8 deletions lib/array.js

This file was deleted.

16 changes: 0 additions & 16 deletions lib/is-array.js

This file was deleted.

20 changes: 14 additions & 6 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,12 @@
"@babel/cli": "^7.17.10",
"@babel/core": "^7.18.2",
"@babel/preset-env": "^7.18.2",
"@babel/preset-typescript": "^7.17.12",
"@babel/register": "^7.17.7",
"@babel/runtime": "^7.18.3",
"@types/lodash.clonedeep": "^4.5.7",
"@types/mocha": "^10.0.10",
"@types/node": "^18.0.0",
"async": "^3.2.3",
"babel-plugin-transform-async-to-promises": "^0.8.18",
"core-js": "3.22.8",
Expand All @@ -41,6 +46,7 @@
"nyc": "^15.1.0",
"prettier": "^2.6.2",
"required": "^1.0.0",
"typescript": "^4.7.4",
"validity": "^1.1.1",
"validity-length": "^2.0.0",
"validity-required": "^1.0.0"
Expand All @@ -57,12 +63,14 @@
],
"scripts": {
"lint:js": "eslint . -f unix --cache --ext '.js'",
"prettier": "prettier --write **/*.js",
"test": "yarn test:code && yarn lint:js",
"test:code": "nyc mocha",
"run-test": "mocha",
"test:watch": "mocha --watch",
"build": "rm -rf dist && mkdir dist && babel schemata.js -d dist && babel lib -d dist/lib",
"prettier": "prettier --write **/*.{js,ts}",
"test": "yarn test:types && yarn test:code && yarn lint:js",
"run-test": "NODE_OPTIONS='--no-experimental-strip-types' mocha --require ./test/babel-register.js --extension ts",
"test:code": "NODE_OPTIONS='--no-experimental-strip-types' nyc mocha --require ./test/babel-register.js --extension ts",
"test:watch": "NODE_OPTIONS='--no-experimental-strip-types' mocha --watch --require ./test/babel-register.js --extension ts",
"test:types": "tsc --noEmit",
"tsc": "tsc --noEmit",
"build": "rm -rf dist && mkdir dist && BABEL_ENV=build babel --extensions \".ts,.js\" src/schemata.ts -d dist && BABEL_ENV=build babel --extensions \".ts,.js\" src/lib -d dist/lib",
"prepublish": "yarn test && yarn build"
},
"engines": {
Expand Down
6 changes: 6 additions & 0 deletions src/declarations.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
declare module 'is-primitive'
declare module 'util.promisify'
declare module 'validity'
declare module 'validity-required'
declare module 'validity-length'
declare module 'async'
21 changes: 21 additions & 0 deletions src/lib/array.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { ISchemata, Properties, SchemataFactory, SchemataType } from './types'

/*
* Array helper to assist creation of nested array schemas
*/
function ArrayType<Q extends Properties, T extends SchemataType<Q>>(
schema: ISchemata<Q, T> | SchemataFactory<Q, T>
) {
const schemata = 'makeDefault' in schema ? schema : schema()

const fn = function (): T[] {
return []
}

fn.arraySchema = schemata

return fn
}

export default ArrayType
module.exports = ArrayType
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
const convertCamelcaseToHuman = (value, delimiter = ' ') =>
const convertCamelcaseToHuman = (value: string, delimiter: string = ' ') =>
value.substring(0, 1).toUpperCase() +
value
.substring(1)
.replace(/([^\s0-9])([A-Z0-9])/g, ($0, $1, $2) => $1 + delimiter + $2)

export default convertCamelcaseToHuman
module.exports = convertCamelcaseToHuman
3 changes: 2 additions & 1 deletion lib/casters/array.js → src/lib/casters/array.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
function castArray(value) {
function castArray(value: any): any[] {
return value === '' || value === null || value === undefined
? []
: Array.isArray(value)
? value
: [value]
}

export default castArray
module.exports = castArray
3 changes: 2 additions & 1 deletion lib/casters/boolean.js → src/lib/casters/boolean.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
function castBoolean(value) {
function castBoolean(value: any): boolean {
if (value === undefined || value === '' || value === null) return null
const falsey = [false, 0, '0', 'false', 'off', 'no']
return falsey.indexOf(value) === -1
}

export default castBoolean
module.exports = castBoolean
3 changes: 2 additions & 1 deletion lib/casters/date.js → src/lib/casters/date.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
function castDate(value) {
function castDate(value: any): Date {
return value === '' || value === null || value === undefined
? null
: value instanceof Date
? value
: new Date(value)
}

export default castDate
module.exports = castDate
3 changes: 2 additions & 1 deletion lib/casters/number.js → src/lib/casters/number.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
function castNumber(value) {
function castNumber(value: any): number {
if (value === undefined || value === '' || value === null) return null
return Number(value)
}

export default castNumber
module.exports = castNumber
3 changes: 2 additions & 1 deletion lib/casters/object.js → src/lib/casters/object.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
function castObject(value) {
function castObject(value: any): any {
// typeof null === 'object', but null is an acceptable value
return typeof value !== 'object' ? {} : value
}

export default castObject
module.exports = castObject
3 changes: 2 additions & 1 deletion lib/casters/string.js → src/lib/casters/string.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
function castString(value) {
function castString(value: any): String {
if (value === undefined || value === '' || value === null) return null
return value.toString && value.toString()
}

export default castString
module.exports = castString
7 changes: 5 additions & 2 deletions lib/has-tag.js → src/lib/has-tag.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,18 @@
import { Properties } from './types'

/**
* Has a property got a tag
*/
const hasTag = (schema, key, tag) => {
const hasTag = (schema: Properties, key: string, tag?: string): boolean => {
// No tag specified
if (tag === undefined) return true

// This property has no tags
if (schema[key].tag === undefined) return false
if (!Array.isArray(schema[key].tag)) return false

return schema[key].tag.indexOf(tag) !== -1
return (schema[key].tag as string[]).indexOf(tag) !== -1
}

export default hasTag
module.exports = hasTag
15 changes: 15 additions & 0 deletions src/lib/is-array.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import isSchemata from './is-schemata'

/*
* Take an object and determine whether it is a schemata array type.
*/
const isArray = (obj: any): boolean => {
// Schemata array types must be objects
if (typeof obj !== 'function') return false

// Array types look like { arraySchema: <schemata> }
return isSchemata(obj.arraySchema)
}

export default isArray
module.exports = isArray
7 changes: 4 additions & 3 deletions lib/is-schemata.js → src/lib/is-schemata.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
const expectedInterface = ['makeBlank', 'makeDefault', 'getName', 'validate']
const isSchemata = (obj) => {
// Schemata instances must be objects
if (typeof obj !== 'object') return false
const isSchemata = (obj: any): boolean => {
// Schemata instances must be functions
if (typeof obj !== 'function') return false
return expectedInterface.every((func) => obj[func] !== undefined)
}

export default isSchemata
module.exports = isSchemata
29 changes: 18 additions & 11 deletions lib/property-caster.js → src/lib/property-caster.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
const isSchemata = require('./is-schemata')
const isSchemataArray = require('./is-array')
const getType = require('./type-getter')
const castArray = require('./casters/array')
const castBoolean = require('./casters/boolean')
const castDate = require('./casters/date')
const castNumber = require('./casters/number')
const castObject = require('./casters/object')
const castString = require('./casters/string')
import isSchemata from './is-schemata'
import isSchemataArray from './is-array'
import getType from './type-getter'
import castArray from './casters/array'
import castBoolean from './casters/boolean'
import castDate from './casters/date'
import castNumber from './casters/number'
import castObject from './casters/object'
import castString from './casters/string'

/**
* Casts a value to a given type.
*
Expand All @@ -17,7 +18,12 @@ const castString = require('./casters/string')
* Throws error if type is undefined.
*
*/
const castProperty = (type, value, key, entityObject) => {
const castProperty = (
type: any,
value?: any,
key?: string,
entityObject?: any
): any => {
if (type === undefined) throw new Error('Missing type')

// First check whether the type of this property is
Expand All @@ -31,7 +37,7 @@ const castProperty = (type, value, key, entityObject) => {
if (isSchemataArray(type)) {
if (!value) return null
if (!Array.isArray(value)) value = [value]
return value.map((v) => type.arraySchema.cast(v))
return value.map((v: any) => type.arraySchema.cast(v))
}

// If the { type: x } is a primitive constructor, use
Expand All @@ -54,4 +60,5 @@ const castProperty = (type, value, key, entityObject) => {
}
}

export default castProperty
module.exports = castProperty
5 changes: 3 additions & 2 deletions lib/type-getter.js → src/lib/type-getter.ts
Original file line number Diff line number Diff line change
@@ -1,17 +1,18 @@
const isSchemata = require('./is-schemata')
import isSchemata from './is-schemata'

/*
* Gets the type of a property. It either returns
* a non-function (string, schemata instance) or it
* calls a function. If that function returns a schemata
* instance, that is returned. Else we return the type as is.
*/
const getType = (type, entityObject) => {
const getType = (type: any, entityObject?: any): any => {
if (typeof type !== 'function') return type

const schemataInstance = type(entityObject)

return isSchemata(schemataInstance) ? schemataInstance : type
}

export default getType
module.exports = getType
Loading
Loading