@modernpoacher/halacious 6.3.1 → 7.0.1

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.
@@ -2,7 +2,7 @@ const hapi = require('@hapi/hapi')
2
2
  const vision = require('@hapi/vision')
3
3
  const halacious = require('#halacious')
4
4
 
5
- const PLUGIN = '@modernpoacher/halacious'
5
+ const PLUGIN = 'halacious' // '@modernpoacher/halacious'
6
6
 
7
7
  async function init () {
8
8
  const server = hapi.server({ port: 8080 })
@@ -4,7 +4,7 @@ const hapi = require('@hapi/hapi')
4
4
  const vision = require('@hapi/vision')
5
5
  const halacious = require('#halacious')
6
6
 
7
- const PLUGIN = '@modernpoacher/halacious'
7
+ const PLUGIN = 'halacious' // '@modernpoacher/halacious'
8
8
 
9
9
  async function init () {
10
10
  const server = hapi.server({ port: 8080 })
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@modernpoacher/halacious",
3
- "version": "6.3.1",
3
+ "version": "7.0.1",
4
4
  "description": "A HAL processor for Hapi",
5
5
  "keywords": [
6
6
  "HAL",
@@ -37,33 +37,31 @@
37
37
  "prepare": "husky install"
38
38
  },
39
39
  "dependencies": {
40
- "@hapi/boom": "9.1.0",
41
- "@hapi/hoek": "9.0.4",
42
- "@hapi/joi": "15.1.1",
40
+ "@hapi/boom": "^10.0.1",
41
+ "@hapi/hoek": "^11.0.4",
43
42
  "async": "^3.2.5",
43
+ "joi": "^17.12.0",
44
44
  "lodash": "^4.17.21",
45
45
  "marked": "^11.1.1",
46
- "marked-gfm-heading-id": "^3.1.2",
47
- "marked-mangle": "^1.1.6",
48
46
  "negotiator": "^0.6.3",
49
47
  "pug": "^3.0.2",
50
48
  "urijs": "^1.19.11",
51
- "url-template": "2.0.8"
49
+ "url-template": "^3.1.1"
52
50
  },
53
51
  "devDependencies": {
54
52
  "@babel/core": "^7.23.7",
55
53
  "@babel/eslint-parser": "^7.23.3",
56
- "@hapi/hapi": "18.4.1",
57
- "@hapi/vision": "6.0.1",
54
+ "@hapi/hapi": "^21.3.2",
55
+ "@hapi/vision": "^7.0.3",
56
+ "@modernpoacher/hooks": "^1.0.456",
58
57
  "@sequencemedia/sinon-chai": "^0.0.6",
59
- "@typescript-eslint/eslint-plugin": "5.61.0",
60
- "@typescript-eslint/parser": "5.61.0",
58
+ "@typescript-eslint/eslint-plugin": "^6.19.0",
59
+ "@typescript-eslint/parser": "^6.19.0",
61
60
  "chai": "^5.0.0",
62
- "chai-string": "^1.5.0",
63
61
  "cross-env": "^7.0.3",
64
62
  "eslint": "^8.56.0",
65
63
  "eslint-config-standard": "^17.1.0",
66
- "eslint-config-standard-with-typescript": "35.0.0",
64
+ "eslint-config-standard-with-typescript": "^43.0.0",
67
65
  "husky": "^8.0.3",
68
66
  "mocha": "^10.2.0",
69
67
  "sinon": "^17.0.1"
package/src/plugin.mjs CHANGED
@@ -1,6 +1,6 @@
1
- import joi from '@hapi/joi'
2
- import boom from '@hapi/boom'
3
- import hoek from '@hapi/hoek'
1
+ import joi from 'joi'
2
+ import * as boom from '@hapi/boom'
3
+ import * as hoek from '@hapi/hoek'
4
4
  import _ from 'lodash'
5
5
  import fs from 'node:fs'
6
6
  import path from 'node:path'
@@ -10,17 +10,11 @@ import async from 'async'
10
10
  import {
11
11
  marked
12
12
  } from 'marked'
13
- import {
14
- mangle
15
- } from 'marked-mangle'
16
- import {
17
- gfmHeadingId
18
- } from 'marked-gfm-heading-id'
19
- import urlTemplate from 'url-template'
20
- import Negotiator from 'negotiator'
21
- import URITemplate from 'urijs/src/URITemplate.js'
22
13
  import url from 'url'
23
14
  import URI from 'urijs'
15
+ import * as urlTemplate from 'url-template'
16
+ import Negotiator from 'negotiator'
17
+ import URITemplate from 'urijs/src/URITemplate.js'
24
18
  import IAM from '#where-am-i'
25
19
  import RepresentationFactory from './representation.mjs'
26
20
 
@@ -61,7 +55,7 @@ function flattenContext (template, ctx) {
61
55
  return result
62
56
  }
63
57
 
64
- const optionsSchema = {
58
+ const optionsSchema = joi.object({
65
59
  absolute: joi.boolean().default(false),
66
60
  host: joi.string(),
67
61
  hostname: joi.string(),
@@ -84,7 +78,7 @@ const optionsSchema = {
84
78
  mediaTypes: joi.array().items(joi.string()).single().default([HAL_MIME_TYPE]),
85
79
  requireHalJsonAcceptHeader: joi.boolean().default(false),
86
80
  marked: joi.object().default({})
87
- }
81
+ })
88
82
 
89
83
  /**
90
84
  * Registers plugin routes and an "api" object with the hapi server.
@@ -92,20 +86,19 @@ const optionsSchema = {
92
86
  * @param opts
93
87
  * @param next
94
88
  */
95
- const plugin = {
89
+ export const plugin = {
96
90
  pkg: PKG,
97
91
 
98
92
  async register (server, opts) {
99
93
  let settings = opts
100
94
 
101
- joi.validate(opts, optionsSchema, (err, validated) => {
102
- if (err) throw err
95
+ const { error, value } = optionsSchema.validate(opts)
103
96
 
104
- settings = validated
105
- })
97
+ if (error) throw error
98
+
99
+ settings = value
106
100
 
107
- marked.use(gfmHeadingId(settings.marked))
108
- marked.use(mangle(settings.marked))
101
+ marked.setOptions(settings.marked)
109
102
 
110
103
  const selection = settings.apiServerLabel
111
104
  ? server.select(settings.apiServerLabel)
@@ -118,7 +111,7 @@ const plugin = {
118
111
  internals.byPrefix = {}
119
112
 
120
113
  // valid rel options
121
- internals.relSchema = {
114
+ internals.relSchema = joi.object({
122
115
  // the rel name, will default to file's basename if available
123
116
  name: joi.string().required(),
124
117
 
@@ -130,17 +123,19 @@ const plugin = {
130
123
 
131
124
  // returns the qualified name of the rel (including the namespace)
132
125
  qname: joi
133
- .func()
126
+ .function()
134
127
  .optional()
135
128
  .default(function () {
136
- return this.namespace
137
- ? util.format('%s:%s', this.namespace.prefix, this.name)
138
- : this.name
129
+ return function () {
130
+ return this.namespace
131
+ ? util.format('%s:%s', this.namespace.prefix, this.name)
132
+ : this.name
133
+ }
139
134
  })
140
- }
135
+ }).unknown()
141
136
 
142
137
  // valid namespace options
143
- internals.nsSchema = {
138
+ internals.nsSchema = joi.object({
144
139
  // the namespace name, will default to dir basename if available
145
140
  name: joi.string().required(),
146
141
 
@@ -158,38 +153,44 @@ const plugin = {
158
153
 
159
154
  // validates and adds a rel to the namespace
160
155
  rel: joi
161
- .func()
156
+ .function()
162
157
  .optional()
163
- .default(function (rel) {
164
- this.rels = this.rels || {}
158
+ .default(function () {
159
+ return function (rel) {
160
+ this.rels = this.rels || {}
161
+
162
+ if (_.isString(rel)) rel = { name: rel }
165
163
 
166
- if (_.isString(rel)) rel = { name: rel }
164
+ rel.name =
165
+ rel.name ||
166
+ (rel.file && path.basename(rel.file, path.extname(rel.file)))
167
+
168
+ const { error, value } = internals.relSchema.validate(rel)
169
+ if (error) throw error
167
170
 
168
- rel.name =
169
- rel.name ||
170
- (rel.file && path.basename(rel.file, path.extname(rel.file)))
171
- joi.validate(rel, internals.relSchema, (err, value) => {
172
- if (err) throw err
173
171
  rel = value
174
- })
175
- this.rels[rel.name] = rel
176
- rel.namespace = this
177
- return this
172
+
173
+ this.rels[rel.name] = rel
174
+ rel.namespace = this
175
+ return this
176
+ }
178
177
  }),
179
178
 
180
179
  // synchronously scans a directory for rel descriptors and adds them to the namespace
181
180
  scanDirectory: joi
182
- .func()
181
+ .function()
183
182
  .optional()
184
- .default(function (directory) {
185
- const files = fs.readdirSync(directory)
186
- files.forEach(function (file) {
187
- this.rel({ file: path.join(directory, file) })
188
- }, this)
183
+ .default(function () {
184
+ return function (directory) {
185
+ const files = fs.readdirSync(directory)
186
+ files.forEach(function (file) {
187
+ this.rel({ file: path.join(directory, file) })
188
+ }, this)
189
189
 
190
- return this
190
+ return this
191
+ }
191
192
  })
192
- }
193
+ })
193
194
 
194
195
  internals.filter = function (request) {
195
196
  return _.get(request.route.settings, 'plugins.hal', true)
@@ -214,13 +215,14 @@ const plugin = {
214
215
  namespace.name || (namespace.dir && path.basename(namespace.dir))
215
216
 
216
217
  // fail fast if the namespace isnt valid
217
- joi.validate(namespace, internals.nsSchema, (err, value) => {
218
- if (err) throw err
219
- namespace = value
218
+ const { error, value } = internals.nsSchema.validate(namespace)
220
219
 
221
- // would prefer to initialize w/ joi but it keeps a static reference to the value for some reason
222
- namespace.rels = {}
223
- })
220
+ if (error) throw error
221
+
222
+ namespace = value
223
+
224
+ // would prefer to initialize w/ joi but it keeps a static reference to the value for some reason
225
+ namespace.rels = {}
224
226
 
225
227
  if (namespace.dir) {
226
228
  namespace.scanDirectory(namespace.dir)
@@ -321,11 +323,10 @@ const plugin = {
321
323
  }
322
324
  } else {
323
325
  // could be globally qualified (e.g. 'self')
324
- joi.validate({ name: namespace }, internals.relSchema, (err, value) => {
325
- if (err) throw err
326
+ const { error, value } = internals.relSchema.validate({ name: namespace })
327
+ if (error) throw error
326
328
 
327
- rel = value
328
- })
329
+ rel = value
329
330
  }
330
331
 
331
332
  return rel
@@ -377,8 +378,8 @@ const plugin = {
377
378
  }
378
379
 
379
380
  // see http://tools.ietf.org/html/draft-kelly-json-hal-06#section-8.2
380
- internals.linkSchema = {
381
- href: joi.alternatives([joi.string(), joi.func()]).required(),
381
+ internals.linkSchema = joi.object({
382
+ href: joi.alternatives([joi.string(), joi.function()]).required(),
382
383
  templated: joi.boolean().optional(),
383
384
  title: joi.string().optional(),
384
385
  type: joi.string().optional(),
@@ -386,7 +387,7 @@ const plugin = {
386
387
  name: joi.string().optional(),
387
388
  profile: joi.string().optional(),
388
389
  hreflang: joi.string().optional()
389
- }
390
+ })
390
391
 
391
392
  internals.isRelativePath = function (path) {
392
393
  return (
@@ -406,10 +407,10 @@ const plugin = {
406
407
  _.isFunction(link) || _.isString(link)
407
408
  ? { href: link }
408
409
  : hoek.clone(link)
409
- joi.validate(link, internals.linkSchema, (err, value) => {
410
- if (err) throw err
411
- link = value
412
- })
410
+ const { error, value } = internals.linkSchema.validate(link)
411
+
412
+ if (error) throw error
413
+ link = value
413
414
 
414
415
  if (
415
416
  relativeTo &&
@@ -505,7 +506,7 @@ const plugin = {
505
506
  const resolveHref = function (href, ctx) {
506
507
  return _.isFunction(href)
507
508
  ? href(rep, ctx)
508
- : urlTemplate.parse(href).expand(flattenContext(href, ctx))
509
+ : urlTemplate.parseTemplate(href).expand(flattenContext(href, ctx))
509
510
  }
510
511
 
511
512
  try {
@@ -855,19 +856,17 @@ const plugin = {
855
856
  * @param filterFn
856
857
  */
857
858
  internals.setFilter = function (filterFn) {
858
- joi.validate(filterFn, joi.func(), (err) => {
859
- if (err) throw err
859
+ const { error } = joi.function().validate(filterFn)
860
+ if (error) throw error
860
861
 
861
- internals.filter = filterFn
862
- })
862
+ internals.filter = filterFn
863
863
  }
864
864
 
865
865
  internals.setUrlBuilder = function (urlBuilder) {
866
- joi.validate(urlBuilder, joi.func(), (err) => {
867
- if (err) throw err
866
+ const { error } = joi.function().validate(urlBuilder)
867
+ if (error) throw error
868
868
 
869
- internals.buildUrl = urlBuilder
870
- })
869
+ internals.buildUrl = urlBuilder
871
870
  }
872
871
 
873
872
  const api = {
@@ -983,7 +982,3 @@ const plugin = {
983
982
  server.ext('onPreStart', internals.preStart)
984
983
  }
985
984
  }
986
-
987
- export {
988
- plugin
989
- }
@@ -1,5 +1,5 @@
1
1
  import _ from 'lodash'
2
- import hoek from '@hapi/hoek'
2
+ import * as hoek from '@hapi/hoek'
3
3
  import URI from 'urijs'
4
4
 
5
5
  const templatedRE = /{*}/
@@ -6,9 +6,10 @@ import hapi from '@hapi/hapi'
6
6
  import vision from '@hapi/vision'
7
7
  import sinon from 'sinon'
8
8
  import sinonChai from '@sequencemedia/sinon-chai'
9
- import chaiString from 'chai-string'
10
9
  import halacious from '#halacious'
11
- import { plugin } from '#halacious/plugin'
10
+ import {
11
+ plugin
12
+ } from '#halacious/plugin'
12
13
  import _ from 'lodash'
13
14
  import url from 'url'
14
15
  import IAM from '#where-am-i'
@@ -16,9 +17,8 @@ import IAM from '#where-am-i'
16
17
  const should = chai.should()
17
18
 
18
19
  chai.use(sinonChai)
19
- chai.use(chaiString)
20
20
 
21
- const PLUGIN = '@modernpoacher/halacious'
21
+ const PLUGIN = 'halacious' // '@modernpoacher/halacious'
22
22
 
23
23
  describe('Halacious Plugin', () => {
24
24
  let server
@@ -35,7 +35,7 @@ describe('Halacious Plugin', () => {
35
35
 
36
36
  xit('should have a registration function', () => {
37
37
  plugin.should.have.property('register')
38
- plugin.register.should.be.a('Function')
38
+ plugin.register.should.be.a('function')
39
39
  })
40
40
 
41
41
  it('should expose a namespace function', (done) => {
@@ -47,7 +47,7 @@ describe('Halacious Plugin', () => {
47
47
  } = server
48
48
 
49
49
  plugin.should.have.property('namespaces')
50
- plugin.namespace.should.be.a('Function')
50
+ plugin.namespace.should.be.a('function')
51
51
  })
52
52
  .then(done)
53
53
  .catch(done)
@@ -70,7 +70,7 @@ describe('Halacious Plugin', () => {
70
70
  namespace.should.have.property('name', 'mycompany')
71
71
  namespace.should.have.property('prefix', 'mco')
72
72
  namespace.should.have.property('rel')
73
- namespace.rel.should.be.a('Function')
73
+ namespace.rel.should.be.a('function')
74
74
  })
75
75
  .then(done)
76
76
  .catch(done)
@@ -1456,7 +1456,7 @@ describe('Halacious Plugin', () => {
1456
1456
  .property('_links')
1457
1457
  .that.has.a.property('self')
1458
1458
  .that.has.a.property('href')
1459
- .that.endsWith('/people/100?donotbreakthis=true')
1459
+ .and.satisfies(href => href.endsWith('/people/100?donotbreakthis=true'))
1460
1460
  })
1461
1461
  .then(done)
1462
1462
  .catch(done)
@@ -5,30 +5,28 @@ import RepresentationFactory from '#halacious/representation'
5
5
 
6
6
  const should = chai.should()
7
7
 
8
- const PLUGIN = '@modernpoacher/halacious'
8
+ const PLUGIN = 'halacious' // '@modernpoacher/halacious'
9
9
 
10
10
  describe('Representation Factory', () => {
11
11
  let server
12
- let plugin
13
12
  let representationFactory
14
13
 
15
- beforeEach((done) => {
14
+ beforeEach(async () => {
16
15
  server = hapi.server({ port: 9191 })
17
16
 
18
- server
19
- .register(halacious)
20
- .then(() => {
21
- plugin = server.plugins[PLUGIN]
22
- representationFactory = new RepresentationFactory(plugin)
23
- done()
24
- })
25
- .catch((err) => {
26
- done(err)
27
- })
17
+ await server.register(halacious)
18
+
19
+ const {
20
+ plugins: {
21
+ [PLUGIN]: plugin
22
+ }
23
+ } = server
24
+
25
+ representationFactory = new RepresentationFactory(plugin)
28
26
  })
29
27
 
30
- afterEach((done) => {
31
- server.stop().then(done).catch(done)
28
+ afterEach(async () => {
29
+ await server.stop()
32
30
  })
33
31
 
34
32
  it('should create a new representation', () => {
@@ -210,6 +208,12 @@ describe('Representation Factory', () => {
210
208
  })
211
209
 
212
210
  it('should link to a registered rel', () => {
211
+ const {
212
+ plugins: {
213
+ [PLUGIN]: plugin
214
+ }
215
+ } = server
216
+
213
217
  plugin.namespaces
214
218
  .add({ name: 'mycompany', prefix: 'mco' })
215
219
  .rel({ name: 'boss' })
@@ -236,6 +240,12 @@ describe('Representation Factory', () => {
236
240
  })
237
241
 
238
242
  it('should include a curie link', () => {
243
+ const {
244
+ plugins: {
245
+ [PLUGIN]: plugin
246
+ }
247
+ } = server
248
+
239
249
  plugin.namespaces
240
250
  .add({ name: 'mycompany', prefix: 'mco' })
241
251
  .rel({ name: 'boss' })
@@ -249,6 +259,12 @@ describe('Representation Factory', () => {
249
259
  })
250
260
 
251
261
  it('should embed an entity', () => {
262
+ const {
263
+ plugins: {
264
+ [PLUGIN]: plugin
265
+ }
266
+ } = server
267
+
252
268
  plugin.namespaces
253
269
  .add({ name: 'mycompany', prefix: 'mco' })
254
270
  .rel({ name: 'boss' })
@@ -284,6 +300,12 @@ describe('Representation Factory', () => {
284
300
  })
285
301
 
286
302
  it('should embed an empty array', () => {
303
+ const {
304
+ plugins: {
305
+ [PLUGIN]: plugin
306
+ }
307
+ } = server
308
+
287
309
  plugin.namespaces
288
310
  .add({ name: 'mycompany', prefix: 'mco' })
289
311
  .rel({ name: 'boss' })
@@ -312,6 +334,12 @@ describe('Representation Factory', () => {
312
334
  })
313
335
 
314
336
  it('should use top level curie link', () => {
337
+ const {
338
+ plugins: {
339
+ [PLUGIN]: plugin
340
+ }
341
+ } = server
342
+
315
343
  plugin.namespaces
316
344
  .add({ name: 'mycompany', prefix: 'mco' })
317
345
  .rel({ name: 'boss' })