yayson 2.1.0 → 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.
@@ -0,0 +1,156 @@
1
+ // TODO: This file was created by bulk-decaffeinate.
2
+ // Sanity-check the conversion and remove this comment.
3
+ /*
4
+ * decaffeinate suggestions:
5
+ * DS101: Remove unnecessary use of Array.from
6
+ * DS102: Remove unnecessary code created because of implicit returns
7
+ * DS205: Consider reworking code to avoid use of IIFEs
8
+ * DS207: Consider shorter variations of null checks
9
+ * Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md
10
+ */
11
+
12
+ module.exports = function () {
13
+ class Record {
14
+ constructor(options) {
15
+ this.type = options.type
16
+ this.data = options.data
17
+ }
18
+ }
19
+
20
+ return class Store {
21
+ constructor(options) {
22
+ this.types = options.types || {}
23
+ this.reset()
24
+ }
25
+
26
+ reset() {
27
+ this.records = []
28
+ return (this.relations = {})
29
+ }
30
+
31
+ toModel(rec, type, models) {
32
+ const model = {...rec.data}
33
+ if (!models[type][model.id]) {
34
+ models[type][model.id] = model
35
+ }
36
+
37
+ const relations = this.relations[type]
38
+ for (let attribute in relations) {
39
+ var relationType = relations[attribute]
40
+ model[attribute] =
41
+ model[attribute] instanceof Array
42
+ ? model[attribute].map((id) => this.find(relationType, id, models))
43
+ : this.find(relationType, model[attribute], models)
44
+ }
45
+
46
+ return model
47
+ }
48
+
49
+ setupRelations(links) {
50
+ return (() => {
51
+ const result = []
52
+ for (let key in links) {
53
+ const value = links[key]
54
+ let [type, attribute] = Array.from(key.split('.'))
55
+ type = this.types[type] || type
56
+ if (!this.relations[type]) {
57
+ this.relations[type] = {}
58
+ }
59
+ result.push((this.relations[type][attribute] = this.types[value.type] || value.type))
60
+ }
61
+ return result
62
+ })()
63
+ }
64
+
65
+ findRecord(type, id) {
66
+ return this.records.find((r) => r.type === type && r.data.id === id)
67
+ }
68
+
69
+ findRecords(type) {
70
+ return this.records.filter((r) => r.type === type)
71
+ }
72
+
73
+ retrive(type, data) {
74
+ this.sync(data)
75
+ const { id } = data[type]
76
+ return this.find(type, id)
77
+ }
78
+
79
+ find(type, id, models) {
80
+ if (models == null) {
81
+ models = {}
82
+ }
83
+ const rec = this.findRecord(type, id)
84
+ if (rec == null) {
85
+ return null
86
+ }
87
+ if (!models[type]) {
88
+ models[type] = {}
89
+ }
90
+ return models[type][id] || this.toModel(rec, type, models)
91
+ }
92
+
93
+ findAll(type, models) {
94
+ if (models == null) {
95
+ models = {}
96
+ }
97
+ const recs = this.findRecords(type)
98
+ if (recs == null) {
99
+ return []
100
+ }
101
+ recs.forEach((rec) => {
102
+ if (!models[type]) {
103
+ models[type] = {}
104
+ }
105
+ return this.toModel(rec, type, models)
106
+ })
107
+ return Object.values(models[type] || {})
108
+ }
109
+
110
+ remove(type, id) {
111
+ type = this.types[type] || type
112
+
113
+ const remove = (record) => {
114
+ const index = this.records.indexOf(record)
115
+ if (!(index < 0)) {
116
+ return this.records.splice(index, 1)
117
+ }
118
+ }
119
+
120
+ if (id != null) {
121
+ return remove(this.findRecord(type, id))
122
+ } else {
123
+ const records = this.findRecords(type)
124
+ return records.forEach(remove)
125
+ }
126
+ }
127
+
128
+ sync(data) {
129
+ this.setupRelations(data.links)
130
+
131
+ return (() => {
132
+ const result = []
133
+ for (var name in data) {
134
+ if (name === 'meta' || name === 'links') {
135
+ continue
136
+ }
137
+
138
+ const value = data[name]
139
+
140
+ const add = (d) => {
141
+ const type = this.types[name] || name
142
+ this.remove(type, d.id)
143
+ return this.records.push(new Record({ type, data: d }))
144
+ }
145
+
146
+ if (value instanceof Array) {
147
+ result.push(value.forEach(add))
148
+ } else {
149
+ result.push(add(value))
150
+ }
151
+ }
152
+ return result
153
+ })()
154
+ }
155
+ }
156
+ }
@@ -0,0 +1,198 @@
1
+ module.exports = function (adapter) {
2
+ const buildLinks = function (link) {
3
+ if (link == null) {
4
+ return
5
+ }
6
+ if (link.self != null || link.related != null) {
7
+ return link
8
+ } else {
9
+ return { self: link }
10
+ }
11
+ }
12
+
13
+ return class Presenter {
14
+ static adapter = adapter
15
+ static type = 'objects'
16
+
17
+ constructor(scope) {
18
+ if (scope == null) {
19
+ scope = {}
20
+ }
21
+ this.scope = scope
22
+ }
23
+
24
+ id(instance) {
25
+ return this.constructor.adapter.id(instance)
26
+ }
27
+
28
+ //eslint-disable-next-line no-unused-vars
29
+ selfLinks(instance) {}
30
+
31
+ links() {}
32
+
33
+ relationships() {}
34
+
35
+ attributes(instance) {
36
+ if (instance == null) {
37
+ return null
38
+ }
39
+ const attributes = { ...this.constructor.adapter.get(instance) }
40
+ delete attributes['id']
41
+
42
+ const relationships = this.relationships()
43
+ for (let key in relationships) {
44
+ delete attributes[key]
45
+ }
46
+ return attributes
47
+ }
48
+
49
+ includeRelationships(scope, instance) {
50
+ const relationships = this.relationships()
51
+ const result = []
52
+ for (var key in relationships) {
53
+ const factory = relationships[key]
54
+ if (!factory) throw new Error(`Presenter for ${key} in ${this.constructor.type} is not defined`)
55
+
56
+ const presenter = new factory(scope)
57
+
58
+ const data = this.constructor.adapter.get(instance, key)
59
+ result.push(presenter.toJSON(data, { include: true }))
60
+ }
61
+ return result
62
+ }
63
+
64
+ buildRelationships(instance) {
65
+ if (instance == null) {
66
+ return null
67
+ }
68
+ const rels = this.relationships()
69
+ const links = this.links(instance) || {}
70
+ let relationships = null
71
+ for (var key in rels) {
72
+ let data = this.constructor.adapter.get(instance, key)
73
+ var presenter = rels[key]
74
+ var buildData = (d) => {
75
+ return (data = {
76
+ id: this.constructor.adapter.id(d),
77
+ type: presenter.type,
78
+ })
79
+ }
80
+ const build = (d) => {
81
+ const rel = {}
82
+ if (d != null) {
83
+ rel.data = buildData(d)
84
+ }
85
+ if (links[key] != null) {
86
+ rel.links = buildLinks(links[key])
87
+ } else if (d == null) {
88
+ rel.data = null
89
+ }
90
+ return rel
91
+ }
92
+ if (!relationships) {
93
+ relationships = {}
94
+ }
95
+ if (!relationships[key]) {
96
+ relationships[key] = {}
97
+ }
98
+ if (data instanceof Array) {
99
+ relationships[key].data = data.map(buildData)
100
+ if (links[key] != null) {
101
+ relationships[key].links = buildLinks(links[key])
102
+ }
103
+ } else {
104
+ relationships[key] = build(data)
105
+ }
106
+ }
107
+ return relationships
108
+ }
109
+
110
+ buildSelfLink(instance) {
111
+ return buildLinks(this.selfLinks(instance))
112
+ }
113
+
114
+ toJSON(instanceOrCollection, options) {
115
+ if (options == null) {
116
+ options = {}
117
+ }
118
+ if (options.meta != null) {
119
+ this.scope.meta = options.meta
120
+ }
121
+ if (options.links != null) {
122
+ this.scope.links = options.links
123
+ }
124
+ if (!this.scope.data) {
125
+ this.scope.data = null
126
+ }
127
+
128
+ if (instanceOrCollection == null) {
129
+ return this.scope
130
+ }
131
+
132
+ if (instanceOrCollection instanceof Array) {
133
+ const collection = instanceOrCollection
134
+ if (!this.scope.data) {
135
+ this.scope.data = []
136
+ }
137
+ collection.forEach((instance) => {
138
+ return this.toJSON(instance, options)
139
+ })
140
+ } else {
141
+ const instance = instanceOrCollection
142
+ let added = true
143
+ const model = {
144
+ id: this.id(instance),
145
+ type: this.constructor.type,
146
+ attributes: this.attributes(instance),
147
+ }
148
+ if (model.id === undefined) {
149
+ delete model.id
150
+ }
151
+ const relationships = this.buildRelationships(instance)
152
+ if (relationships != null) {
153
+ model.relationships = relationships
154
+ }
155
+ const links = this.buildSelfLink(instance)
156
+ if (links != null) {
157
+ model.links = links
158
+ }
159
+
160
+ if (options.include) {
161
+ if (!this.scope.included) {
162
+ this.scope.included = []
163
+ }
164
+ if (!this.scope.included.concat(this.scope.data).some((i) => i.id === model.id && i.type === model.type)) {
165
+ this.scope.included.push(model)
166
+ } else {
167
+ added = false
168
+ }
169
+ } else if (this.scope.data != null) {
170
+ if (!(this.scope.data instanceof Array) || !this.scope.data.some((i) => i.id === model.id)) {
171
+ this.scope.data.push(model)
172
+ } else {
173
+ added = false
174
+ }
175
+ } else {
176
+ this.scope.data = model
177
+ }
178
+
179
+ if (added) {
180
+ this.includeRelationships(this.scope, instance)
181
+ }
182
+ }
183
+ return this.scope
184
+ }
185
+
186
+ render(instanceOrCollection, options) {
187
+ return this.toJSON(instanceOrCollection, options)
188
+ }
189
+
190
+ static toJSON() {
191
+ return new this().toJSON(...arguments)
192
+ }
193
+
194
+ static render() {
195
+ return new this().render(...arguments)
196
+ }
197
+ }
198
+ }
@@ -0,0 +1,194 @@
1
+ module.exports = function () {
2
+ class Record {
3
+ constructor(options) {
4
+ ;({
5
+ id: this.id,
6
+ type: this.type,
7
+ attributes: this.attributes,
8
+ relationships: this.relationships,
9
+ links: this.links,
10
+ meta: this.meta,
11
+ } = options)
12
+ }
13
+ }
14
+
15
+ class Store {
16
+ //eslint-disable-next-line no-unused-vars
17
+ constructor(options) {
18
+ this.reset()
19
+ }
20
+
21
+ reset() {
22
+ this.records = []
23
+ }
24
+
25
+ toModel(rec, type, models) {
26
+ let typeAttribute
27
+ const model = { ...(rec.attributes || {}) }
28
+ if (model.type) {
29
+ typeAttribute = model.type
30
+ }
31
+
32
+ model.id = rec.id
33
+ model.type = rec.type
34
+ if (!models[type]) {
35
+ models[type] = {}
36
+ }
37
+ if (!models[type][rec.id]) {
38
+ models[type][rec.id] = model
39
+ }
40
+
41
+ if (Object.prototype.hasOwnProperty.call(model, 'meta')) {
42
+ model.attributes = { meta: model.meta }
43
+ delete model.meta
44
+ }
45
+
46
+ if (rec.meta != null) {
47
+ model.meta = rec.meta
48
+ }
49
+
50
+ if (rec.links != null) {
51
+ model.links = rec.links
52
+ }
53
+
54
+ if (rec.relationships != null) {
55
+ for (let key in rec.relationships) {
56
+ const rel = rec.relationships[key]
57
+ const { data } = rel
58
+ const { links } = rel
59
+ const { meta } = rel
60
+
61
+ model[key] = null
62
+ if (data == null && links == null) {
63
+ continue
64
+ }
65
+ const resolve = ({ type, id }) => {
66
+ return this.find(type, id, models)
67
+ }
68
+ model[key] = data instanceof Array ? data.map(resolve) : data != null ? resolve(data) : {}
69
+
70
+ // Model of the relation
71
+ const currentModel = model[key]
72
+
73
+ if (currentModel != null) {
74
+ // retain the links and meta from the relationship entry
75
+ // use as underscore property name because the currentModel may also have a link and meta reference
76
+ currentModel._links = links || {}
77
+ currentModel._meta = meta || {}
78
+ }
79
+ }
80
+ }
81
+
82
+ if (typeAttribute) {
83
+ model.type = typeAttribute
84
+ }
85
+ return model
86
+ }
87
+
88
+ findRecord(type, id) {
89
+ return this.records.find((r) => r.type === type && r.id === id)
90
+ }
91
+
92
+ findRecords(type) {
93
+ return this.records.filter((r) => r.type === type)
94
+ }
95
+
96
+ find(type, id, models) {
97
+ if (models == null) {
98
+ models = {}
99
+ }
100
+ const rec = this.findRecord(type, id)
101
+ if (rec == null) {
102
+ return null
103
+ }
104
+ if (!models[type]) {
105
+ models[type] = {}
106
+ }
107
+ return models[type][id] || this.toModel(rec, type, models)
108
+ }
109
+
110
+ findAll(type, models) {
111
+ if (models == null) {
112
+ models = {}
113
+ }
114
+ const recs = this.findRecords(type)
115
+ if (recs == null) {
116
+ return []
117
+ }
118
+ recs.forEach((rec) => {
119
+ if (!models[type]) {
120
+ models[type] = {}
121
+ }
122
+ return this.toModel(rec, type, models)
123
+ })
124
+ return Object.values(models[type] || {})
125
+ }
126
+
127
+ remove(type, id) {
128
+ const remove = (record) => {
129
+ const index = this.records.indexOf(record)
130
+ if (!(index < 0)) {
131
+ return this.records.splice(index, 1)
132
+ }
133
+ }
134
+
135
+ if (id != null) {
136
+ return remove(this.findRecord(type, id))
137
+ } else {
138
+ const records = this.findRecords(type)
139
+ return records.map(remove)
140
+ }
141
+ }
142
+
143
+ sync(body) {
144
+ const sync = (data) => {
145
+ if (data == null) {
146
+ return null
147
+ }
148
+ const add = (obj) => {
149
+ const { type, id } = obj
150
+ this.remove(type, id)
151
+ const rec = new Record(obj)
152
+ this.records.push(rec)
153
+ return rec
154
+ }
155
+
156
+ if (data instanceof Array) {
157
+ return data.map(add)
158
+ } else {
159
+ return add(data)
160
+ }
161
+ }
162
+
163
+ sync(body.included)
164
+ const recs = sync(body.data)
165
+
166
+ if (recs == null) {
167
+ return null
168
+ }
169
+
170
+ const models = {}
171
+ let result = null
172
+
173
+ if (recs instanceof Array) {
174
+ result = recs.map((rec) => {
175
+ return this.toModel(rec, rec.type, models)
176
+ })
177
+ } else {
178
+ result = this.toModel(recs, recs.type, models)
179
+ }
180
+
181
+ if (Object.prototype.hasOwnProperty.call(body, 'links')) {
182
+ result.links = body.links
183
+ }
184
+
185
+ if (Object.prototype.hasOwnProperty.call(body, 'meta')) {
186
+ result.meta = body.meta
187
+ }
188
+
189
+ return result
190
+ }
191
+ }
192
+
193
+ return Store
194
+ }
package/src/yayson.js ADDED
@@ -0,0 +1,23 @@
1
+ const Adapter = require('./yayson/adapter')
2
+ const adapters = require('./yayson/adapters')
3
+ const presenter = require('./yayson/presenter')
4
+ const store = require('./yayson/store')
5
+
6
+ const lookupAdapter = function (nameOrAdapter) {
7
+ if (nameOrAdapter === 'default') {
8
+ return Adapter
9
+ }
10
+ return adapters[nameOrAdapter] || nameOrAdapter || Adapter
11
+ }
12
+
13
+ module.exports = function ({ adapter: nameOrAdapter } = {}) {
14
+ const adapter = lookupAdapter(nameOrAdapter)
15
+ const Presenter = presenter(lookupAdapter(adapter))
16
+ const Store = store()
17
+
18
+ return {
19
+ Store,
20
+ Presenter,
21
+ Adapter,
22
+ }
23
+ }
@@ -0,0 +1,27 @@
1
+ const path = require('path')
2
+ const { merge } = require('webpack-merge')
3
+ const common = require('./webpack.common.js')
4
+
5
+ module.exports = merge(common, {
6
+ entry: {
7
+ tests: './test/browser.js',
8
+ },
9
+ devtool: 'eval',
10
+ output: {
11
+ path: __dirname + '/test',
12
+ filename: '[name].js',
13
+ },
14
+ performance: {
15
+ maxAssetSize: 3000000,
16
+ maxEntrypointSize: 3000000,
17
+ assetFilter: (asset) => {
18
+ console.log(1212, asset, asset.match('tests.js'))
19
+ return asset.match('tests.js')
20
+ },
21
+ },
22
+ devServer: {
23
+ static: [path.join(__dirname, 'test'), path.join(__dirname, 'dist'), path.join(__dirname, 'node_modules/mocha')],
24
+ compress: true,
25
+ port: 9000,
26
+ },
27
+ })
@@ -0,0 +1,39 @@
1
+ module.exports = {
2
+ mode: 'production',
3
+ entry: {
4
+ yayson: './src/yayson.js',
5
+ 'yayson.min': './src/yayson.js',
6
+ 'yayson-legacy': './src/legacy.js',
7
+ 'yayson-legacy.min': './src/legacy.js',
8
+ },
9
+ output: {
10
+ path: __dirname + '/dist',
11
+ filename: '[name].js',
12
+ library: 'yayson',
13
+ },
14
+ resolve: {
15
+ fallback: { util: false },
16
+ },
17
+ module: {
18
+ rules: [
19
+ {
20
+ test: /\.js$/,
21
+ exclude: /node_modules/,
22
+ use: {
23
+ loader: 'babel-loader',
24
+ options: {
25
+ presets: [
26
+ [
27
+ '@babel/preset-env',
28
+ {
29
+ corejs: '3',
30
+ useBuiltIns: 'entry',
31
+ },
32
+ ],
33
+ ],
34
+ },
35
+ },
36
+ },
37
+ ],
38
+ },
39
+ }
@@ -0,0 +1,21 @@
1
+ const webpack = require('webpack')
2
+ const { merge } = require('webpack-merge')
3
+ const common = require('./webpack.common.js')
4
+ const PACKAGE = require('./package.json')
5
+ const TerserPlugin = require('terser-webpack-plugin')
6
+
7
+ module.exports = merge(common, {
8
+ optimization: {
9
+ minimize: true,
10
+ minimizer: [
11
+ new TerserPlugin({
12
+ extractComments: false,
13
+ }),
14
+ ],
15
+ },
16
+ plugins: [
17
+ new webpack.BannerPlugin({
18
+ banner: `yayson v ${PACKAGE.version} (${PACKAGE.homepage}) by ${PACKAGE.author}`,
19
+ }),
20
+ ],
21
+ })
@@ -1,35 +0,0 @@
1
- # Javascript Node CircleCI 2.0 configuration file
2
- #
3
- # Check https://circleci.com/docs/2.0/language-javascript/ for more details
4
- #
5
- version: 2
6
- jobs:
7
- build:
8
- docker:
9
- # specify the version you desire here
10
- - image: circleci/node:6.11
11
- environment:
12
- NODE_ENV: 'test'
13
-
14
- working_directory: ~/repo
15
-
16
- steps:
17
- - checkout
18
- - run:
19
- name: Setup Environment Variables
20
- command: |
21
- echo 'export NODE_ENV="test"' >> $BASH_ENV
22
- # Download and cache dependencies
23
- - restore_cache:
24
- keys:
25
- - v1-dependencies-{{ checksum "package.json" }}
26
- # fallback to using the latest cache if no exact match is found
27
- - v1-dependencies-
28
- - run: npm install
29
- - save_cache:
30
- paths:
31
- - node_modules
32
- key: v1-dependencies-{{ checksum "package.json" }}
33
-
34
- # run tests!
35
- - run: npm test