Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion packages/js/core/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -340,7 +340,7 @@ export interface UriConfig {
/**
* A Polywrap URI. Some examples of valid URIs are:
* wrap://ipfs/QmHASH
* wrap://ens/sub.dimain.eth
* wrap://ens/sub.domain.eth
* wrap://fs/directory/file.txt
* wrap://uns/domain.crypto
*
Expand Down
29 changes: 28 additions & 1 deletion packages/js/core/src/__tests__/Uri.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ describe("Uri", () => {
});

it("Fails if an authority is not present", () => {
expect(() => new Uri("wrap://path")).toThrowError(/URI is malformed,/);
expect(() => new Uri("wrap://path")).toThrowError(/URI authority is missing,/);
});

it("Fails if a path is not present", () => {
Expand Down Expand Up @@ -50,4 +50,31 @@ describe("Uri", () => {
path: "uri",
});
});

it("Infers common URI authorities", () => {
Copy link
Copy Markdown
Contributor

@nerfZael nerfZael Apr 17, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Common URI authorities seem awkward.
How do we decide which authorities are/should be common and which shouldn't?
Can/should different core libraries (js, rust, python) support different common authorities?
If we're adding ENS, should we add name services of other chains?

This feature would also make it impossible to differentiate between WRAP URIs and other protocol's URIs. (Both would be valid URIs for Uri.parseUri)

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The "common URI authorities" thing is just the name of the test. It's not part of the feature. I think it's important to test that the feature works with the URI authorities we are commonly using right now. Are there other test cases you have in mind? Would you like more test cases in general?

Regarding the differentiation between WRAP URIs and other protocol URIs with Uri.parseUri, I don't think much has changed. Since we don't require the wrap:// schema prefix, users can already write something like myAuthority/anyPath.tiktok and it would be parsed without issue. This feature only changes the accepted URI format such that if an authority is not found but a schema (e.g. http://) is present, we assume the schema is the authority of a WRAP URI. It's surface-level formatting flexibility. It doesn't change how URIs interact with the toolchain, or which URIs can resolve to wrappers.

let uri = new Uri("https://domain.com/path/to/thing");
expect(uri.uri).toEqual("wrap://https/domain.com/path/to/thing");
expect(uri.authority).toEqual("https");
expect(uri.path).toEqual("domain.com/path/to/thing");

uri = new Uri("http://domain.com/path/to/thing");
expect(uri.uri).toEqual("wrap://http/domain.com/path/to/thing");
expect(uri.authority).toEqual("http");
expect(uri.path).toEqual("domain.com/path/to/thing");

uri = new Uri("ipfs://QmaM318ABUXDhc5eZGGbmDxkb2ZgnbLxigm5TyZcCsh1Kw");
expect(uri.uri).toEqual("wrap://ipfs/QmaM318ABUXDhc5eZGGbmDxkb2ZgnbLxigm5TyZcCsh1Kw");
expect(uri.authority).toEqual("ipfs");
expect(uri.path).toEqual("QmaM318ABUXDhc5eZGGbmDxkb2ZgnbLxigm5TyZcCsh1Kw");

uri = new Uri("ens://domain.eth");
expect(uri.uri).toEqual("wrap://ens/domain.eth");
expect(uri.authority).toEqual("ens");
expect(uri.path).toEqual("domain.eth");

uri = new Uri("ens://domain.eth:pkg@1.0.0");
expect(uri.uri).toEqual("wrap://ens/domain.eth:pkg@1.0.0");
expect(uri.authority).toEqual("ens");
expect(uri.path).toEqual("domain.eth:pkg@1.0.0");
});
});
54 changes: 41 additions & 13 deletions packages/js/core/src/types/Uri.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import { RegExpGroups } from "../utils/RegExpGroups";

import { Result, ResultErr, ResultOk } from "@polywrap/result";

// $start: UriConfig
Expand All @@ -18,7 +20,7 @@ export interface UriConfig {
/**
* A Polywrap URI. Some examples of valid URIs are:
* wrap://ipfs/QmHASH
* wrap://ens/sub.dimain.eth
* wrap://ens/sub.domain.eth
* wrap://fs/directory/file.txt
* wrap://uns/domain.crypto
*
Expand Down Expand Up @@ -135,17 +137,10 @@ export class Uri {
}

// Extract the authoriy & path
const result = processed.match(/wrap:\/\/([a-z][a-z0-9-_]+)\/(.*)/);
let uriParts: string[];

// Remove all empty strings
if (result) {
uriParts = result.filter((str) => !!str);
} else {
uriParts = [];
}
const re = /^wrap:\/\/((?<authority>[a-z][a-z0-9-_]+)\/)?(?<path>.*)$/;
const result: RegExpGroups<"authority" | "path"> = re.exec(processed);

if (uriParts.length !== 3) {
if (!result || !result.groups || !result.groups.path) {
return ResultErr(
Error(
`URI is malformed, here are some examples of valid URIs:\n` +
Expand All @@ -157,10 +152,30 @@ export class Uri {
);
}

let { authority, path } = result.groups;

if (!authority) {
const inferred = Uri.inferAuthority(path);
if (!inferred) {
return ResultErr(
Error(
`URI authority is missing, here are some examples of valid URIs:\n` +
`wrap://ipfs/QmHASH\n` +
`wrap://ens/domain.eth\n` +
`ens/domain.eth\n\n` +
`Invalid URI Received: ${uri}`
)
);
}
authority = inferred.authority;
path = inferred.path;
processed = `wrap://${authority}/${path}`;
}

return ResultOk({
uri: processed,
authority: uriParts[1],
path: uriParts[2],
authority,
path,
});
}

Expand Down Expand Up @@ -194,4 +209,17 @@ export class Uri {
public toJSON(): string /* $ */ {
return this._config.uri;
}

private static inferAuthority(_path: string): UriConfig | undefined {
const re = /^(?<authority>[a-z][a-z0-9-_]+):\/\/(?<path>.*)$/;
Comment thread
krisbitney marked this conversation as resolved.
const result: RegExpGroups<"authority" | "path"> = re.exec(_path);

if (!result || !result.groups) {
return undefined;
}
const authority = result.groups.authority as string;
const path = result.groups.path as string;

return { authority, path, uri: `wrap://${authority}/${path}` };
}
}
12 changes: 3 additions & 9 deletions packages/js/core/src/types/WrapError.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { CleanResolutionStep } from "../algorithms";
import { RegExpGroups } from "../utils/RegExpGroups";

export type ErrorSource = Readonly<{
file?: string;
Expand Down Expand Up @@ -52,12 +53,6 @@ export interface WrapErrorOptions {
innerError?: WrapError;
}

type RegExpGroups<T extends string> =
| (RegExpExecArray & {
groups?: { [name in T]: string | undefined } | { [key: string]: string };
})
| null;

export class WrapError extends Error {
readonly name: string = "WrapError";
readonly code: WrapErrorCode;
Expand Down Expand Up @@ -174,7 +169,7 @@ export class WrapError extends Error {
| "resolutionStack"
| "cause"
> = WrapError.re.exec(error);
if (!result) {
if (!result || !result.groups) {
return undefined;
}
const {
Expand All @@ -188,8 +183,7 @@ export class WrapError extends Error {
col,
resolutionStack: resolutionStackStr,
cause,
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
} = result.groups!;
} = result.groups;

const code = parseInt(codeStr as string);

Expand Down
5 changes: 5 additions & 0 deletions packages/js/core/src/utils/RegExpGroups.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
export type RegExpGroups<T extends string> =
| (RegExpExecArray & {
groups?: { [name in T]: string | undefined } | { [key: string]: string };
})
| null;
1 change: 1 addition & 0 deletions packages/js/core/src/utils/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,4 @@ export * from "./combinePaths";
export * from "./getEnvFromUriHistory";
export * from "./is-buffer";
export * from "./typesHandler";
export * from "./RegExpGroups";
8 changes: 1 addition & 7 deletions packages/js/plugin/src/utils/getErrorSource.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,4 @@
import { ErrorSource } from "@polywrap/core-js";

type RegExpGroups<T extends string> =
| (RegExpExecArray & {
groups?: { [name in T]: string | undefined } | { [key: string]: string };
})
| null;
import { ErrorSource, RegExpGroups } from "@polywrap/core-js";

const re = /\((?<file>.*):(?<row>\d+):(?<col>\d+)\)$/;

Expand Down