javascript-solid-server 0.0.109 → 0.0.110

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "javascript-solid-server",
3
- "version": "0.0.109",
3
+ "version": "0.0.110",
4
4
  "description": "A minimal, fast Solid server",
5
5
  "main": "src/index.js",
6
6
  "type": "module",
@@ -57,6 +57,7 @@ export function parseN3Patch(patchText, baseUri) {
57
57
 
58
58
  /**
59
59
  * Parse triples from N3 block content
60
+ * Handles Turtle semicolon shorthand (same subject, different predicate-object)
60
61
  */
61
62
  function parseTriples(content, prefixes, baseUri) {
62
63
  const triples = [];
@@ -68,11 +69,37 @@ function parseTriples(content, prefixes, baseUri) {
68
69
  // Split by '.' but be careful with strings containing '.'
69
70
  const statements = splitStatements(content);
70
71
 
72
+ let lastSubject = null;
71
73
  for (const stmt of statements) {
72
- const triple = parseStatement(stmt.trim(), prefixes, baseUri);
73
- if (triple) {
74
- triples.push(triple);
74
+ const trimmed = stmt.trim();
75
+ if (!trimmed) continue;
76
+
77
+ const tokens = tokenize(trimmed);
78
+ if (tokens.length < 2) continue;
79
+
80
+ let subject, predicate, object;
81
+
82
+ if (tokens.length >= 3) {
83
+ // Full triple: subject predicate object
84
+ subject = resolveValue(tokens[0], prefixes, baseUri);
85
+ predicate = resolveValue(tokens[1], prefixes, baseUri);
86
+ object = resolveValue(tokens.slice(2).join(' '), prefixes, baseUri);
87
+ lastSubject = subject;
88
+ } else if (tokens.length === 2 && lastSubject) {
89
+ // Semicolon continuation: predicate object (reuse last subject)
90
+ subject = lastSubject;
91
+ predicate = resolveValue(tokens[0], prefixes, baseUri);
92
+ object = resolveValue(tokens[1], prefixes, baseUri);
93
+ } else {
94
+ continue;
95
+ }
96
+
97
+ // Handle 'a' as rdf:type
98
+ if (predicate === 'a') {
99
+ predicate = 'http://www.w3.org/1999/02/22-rdf-syntax-ns#type';
75
100
  }
101
+
102
+ triples.push({ subject, predicate, object });
76
103
  }
77
104
 
78
105
  return triples;
@@ -86,11 +113,18 @@ function splitStatements(content) {
86
113
  let current = '';
87
114
  let inString = false;
88
115
  let stringChar = null;
116
+ let inIri = false;
89
117
 
90
118
  for (let i = 0; i < content.length; i++) {
91
119
  const char = content[i];
92
120
 
93
- if (!inString && (char === '"' || char === "'")) {
121
+ if (!inString && !inIri && char === '<') {
122
+ inIri = true;
123
+ current += char;
124
+ } else if (inIri && char === '>') {
125
+ inIri = false;
126
+ current += char;
127
+ } else if (!inIri && !inString && (char === '"' || char === "'")) {
94
128
  inString = true;
95
129
  stringChar = char;
96
130
  current += char;
@@ -98,12 +132,12 @@ function splitStatements(content) {
98
132
  inString = false;
99
133
  stringChar = null;
100
134
  current += char;
101
- } else if (!inString && char === '.') {
135
+ } else if (!inString && !inIri && char === '.') {
102
136
  if (current.trim()) {
103
137
  statements.push(current);
104
138
  }
105
139
  current = '';
106
- } else if (!inString && char === ';') {
140
+ } else if (!inString && !inIri && char === ';') {
107
141
  // Turtle shorthand - same subject, different predicate
108
142
  if (current.trim()) {
109
143
  statements.push(current);
@@ -121,23 +155,6 @@ function splitStatements(content) {
121
155
  return statements;
122
156
  }
123
157
 
124
- /**
125
- * Parse a single N3 statement into a triple
126
- */
127
- function parseStatement(stmt, prefixes, baseUri) {
128
- if (!stmt) return null;
129
-
130
- // Tokenize - split by whitespace but respect quotes
131
- const tokens = tokenize(stmt);
132
- if (tokens.length < 3) return null;
133
-
134
- const subject = resolveValue(tokens[0], prefixes, baseUri);
135
- const predicate = resolveValue(tokens[1], prefixes, baseUri);
136
- const object = resolveValue(tokens.slice(2).join(' '), prefixes, baseUri);
137
-
138
- return { subject, predicate, object };
139
- }
140
-
141
158
  /**
142
159
  * Tokenize a statement respecting quoted strings
143
160
  */
@@ -185,6 +185,67 @@ describe('PATCH Operations', () => {
185
185
  assert.ok(data['@graph'], 'Should have @graph');
186
186
  assert.strictEqual(data['@graph'].length, 2, 'Should have 2 nodes');
187
187
  });
188
+
189
+ it('should handle semicolon shorthand and rdf:type "a" keyword', async () => {
190
+ // Create initial resource with @graph
191
+ const initial = {
192
+ '@context': { 'solid': 'http://www.w3.org/ns/solid/terms#' },
193
+ '@graph': []
194
+ };
195
+
196
+ await request('/patchtest/public/patch-semicolon.json', {
197
+ method: 'PUT',
198
+ headers: { 'Content-Type': 'application/ld+json' },
199
+ body: JSON.stringify(initial),
200
+ auth: 'patchtest'
201
+ });
202
+
203
+ // Use semicolons and 'a' keyword (Turtle shorthand)
204
+ const patch = `
205
+ @prefix solid: <http://www.w3.org/ns/solid/terms#>.
206
+ @prefix wf: <http://www.w3.org/2005/01/wf/flow#>.
207
+ _:patch a solid:InsertDeletePatch;
208
+ solid:inserts {
209
+ <#reg1> a solid:TypeRegistration;
210
+ solid:forClass wf:Tracker;
211
+ solid:instance <https://example.com/todo/data.jsonld#this>.
212
+ }.
213
+ `;
214
+
215
+ const res = await request('/patchtest/public/patch-semicolon.json', {
216
+ method: 'PATCH',
217
+ headers: { 'Content-Type': 'text/n3' },
218
+ body: patch,
219
+ auth: 'patchtest'
220
+ });
221
+
222
+ assertStatus(res, 204);
223
+
224
+ // Verify all three triples were inserted
225
+ const verify = await request('/patchtest/public/patch-semicolon.json');
226
+ const data = await verify.json();
227
+ const node = data['@graph'].find(n => n['@id'] && n['@id'].includes('#reg1'));
228
+ assert.ok(node, 'Should have the reg1 node');
229
+
230
+ // Check rdf:type value (from 'a' keyword)
231
+ const rdfType = node['rdf:type'] || node['http://www.w3.org/1999/02/22-rdf-syntax-ns#type'];
232
+ assert.ok(rdfType, 'Should have rdf:type (from "a" keyword)');
233
+ const typeId = rdfType['@id'] || rdfType;
234
+ assert.ok(String(typeId).includes('TypeRegistration'), `rdf:type should be TypeRegistration, got ${typeId}`);
235
+
236
+ // Check solid:forClass value
237
+ const forClass = node['solid:forClass'];
238
+ assert.ok(forClass, 'Should have solid:forClass');
239
+ const forClassId = forClass['@id'] || forClass;
240
+ assert.ok(String(forClassId).includes('Tracker'), `solid:forClass should be Tracker, got ${forClassId}`);
241
+
242
+ // Check solid:instance value (contains a dot in the IRI - tests IRI splitting)
243
+ const instance = node['solid:instance'];
244
+ assert.ok(instance, 'Should have solid:instance');
245
+ const instanceId = instance['@id'] || instance;
246
+ assert.strictEqual(instanceId, 'https://example.com/todo/data.jsonld#this',
247
+ 'solid:instance should have full IRI preserved');
248
+ });
188
249
  });
189
250
 
190
251
  describe('PATCH Error Handling', () => {