{"version":3,"file":"ConnectedLdoDataset.mjs","names":[],"sources":["../src/ConnectedLdoDataset.ts"],"sourcesContent":["/* eslint-disable @typescript-eslint/no-explicit-any */\nimport type { LdoBase, ShapeType } from \"@ldo/ldo\";\nimport { LdoDataset, startTransaction } from \"@ldo/ldo\";\nimport type { ConnectedPlugin } from \"./types/ConnectedPlugin\";\nimport type { Dataset, DatasetFactory, Quad } from \"@rdfjs/types\";\nimport type { ITransactionDatasetFactory } from \"@ldo/subscribable-dataset\";\nimport { InvalidIdentifierResource } from \"./InvalidIdentifierResource\";\nimport type { ConnectedContext } from \"./types/ConnectedContext\";\nimport type {\n  GetResourceReturnType,\n  IConnectedLdoDataset,\n} from \"./types/IConnectedLdoDataset\";\nimport { ConnectedLdoTransactionDataset } from \"./ConnectedLdoTransactionDataset\";\nimport type { SubjectNode } from \"@ldo/rdf-utils\";\nimport { ConnectedLdoBuilder } from \"./ConnectedLdoBuilder\";\nimport jsonldDatasetProxy from \"@ldo/jsonld-dataset-proxy\";\n\n/**\n * A ConnectedLdoDataset has all the functionality of a LdoDataset with the\n * added functionality of keeping track of fetched remote Resources.\n *\n * It is recommended to use the { @link createConnectedLdoDataset } to\n * initialize this class.\n *\n * @example\n * ```typescript\n * import { createConnectedLdoDataset } from \"@ldo/connected\";\n * import { ProfileShapeType } from \"./_ldo/profile.shapeTypes.ts\"\n *\n * // At least one plugin needs to be provided to a ConnectedLdoDataset. In this\n * // example we'll use both the Solid and NextGraph plugins.\n * import { solidConnectedPlugin } from \"@ldo/connected-solid\";\n * import { nextGraphConnectedPlugin } from \"@ldo/connected-nextgraph\";\n *\n * // ...\n *\n * const connectedLdoDataset = createConnectedLdoDataset([\n *   solidConnectedPlugin,\n *   nextGraphConnectedPlugin\n * ]);\n *\n * const profileDocument = connectedLdoDataset\n *   .getResource(\"https://example.com/profile\");\n * await profileDocument.read();\n *\n * const profile = connectedLdoDataset\n *   .using(ProfileShapeType)\n *   .fromSubject(\"https://example.com/profile#me\");\n * ```\n */\nexport class ConnectedLdoDataset<\n    Plugins extends ConnectedPlugin<any, any, any, any>[],\n  >\n  extends LdoDataset\n  implements IConnectedLdoDataset<Plugins>\n{\n  /**\n   * @internal\n   *\n   * A list of plugins used by this dataset\n   */\n  private plugins: Plugins;\n  /**\n   * @internal\n   *\n   * A mapping between a resource URI and a resource\n   */\n  protected resourceMap: Map<string, Plugins[number][\"types\"][\"resource\"]>;\n\n  /**\n   * @internal\n   *\n   * Context for each plugin (usually utilities for authentication)\n   */\n  protected context: ConnectedContext<Plugins>;\n\n  /**\n   * It is recommended to use the `createConnectedLdoDataset` function to\n   * instantiate a ConnectedLdoDataset.\n   *\n   * @param plugins An array of plugins for each platform to connect to\n   * @param datasetFactory Creates Datasets\n   * @param transactionDatasetFactory Creates Transaction Datasets\n   * @param initialDataset Initial quads\n   */\n  constructor(\n    plugins: Plugins,\n    datasetFactory: DatasetFactory<Quad, Quad>,\n    transactionDatasetFactory: ITransactionDatasetFactory<Quad>,\n    initialDataset?: Dataset<Quad, Quad>,\n  ) {\n    super(datasetFactory, transactionDatasetFactory, initialDataset);\n    this.plugins = plugins;\n    this.resourceMap = new Map();\n    // eslint-disable-next-line @typescript-eslint/ban-ts-comment\n    // @ts-ignore this is a builder. It will eventually match\n    this.context = {\n      dataset: this,\n    };\n    this.plugins.forEach(\n      (plugin) => (this.context[plugin.name] = plugin.initialContext),\n    );\n  }\n\n  /**\n   * @internal\n   *\n   * A helper function to return a plugin based in the plugin name and uri.\n   */\n  private getValidPlugin(\n    uri: string,\n    pluginName?: string,\n  ): Plugins[number] | undefined {\n    // Check for which plugins this uri is valid\n    const validPlugins = this.plugins\n      .filter((plugin) => plugin.isUriValid(uri))\n      .filter((plugin) => (pluginName ? pluginName === plugin.name : true));\n    if (validPlugins.length === 0) {\n      return undefined;\n    } else if (validPlugins.length > 1) {\n      // TODO: LDO is currently not architected to have an ID valid in multiple\n      // protocols. This will need to be refactored if this is ever the case.\n      throw new Error(\n        \"LDO Connect does not currently support two plugins with overlappng uris\",\n      );\n    }\n    return validPlugins[0];\n  }\n\n  /**\n   * Retireves a representation of a Resource at the given URI. This resource\n   * represents the current state of the resource: whether it is currently\n   * fetched or in the process of fetching as well as some information about it.\n   *\n   * @param uri - the URI of the resource\n   * @param pluginName - optionally, force this function to choose a specific\n   * plugin to use rather than perform content negotiation.\n   *\n   * @returns a Resource\n   *\n   * @example\n   * ```typescript\n   * const profileDocument = connectedLdoDataset\n   *   .getResource(\"https://example.com/profile\");\n   * ```\n   */\n  getResource<\n    Name extends Plugins[number][\"name\"],\n    Plugin extends Extract<Plugins[number], { name: Name }>,\n    UriType extends string,\n  >(uri: UriType, pluginName?: Name): GetResourceReturnType<Plugin, UriType> {\n    const plugin = this.getValidPlugin(uri, pluginName);\n    if (!plugin) return new InvalidIdentifierResource(uri) as any;\n    const normalizedUri = plugin.normalizeUri?.(uri) ?? uri;\n\n    let resource = this.resourceMap.get(normalizedUri);\n    if (!resource) {\n      resource = plugin.getResource(uri, this.context);\n      this.resourceMap.set(normalizedUri, resource);\n    }\n    // HACK: cast to any\n    return resource as any;\n  }\n\n  getResources(): GetResourceReturnType<Plugins[number], string>[] {\n    return Array.from(this.resourceMap.values());\n  }\n\n  getFetchedResources(): GetResourceReturnType<Plugins[number], string>[] {\n    return this.getResources().filter((resource) => resource.isFetched());\n  }\n\n  /**\n   * Generates a random uri and creates a resource.\n   *\n   * @param pluginName - A string name for the platform you'd like to create\n   * the resource on.\n   * @param createResourceOptions - Some set of options specific to the plugin\n   * you've selected.\n   * @returns A created resource or an error\n   *\n   * @example\n   * ```typescript\n   * const profileDocument = await connectedLdoDataset\n   *   .createResource(\"solid\");\n   * ```\n   */\n  async createResource<\n    Name extends Plugins[number][\"name\"],\n    Plugin extends Extract<Plugins[number], { name: Name }>,\n  >(\n    pluginName: Name,\n    createResourceOptions?: Plugin[\"types\"][\"createResourceOptions\"],\n  ): Promise<ReturnType<Plugin[\"createResource\"]>> {\n    const validPlugin = this.plugins.find(\n      (plugin) => pluginName === plugin.name,\n    )!;\n    const newResourceResult = await validPlugin.createResource(\n      // eslint-disable-next-line @typescript-eslint/ban-ts-comment\n      // @ts-ignore I have no idea why this doesn't work\n      this.context,\n      createResourceOptions,\n    );\n    // HACK: cast to any\n    if (newResourceResult.isError) return newResourceResult as any;\n    this.resourceMap.set(newResourceResult.uri, newResourceResult);\n    // HACK: cast to any\n    return newResourceResult as any;\n  }\n\n  /**\n   * Removes a resource from local memory\n   * @param uri - the URI of the resource to remove\n   * @returns true if the resource was present before removal\n   *\n   * @example\n   * ```typescript\n   * connectedLdoDataset.forgetResource(\"https://example.com/resource.ttl\");\n   * ```\n   */\n  forgetResource(uri: string): boolean {\n    const plugin = this.getValidPlugin(uri);\n    const normalizedUri = plugin?.normalizeUri?.(uri) ?? uri;\n    return this.resourceMap.delete(normalizedUri);\n  }\n\n  /**\n   * Removes all resources from memory\n   *\n   * @example\n   * ```typescript\n   * connectedLdoDataset.forgetAllResources();\n   * ```\n   */\n  forgetAllResources(): void {\n    this.resourceMap.clear();\n  }\n\n  /**\n   * Shorthand for connectedLdoDataset\n   *   .usingType(shapeType)\n   *   .write(...resources.map((r) => r.uri))\n   *   .fromSubject(subject);\n   * @param shapeType - The shapetype to represent the data\n   * @param subject - A subject URI\n   * @param resources - The resources changes to should written to\n   *\n   * @example\n   * ```typescript\n   * import { ProfielShapeType } from \"./_ldo/foafProfile.shapeType.ts\"\n   *\n   * const resource = connectedLdoDataset\n   *   .getResource(\"https://example.com/profile\");\n   * const profile = connectedLdoDataset.createData(\n   *   ProfileShapeType,\n   *   \"https://example.com/profile#me\",\n   *   resource\n   * );\n   * ```\n   */\n  createData<Type extends LdoBase>(\n    shapeType: ShapeType<Type>,\n    subject: string | SubjectNode,\n    resource: Plugins[number][\"types\"][\"resource\"],\n    ...additionalResources: Plugins[number][\"types\"][\"resource\"][]\n  ): Type {\n    const resources = [resource, ...additionalResources];\n    const linkedDataObject = this.usingType(shapeType)\n      .write(...resources.map((r) => r.uri))\n      .fromSubject(subject);\n    startTransaction(linkedDataObject);\n    return linkedDataObject;\n  }\n\n  /**\n   * Sets conetext for a specific plugin\n   *\n   * @param pluginName - the name of the plugin\n   * @param context - the context for this specific plugin\n   */\n  setContext<\n    Name extends Plugins[number][\"name\"],\n    Plugin extends Extract<Plugins[number], { name: Name }>,\n  >(pluginName: Name, context: Plugin[\"types\"][\"context\"]) {\n    // eslint-disable-next-line @typescript-eslint/ban-ts-comment\n    // @ts-ignore\n    this.context[pluginName] = { ...this.context[pluginName], ...context };\n  }\n\n  public usingType<Type extends LdoBase>(\n    shapeType: ShapeType<Type>,\n  ): ConnectedLdoBuilder<Type, Plugins> {\n    const proxyBuilder = jsonldDatasetProxy(this, shapeType.context);\n    return new ConnectedLdoBuilder(this, proxyBuilder, shapeType);\n  }\n\n  public startTransaction(): ConnectedLdoTransactionDataset<Plugins> {\n    return new ConnectedLdoTransactionDataset(\n      this,\n      this.context,\n      this.datasetFactory,\n      this.transactionDatasetFactory,\n    );\n  }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAkDA,IAAa,sBAAb,cAGU,WAEV;;;;;;;;;;CA8BE,YACE,SACA,gBACA,2BACA,gBACA;AACA,QAAM,gBAAgB,2BAA2B,eAAe;AAChE,OAAK,UAAU;AACf,OAAK,8BAAc,IAAI,KAAK;AAG5B,OAAK,UAAU,EACb,SAAS,MACV;AACD,OAAK,QAAQ,SACV,WAAY,KAAK,QAAQ,OAAO,QAAQ,OAAO,eACjD;;;;;;;CAQH,eACE,KACA,YAC6B;EAE7B,MAAM,eAAe,KAAK,QACvB,QAAQ,WAAW,OAAO,WAAW,IAAI,CAAC,CAC1C,QAAQ,WAAY,aAAa,eAAe,OAAO,OAAO,KAAM;AACvE,MAAI,aAAa,WAAW,EAC1B;WACS,aAAa,SAAS,EAG/B,OAAM,IAAI,MACR,0EACD;AAEH,SAAO,aAAa;;;;;;;;;;;;;;;;;;;CAoBtB,YAIE,KAAc,YAA2D;EACzE,MAAM,SAAS,KAAK,eAAe,KAAK,WAAW;AACnD,MAAI,CAAC,OAAQ,QAAO,IAAI,0BAA0B,IAAI;EACtD,MAAM,gBAAgB,OAAO,eAAe,IAAI,IAAI;EAEpD,IAAI,WAAW,KAAK,YAAY,IAAI,cAAc;AAClD,MAAI,CAAC,UAAU;AACb,cAAW,OAAO,YAAY,KAAK,KAAK,QAAQ;AAChD,QAAK,YAAY,IAAI,eAAe,SAAS;;AAG/C,SAAO;;CAGT,eAAiE;AAC/D,SAAO,MAAM,KAAK,KAAK,YAAY,QAAQ,CAAC;;CAG9C,sBAAwE;AACtE,SAAO,KAAK,cAAc,CAAC,QAAQ,aAAa,SAAS,WAAW,CAAC;;;;;;;;;;;;;;;;;CAkBvE,MAAM,eAIJ,YACA,uBAC+C;EAI/C,MAAM,oBAAoB,MAHN,KAAK,QAAQ,MAC9B,WAAW,eAAe,OAAO,KAEO,CAAC,eAG1C,KAAK,SACL,sBACD;AAED,MAAI,kBAAkB,QAAS,QAAO;AACtC,OAAK,YAAY,IAAI,kBAAkB,KAAK,kBAAkB;AAE9D,SAAO;;;;;;;;;;;;CAaT,eAAe,KAAsB;EAEnC,MAAM,gBADS,KAAK,eAAe,IACP,EAAE,eAAe,IAAI,IAAI;AACrD,SAAO,KAAK,YAAY,OAAO,cAAc;;;;;;;;;;CAW/C,qBAA2B;AACzB,OAAK,YAAY,OAAO;;;;;;;;;;;;;;;;;;;;;;;;CAyB1B,WACE,WACA,SACA,UACA,GAAG,qBACG;EACN,MAAM,YAAY,CAAC,UAAU,GAAG,oBAAoB;EACpD,MAAM,mBAAmB,KAAK,UAAU,UAAU,CAC/C,MAAM,GAAG,UAAU,KAAK,MAAM,EAAE,IAAI,CAAC,CACrC,YAAY,QAAQ;AACvB,mBAAiB,iBAAiB;AAClC,SAAO;;;;;;;;CAST,WAGE,YAAkB,SAAqC;AAGvD,OAAK,QAAQ,cAAc;GAAE,GAAG,KAAK,QAAQ;GAAa,GAAG;GAAS;;CAGxE,UACE,WACoC;EACpC,MAAM,eAAe,mBAAmB,MAAM,UAAU,QAAQ;AAChE,SAAO,IAAI,oBAAoB,MAAM,cAAc,UAAU;;CAG/D,mBAAmE;AACjE,SAAO,IAAI,+BACT,MACA,KAAK,SACL,KAAK,gBACL,KAAK,0BACN"}