1- /* eslint-disable @typescript-eslint/naming-convention */
21import type { TFile } from "obsidian" ;
3- import type { DiscourseNode } from "~/types" ;
2+ import type {
3+ DiscourseNode ,
4+ DiscourseRelation ,
5+ DiscourseRelationType ,
6+ RelationInstance ,
7+ } from "~/types" ;
48import type { SupabaseContext } from "./supabaseContext" ;
9+ import type { DiscourseNodeInVault } from "./getDiscourseNodes" ;
510import type { LocalConceptDataInput } from "@repo/database/inputTypes" ;
611import type { ObsidianDiscourseNodeData } from "./syncDgNodesToSupabase" ;
712import type { Json } from "@repo/database/dbTypes" ;
8- import DiscourseGraphPlugin from ".." ;
913
1014/**
1115 * Get extra data (author, timestamps) from file metadata
@@ -14,6 +18,7 @@ const getNodeExtraData = (
1418 file : TFile ,
1519 accountLocalId : string ,
1620) : {
21+ /* eslint-disable @typescript-eslint/naming-convention */
1722 author_local_id : string ;
1823 created : string ;
1924 last_modified : string ;
@@ -23,6 +28,7 @@ const getNodeExtraData = (
2328 created : new Date ( file . stat . ctime ) . toISOString ( ) ,
2429 last_modified : new Date ( file . stat . mtime ) . toISOString ( ) ,
2530 } ;
31+ /* eslint-enable @typescript-eslint/naming-convention */
2632} ;
2733
2834export const discourseNodeSchemaToLocalConcept = ( {
@@ -34,8 +40,23 @@ export const discourseNodeSchemaToLocalConcept = ({
3440 node : DiscourseNode ;
3541 accountLocalId : string ;
3642} ) : LocalConceptDataInput => {
37- const { description, template, id, name, created, modified, ...otherData } =
38- node ;
43+ const {
44+ description,
45+ template,
46+ id,
47+ name,
48+ created,
49+ modified,
50+ importedFromRid,
51+ ...otherData
52+ } = node ;
53+ /* eslint-disable @typescript-eslint/naming-convention */
54+ const literal_content : Record < string , Json > = {
55+ label : name ,
56+ source_data : otherData ,
57+ } ;
58+ if ( template ) literal_content . template = template ;
59+ if ( importedFromRid ) literal_content . importedFromRid = importedFromRid ;
3960 return {
4061 space_id : context . spaceId ,
4162 name,
@@ -45,11 +66,107 @@ export const discourseNodeSchemaToLocalConcept = ({
4566 created : new Date ( created ) . toISOString ( ) ,
4667 last_modified : new Date ( modified ) . toISOString ( ) ,
4768 description : description ,
48- literal_content : {
49- label : name ,
50- template : template ,
51- source_data : otherData ,
69+ literal_content,
70+ /* eslint-enable @typescript-eslint/naming-convention */
71+ } ;
72+ } ;
73+
74+ const STANDARD_ROLES = [ "source" , "destination" ] ;
75+
76+ export const discourseRelationTypeToLocalConcept = ( {
77+ context,
78+ relationType,
79+ accountLocalId,
80+ } : {
81+ context : SupabaseContext ;
82+ relationType : DiscourseRelationType ;
83+ accountLocalId : string ;
84+ } ) : LocalConceptDataInput => {
85+ const {
86+ id,
87+ label,
88+ complement,
89+ created,
90+ modified,
91+ importedFromRid,
92+ ...otherData
93+ } = relationType ;
94+ // eslint-disable-next-line @typescript-eslint/naming-convention
95+ const literal_content : Record < string , Json > = {
96+ roles : STANDARD_ROLES ,
97+ label,
98+ complement,
99+ // eslint-disable-next-line @typescript-eslint/naming-convention
100+ source_data : otherData ,
101+ } ;
102+ if ( importedFromRid ) literal_content . importedFromRid = importedFromRid ;
103+
104+ return {
105+ /* eslint-disable @typescript-eslint/naming-convention */
106+ space_id : context . spaceId ,
107+ name : label ,
108+ source_local_id : id ,
109+ is_schema : true ,
110+ author_local_id : accountLocalId ,
111+ created : new Date ( created ) . toISOString ( ) ,
112+ last_modified : new Date ( modified ) . toISOString ( ) ,
113+ literal_content,
114+ /* eslint-enable @typescript-eslint/naming-convention */
115+ } ;
116+ } ;
117+
118+ export const discourseRelationTripleSchemaToLocalConcept = ( {
119+ context,
120+ relation,
121+ accountLocalId,
122+ nodeTypesById,
123+ relationTypesById,
124+ } : {
125+ context : SupabaseContext ;
126+ relation : DiscourseRelation ;
127+ accountLocalId : string ;
128+ nodeTypesById : Record < string , DiscourseNode > ;
129+ relationTypesById : Record < string , DiscourseRelationType > ;
130+ } ) : LocalConceptDataInput => {
131+ const {
132+ id,
133+ relationshipTypeId,
134+ sourceId,
135+ destinationId,
136+ created,
137+ modified,
138+ importedFromRid,
139+ } = relation ;
140+ const sourceName = nodeTypesById [ sourceId ] ?. name ?? sourceId ;
141+ const destinationName = nodeTypesById [ destinationId ] ?. name ?? destinationId ;
142+ const relationType = relationTypesById [ relationshipTypeId ] ;
143+ if ( ! relationType )
144+ throw new Error ( `missing relation type ${ relationshipTypeId } ` ) ;
145+ const { label, complement } = relationType ;
146+ // eslint-disable-next-line @typescript-eslint/naming-convention
147+ const literal_content : Record < string , Json > = {
148+ roles : STANDARD_ROLES ,
149+ label,
150+ complement,
151+ } ;
152+ if ( importedFromRid ) literal_content . importedFromRid = importedFromRid ;
153+
154+ return {
155+ /* eslint-disable @typescript-eslint/naming-convention */
156+ space_id : context . spaceId ,
157+ name : `${ sourceName } -${ label } -> ${ destinationName } ` ,
158+ source_local_id : id ,
159+ is_schema : true ,
160+ author_local_id : accountLocalId ,
161+ created : new Date ( created ) . toISOString ( ) ,
162+ last_modified : new Date ( modified ) . toISOString ( ) ,
163+ literal_content,
164+ local_reference_content : {
165+ relation_type : relationshipTypeId ,
166+ source : sourceId ,
167+ destination : destinationId ,
52168 } ,
169+ /* eslint-enable @typescript-eslint/naming-convention */
53170 } ;
54171} ;
55172
@@ -66,21 +183,78 @@ export const discourseNodeInstanceToLocalConcept = ({
66183 accountLocalId : string ;
67184} ) : LocalConceptDataInput => {
68185 const extraData = getNodeExtraData ( nodeData . file , accountLocalId ) ;
69- const { nodeInstanceId, nodeTypeId, ...otherData } = nodeData . frontmatter ;
186+ const { nodeInstanceId, nodeTypeId, importedFromRid, ...otherData } =
187+ nodeData . frontmatter ;
188+ // eslint-disable-next-line @typescript-eslint/naming-convention
189+ const literal_content : Record < string , Json > = {
190+ label : nodeData . file . basename ,
191+ // eslint-disable-next-line @typescript-eslint/naming-convention
192+ source_data : otherData as unknown as Json ,
193+ } ;
194+ if ( importedFromRid && typeof importedFromRid === "string" )
195+ literal_content . importedFromRid = importedFromRid ;
70196 return {
197+ /* eslint-disable @typescript-eslint/naming-convention */
71198 space_id : context . spaceId ,
72199 name : nodeData . file . path ,
73200 source_local_id : nodeInstanceId as string ,
74201 schema_represented_by_local_id : nodeTypeId as string ,
75202 is_schema : false ,
76- literal_content : {
77- label : nodeData . file . basename ,
78- source_data : otherData as unknown as Json ,
79- } ,
203+ literal_content,
204+ /* eslint-enable @typescript-eslint/naming-convention */
80205 ...extraData ,
81206 } ;
82207} ;
83208
209+ export const relationInstanceToLocalConcept = ( {
210+ context,
211+ relationTypesById,
212+ allNodesById,
213+ relationInstanceData,
214+ } : {
215+ context : SupabaseContext ;
216+ relationTypesById : Record < string , DiscourseRelationType > ;
217+ allNodesById : Record < string , DiscourseNodeInVault > ;
218+ relationInstanceData : RelationInstance ;
219+ } ) : LocalConceptDataInput | null => {
220+ const { type, created, lastModified, source, destination, importedFromRid } =
221+ relationInstanceData ;
222+ const relationType = relationTypesById [ type ] ;
223+
224+ if ( ! relationType ) {
225+ console . error ( "Missing relationType id " + type ) ;
226+ return null ;
227+ }
228+ const sourceNode = allNodesById [ source ] ;
229+ const destinationNode = allNodesById [ destination ] ;
230+ if (
231+ sourceNode ?. frontmatter . importedFromRid ||
232+ destinationNode ?. frontmatter . importedFromRid
233+ )
234+ return null ; // punt relation to imported nodes for now.
235+ // otherwise put the importedFromRid in source, dest.
236+
237+ /* eslint-disable @typescript-eslint/naming-convention */
238+ const literal_content : Record < string , Json > = { } ;
239+ if ( importedFromRid ) literal_content . importedFromRid = importedFromRid ;
240+ return {
241+ space_id : context . spaceId ,
242+ name : `[[${ sourceNode ? sourceNode . file . basename : source } ]] -${ relationType . label } -> [[${ destinationNode ? destinationNode . file . basename : destination } ]]` ,
243+ source_local_id : relationInstanceData . id ,
244+ author_local_id : relationInstanceData . author ,
245+ schema_represented_by_local_id : type ,
246+ is_schema : false ,
247+ created : new Date ( created ) . toISOString ( ) ,
248+ last_modified : new Date ( lastModified ?? created ) . toISOString ( ) ,
249+ literal_content,
250+ local_reference_content : {
251+ source,
252+ destination,
253+ } ,
254+ /* eslint-enable @typescript-eslint/naming-convention */
255+ } ;
256+ } ;
257+
84258export const relatedConcepts = ( concept : LocalConceptDataInput ) : string [ ] => {
85259 const relations = Object . values (
86260 concept . local_reference_content || { } ,
@@ -103,23 +277,26 @@ const orderConceptsRec = (
103277 ordered : LocalConceptDataInput [ ] ,
104278 concept : LocalConceptDataInput ,
105279 remainder : { [ key : string ] : LocalConceptDataInput } ,
280+ processed : Set < string > ,
106281) : Set < string > => {
107282 const relatedConceptIds = relatedConcepts ( concept ) ;
108283 let missing : Set < string > = new Set ( ) ;
109284 while ( relatedConceptIds . length > 0 ) {
110285 const relatedConceptId = relatedConceptIds . shift ( ) ! ;
286+ if ( processed . has ( relatedConceptId ) ) continue ;
111287 const relatedConcept = remainder [ relatedConceptId ] ;
112288 if ( relatedConcept === undefined ) {
113289 missing . add ( relatedConceptId ) ;
114290 } else {
115291 missing = new Set ( [
116292 ...missing ,
117- ...orderConceptsRec ( ordered , relatedConcept , remainder ) ,
293+ ...orderConceptsRec ( ordered , relatedConcept , remainder , processed ) ,
118294 ] ) ;
119295 delete remainder [ relatedConceptId ] ;
120296 }
121297 }
122298 ordered . push ( concept ) ;
299+ processed . add ( concept . source_local_id ! ) ;
123300 delete remainder [ concept . source_local_id ! ] ;
124301 return missing ;
125302} ;
@@ -143,14 +320,15 @@ export const orderConceptsByDependency = (
143320 ) ;
144321 const ordered : LocalConceptDataInput [ ] = [ ] ;
145322 let missing : Set < string > = new Set ( ) ;
323+ const processed : Set < string > = new Set ( ) ;
146324 while ( Object . keys ( conceptById ) . length > 0 ) {
147325 const first = Object . values ( conceptById ) [ 0 ] ;
148326 if ( ! first ) break ;
149327 missing = new Set ( [
150328 ...missing ,
151- ...orderConceptsRec ( ordered , first , conceptById ) ,
329+ ...orderConceptsRec ( ordered , first , conceptById , processed ) ,
152330 ] ) ;
153- if ( missing . size > 0 ) console . error ( `missing: ${ [ ...missing ] } ` ) ;
331+ if ( missing . size > 0 ) console . error ( `missing: ${ [ ...missing ] . join ( ", " ) } ` ) ;
154332 }
155333 return { ordered, missing : Array . from ( missing ) } ;
156334} ;
0 commit comments