{"version":3,"file":"exploreLinks.mjs","names":[],"sources":["../../src/linkTraversal/exploreLinks.ts"],"sourcesContent":["import type { LdoBase, ShapeType } from \"@ldo/ldo\";\nimport type { ConnectedPlugin } from \"../types/ConnectedPlugin\";\nimport type { SubjectNode } from \"@ldo/rdf-utils\";\nimport type { LQInput } from \"../types/ILinkQuery\";\nimport { BasicLdSet } from \"@ldo/jsonld-dataset-proxy\";\nimport type { IConnectedLdoDataset } from \"../types/IConnectedLdoDataset\";\nimport { createTrackingProxyBuilder } from \"../trackingProxy/createTrackingProxy\";\nimport type { nodeEventListener } from \"@ldo/subscribable-dataset\";\nimport type { Quad } from \"@rdfjs/types\";\n\n/**\n * @internal\n */\ninterface ExploreLinksOptions<Plugins extends ConnectedPlugin[]> {\n  onResourceEncountered?: (\n    resource: Plugins[number][\"types\"][\"resource\"],\n  ) => Promise<void>;\n  onCoveredDataChanged?: nodeEventListener<Quad>;\n  shouldRefreshResources?: boolean;\n}\n\n/**\n * @internal\n */\nexport async function exploreLinks<\n  Type extends LdoBase,\n  Plugins extends ConnectedPlugin[],\n>(\n  dataset: IConnectedLdoDataset<Plugins>,\n  shapeType: ShapeType<Type>,\n  startingResource: Plugins[number][\"types\"][\"resource\"],\n  startingSubject: SubjectNode | string,\n  queryInput: LQInput<Type>,\n  options?: ExploreLinksOptions<Plugins>,\n): Promise<void> {\n  // Do an initial check of the resources.\n  const readResult = options?.shouldRefreshResources\n    ? await startingResource.read()\n    : await startingResource.readIfUnfetched();\n  if (readResult.isError) return;\n\n  if (options?.onResourceEncountered)\n    await options?.onResourceEncountered(startingResource);\n\n  const proxyBuilder = options?.onCoveredDataChanged\n    ? createTrackingProxyBuilder(\n        dataset,\n        shapeType,\n        options?.onCoveredDataChanged,\n      )\n    : dataset.usingType(shapeType);\n  const ldObject = proxyBuilder.fromSubject(startingSubject);\n\n  const encounteredDuringThisExploration = new Set<string>([\n    startingResource.uri,\n  ]);\n\n  // Recursively explore the rest\n  await exploreLinksRecursive(\n    dataset,\n    ldObject,\n    queryInput,\n    encounteredDuringThisExploration,\n    options,\n  );\n}\n\nexport async function exploreLinksRecursive<\n  Type extends LdoBase,\n  Plugins extends ConnectedPlugin[],\n>(\n  dataset: IConnectedLdoDataset<Plugins>,\n  ldObject: Type,\n  queryInput: LQInput<Type>,\n  encounteredDuringThisExploration: Set<string>,\n  options?: ExploreLinksOptions<Plugins>,\n): Promise<void> {\n  const shouldFetch = shouldFetchResource(\n    dataset,\n    ldObject,\n    queryInput,\n    encounteredDuringThisExploration,\n  );\n  const resourceToFetch = dataset.getResource(ldObject[\"@id\"]);\n  if (shouldFetch) {\n    const readResult = options?.shouldRefreshResources\n      ? await resourceToFetch.read()\n      : await resourceToFetch.readIfUnfetched();\n    // If there was an error with the read, the traversal is done.\n    if (readResult.isError) {\n      return;\n    }\n  }\n  if (!encounteredDuringThisExploration.has(resourceToFetch.uri)) {\n    encounteredDuringThisExploration.add(resourceToFetch.uri);\n    if (options?.onResourceEncountered)\n      await options.onResourceEncountered(resourceToFetch);\n  }\n  // Recurse through the other elemenets\n  await Promise.all(\n    Object.entries(queryInput).map(async ([queryKey, queryValue]) => {\n      if (\n        queryValue != undefined &&\n        queryValue !== true &&\n        ldObject[queryKey] != undefined\n      ) {\n        if (ldObject[queryKey] instanceof BasicLdSet) {\n          await Promise.all(\n            ldObject[queryKey].map(async (item) => {\n              await exploreLinksRecursive(\n                dataset,\n                item,\n                queryValue,\n                encounteredDuringThisExploration,\n                options,\n              );\n            }),\n          );\n        } else {\n          await exploreLinksRecursive(\n            dataset,\n            ldObject[queryKey],\n            queryValue,\n            encounteredDuringThisExploration,\n            options,\n          );\n        }\n      }\n    }),\n  );\n}\n\n/**\n * Determines if a resource needs to be fetched based on given data\n */\nexport function shouldFetchResource<\n  Type extends LdoBase,\n  Plugins extends ConnectedPlugin[],\n>(\n  dataset: IConnectedLdoDataset<Plugins>,\n  ldObject: Type,\n  queryInput: LQInput<Type>,\n  encounteredDuringThisExploration: Set<string>,\n): boolean {\n  const linkedResourceUri: string | undefined = ldObject[\"@id\"];\n  // If it's a blank node, no need to fetch\n  if (!linkedResourceUri) return false;\n  const linkedResource = dataset.getResource(linkedResourceUri);\n  // If we've already explored the resource in this exporation, do not fetch\n  if (encounteredDuringThisExploration.has(linkedResource.uri)) return false;\n\n  return Object.entries(queryInput).some(([queryKey, queryValue]) => {\n    // If value is undefined then no need to fetch\n    if (!queryValue) return false;\n    // Always fetch if there's a set in the object\n    if (ldObject[queryKey] instanceof BasicLdSet) return true;\n    // Fetch if a singleton set is not present\n    if (ldObject[queryKey] == undefined) return true;\n    // Otherwise no need t to fetch\n    return false;\n  });\n}\n"],"mappings":";;;;;;AAwBA,eAAsB,aAIpB,SACA,WACA,kBACA,iBACA,YACA,SACe;AAKf,MAHmB,SAAS,yBACxB,MAAM,iBAAiB,MAAM,GAC7B,MAAM,iBAAiB,iBAAiB,EAC7B,QAAS;AAExB,KAAI,SAAS,sBACX,OAAM,SAAS,sBAAsB,iBAAiB;AAgBxD,OAAM,sBACJ,UAfmB,SAAS,uBAC1B,2BACE,SACA,WACA,SAAS,qBACV,GACD,QAAQ,UAAU,UAAU,EACF,YAAY,gBAShC,EACR,YACA,IAT2C,IAAY,CACvD,iBAAiB,IAClB,CAOiC,EAChC,QACD;;AAGH,eAAsB,sBAIpB,SACA,UACA,YACA,kCACA,SACe;CACf,MAAM,cAAc,oBAClB,SACA,UACA,YACA,iCACD;CACD,MAAM,kBAAkB,QAAQ,YAAY,SAAS,OAAO;AAC5D,KAAI;OACiB,SAAS,yBACxB,MAAM,gBAAgB,MAAM,GAC5B,MAAM,gBAAgB,iBAAiB,EAE5B,QACb;;AAGJ,KAAI,CAAC,iCAAiC,IAAI,gBAAgB,IAAI,EAAE;AAC9D,mCAAiC,IAAI,gBAAgB,IAAI;AACzD,MAAI,SAAS,sBACX,OAAM,QAAQ,sBAAsB,gBAAgB;;AAGxD,OAAM,QAAQ,IACZ,OAAO,QAAQ,WAAW,CAAC,IAAI,OAAO,CAAC,UAAU,gBAAgB;AAC/D,MACE,cAAc,KAAA,KACd,eAAe,QACf,SAAS,aAAa,KAAA,EAEtB,KAAI,SAAS,qBAAqB,WAChC,OAAM,QAAQ,IACZ,SAAS,UAAU,IAAI,OAAO,SAAS;AACrC,SAAM,sBACJ,SACA,MACA,YACA,kCACA,QACD;IACD,CACH;MAED,OAAM,sBACJ,SACA,SAAS,WACT,YACA,kCACA,QACD;GAGL,CACH;;;;;AAMH,SAAgB,oBAId,SACA,UACA,YACA,kCACS;CACT,MAAM,oBAAwC,SAAS;AAEvD,KAAI,CAAC,kBAAmB,QAAO;CAC/B,MAAM,iBAAiB,QAAQ,YAAY,kBAAkB;AAE7D,KAAI,iCAAiC,IAAI,eAAe,IAAI,CAAE,QAAO;AAErE,QAAO,OAAO,QAAQ,WAAW,CAAC,MAAM,CAAC,UAAU,gBAAgB;AAEjE,MAAI,CAAC,WAAY,QAAO;AAExB,MAAI,SAAS,qBAAqB,WAAY,QAAO;AAErD,MAAI,SAAS,aAAa,KAAA,EAAW,QAAO;AAE5C,SAAO;GACP"}