Remove federtaed argument
This commit is contained in:
parent
14e22d41dc
commit
5b2c36833c
3 changed files with 205 additions and 227 deletions
|
@ -1,78 +1,64 @@
|
||||||
|
/* eslint-disable no-fallthrough */
|
||||||
|
|
||||||
import {
|
import {
|
||||||
parseHash,
|
parseHash,
|
||||||
parsePermalink,
|
parsePermalink,
|
||||||
parseArgs,
|
parseArgs,
|
||||||
verifiers,
|
verifiers,
|
||||||
identifyTypeFromRegex,
|
identifyTypeFromRegex,
|
||||||
toURL,
|
toURL,
|
||||||
} from "./parser";
|
} from "./parser";
|
||||||
|
|
||||||
import { LinkDiscriminator } from "./types";
|
import { LinkKind } from "./types";
|
||||||
|
|
||||||
function identifierType(id: string) {
|
const identifierType = (id: string): LinkKind =>
|
||||||
return identifyTypeFromRegex(id, verifiers, LinkDiscriminator.ParseFailed);
|
identifyTypeFromRegex(id, verifiers, LinkKind.ParseFailed);
|
||||||
}
|
|
||||||
|
|
||||||
it("types identifiers correctly", () => {
|
it("types identifiers correctly", () => {
|
||||||
expect(identifierType("@user:matrix.org")).toEqual(LinkDiscriminator.UserId);
|
expect(identifierType("@user:matrix.org")).toEqual(LinkKind.UserId);
|
||||||
expect(identifierType("!room:matrix.org")).toEqual(LinkDiscriminator.RoomId);
|
expect(identifierType("!room:matrix.org")).toEqual(LinkKind.RoomId);
|
||||||
expect(identifierType("!somewhere:example.org/$event:example.org")).toEqual(
|
expect(identifierType("!somewhere:example.org/$event:example.org")).toEqual(
|
||||||
LinkDiscriminator.Permalink
|
LinkKind.Permalink
|
||||||
);
|
);
|
||||||
expect(identifierType("+group:matrix.org")).toEqual(
|
expect(identifierType("+group:matrix.org")).toEqual(LinkKind.GroupId);
|
||||||
LinkDiscriminator.GroupId
|
expect(identifierType("#alias:matrix.org")).toEqual(LinkKind.Alias);
|
||||||
);
|
|
||||||
expect(identifierType("#alias:matrix.org")).toEqual(LinkDiscriminator.Alias);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it("types garbage as such", () => {
|
it("types garbage as such", () => {
|
||||||
expect(identifierType("sdfa;fdlkja")).toEqual(LinkDiscriminator.ParseFailed);
|
expect(identifierType("sdfa;fdlkja")).toEqual(LinkKind.ParseFailed);
|
||||||
expect(identifierType("$event$matrix.org")).toEqual(
|
expect(identifierType("$event$matrix.org")).toEqual(LinkKind.ParseFailed);
|
||||||
LinkDiscriminator.ParseFailed
|
expect(identifierType("/user:matrix.org")).toEqual(LinkKind.ParseFailed);
|
||||||
);
|
|
||||||
expect(identifierType("/user:matrix.org")).toEqual(
|
|
||||||
LinkDiscriminator.ParseFailed
|
|
||||||
);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it("parses vias", () => {
|
it("parses args correctly", () => {
|
||||||
expect(
|
expect(
|
||||||
parseArgs("via=example.org&via=alt.example.org")
|
parseArgs("via=example.org&via=alt.example.org")
|
||||||
).toHaveProperty("vias", ["example.org", "alt.example.org"]);
|
).toHaveProperty("vias", ["example.org", "alt.example.org"]);
|
||||||
});
|
expect(parseArgs("sharer=blah")).toHaveProperty("sharer", "blah");
|
||||||
|
expect(parseArgs("client=blah.com")).toHaveProperty("client", "blah.com");
|
||||||
it("parses sharer", () => {
|
|
||||||
expect(parseArgs("sharer=blah")).toHaveProperty("sharer", "blah");
|
|
||||||
});
|
|
||||||
|
|
||||||
it("parses client", () => {
|
|
||||||
expect(parseArgs("client=blah.com")).toHaveProperty("client", "blah.com");
|
|
||||||
});
|
|
||||||
|
|
||||||
it("parses federated", () => {
|
|
||||||
expect(parseArgs("federated=true")).toHaveProperty("federated", true);
|
|
||||||
expect(parseArgs("federated=false")).toHaveProperty("federated", false);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it("parses permalinks", () => {
|
it("parses permalinks", () => {
|
||||||
expect(parsePermalink("!somewhere:example.org/$event:example.org")).toEqual({
|
expect(parsePermalink("!somewhere:example.org/$event:example.org")).toEqual(
|
||||||
roomKind: LinkDiscriminator.RoomId,
|
{
|
||||||
roomLink: "!somewhere:example.org",
|
roomKind: LinkKind.RoomId,
|
||||||
eventId: "$event:example.org",
|
roomLink: "!somewhere:example.org",
|
||||||
});
|
eventId: "$event:example.org",
|
||||||
|
}
|
||||||
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("formats links correctly", () => {
|
it("formats links correctly", () => {
|
||||||
const bigLink =
|
const bigLink =
|
||||||
"!somewhere:example.org/$event:example.org?via=dfasdf&via=jfjafjaf";
|
"!somewhere:example.org/$event:example.org?via=dfasdf&via=jfjafjaf";
|
||||||
const origin = "https://matrix.org";
|
const origin = "https://matrix.org";
|
||||||
const prefix = origin + "/#/";
|
const prefix = origin + "/#/";
|
||||||
const parse = parseHash(bigLink);
|
const parse = parseHash(bigLink);
|
||||||
|
|
||||||
switch (parse.kind) {
|
switch (parse.kind) {
|
||||||
case LinkDiscriminator.ParseFailed:
|
case LinkKind.ParseFailed:
|
||||||
fail("Parse failed");
|
fail("Parse failed");
|
||||||
default:
|
default:
|
||||||
expect(toURL(origin, parse).toString()).toEqual(prefix + bigLink);
|
expect(toURL(origin, parse).toString()).toEqual(prefix + bigLink);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,93 +1,134 @@
|
||||||
import forEach from "lodash/forEach";
|
import forEach from "lodash/forEach";
|
||||||
|
|
||||||
import {
|
import {
|
||||||
LinkDiscriminator,
|
LinkKind,
|
||||||
SafeLink,
|
SafeLink,
|
||||||
Link,
|
Link,
|
||||||
LinkContent,
|
LinkContent,
|
||||||
Arguments,
|
Arguments,
|
||||||
|
Permalink,
|
||||||
} from "./types";
|
} from "./types";
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Verifiers are regexes which will match valid
|
||||||
|
* identifiers to their type. (This is a lie, they
|
||||||
|
* can return anything)
|
||||||
|
*/
|
||||||
|
type Verifier<A> = [RegExp, A];
|
||||||
|
export const roomVerifiers: Verifier<LinkKind.Alias | LinkKind.RoomId>[] = [
|
||||||
|
[/^#([^/:]+?):(.+)$/, LinkKind.Alias],
|
||||||
|
[/^!([^/:]+?):(.+)$/, LinkKind.RoomId],
|
||||||
|
];
|
||||||
|
export const verifiers: Verifier<LinkKind>[] = [
|
||||||
|
[/^[!#]([^/:]+?):(.+?)\/\$([^/:]+?):(.+?)$/, LinkKind.Permalink],
|
||||||
|
[/^@([^/:]+?):(.+)$/, LinkKind.UserId],
|
||||||
|
[/^\+([^/:]+?):(.+)$/, LinkKind.GroupId],
|
||||||
|
...roomVerifiers,
|
||||||
|
];
|
||||||
|
|
||||||
|
/*
|
||||||
|
* identifyTypeFromRegex applies the verifiers to the identifier and
|
||||||
|
* returns the identifier's type
|
||||||
|
*/
|
||||||
|
export function identifyTypeFromRegex<T, F>(
|
||||||
|
identifier: string,
|
||||||
|
verifiers: Verifier<T>[],
|
||||||
|
fail: F
|
||||||
|
): T | F {
|
||||||
|
if (identifier !== encodeURI(identifier)) {
|
||||||
|
return fail;
|
||||||
|
}
|
||||||
|
|
||||||
|
return verifiers.reduce<T | F>((kind, verifier) => {
|
||||||
|
if (kind !== fail) {
|
||||||
|
return kind;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (identifier.match(verifier[0])) {
|
||||||
|
return verifier[1];
|
||||||
|
}
|
||||||
|
|
||||||
|
return kind;
|
||||||
|
}, fail);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Parses a permalink.
|
||||||
|
* Assumes the permalink is correct.
|
||||||
|
*/
|
||||||
|
export function parsePermalink(identifier: string): Permalink {
|
||||||
|
const [roomLink, eventId] = identifier.split("/");
|
||||||
|
const roomKind = identifyTypeFromRegex(
|
||||||
|
roomLink,
|
||||||
|
roomVerifiers,
|
||||||
|
// This is hacky but we're assuming identifier is a valid permalink
|
||||||
|
LinkKind.Alias
|
||||||
|
);
|
||||||
|
|
||||||
|
return {
|
||||||
|
roomKind,
|
||||||
|
roomLink,
|
||||||
|
eventId,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Repalces null with undefined
|
||||||
|
*/
|
||||||
|
function bottomExchange<T>(nullable: T | null): T | undefined {
|
||||||
|
if (nullable === null) return undefined;
|
||||||
|
return nullable;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* parseArgs parses the <extra args> part of matrix.to links
|
||||||
|
*/
|
||||||
|
export function parseArgs(args: string): Arguments {
|
||||||
|
const params = new URLSearchParams(args);
|
||||||
|
|
||||||
|
return {
|
||||||
|
vias: params.getAll("via"),
|
||||||
|
client: bottomExchange(params.get("client")),
|
||||||
|
sharer: bottomExchange(params.get("sharer")),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* parseLink takes a striped matrix.to hash link (without the '#/' prefix)
|
* parseLink takes a striped matrix.to hash link (without the '#/' prefix)
|
||||||
* and parses into a Link. If the parse failed the result will
|
* and parses into a Link. If the parse failed the result will
|
||||||
* be ParseFailed
|
* be ParseFailed
|
||||||
*/
|
*/
|
||||||
export function parseHash(hash: string): Link {
|
export function parseHash(hash: string): Link {
|
||||||
const [identifier, args] = hash.split("?");
|
const [identifier, args] = hash.split("?");
|
||||||
|
|
||||||
const kind = identifyTypeFromRegex(
|
const kind = identifyTypeFromRegex(
|
||||||
identifier,
|
identifier,
|
||||||
verifiers,
|
verifiers,
|
||||||
LinkDiscriminator.ParseFailed
|
LinkKind.ParseFailed
|
||||||
);
|
);
|
||||||
|
|
||||||
let parsedLink: LinkContent = {
|
const parsedLink: LinkContent = {
|
||||||
identifier,
|
identifier,
|
||||||
arguments: parseArgs(args),
|
arguments: parseArgs(args),
|
||||||
originalLink: hash,
|
originalLink: hash,
|
||||||
};
|
};
|
||||||
|
|
||||||
if (kind === LinkDiscriminator.Permalink) {
|
if (kind === LinkKind.Permalink) {
|
||||||
const { roomKind, roomLink, eventId } = parsePermalink(identifier);
|
const { roomKind, roomLink, eventId } = parsePermalink(identifier);
|
||||||
|
|
||||||
|
return {
|
||||||
|
kind,
|
||||||
|
...parsedLink,
|
||||||
|
roomKind,
|
||||||
|
roomLink,
|
||||||
|
eventId,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
kind,
|
kind,
|
||||||
...parsedLink,
|
...parsedLink,
|
||||||
roomKind,
|
|
||||||
roomLink,
|
|
||||||
eventId,
|
|
||||||
};
|
};
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
kind,
|
|
||||||
...parsedLink,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Parses a permalink.
|
|
||||||
* Assumes the permalink is correct.
|
|
||||||
*/
|
|
||||||
export function parsePermalink(identifier: string) {
|
|
||||||
const [roomLink, eventId] = identifier.split("/");
|
|
||||||
const roomKind = identifyTypeFromRegex(
|
|
||||||
roomLink,
|
|
||||||
roomVerifiers,
|
|
||||||
// This is hacky but we're assuming identifier is a valid permalink
|
|
||||||
LinkDiscriminator.Alias
|
|
||||||
);
|
|
||||||
|
|
||||||
return {
|
|
||||||
roomKind,
|
|
||||||
roomLink,
|
|
||||||
eventId,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* parseArgs parses the <extra args> part of matrix.to links
|
|
||||||
*/
|
|
||||||
export function parseArgs(args: string): Arguments {
|
|
||||||
const params = new URLSearchParams(args);
|
|
||||||
const _federated = params.get("federated");
|
|
||||||
const federated = _federated !== null ? _federated === "true" : null;
|
|
||||||
|
|
||||||
return {
|
|
||||||
vias: params.getAll("via"),
|
|
||||||
federated: bottomExchange(federated),
|
|
||||||
client: bottomExchange(params.get("client")),
|
|
||||||
sharer: bottomExchange(params.get("sharer")),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Repalces null with undefined
|
|
||||||
*/
|
|
||||||
function bottomExchange<T>(nullable: T | null): T | undefined {
|
|
||||||
if (nullable === null) return undefined;
|
|
||||||
return nullable;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -96,72 +137,27 @@ function bottomExchange<T>(nullable: T | null): T | undefined {
|
||||||
* This is handy function in case the Link was constructed.
|
* This is handy function in case the Link was constructed.
|
||||||
*/
|
*/
|
||||||
export function toURL(origin: string, link: SafeLink): URL {
|
export function toURL(origin: string, link: SafeLink): URL {
|
||||||
switch (link.kind) {
|
const params = new URLSearchParams();
|
||||||
case LinkDiscriminator.GroupId:
|
const url = new URL(origin);
|
||||||
case LinkDiscriminator.UserId:
|
switch (link.kind) {
|
||||||
case LinkDiscriminator.RoomId:
|
case LinkKind.GroupId:
|
||||||
case LinkDiscriminator.Alias:
|
case LinkKind.UserId:
|
||||||
case LinkDiscriminator.Permalink:
|
case LinkKind.RoomId:
|
||||||
const params = new URLSearchParams();
|
case LinkKind.Alias:
|
||||||
forEach(link.arguments, (value, key) => {
|
case LinkKind.Permalink:
|
||||||
if (value === undefined) {
|
forEach(link.arguments, (value, key) => {
|
||||||
// do nothing
|
if (value === undefined) {
|
||||||
} else if (key === "vias") {
|
// do nothing
|
||||||
(<string[]>(<unknown>value)).forEach((via) =>
|
} else if (key === "vias") {
|
||||||
params.append("via", via)
|
(value as string[]).forEach((via) =>
|
||||||
);
|
params.append("via", via)
|
||||||
} else {
|
);
|
||||||
params.append(key, value.toString());
|
} else {
|
||||||
}
|
params.append(key, value.toString());
|
||||||
});
|
}
|
||||||
|
});
|
||||||
|
|
||||||
const url = new URL(origin);
|
url.hash = `/${link.identifier}?${params.toString()}`;
|
||||||
url.hash = `/${link.identifier}?${params.toString()}`;
|
|
||||||
return url;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Verifiers are regexes which will match valid
|
|
||||||
* identifiers to their type. (This is a lie, they
|
|
||||||
* can return anything)
|
|
||||||
*/
|
|
||||||
type Verifier<A> = [RegExp, A];
|
|
||||||
export const roomVerifiers: Verifier<
|
|
||||||
LinkDiscriminator.Alias | LinkDiscriminator.RoomId
|
|
||||||
>[] = [
|
|
||||||
[/^#([^\/:]+?):(.+)$/, LinkDiscriminator.Alias],
|
|
||||||
[/^!([^\/:]+?):(.+)$/, LinkDiscriminator.RoomId],
|
|
||||||
];
|
|
||||||
export const verifiers: Verifier<LinkDiscriminator>[] = [
|
|
||||||
[/^[\!#]([^\/:]+?):(.+?)\/\$([^\/:]+?):(.+?)$/, LinkDiscriminator.Permalink],
|
|
||||||
[/^@([^\/:]+?):(.+)$/, LinkDiscriminator.UserId],
|
|
||||||
[/^\+([^\/:]+?):(.+)$/, LinkDiscriminator.GroupId],
|
|
||||||
...roomVerifiers,
|
|
||||||
];
|
|
||||||
|
|
||||||
/*
|
|
||||||
* identifyTypeFromRegex applies the verifiers to the identifier and
|
|
||||||
* returns the identifier's type
|
|
||||||
*/
|
|
||||||
export function identifyTypeFromRegex<T, F>(
|
|
||||||
identifier: string,
|
|
||||||
verifiers: Verifier<T>[],
|
|
||||||
fail: F
|
|
||||||
): T | F {
|
|
||||||
if (identifier !== encodeURI(identifier)) {
|
|
||||||
return fail;
|
|
||||||
}
|
|
||||||
|
|
||||||
return verifiers.reduce<T | F>((discriminator, verifier) => {
|
|
||||||
if (discriminator !== fail) {
|
|
||||||
return discriminator;
|
|
||||||
}
|
}
|
||||||
|
return url;
|
||||||
if (identifier.match(verifier[0])) {
|
|
||||||
return verifier[1];
|
|
||||||
}
|
|
||||||
|
|
||||||
return discriminator;
|
|
||||||
}, fail);
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,56 +1,52 @@
|
||||||
export interface Arguments {
|
export interface Arguments {
|
||||||
vias: string[];
|
vias: string[];
|
||||||
// Schemeless http identifier
|
// Schemeless http identifier
|
||||||
client?: string;
|
client?: string;
|
||||||
// Indicates whether a room exists on a federating server (assumed to be the
|
// MXID
|
||||||
// default), or if the client must connect via the server identified by the
|
sharer?: string;
|
||||||
// room ID or event ID
|
|
||||||
federated?: boolean;
|
|
||||||
// MXID
|
|
||||||
sharer?: string;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface LinkContent {
|
export interface LinkContent {
|
||||||
identifier: string;
|
identifier: string;
|
||||||
arguments: Arguments;
|
arguments: Arguments;
|
||||||
originalLink: string;
|
originalLink: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export enum LinkDiscriminator {
|
export enum LinkKind {
|
||||||
Alias = "ALIAS",
|
Alias = "ALIAS",
|
||||||
RoomId = "ROOM_ID",
|
RoomId = "ROOM_ID",
|
||||||
UserId = "USER_ID",
|
UserId = "USER_ID",
|
||||||
Permalink = "PERMALINK",
|
Permalink = "PERMALINK",
|
||||||
GroupId = "GROUP_ID",
|
GroupId = "GROUP_ID",
|
||||||
ParseFailed = "PARSE_FAILED",
|
ParseFailed = "PARSE_FAILED",
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface Alias extends LinkContent {
|
export interface Alias extends LinkContent {
|
||||||
kind: LinkDiscriminator.Alias;
|
kind: LinkKind.Alias;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface RoomId extends LinkContent {
|
export interface RoomId extends LinkContent {
|
||||||
kind: LinkDiscriminator.RoomId;
|
kind: LinkKind.RoomId;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface UserId extends LinkContent {
|
export interface UserId extends LinkContent {
|
||||||
kind: LinkDiscriminator.UserId;
|
kind: LinkKind.UserId;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface GroupId extends LinkContent {
|
export interface GroupId extends LinkContent {
|
||||||
kind: LinkDiscriminator.GroupId;
|
kind: LinkKind.GroupId;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface Permalink extends LinkContent {
|
export interface Permalink extends LinkContent {
|
||||||
kind: LinkDiscriminator.Permalink;
|
kind: LinkKind.Permalink;
|
||||||
roomKind: LinkDiscriminator.RoomId | LinkDiscriminator.Alias;
|
roomKind: LinkKind.RoomId | LinkKind.Alias;
|
||||||
roomLink: string;
|
roomLink: string;
|
||||||
eventId: string;
|
eventId: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ParseFailed {
|
export interface ParseFailed {
|
||||||
kind: LinkDiscriminator.ParseFailed;
|
kind: LinkKind.ParseFailed;
|
||||||
originalLink: string;
|
originalLink: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export type SafeLink = Alias | RoomId | UserId | Permalink | GroupId;
|
export type SafeLink = Alias | RoomId | UserId | Permalink | GroupId;
|
||||||
|
|
Loading…
Reference in a new issue