@@ -533,10 +533,86 @@ export async function handlePatch(request, reply) {
533533 } ) ;
534534 }
535535 } else {
536- // Parse as plain JSON-LD
536+ // Try to parse as JSON-LD first
537537 try {
538538 document = JSON . parse ( contentStr ) ;
539539 } catch ( e ) {
540+ // Not JSON - might be Turtle, handle with RDF store for SPARQL Update
541+ if ( isSparqlUpdate ) {
542+ // Parse Turtle and apply SPARQL Update directly
543+ const { Parser, Writer } = await import ( 'n3' ) ;
544+ const parser = new Parser ( { baseIRI : resourceUrl } ) ;
545+ let quads ;
546+ try {
547+ quads = parser . parse ( contentStr ) ;
548+ } catch ( parseErr ) {
549+ return reply . code ( 409 ) . send ( {
550+ error : 'Conflict' ,
551+ message : 'Resource is not valid Turtle: ' + parseErr . message
552+ } ) ;
553+ }
554+
555+ // Parse the SPARQL Update
556+ const patchContent = Buffer . isBuffer ( request . body ) ? request . body . toString ( ) : request . body ;
557+ let update ;
558+ try {
559+ update = parseSparqlUpdate ( patchContent , resourceUrl ) ;
560+ } catch ( parseErr ) {
561+ return reply . code ( 400 ) . send ( {
562+ error : 'Bad Request' ,
563+ message : 'Invalid SPARQL Update: ' + parseErr . message
564+ } ) ;
565+ }
566+
567+ // Apply deletes
568+ for ( const triple of update . deletes ) {
569+ quads = quads . filter ( q => {
570+ const matches = q . subject . value === triple . subject &&
571+ q . predicate . value === triple . predicate &&
572+ ( q . object . value === ( triple . object [ '@id' ] || triple . object [ '@value' ] || triple . object ) ) ;
573+ return ! matches ;
574+ } ) ;
575+ }
576+
577+ // Apply inserts
578+ const { DataFactory } = await import ( 'n3' ) ;
579+ const { namedNode, literal } = DataFactory ;
580+ for ( const triple of update . inserts ) {
581+ const subj = namedNode ( triple . subject ) ;
582+ const pred = namedNode ( triple . predicate ) ;
583+ let obj ;
584+ if ( triple . object [ '@id' ] ) {
585+ obj = namedNode ( triple . object [ '@id' ] ) ;
586+ } else if ( typeof triple . object === 'string' ) {
587+ obj = literal ( triple . object ) ;
588+ } else {
589+ obj = literal ( triple . object [ '@value' ] || triple . object ) ;
590+ }
591+ quads . push ( DataFactory . quad ( subj , pred , obj ) ) ;
592+ }
593+
594+ // Serialize back to Turtle
595+ const writer = new Writer ( { prefixes : { } } ) ;
596+ quads . forEach ( q => writer . addQuad ( q ) ) ;
597+ let turtleOutput ;
598+ writer . end ( ( err , result ) => { turtleOutput = result ; } ) ;
599+
600+ const success = await storage . write ( storagePath , Buffer . from ( turtleOutput ) ) ;
601+ if ( ! success ) {
602+ return reply . code ( 500 ) . send ( { error : 'Write failed' } ) ;
603+ }
604+
605+ const origin = request . headers . origin ;
606+ const headers = getAllHeaders ( { isContainer : false , origin, resourceUrl } ) ;
607+ Object . entries ( headers ) . forEach ( ( [ k , v ] ) => reply . header ( k , v ) ) ;
608+
609+ if ( request . notificationsEnabled ) {
610+ emitChange ( resourceUrl ) ;
611+ }
612+
613+ return reply . code ( resourceExists ? 204 : 201 ) . send ( ) ;
614+ }
615+
540616 return reply . code ( 409 ) . send ( {
541617 error : 'Conflict' ,
542618 message : 'Resource is not valid JSON-LD and cannot be patched'
0 commit comments