solid-server 5.7.11 → 5.8.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.
@@ -17,17 +17,30 @@ jobs:
17
17
 
18
18
  strategy:
19
19
  matrix:
20
- node-version: [18.x]
20
+ node-version: [ '>=20.17.0' ]
21
21
  os: [ubuntu-latest]
22
22
 
23
23
  steps:
24
- - uses: actions/checkout@v2
24
+ - uses: actions/checkout@v4
25
+
26
+ # extract repository name
27
+ - if: github.event_name == 'pull_request'
28
+ run: echo "REPO_NAME=${{ github.event.pull_request.head.repo.full_name }}" >> $GITHUB_ENV
29
+
30
+ - if: github.event_name != 'pull_request'
31
+ run: echo "REPO_NAME=${GITHUB_REPOSITORY}" >> $GITHUB_ENV
32
+
25
33
  # extract branch name
26
34
  - if: github.event_name == 'pull_request'
27
35
  run: echo "BRANCH_NAME=${GITHUB_HEAD_REF}" >> $GITHUB_ENV
36
+
28
37
  - if: github.event_name != 'pull_request'
29
38
  run: echo "BRANCH_NAME=${GITHUB_REF#refs/heads/}" >> $GITHUB_ENV
30
39
 
40
+ # print repository name
41
+ - name: Get repository name
42
+ run: echo 'The repository name is' $REPO_NAME
43
+
31
44
  # print branch name
32
45
  - name: Get branch name
33
46
  run: echo 'The branch name is' $BRANCH_NAME
@@ -40,12 +53,12 @@ jobs:
40
53
  # test code
41
54
  - run: npm run standard
42
55
  - run: npm run validate
43
- - run: npm run nyc
56
+ - run: npm run c8
44
57
  # Test global install of the package
45
58
  - run: npm pack .
46
59
  - run: npm install -g solid-server-*.tgz
47
60
  # Run the Solid test-suite
48
- - run: bash test/surface/run-solid-test-suite.sh $BRANCH_NAME
61
+ - run: bash test/surface/run-solid-test-suite.sh $BRANCH_NAME $REPO_NAME
49
62
 
50
63
  # TODO: The pipeline should automate publication to npm, so that the docker build gets the correct version
51
64
  # This job will only dockerize solid-server@latest / solid-server@<tag-name> from npmjs.com!
@@ -56,7 +69,7 @@ jobs:
56
69
  runs-on: ubuntu-latest
57
70
  steps:
58
71
 
59
- - uses: actions/checkout@v2
72
+ - uses: actions/checkout@v4
60
73
 
61
74
  - uses: olegtarasov/get-tag@v2.1
62
75
  id: tagName
package/README.md CHANGED
@@ -138,7 +138,7 @@ Your users will have a dedicated folder under `./data` at `./data/<username>.<yo
138
138
  > To use Gmail you may need to configure ["Allow Less Secure Apps"](https://www.google.com/settings/security/lesssecureapps) in your Gmail account unless you are using 2FA in which case you would have to create an [Application Specific](https://security.google.com/settings/security/apppasswords) password. You also may need to unlock your account with ["Allow access to your Google account"](https://accounts.google.com/DisplayUnlockCaptcha) to use SMTP.
139
139
 
140
140
  also add to `config.json`
141
- ```
141
+ ```
142
142
  "useEmail": true,
143
143
  "emailHost": "smtp.gmail.com",
144
144
  "emailPort": "465",
@@ -206,6 +206,7 @@ $ solid start --help
206
206
  --multiuser Enable multi-user mode
207
207
  --idp [value] Obsolete; use --multiuser
208
208
  --no-live Disable live support through WebSockets
209
+ --no-prep Disable Per Resource Events
209
210
  --proxy [value] Obsolete; use --corsProxy
210
211
  --cors-proxy [value] Serve the CORS proxy on this path
211
212
  --suppress-data-browser Suppress provision of a data browser
@@ -271,7 +272,7 @@ docker run -p 8443:8443 --name solid node-solid-server
271
272
 
272
273
 
273
274
  This will enable you to login to solid on https://localhost:8443 and then create a new account
274
- but not yet use that account. After a new account is made you will need to create an entry for
275
+ but not yet use that account. After a new account is made you will need to create an entry for
275
276
  it in your local (/etc/)hosts file in line with the account and subdomain, i.e. --
276
277
 
277
278
  ```pre
@@ -280,16 +281,16 @@ it in your local (/etc/)hosts file in line with the account and subdomain, i.e.
280
281
 
281
282
  You can modify the config within the docker container as follows:
282
283
 
283
- - Copy the `config.json` to the current directory with:
284
+ - Copy the `config.json` to the current directory with:
284
285
  ```bash
285
286
  docker cp solid:/usr/src/app/config.json .
286
287
  ```
287
288
  - Edit the `config.json` file
288
- - Copy the file back with
289
+ - Copy the file back with
289
290
  ```bash
290
291
  docker cp config.json solid:/usr/src/app/
291
292
  ```
292
- - Restart the server with
293
+ - Restart the server with
293
294
  ```bash
294
295
  docker restart solid
295
296
  ```
@@ -143,6 +143,12 @@ module.exports = [
143
143
  flag: true,
144
144
  default: false
145
145
  },
146
+ {
147
+ name: 'no-prep',
148
+ help: 'Disable Per Resource Events',
149
+ flag: true,
150
+ default: false
151
+ },
146
152
  // {
147
153
  // full: 'default-app',
148
154
  // help: 'URI to use as a default app for resources (default: https://linkeddata.github.io/warp/#/list/)'
package/bin/solid CHANGED
@@ -1,3 +1,3 @@
1
- #!/usr/bin/env node
1
+ #!/usr/bin/env -S node --experimental-require-module
2
2
  const startCli = require('./lib/cli')
3
3
  startCli()
package/bin/solid.js CHANGED
@@ -1,3 +1,3 @@
1
- #!/usr/bin/env node
1
+ #!/usr/bin/env -S node --experimental-require-module
2
2
  const startCli = require('./lib/cli')
3
3
  startCli()
package/lib/create-app.js CHANGED
@@ -28,6 +28,11 @@ const ResourceMapper = require('./resource-mapper')
28
28
  const aclCheck = require('@solid/acl-check')
29
29
  const { version } = require('../package.json')
30
30
 
31
+ const acceptEvents = require('express-accept-events').default
32
+ const events = require('express-negotiate-events').default
33
+ const eventID = require('express-prep/event-id').default
34
+ const prep = require('express-prep').default
35
+
31
36
  const corsSettings = cors({
32
37
  methods: [
33
38
  'OPTIONS', 'HEAD', 'GET', 'PATCH', 'POST', 'PUT', 'DELETE'
@@ -61,6 +66,12 @@ function createApp (argv = {}) {
61
66
 
62
67
  const app = express()
63
68
 
69
+ // Add PREP support
70
+ if (argv.prep) {
71
+ app.use(eventID)
72
+ app.use(acceptEvents, events, prep)
73
+ }
74
+
64
75
  initAppLocals(app, argv, ldp)
65
76
  initHeaders(app)
66
77
  initViews(app, configPath)
@@ -115,7 +126,7 @@ function createApp (argv = {}) {
115
126
  }
116
127
 
117
128
  // Attach the LDP middleware
118
- app.use('/', LdpMiddleware(corsSettings))
129
+ app.use('/', LdpMiddleware(corsSettings, argv.prep))
119
130
 
120
131
  // https://stackoverflow.com/questions/51741383/nodejs-express-return-405-for-un-supported-method
121
132
  app.use(function (req, res, next) {
@@ -168,6 +179,7 @@ function initAppLocals (app, argv, ldp) {
168
179
  app.locals.enforceToc = argv.enforceToc
169
180
  app.locals.tocUri = argv.tocUri
170
181
  app.locals.disablePasswordChecks = argv.disablePasswordChecks
182
+ app.locals.prep = argv.prep
171
183
 
172
184
  if (argv.email && argv.email.host) {
173
185
  app.locals.emailService = new EmailService(argv.templates.email, argv.email)
@@ -287,7 +299,7 @@ function initWebId (argv, app, ldp) {
287
299
  initAuthentication(app, argv)
288
300
 
289
301
  if (argv.multiuser) {
290
- app.use(vhost('*', LdpMiddleware(corsSettings)))
302
+ app.use(vhost('*', LdpMiddleware(corsSettings, argv.prep)))
291
303
  }
292
304
  }
293
305
 
package/lib/debug.js CHANGED
@@ -15,3 +15,4 @@ exports.accounts = debug('solid:accounts')
15
15
  exports.email = debug('solid:email')
16
16
  exports.ldp = debug('solid:ldp')
17
17
  exports.fs = debug('solid:fs')
18
+ exports.prep = debug('solid:prep')
@@ -6,9 +6,12 @@ async function handler (req, res, next) {
6
6
  debug('DELETE -- Request on' + req.originalUrl)
7
7
 
8
8
  const ldp = req.app.locals.ldp
9
+ const prep = req.app.locals.prep
9
10
  try {
10
11
  await ldp.delete(req)
11
12
  debug('DELETE -- Ok.')
13
+ // Add event-id for notifications
14
+ prep && res.setHeader('Event-ID', res.setEventID())
12
15
  res.sendStatus(200)
13
16
  next()
14
17
  } catch (err) {
@@ -17,9 +17,13 @@ const translate = require('../utils.js').translate
17
17
  const error = require('../http-error')
18
18
 
19
19
  const RDFs = require('../ldp').mimeTypesAsArray()
20
+ const isRdf = require('../ldp').mimeTypeIsRdf
21
+
22
+ const prepConfig = 'accept=("message/rfc822" "application/ld+json" "text/turtle")'
20
23
 
21
24
  async function handler (req, res, next) {
22
25
  const ldp = req.app.locals.ldp
26
+ const prep = req.app.locals.prep
23
27
  const includeBody = req.method === 'GET'
24
28
  const negotiator = new Negotiator(req)
25
29
  const baseUri = ldp.resourceMapper.resolveUrl(req.hostname, req.path)
@@ -103,7 +107,7 @@ async function handler (req, res, next) {
103
107
  debug(' sending data browser file: ' + dataBrowserPath)
104
108
  res.sendFile(dataBrowserPath)
105
109
  return
106
- } else if (stream) {
110
+ } else if (stream) { // EXIT text/html
107
111
  res.setHeader('Content-Type', contentType)
108
112
  return stream.pipe(res)
109
113
  }
@@ -111,14 +115,27 @@ async function handler (req, res, next) {
111
115
 
112
116
  // If request accepts the content-type we found
113
117
  if (stream && negotiator.mediaType([contentType])) {
114
- res.setHeader('Content-Type', contentType)
118
+ let headers = {
119
+ 'Content-Type': contentType
120
+ }
115
121
  if (contentRange) {
116
- const headers = { 'Content-Range': contentRange, 'Accept-Ranges': 'bytes', 'Content-Length': chunksize }
117
- res.writeHead(206, headers)
118
- return stream.pipe(res)
119
- } else {
120
- return stream.pipe(res)
122
+ headers = {
123
+ ...headers,
124
+ 'Content-Range': contentRange,
125
+ 'Accept-Ranges': 'bytes',
126
+ 'Content-Length': chunksize
127
+ }
128
+ res.statusCode = 206
121
129
  }
130
+
131
+ if (prep & isRdf(contentType) && !res.sendEvents({
132
+ config: { prep: prepConfig },
133
+ body: stream,
134
+ isBodyStream: true,
135
+ headers
136
+ })) return
137
+ res.set(headers)
138
+ return stream.pipe(res)
122
139
  }
123
140
 
124
141
  // If it is not in our RDFs we can't even translate,
@@ -130,6 +147,14 @@ async function handler (req, res, next) {
130
147
  // Translate from the contentType found to the possibleRDFType desired
131
148
  const data = await translate(stream, baseUri, contentType, possibleRDFType)
132
149
  debug(req.originalUrl + ' translating ' + contentType + ' -> ' + possibleRDFType)
150
+ const headers = {
151
+ 'Content-Type': possibleRDFType
152
+ }
153
+ if (prep && isRdf(contentType) && !res.sendEvents({
154
+ config: { prep: prepConfig },
155
+ body: data,
156
+ headers
157
+ })) return
133
158
  res.setHeader('Content-Type', possibleRDFType)
134
159
  res.send(data)
135
160
  return next()
@@ -0,0 +1,130 @@
1
+ module.exports = handler
2
+
3
+ const libPath = require('path/posix')
4
+
5
+ const headerTemplate = require('express-prep/templates').header
6
+ const solidRDFTemplate = require('../rdf-notification-template')
7
+ const debug = require('../debug').prep
8
+
9
+ const ALLOWED_RDF_MIME_TYPES = [
10
+ 'application/ld+json',
11
+ 'application/activity+json',
12
+ 'text/turtle'
13
+ ]
14
+
15
+ function getParent (path) {
16
+ if (path === '' || path === '/') return
17
+ const parent = libPath.dirname(path)
18
+ return parent === '/' ? '/' : `${parent}/`
19
+ }
20
+
21
+ function getActivity (method, path) {
22
+ if (method === 'DELETE') {
23
+ return 'Delete'
24
+ }
25
+ if (method === 'POST' && path.endsWith('/')) {
26
+ return 'Add'
27
+ }
28
+ return 'Update'
29
+ }
30
+
31
+ function getParentActivity (method, status) {
32
+ if (method === 'DELETE') {
33
+ return 'Remove'
34
+ }
35
+ if (status === 201) {
36
+ return 'Add'
37
+ }
38
+ return 'Update'
39
+ }
40
+
41
+ function handler (req, res, next) {
42
+ const { trigger, defaultNotification } = res.events.prep
43
+
44
+ const { method, path } = req
45
+ const { statusCode } = res
46
+ const eventID = res.getHeader('event-id')
47
+ const fullUrl = new URL(path, `${req.protocol}://${req.hostname}/`)
48
+
49
+ // Date is a hack since node does not seem to provide access to send date.
50
+ // Date needs to be shared with parent notification
51
+ const eventDate = res._header.match(/^Date: (.*?)$/m)?.[1] ||
52
+ new Date().toUTCString()
53
+
54
+ // If the resource itself newly created,
55
+ // it could not have been subscribed for notifications already
56
+ if (!((method === 'PUT' || method === 'PATCH') && statusCode === 201)) {
57
+ try {
58
+ trigger({
59
+ generateNotification (
60
+ negotiatedFields
61
+ ) {
62
+ const mediaType = negotiatedFields['content-type']
63
+ const activity = getActivity(method, path)
64
+ const target = activity === 'Add'
65
+ ? res.getHeader('location')
66
+ : undefined
67
+ if (ALLOWED_RDF_MIME_TYPES.includes(mediaType?.[0])) {
68
+ return `${headerTemplate(negotiatedFields)}\r\n${solidRDFTemplate({
69
+ activity,
70
+ eventID,
71
+ object: String(fullUrl),
72
+ target,
73
+ date: eventDate,
74
+ // We use eTag as a proxy for state for now
75
+ state: res.getHeader('ETag'),
76
+ mediaType
77
+ })}`
78
+ } else {
79
+ return defaultNotification()
80
+ }
81
+ }
82
+ })
83
+ } catch (error) {
84
+ debug(`Failed to trigger notification on route ${fullUrl}`)
85
+ // No special handling is necessary since the resource mutation was
86
+ // already successful. The purpose of this block is to prevent Express
87
+ // from triggering error handling middleware when notifications fail.
88
+ // An error notification might be sent in the future.
89
+ }
90
+ }
91
+
92
+ // Write a notification to parent container
93
+ // POST in Solid creates a child resource
94
+ const parent = getParent(path)
95
+ if (parent && method !== 'POST') {
96
+ const parentID = res.setEventID(parent)
97
+ const parentUrl = new URL(parent, fullUrl)
98
+ try {
99
+ trigger({
100
+ path: parent,
101
+ generateNotification (
102
+ negotiatedFields
103
+ ) {
104
+ const mediaType = negotiatedFields['content-type']
105
+ const activity = getParentActivity(method, statusCode)
106
+ const target = activity !== 'Update' ? String(fullUrl) : undefined
107
+ if (ALLOWED_RDF_MIME_TYPES.includes(mediaType?.[0])) {
108
+ return `${headerTemplate(negotiatedFields)}\r\n${solidRDFTemplate({
109
+ activity,
110
+ eventID: parentID,
111
+ date: eventDate,
112
+ object: String(parentUrl),
113
+ target,
114
+ eTag: undefined,
115
+ mediaType
116
+ })}`
117
+ }
118
+ }
119
+ })
120
+ } catch (error) {
121
+ debug(`Failed to trigger notification on parent route ${parentUrl}`)
122
+ // No special handling is necessary since the resource mutation was
123
+ // already successful. The purpose of this block is to prevent Express
124
+ // from triggering error handling middleware when notifications fail.
125
+ // An error notification might be sent in the future.
126
+ }
127
+ }
128
+
129
+ next()
130
+ }
@@ -39,6 +39,7 @@ function contentForNew (contentType) {
39
39
  // Handles a PATCH request
40
40
  async function patchHandler (req, res, next) {
41
41
  debug(`PATCH -- ${req.originalUrl}`)
42
+ const prep = req.app.locals.prep
42
43
  try {
43
44
  // Obtain details of the target resource
44
45
  const ldp = req.app.locals.ldp
@@ -91,7 +92,10 @@ async function patchHandler (req, res, next) {
91
92
  return writeGraph(graph, resource, ldp.resourceMapper.resolveFilePath(req.hostname), ldp.serverUri)
92
93
  })
93
94
 
94
- // Send the result to the client
95
+ // Add event-id for notifications
96
+ prep && res.setHeader('Event-ID', res.setEventID())
97
+ // Send the status and result to the client
98
+ res.status(resourceExists ? 200 : 201)
95
99
  res.send(result)
96
100
  } catch (err) {
97
101
  return next(err)
@@ -11,6 +11,7 @@ const getContentType = require('../utils').getContentType
11
11
 
12
12
  async function handler (req, res, next) {
13
13
  const ldp = req.app.locals.ldp
14
+ const prep = req.app.locals.prep
14
15
  const contentType = getContentType(req.headers)
15
16
  debug('content-type is ', contentType)
16
17
  // Handle SPARQL(-update?) query
@@ -72,6 +73,8 @@ async function handler (req, res, next) {
72
73
  // Handled by backpressure of streams!
73
74
  busboy.on('finish', function () {
74
75
  debug('Done storing files')
76
+ // Add event-id for notifications
77
+ prep && res.setHeader('Event-ID', res.setEventID())
75
78
  res.sendStatus(200)
76
79
  next()
77
80
  })
@@ -91,6 +94,8 @@ async function handler (req, res, next) {
91
94
  debug('File stored in ' + resourcePath)
92
95
  header.addLinks(res, links)
93
96
  res.set('Location', resourcePath)
97
+ // Add event-id for notifications
98
+ prep && res.setHeader('Event-ID', res.setEventID())
94
99
  res.sendStatus(201)
95
100
  next()
96
101
  },
@@ -59,6 +59,7 @@ async function checkPermission (request, resourceExists) {
59
59
  // TODO could be renamed as putResource (it now covers container and non-container)
60
60
  async function putStream (req, res, next, stream = req) {
61
61
  const ldp = req.app.locals.ldp
62
+ const prep = req.app.locals.prep
62
63
  // try {
63
64
  // Obtain details of the target resource
64
65
  let resourceExists = true
@@ -77,7 +78,9 @@ async function putStream (req, res, next, stream = req) {
77
78
  // Fails with Append on existing resource
78
79
  if (!req.originalUrl.endsWith('.acl')) await checkPermission(req, resourceExists)
79
80
  await ldp.put(req, stream, getContentType(req.headers))
80
- res.sendStatus(201)
81
+ // Add event-id for notifications
82
+ prep && res.setHeader('Event-ID', res.setEventID())
83
+ res.sendStatus(resourceExists ? 204 : 201)
81
84
  return next()
82
85
  } catch (err) {
83
86
  err.message = 'Can\'t write file/folder: ' + err.message
@@ -10,8 +10,9 @@ const del = require('./handlers/delete')
10
10
  const patch = require('./handlers/patch')
11
11
  const index = require('./handlers/index')
12
12
  const copy = require('./handlers/copy')
13
+ const notify = require('./handlers/notify')
13
14
 
14
- function LdpMiddleware (corsSettings) {
15
+ function LdpMiddleware (corsSettings, prep) {
15
16
  const router = express.Router('/')
16
17
 
17
18
  // Add Link headers
@@ -28,5 +29,12 @@ function LdpMiddleware (corsSettings) {
28
29
  router.put('/*', allow('Append'), put)
29
30
  router.delete('/*', allow('Write'), del)
30
31
 
32
+ if (prep) {
33
+ router.post('/*', notify)
34
+ router.patch('/*', notify)
35
+ router.put('/*', notify)
36
+ router.delete('/*', notify)
37
+ }
38
+
31
39
  return router
32
40
  }
package/lib/ldp.js CHANGED
@@ -242,16 +242,15 @@ class LDP {
242
242
  if (isOverQuota) {
243
243
  throw error(413, 'User has exceeded their storage quota')
244
244
  }
245
- // Set url using folder/.meta. This is Hack to find folder path
246
- if (container) {
247
- if (typeof url !== 'string') {
248
- url.url = url.url + suffixMeta
249
- } else {
250
- url = url + suffixMeta
251
- }
252
- contentType = 'text/turtle'
253
- }
254
- const { path } = await this.resourceMapper.mapUrlToFile({ url, contentType, createIfNotExists: true })
245
+ // Set url using folder/.meta
246
+ let { path } = await this.resourceMapper.mapUrlToFile({
247
+ url,
248
+ contentType,
249
+ createIfNotExists: true,
250
+ searchIndex: false
251
+ })
252
+
253
+ if (container) { path += suffixMeta }
255
254
  // debug.handlers(container + ' item ' + (url.url || url) + ' ' + contentType + ' ' + path)
256
255
  // check if file exists, and in that case that it has the same extension
257
256
  if (!container) { await this.checkFileExtension(url, path) }
@@ -264,7 +263,6 @@ class LDP {
264
263
  // clearAclCache()
265
264
  }
266
265
  // Directory created, now write the file
267
- if (container) return
268
266
  return withLock(path, () => new Promise((resolve, reject) => {
269
267
  // HACK: the middleware in webid-oidc.js uses body-parser, thus ending the stream of data
270
268
  // for JSON bodies. So, the stream needs to be reset
@@ -0,0 +1,66 @@
1
+ const CONTEXT_ACTIVITYSTREAMS = 'https://www.w3.org/ns/activitystreams'
2
+ const CONTEXT_NOTIFICATION = 'https://www.w3.org/ns/solid/notification/v1'
3
+ const CONTEXT_XML_SCHEMA = 'http://www.w3.org/2001/XMLSchema'
4
+
5
+ function generateJSONNotification ({
6
+ activity: type,
7
+ eventId: id,
8
+ date: published,
9
+ object,
10
+ target,
11
+ state = undefined
12
+ }) {
13
+ return {
14
+ published,
15
+ type,
16
+ id,
17
+ object,
18
+ ...(type === 'Add') && { target },
19
+ ...(type === 'Remove') && { origin: target },
20
+ ...(state) && { state }
21
+ }
22
+ }
23
+
24
+ function generateTurtleNotification ({
25
+ activity,
26
+ eventId,
27
+ date,
28
+ object,
29
+ target,
30
+ state = undefined
31
+ }) {
32
+ const stateLine = `\n notify:state "${state}" ;`
33
+
34
+ return `@prefix as: <${CONTEXT_ACTIVITYSTREAMS}#> .
35
+ @prefix notify: <${CONTEXT_NOTIFICATION}#> .
36
+ @prefix xsd: <${CONTEXT_XML_SCHEMA}#> .
37
+
38
+ <${eventId}> a as:${activity} ;${state && stateLine}
39
+ as:object ${object} ;
40
+ as:published "${date}"^^xsd:dateTime .`.replaceAll('\n', '\r\n')
41
+ }
42
+
43
+ function serializeToJSONLD (notification, isActivityStreams = false) {
44
+ notification['@context'] = [CONTEXT_NOTIFICATION]
45
+ if (!isActivityStreams) {
46
+ notification['@context'].unshift(CONTEXT_ACTIVITYSTREAMS)
47
+ }
48
+ return JSON.stringify(notification, null, 2)
49
+ }
50
+
51
+ function rdfTemplate (props) {
52
+ const { mediaType } = props
53
+ if (mediaType[0] === 'application/activity+json' || (mediaType[0] === 'application/ld+json' && mediaType[1].get('profile')?.toLowerCase() === 'https://www.w3.org/ns/activitystreams')) {
54
+ return serializeToJSONLD(generateJSONNotification(props), true)
55
+ }
56
+
57
+ if (mediaType[0] === 'application/ld+json') {
58
+ return serializeToJSONLD(generateJSONNotification(props))
59
+ }
60
+
61
+ if (mediaType[0] === 'text/turtle') {
62
+ return generateTurtleNotification(props)
63
+ }
64
+ }
65
+
66
+ module.exports = rdfTemplate
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "solid-server",
3
3
  "description": "Solid server on top of the file-system",
4
- "version": "5.7.11",
4
+ "version": "5.8.0",
5
5
  "author": {
6
6
  "name": "Tim Berners-Lee",
7
7
  "email": "timbl@w3.org"
@@ -74,7 +74,10 @@
74
74
  "cors": "^2.8.5",
75
75
  "debug": "^4.3.4",
76
76
  "express": "^4.18.3",
77
+ "express-accept-events": "^0.3.0",
77
78
  "express-handlebars": "^5.3.5",
79
+ "express-negotiate-events": "^0.3.0",
80
+ "express-prep": "^0.6.3",
78
81
  "express-session": "^1.18.0",
79
82
  "extend": "^3.0.2",
80
83
  "from2": "^2.3.0",
@@ -114,7 +117,9 @@
114
117
  "vhost": "^3.0.2"
115
118
  },
116
119
  "devDependencies": {
120
+ "@cxres/structured-headers": "^2.0.0-alpha.1-nesting.0",
117
121
  "@solid/solid-auth-oidc": "0.3.0",
122
+ "c8": "^10.1.2",
118
123
  "chai": "^4.4.1",
119
124
  "chai-as-promised": "7.1.1",
120
125
  "cross-env": "7.0.3",
@@ -124,8 +129,8 @@
124
129
  "mocha": "^10.3.0",
125
130
  "nock": "^13.5.4",
126
131
  "node-mocks-http": "^1.14.1",
127
- "nyc": "15.1.0",
128
132
  "pre-commit": "1.2.2",
133
+ "prep-fetch": "^0.1.0",
129
134
  "randombytes": "2.1.0",
130
135
  "sinon": "12.0.1",
131
136
  "sinon-chai": "3.7.0",
@@ -141,12 +146,12 @@
141
146
  "main": "index.js",
142
147
  "scripts": {
143
148
  "build": "echo nothing to build",
144
- "solid": "node ./bin/solid",
145
- "standard": "standard '{bin,examples,lib,test}/**/*.js'",
149
+ "solid": "node --experimental-require-module ./bin/solid",
150
+ "standard": "standard \"{bin,examples,lib,test}/**/*.js\"",
146
151
  "validate": "node ./test/validate-turtle.js",
147
- "nyc": "cross-env NODE_TLS_REJECT_UNAUTHORIZED=0 nyc --reporter=text-summary mocha --recursive test/integration/ test/unit/",
148
- "mocha": "cross-env NODE_TLS_REJECT_UNAUTHORIZED=0 mocha --recursive test/integration/ test/unit/",
149
- "mocha-integration": "cross-env NODE_TLS_REJECT_UNAUTHORIZED=0 mocha --recursive test/integration/http-test.js",
152
+ "c8": "cross-env NODE_TLS_REJECT_UNAUTHORIZED=0 c8 --reporter=text-summary mocha -n experimental-require-module --recursive test/integration/ test/unit/",
153
+ "mocha": "cross-env NODE_TLS_REJECT_UNAUTHORIZED=0 mocha -n experimental-require-module --recursive test/integration/ test/unit/",
154
+ "mocha-integration": "cross-env NODE_TLS_REJECT_UNAUTHORIZED=0 mocha -n experimental-require-module --recursive test/integration/http-test.js",
150
155
  "mocha-account-creation-oidc": "cross-env NODE_TLS_REJECT_UNAUTHORIZED=0 mocha --recursive test/integration/account-creation-oidc-test.js",
151
156
  "mocha-account-manager": "cross-env NODE_TLS_REJECT_UNAUTHORIZED=0 mocha --recursive test/integration/account-manager-test.js",
152
157
  "mocha-account-template": "cross-env NODE_TLS_REJECT_UNAUTHORIZED=0 mocha --recursive test/integration/account-template-test.js",
@@ -156,11 +161,11 @@
156
161
  "mocha-ldp": "cross-env NODE_TLS_REJECT_UNAUTHORIZED=0 mocha --recursive test/integration/ldp-test.js",
157
162
  "prepublishOnly": "npm test",
158
163
  "postpublish": "git push --follow-tags",
159
- "test": "npm run standard && npm run validate && npm run nyc",
164
+ "test": "npm run standard && npm run validate && npm run c8",
160
165
  "clean": "rimraf config/templates config/views",
161
166
  "reset": "rimraf .db data && npm run clean"
162
167
  },
163
- "nyc": {
168
+ "c8": {
164
169
  "reporter": [
165
170
  "html",
166
171
  "text-summary"
@@ -174,13 +179,15 @@
174
179
  "before",
175
180
  "beforeEach",
176
181
  "describe",
177
- "it"
182
+ "it",
183
+ "fetch",
184
+ "AbortController"
178
185
  ]
179
186
  },
180
187
  "bin": {
181
188
  "solid": "bin/solid"
182
189
  },
183
190
  "engines": {
184
- "node": ">=12.0"
191
+ "node": ">=20.17.0 <21 || >=22.8.0"
185
192
  }
186
193
  }