11/**
22 * NanopubBuilder - Builds nanopubs from complete template structure
3- * FINAL VERSION - properly handles empty instances and optional fields
4- * FIXED: Handles nt:CREATOR placeholder
3+ * properly handles empty instances and optional fields
4+ * Handles nt:CREATOR placeholder
55 */
66
77export class NanopubBuilder {
@@ -23,6 +23,9 @@ export class NanopubBuilder {
2323 const tempId = this . generateRandomId ( ) ;
2424 const baseUri = `http://purl.org/nanopub/temp/${ tempId } ` ;
2525
26+ // Store base URI for IntroducedResource resolution
27+ this . currentNanopubBaseUri = baseUri ;
28+
2629 const prefixes = this . buildPrefixes ( tempId ) ;
2730 const head = this . buildHead ( ) ;
2831 const assertion = this . buildAssertion ( ) ;
@@ -154,24 +157,32 @@ ${triples.join('\n')}
154157 object : this . formData [ `${ stmt . id } _object${ suffix } ` ]
155158 } ;
156159
157- // If subject is a placeholder but not in form data, try to find it
160+ // Check if subject/object are IntroducedResource or LocalResource
161+ const subjectPlaceholder = this . template . placeholders ?. find ( p => p . id === stmt . subject ) ;
162+ const objectPlaceholder = this . template . placeholders ?. find ( p => p . id === stmt . object ) ;
163+
164+ const subjectIsResource = subjectPlaceholder &&
165+ ( subjectPlaceholder . isIntroducedResource || subjectPlaceholder . isLocalResource ) ;
166+ const objectIsResource = objectPlaceholder &&
167+ ( objectPlaceholder . isIntroducedResource || objectPlaceholder . isLocalResource ) ;
168+
169+ // If subject is IntroducedResource/LocalResource and we don't have form data for it,
170+ // look for it in other statements (shared resource across multiple statements)
158171 if ( ! data . subject && stmt . subjectIsPlaceholder ) {
159172 const subjectId = stmt . subject ;
160- for ( const otherStmt of this . template . statements ) {
161- if ( otherStmt . subjectIsPlaceholder && otherStmt . subject === subjectId ) {
162- const val = this . formData [ `${ otherStmt . id } _subject` ] ;
163- if ( val ) {
164- data . subject = val ;
165- break ;
166- }
167- }
168- if ( otherStmt . objectIsPlaceholder && otherStmt . object === subjectId ) {
169- const val = this . formData [ `${ otherStmt . id } _object` ] ;
170- if ( val ) {
171- data . subject = val ;
172- break ;
173- }
174- }
173+ const foundValue = this . findPlaceholderValue ( subjectId ) ;
174+ if ( foundValue ) {
175+ data . subject = foundValue ;
176+ }
177+ }
178+
179+ // If object is IntroducedResource/LocalResource and we don't have form data for it,
180+ // look for it in other statements
181+ if ( ! data . object && stmt . objectIsPlaceholder ) {
182+ const objectId = stmt . object ;
183+ const foundValue = this . findPlaceholderValue ( objectId ) ;
184+ if ( foundValue ) {
185+ data . object = foundValue ;
175186 }
176187 }
177188
@@ -180,12 +191,14 @@ ${triples.join('\n')}
180191 const hasPredicate = data . predicate && data . predicate !== '' ;
181192 const hasObject = data . object && data . object !== '' ;
182193
183- // For optional statements, skip if no object data
184- if ( stmt . optional && ! hasObject ) {
194+ // For optional statements, skip if no object data (unless object is a resource type)
195+ if ( stmt . optional && ! hasObject && ! objectIsResource ) {
185196 return null ;
186197 }
187198
188- // For non-optional, we need data for editable fields
199+ // For non-optional statements:
200+ // - If both subject and object placeholders are empty, skip
201+ // - Otherwise, proceed
189202 if ( ! stmt . optional ) {
190203 if ( stmt . objectIsPlaceholder && ! hasObject ) {
191204 return null ;
@@ -282,6 +295,13 @@ ${triples.join('\n')}
282295 // Get placeholder metadata
283296 const placeholder = this . template . placeholders ?. find ( p => p . id === placeholderId ) ;
284297
298+ // Handle IntroducedResource/LocalResource - these create new URIs based on current nanopub
299+ if ( placeholder && ( placeholder . isIntroducedResource || placeholder . isLocalResource ) ) {
300+ // Use the current nanopub's base URI + the user's input as suffix
301+ const baseUri = this . currentNanopubBaseUri || 'http://purl.org/nanopub/temp/unknown' ;
302+ return `<${ baseUri } /${ value } >` ;
303+ }
304+
285305 // Handle AutoEscapeUriPlaceholder
286306 if ( placeholder ?. type === 'AutoEscapeUriPlaceholder' && placeholder . prefix ) {
287307 const encoded = encodeURIComponent ( value ) . replace ( / % 2 0 / g, '+' ) ;
@@ -341,17 +361,22 @@ ${triples.join('\n')}
341361 }
342362
343363 // Add npx:introduces for introduced resources
364+ const introducedUris = [ ] ;
344365 for ( const placeholder of this . template . placeholders || [ ] ) {
345- if ( placeholder . isIntroducedResource && placeholder . prefix ) {
366+ if ( placeholder . isIntroducedResource ) {
346367 const value = this . findPlaceholderValue ( placeholder . id ) ;
347368 if ( value ) {
348- const encoded = encodeURIComponent ( value ) . replace ( / % 2 0 / g, '+' ) ;
349- lines . push ( `;
350- npx:introduces <${ placeholder . prefix } ${ encoded } >` ) ;
369+ const uri = `${ this . currentNanopubBaseUri } /${ value } ` ;
370+ introducedUris . push ( `<${ uri } >` ) ;
351371 }
352372 }
353373 }
354374
375+ if ( introducedUris . length > 0 ) {
376+ lines . push ( `;
377+ npx:introduces ${ introducedUris . join ( ', ' ) } ` ) ;
378+ }
379+
355380 // Add label
356381 if ( this . template . labelPattern ) {
357382 const label = this . generateLabel ( ) ;
@@ -376,7 +401,9 @@ ${lines.join('\n')}
376401 * Find value for a placeholder in form data
377402 */
378403 findPlaceholderValue ( placeholderId ) {
404+ // Check all statements for this placeholder in either subject or object position
379405 for ( const stmt of this . template . statements || [ ] ) {
406+ // Check if placeholder is the subject
380407 if ( stmt . subject === placeholderId || stmt . subject === `sub:${ placeholderId } ` ) {
381408 const value = this . formData [ `${ stmt . id } _subject` ] ;
382409 if ( value ) return value ;
@@ -389,6 +416,7 @@ ${lines.join('\n')}
389416 }
390417 }
391418
419+ // Check if placeholder is the object
392420 if ( stmt . object === placeholderId || stmt . object === `sub:${ placeholderId } ` ) {
393421 const value = this . formData [ `${ stmt . id } _object` ] ;
394422 if ( value ) return value ;
0 commit comments