Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

package path not exported for 'access/universal' #1004

Closed
vanhumbeecka opened this issue Apr 24, 2021 · 7 comments
Closed

package path not exported for 'access/universal' #1004

vanhumbeecka opened this issue Apr 24, 2021 · 7 comments
Assignees
Labels
bug Something isn't working

Comments

@vanhumbeecka
Copy link
Contributor

When using the @inrupt/solid-client library in a NodeJS back-end, I'm getting the following stacktrace when adding this line:

import { setPublicAccess } from '@inrupt/solid-client/dist/access/universal'

I haven't seen this problem when using the solid-client in the front-end, only now when using it in back-end. I'm using TypeScript (v4.2)

@inrupt/solid-client version is 1.6.1

Stacktrace is:

internal/modules/cjs/loader.js:450
      throw e;
      ^

Error [ERR_PACKAGE_PATH_NOT_EXPORTED]: Package subpath './dist/access/universal' is not defined by "exports" in /Users/andries/projects/consolidate/survey-aggregator/node_modules/@inrupt/solid-client/package.json
    at throwExportsNotFound (internal/modules/esm/resolve.js:285:9)
    at packageExportsResolve (internal/modules/esm/resolve.js:491:3)
    at resolveExports (internal/modules/cjs/loader.js:444:36)
    at Function.Module._findPath (internal/modules/cjs/loader.js:484:31)
    at Function.Module._resolveFilename (internal/modules/cjs/loader.js:818:27)
    at Function.Module._load (internal/modules/cjs/loader.js:687:27)
    at Module.require (internal/modules/cjs/loader.js:903:19)
    at require (internal/modules/cjs/helpers.js:74:18)
    at Object.<anonymous> (/Users/andries/projects/consolidate/survey-aggregator/dist/modules/survey/SurveyCreatorService.js:11:21)
    at Module._compile (internal/modules/cjs/loader.js:1015:30) {
  code: 'ERR_PACKAGE_PATH_NOT_EXPORTED'
}

@vanhumbeecka vanhumbeecka added the bug Something isn't working label Apr 24, 2021
@Vinnl
Copy link
Contributor

Vinnl commented Apr 25, 2021

Hi @vanhumbeecka, the problem is the /dist in your import, which isn't the correct module path. As the docs show, the import path is as follows:

import { setPublicAccess } from '@inrupt/solid-client/access/universal'

Let me know if that resolves your issue.

PS. The same holds true in the front-end. While some (older versions of) bundlers might allow you to reach into the internals of a package and import from any of the package files directly, later versions (e.g. Webpack 5 and above) will only allow you to import from modules explicitly exported by the package. So to be future-proof, best to use the correct module paths in your front-end code as well.

@Vinnl Vinnl self-assigned this Apr 25, 2021
@vanhumbeecka
Copy link
Contributor Author

Hi @Vinnl , I did noticed the difference in the examples, but when trying to import in that way, I get typescript errors:

image

Not sure if this has something to do with my TS settings (listed below)

{
  "compilerOptions": {
    "module": "commonjs",
    "declaration": true,
    "removeComments": true,
    "emitDecoratorMetadata": true,
    "experimentalDecorators": true,
    "allowSyntheticDefaultImports": true,
    "target": "es2017",
    "sourceMap": true,
    "outDir": "./dist",
    "baseUrl": "./",
    "incremental": true,
    "strict": true,
    "esModuleInterop": true
  },
  "exclude": [
    "node_modules"
  ]
}

@Vinnl
Copy link
Contributor

Vinnl commented Apr 26, 2021

Hi @vanhumbeecka, unfortunately TypeScript does not support exports maps (Node docs, Webpack docs), although it is planned for the next version (specific issue). We will make sure to produce the relevant type definitions as soon as TypeScript supports it.

Edit: See bottom of this comment for an alternative approach.

If you don't want to wait for that, what you could do in the meantime is to provide your own type definitions — though keep in mind that you'll then be responsible for keeping them aligned with the actual code when new versions of @inrupt/solid-client are released (though I wouldn't expect major issues there). I think something like this could work:

declare module "@inrupt/solid-client/access/universal" {
  /**
    * Copyright 2021 Inrupt Inc.
    *
    * Permission is hereby granted, free of charge, to any person obtaining a copy
    * of this software and associated documentation files (the "Software"), to deal in
    * the Software without restriction, including without limitation the rights to use,
    * copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
    * Software, and to permit persons to whom the Software is furnished to do so,
    * subject to the following conditions:
    *
    * The above copyright notice and this permission notice shall be included in
    * all copies or substantial portions of the Software.
    *
    * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
    * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
    * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
    * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
    * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
    * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
    */
  import { UrlString, WebId } from "@inrupt/solid-client";
  /**
   * Each of the following access modes is in one of two states:
   * - true: this access mode is granted, or
   * - false: this access mode is not granted.
   * @since 1.5.0
   */
  export interface Access {
      read: boolean;
      append: boolean;
      write: boolean;
      controlRead: boolean;
      controlWrite: boolean;
  }
  /**
   * Get an overview of what access is defined for a given Agent.
   *
   * This function works with Solid Pods that implement either the Web Access
   * Control spec or the Access Control Policies proposal, with some caveats:
   *
   * - If access to the given Resource has been set using anything other than the
   *   functions in this module, it is possible that it has been set in a way that
   *   prevents this function from reliably reading access, in which case it will
   *   resolve to `null`.
   * - It will only return access specified explicitly for the given Agent. If
   *   additional restrictions are set up to apply to the given Agent in a
   *   particular situation, those will not be reflected in the return value of
   *   this function.
   * - It will only return access specified explicitly for the given Resource.
   *   In other words, if the Resource is a Container, the returned Access may not
   *   apply to contained Resources.
   * - If the current user does not have permission to view access for the given
   *   Resource, this function will resolve to `null`.
   *
   * @param resourceUrl URL of the Resource you want to read the access for.
   * @param webId WebID of the Agent you want to get the access for.
   * @since 1.5.0
   */
  export declare function getAgentAccess(resourceUrl: UrlString, webId: WebId, options?: {
      fetch: ((input: RequestInfo, init?: RequestInit | undefined) => Promise<Response>) & typeof fetch;
  }): Promise<Access | null>;
  /**
   * Set access to a Resource for a specific Agent.
   *
   * This function works with Solid Pods that implement either the Web Access
   * Control spec or the Access Control Policies proposal, with some caveats:
   *
   * - If access to the given Resource has been set using anything other than the
   *   functions in this module, it is possible that it has been set in a way that
   *   prevents this function from reliably setting access, in which case it will
   *   resolve to `null`.
   * - It will only set access explicitly for the given Agent. In other words,
   *   additional restrictions could be present that further restrict or loosen
   *   what access the given Agent has in particular circumstances.
   * - The provided access will only apply to the given Resource. In other words,
   *   if the Resource is a Container, the configured Access may not apply to
   *   contained Resources.
   * - If the current user does not have permission to view or change access for
   *   the given Resource, this function will resolve to `null`.
   *
   * Additionally, two caveats apply to users with a Pod server that uses WAC:
   * - If the Resource did not have an ACL yet, a new one will be initialised.
   *   This means that changes to the ACL of a parent Container can no longer
   *   affect access people have to this Resource, although existing access will
   *   be preserved.
   * - Setting different values for `controlRead` and `controlWrite` is not
   *   supported, and **will throw an error**. If you expect (some of) your users
   *   to have Pods implementing WAC, be sure to pass the same value for both.
   *
   * @param resourceUrl URL of the Resource you want to change the Agent's access to.
   * @param webId WebID of the Agent you want to set access for.
   * @param access What access permissions you want to set for the given Agent to the given Resource. Possible properties are `read`, `append`, `write`, `controlRead` and `controlWrite`: set to `true` to allow, to `false` to stop allowing, or `undefined` to leave unchanged. Take note that `controlRead` and `controlWrite` can not have distinct values for a Pod server implementing Web Access Control; trying this will throw an error.
   * @returns What access has been set for the given Agent explicitly.
   * @since 1.5.0
   */
  export declare function setAgentAccess(resourceUrl: UrlString, webId: WebId, access: Partial<Access>, options?: {
      fetch: ((input: RequestInfo, init?: RequestInit | undefined) => Promise<Response>) & typeof fetch;
  }): Promise<Access | null>;
  /**
   * Get an overview of what access is defined for all Agents with respect to a given
   * Resource.
   *
   * This function works with Solid Pods that implement either the Web Access
   * Control spec or the Access Control Policies proposal, with some caveats:
   *
   * - If access to the given Resource has been set using anything other than the
   *   functions in this module, it is possible that it has been set in a way that
   *   prevents this function from reliably reading access, in which case it will
   *   resolve to `null`.
   * - It will only return access specified explicitly for the returned Agents. If
   *   additional restrictions are set up to apply to the listed Agents in a
   *   particular situation, those will not be reflected in the return value of
   *   this function.
   * - It will only return access specified explicitly for the given Resource.
   *   In other words, if the Resource is a Container, the returned Access may not
   *   apply to contained Resources.
   * - If the current user does not have permission to view access for the given
   *   Resource, this function will resolve to `null`.
   *
   * @param resourceUrl URL of the Resource you want to read the access for.
   * @returns The access information to the Resource, grouped by Agent.
   * @since 1.5.0
   */
  export declare function getAgentAccessAll(resourceUrl: UrlString, options?: {
      fetch: ((input: RequestInfo, init?: RequestInit | undefined) => Promise<Response>) & typeof fetch;
  }): Promise<Record<WebId, Access> | null>;
  /**
   * Get an overview of what access is defined for a given Group.
   *
   * This function works with Solid Pods that implement either the Web Access
   * Control spec or the Access Control Policies proposal, with some caveats:
   *
   * - If access to the given Resource has been set using anything other than the
   *   functions in this module, it is possible that it has been set in a way that
   *   prevents this function from reliably reading access, in which case it will
   *   resolve to `null`.
   * - It will only return access specified explicitly for the given Group. If
   *   additional restrictions are set up to apply to the given Group in a
   *   particular situation, those will not be reflected in the return value of
   *   this function.
   * - It will only return access specified explicitly for the given Resource.
   *   In other words, if the Resource is a Container, the returned Access may not
   *   apply to contained Resources.
   * - If the current user does not have permission to view access for the given
   *   Resource, this function will resolve to `null`.
   *
   * @param resourceUrl URL of the Resource you want to read the access for.
   * @param webId WebID of the Group you want to get the access for.
   * @since 1.5.0
   */
  export declare function getGroupAccess(resourceUrl: UrlString, webId: WebId, options?: {
      fetch: ((input: RequestInfo, init?: RequestInit | undefined) => Promise<Response>) & typeof fetch;
  }): Promise<Access | null>;
  /**
   * Get an overview of what access is defined for all Groups with respect to a given
   * Resource.
   *
   * This function works with Solid Pods that implement either the Web Access
   * Control spec or the Access Control Policies proposal, with some caveats:
   *
   * - If access to the given Resource has been set using anything other than the
   *   functions in this module, it is possible that it has been set in a way that
   *   prevents this function from reliably reading access, in which case it will
   *   resolve to `null`.
   * - It will only return access specified explicitly for the returned Groups. If
   *   additional restrictions are set up to apply to the listed Groups in a
   *   particular situation, those will not be reflected in the return value of
   *   this function.
   * - It will only return access specified explicitly for the given Resource.
   *   In other words, if the Resource is a Container, the returned Access may not
   *   apply to contained Resources.
   * - If the current user does not have permission to view access for the given
   *   Resource, this function will resolve to `null`.
   *
   * @param resourceUrl URL of the Resource you want to read the access for.
   * @returns The access information to the Resource, sorted by Group.
   * @since 1.5.0
   */
  export declare function getGroupAccessAll(resourceUrl: UrlString, options?: {
      fetch: ((input: RequestInfo, init?: RequestInit | undefined) => Promise<Response>) & typeof fetch;
  }): Promise<Record<UrlString, Access> | null>;
  /**
   * Set access to a Resource for a specific Group.
   *
   * This function works with Solid Pods that implement either the Web Access
   * Control spec or the Access Control Policies proposal, with some caveats:
   *
   * - If access to the given Resource has been set using anything other than the
   *   functions in this module, it is possible that it has been set in a way that
   *   prevents this function from reliably setting access, in which case it will
   *   resolve to `null`.
   * - It will only set access explicitly for the given Group. In other words,
   *   additional restrictions could be present that further restrict or loosen
   *   what access the given Group has in particular circumstances.
   * - The provided access will only apply to the given Resource. In other words,
   *   if the Resource is a Container, the configured Access may not apply to
   *   contained Resources.
   * - If the current user does not have permission to view or change access for
   *   the given Resource, this function will resolve to `null`.
   *
   * Additionally, two caveats apply to users with a Pod server that uses WAC:
   * - If the Resource did not have an ACL yet, a new one will be initialised.
   *   This means that changes to the ACL of a parent Container can no longer
   *   affect access people have to this Resource, although existing access will
   *   be preserved.
   * - Setting different values for `controlRead` and `controlWrite` is not
   *   supported, and **will throw an error**. If you expect (some of) your users
   *   to have Pods implementing WAC, be sure to pass the same value for both.
   *
   * @param resourceUrl URL of the Resource you want to change the Group's access to.
   * @param groupUrl URL of the Group you want to set access for.
   * @param access What access permissions you want to set for the given Group to the given Resource. Possible properties are `read`, `append`, `write`, `controlRead` and `controlWrite`: set to `true` to allow, to `false` to stop allowing, or `undefined` to leave unchanged. Take note that `controlRead` and `controlWrite` can not have distinct values for a Pod server implementing Web Access Control; trying this will throw an error.
   * @returns What access has been set for the given Group explicitly.
   * @since 1.5.0
   */
  export declare function setGroupAccess(resourceUrl: UrlString, groupUrl: UrlString, access: Partial<Access>, options?: {
      fetch: ((input: RequestInfo, init?: RequestInit | undefined) => Promise<Response>) & typeof fetch;
  }): Promise<Access | null>;
  /**
   * Get an overview of what access is defined for everyone.
   *
   * This function works with Solid Pods that implement either the Web Access
   * Control spec or the Access Control Policies proposal, with some caveats:
   *
   * - If access to the given Resource has been set using anything other than the
   *   functions in this module, it is possible that it has been set in a way that
   *   prevents this function from reliably reading access, in which case it will
   *   resolve to `null`.
   * - It will only return access specified explicitly for everyone. If
   *   additional restrictions are set up to apply to users in a particular
   *   situation, those will not be reflected in the return value of this
   *   function.
   * - It will only return access specified explicitly for the given Resource.
   *   In other words, if the Resource is a Container, the returned Access may not
   *   apply to contained Resources.
   * - If the current user does not have permission to view access for the given
   *   Resource, this function will resolve to `null`.
   *
   * @param resourceUrl URL of the Resource you want to read the access for.
   * @since 1.5.0
   */
  export declare function getPublicAccess(resourceUrl: UrlString, options?: {
      fetch: ((input: RequestInfo, init?: RequestInit | undefined) => Promise<Response>) & typeof fetch;
  }): Promise<Access | null>;

  /**
   * Set access to a Resource for everybody.
   *
   * This function works with Solid Pods that implement either the Web Access
   * Control spec or the Access Control Policies proposal, with some caveats:
   *
   * - If access to the given Resource has been set using anything other than the
   *   functions in this module, it is possible that it has been set in a way that
   *   prevents this function from reliably setting access, in which case it will
   *   resolve to `null`.
   * - It will only set access explicitly for everybody. In other words,
   *   additional restrictions could be present that further restrict or loosen
   *   what access a user has in particular circumstances.
   * - The provided access will only apply to the given Resource. In other words,
   *   if the Resource is a Container, the configured Access may not apply to
   *   contained Resources.
   * - If the current user does not have permission to view or change access for
   *   the given Resource, this function will resolve to `null`.
   *
   * Additionally, two caveats apply to users with a Pod server that uses WAC:
   * - If the Resource did not have an ACL yet, a new one will be initialised.
   *   This means that changes to the ACL of a parent Container can no longer
   *   affect access people have to this Resource, although existing access will
   *   be preserved.
   * - Setting different values for `controlRead` and `controlWrite` is not
   *   supported, and **will throw an error**. If you expect (some of) your users
   *   to have Pods implementing WAC, be sure to pass the same value for both.
   *
   * @param resourceUrl URL of the Resource you want to change public access to.
   * @param access What access permissions you want to set for everybody to the given Resource. Possible properties are `read`, `append`, `write`, `controlRead` and `controlWrite`: set to `true` to allow, to `false` to stop allowing, or `undefined` to leave unchanged. Take note that `controlRead` and `controlWrite` can not have distinct values for a Pod server implementing Web Access Control; trying this will throw an error.
   * @returns What access has been set for everybody explicitly.
   * @since 1.5.0
   */
  export declare function setPublicAccess(resourceUrl: UrlString, access: Partial<Access>, options?: {
      fetch: ((input: RequestInfo, init?: RequestInit | undefined) => Promise<Response>) & typeof fetch;
  }): Promise<Access | null>;
  export { getAccessFor, getAccessForAll, setAccessFor } from "./for";
}

(Based on the module's own type definition. You can of course also just have it export any.)

Edit: Looks like it didn't make the cut for the next version :(

Edit 2: Actually, an alternative approach that might be preferable while we wait for TypeScript support: all the universal access API's are also available on an access object exported from the top-level. Since this prevents tree-shaking and is a bit idiosyncratic we did not document or generally recommend this, but if you're running in Node, that might not be so much of a problem, and you can always migrate to the direct module import when needed. So you could do something like this:

import { access } from "@inrupt/solid-client";

access.setPublicAccess(/* ... */).then(/* ... */);

@vanhumbeecka
Copy link
Contributor Author

Awesome. Thanks for the quick answer. The intermediate solution you are proposing with import { access } from "@inrupt/solid-client" works perfect for now.

I will keep this in mind this is a work-around that prevents tree-shaking.

@ianconsolata
Copy link
Contributor

@Vinnl could y'all update https://docs.inrupt.com/developer-tools/javascript/client-libraries/tutorial/manage-access/ and other areas of the inrupt client library docs to reflect that solution (import { access } from "@inrupt/solid-client"). There is no indication in the docs that the access methods should be imported in a special way compared to the rest of the documented functions (like useThing). The api docs are great, but I spent most of the afternoon trying to figure out how to actually import the functions listed in the docs.

@Vinnl
Copy link
Contributor

Vinnl commented Mar 16, 2022

@ianconsolata I don't work at Inrupt anymore so I can't (/cc @kay-kim), but the docs are written for people who don't use TS as well, and as I mentioned above, that approach comes with a couple of downsides that we didn't really want to subject those people to if not needed. (And what I didn't mention: as it's not documented and pollutes the global namespace of the package, it's harder to give strong guarantees about backwards compatibility. In fact, looking at the release notes, it looks like breaking changes have been introduced for that approach specifically already.)

That said, it's getting more and more common to provide code examples in both TS and JS (example), so Inrupt could consider doing that as well, That would give them a place to both highlight the above TS-specific problem, as well as the risks of adopting the workaround.

(And of course, ideally TS will soon ship with proper support for submodule exports, but following that issue, it looks like it's wrought with challenges that will haunt them for a while.)

@kay-kim
Copy link
Contributor

kay-kim commented Mar 16, 2022

Thanks mister @Vinnl . Have created a ticket for me to do so.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working
Projects
None yet
Development

No branches or pull requests

4 participants