solid-server 5.8.2 → 5.8.3

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,7 +17,7 @@ jobs:
17
17
 
18
18
  strategy:
19
19
  matrix:
20
- node-version: [ '>=20.17.0' ]
20
+ node-version: [ '^20.17.0' ]
21
21
  os: [ubuntu-latest]
22
22
 
23
23
  steps:
@@ -6,12 +6,9 @@ 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
10
9
  try {
11
10
  await ldp.delete(req)
12
11
  debug('DELETE -- Ok.')
13
- // Add event-id for notifications
14
- prep && res.setHeader('Event-ID', res.setEventID())
15
12
  res.sendStatus(200)
16
13
  next()
17
14
  } catch (err) {
@@ -38,18 +38,32 @@ function getParentActivity (method, status) {
38
38
  return 'Update'
39
39
  }
40
40
 
41
+ function filterMillseconds (isoDate) {
42
+ return `${isoDate.substring(0, 19)}${isoDate.substring(23)}`
43
+ }
44
+
45
+ function getDate (date) {
46
+ if (date) {
47
+ const eventDate = new Date(date)
48
+ if (!isNaN(eventDate.valueOf())) {
49
+ return filterMillseconds(eventDate.toISOString())
50
+ }
51
+ }
52
+ const now = new Date()
53
+ return filterMillseconds(now.toISOString())
54
+ }
55
+
41
56
  function handler (req, res, next) {
42
57
  const { trigger, defaultNotification } = res.events.prep
43
58
 
44
59
  const { method, path } = req
45
60
  const { statusCode } = res
46
- const eventID = res.getHeader('event-id')
61
+ const eventID = res.setEventID()
47
62
  const fullUrl = new URL(path, `${req.protocol}://${req.hostname}/`)
48
63
 
49
64
  // Date is a hack since node does not seem to provide access to send date.
50
65
  // Date needs to be shared with parent notification
51
- const eventDate = res._header.match(/^Date: (.*?)$/m)?.[1] ||
52
- new Date().toUTCString()
66
+ const eventDate = getDate(res._header.match(/^Date: (.*?)$/m)?.[1])
53
67
 
54
68
  // If the resource itself newly created,
55
69
  // it could not have been subscribed for notifications already
@@ -61,18 +75,19 @@ function handler (req, res, next) {
61
75
  ) {
62
76
  const mediaType = negotiatedFields['content-type']
63
77
  const activity = getActivity(method, path)
64
- const target = activity === 'Add'
78
+ const object = activity === 'Add'
65
79
  ? res.getHeader('location')
80
+ : String(fullUrl)
81
+ const target = activity === 'Add'
82
+ ? String(fullUrl)
66
83
  : undefined
67
84
  if (ALLOWED_RDF_MIME_TYPES.includes(mediaType?.[0])) {
68
85
  return `${headerTemplate(negotiatedFields)}\r\n${solidRDFTemplate({
69
86
  activity,
70
87
  eventID,
71
- object: String(fullUrl),
88
+ object,
72
89
  target,
73
90
  date: eventDate,
74
- // We use eTag as a proxy for state for now
75
- state: res.getHeader('ETag'),
76
91
  mediaType
77
92
  })}`
78
93
  } else {
@@ -93,7 +108,10 @@ function handler (req, res, next) {
93
108
  // POST in Solid creates a child resource
94
109
  const parent = getParent(path)
95
110
  if (parent && method !== 'POST') {
96
- const parentID = res.setEventID(parent)
111
+ res.setEventID({
112
+ path: parent,
113
+ id: eventID
114
+ })
97
115
  const parentUrl = new URL(parent, fullUrl)
98
116
  try {
99
117
  trigger({
@@ -103,15 +121,15 @@ function handler (req, res, next) {
103
121
  ) {
104
122
  const mediaType = negotiatedFields['content-type']
105
123
  const activity = getParentActivity(method, statusCode)
106
- const target = activity !== 'Update' ? String(fullUrl) : undefined
124
+ const object = activity === 'Update' ? String(parentUrl) : String(fullUrl)
125
+ const target = activity === 'Update' ? undefined : String(parentUrl)
107
126
  if (ALLOWED_RDF_MIME_TYPES.includes(mediaType?.[0])) {
108
127
  return `${headerTemplate(negotiatedFields)}\r\n${solidRDFTemplate({
109
128
  activity,
110
- eventID: parentID,
129
+ eventID,
111
130
  date: eventDate,
112
- object: String(parentUrl),
131
+ object,
113
132
  target,
114
- eTag: undefined,
115
133
  mediaType
116
134
  })}`
117
135
  }
@@ -39,7 +39,6 @@ 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
43
42
  try {
44
43
  // Obtain details of the target resource
45
44
  const ldp = req.app.locals.ldp
@@ -91,9 +90,6 @@ async function patchHandler (req, res, next) {
91
90
  await applyPatch(patchObject, graph, url)
92
91
  return writeGraph(graph, resource, ldp.resourceMapper.resolveFilePath(req.hostname), ldp.serverUri)
93
92
  })
94
-
95
- // Add event-id for notifications
96
- prep && res.setHeader('Event-ID', res.setEventID())
97
93
  // Send the status and result to the client
98
94
  res.status(resourceExists ? 200 : 201)
99
95
  res.send(result)
@@ -11,7 +11,6 @@ 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
15
14
  const contentType = getContentType(req.headers)
16
15
  debug('content-type is ', contentType)
17
16
  // Handle SPARQL(-update?) query
@@ -73,8 +72,6 @@ async function handler (req, res, next) {
73
72
  // Handled by backpressure of streams!
74
73
  busboy.on('finish', function () {
75
74
  debug('Done storing files')
76
- // Add event-id for notifications
77
- prep && res.setHeader('Event-ID', res.setEventID())
78
75
  res.sendStatus(200)
79
76
  next()
80
77
  })
@@ -94,8 +91,6 @@ async function handler (req, res, next) {
94
91
  debug('File stored in ' + resourcePath)
95
92
  header.addLinks(res, links)
96
93
  res.set('Location', resourcePath)
97
- // Add event-id for notifications
98
- prep && res.setHeader('Event-ID', res.setEventID())
99
94
  res.sendStatus(201)
100
95
  next()
101
96
  },
@@ -59,7 +59,6 @@ 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
63
62
  // try {
64
63
  // Obtain details of the target resource
65
64
  let resourceExists = true
@@ -78,8 +77,6 @@ async function putStream (req, res, next, stream = req) {
78
77
  // Fails with Append on existing resource
79
78
  if (!req.originalUrl.endsWith('.acl')) await checkPermission(req, resourceExists)
80
79
  await ldp.put(req, stream, getContentType(req.headers))
81
- // Add event-id for notifications
82
- prep && res.setHeader('Event-ID', res.setEventID())
83
80
  res.sendStatus(resourceExists ? 204 : 201)
84
81
  return next()
85
82
  } catch (err) {
@@ -1,10 +1,12 @@
1
+ const uuid = require('uuid')
2
+
1
3
  const CONTEXT_ACTIVITYSTREAMS = 'https://www.w3.org/ns/activitystreams'
2
4
  const CONTEXT_NOTIFICATION = 'https://www.w3.org/ns/solid/notification/v1'
3
5
  const CONTEXT_XML_SCHEMA = 'http://www.w3.org/2001/XMLSchema'
4
6
 
5
7
  function generateJSONNotification ({
6
8
  activity: type,
7
- eventId: id,
9
+ eventID,
8
10
  date: published,
9
11
  object,
10
12
  target,
@@ -13,30 +15,40 @@ function generateJSONNotification ({
13
15
  return {
14
16
  published,
15
17
  type,
16
- id,
18
+ id: `urn:uuid:${uuid.v4()}`,
19
+ ...(eventID) && { state: eventID },
17
20
  object,
18
21
  ...(type === 'Add') && { target },
19
- ...(type === 'Remove') && { origin: target },
20
- ...(state) && { state }
22
+ ...(type === 'Remove') && { origin: target }
21
23
  }
22
24
  }
23
25
 
24
26
  function generateTurtleNotification ({
25
27
  activity,
26
- eventId,
28
+ eventID,
27
29
  date,
28
30
  object,
29
- target,
30
- state = undefined
31
+ target
31
32
  }) {
32
- const stateLine = `\n notify:state "${state}" ;`
33
+ let targetLine = ''
34
+ let stateLine = ''
35
+
36
+ if (activity === 'Add') {
37
+ targetLine = `\n as:target <${target}> ;`
38
+ }
39
+ if (activity === 'Remove') {
40
+ targetLine = `\n as:origin <${target}> ;`
41
+ }
42
+ if (eventID) {
43
+ stateLine = `\n notify:state "${eventID}" ;`
44
+ }
33
45
 
34
46
  return `@prefix as: <${CONTEXT_ACTIVITYSTREAMS}#> .
35
47
  @prefix notify: <${CONTEXT_NOTIFICATION}#> .
36
48
  @prefix xsd: <${CONTEXT_XML_SCHEMA}#> .
37
49
 
38
- <${eventId}> a as:${activity} ;${state && stateLine}
39
- as:object ${object} ;
50
+ <urn:uuid:${uuid.v4()}> a as:${activity} ;
51
+ as:object <${object}> ;${targetLine}${stateLine}
40
52
  as:published "${date}"^^xsd:dateTime .`.replaceAll('\n', '\r\n')
41
53
  }
42
54
 
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.8.2",
4
+ "version": "5.8.3",
5
5
  "author": {
6
6
  "name": "Tim Berners-Lee",
7
7
  "email": "timbl@w3.org"
@@ -77,7 +77,7 @@
77
77
  "express-accept-events": "^0.3.0",
78
78
  "express-handlebars": "^5.3.5",
79
79
  "express-negotiate-events": "^0.3.0",
80
- "express-prep": "^0.6.3",
80
+ "express-prep": "^0.6.4",
81
81
  "express-session": "^1.18.0",
82
82
  "extend": "^3.0.2",
83
83
  "from2": "^2.3.0",
@@ -92,7 +92,7 @@
92
92
  "ip-range-check": "0.2.0",
93
93
  "is-ip": "^3.1.0",
94
94
  "li": "^1.3.0",
95
- "mashlib": "^1.8.11",
95
+ "mashlib": "^1.10.2",
96
96
  "mime-types": "^2.1.35",
97
97
  "negotiator": "^0.6.3",
98
98
  "node-fetch": "^2.7.0",