yayson 2.0.10-quickfix → 3.0.0

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/.eslintrc.json ADDED
@@ -0,0 +1,28 @@
1
+ {
2
+ "env": {
3
+ "commonjs": true,
4
+ "es6": true,
5
+ "mocha": true,
6
+ "node": true
7
+ },
8
+ "parser": "@babel/eslint-parser",
9
+ "parserOptions": {
10
+ "ecmaVersion": 2020
11
+ },
12
+ "ignorePatterns": ["node_modules", "dist"],
13
+ "rules": {
14
+ "eqeqeq": ["error", "smart"],
15
+ "semi": ["error", "never", { "beforeStatementContinuationChars": "always" }],
16
+ "no-extra-semi": "off"
17
+ },
18
+ "plugins": ["mocha"],
19
+ "extends": ["eslint:recommended", "plugin:mocha/recommended"],
20
+ "overrides": [
21
+ {
22
+ "files": ["test/**/*.js"],
23
+ "rules": {
24
+ "no-unused-vars": "off"
25
+ }
26
+ }
27
+ ]
28
+ }
@@ -0,0 +1,30 @@
1
+ # This workflow will do a clean install of node dependencies, build the source code and run tests across different versions of node
2
+ # For more information see: https://help.github.com/actions/language-and-framework-guides/using-nodejs-with-github-actions
3
+
4
+ name: Node.js CI
5
+
6
+ on:
7
+ push:
8
+ branches: [ master ]
9
+ pull_request:
10
+ branches: [ master ]
11
+
12
+ jobs:
13
+ build:
14
+
15
+ runs-on: ubuntu-latest
16
+
17
+ strategy:
18
+ matrix:
19
+ node-version: [14.x, 16.x, 18.x]
20
+ # See supported Node.js release schedule at https://nodejs.org/en/about/releases/
21
+
22
+ steps:
23
+ - uses: actions/checkout@v2
24
+ - name: Use Node.js ${{ matrix.node-version }}
25
+ uses: actions/setup-node@v2
26
+ with:
27
+ node-version: ${{ matrix.node-version }}
28
+ - run: npm ci
29
+ - run: npm run build --if-present
30
+ - run: npm test
package/.mocharc.js ADDED
@@ -0,0 +1,15 @@
1
+ 'use strict'
2
+
3
+ module.exports = {
4
+ recursive: true,
5
+ require: ['test/node.js'],
6
+ diff: true,
7
+ extension: ['js'],
8
+ package: './package.json',
9
+ reporter: 'spec',
10
+ slow: 75,
11
+ timeout: 10000,
12
+ ui: 'bdd',
13
+ spec: ['test/yayson/**/*.js'],
14
+ 'watch-files': ['src/**/*.js', 'test/**/*.js'],
15
+ }
package/.nvmrc ADDED
@@ -0,0 +1 @@
1
+ v18.13.0
package/README.md CHANGED
@@ -1,6 +1,8 @@
1
1
  # YAYSON
2
2
 
3
- A library for serializing and reading [JSON API](http://jsonapi.org) data in JavaScript. As of 2.0.0 YAYSON aims to support JSON API version 1.
3
+ A library for serializing and reading [JSON API](http://jsonapi.org) data in JavaScript.
4
+
5
+ From version 3 we now support native JavaScript classes. YAYSON has zero dependencies and works in the browser and in node 14 and up.
4
6
 
5
7
  [![NPM](https://nodei.co/npm/yayson.png?downloads=true)](https://nodei.co/npm/yayson/)
6
8
 
@@ -10,55 +12,45 @@ A library for serializing and reading [JSON API](http://jsonapi.org) data in Jav
10
12
  Install yayson by running:
11
13
 
12
14
  ```
13
- $ npm install yayson --save
14
- ```
15
-
16
- ## Presenting data
17
-
18
- A basic `Presenter` can look like this in Coffeescript:
19
-
20
- ```coffee
21
- {Presenter} = require('yayson')(adapter: 'default')
22
-
23
- class ItemsPresenter extends Presenter
24
- type: 'items'
25
15
 
26
- item =
27
- id: 5
28
- name: 'First'
29
16
 
30
- ItemsPresenter.render(item)
17
+ $ npm i yayson
31
18
  ```
32
19
 
33
- Or in plain JavaScript:
20
+ ## Presenting data
21
+
22
+ A basic `Presenter` can look like this:
34
23
 
35
24
  ```javascript
36
- const Presenter = require('yayson')({
37
- adapter: 'default'
38
- }).Presenter;
25
+ const yayson = require('yayson')
26
+ const { Presenter } = yayson()
39
27
 
40
- class ItemsPresenter extends Presenter {};
41
- ItemsPresenter.prototype.type = 'items';
28
+ class BikePresenter extends Presenter {
29
+ static type = 'bikes'
30
+ }
42
31
 
43
- var item = {
44
- id: 5,
45
- name: 'First'
46
- };
32
+ const bike = {
33
+ id: 5,
34
+ name: 'Monark'
35
+ };
47
36
 
48
- ItemsPresenter.render(item);
37
+ BikePresenter.render(bike);
49
38
  ```
50
39
 
51
40
 
52
41
  This would produce:
53
42
 
54
43
  ```javascript
44
+
45
+
46
+
55
47
  {
56
48
  data: {
57
49
  id: 5,
58
- type: 'items',
50
+ type: 'bikes',
59
51
  attributes: {
60
52
  id: 5,
61
- name: 'First'
53
+ name: 'Monark'
62
54
  }
63
55
  }
64
56
  }
@@ -69,46 +61,28 @@ be an array.
69
61
 
70
62
  A bit more advanced example:
71
63
 
72
- ```coffee
73
- {Presenter} = require('yayson')(adapter: 'default')
74
-
75
- class ItemsPresenter extends Presenter
76
- type: 'items'
77
-
78
- attributes: ->
79
- attrs = super
80
- attrs.start = moment.utc(attrs.start).toDate()
81
- attrs
82
-
83
- relationships: ->
84
- event: EventsPresenter
85
-
86
- ItemsPresenter.render(item)
87
- ```
88
-
89
- In JavaScript this would be done as:
90
64
 
91
65
  ```javascript
92
66
 
93
- var Presenter = require('yayson')().Presenter;
94
-
95
- class ItemsPresenter extends Presenter {};
96
- ItemsPresenter.prototype.type = 'items'
67
+ const yayson = require('yayson')
68
+ const { Presenter } = yayson()
97
69
 
98
- ItemsPresenter.prototype.attributes = function() {
99
- var attrs = Presenter.prototype.attributes.apply(this, arguments);
70
+ class WheelPresenter extends Presenter {
71
+ static type = 'wheels'
100
72
 
101
- attrs.start = moment.utc(attrs.start).toDate();
102
- return attrs;
73
+ relationships() {
74
+ return { bike: BikePresenter }
75
+ }
103
76
  }
104
77
 
105
- ItemsPresenter.prototype.relationships = function() {
106
- return {
107
- event: EventsPresenter
78
+ class BikePresenter extends Presenter {
79
+ static type = 'bikes'
80
+ relationships() {
81
+ return { wheels: WheelPresenter }
108
82
  }
109
83
  }
110
84
 
111
- ItemsPresenter.render(item)
85
+
112
86
  ```
113
87
 
114
88
  ### Sequelize support
@@ -117,28 +91,25 @@ By default it is set up to handle standard JS objects. You can also make
117
91
  it handle Sequelize.js models like this:
118
92
 
119
93
  ```javascript
120
- {Presenter} = require('yayson')({adapter: 'sequelize'})
94
+
95
+ const yayson = require('yayson')
96
+ {Presenter} = yayson({adapter: 'sequelize'})
121
97
 
122
98
  ```
123
99
 
124
100
  You can also define your own adapter globally:
125
101
 
126
102
  ```javascript
127
- {Presenter} = require('yayson')(adapter: {
103
+
104
+
105
+ const yayson = require('yayson')
106
+ {Presenter} = yayson(adapter: {
128
107
  id: function(model){ return 'omg' + model.id},
129
108
  get: function(model, key){ return model[key] }
130
109
  })
131
110
 
132
111
  ```
133
112
 
134
- Or at Presenter level:
135
-
136
- ```javascript
137
- ItemPresenter.adapter = {
138
- id: function(model){ return 'omg' + model.id},
139
- get: function(model, key){ return model[key] }
140
- }
141
- ```
142
113
 
143
114
  Take a look at the SequelizeAdapter if you want to extend YAYSON to your ORM. Pull requests are welcome. :)
144
115
 
@@ -146,13 +117,17 @@ Take a look at the SequelizeAdapter if you want to extend YAYSON to your ORM. Pu
146
117
 
147
118
  You can add metadata to the top level object.
148
119
 
149
- ``` coffee
150
- ItemsPresenter.render(items, meta: count: 10)
120
+ ``` javascript
121
+
122
+
123
+ ItemsPresenter.render(items, {meta: count: 10})
151
124
  ```
152
125
 
153
126
  This would produce:
154
127
 
155
128
  ```javascript
129
+
130
+
156
131
  {
157
132
  meta: {
158
133
  count: 10
@@ -172,16 +147,25 @@ This would produce:
172
147
 
173
148
  You can use a `Store` can like this:
174
149
 
175
- ```coffee
176
- {Store} = require('yayson')()
177
- store = new Store()
150
+ ```javascript
151
+ const {Store} = require('yayson')();
152
+ const store = new Store();
178
153
 
179
- adapter.get(path: '/events/' + id).then (data) ->
180
- event = store.sync(data)
154
+ const data = await adapter.get({path: '/events/' + id});
155
+ const event = store.sync(data);
181
156
  ```
182
157
 
183
158
  This will give you the parsed event with all its relationships.
184
159
 
160
+ Its also possible to find in the synched data:
161
+
162
+
163
+ ```javascript
164
+ const event = this.store.find('events', id)
165
+
166
+ const images = this.store.findAll('images')
167
+ ```
168
+
185
169
 
186
170
  ## Use in the browser
187
171
 
@@ -189,6 +173,7 @@ Recommended way is to use it via [webpack](https://github.com/webpack/webpack) o
189
173
 
190
174
  If you just want to try it out, copy the file `dist/yayson.js` to your project. Then simply include it:
191
175
  ```html
176
+
192
177
  <script src="./lib/yayson.js"></script>
193
178
  ```
194
179
  Then you can `var yayson = window.yayson()` use the `yayson.Presenter` and `yayson.Store` as usual.
@@ -202,5 +187,19 @@ Then you can `var yayson = window.yayson()` use the `yayson.Presenter` and `yays
202
187
  - Safari iOS
203
188
 
204
189
  #### Untested, but should work
205
- - IE 9+
190
+ - IE 11
206
191
  - Android
192
+
193
+
194
+ ## Legacy support
195
+
196
+ Earlier versions of JSON API worked a bit different from 1.0. Therefore YAYSON provides legacy presenters and stores in order to have interoperability between the versions. Its used similar to the standard presenters:
197
+
198
+ ```javascript
199
+
200
+ const yayson = require('yayson/legacy')
201
+ const { Presenter } = yayson()
202
+
203
+ ```
204
+
205
+
package/RELEASE.md CHANGED
@@ -1,9 +1,3 @@
1
1
  # Release process
2
2
 
3
- 1. Compile with: gulp
4
- 2. Bump version number in package.json
5
- 3. git commit -m 'Version 1.0.5'
6
- 4. git tag -a v1.0.5 -m "Version 1.0.5"
7
- 5. git push
8
- 6. git push --tags
9
- 7. npm publish
3
+ Should be just a single command with `npm run release`
@@ -0,0 +1,4 @@
1
+ {
2
+ "presets": [],
3
+ "plugins": []
4
+ }
package/legacy.js ADDED
@@ -0,0 +1,2 @@
1
+ const yayson = require('./src/legacy')
2
+ module.exports = yayson
package/package.json CHANGED
@@ -1,10 +1,19 @@
1
1
  {
2
2
  "name": "yayson",
3
- "version": "2.0.10-quickfix",
3
+ "version": "3.0.0",
4
4
  "description": "A library for serializing and reading JSON API standardized data in JavaScript.",
5
- "main": "lib/yayson.js",
5
+ "main": "src/yayson.js",
6
+ "engines": {
7
+ "node": ">=14",
8
+ "npm": ">=6"
9
+ },
6
10
  "scripts": {
7
- "test": "mocha"
11
+ "test": "mocha",
12
+ "build": "webpack --config webpack.dist.js",
13
+ "test-browser": "webpack-dev-server --config webpack.browser.js",
14
+ "preversion": "npm test && npm run build && git add dist/yayson.js",
15
+ "postversion": "git push --follow-tags origin master",
16
+ "release": "npm version minor"
8
17
  },
9
18
  "repository": {
10
19
  "type": "git",
@@ -28,29 +37,26 @@
28
37
  "url": "https://github.com/confetti/yayson/issues"
29
38
  },
30
39
  "homepage": "https://github.com/confetti/yayson",
40
+ "browserslist": [
41
+ "defaults",
42
+ "not IE 11"
43
+ ],
31
44
  "devDependencies": {
32
- "bower": "^1.7.7",
33
- "browserify": "^16.2.3",
34
- "chai": "^4.2.0",
35
- "coffee-script": "^1.12.7",
36
- "coffeeify": "^2.1.0",
37
- "ecstatic": "^4.0.0",
38
- "gulp": "^4.0.0",
39
- "gulp-coffee": "^3.0.3",
40
- "gulp-util": "^3.0.1",
41
- "mocha": "*",
42
- "sinon": "^7.3.1",
43
- "sinon-chai": "^3.3.0",
44
- "vinyl-source-stream": "^2.0.0"
45
- },
46
- "optionalDependencies": {
47
- "lodash": "^4.17.4",
48
- "q": "^1.0.1",
49
- "underscore": "^1.8.3"
50
- },
51
- "browser": {
52
- "lodash": false,
53
- "q": false,
54
- "underscore": false
45
+ "@babel/core": "^7.20.12",
46
+ "@babel/eslint-parser": "^7.19.1",
47
+ "@babel/preset-env": "^7.20.2",
48
+ "babel-loader": "^9.1.2",
49
+ "chai": "^4.3.7",
50
+ "core-js": "^3.27.2",
51
+ "eslint": "^8.33.0",
52
+ "eslint-plugin-mocha": "^10.1.0",
53
+ "mocha": "^10.2.0",
54
+ "prettier": "^2.8.3",
55
+ "sinon": "^15.0.1",
56
+ "sinon-chai": "^3.7.0",
57
+ "webpack": "^5.75.0",
58
+ "webpack-cli": "^5.0.1",
59
+ "webpack-dev-server": "^4.11.1",
60
+ "webpack-merge": "^5.8.0"
55
61
  }
56
62
  }
@@ -0,0 +1,5 @@
1
+ module.exports = {
2
+ semi: false,
3
+ singleQuote: true,
4
+ printWidth: 120,
5
+ }
package/src/legacy.js ADDED
@@ -0,0 +1,14 @@
1
+ const yayson = require('./yayson')
2
+ const legacyPresenter = require('./yayson/legacy-presenter')
3
+ const legacyStore = require('./yayson/legacy-store')
4
+
5
+ module.exports = function (options = {}) {
6
+ const { Store, Presenter, Adapter } = yayson(options)
7
+ return {
8
+ Store: legacyStore(Store),
9
+ Presenter: legacyPresenter(Presenter),
10
+ Adapter,
11
+ }
12
+ }
13
+
14
+
@@ -0,0 +1,18 @@
1
+ class Adapter {
2
+ static get(model, key) {
3
+ if (key) {
4
+ return model[key]
5
+ }
6
+ return model
7
+ }
8
+
9
+ static id(model) {
10
+ const id = this.get(model, 'id')
11
+ if (id === undefined) {
12
+ return id
13
+ }
14
+ return `${id}`
15
+ }
16
+ }
17
+
18
+ module.exports = Adapter
@@ -0,0 +1 @@
1
+ module.exports = { sequelize: require('./sequelize') }
@@ -0,0 +1,11 @@
1
+ const Adapter = require('../adapter')
2
+
3
+ class SequelizeAdapter extends Adapter {
4
+ static get(model, key) {
5
+ if (model != null) {
6
+ return model.get(key)
7
+ }
8
+ }
9
+ }
10
+
11
+ module.exports = SequelizeAdapter
@@ -0,0 +1,121 @@
1
+ module.exports = function (Presenter) {
2
+ return class LegacyPresenter extends Presenter {
3
+ static type = 'object'
4
+
5
+ pluralType() {
6
+ return this.constructor.plural || this.constructor.type + 's'
7
+ }
8
+
9
+ attributes(instance) {
10
+ if (!instance) {
11
+ return null
12
+ }
13
+ const attributes = { ...this.constructor.adapter.get(instance) }
14
+ const relationships = this.relationships()
15
+ for (let key in relationships) {
16
+ var id
17
+ const data = attributes[key]
18
+ if (data == null) {
19
+ id = attributes[key + 'Id']
20
+ if (id != null) {
21
+ attributes[key] = id
22
+ }
23
+ } else if (data instanceof Array) {
24
+ attributes[key] = data.map((obj) => obj.id)
25
+ } else {
26
+ attributes[key] = data.id
27
+ }
28
+ }
29
+ return attributes
30
+ }
31
+
32
+ includeRelationships(scope, instance) {
33
+ if (!scope.links) {
34
+ scope.links = {}
35
+ }
36
+ const relationships = this.relationships()
37
+ const result = []
38
+ for (var key in relationships) {
39
+ const factory = relationships[key]
40
+ if (!factory) throw new Error(`Presenter for ${key} in ${this.constructor.type} is not defined`)
41
+
42
+ const presenter = new factory(scope)
43
+
44
+ const data = this.constructor.adapter.get(instance, key)
45
+ if (data != null) {
46
+ presenter.toJSON(data, { defaultPlural: true })
47
+ }
48
+
49
+ const type = scope[this.pluralType()] != null ? this.pluralType() : this.constructor.type
50
+ const keyName = scope[presenter.pluralType()] != null ? presenter.pluralType() : presenter.constructor.type
51
+ const link = { type: keyName }
52
+ scope.links[`${type}.${key}`] = link
53
+ result.push(link)
54
+ }
55
+ return result
56
+ }
57
+
58
+ toJSON(instanceOrCollection, options) {
59
+ let type
60
+ if (options == null) {
61
+ options = {}
62
+ }
63
+ if (instanceOrCollection instanceof Array) {
64
+ const collection = instanceOrCollection
65
+ if (!this.scope[(type = this.pluralType())]) {
66
+ this.scope[type] = []
67
+ }
68
+ collection.forEach((instance) => {
69
+ return this.toJSON(instance)
70
+ })
71
+ } else {
72
+ let links
73
+ const instance = instanceOrCollection
74
+ let added = true
75
+ const attrs = this.attributes(instance)
76
+ if ((links = this.links())) {
77
+ attrs.links = links
78
+ }
79
+ // If eg x.image already exists
80
+ if (this.scope[this.constructor.type] && !this.scope[this.pluralType()]) {
81
+ if (this.scope[this.constructor.type].id !== attrs.id) {
82
+ this.scope[this.pluralType()] = [this.scope[this.constructor.type]]
83
+ delete this.scope[this.constructor.type]
84
+ this.scope[this.pluralType()].push(attrs)
85
+ } else {
86
+ added = false
87
+ }
88
+
89
+ // If eg x.images already exists
90
+ } else if (this.scope[this.pluralType()]) {
91
+ if (!this.scope[this.pluralType()].some((i) => i.id === attrs.id)) {
92
+ this.scope[this.pluralType()].push(attrs)
93
+ } else {
94
+ added = false
95
+ }
96
+ } else if (options.defaultPlural) {
97
+ this.scope[this.pluralType()] = [attrs]
98
+ } else {
99
+ this.scope[this.constructor.type] = attrs
100
+ }
101
+
102
+ if (added) {
103
+ this.includeRelationships(this.scope, instance)
104
+ }
105
+ }
106
+ return this.scope
107
+ }
108
+
109
+ render(instanceOrCollection) {
110
+ return this.toJSON(instanceOrCollection)
111
+ }
112
+
113
+ static toJSON() {
114
+ return new this().toJSON(...arguments)
115
+ }
116
+
117
+ static render() {
118
+ return new this().render(...arguments)
119
+ }
120
+ }
121
+ }