Socket
Socket
Sign inDemoInstall

@jsenv/url-meta

Package Overview
Dependencies
Maintainers
2
Versions
32
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@jsenv/url-meta - npm Package Compare versions

Comparing version 8.1.0 to 8.2.0

6

package.json
{
"name": "@jsenv/url-meta",
"version": "8.1.0",
"version": "8.2.0",
"license": "MIT",
"repository": {
"type": "git",
"url": "https://github.com/jsenv/jsenv-core",
"directory": "packages/url-meta"
"url": "https://github.com/jsenv/core",
"directory": "packages/independent/url-meta"
},

@@ -10,0 +10,0 @@ "publishConfig": {

@@ -6,3 +6,3 @@ # url meta

```js
import { URL_META } from "@jsenv/url-meta"
import { URL_META } from "@jsenv/url-meta";

@@ -15,9 +15,9 @@ // conditionally associates url and values

},
}
};
const getUrlColor = (url) => {
const { color } = URL_META.applyAssociations({ url, associations })
return color
}
console.log(`file.json color is ${getUrlColor("file:///file.json")}`)
console.log(`file.js color is ${getUrlColor("file:///file.js")}`)
const { color } = URL_META.applyAssociations({ url, associations });
return color;
};
console.log(`file.json color is ${getUrlColor("file:///file.json")}`);
console.log(`file.js color is ${getUrlColor("file:///file.js")}`);
```

@@ -58,3 +58,3 @@

},
}
};
```

@@ -70,3 +70,3 @@

```js
import { URL_META } from "@jsenv/url-meta"
import { URL_META } from "@jsenv/url-meta";

@@ -81,4 +81,4 @@ const associations = URL_META.resolveAssociations(

"file:///Users/directory/",
)
console.log(JSON.stringify(associations, null, " "))
);
console.log(JSON.stringify(associations, null, " "));
```

@@ -85,0 +85,0 @@

@@ -1,5 +0,5 @@

import { applyPatternMatching } from "./pattern_matching.js"
import { applyPatternMatching } from "./pattern_matching.js";
export const applyAliases = ({ url, aliases }) => {
let aliasFullMatchResult
let aliasFullMatchResult;
const aliasMatchingKey = Object.keys(aliases).find((key) => {

@@ -9,21 +9,21 @@ const aliasMatchResult = applyPatternMatching({

url,
})
});
if (aliasMatchResult.matched) {
aliasFullMatchResult = aliasMatchResult
return true
aliasFullMatchResult = aliasMatchResult;
return true;
}
return false
})
return false;
});
if (!aliasMatchingKey) {
return url
return url;
}
const { matchGroups } = aliasFullMatchResult
const alias = aliases[aliasMatchingKey]
const parts = alias.split("*")
const { matchGroups } = aliasFullMatchResult;
const alias = aliases[aliasMatchingKey];
const parts = alias.split("*");
const newUrl = parts.reduce((previous, value, index) => {
return `${previous}${value}${
index === parts.length - 1 ? "" : matchGroups[index]
}`
}, "")
return newUrl
}
}`;
}, "");
return newUrl;
};

@@ -1,2 +0,2 @@

import { isPlainObject } from "./assertions.js"
import { isPlainObject } from "./assertions.js";

@@ -7,11 +7,11 @@ export const asFlatAssociations = (associations) => {

`associations must be a plain object, got ${associations}`,
)
);
}
const flatAssociations = {}
const flatAssociations = {};
Object.keys(associations).forEach((associationName) => {
const associationValue = associations[associationName]
const associationValue = associations[associationName];
if (isPlainObject(associationValue)) {
Object.keys(associationValue).forEach((pattern) => {
const patternValue = associationValue[pattern]
const previousValue = flatAssociations[pattern]
const patternValue = associationValue[pattern];
const previousValue = flatAssociations[pattern];
if (isPlainObject(previousValue)) {

@@ -21,12 +21,12 @@ flatAssociations[pattern] = {

[associationName]: patternValue,
}
};
} else {
flatAssociations[pattern] = {
[associationName]: patternValue,
}
};
}
})
});
}
})
return flatAssociations
}
});
return flatAssociations;
};
export const assertSpecifierMetaMap = (value, checkComposition = true) => {
if (!isPlainObject(value)) {
throw new TypeError(`specifierMetaMap must be a plain object, got ${value}`)
throw new TypeError(
`specifierMetaMap must be a plain object, got ${value}`,
);
}
if (checkComposition) {
const plainObject = value
const plainObject = value;
Object.keys(plainObject).forEach((key) => {
assertUrlLike(key, "specifierMetaMap key")
const value = plainObject[key]
assertUrlLike(key, "specifierMetaMap key");
const value = plainObject[key];
if (value !== null && !isPlainObject(value)) {
throw new TypeError(
`specifierMetaMap value must be a plain object or null, got ${value} under key ${key}`,
)
);
}
})
});
}
}
};
export const assertUrlLike = (value, name = "url") => {
if (typeof value !== "string") {
throw new TypeError(`${name} must be a url string, got ${value}`)
throw new TypeError(`${name} must be a url string, got ${value}`);
}

@@ -26,3 +28,3 @@ if (isWindowsPathnameSpecifier(value)) {

`${name} must be a url but looks like a windows pathname, got ${value}`,
)
);
}

@@ -32,28 +34,28 @@ if (!hasScheme(value)) {

`${name} must be a url and no scheme found, got ${value}`,
)
);
}
}
};
export const isPlainObject = (value) => {
if (value === null) {
return false
return false;
}
if (typeof value === "object") {
if (Array.isArray(value)) {
return false
return false;
}
return true
return true;
}
return false
}
return false;
};
const isWindowsPathnameSpecifier = (specifier) => {
const firstChar = specifier[0]
if (!/[a-zA-Z]/.test(firstChar)) return false
const secondChar = specifier[1]
if (secondChar !== ":") return false
const thirdChar = specifier[2]
return thirdChar === "/" || thirdChar === "\\"
}
const firstChar = specifier[0];
if (!/[a-zA-Z]/.test(firstChar)) return false;
const secondChar = specifier[1];
if (secondChar !== ":") return false;
const thirdChar = specifier[2];
return thirdChar === "/" || thirdChar === "\\";
};
const hasScheme = (specifier) => /^[a-zA-Z]+:/.test(specifier)
const hasScheme = (specifier) => /^[a-zA-Z]+:/.test(specifier);

@@ -1,8 +0,9 @@

import { assertUrlLike, isPlainObject } from "./assertions.js"
import { asFlatAssociations } from "./as_flat_associations.js"
import { applyPatternMatching } from "./pattern_matching.js"
import { assertUrlLike, isPlainObject } from "./assertions.js";
import { asFlatAssociations } from "./as_flat_associations.js";
import { applyPatternMatching } from "./pattern_matching.js";
export const applyAssociations = ({ url, associations }) => {
assertUrlLike(url)
const flatAssociations = asFlatAssociations(associations)
if (url && typeof url.href === "string") url = url.href;
assertUrlLike(url);
const flatAssociations = asFlatAssociations(associations);
return Object.keys(flatAssociations).reduce((previousValue, pattern) => {

@@ -12,5 +13,5 @@ const { matched } = applyPatternMatching({

url,
})
});
if (matched) {
const value = flatAssociations[pattern]
const value = flatAssociations[pattern];
if (isPlainObject(previousValue) && isPlainObject(value)) {

@@ -20,8 +21,8 @@ return {

...value,
}
};
}
return value
return value;
}
return previousValue
}, {})
}
return previousValue;
}, {});
};

@@ -7,3 +7,3 @@ /*

import { assertUrlLike } from "./assertions.js"
import { assertUrlLike } from "./assertions.js";

@@ -28,15 +28,16 @@ /** @module jsenv_url_meta **/

export const applyPatternMatching = ({ url, pattern }) => {
assertUrlLike(pattern, "pattern")
assertUrlLike(url, "url")
const { matched, patternIndex, index, groups } = applyMatching(pattern, url)
const matchGroups = []
let groupIndex = 0
assertUrlLike(pattern, "pattern");
if (url && typeof url.href === "string") url = url.href;
assertUrlLike(url, "url");
const { matched, patternIndex, index, groups } = applyMatching(pattern, url);
const matchGroups = [];
let groupIndex = 0;
groups.forEach((group) => {
if (group.name) {
matchGroups[group.name] = group.string
matchGroups[group.name] = group.string;
} else {
matchGroups[groupIndex] = group.string
groupIndex++
matchGroups[groupIndex] = group.string;
groupIndex++;
}
})
});
return {

@@ -47,53 +48,53 @@ matched,

matchGroups,
}
}
};
};
const applyMatching = (pattern, string) => {
const groups = []
let patternIndex = 0
let index = 0
let remainingPattern = pattern
let remainingString = string
let restoreIndexes = true
const groups = [];
let patternIndex = 0;
let index = 0;
let remainingPattern = pattern;
let remainingString = string;
let restoreIndexes = true;
const consumePattern = (count) => {
const subpattern = remainingPattern.slice(0, count)
remainingPattern = remainingPattern.slice(count)
patternIndex += count
return subpattern
}
const subpattern = remainingPattern.slice(0, count);
remainingPattern = remainingPattern.slice(count);
patternIndex += count;
return subpattern;
};
const consumeString = (count) => {
const substring = remainingString.slice(0, count)
remainingString = remainingString.slice(count)
index += count
return substring
}
const substring = remainingString.slice(0, count);
remainingString = remainingString.slice(count);
index += count;
return substring;
};
const consumeRemainingString = () => {
return consumeString(remainingString.length)
}
return consumeString(remainingString.length);
};
let matched
let matched;
const iterate = () => {
const patternIndexBefore = patternIndex
const indexBefore = index
matched = matchOne()
const patternIndexBefore = patternIndex;
const indexBefore = index;
matched = matchOne();
if (matched === undefined) {
consumePattern(1)
consumeString(1)
iterate()
return
consumePattern(1);
consumeString(1);
iterate();
return;
}
if (matched === false && restoreIndexes) {
patternIndex = patternIndexBefore
index = indexBefore
patternIndex = patternIndexBefore;
index = indexBefore;
}
}
};
const matchOne = () => {
// pattern consumed and string consumed
if (remainingPattern === "" && remainingString === "") {
return true // string fully matched pattern
return true; // string fully matched pattern
}
// pattern consumed, string not consumed
if (remainingPattern === "" && remainingString !== "") {
return false // fails because string longer than expected
return false; // fails because string longer than expected
}

@@ -105,9 +106,9 @@ // -- from this point pattern is not consumed --

// trailing "**" is optional
consumePattern(2)
return true
consumePattern(2);
return true;
}
if (remainingPattern === "*") {
groups.push({ string: "" })
groups.push({ string: "" });
}
return false // fail because string shorter than expected
return false; // fail because string shorter than expected
}

@@ -119,20 +120,20 @@ // -- from this point pattern and string are not consumed --

// trailing slash match remaining
consumePattern(1)
groups.push({ string: consumeRemainingString() })
return true
consumePattern(1);
groups.push({ string: consumeRemainingString() });
return true;
}
return false
return false;
}
// fast path trailing '**'
if (remainingPattern === "**") {
consumePattern(2)
consumeRemainingString()
return true
consumePattern(2);
consumeRemainingString();
return true;
}
// pattern leading **
if (remainingPattern.slice(0, 2) === "**") {
consumePattern(2) // consumes "**"
let skipAllowed = true
consumePattern(2); // consumes "**"
let skipAllowed = true;
if (remainingPattern[0] === "/") {
consumePattern(1) // consumes "/"
consumePattern(1); // consumes "/"
// when remainingPattern was preceeded by "**/"

@@ -142,3 +143,3 @@ // and remainingString have no "/"

if (!remainingString.includes("/")) {
skipAllowed = false
skipAllowed = false;
}

@@ -148,4 +149,4 @@ }

if (remainingPattern === "") {
consumeRemainingString()
return true
consumeRemainingString();
return true;
}

@@ -157,21 +158,21 @@ if (skipAllowed) {

canSkipSlash: true,
})
groups.push(...skipResult.groups)
consumePattern(skipResult.patternIndex)
consumeRemainingString()
restoreIndexes = false
return skipResult.matched
});
groups.push(...skipResult.groups);
consumePattern(skipResult.patternIndex);
consumeRemainingString();
restoreIndexes = false;
return skipResult.matched;
}
}
if (remainingPattern[0] === "*") {
consumePattern(1) // consumes "*"
consumePattern(1); // consumes "*"
if (remainingPattern === "") {
// matches everything except "/"
const slashIndex = remainingString.indexOf("/")
const slashIndex = remainingString.indexOf("/");
if (slashIndex === -1) {
groups.push({ string: consumeRemainingString() })
return true
groups.push({ string: consumeRemainingString() });
return true;
}
groups.push({ string: consumeString(slashIndex) })
return false
groups.push({ string: consumeString(slashIndex) });
return false;
}

@@ -181,5 +182,5 @@ // the next char must not the one expected by remainingPattern[0]

if (remainingPattern[0] === remainingString[0]) {
groups.push({ string: "" })
patternIndex = patternIndex - 1
return false
groups.push({ string: "" });
patternIndex = patternIndex - 1;
return false;
}

@@ -190,15 +191,15 @@ const skipResult = skipUntilMatch({

canSkipSlash: false,
})
groups.push(skipResult.group, ...skipResult.groups)
consumePattern(skipResult.patternIndex)
consumeString(skipResult.index)
restoreIndexes = false
return skipResult.matched
});
groups.push(skipResult.group, ...skipResult.groups);
consumePattern(skipResult.patternIndex);
consumeString(skipResult.index);
restoreIndexes = false;
return skipResult.matched;
}
if (remainingPattern[0] !== remainingString[0]) {
return false
return false;
}
return undefined
}
iterate()
return undefined;
};
iterate();

@@ -210,10 +211,10 @@ return {

groups,
}
}
};
};
const skipUntilMatch = ({ pattern, string, canSkipSlash }) => {
let index = 0
let remainingString = string
let longestAttemptRange = null
let isLastAttempt = false
let index = 0;
let remainingString = string;
let longestAttemptRange = null;
let isLastAttempt = false;

@@ -229,7 +230,7 @@ const failure = () => {

},
}
}
};
};
const tryToMatch = () => {
const matchAttempt = applyMatching(pattern, remainingString)
const matchAttempt = applyMatching(pattern, remainingString);
if (matchAttempt.matched) {

@@ -247,5 +248,5 @@ return {

},
}
};
}
const attemptIndex = matchAttempt.index
const attemptIndex = matchAttempt.index;
const attemptRange = {

@@ -256,3 +257,3 @@ patternIndex: matchAttempt.patternIndex,

groups: matchAttempt.groups,
}
};
if (

@@ -262,26 +263,26 @@ !longestAttemptRange ||

) {
longestAttemptRange = attemptRange
longestAttemptRange = attemptRange;
}
if (isLastAttempt) {
return failure()
return failure();
}
const nextIndex = attemptIndex + 1
const nextIndex = attemptIndex + 1;
if (nextIndex >= remainingString.length) {
return failure()
return failure();
}
if (remainingString[0] === "/") {
if (!canSkipSlash) {
return failure()
return failure();
}
// when it's the last slash, the next attempt is the last
if (remainingString.indexOf("/", 1) === -1) {
isLastAttempt = true
isLastAttempt = true;
}
}
// search against the next unattempted string
index += nextIndex
remainingString = remainingString.slice(nextIndex)
return tryToMatch()
}
return tryToMatch()
}
index += nextIndex;
remainingString = remainingString.slice(nextIndex);
return tryToMatch();
};
return tryToMatch();
};

@@ -1,30 +0,31 @@

import { assertUrlLike } from "./assertions.js"
import { assertUrlLike } from "./assertions.js";
export const resolveAssociations = (associations, baseUrl) => {
assertUrlLike(baseUrl, "baseUrl")
const associationsResolved = {}
if (baseUrl && typeof baseUrl.href === "string") baseUrl = baseUrl.href;
assertUrlLike(baseUrl, "baseUrl");
const associationsResolved = {};
Object.keys(associations).forEach((key) => {
const value = associations[key]
const value = associations[key];
if (typeof value === "object" && value !== null) {
const valueMapResolved = {}
const valueMapResolved = {};
Object.keys(value).forEach((pattern) => {
const valueAssociated = value[pattern]
const patternResolved = normalizeUrlPattern(pattern, baseUrl)
valueMapResolved[patternResolved] = valueAssociated
})
associationsResolved[key] = valueMapResolved
const valueAssociated = value[pattern];
const patternResolved = normalizeUrlPattern(pattern, baseUrl);
valueMapResolved[patternResolved] = valueAssociated;
});
associationsResolved[key] = valueMapResolved;
} else {
associationsResolved[key] = value
associationsResolved[key] = value;
}
})
return associationsResolved
}
});
return associationsResolved;
};
const normalizeUrlPattern = (urlPattern, baseUrl) => {
try {
return String(new URL(urlPattern, baseUrl))
return String(new URL(urlPattern, baseUrl));
} catch (e) {
// it's not really an url, no need to perform url resolution nor encoding
return urlPattern
return urlPattern;
}
}
};

@@ -1,29 +0,30 @@

import { assertUrlLike, isPlainObject } from "./assertions.js"
import { asFlatAssociations } from "./as_flat_associations.js"
import { applyPatternMatching } from "./pattern_matching.js"
import { assertUrlLike, isPlainObject } from "./assertions.js";
import { asFlatAssociations } from "./as_flat_associations.js";
import { applyPatternMatching } from "./pattern_matching.js";
export const urlChildMayMatch = ({ url, associations, predicate }) => {
assertUrlLike(url, "url")
if (url && typeof url.href === "string") url = url.href;
assertUrlLike(url, "url");
// the function was meants to be used on url ending with '/'
if (!url.endsWith("/")) {
throw new Error(`url should end with /, got ${url}`)
throw new Error(`url should end with /, got ${url}`);
}
if (typeof predicate !== "function") {
throw new TypeError(`predicate must be a function, got ${predicate}`)
throw new TypeError(`predicate must be a function, got ${predicate}`);
}
const flatAssociations = asFlatAssociations(associations)
const flatAssociations = asFlatAssociations(associations);
// for full match we must create an object to allow pattern to override previous ones
let fullMatchMeta = {}
let someFullMatch = false
let fullMatchMeta = {};
let someFullMatch = false;
// for partial match, any meta satisfying predicate will be valid because
// we don't know for sure if pattern will still match for a file inside pathname
const partialMatchMetaArray = []
const partialMatchMetaArray = [];
Object.keys(flatAssociations).forEach((pattern) => {
const value = flatAssociations[pattern]
const value = flatAssociations[pattern];
const matchResult = applyPatternMatching({
pattern,
url,
})
});
if (matchResult.matched) {
someFullMatch = true
someFullMatch = true;
if (isPlainObject(fullMatchMeta) && isPlainObject(value)) {

@@ -33,16 +34,16 @@ fullMatchMeta = {

...value,
}
};
} else {
fullMatchMeta = value
fullMatchMeta = value;
}
} else if (someFullMatch === false && matchResult.urlIndex >= url.length) {
partialMatchMetaArray.push(value)
partialMatchMetaArray.push(value);
}
})
});
if (someFullMatch) {
return Boolean(predicate(fullMatchMeta))
return Boolean(predicate(fullMatchMeta));
}
return partialMatchMetaArray.some((partialMatchMeta) =>
predicate(partialMatchMeta),
)
}
);
};

@@ -1,6 +0,6 @@

import { resolveAssociations } from "./resolve_associations.js"
import { applyAssociations } from "./associations.js"
import { applyAliases } from "./aliases.js"
import { applyPatternMatching } from "./pattern_matching.js"
import { urlChildMayMatch } from "./url_child_may_match.js"
import { resolveAssociations } from "./resolve_associations.js";
import { applyAssociations } from "./associations.js";
import { applyAliases } from "./aliases.js";
import { applyPatternMatching } from "./pattern_matching.js";
import { urlChildMayMatch } from "./url_child_may_match.js";

@@ -13,2 +13,2 @@ export const URL_META = {

applyAliases,
}
};
SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc