use canonical_url for everything #187
|
@ -6,6 +6,7 @@
|
|||
./flow-typed
|
||||
|
||||
[options]
|
||||
suppress_comment=\\(.\\|\n\\)*\\$FlowFixMe
|
||||
module.system.node.resolve_dirname=./src
|
||||
module.name_mapper='^redux\(.*\)$' -> '<PROJECT_ROOT>/src/redux\1'
|
||||
module.name_mapper='^util\(.*\)$' -> '<PROJECT_ROOT>/src/util\1'
|
||||
|
|
353
dist/bundle.es.js
vendored
|
@ -897,45 +897,46 @@ const getSearchQueryString = (query, options = {}, includeUserOptions = false) =
|
|||
|
||||
var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; };
|
||||
|
||||
function _objectWithoutProperties(obj, keys) { var target = {}; for (var i in obj) { if (keys.indexOf(i) >= 0) continue; if (!Object.prototype.hasOwnProperty.call(obj, i)) continue; target[i] = obj[i]; } return target; }
|
||||
const channelNameMinLength = 1;
|
||||
const claimIdMaxLength = 40;
|
||||
|
||||
// see https://spec.lbry.com/#urls
|
||||
const regexInvalidURI = /[ =&#:$@%?;/\\"<>%{}|^~[\]`\u{0000}-\u{0008}\u{000b}-\u{000c}\u{000e}-\u{001F}\u{D800}-\u{DFFF}\u{FFFE}-\u{FFFF}]/u;
|
||||
const regexAddress = /^(b|r)(?=[^0OIl]{32,33})[0-9A-Za-z]{32,33}$/;
|
||||
const regexPartProtocol = '^((?:lbry://)?)';
|
||||
const regexPartStreamOrChannelName = '([^:$#/]*)';
|
||||
const regexPartModifierSeparator = '([:$#]?)([^/]*)';
|
||||
|
||||
/**
|
||||
* Parses a LBRY name into its component parts. Throws errors with user-friendly
|
||||
* messages for invalid names.
|
||||
*
|
||||
* N.B. that "name" indicates the value in the name position of the URI. For
|
||||
* claims for channel content, this will actually be the channel name, and
|
||||
* the content name is in the path (e.g. lbry://@channel/content)
|
||||
*
|
||||
* In most situations, you'll want to use the contentName and channelName keys
|
||||
* and ignore the name key.
|
||||
*
|
||||
* Returns a dictionary with keys:
|
||||
* - name (string): The value in the "name" position in the URI. Note that this
|
||||
* could be either content name or channel name; see above.
|
||||
* - path (string, if persent)
|
||||
* - claimSequence (int, if present)
|
||||
* - bidPosition (int, if present)
|
||||
* - claimId (string, if present)
|
||||
* - path (string)
|
||||
* - isChannel (boolean)
|
||||
* - contentName (string): For anon claims, the name; for channel claims, the path
|
||||
* - channelName (string, if present): Channel name without @
|
||||
* - streamName (string, if present)
|
||||
* - streamClaimId (string, if present)
|
||||
* - channelName (string, if present)
|
||||
* - channelClaimId (string, if present)
|
||||
* - primaryClaimSequence (int, if present)
|
||||
* - secondaryClaimSequence (int, if present)
|
||||
* - primaryBidPosition (int, if present)
|
||||
* - secondaryBidPosition (int, if present)
|
||||
*/
|
||||
function parseURI(URI, requireProto = false) {
|
||||
// Break into components. Empty sub-matches are converted to null
|
||||
const componentsRegex = new RegExp('^((?:lbry://)?)' + // protocol
|
||||
'([^:$#/]*)' + // claim name (stops at the first separator or end)
|
||||
'([:$#]?)([^/]*)' + // modifier separator, modifier (stops at the first path separator or end)
|
||||
'(/?)(.*)' // path separator, path
|
||||
);
|
||||
const [proto, claimName, modSep, modVal, pathSep, path] = componentsRegex.exec(URI).slice(1).map(match => match || null);
|
||||
|
||||
let contentName;
|
||||
function parseURI(URL, requireProto = false) {
|
||||
// Break into components. Empty sub-matches are converted to null
|
||||
const componentsRegex = new RegExp(regexPartProtocol + // protocol
|
||||
regexPartStreamOrChannelName + // stream or channel name (stops at the first separator or end)
|
||||
regexPartModifierSeparator + // modifier separator, modifier (stops at the first path separator or end)
|
||||
'(/?)' + // path separator, there should only be one (optional) slash to separate the stream and channel parts
|
||||
regexPartStreamOrChannelName + regexPartModifierSeparator);
|
||||
|
||||
const regexMatch = componentsRegex.exec(URL) || [];
|
||||
const [proto, ...rest] = regexMatch.slice(1).map(match => match || null);
|
||||
const path = rest.join('');
|
||||
const [streamNameOrChannelName, primaryModSeparator, primaryModValue, pathSep, possibleStreamName, secondaryModSeparator, secondaryModValue] = rest;
|
||||
|
||||
// Validate protocol
|
||||
if (requireProto && !proto) {
|
||||
|
@ -943,14 +944,15 @@ function parseURI(URI, requireProto = false) {
|
|||
}
|
||||
|
||||
// Validate and process name
|
||||
if (!claimName) {
|
||||
if (!streamNameOrChannelName) {
|
||||
throw new Error(__('URI does not include name.'));
|
||||
}
|
||||
|
||||
const isChannel = claimName.startsWith('@');
|
||||
const channelName = isChannel ? claimName.slice(1) : claimName;
|
||||
const includesChannel = streamNameOrChannelName.startsWith('@');
|
||||
const isChannel = streamNameOrChannelName.startsWith('@') && !possibleStreamName;
|
||||
const channelName = includesChannel && streamNameOrChannelName.slice(1);
|
||||
|
||||
if (isChannel) {
|
||||
if (includesChannel) {
|
||||
if (!channelName) {
|
||||
throw new Error(__('No channel name after @.'));
|
||||
}
|
||||
|
@ -958,30 +960,42 @@ function parseURI(URI, requireProto = false) {
|
|||
if (channelName.length < channelNameMinLength) {
|
||||
throw new Error(__(`Channel names must be at least %s characters.`, channelNameMinLength));
|
||||
}
|
||||
|
||||
contentName = path;
|
||||
}
|
||||
|
||||
const nameBadChars = (channelName || claimName).match(regexInvalidURI);
|
||||
if (nameBadChars) {
|
||||
throw new Error(__(`Invalid character %s in name: %s.`, nameBadChars.length === 1 ? '' : 's', nameBadChars.join(', ')));
|
||||
}
|
||||
// Validate and process modifier
|
||||
const [primaryClaimId, primaryClaimSequence, primaryBidPosition] = parseURIModifier(primaryModSeparator, primaryModValue);
|
||||
const [secondaryClaimId, secondaryClaimSequence, secondaryBidPosition] = parseURIModifier(secondaryModSeparator, secondaryModValue);
|
||||
const streamName = includesChannel ? possibleStreamName : streamNameOrChannelName;
|
||||
const streamClaimId = includesChannel ? secondaryClaimId : primaryClaimId;
|
||||
const channelClaimId = includesChannel && primaryClaimId;
|
||||
|
||||
// Validate and process modifier (claim ID, bid position or claim sequence)
|
||||
return _extends({
|
||||
isChannel,
|
||||
path
|
||||
}, streamName ? { streamName } : {}, streamClaimId ? { streamClaimId } : {}, channelName ? { channelName } : {}, channelClaimId ? { channelClaimId } : {}, primaryClaimSequence ? { primaryClaimSequence: parseInt(primaryClaimSequence, 10) } : {}, secondaryClaimSequence ? { secondaryClaimSequence: parseInt(secondaryClaimSequence, 10) } : {}, primaryBidPosition ? { primaryBidPosition: parseInt(primaryBidPosition, 10) } : {}, secondaryBidPosition ? { secondaryBidPosition: parseInt(secondaryBidPosition, 10) } : {}, {
|
||||
|
||||
// The values below should not be used for new uses of parseURI
|
||||
// They will not work properly with canonical_urls
|
||||
claimName: streamNameOrChannelName,
|
||||
claimId: primaryClaimId
|
||||
}, streamName ? { contentName: streamName } : {});
|
||||
}
|
||||
|
||||
function parseURIModifier(modSeperator, modValue) {
|
||||
let claimId;
|
||||
let claimSequence;
|
||||
let bidPosition;
|
||||
if (modSep) {
|
||||
if (!modVal) {
|
||||
throw new Error(__(`No modifier provided after separator %s.`, modSep));
|
||||
if (modSeperator) {
|
||||
if (!modValue) {
|
||||
throw new Error(__(`No modifier provided after separator %s.`, modSeperator));
|
||||
}
|
||||
|
||||
if (modSep === '#') {
|
||||
claimId = modVal;
|
||||
} else if (modSep === ':') {
|
||||
claimSequence = modVal;
|
||||
} else if (modSep === '$') {
|
||||
bidPosition = modVal;
|
||||
if (modSeperator === '#') {
|
||||
claimId = modValue;
|
||||
} else if (modSeperator === ':') {
|
||||
claimSequence = modValue;
|
||||
} else if (modSeperator === '$') {
|
||||
bidPosition = modValue;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -997,27 +1011,7 @@ function parseURI(URI, requireProto = false) {
|
|||
throw new Error(__('Bid position must be a number.'));
|
||||
}
|
||||
|
||||
// Validate and process path
|
||||
if (path) {
|
||||
if (!isChannel) {
|
||||
throw new Error(__('Only channel URIs may have a path.'));
|
||||
}
|
||||
|
||||
const pathBadChars = path.match(regexInvalidURI);
|
||||
if (pathBadChars) {
|
||||
throw new Error(__(`Invalid character in path: %s`, pathBadChars.join(', ')));
|
||||
}
|
||||
|
||||
contentName = path;
|
||||
} else if (pathSep) {
|
||||
throw new Error(__('No path provided after /'));
|
||||
}
|
||||
|
||||
return _extends({
|
||||
claimName,
|
||||
path,
|
||||
isChannel
|
||||
}, contentName ? { contentName } : {}, channelName ? { channelName } : {}, claimSequence ? { claimSequence: parseInt(claimSequence, 10) } : {}, bidPosition ? { bidPosition: parseInt(bidPosition, 10) } : {}, claimId ? { claimId } : {}, path ? { path } : {});
|
||||
return [claimId, claimSequence, bidPosition];
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1025,67 +1019,107 @@ function parseURI(URI, requireProto = false) {
|
|||
*
|
||||
* The channelName key will accept names with or without the @ prefix.
|
||||
*/
|
||||
function buildURI(URIObj, includeProto = true, protoDefault = 'lbry://') {
|
||||
const { claimId, claimSequence, bidPosition, contentName, channelName } = URIObj;
|
||||
function buildURI(UrlObj, includeProto = true, protoDefault = 'lbry://') {
|
||||
const {
|
||||
streamName,
|
||||
streamClaimId,
|
||||
channelName,
|
||||
channelClaimId,
|
||||
primaryClaimSequence,
|
||||
primaryBidPosition,
|
||||
secondaryClaimSequence,
|
||||
secondaryBidPosition
|
||||
} = UrlObj,
|
||||
deprecatedParts = _objectWithoutProperties(UrlObj, ['streamName', 'streamClaimId', 'channelName', 'channelClaimId', 'primaryClaimSequence', 'primaryBidPosition', 'secondaryClaimSequence', 'secondaryBidPosition']);
|
||||
const { claimId, claimName, contentName } = deprecatedParts;
|
||||
|
||||
let { claimName, path } = URIObj;
|
||||
|
||||
if (channelName) {
|
||||
const channelNameFormatted = channelName.startsWith('@') ? channelName : `@${channelName}`;
|
||||
if (!claimName) {
|
||||
claimName = channelNameFormatted;
|
||||
} else if (claimName !== channelNameFormatted) {
|
||||
throw new Error(__('Received a channel content URI, but claim name and channelName do not match. "name" represents the value in the name position of the URI (lbry://name...), which for channel content will be the channel name. In most cases, to construct a channel URI you should just pass channelName and contentName.'));
|
||||
}
|
||||
if (!claimName && !channelName && !streamName) {
|
||||
throw new Error(__("'claimName', 'channelName', and 'streamName' are all empty. One must be present to build a url."));
|
||||
}
|
||||
|
||||
if (contentName) {
|
||||
if (!claimName) {
|
||||
claimName = contentName;
|
||||
} else if (!path) {
|
||||
path = contentName;
|
||||
}
|
||||
if (path && path !== contentName) {
|
||||
throw new Error(__('Path and contentName do not match. Only one is required; most likely you wanted contentName.'));
|
||||
}
|
||||
}
|
||||
const formattedChannelName = channelName && (channelName.startsWith('@') ? channelName : `@${channelName}`);
|
||||
const primaryClaimName = claimName || contentName || formattedChannelName || streamName;
|
||||
const primaryClaimId = claimId || (formattedChannelName ? channelClaimId : streamClaimId);
|
||||
const secondaryClaimName = !claimName && contentName || (formattedChannelName ? streamName : null);
|
||||
const secondaryClaimId = secondaryClaimName && streamClaimId;
|
||||
|
||||
return (includeProto ? protoDefault : '') + claimName + (claimId ? `#${claimId}` : '') + (claimSequence ? `:${claimSequence}` : '') + (bidPosition ? `${bidPosition}` : '') + (path ? `/${path}` : '');
|
||||
return (includeProto ? protoDefault : '') +
|
||||
// primaryClaimName will always exist here because we throw above if there is no "name" value passed in
|
||||
// $FlowFixMe
|
||||
primaryClaimName + (primaryClaimId ? `#${primaryClaimId}` : '') + (primaryClaimSequence ? `:${primaryClaimSequence}` : '') + (primaryBidPosition ? `${primaryBidPosition}` : '') + (secondaryClaimName ? `/${secondaryClaimName}` : '') + (secondaryClaimId ? `#${secondaryClaimId}` : '') + (secondaryClaimSequence ? `:${secondaryClaimSequence}` : '') + (secondaryBidPosition ? `${secondaryBidPosition}` : '');
|
||||
}
|
||||
|
||||
/* Takes a parseable LBRY URI and converts it to standard, canonical format */
|
||||
function normalizeURI(URI) {
|
||||
const { claimName, path, bidPosition, claimSequence, claimId } = parseURI(URI);
|
||||
return buildURI({ claimName, path, claimSequence, bidPosition, claimId });
|
||||
/* Takes a parseable LBRY URL and converts it to standard, canonical format */
|
||||
function normalizeURI(URL) {
|
||||
const {
|
||||
streamName,
|
||||
streamClaimId,
|
||||
channelName,
|
||||
channelClaimId,
|
||||
primaryClaimSequence,
|
||||
primaryBidPosition,
|
||||
secondaryClaimSequence,
|
||||
secondaryBidPosition
|
||||
} = parseURI(URL);
|
||||
|
||||
return buildURI({
|
||||
streamName,
|
||||
streamClaimId,
|
||||
channelName,
|
||||
channelClaimId,
|
||||
primaryClaimSequence,
|
||||
primaryBidPosition,
|
||||
secondaryClaimSequence,
|
||||
secondaryBidPosition
|
||||
});
|
||||
}
|
||||
|
||||
function isURIValid(URI) {
|
||||
let parts;
|
||||
function isURIValid(URL) {
|
||||
try {
|
||||
parts = parseURI(normalizeURI(URI));
|
||||
parseURI(normalizeURI(URL));
|
||||
} catch (error) {
|
||||
return false;
|
||||
}
|
||||
return parts && parts.claimName;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
function isNameValid(claimName) {
|
||||
return !regexInvalidURI.test(claimName);
|
||||
}
|
||||
|
||||
function isURIClaimable(URI) {
|
||||
function isURIClaimable(URL) {
|
||||
let parts;
|
||||
try {
|
||||
parts = parseURI(normalizeURI(URI));
|
||||
parts = parseURI(normalizeURI(URL));
|
||||
} catch (error) {
|
||||
return false;
|
||||
}
|
||||
return parts && parts.claimName && !parts.claimId && !parts.bidPosition && !parts.claimSequence && !parts.isChannel && !parts.path;
|
||||
|
||||
return parts && parts.streamName && !parts.streamClaimId && !parts.isChannel;
|
||||
}
|
||||
|
||||
function convertToShareLink(URI) {
|
||||
const { claimName, path, bidPosition, claimSequence, claimId } = parseURI(URI);
|
||||
return buildURI({ claimName, path, claimSequence, bidPosition, claimId }, true, 'https://open.lbry.com/');
|
||||
function convertToShareLink(URL) {
|
||||
const {
|
||||
streamName,
|
||||
streamClaimId,
|
||||
channelName,
|
||||
channelClaimId,
|
||||
primaryBidPosition,
|
||||
primaryClaimSequence,
|
||||
secondaryBidPosition,
|
||||
secondaryClaimSequence
|
||||
} = parseURI(URL);
|
||||
return buildURI({
|
||||
streamName,
|
||||
streamClaimId,
|
||||
channelName,
|
||||
channelClaimId,
|
||||
primaryBidPosition,
|
||||
primaryClaimSequence,
|
||||
secondaryBidPosition,
|
||||
secondaryClaimSequence
|
||||
}, true, 'https://open.lbry.com/');
|
||||
}
|
||||
|
||||
var _extends$1 = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; };
|
||||
|
@ -1130,13 +1164,13 @@ const selectSearchSuggestions = reselect.createSelector(selectSearchValue, selec
|
|||
let searchSuggestions = [];
|
||||
try {
|
||||
const uri = normalizeURI(query);
|
||||
const { claimName, isChannel } = parseURI(uri);
|
||||
const { channelName, streamName, isChannel } = parseURI(uri);
|
||||
searchSuggestions.push({
|
||||
value: claimName,
|
||||
value: streamName,
|
||||
type: SEARCH_TYPES.SEARCH
|
||||
}, {
|
||||
value: uri,
|
||||
shorthand: isChannel ? claimName.slice(1) : claimName,
|
||||
shorthand: isChannel ? channelName : streamName,
|
||||
type: isChannel ? SEARCH_TYPES.CHANNEL : SEARCH_TYPES.FILE
|
||||
});
|
||||
} catch (e) {
|
||||
|
@ -1157,11 +1191,11 @@ const selectSearchSuggestions = reselect.createSelector(selectSearchValue, selec
|
|||
// determine if it's a channel
|
||||
try {
|
||||
const uri = normalizeURI(suggestion);
|
||||
const { claimName, isChannel } = parseURI(uri);
|
||||
const { channelName, streamName, isChannel } = parseURI(uri);
|
||||
|
||||
return {
|
||||
value: uri,
|
||||
shorthand: isChannel ? claimName.slice(1) : claimName,
|
||||
shorthand: isChannel ? channelName : streamName,
|
||||
type: isChannel ? SEARCH_TYPES.CHANNEL : SEARCH_TYPES.FILE
|
||||
};
|
||||
} catch (e) {
|
||||
|
@ -1377,7 +1411,7 @@ const selectTransactionListFilter = reselect.createSelector(selectState$1, state
|
|||
|
||||
var _extends$2 = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; };
|
||||
|
||||
function _objectWithoutProperties(obj, keys) { var target = {}; for (var i in obj) { if (keys.indexOf(i) >= 0) continue; if (!Object.prototype.hasOwnProperty.call(obj, i)) continue; target[i] = obj[i]; } return target; }
|
||||
function _objectWithoutProperties$1(obj, keys) { var target = {}; for (var i in obj) { if (keys.indexOf(i) >= 0) continue; if (!Object.prototype.hasOwnProperty.call(obj, i)) continue; target[i] = obj[i]; } return target; }
|
||||
|
||||
const matureTagMap = MATURE_TAGS.reduce((acc, tag) => _extends$2({}, acc, { [tag]: true }), {});
|
||||
|
||||
|
@ -1404,7 +1438,7 @@ const isClaimNsfw = claim => {
|
|||
function createNormalizedClaimSearchKey(options) {
|
||||
// Ignore page because we don't care what the last page searched was, we want everything
|
||||
// Ignore release_time because that will change depending on when you call claim_search ex: release_time: ">12344567"
|
||||
const rest = _objectWithoutProperties(options, ['page', 'release_time']);
|
||||
const rest = _objectWithoutProperties$1(options, ['page', 'release_time']);
|
||||
const query = JSON.stringify(rest);
|
||||
return query;
|
||||
}
|
||||
|
@ -1445,8 +1479,10 @@ const selectPendingClaims = reselect.createSelector(selectState$2, state => Obje
|
|||
|
||||
const makeSelectClaimIsPending = uri => reselect.createSelector(selectPendingById, pendingById => {
|
||||
let claimId;
|
||||
|
||||
try {
|
||||
({ claimId } = parseURI(uri));
|
||||
const { isChannel, channelClaimId, streamClaimId } = parseURI(uri);
|
||||
claimId = isChannel ? channelClaimId : streamClaimId;
|
||||
} catch (e) {}
|
||||
|
||||
if (claimId) {
|
||||
|
@ -1455,7 +1491,8 @@ const makeSelectClaimIsPending = uri => reselect.createSelector(selectPendingByI
|
|||
});
|
||||
|
||||
const makeSelectPendingByUri = uri => reselect.createSelector(selectPendingById, pendingById => {
|
||||
const { claimId } = parseURI(uri);
|
||||
const { isChannel, channelClaimId, streamClaimId } = parseURI(uri);
|
||||
const claimId = isChannel ? channelClaimId : streamClaimId;
|
||||
return pendingById[claimId];
|
||||
});
|
||||
|
||||
|
@ -1464,13 +1501,16 @@ const makeSelectClaimForUri = uri => reselect.createSelector(selectClaimsByUri,
|
|||
// It won't be in claimsByUri because resolving it will return nothing
|
||||
|
||||
let valid;
|
||||
let claimId;
|
||||
let channelClaimId;
|
||||
let streamClaimId;
|
||||
let isChannel;
|
||||
try {
|
||||
({ claimId } = parseURI(uri));
|
||||
({ isChannel, channelClaimId, streamClaimId } = parseURI(uri));
|
||||
valid = true;
|
||||
} catch (e) {}
|
||||
|
||||
if (valid) {
|
||||
const claimId = isChannel ? channelClaimId : streamClaimId;
|
||||
const pendingClaim = pendingById[claimId];
|
||||
|
||||
if (pendingClaim) {
|
||||
|
@ -1720,15 +1760,18 @@ const selectClaimSearchByQueryLastPageReached = reselect.createSelector(selectSt
|
|||
|
||||
const makeSelectShortUrlForUri = uri => reselect.createSelector(makeSelectClaimForUri(uri), claim => claim && claim.short_url);
|
||||
|
||||
const makeSelectCanonicalUrlForUri = uri => reselect.createSelector(makeSelectClaimForUri(uri), claim => claim && claim.canonical_url);
|
||||
|
||||
const makeSelectSupportsForUri = uri => reselect.createSelector(selectSupportsByOutpoint, makeSelectClaimForUri(uri), (byOutpoint, claim) => {
|
||||
if (!claim || !claim.is_mine) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const { claim_id: claimId } = claim;
|
||||
let total = parseFloat("0.0");
|
||||
let total = 0;
|
||||
|
||||
Object.values(byOutpoint).forEach(support => {
|
||||
// $FlowFixMe
|
||||
const { claim_id, amount } = support;
|
||||
total = claim_id === claimId && amount ? total + parseFloat(amount) : total;
|
||||
});
|
||||
|
@ -2433,10 +2476,10 @@ function doClaimSearch(options = {
|
|||
|
||||
const success = data => {
|
||||
const resolveInfo = {};
|
||||
const uris = [];
|
||||
const urls = [];
|
||||
data.items.forEach(stream => {
|
||||
resolveInfo[stream.permanent_url] = { stream };
|
||||
uris.push(stream.permanent_url);
|
||||
resolveInfo[stream.canonical_url] = { stream };
|
||||
urls.push(stream.canonical_url);
|
||||
});
|
||||
|
||||
dispatch({
|
||||
|
@ -2444,7 +2487,7 @@ function doClaimSearch(options = {
|
|||
data: {
|
||||
query,
|
||||
resolveInfo,
|
||||
uris,
|
||||
urls,
|
||||
append: options.page && options.page !== 1,
|
||||
pageSize: options.page_size
|
||||
}
|
||||
|
@ -2858,12 +2901,12 @@ function doSetFileListSort(page, value) {
|
|||
};
|
||||
}
|
||||
|
||||
function _objectWithoutProperties$1(obj, keys) { var target = {}; for (var i in obj) { if (keys.indexOf(i) >= 0) continue; if (!Object.prototype.hasOwnProperty.call(obj, i)) continue; target[i] = obj[i]; } return target; }
|
||||
function _objectWithoutProperties$2(obj, keys) { var target = {}; for (var i in obj) { if (keys.indexOf(i) >= 0) continue; if (!Object.prototype.hasOwnProperty.call(obj, i)) continue; target[i] = obj[i]; } return target; }
|
||||
|
||||
const selectState$5 = state => state.publish || {};
|
||||
|
||||
const selectPublishFormValues = reselect.createSelector(selectState$5, state => {
|
||||
const formValues = _objectWithoutProperties$1(state, ['pendingPublish']);
|
||||
const formValues = _objectWithoutProperties$2(state, ['pendingPublish']);
|
||||
return formValues;
|
||||
});
|
||||
const makeSelectPublishFormValue = item => reselect.createSelector(selectState$5, state => state[item]);
|
||||
|
@ -2876,8 +2919,16 @@ const selectIsStillEditing = reselect.createSelector(selectPublishFormValues, pu
|
|||
return false;
|
||||
}
|
||||
|
||||
const { isChannel: currentIsChannel, claimName: currentClaimName, contentName: currentContentName } = parseURI(uri);
|
||||
const { isChannel: editIsChannel, claimName: editClaimName, contentName: editContentName } = parseURI(editingURI);
|
||||
const {
|
||||
isChannel: currentIsChannel,
|
||||
claimName: currentClaimName,
|
||||
contentName: currentContentName
|
||||
} = parseURI(uri);
|
||||
const {
|
||||
isChannel: editIsChannel,
|
||||
claimName: editClaimName,
|
||||
contentName: editContentName
|
||||
} = parseURI(editingURI);
|
||||
|
||||
// Depending on the previous/current use of a channel, we need to compare different things
|
||||
// ex: going from a channel to anonymous, the new uri won't return contentName, so we need to use claimName
|
||||
|
@ -2904,7 +2955,7 @@ const selectIsResolvingPublishUris = reselect.createSelector(selectState$5, sele
|
|||
|
||||
let isResolvingShortUri;
|
||||
if (isChannel) {
|
||||
const shortUri = buildURI({ contentName: name });
|
||||
const shortUri = buildURI({ streamName: name });
|
||||
isResolvingShortUri = resolvingUris.includes(shortUri);
|
||||
}
|
||||
|
||||
|
@ -3376,13 +3427,21 @@ from, isBackgroundSearch = false) => (dispatch, getState) => {
|
|||
const actions = [];
|
||||
|
||||
data.forEach(result => {
|
||||
if (result.name) {
|
||||
const uri = buildURI({
|
||||
claimName: result.name,
|
||||
claimId: result.claimId
|
||||
});
|
||||
actions.push(doResolveUri(uri));
|
||||
uris.push(uri);
|
||||
if (result) {
|
||||
const { name, claimId } = result;
|
||||
const urlObj = {};
|
||||
|
||||
if (name.startsWith('@')) {
|
||||
urlObj.channelName = name;
|
||||
urlObj.channelClaimId = claimId;
|
||||
} else {
|
||||
urlObj.streamName = name;
|
||||
urlObj.streamClaimId = claimId;
|
||||
}
|
||||
|
||||
const url = buildURI(urlObj);
|
||||
actions.push(doResolveUri(url));
|
||||
uris.push(url);
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -3561,26 +3620,25 @@ function handleClaimAction(state, action) {
|
|||
const byId = Object.assign({}, state.byId);
|
||||
const channelClaimCounts = Object.assign({}, state.channelClaimCounts);
|
||||
|
||||
Object.entries(resolveInfo).forEach(([uri, resolveResponse]) => {
|
||||
Object.entries(resolveInfo).forEach(([url, resolveResponse]) => {
|
||||
// $FlowFixMe
|
||||
if (resolveResponse.claimsInChannel) {
|
||||
// $FlowFixMe
|
||||
channelClaimCounts[uri] = resolveResponse.claimsInChannel;
|
||||
const { claimsInChannel, stream, channel } = resolveResponse;
|
||||
if (claimsInChannel) {
|
||||
channelClaimCounts[url] = claimsInChannel;
|
||||
}
|
||||
});
|
||||
|
||||
// $FlowFixMe
|
||||
Object.entries(resolveInfo).forEach(([uri, { channel, stream }]) => {
|
||||
if (stream) {
|
||||
byId[stream.claim_id] = stream;
|
||||
byUri[uri] = stream.claim_id;
|
||||
byUri[url] = stream.claim_id;
|
||||
}
|
||||
|
||||
if (channel) {
|
||||
byId[channel.claim_id] = channel;
|
||||
byUri[stream ? channel.permanent_url : uri] = channel.claim_id;
|
||||
byUri[stream ? channel.canonical_url : url] = channel.claim_id;
|
||||
}
|
||||
|
||||
if (!stream && !channel) {
|
||||
byUri[uri] = null;
|
||||
byUri[url] = null;
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -3792,17 +3850,17 @@ reducers[CLAIM_SEARCH_COMPLETED] = (state, action) => {
|
|||
const fetchingClaimSearchByQuery = Object.assign({}, state.fetchingClaimSearchByQuery);
|
||||
const claimSearchByQuery = Object.assign({}, state.claimSearchByQuery);
|
||||
const claimSearchByQueryLastPageReached = Object.assign({}, state.claimSearchByQueryLastPageReached);
|
||||
const { append, query, uris, pageSize } = action.data;
|
||||
const { append, query, urls, pageSize } = action.data;
|
||||
|
||||
if (append) {
|
||||
// todo: check for duplicate uris when concatenating?
|
||||
claimSearchByQuery[query] = claimSearchByQuery[query] && claimSearchByQuery[query].length ? claimSearchByQuery[query].concat(uris) : uris;
|
||||
// todo: check for duplicate urls when concatenating?
|
||||
claimSearchByQuery[query] = claimSearchByQuery[query] && claimSearchByQuery[query].length ? claimSearchByQuery[query].concat(urls) : urls;
|
||||
} else {
|
||||
claimSearchByQuery[query] = uris;
|
||||
claimSearchByQuery[query] = urls;
|
||||
}
|
||||
|
||||
// the returned number of uris is less than the page size, so we're on the last page
|
||||
claimSearchByQueryLastPageReached[query] = uris.length < pageSize;
|
||||
// the returned number of urls is less than the page size, so we're on the last page
|
||||
claimSearchByQueryLastPageReached[query] = urls.length < pageSize;
|
||||
|
||||
delete fetchingClaimSearchByQuery[query];
|
||||
|
||||
|
@ -4232,7 +4290,7 @@ const notificationsReducer = handleActions({
|
|||
|
||||
var _extends$a = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; };
|
||||
|
||||
function _objectWithoutProperties$2(obj, keys) { var target = {}; for (var i in obj) { if (keys.indexOf(i) >= 0) continue; if (!Object.prototype.hasOwnProperty.call(obj, i)) continue; target[i] = obj[i]; } return target; }
|
||||
function _objectWithoutProperties$3(obj, keys) { var target = {}; for (var i in obj) { if (keys.indexOf(i) >= 0) continue; if (!Object.prototype.hasOwnProperty.call(obj, i)) continue; target[i] = obj[i]; } return target; }
|
||||
|
||||
const defaultState$6 = {
|
||||
editingURI: undefined,
|
||||
|
@ -4282,7 +4340,7 @@ const publishReducer = handleActions({
|
|||
publishSuccess: true
|
||||
}),
|
||||
[DO_PREPARE_EDIT]: (state, action) => {
|
||||
const publishData = _objectWithoutProperties$2(action.data, []);
|
||||
const publishData = _objectWithoutProperties$3(action.data, []);
|
||||
const { channel, name, uri } = publishData;
|
||||
|
||||
// The short uri is what is presented to the user
|
||||
|
@ -4892,6 +4950,7 @@ exports.isNameValid = isNameValid;
|
|||
exports.isURIClaimable = isURIClaimable;
|
||||
exports.isURIValid = isURIValid;
|
||||
exports.makeSelectAmountForUri = makeSelectAmountForUri;
|
||||
exports.makeSelectCanonicalUrlForUri = makeSelectCanonicalUrlForUri;
|
||||
exports.makeSelectChannelForClaimUri = makeSelectChannelForClaimUri;
|
||||
exports.makeSelectClaimForUri = makeSelectClaimForUri;
|
||||
exports.makeSelectClaimIsMine = makeSelectClaimIsMine;
|
||||
|
|
1
dist/flow-typed/Claim.js
vendored
|
@ -23,6 +23,7 @@ declare type GenericClaim = {
|
|||
decoded_claim: boolean, // Not available currently https://github.com/lbryio/lbry/issues/2044
|
||||
timestamp?: number, // date of last transaction
|
||||
height: number, // block height the tx was confirmed
|
||||
is_mine: boolean,
|
||||
name: string,
|
||||
normalized_name: string, // `name` normalized via unicode NFD spec,
|
||||
nout: number, // index number for an output of a tx
|
||||
|
|
2
dist/flow-typed/Lbry.js
vendored
|
@ -65,7 +65,7 @@ declare type VersionResponse = {
|
|||
|
||||
declare type ResolveResponse = {
|
||||
// Keys are the url(s) passed to resolve
|
||||
[string]: Claim | { error?: {} },
|
||||
[string]: { error?: {}, stream?: StreamClaim, channel?: ChannelClaim, claimsInChannel?: number },
|
||||
};
|
||||
|
||||
declare type GetResponse = FileListItem & { error?: string };
|
||||
|
|
2
dist/flow-typed/i18n.js
vendored
Normal file
|
@ -0,0 +1,2 @@
|
|||
// @flow
|
||||
declare function __(a: string, b?: string | number): string;
|
1
flow-typed/Claim.js
vendored
|
@ -23,6 +23,7 @@ declare type GenericClaim = {
|
|||
decoded_claim: boolean, // Not available currently https://github.com/lbryio/lbry/issues/2044
|
||||
timestamp?: number, // date of last transaction
|
||||
height: number, // block height the tx was confirmed
|
||||
is_mine: boolean,
|
||||
name: string,
|
||||
normalized_name: string, // `name` normalized via unicode NFD spec,
|
||||
nout: number, // index number for an output of a tx
|
||||
|
|
2
flow-typed/Lbry.js
vendored
|
@ -65,7 +65,7 @@ declare type VersionResponse = {
|
|||
|
||||
declare type ResolveResponse = {
|
||||
// Keys are the url(s) passed to resolve
|
||||
[string]: Claim | { error?: {} },
|
||||
[string]: { error?: {}, stream?: StreamClaim, channel?: ChannelClaim, claimsInChannel?: number },
|
||||
};
|
||||
|
||||
declare type GetResponse = FileListItem & { error?: string };
|
||||
|
|
2
flow-typed/i18n.js
vendored
Normal file
|
@ -0,0 +1,2 @@
|
|||
// @flow
|
||||
declare function __(a: string, b?: string | number): string;
|
20
flow-typed/lbryURI.js
vendored
Normal file
|
@ -0,0 +1,20 @@
|
|||
// @flow
|
||||
declare type LbryUrlObj = {
|
||||
// Path and channel will always exist when calling parseURI
|
||||
// But they may not exist when code calls buildURI
|
||||
isChannel?: boolean,
|
||||
path?: string,
|
||||
streamName?: string,
|
||||
streamClaimId?: string,
|
||||
channelName?: string,
|
||||
channelClaimId?: string,
|
||||
primaryClaimSequence?: number,
|
||||
secondaryClaimSequence?: number,
|
||||
primaryBidPosition?: number,
|
||||
secondaryBidPosition?: number,
|
||||
|
||||
// Below are considered deprecated and should not be used due to unreliableness with claim.canonical_url
|
||||
claimName?: string,
|
||||
claimId?: string,
|
||||
contentName?: string,
|
||||
};
|
|
@ -21,7 +21,7 @@
|
|||
"main": "dist/bundle.es.js",
|
||||
"module": "dist/bundle.es.js",
|
||||
"scripts": {
|
||||
"build": "rollup --config",
|
||||
"build": "NODE_ENV=production rollup --config",
|
||||
"dev": "rollup --config --watch",
|
||||
"precommit": "flow check && lint-staged",
|
||||
"lint": "eslint 'src/**/*.js' --fix",
|
||||
|
@ -59,7 +59,8 @@
|
|||
"rollup-plugin-copy": "^1.1.0",
|
||||
"rollup-plugin-eslint": "^5.1.0",
|
||||
"rollup-plugin-flow": "^1.1.1",
|
||||
"rollup-plugin-includepaths": "^0.2.3"
|
||||
"rollup-plugin-includepaths": "^0.2.3",
|
||||
"rollup-plugin-replace": "^2.2.0"
|
||||
},
|
||||
"engines": {
|
||||
"yarn": "^1.3"
|
||||
|
|
|
@ -2,6 +2,7 @@ import babel from 'rollup-plugin-babel';
|
|||
import flow from 'rollup-plugin-flow';
|
||||
import includePaths from 'rollup-plugin-includepaths';
|
||||
import copy from 'rollup-plugin-copy';
|
||||
import replace from 'rollup-plugin-replace';
|
||||
|
||||
let includePathOptions = {
|
||||
include: {},
|
||||
|
@ -10,6 +11,8 @@ let includePathOptions = {
|
|||
extensions: ['.js'],
|
||||
};
|
||||
|
||||
const production = process.env.NODE_ENV === 'production';
|
||||
|
||||
export default {
|
||||
input: 'src/index.js',
|
||||
output: {
|
||||
|
@ -24,5 +27,10 @@ export default {
|
|||
presets: ['stage-2'],
|
||||
}),
|
||||
copy({ targets: ['flow-typed'] }),
|
||||
replace({
|
||||
'process.env.NODE_ENV': production
|
||||
? JSON.stringify('production')
|
||||
: JSON.stringify('development'),
|
||||
}),
|
||||
],
|
||||
};
|
||||
|
|
|
@ -172,6 +172,7 @@ export {
|
|||
makeSelectPendingByUri,
|
||||
makeSelectClaimsInChannelForCurrentPageState,
|
||||
makeSelectShortUrlForUri,
|
||||
makeSelectCanonicalUrlForUri,
|
||||
makeSelectSupportsForUri,
|
||||
selectPendingById,
|
||||
selectClaimsById,
|
||||
|
|
320
src/lbryURI.js
|
@ -1,46 +1,55 @@
|
|||
// @flow
|
||||
const isProduction = process.env.NODE_ENV === 'production';
|
||||
const channelNameMinLength = 1;
|
||||
const claimIdMaxLength = 40;
|
||||
|
||||
// see https://spec.lbry.com/#urls
|
||||
export const regexInvalidURI = /[ =&#:$@%?;/\\"<>%{}|^~[\]`\u{0000}-\u{0008}\u{000b}-\u{000c}\u{000e}-\u{001F}\u{D800}-\u{DFFF}\u{FFFE}-\u{FFFF}]/u;
|
||||
export const regexAddress = /^(b|r)(?=[^0OIl]{32,33})[0-9A-Za-z]{32,33}$/;
|
||||
const regexPartProtocol = '^((?:lbry://)?)';
|
||||
const regexPartStreamOrChannelName = '([^:$#/]*)';
|
||||
const regexPartModifierSeparator = '([:$#]?)([^/]*)';
|
||||
|
||||
/**
|
||||
* Parses a LBRY name into its component parts. Throws errors with user-friendly
|
||||
* messages for invalid names.
|
||||
*
|
||||
* N.B. that "name" indicates the value in the name position of the URI. For
|
||||
* claims for channel content, this will actually be the channel name, and
|
||||
* the content name is in the path (e.g. lbry://@channel/content)
|
||||
*
|
||||
* In most situations, you'll want to use the contentName and channelName keys
|
||||
* and ignore the name key.
|
||||
*
|
||||
* Returns a dictionary with keys:
|
||||
* - name (string): The value in the "name" position in the URI. Note that this
|
||||
* could be either content name or channel name; see above.
|
||||
* - path (string, if persent)
|
||||
* - claimSequence (int, if present)
|
||||
* - bidPosition (int, if present)
|
||||
* - claimId (string, if present)
|
||||
* - path (string)
|
||||
* - isChannel (boolean)
|
||||
* - contentName (string): For anon claims, the name; for channel claims, the path
|
||||
* - channelName (string, if present): Channel name without @
|
||||
* - streamName (string, if present)
|
||||
* - streamClaimId (string, if present)
|
||||
* - channelName (string, if present)
|
||||
* - channelClaimId (string, if present)
|
||||
* - primaryClaimSequence (int, if present)
|
||||
* - secondaryClaimSequence (int, if present)
|
||||
* - primaryBidPosition (int, if present)
|
||||
* - secondaryBidPosition (int, if present)
|
||||
*/
|
||||
export function parseURI(URI, requireProto = false) {
|
||||
|
||||
export function parseURI(URL: string, requireProto: boolean = false): LbryUrlObj {
|
||||
// Break into components. Empty sub-matches are converted to null
|
||||
const componentsRegex = new RegExp(
|
||||
'^((?:lbry://)?)' + // protocol
|
||||
'([^:$#/]*)' + // claim name (stops at the first separator or end)
|
||||
'([:$#]?)([^/]*)' + // modifier separator, modifier (stops at the first path separator or end)
|
||||
'(/?)(.*)' // path separator, path
|
||||
regexPartProtocol + // protocol
|
||||
regexPartStreamOrChannelName + // stream or channel name (stops at the first separator or end)
|
||||
regexPartModifierSeparator + // modifier separator, modifier (stops at the first path separator or end)
|
||||
'(/?)' + // path separator, there should only be one (optional) slash to separate the stream and channel parts
|
||||
regexPartStreamOrChannelName +
|
||||
regexPartModifierSeparator
|
||||
);
|
||||
const [proto, claimName, modSep, modVal, pathSep, path] = componentsRegex
|
||||
.exec(URI)
|
||||
.slice(1)
|
||||
.map(match => match || null);
|
||||
|
||||
let contentName;
|
||||
const regexMatch = componentsRegex.exec(URL) || [];
|
||||
const [proto, ...rest] = regexMatch.slice(1).map(match => match || null);
|
||||
const path = rest.join('');
|
||||
const [
|
||||
streamNameOrChannelName,
|
||||
primaryModSeparator,
|
||||
primaryModValue,
|
||||
pathSep,
|
||||
possibleStreamName,
|
||||
secondaryModSeparator,
|
||||
secondaryModValue,
|
||||
] = rest;
|
||||
|
||||
// Validate protocol
|
||||
if (requireProto && !proto) {
|
||||
|
@ -48,14 +57,15 @@ export function parseURI(URI, requireProto = false) {
|
|||
}
|
||||
|
||||
// Validate and process name
|
||||
if (!claimName) {
|
||||
if (!streamNameOrChannelName) {
|
||||
throw new Error(__('URI does not include name.'));
|
||||
}
|
||||
|
||||
const isChannel = claimName.startsWith('@');
|
||||
const channelName = isChannel ? claimName.slice(1) : claimName;
|
||||
const includesChannel = streamNameOrChannelName.startsWith('@');
|
||||
I like removing validation from this. We already have I like removing validation from this. We already have `isURIValid` to validate
|
||||
const isChannel = streamNameOrChannelName.startsWith('@') && !possibleStreamName;
|
||||
const channelName = includesChannel && streamNameOrChannelName.slice(1);
|
||||
|
||||
if (isChannel) {
|
||||
if (includesChannel) {
|
||||
if (!channelName) {
|
||||
Not sure the best way to do this. Should Not sure the best way to do this.
Should `path` include the claimId? Should there be `path` and `pathClaimId`?
According to https://lbry.tech/spec#urls, the Channel name, claim name, and any modifiers would be sub-properties of the path (and could potentially be returned/parsed out by this function as well). @lyoshenka, please correct me if I'm wrong about this. @seanyesmunt we should try to have this function use the same names and standards that the spec specifies. If you think we should do something different than what the spec says, we can also consider amending the spec. According to https://lbry.tech/spec#urls, the `path` should include the channel name, claim name, and any modifiers. The path is everything between the scheme and any query parameters.
Channel name, claim name, and any modifiers would be sub-properties of the path (and could potentially be returned/parsed out by this function as well).
@lyoshenka, please correct me if I'm wrong about this.
@seanyesmunt we should try to have this function use the same names and standards that the spec specifies. If you think we should do something different than what the spec says, we can also consider amending the spec.
|
||||
throw new Error(__('No channel name after @.'));
|
||||
}
|
||||
|
@ -63,36 +73,58 @@ export function parseURI(URI, requireProto = false) {
|
|||
if (channelName.length < channelNameMinLength) {
|
||||
throw new Error(__(`Channel names must be at least %s characters.`, channelNameMinLength));
|
||||
}
|
||||
|
||||
contentName = path;
|
||||
}
|
||||
|
||||
const nameBadChars = (channelName || claimName).match(regexInvalidURI);
|
||||
if (nameBadChars) {
|
||||
throw new Error(
|
||||
__(
|
||||
`Invalid character %s in name: %s.`,
|
||||
nameBadChars.length === 1 ? '' : 's',
|
||||
nameBadChars.join(', ')
|
||||
)
|
||||
);
|
||||
}
|
||||
// Validate and process modifier
|
||||
const [primaryClaimId, primaryClaimSequence, primaryBidPosition] = parseURIModifier(
|
||||
primaryModSeparator,
|
||||
primaryModValue
|
||||
);
|
||||
const [secondaryClaimId, secondaryClaimSequence, secondaryBidPosition] = parseURIModifier(
|
||||
secondaryModSeparator,
|
||||
secondaryModValue
|
||||
);
|
||||
const streamName = includesChannel ? possibleStreamName : streamNameOrChannelName;
|
||||
const streamClaimId = includesChannel ? secondaryClaimId : primaryClaimId;
|
||||
const channelClaimId = includesChannel && primaryClaimId;
|
||||
|
||||
// Validate and process modifier (claim ID, bid position or claim sequence)
|
||||
return {
|
||||
isChannel,
|
||||
path,
|
||||
...(streamName ? { streamName } : {}),
|
||||
...(streamClaimId ? { streamClaimId } : {}),
|
||||
...(channelName ? { channelName } : {}),
|
||||
...(channelClaimId ? { channelClaimId } : {}),
|
||||
...(primaryClaimSequence ? { primaryClaimSequence: parseInt(primaryClaimSequence, 10) } : {}),
|
||||
...(secondaryClaimSequence
|
||||
? { secondaryClaimSequence: parseInt(secondaryClaimSequence, 10) }
|
||||
: {}),
|
||||
...(primaryBidPosition ? { primaryBidPosition: parseInt(primaryBidPosition, 10) } : {}),
|
||||
...(secondaryBidPosition ? { secondaryBidPosition: parseInt(secondaryBidPosition, 10) } : {}),
|
||||
|
||||
// The values below should not be used for new uses of parseURI
|
||||
// They will not work properly with canonical_urls
|
||||
claimName: streamNameOrChannelName,
|
||||
claimId: primaryClaimId,
|
||||
...(streamName ? { contentName: streamName } : {}),
|
||||
};
|
||||
}
|
||||
|
||||
function parseURIModifier(modSeperator: ?string, modValue: ?string) {
|
||||
let claimId;
|
||||
let claimSequence;
|
||||
let bidPosition;
|
||||
if (modSep) {
|
||||
if (!modVal) {
|
||||
throw new Error(__(`No modifier provided after separator %s.`, modSep));
|
||||
if (modSeperator) {
|
||||
if (!modValue) {
|
||||
throw new Error(__(`No modifier provided after separator %s.`, modSeperator));
|
||||
}
|
||||
|
||||
if (modSep === '#') {
|
||||
claimId = modVal;
|
||||
} else if (modSep === ':') {
|
||||
claimSequence = modVal;
|
||||
} else if (modSep === '$') {
|
||||
bidPosition = modVal;
|
||||
if (modSeperator === '#') {
|
||||
claimId = modValue;
|
||||
} else if (modSeperator === ':') {
|
||||
claimSequence = modValue;
|
||||
} else if (modSeperator === '$') {
|
||||
bidPosition = modValue;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -108,33 +140,7 @@ export function parseURI(URI, requireProto = false) {
|
|||
throw new Error(__('Bid position must be a number.'));
|
||||
}
|
||||
|
||||
// Validate and process path
|
||||
if (path) {
|
||||
if (!isChannel) {
|
||||
throw new Error(__('Only channel URIs may have a path.'));
|
||||
}
|
||||
|
||||
const pathBadChars = path.match(regexInvalidURI);
|
||||
if (pathBadChars) {
|
||||
throw new Error(__(`Invalid character in path: %s`, pathBadChars.join(', ')));
|
||||
}
|
||||
|
||||
contentName = path;
|
||||
} else if (pathSep) {
|
||||
throw new Error(__('No path provided after /'));
|
||||
}
|
||||
|
||||
return {
|
||||
claimName,
|
||||
path,
|
||||
isChannel,
|
||||
...(contentName ? { contentName } : {}),
|
||||
...(channelName ? { channelName } : {}),
|
||||
...(claimSequence ? { claimSequence: parseInt(claimSequence, 10) } : {}),
|
||||
...(bidPosition ? { bidPosition: parseInt(bidPosition, 10) } : {}),
|
||||
...(claimId ? { claimId } : {}),
|
||||
...(path ? { path } : {}),
|
||||
};
|
||||
return [claimId, claimSequence, bidPosition];
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -142,91 +148,145 @@ export function parseURI(URI, requireProto = false) {
|
|||
*
|
||||
* The channelName key will accept names with or without the @ prefix.
|
||||
*/
|
||||
export function buildURI(URIObj, includeProto = true, protoDefault = 'lbry://') {
|
||||
const { claimId, claimSequence, bidPosition, contentName, channelName } = URIObj;
|
||||
export function buildURI(
|
||||
UrlObj: LbryUrlObj,
|
||||
includeProto: boolean = true,
|
||||
protoDefault: string = 'lbry://'
|
||||
): string {
|
||||
const {
|
||||
streamName,
|
||||
streamClaimId,
|
||||
channelName,
|
||||
channelClaimId,
|
||||
primaryClaimSequence,
|
||||
primaryBidPosition,
|
||||
secondaryClaimSequence,
|
||||
secondaryBidPosition,
|
||||
...deprecatedParts
|
||||
} = UrlObj;
|
||||
const { claimId, claimName, contentName } = deprecatedParts;
|
||||
|
||||
let { claimName, path } = URIObj;
|
||||
|
||||
if (channelName) {
|
||||
const channelNameFormatted = channelName.startsWith('@') ? channelName : `@${channelName}`;
|
||||
if (!claimName) {
|
||||
claimName = channelNameFormatted;
|
||||
} else if (claimName !== channelNameFormatted) {
|
||||
throw new Error(
|
||||
if (!isProduction) {
|
||||
if (claimId) {
|
||||
console.error(
|
||||
__("'claimId' should no longer be used. Use 'streamClaimId' or 'channelClaimId' instead")
|
||||
);
|
||||
}
|
||||
if (claimName) {
|
||||
console.error(
|
||||
__(
|
||||
'Received a channel content URI, but claim name and channelName do not match. "name" represents the value in the name position of the URI (lbry://name...), which for channel content will be the channel name. In most cases, to construct a channel URI you should just pass channelName and contentName.'
|
||||
"'claimName' should no longer be used. Use 'streamClaimName' or 'channelClaimName' instead"
|
||||
)
|
||||
);
|
||||
}
|
||||
if (contentName) {
|
||||
console.error(__("'contentName' should no longer be used. Use 'streamName' instead"));
|
||||
}
|
||||
}
|
||||
|
||||
if (contentName) {
|
||||
if (!claimName) {
|
||||
claimName = contentName;
|
||||
} else if (!path) {
|
||||
path = contentName;
|
||||
}
|
||||
if (path && path !== contentName) {
|
||||
throw new Error(
|
||||
__(
|
||||
'Path and contentName do not match. Only one is required; most likely you wanted contentName.'
|
||||
)
|
||||
);
|
||||
}
|
||||
if (!claimName && !channelName && !streamName) {
|
||||
throw new Error(
|
||||
__(
|
||||
"'claimName', 'channelName', and 'streamName' are all empty. One must be present to build a url."
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
const formattedChannelName =
|
||||
channelName && (channelName.startsWith('@') ? channelName : `@${channelName}`);
|
||||
const primaryClaimName = claimName || contentName || formattedChannelName || streamName;
|
||||
const primaryClaimId = claimId || (formattedChannelName ? channelClaimId : streamClaimId);
|
||||
const secondaryClaimName =
|
||||
(!claimName && contentName) || (formattedChannelName ? streamName : null);
|
||||
const secondaryClaimId = secondaryClaimName && streamClaimId;
|
||||
|
||||
return (
|
||||
(includeProto ? protoDefault : '') +
|
||||
claimName +
|
||||
(claimId ? `#${claimId}` : '') +
|
||||
(claimSequence ? `:${claimSequence}` : '') +
|
||||
(bidPosition ? `${bidPosition}` : '') +
|
||||
(path ? `/${path}` : '')
|
||||
// primaryClaimName will always exist here because we throw above if there is no "name" value passed in
|
||||
// $FlowFixMe
|
||||
primaryClaimName +
|
||||
(primaryClaimId ? `#${primaryClaimId}` : '') +
|
||||
(primaryClaimSequence ? `:${primaryClaimSequence}` : '') +
|
||||
(primaryBidPosition ? `${primaryBidPosition}` : '') +
|
||||
(secondaryClaimName ? `/${secondaryClaimName}` : '') +
|
||||
(secondaryClaimId ? `#${secondaryClaimId}` : '') +
|
||||
(secondaryClaimSequence ? `:${secondaryClaimSequence}` : '') +
|
||||
(secondaryBidPosition ? `${secondaryBidPosition}` : '')
|
||||
);
|
||||
}
|
||||
|
||||
/* Takes a parseable LBRY URI and converts it to standard, canonical format */
|
||||
export function normalizeURI(URI) {
|
||||
const { claimName, path, bidPosition, claimSequence, claimId } = parseURI(URI);
|
||||
return buildURI({ claimName, path, claimSequence, bidPosition, claimId });
|
||||
/* Takes a parseable LBRY URL and converts it to standard, canonical format */
|
||||
export function normalizeURI(URL: string) {
|
||||
const {
|
||||
streamName,
|
||||
streamClaimId,
|
||||
channelName,
|
||||
channelClaimId,
|
||||
primaryClaimSequence,
|
||||
primaryBidPosition,
|
||||
secondaryClaimSequence,
|
||||
secondaryBidPosition,
|
||||
} = parseURI(URL);
|
||||
|
||||
return buildURI({
|
||||
streamName,
|
||||
streamClaimId,
|
||||
channelName,
|
||||
channelClaimId,
|
||||
primaryClaimSequence,
|
||||
primaryBidPosition,
|
||||
secondaryClaimSequence,
|
||||
secondaryBidPosition,
|
||||
});
|
||||
}
|
||||
|
||||
export function isURIValid(URI) {
|
||||
let parts;
|
||||
export function isURIValid(URL: string): boolean {
|
||||
try {
|
||||
parts = parseURI(normalizeURI(URI));
|
||||
parseURI(normalizeURI(URL));
|
||||
} catch (error) {
|
||||
return false;
|
||||
}
|
||||
return parts && parts.claimName;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
export function isNameValid(claimName) {
|
||||
export function isNameValid(claimName: string) {
|
||||
return !regexInvalidURI.test(claimName);
|
||||
}
|
||||
|
||||
export function isURIClaimable(URI) {
|
||||
export function isURIClaimable(URL: string) {
|
||||
let parts;
|
||||
try {
|
||||
parts = parseURI(normalizeURI(URI));
|
||||
parts = parseURI(normalizeURI(URL));
|
||||
} catch (error) {
|
||||
return false;
|
||||
}
|
||||
return (
|
||||
parts &&
|
||||
parts.claimName &&
|
||||
!parts.claimId &&
|
||||
!parts.bidPosition &&
|
||||
!parts.claimSequence &&
|
||||
!parts.isChannel &&
|
||||
!parts.path
|
||||
);
|
||||
|
||||
return parts && parts.streamName && !parts.streamClaimId && !parts.isChannel;
|
||||
}
|
||||
|
||||
export function convertToShareLink(URI) {
|
||||
const { claimName, path, bidPosition, claimSequence, claimId } = parseURI(URI);
|
||||
export function convertToShareLink(URL: string) {
|
||||
const {
|
||||
streamName,
|
||||
streamClaimId,
|
||||
channelName,
|
||||
channelClaimId,
|
||||
primaryBidPosition,
|
||||
primaryClaimSequence,
|
||||
secondaryBidPosition,
|
||||
secondaryClaimSequence,
|
||||
} = parseURI(URL);
|
||||
return buildURI(
|
||||
{ claimName, path, claimSequence, bidPosition, claimId },
|
||||
{
|
||||
streamName,
|
||||
streamClaimId,
|
||||
channelName,
|
||||
channelClaimId,
|
||||
primaryBidPosition,
|
||||
primaryClaimSequence,
|
||||
secondaryBidPosition,
|
||||
secondaryClaimSequence,
|
||||
},
|
||||
true,
|
||||
'https://open.lbry.com/'
|
||||
);
|
||||
|
|
|
@ -328,10 +328,10 @@ export function doClaimSearch(
|
|||
|
||||
why keep two sets of URIs? why keep two sets of URIs?
|
||||
const success = (data: ClaimSearchResponse) => {
|
||||
const resolveInfo = {};
|
||||
const uris = [];
|
||||
const urls = [];
|
||||
data.items.forEach((stream: Claim) => {
|
||||
resolveInfo[stream.permanent_url] = { stream };
|
||||
uris.push(stream.permanent_url);
|
||||
resolveInfo[stream.canonical_url] = { stream };
|
||||
urls.push(stream.canonical_url);
|
||||
});
|
||||
|
||||
dispatch({
|
||||
|
@ -339,7 +339,7 @@ export function doClaimSearch(
|
|||
data: {
|
||||
query,
|
||||
resolveInfo,
|
||||
uris,
|
||||
urls,
|
||||
append: options.page && options.page !== 1,
|
||||
pageSize: options.page_size,
|
||||
},
|
||||
|
|
|
@ -112,18 +112,26 @@ export const doSearch = (
|
|||
|
||||
fetch(`${CONNECTION_STRING}search?${queryWithOptions}`)
|
||||
.then(handleFetchResponse)
|
||||
.then((data: Array<{ name: String, claimId: string }>) => {
|
||||
.then((data: Array<{ name: string, claimId: string }>) => {
|
||||
const uris = [];
|
||||
const actions = [];
|
||||
|
||||
data.forEach(result => {
|
||||
if (result.name) {
|
||||
const uri = buildURI({
|
||||
claimName: result.name,
|
||||
claimId: result.claimId,
|
||||
});
|
||||
actions.push(doResolveUri(uri));
|
||||
uris.push(uri);
|
||||
if (result) {
|
||||
const { name, claimId } = result;
|
||||
const urlObj: LbryUrlObj = {};
|
||||
|
||||
if (name.startsWith('@')) {
|
||||
urlObj.channelName = name;
|
||||
urlObj.channelClaimId = claimId;
|
||||
} else {
|
||||
urlObj.streamName = name;
|
||||
urlObj.streamClaimId = claimId;
|
||||
}
|
||||
|
||||
const url = buildURI(urlObj);
|
||||
actions.push(doResolveUri(url));
|
||||
uris.push(url);
|
||||
}
|
||||
});
|
||||
|
||||
|
|
|
@ -66,26 +66,25 @@ function handleClaimAction(state: State, action: any): State {
|
|||
const byId = Object.assign({}, state.byId);
|
||||
const channelClaimCounts = Object.assign({}, state.channelClaimCounts);
|
||||
URI or URL? are we consistent with when we use one vs. the other? URI or URL? are we consistent with when we use one vs. the other?
|
||||
|
||||
Object.entries(resolveInfo).forEach(([uri: string, resolveResponse: Claim]) => {
|
||||
Object.entries(resolveInfo).forEach(([url: string, resolveResponse: ResolveResponse]) => {
|
||||
// $FlowFixMe
|
||||
if (resolveResponse.claimsInChannel) {
|
||||
// $FlowFixMe
|
||||
channelClaimCounts[uri] = resolveResponse.claimsInChannel;
|
||||
const { claimsInChannel, stream, channel } = resolveResponse;
|
||||
if (claimsInChannel) {
|
||||
channelClaimCounts[url] = claimsInChannel;
|
||||
}
|
||||
});
|
||||
|
||||
// $FlowFixMe
|
||||
Object.entries(resolveInfo).forEach(([uri, { channel, stream }]) => {
|
||||
if (stream) {
|
||||
byId[stream.claim_id] = stream;
|
||||
byUri[uri] = stream.claim_id;
|
||||
byUri[url] = stream.claim_id;
|
||||
}
|
||||
|
||||
if (channel) {
|
||||
byId[channel.claim_id] = channel;
|
||||
byUri[stream ? channel.permanent_url : uri] = channel.claim_id;
|
||||
byUri[stream ? channel.canonical_url : url] = channel.claim_id;
|
||||
}
|
||||
|
||||
if (!stream && !channel) {
|
||||
byUri[uri] = null;
|
||||
byUri[url] = null;
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -305,20 +304,20 @@ reducers[ACTIONS.CLAIM_SEARCH_COMPLETED] = (state: State, action: any): State =>
|
|||
{},
|
||||
state.claimSearchByQueryLastPageReached
|
||||
);
|
||||
const { append, query, uris, pageSize } = action.data;
|
||||
const { append, query, urls, pageSize } = action.data;
|
||||
|
||||
if (append) {
|
||||
// todo: check for duplicate uris when concatenating?
|
||||
// todo: check for duplicate urls when concatenating?
|
||||
claimSearchByQuery[query] =
|
||||
claimSearchByQuery[query] && claimSearchByQuery[query].length
|
||||
? claimSearchByQuery[query].concat(uris)
|
||||
: uris;
|
||||
? claimSearchByQuery[query].concat(urls)
|
||||
: urls;
|
||||
} else {
|
||||
claimSearchByQuery[query] = uris;
|
||||
claimSearchByQuery[query] = urls;
|
||||
}
|
||||
|
||||
// the returned number of uris is less than the page size, so we're on the last page
|
||||
claimSearchByQueryLastPageReached[query] = uris.length < pageSize;
|
||||
// the returned number of urls is less than the page size, so we're on the last page
|
||||
claimSearchByQueryLastPageReached[query] = urls.length < pageSize;
|
||||
|
||||
delete fetchingClaimSearchByQuery[query];
|
||||
|
||||
|
|
|
@ -62,8 +62,10 @@ export const makeSelectClaimIsPending = (uri: string) =>
|
|||
selectPendingById,
|
||||
pendingById => {
|
||||
let claimId;
|
||||
|
||||
try {
|
||||
({ claimId } = parseURI(uri));
|
||||
const { isChannel, channelClaimId, streamClaimId } = parseURI(uri);
|
||||
claimId = isChannel ? channelClaimId : streamClaimId;
|
||||
} catch (e) {}
|
||||
|
||||
if (claimId) {
|
||||
|
@ -76,7 +78,8 @@ export const makeSelectPendingByUri = (uri: string) =>
|
|||
createSelector(
|
||||
selectPendingById,
|
||||
pendingById => {
|
||||
const { claimId } = parseURI(uri);
|
||||
const { isChannel, channelClaimId, streamClaimId } = parseURI(uri);
|
||||
const claimId = isChannel ? channelClaimId : streamClaimId;
|
||||
return pendingById[claimId];
|
||||
}
|
||||
);
|
||||
|
@ -90,13 +93,16 @@ export const makeSelectClaimForUri = (uri: string) =>
|
|||
// It won't be in claimsByUri because resolving it will return nothing
|
||||
|
||||
let valid;
|
||||
let claimId;
|
||||
let channelClaimId;
|
||||
let streamClaimId;
|
||||
let isChannel;
|
||||
try {
|
||||
({ claimId } = parseURI(uri));
|
||||
({ isChannel, channelClaimId, streamClaimId } = parseURI(uri));
|
||||
valid = true;
|
||||
} catch (e) {}
|
||||
|
||||
if (valid) {
|
||||
const claimId = isChannel ? channelClaimId : streamClaimId;
|
||||
const pendingClaim = pendingById[claimId];
|
||||
|
||||
if (pendingClaim) {
|
||||
|
@ -521,23 +527,18 @@ export const selectClaimSearchByQueryLastPageReached = createSelector(
|
|||
state => state.claimSearchByQueryLastPageReached || {}
|
||||
);
|
||||
|
||||
export const makeSelectClaimSearchUrisByOptions = (options: {}) =>
|
||||
createSelector(
|
||||
selectClaimSearchByQuery,
|
||||
byQuery => {
|
||||
// We don't care what options are passed to this selector. Just forward them.
|
||||
// $FlowFixMe
|
||||
const query = createNormalizedClaimSearchKey(options);
|
||||
return byQuery[query];
|
||||
}
|
||||
);
|
||||
|
||||
export const makeSelectShortUrlForUri = (uri: string) =>
|
||||
createSelector(
|
||||
makeSelectClaimForUri(uri),
|
||||
claim => claim && claim.short_url
|
||||
);
|
||||
|
||||
export const makeSelectCanonicalUrlForUri = (uri: string) =>
|
||||
createSelector(
|
||||
makeSelectClaimForUri(uri),
|
||||
claim => claim && claim.canonical_url
|
||||
);
|
||||
|
||||
export const makeSelectSupportsForUri = (uri: string) =>
|
||||
createSelector(
|
||||
selectSupportsByOutpoint,
|
||||
|
@ -548,11 +549,12 @@ export const makeSelectSupportsForUri = (uri: string) =>
|
|||
}
|
||||
|
||||
const { claim_id: claimId } = claim;
|
||||
let total = parseFloat("0.0");
|
||||
let total = 0;
|
||||
|
||||
Object.values(byOutpoint).forEach(support => {
|
||||
const { claim_id, amount } = support
|
||||
total = (claim_id === claimId && amount) ? total + parseFloat(amount) : total;
|
||||
// $FlowFixMe
|
||||
const { claim_id, amount } = support;
|
||||
total = claim_id === claimId && amount ? total + parseFloat(amount) : total;
|
||||
});
|
||||
|
||||
return total;
|
||||
|
|
|
@ -33,8 +33,16 @@ export const selectIsStillEditing = createSelector(
|
|||
return false;
|
||||
}
|
||||
|
||||
const { isChannel: currentIsChannel, claimName: currentClaimName, contentName: currentContentName } = parseURI(uri);
|
||||
const { isChannel: editIsChannel, claimName: editClaimName, contentName: editContentName } = parseURI(editingURI);
|
||||
const {
|
||||
isChannel: currentIsChannel,
|
||||
claimName: currentClaimName,
|
||||
contentName: currentContentName,
|
||||
} = parseURI(uri);
|
||||
const {
|
||||
isChannel: editIsChannel,
|
||||
claimName: editClaimName,
|
||||
contentName: editContentName,
|
||||
} = parseURI(editingURI);
|
||||
|
||||
// Depending on the previous/current use of a channel, we need to compare different things
|
||||
// ex: going from a channel to anonymous, the new uri won't return contentName, so we need to use claimName
|
||||
|
@ -60,7 +68,9 @@ export const selectMyClaimForUri = createSelector(
|
|||
return isStillEditing
|
||||
? claimsById[editClaimId]
|
||||
: myClaims.find(claim =>
|
||||
!contentName ? claim.name === claimName : claim.name === contentName || claim.name === claimName
|
||||
!contentName
|
||||
? claim.name === claimName
|
||||
: claim.name === contentName || claim.name === claimName
|
||||
);
|
||||
}
|
||||
);
|
||||
|
@ -75,7 +85,7 @@ export const selectIsResolvingPublishUris = createSelector(
|
|||
|
||||
let isResolvingShortUri;
|
||||
if (isChannel) {
|
||||
const shortUri = buildURI({ contentName: name });
|
||||
const shortUri = buildURI({ streamName: name });
|
||||
isResolvingShortUri = resolvingUris.includes(shortUri);
|
||||
}
|
||||
|
||||
|
|
|
@ -77,15 +77,15 @@ export const selectSearchSuggestions: Array<SearchSuggestion> = createSelector(
|
|||
let searchSuggestions = [];
|
||||
try {
|
||||
const uri = normalizeURI(query);
|
||||
const { claimName, isChannel } = parseURI(uri);
|
||||
const { channelName, streamName, isChannel } = parseURI(uri);
|
||||
searchSuggestions.push(
|
||||
{
|
||||
value: claimName,
|
||||
value: streamName,
|
||||
type: SEARCH_TYPES.SEARCH,
|
||||
},
|
||||
{
|
||||
value: uri,
|
||||
shorthand: isChannel ? claimName.slice(1) : claimName,
|
||||
shorthand: isChannel ? channelName : streamName,
|
||||
type: isChannel ? SEARCH_TYPES.CHANNEL : SEARCH_TYPES.FILE,
|
||||
}
|
||||
);
|
||||
|
@ -110,11 +110,11 @@ export const selectSearchSuggestions: Array<SearchSuggestion> = createSelector(
|
|||
// determine if it's a channel
|
||||
try {
|
||||
const uri = normalizeURI(suggestion);
|
||||
const { claimName, isChannel } = parseURI(uri);
|
||||
const { channelName, streamName, isChannel } = parseURI(uri);
|
||||
|
||||
return {
|
||||
value: uri,
|
||||
shorthand: isChannel ? claimName.slice(1) : claimName,
|
||||
shorthand: isChannel ? channelName : streamName,
|
||||
type: isChannel ? SEARCH_TYPES.CHANNEL : SEARCH_TYPES.FILE,
|
||||
};
|
||||
} catch (e) {
|
||||
|
|
|
@ -1,13 +0,0 @@
|
|||
// @flow
|
||||
import { parseURI } from 'lbryURI';
|
||||
|
||||
export const formatLbryUriForWeb = (uri: string) => {
|
||||
const { claimName, claimId } = parseURI(uri);
|
||||
|
||||
let webUrl = `/${claimName}`;
|
||||
if (claimId) {
|
||||
webUrl += `/${claimId}`;
|
||||
}
|
||||
|
||||
return webUrl;
|
||||
};
|
32
yarn.lock
|
@ -1911,6 +1911,11 @@ estree-walker@^0.6.0:
|
|||
resolved "https://registry.yarnpkg.com/estree-walker/-/estree-walker-0.6.0.tgz#5d865327c44a618dde5699f763891ae31f257dae"
|
||||
integrity sha512-peq1RfVAVzr3PU/jL31RaOjUKLoZJpObQWJJ+LgfcxDUifyLZ1RjPQZTl0pzj2uJ45b7A7XpyppXvxdEqzo4rw==
|
||||
|
||||
estree-walker@^0.6.1:
|
||||
version "0.6.1"
|
||||
resolved "https://registry.yarnpkg.com/estree-walker/-/estree-walker-0.6.1.tgz#53049143f40c6eb918b23671d1fe3219f3a1b362"
|
||||
integrity sha512-SqmZANLWS0mnatqbSfRP5g8OXZC12Fgg1IwNtLsyHDzJizORW4khDfjPqJZsemPWBB2uqykUah5YpQ6epsqC/w==
|
||||
|
||||
esutils@^2.0.2:
|
||||
version "2.0.2"
|
||||
resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.2.tgz#0abf4f1caa5bcb1f7a9d8acc6dea4faaa04bac9b"
|
||||
|
@ -3440,6 +3445,13 @@ lru-cache@^4.0.1:
|
|||
pseudomap "^1.0.2"
|
||||
yallist "^2.1.2"
|
||||
|
||||
magic-string@^0.25.2:
|
||||
version "0.25.3"
|
||||
resolved "https://registry.yarnpkg.com/magic-string/-/magic-string-0.25.3.tgz#34b8d2a2c7fec9d9bdf9929a3fd81d271ef35be9"
|
||||
integrity sha512-6QK0OpF/phMz0Q2AxILkX2mFhi7m+WMwTRg0LQKq/WBB0cDP4rYH3Wp4/d3OTXlrPLVJT/RFqj8tFeAR4nk8AA==
|
||||
dependencies:
|
||||
sourcemap-codec "^1.4.4"
|
||||
|
||||
make-dir@^1.0.0:
|
||||
version "1.2.0"
|
||||
resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-1.2.0.tgz#6d6a49eead4aae296c53bbf3a1a008bd6c89469b"
|
||||
|
@ -4379,6 +4391,14 @@ rollup-plugin-includepaths@^0.2.3:
|
|||
resolved "https://registry.yarnpkg.com/rollup-plugin-includepaths/-/rollup-plugin-includepaths-0.2.3.tgz#244d21b9669a0debe476d825e4a02ed08c06b258"
|
||||
integrity sha512-4QbSIZPDT+FL4SViEVCRi4cGCA64zQJu7u5qmCkO3ecHy+l9EQBsue15KfCpddfb6Br0q47V/v2+E2YUiqts9g==
|
||||
|
||||
rollup-plugin-replace@^2.2.0:
|
||||
version "2.2.0"
|
||||
resolved "https://registry.yarnpkg.com/rollup-plugin-replace/-/rollup-plugin-replace-2.2.0.tgz#f41ae5372e11e7a217cde349c8b5d5fd115e70e3"
|
||||
integrity sha512-/5bxtUPkDHyBJAKketb4NfaeZjL5yLZdeUihSfbF2PQMz+rSTEb8ARKoOl3UBT4m7/X+QOXJo3sLTcq+yMMYTA==
|
||||
dependencies:
|
||||
magic-string "^0.25.2"
|
||||
rollup-pluginutils "^2.6.0"
|
||||
|
||||
rollup-pluginutils@^1.5.0, rollup-pluginutils@^1.5.1:
|
||||
version "1.5.2"
|
||||
resolved "https://registry.yarnpkg.com/rollup-pluginutils/-/rollup-pluginutils-1.5.2.tgz#1e156e778f94b7255bfa1b3d0178be8f5c552408"
|
||||
|
@ -4395,6 +4415,13 @@ rollup-pluginutils@^2.3.0:
|
|||
estree-walker "^0.6.0"
|
||||
micromatch "^3.1.10"
|
||||
|
||||
rollup-pluginutils@^2.6.0:
|
||||
version "2.8.1"
|
||||
resolved "https://registry.yarnpkg.com/rollup-pluginutils/-/rollup-pluginutils-2.8.1.tgz#8fa6dd0697344938ef26c2c09d2488ce9e33ce97"
|
||||
integrity sha512-J5oAoysWar6GuZo0s+3bZ6sVZAC0pfqKz68De7ZgDi5z63jOVZn1uJL/+z1jeKHNbGII8kAyHF5q8LnxSX5lQg==
|
||||
dependencies:
|
||||
estree-walker "^0.6.1"
|
||||
|
||||
rollup@^1.8.0:
|
||||
version "1.8.0"
|
||||
resolved "https://registry.yarnpkg.com/rollup/-/rollup-1.8.0.tgz#e3ce8b708ad4325166717f74f244f691595d35e2"
|
||||
|
@ -4613,6 +4640,11 @@ source-map@^0.6.0, source-map@~0.6.1:
|
|||
resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263"
|
||||
integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==
|
||||
|
||||
sourcemap-codec@^1.4.4:
|
||||
version "1.4.6"
|
||||
resolved "https://registry.yarnpkg.com/sourcemap-codec/-/sourcemap-codec-1.4.6.tgz#e30a74f0402bad09807640d39e971090a08ce1e9"
|
||||
integrity sha512-1ZooVLYFxC448piVLBbtOxFcXwnymH9oUF8nRd3CuYDVvkRBxRl6pB4Mtas5a4drtL+E8LDgFkQNcgIw6tc8Hg==
|
||||
|
||||
spdx-correct@^3.0.0:
|
||||
version "3.0.0"
|
||||
resolved "https://registry.yarnpkg.com/spdx-correct/-/spdx-correct-3.0.0.tgz#05a5b4d7153a195bc92c3c425b69f3b2a9524c82"
|
||||
|
|
This could possibly be removed or moved. The function is called parseURI, not parseAndValidateURI. It's one thing to error if invalid characters make it impossible to parse, but I think erroring on a validly structured URI is probably incorrect here.