Updated the files.

This commit is contained in:
Batuhan Berk Başoğlu 2024-02-08 19:38:41 -05:00
parent 1553e6b971
commit 753967d4f5
23418 changed files with 3784666 additions and 0 deletions

21
my-app/node_modules/@tufjs/canonical-json/LICENSE generated vendored Executable file
View file

@ -0,0 +1,21 @@
MIT License
Copyright (c) 2022 GitHub and the TUF Contributors
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

53
my-app/node_modules/@tufjs/canonical-json/README.md generated vendored Executable file
View file

@ -0,0 +1,53 @@
# @tufjs/canonical-json
JSON canonicalization compliant with the [OLPC Canonical JSON specification][1].
## Why
If you're looking for [RFC 8785][2] compliant JSON canonicalization there are
[any][3] [number][4] [of][5] [libraries][6] [to][7] [choose][8] [from][9].
You should only select this library if you know that you specifically need
support for the [OLPC][1]-style of canonicalization.
One reason you might chose OLPC compliance is for interoperability with
[The Update Framework][10] which specifically calls out OLPC as the
canonicalization standard for computing signatures over TUF metadata.
The canonicalized strings generated by this library are compatible with those
generated by the Python-based [securesystemslib][11] library and the Go-based
[go-securesystemslib][12] library.
## Installation
```console
npm install @tufjs/canonical-json
```
## Usage
```javascript
const json = require('@tufjs/canonical-json')
const obj = {
bool: true,
num: 42,
ary: [1, 2, 3],
str: "foo\\bar"
}
console.log(json.canonicalize(obj))
// output: {"ary":[1,2,3],"bool":true,"num":42,"str":"foo\\bar"}
```
[1]: https://wiki.laptop.org/go/Canonical_JSON
[2]: https://www.rfc-editor.org/rfc/rfc8785
[3]: https://www.npmjs.com/package/@stratumn/canonicaljson
[4]: https://www.npmjs.com/package/@truestamp/canonify
[5]: https://www.npmjs.com/package/canonical-json
[6]: https://www.npmjs.com/package/canonicalize
[7]: https://www.npmjs.com/package/canonicalize-json
[8]: https://www.npmjs.com/package/json-canonicalize
[9]: https://www.npmjs.com/package/another-json
[10]: https://theupdateframework.github.io/specification/latest/#metaformat
[11]: https://github.com/secure-systems-lab/securesystemslib
[12]: https://github.com/secure-systems-lab/go-securesystemslib

2
my-app/node_modules/@tufjs/canonical-json/lib/index.d.ts generated vendored Executable file
View file

@ -0,0 +1,2 @@
// eslint-disable-next-line @typescript-eslint/no-explicit-any
export const canonicalize: (object: any) => string;

64
my-app/node_modules/@tufjs/canonical-json/lib/index.js generated vendored Executable file
View file

@ -0,0 +1,64 @@
const COMMA = ',';
const COLON = ':';
const LEFT_SQUARE_BRACKET = '[';
const RIGHT_SQUARE_BRACKET = ']';
const LEFT_CURLY_BRACKET = '{';
const RIGHT_CURLY_BRACKET = '}';
// Recursively encodes the supplied object according to the canonical JSON form
// as specified at http://wiki.laptop.org/go/Canonical_JSON. It's a restricted
// dialect of JSON in which keys are lexically sorted, floats are not allowed,
// and only double quotes and backslashes are escaped.
function canonicalize(object) {
const buffer = [];
if (typeof object === 'string') {
buffer.push(canonicalizeString(object));
} else if (typeof object === 'boolean') {
buffer.push(JSON.stringify(object));
} else if (Number.isInteger(object)) {
buffer.push(JSON.stringify(object));
} else if (object === null) {
buffer.push(JSON.stringify(object));
} else if (Array.isArray(object)) {
buffer.push(LEFT_SQUARE_BRACKET);
let first = true;
object.forEach((element) => {
if (!first) {
buffer.push(COMMA);
}
first = false;
buffer.push(canonicalize(element));
});
buffer.push(RIGHT_SQUARE_BRACKET);
} else if (typeof object === 'object') {
buffer.push(LEFT_CURLY_BRACKET);
let first = true;
Object.keys(object)
.sort()
.forEach((property) => {
if (!first) {
buffer.push(COMMA);
}
first = false;
buffer.push(canonicalizeString(property));
buffer.push(COLON);
buffer.push(canonicalize(object[property]));
});
buffer.push(RIGHT_CURLY_BRACKET);
} else {
throw new TypeError('cannot encode ' + object.toString());
}
return buffer.join('');
}
// String canonicalization consists of escaping backslash (\) and double
// quote (") characters and wrapping the resulting string in double quotes.
function canonicalizeString(string) {
const escapedString = string.replace(/\\/g, '\\\\').replace(/"/g, '\\"');
return '"' + escapedString + '"';
}
module.exports = {
canonicalize,
};

35
my-app/node_modules/@tufjs/canonical-json/package.json generated vendored Executable file
View file

@ -0,0 +1,35 @@
{
"name": "@tufjs/canonical-json",
"version": "2.0.0",
"description": "OLPC JSON canonicalization",
"main": "lib/index.js",
"typings": "lib/index.d.ts",
"license": "MIT",
"keywords": [
"json",
"canonical",
"canonicalize",
"canonicalization",
"crypto",
"signature",
"olpc"
],
"author": "bdehamer@github.com",
"repository": {
"type": "git",
"url": "git+https://github.com/theupdateframework/tuf-js.git"
},
"homepage": "https://github.com/theupdateframework/tuf-js/tree/main/packages/canonical-json#readme",
"bugs": {
"url": "https://github.com/theupdateframework/tuf-js/issues"
},
"files": [
"lib/"
],
"scripts": {
"test": "jest"
},
"engines": {
"node": "^16.14.0 || >=18.0.0"
}
}

21
my-app/node_modules/@tufjs/models/LICENSE generated vendored Executable file
View file

@ -0,0 +1,21 @@
MIT License
Copyright (c) 2022 GitHub and the TUF Contributors
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

3
my-app/node_modules/@tufjs/models/README.md generated vendored Executable file
View file

@ -0,0 +1,3 @@
# @tufjs/models
TUF metadata model objects.

37
my-app/node_modules/@tufjs/models/dist/base.d.ts generated vendored Executable file
View file

@ -0,0 +1,37 @@
import { Signature } from './signature';
import { JSONObject, JSONValue } from './utils';
export interface Signable {
signatures: Record<string, Signature>;
signed: Signed;
}
export interface SignedOptions {
version?: number;
specVersion?: string;
expires?: string;
unrecognizedFields?: Record<string, JSONValue>;
}
export declare enum MetadataKind {
Root = "root",
Timestamp = "timestamp",
Snapshot = "snapshot",
Targets = "targets"
}
export declare function isMetadataKind(value: unknown): value is MetadataKind;
/***
* A base class for the signed part of TUF metadata.
*
* Objects with base class Signed are usually included in a ``Metadata`` object
* on the signed attribute. This class provides attributes and methods that
* are common for all TUF metadata types (roles).
*/
export declare abstract class Signed {
readonly specVersion: string;
readonly expires: string;
readonly version: number;
readonly unrecognizedFields: Record<string, JSONValue>;
constructor(options: SignedOptions);
equals(other: Signed): boolean;
isExpired(referenceTime?: Date): boolean;
static commonFieldsFromJSON(data: JSONObject): SignedOptions;
abstract toJSON(): JSONObject;
}

83
my-app/node_modules/@tufjs/models/dist/base.js generated vendored Executable file
View file

@ -0,0 +1,83 @@
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.Signed = exports.isMetadataKind = exports.MetadataKind = void 0;
const util_1 = __importDefault(require("util"));
const error_1 = require("./error");
const utils_1 = require("./utils");
const SPECIFICATION_VERSION = ['1', '0', '31'];
var MetadataKind;
(function (MetadataKind) {
MetadataKind["Root"] = "root";
MetadataKind["Timestamp"] = "timestamp";
MetadataKind["Snapshot"] = "snapshot";
MetadataKind["Targets"] = "targets";
})(MetadataKind || (exports.MetadataKind = MetadataKind = {}));
function isMetadataKind(value) {
return (typeof value === 'string' &&
Object.values(MetadataKind).includes(value));
}
exports.isMetadataKind = isMetadataKind;
/***
* A base class for the signed part of TUF metadata.
*
* Objects with base class Signed are usually included in a ``Metadata`` object
* on the signed attribute. This class provides attributes and methods that
* are common for all TUF metadata types (roles).
*/
class Signed {
constructor(options) {
this.specVersion = options.specVersion || SPECIFICATION_VERSION.join('.');
const specList = this.specVersion.split('.');
if (!(specList.length === 2 || specList.length === 3) ||
!specList.every((item) => isNumeric(item))) {
throw new error_1.ValueError('Failed to parse specVersion');
}
// major version must match
if (specList[0] != SPECIFICATION_VERSION[0]) {
throw new error_1.ValueError('Unsupported specVersion');
}
this.expires = options.expires || new Date().toISOString();
this.version = options.version || 1;
this.unrecognizedFields = options.unrecognizedFields || {};
}
equals(other) {
if (!(other instanceof Signed)) {
return false;
}
return (this.specVersion === other.specVersion &&
this.expires === other.expires &&
this.version === other.version &&
util_1.default.isDeepStrictEqual(this.unrecognizedFields, other.unrecognizedFields));
}
isExpired(referenceTime) {
if (!referenceTime) {
referenceTime = new Date();
}
return referenceTime >= new Date(this.expires);
}
static commonFieldsFromJSON(data) {
const { spec_version, expires, version, ...rest } = data;
if (utils_1.guard.isDefined(spec_version) && !(typeof spec_version === 'string')) {
throw new TypeError('spec_version must be a string');
}
if (utils_1.guard.isDefined(expires) && !(typeof expires === 'string')) {
throw new TypeError('expires must be a string');
}
if (utils_1.guard.isDefined(version) && !(typeof version === 'number')) {
throw new TypeError('version must be a number');
}
return {
specVersion: spec_version,
expires,
version,
unrecognizedFields: rest,
};
}
}
exports.Signed = Signed;
function isNumeric(str) {
return !isNaN(Number(str));
}

32
my-app/node_modules/@tufjs/models/dist/delegations.d.ts generated vendored Executable file
View file

@ -0,0 +1,32 @@
import { Key } from './key';
import { DelegatedRole, SuccinctRoles } from './role';
import { JSONObject, JSONValue } from './utils';
type DelegatedRoleMap = Record<string, DelegatedRole>;
type KeyMap = Record<string, Key>;
interface DelegationsOptions {
keys: KeyMap;
roles?: DelegatedRoleMap;
succinctRoles?: SuccinctRoles;
unrecognizedFields?: Record<string, JSONValue>;
}
/**
* A container object storing information about all delegations.
*
* Targets roles that are trusted to provide signed metadata files
* describing targets with designated pathnames and/or further delegations.
*/
export declare class Delegations {
readonly keys: KeyMap;
readonly roles?: DelegatedRoleMap;
readonly unrecognizedFields?: Record<string, JSONValue>;
readonly succinctRoles?: SuccinctRoles;
constructor(options: DelegationsOptions);
equals(other: Delegations): boolean;
rolesForTarget(targetPath: string): Generator<{
role: string;
terminating: boolean;
}>;
toJSON(): JSONObject;
static fromJSON(data: JSONObject): Delegations;
}
export {};

115
my-app/node_modules/@tufjs/models/dist/delegations.js generated vendored Executable file
View file

@ -0,0 +1,115 @@
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.Delegations = void 0;
const util_1 = __importDefault(require("util"));
const error_1 = require("./error");
const key_1 = require("./key");
const role_1 = require("./role");
const utils_1 = require("./utils");
/**
* A container object storing information about all delegations.
*
* Targets roles that are trusted to provide signed metadata files
* describing targets with designated pathnames and/or further delegations.
*/
class Delegations {
constructor(options) {
this.keys = options.keys;
this.unrecognizedFields = options.unrecognizedFields || {};
if (options.roles) {
if (Object.keys(options.roles).some((roleName) => role_1.TOP_LEVEL_ROLE_NAMES.includes(roleName))) {
throw new error_1.ValueError('Delegated role name conflicts with top-level role name');
}
}
this.succinctRoles = options.succinctRoles;
this.roles = options.roles;
}
equals(other) {
if (!(other instanceof Delegations)) {
return false;
}
return (util_1.default.isDeepStrictEqual(this.keys, other.keys) &&
util_1.default.isDeepStrictEqual(this.roles, other.roles) &&
util_1.default.isDeepStrictEqual(this.unrecognizedFields, other.unrecognizedFields) &&
util_1.default.isDeepStrictEqual(this.succinctRoles, other.succinctRoles));
}
*rolesForTarget(targetPath) {
if (this.roles) {
for (const role of Object.values(this.roles)) {
if (role.isDelegatedPath(targetPath)) {
yield { role: role.name, terminating: role.terminating };
}
}
}
else if (this.succinctRoles) {
yield {
role: this.succinctRoles.getRoleForTarget(targetPath),
terminating: true,
};
}
}
toJSON() {
const json = {
keys: keysToJSON(this.keys),
...this.unrecognizedFields,
};
if (this.roles) {
json.roles = rolesToJSON(this.roles);
}
else if (this.succinctRoles) {
json.succinct_roles = this.succinctRoles.toJSON();
}
return json;
}
static fromJSON(data) {
const { keys, roles, succinct_roles, ...unrecognizedFields } = data;
let succinctRoles;
if (utils_1.guard.isObject(succinct_roles)) {
succinctRoles = role_1.SuccinctRoles.fromJSON(succinct_roles);
}
return new Delegations({
keys: keysFromJSON(keys),
roles: rolesFromJSON(roles),
unrecognizedFields,
succinctRoles,
});
}
}
exports.Delegations = Delegations;
function keysToJSON(keys) {
return Object.entries(keys).reduce((acc, [keyId, key]) => ({
...acc,
[keyId]: key.toJSON(),
}), {});
}
function rolesToJSON(roles) {
return Object.values(roles).map((role) => role.toJSON());
}
function keysFromJSON(data) {
if (!utils_1.guard.isObjectRecord(data)) {
throw new TypeError('keys is malformed');
}
return Object.entries(data).reduce((acc, [keyID, keyData]) => ({
...acc,
[keyID]: key_1.Key.fromJSON(keyID, keyData),
}), {});
}
function rolesFromJSON(data) {
let roleMap;
if (utils_1.guard.isDefined(data)) {
if (!utils_1.guard.isObjectArray(data)) {
throw new TypeError('roles is malformed');
}
roleMap = data.reduce((acc, role) => {
const delegatedRole = role_1.DelegatedRole.fromJSON(role);
return {
...acc,
[delegatedRole.name]: delegatedRole,
};
}, {});
}
return roleMap;
}

12
my-app/node_modules/@tufjs/models/dist/error.d.ts generated vendored Executable file
View file

@ -0,0 +1,12 @@
export declare class ValueError extends Error {
}
export declare class RepositoryError extends Error {
}
export declare class UnsignedMetadataError extends RepositoryError {
}
export declare class LengthOrHashMismatchError extends RepositoryError {
}
export declare class CryptoError extends Error {
}
export declare class UnsupportedAlgorithmError extends CryptoError {
}

27
my-app/node_modules/@tufjs/models/dist/error.js generated vendored Executable file
View file

@ -0,0 +1,27 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.UnsupportedAlgorithmError = exports.CryptoError = exports.LengthOrHashMismatchError = exports.UnsignedMetadataError = exports.RepositoryError = exports.ValueError = void 0;
// An error about insufficient values
class ValueError extends Error {
}
exports.ValueError = ValueError;
// An error with a repository's state, such as a missing file.
// It covers all exceptions that come from the repository side when
// looking from the perspective of users of metadata API or ngclient.
class RepositoryError extends Error {
}
exports.RepositoryError = RepositoryError;
// An error about metadata object with insufficient threshold of signatures.
class UnsignedMetadataError extends RepositoryError {
}
exports.UnsignedMetadataError = UnsignedMetadataError;
// An error while checking the length and hash values of an object.
class LengthOrHashMismatchError extends RepositoryError {
}
exports.LengthOrHashMismatchError = LengthOrHashMismatchError;
class CryptoError extends Error {
}
exports.CryptoError = CryptoError;
class UnsupportedAlgorithmError extends CryptoError {
}
exports.UnsupportedAlgorithmError = UnsupportedAlgorithmError;

40
my-app/node_modules/@tufjs/models/dist/file.d.ts generated vendored Executable file
View file

@ -0,0 +1,40 @@
/// <reference types="node" />
/// <reference types="node" />
import { Readable } from 'stream';
import { JSONObject, JSONValue } from './utils';
interface MetaFileOptions {
version: number;
length?: number;
hashes?: Record<string, string>;
unrecognizedFields?: Record<string, JSONValue>;
}
export declare class MetaFile {
readonly version: number;
readonly length?: number;
readonly hashes?: Record<string, string>;
readonly unrecognizedFields?: Record<string, JSONValue>;
constructor(opts: MetaFileOptions);
equals(other: MetaFile): boolean;
verify(data: Buffer): void;
toJSON(): JSONObject;
static fromJSON(data: JSONObject): MetaFile;
}
interface TargetFileOptions {
length: number;
path: string;
hashes: Record<string, string>;
unrecognizedFields?: Record<string, JSONValue>;
}
export declare class TargetFile {
readonly length: number;
readonly path: string;
readonly hashes: Record<string, string>;
readonly unrecognizedFields: Record<string, JSONValue>;
constructor(opts: TargetFileOptions);
get custom(): Record<string, unknown>;
equals(other: TargetFile): boolean;
verify(stream: Readable): Promise<void>;
toJSON(): JSONObject;
static fromJSON(path: string, data: JSONObject): TargetFile;
}
export {};

183
my-app/node_modules/@tufjs/models/dist/file.js generated vendored Executable file
View file

@ -0,0 +1,183 @@
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.TargetFile = exports.MetaFile = void 0;
const crypto_1 = __importDefault(require("crypto"));
const util_1 = __importDefault(require("util"));
const error_1 = require("./error");
const utils_1 = require("./utils");
// A container with information about a particular metadata file.
//
// This class is used for Timestamp and Snapshot metadata.
class MetaFile {
constructor(opts) {
if (opts.version <= 0) {
throw new error_1.ValueError('Metafile version must be at least 1');
}
if (opts.length !== undefined) {
validateLength(opts.length);
}
this.version = opts.version;
this.length = opts.length;
this.hashes = opts.hashes;
this.unrecognizedFields = opts.unrecognizedFields || {};
}
equals(other) {
if (!(other instanceof MetaFile)) {
return false;
}
return (this.version === other.version &&
this.length === other.length &&
util_1.default.isDeepStrictEqual(this.hashes, other.hashes) &&
util_1.default.isDeepStrictEqual(this.unrecognizedFields, other.unrecognizedFields));
}
verify(data) {
// Verifies that the given data matches the expected length.
if (this.length !== undefined) {
if (data.length !== this.length) {
throw new error_1.LengthOrHashMismatchError(`Expected length ${this.length} but got ${data.length}`);
}
}
// Verifies that the given data matches the supplied hashes.
if (this.hashes) {
Object.entries(this.hashes).forEach(([key, value]) => {
let hash;
try {
hash = crypto_1.default.createHash(key);
}
catch (e) {
throw new error_1.LengthOrHashMismatchError(`Hash algorithm ${key} not supported`);
}
const observedHash = hash.update(data).digest('hex');
if (observedHash !== value) {
throw new error_1.LengthOrHashMismatchError(`Expected hash ${value} but got ${observedHash}`);
}
});
}
}
toJSON() {
const json = {
version: this.version,
...this.unrecognizedFields,
};
if (this.length !== undefined) {
json.length = this.length;
}
if (this.hashes) {
json.hashes = this.hashes;
}
return json;
}
static fromJSON(data) {
const { version, length, hashes, ...rest } = data;
if (typeof version !== 'number') {
throw new TypeError('version must be a number');
}
if (utils_1.guard.isDefined(length) && typeof length !== 'number') {
throw new TypeError('length must be a number');
}
if (utils_1.guard.isDefined(hashes) && !utils_1.guard.isStringRecord(hashes)) {
throw new TypeError('hashes must be string keys and values');
}
return new MetaFile({
version,
length,
hashes,
unrecognizedFields: rest,
});
}
}
exports.MetaFile = MetaFile;
// Container for info about a particular target file.
//
// This class is used for Target metadata.
class TargetFile {
constructor(opts) {
validateLength(opts.length);
this.length = opts.length;
this.path = opts.path;
this.hashes = opts.hashes;
this.unrecognizedFields = opts.unrecognizedFields || {};
}
get custom() {
const custom = this.unrecognizedFields['custom'];
if (!custom || Array.isArray(custom) || !(typeof custom === 'object')) {
return {};
}
return custom;
}
equals(other) {
if (!(other instanceof TargetFile)) {
return false;
}
return (this.length === other.length &&
this.path === other.path &&
util_1.default.isDeepStrictEqual(this.hashes, other.hashes) &&
util_1.default.isDeepStrictEqual(this.unrecognizedFields, other.unrecognizedFields));
}
async verify(stream) {
let observedLength = 0;
// Create a digest for each hash algorithm
const digests = Object.keys(this.hashes).reduce((acc, key) => {
try {
acc[key] = crypto_1.default.createHash(key);
}
catch (e) {
throw new error_1.LengthOrHashMismatchError(`Hash algorithm ${key} not supported`);
}
return acc;
}, {});
// Read stream chunk by chunk
for await (const chunk of stream) {
// Keep running tally of stream length
observedLength += chunk.length;
// Append chunk to each digest
Object.values(digests).forEach((digest) => {
digest.update(chunk);
});
}
// Verify length matches expected value
if (observedLength !== this.length) {
throw new error_1.LengthOrHashMismatchError(`Expected length ${this.length} but got ${observedLength}`);
}
// Verify each digest matches expected value
Object.entries(digests).forEach(([key, value]) => {
const expected = this.hashes[key];
const actual = value.digest('hex');
if (actual !== expected) {
throw new error_1.LengthOrHashMismatchError(`Expected hash ${expected} but got ${actual}`);
}
});
}
toJSON() {
return {
length: this.length,
hashes: this.hashes,
...this.unrecognizedFields,
};
}
static fromJSON(path, data) {
const { length, hashes, ...rest } = data;
if (typeof length !== 'number') {
throw new TypeError('length must be a number');
}
if (!utils_1.guard.isStringRecord(hashes)) {
throw new TypeError('hashes must have string keys and values');
}
return new TargetFile({
length,
path,
hashes,
unrecognizedFields: rest,
});
}
}
exports.TargetFile = TargetFile;
// Check that supplied length if valid
function validateLength(length) {
if (length < 0) {
throw new error_1.ValueError('Length must be at least 0');
}
}

10
my-app/node_modules/@tufjs/models/dist/index.d.ts generated vendored Executable file
View file

@ -0,0 +1,10 @@
export { MetadataKind } from './base';
export { ValueError } from './error';
export { MetaFile, TargetFile } from './file';
export { Key } from './key';
export { Metadata } from './metadata';
export { Root } from './root';
export { Signature } from './signature';
export { Snapshot } from './snapshot';
export { Targets } from './targets';
export { Timestamp } from './timestamp';

24
my-app/node_modules/@tufjs/models/dist/index.js generated vendored Executable file
View file

@ -0,0 +1,24 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.Timestamp = exports.Targets = exports.Snapshot = exports.Signature = exports.Root = exports.Metadata = exports.Key = exports.TargetFile = exports.MetaFile = exports.ValueError = exports.MetadataKind = void 0;
var base_1 = require("./base");
Object.defineProperty(exports, "MetadataKind", { enumerable: true, get: function () { return base_1.MetadataKind; } });
var error_1 = require("./error");
Object.defineProperty(exports, "ValueError", { enumerable: true, get: function () { return error_1.ValueError; } });
var file_1 = require("./file");
Object.defineProperty(exports, "MetaFile", { enumerable: true, get: function () { return file_1.MetaFile; } });
Object.defineProperty(exports, "TargetFile", { enumerable: true, get: function () { return file_1.TargetFile; } });
var key_1 = require("./key");
Object.defineProperty(exports, "Key", { enumerable: true, get: function () { return key_1.Key; } });
var metadata_1 = require("./metadata");
Object.defineProperty(exports, "Metadata", { enumerable: true, get: function () { return metadata_1.Metadata; } });
var root_1 = require("./root");
Object.defineProperty(exports, "Root", { enumerable: true, get: function () { return root_1.Root; } });
var signature_1 = require("./signature");
Object.defineProperty(exports, "Signature", { enumerable: true, get: function () { return signature_1.Signature; } });
var snapshot_1 = require("./snapshot");
Object.defineProperty(exports, "Snapshot", { enumerable: true, get: function () { return snapshot_1.Snapshot; } });
var targets_1 = require("./targets");
Object.defineProperty(exports, "Targets", { enumerable: true, get: function () { return targets_1.Targets; } });
var timestamp_1 = require("./timestamp");
Object.defineProperty(exports, "Timestamp", { enumerable: true, get: function () { return timestamp_1.Timestamp; } });

21
my-app/node_modules/@tufjs/models/dist/key.d.ts generated vendored Executable file
View file

@ -0,0 +1,21 @@
import { Signable } from './base';
import { JSONObject, JSONValue } from './utils';
export interface KeyOptions {
keyID: string;
keyType: string;
scheme: string;
keyVal: Record<string, string>;
unrecognizedFields?: Record<string, JSONValue>;
}
export declare class Key {
readonly keyID: string;
readonly keyType: string;
readonly scheme: string;
readonly keyVal: Record<string, string>;
readonly unrecognizedFields?: Record<string, JSONValue>;
constructor(options: KeyOptions);
verifySignature(metadata: Signable): void;
equals(other: Key): boolean;
toJSON(): JSONObject;
static fromJSON(keyID: string, data: JSONObject): Key;
}

85
my-app/node_modules/@tufjs/models/dist/key.js generated vendored Executable file
View file

@ -0,0 +1,85 @@
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.Key = void 0;
const util_1 = __importDefault(require("util"));
const error_1 = require("./error");
const utils_1 = require("./utils");
const key_1 = require("./utils/key");
// A container class representing the public portion of a Key.
class Key {
constructor(options) {
const { keyID, keyType, scheme, keyVal, unrecognizedFields } = options;
this.keyID = keyID;
this.keyType = keyType;
this.scheme = scheme;
this.keyVal = keyVal;
this.unrecognizedFields = unrecognizedFields || {};
}
// Verifies the that the metadata.signatures contains a signature made with
// this key and is correctly signed.
verifySignature(metadata) {
const signature = metadata.signatures[this.keyID];
if (!signature)
throw new error_1.UnsignedMetadataError('no signature for key found in metadata');
if (!this.keyVal.public)
throw new error_1.UnsignedMetadataError('no public key found');
const publicKey = (0, key_1.getPublicKey)({
keyType: this.keyType,
scheme: this.scheme,
keyVal: this.keyVal.public,
});
const signedData = metadata.signed.toJSON();
try {
if (!utils_1.crypto.verifySignature(signedData, publicKey, signature.sig)) {
throw new error_1.UnsignedMetadataError(`failed to verify ${this.keyID} signature`);
}
}
catch (error) {
if (error instanceof error_1.UnsignedMetadataError) {
throw error;
}
throw new error_1.UnsignedMetadataError(`failed to verify ${this.keyID} signature`);
}
}
equals(other) {
if (!(other instanceof Key)) {
return false;
}
return (this.keyID === other.keyID &&
this.keyType === other.keyType &&
this.scheme === other.scheme &&
util_1.default.isDeepStrictEqual(this.keyVal, other.keyVal) &&
util_1.default.isDeepStrictEqual(this.unrecognizedFields, other.unrecognizedFields));
}
toJSON() {
return {
keytype: this.keyType,
scheme: this.scheme,
keyval: this.keyVal,
...this.unrecognizedFields,
};
}
static fromJSON(keyID, data) {
const { keytype, scheme, keyval, ...rest } = data;
if (typeof keytype !== 'string') {
throw new TypeError('keytype must be a string');
}
if (typeof scheme !== 'string') {
throw new TypeError('scheme must be a string');
}
if (!utils_1.guard.isStringRecord(keyval)) {
throw new TypeError('keyval must be a string record');
}
return new Key({
keyID,
keyType: keytype,
scheme,
keyVal: keyval,
unrecognizedFields: rest,
});
}
}
exports.Key = Key;

48
my-app/node_modules/@tufjs/models/dist/metadata.d.ts generated vendored Executable file
View file

@ -0,0 +1,48 @@
/// <reference types="node" />
import { MetadataKind, Signable } from './base';
import { Root } from './root';
import { Signature } from './signature';
import { Snapshot } from './snapshot';
import { Targets } from './targets';
import { Timestamp } from './timestamp';
import { JSONObject, JSONValue } from './utils';
type MetadataType = Root | Timestamp | Snapshot | Targets;
/***
* A container for signed TUF metadata.
*
* Provides methods to convert to and from json, read and write to and
* from JSON and to create and verify metadata signatures.
*
* ``Metadata[T]`` is a generic container type where T can be any one type of
* [``Root``, ``Timestamp``, ``Snapshot``, ``Targets``]. The purpose of this
* is to allow static type checking of the signed attribute in code using
* Metadata::
*
* root_md = Metadata[Root].fromJSON("root.json")
* # root_md type is now Metadata[Root]. This means signed and its
* # attributes like consistent_snapshot are now statically typed and the
* # types can be verified by static type checkers and shown by IDEs
*
* Using a type constraint is not required but not doing so means T is not a
* specific type so static typing cannot happen. Note that the type constraint
* ``[Root]`` is not validated at runtime (as pure annotations are not available
* then).
*
* Apart from ``expires`` all of the arguments to the inner constructors have
* reasonable default values for new metadata.
*/
export declare class Metadata<T extends MetadataType> implements Signable {
signed: T;
signatures: Record<string, Signature>;
unrecognizedFields: Record<string, JSONValue>;
constructor(signed: T, signatures?: Record<string, Signature>, unrecognizedFields?: Record<string, JSONValue>);
sign(signer: (data: Buffer) => Signature, append?: boolean): void;
verifyDelegate(delegatedRole: string, delegatedMetadata: Metadata<MetadataType>): void;
equals(other: T): boolean;
toJSON(): JSONObject;
static fromJSON(type: MetadataKind.Root, data: JSONObject): Metadata<Root>;
static fromJSON(type: MetadataKind.Timestamp, data: JSONObject): Metadata<Timestamp>;
static fromJSON(type: MetadataKind.Snapshot, data: JSONObject): Metadata<Snapshot>;
static fromJSON(type: MetadataKind.Targets, data: JSONObject): Metadata<Targets>;
}
export {};

158
my-app/node_modules/@tufjs/models/dist/metadata.js generated vendored Executable file
View file

@ -0,0 +1,158 @@
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.Metadata = void 0;
const canonical_json_1 = require("@tufjs/canonical-json");
const util_1 = __importDefault(require("util"));
const base_1 = require("./base");
const error_1 = require("./error");
const root_1 = require("./root");
const signature_1 = require("./signature");
const snapshot_1 = require("./snapshot");
const targets_1 = require("./targets");
const timestamp_1 = require("./timestamp");
const utils_1 = require("./utils");
/***
* A container for signed TUF metadata.
*
* Provides methods to convert to and from json, read and write to and
* from JSON and to create and verify metadata signatures.
*
* ``Metadata[T]`` is a generic container type where T can be any one type of
* [``Root``, ``Timestamp``, ``Snapshot``, ``Targets``]. The purpose of this
* is to allow static type checking of the signed attribute in code using
* Metadata::
*
* root_md = Metadata[Root].fromJSON("root.json")
* # root_md type is now Metadata[Root]. This means signed and its
* # attributes like consistent_snapshot are now statically typed and the
* # types can be verified by static type checkers and shown by IDEs
*
* Using a type constraint is not required but not doing so means T is not a
* specific type so static typing cannot happen. Note that the type constraint
* ``[Root]`` is not validated at runtime (as pure annotations are not available
* then).
*
* Apart from ``expires`` all of the arguments to the inner constructors have
* reasonable default values for new metadata.
*/
class Metadata {
constructor(signed, signatures, unrecognizedFields) {
this.signed = signed;
this.signatures = signatures || {};
this.unrecognizedFields = unrecognizedFields || {};
}
sign(signer, append = true) {
const bytes = Buffer.from((0, canonical_json_1.canonicalize)(this.signed.toJSON()));
const signature = signer(bytes);
if (!append) {
this.signatures = {};
}
this.signatures[signature.keyID] = signature;
}
verifyDelegate(delegatedRole, delegatedMetadata) {
let role;
let keys = {};
switch (this.signed.type) {
case base_1.MetadataKind.Root:
keys = this.signed.keys;
role = this.signed.roles[delegatedRole];
break;
case base_1.MetadataKind.Targets:
if (!this.signed.delegations) {
throw new error_1.ValueError(`No delegations found for ${delegatedRole}`);
}
keys = this.signed.delegations.keys;
if (this.signed.delegations.roles) {
role = this.signed.delegations.roles[delegatedRole];
}
else if (this.signed.delegations.succinctRoles) {
if (this.signed.delegations.succinctRoles.isDelegatedRole(delegatedRole)) {
role = this.signed.delegations.succinctRoles;
}
}
break;
default:
throw new TypeError('invalid metadata type');
}
if (!role) {
throw new error_1.ValueError(`no delegation found for ${delegatedRole}`);
}
const signingKeys = new Set();
role.keyIDs.forEach((keyID) => {
const key = keys[keyID];
// If we dont' have the key, continue checking other keys
if (!key) {
return;
}
try {
key.verifySignature(delegatedMetadata);
signingKeys.add(key.keyID);
}
catch (error) {
// continue
}
});
if (signingKeys.size < role.threshold) {
throw new error_1.UnsignedMetadataError(`${delegatedRole} was signed by ${signingKeys.size}/${role.threshold} keys`);
}
}
equals(other) {
if (!(other instanceof Metadata)) {
return false;
}
return (this.signed.equals(other.signed) &&
util_1.default.isDeepStrictEqual(this.signatures, other.signatures) &&
util_1.default.isDeepStrictEqual(this.unrecognizedFields, other.unrecognizedFields));
}
toJSON() {
const signatures = Object.values(this.signatures).map((signature) => {
return signature.toJSON();
});
return {
signatures,
signed: this.signed.toJSON(),
...this.unrecognizedFields,
};
}
static fromJSON(type, data) {
const { signed, signatures, ...rest } = data;
if (!utils_1.guard.isDefined(signed) || !utils_1.guard.isObject(signed)) {
throw new TypeError('signed is not defined');
}
if (type !== signed._type) {
throw new error_1.ValueError(`expected '${type}', got ${signed['_type']}`);
}
let signedObj;
switch (type) {
case base_1.MetadataKind.Root:
signedObj = root_1.Root.fromJSON(signed);
break;
case base_1.MetadataKind.Timestamp:
signedObj = timestamp_1.Timestamp.fromJSON(signed);
break;
case base_1.MetadataKind.Snapshot:
signedObj = snapshot_1.Snapshot.fromJSON(signed);
break;
case base_1.MetadataKind.Targets:
signedObj = targets_1.Targets.fromJSON(signed);
break;
default:
throw new TypeError('invalid metadata type');
}
const sigMap = signaturesFromJSON(signatures);
return new Metadata(signedObj, sigMap, rest);
}
}
exports.Metadata = Metadata;
function signaturesFromJSON(data) {
if (!utils_1.guard.isObjectArray(data)) {
throw new TypeError('signatures is not an array');
}
return data.reduce((acc, sigData) => {
const signature = signature_1.Signature.fromJSON(sigData);
return { ...acc, [signature.keyID]: signature };
}, {});
}

103
my-app/node_modules/@tufjs/models/dist/role.d.ts generated vendored Executable file
View file

@ -0,0 +1,103 @@
import { JSONObject, JSONValue } from './utils';
export declare const TOP_LEVEL_ROLE_NAMES: string[];
export interface RoleOptions {
keyIDs: string[];
threshold: number;
unrecognizedFields?: Record<string, JSONValue>;
}
/**
* Container that defines which keys are required to sign roles metadata.
*
* Role defines how many keys are required to successfully sign the roles
* metadata, and which keys are accepted.
*/
export declare class Role {
readonly keyIDs: string[];
readonly threshold: number;
readonly unrecognizedFields?: Record<string, JSONValue>;
constructor(options: RoleOptions);
equals(other: Role): boolean;
toJSON(): JSONObject;
static fromJSON(data: JSONObject): Role;
}
interface DelegatedRoleOptions extends RoleOptions {
name: string;
terminating: boolean;
paths?: string[];
pathHashPrefixes?: string[];
}
/**
* A container with information about a delegated role.
*
* A delegation can happen in two ways:
* - ``paths`` is set: delegates targets matching any path pattern in ``paths``
* - ``pathHashPrefixes`` is set: delegates targets whose target path hash
* starts with any of the prefixes in ``pathHashPrefixes``
*
* ``paths`` and ``pathHashPrefixes`` are mutually exclusive: both cannot be
* set, at least one of them must be set.
*/
export declare class DelegatedRole extends Role {
readonly name: string;
readonly terminating: boolean;
readonly paths?: string[];
readonly pathHashPrefixes?: string[];
constructor(opts: DelegatedRoleOptions);
equals(other: DelegatedRole): boolean;
isDelegatedPath(targetFilepath: string): boolean;
toJSON(): JSONObject;
static fromJSON(data: JSONObject): DelegatedRole;
}
interface SuccinctRolesOption extends RoleOptions {
bitLength: number;
namePrefix: string;
}
/**
* Succinctly defines a hash bin delegation graph.
*
* A ``SuccinctRoles`` object describes a delegation graph that covers all
* targets, distributing them uniformly over the delegated roles (i.e. bins)
* in the graph.
*
* The total number of bins is 2 to the power of the passed ``bit_length``.
*
* Bin names are the concatenation of the passed ``name_prefix`` and a
* zero-padded hex representation of the bin index separated by a hyphen.
*
* The passed ``keyids`` and ``threshold`` is used for each bin, and each bin
* is 'terminating'.
*
* For details: https://github.com/theupdateframework/taps/blob/master/tap15.md
*/
export declare class SuccinctRoles extends Role {
readonly bitLength: number;
readonly namePrefix: string;
readonly numberOfBins: number;
readonly suffixLen: number;
constructor(opts: SuccinctRolesOption);
equals(other: SuccinctRoles): boolean;
/***
* Calculates the name of the delegated role responsible for 'target_filepath'.
*
* The target at path ''target_filepath' is assigned to a bin by casting
* the left-most 'bit_length' of bits of the file path hash digest to
* int, using it as bin index between 0 and '2**bit_length - 1'.
*
* Args:
* target_filepath: URL path to a target file, relative to a base
* targets URL.
*/
getRoleForTarget(targetFilepath: string): string;
getRoles(): Generator<string>;
/***
* Determines whether the given ``role_name`` is in one of
* the delegated roles that ``SuccinctRoles`` represents.
*
* Args:
* role_name: The name of the role to check against.
*/
isDelegatedRole(roleName: string): boolean;
toJSON(): JSONObject;
static fromJSON(data: JSONObject): SuccinctRoles;
}
export {};

299
my-app/node_modules/@tufjs/models/dist/role.js generated vendored Executable file
View file

@ -0,0 +1,299 @@
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.SuccinctRoles = exports.DelegatedRole = exports.Role = exports.TOP_LEVEL_ROLE_NAMES = void 0;
const crypto_1 = __importDefault(require("crypto"));
const minimatch_1 = require("minimatch");
const util_1 = __importDefault(require("util"));
const error_1 = require("./error");
const utils_1 = require("./utils");
exports.TOP_LEVEL_ROLE_NAMES = [
'root',
'targets',
'snapshot',
'timestamp',
];
/**
* Container that defines which keys are required to sign roles metadata.
*
* Role defines how many keys are required to successfully sign the roles
* metadata, and which keys are accepted.
*/
class Role {
constructor(options) {
const { keyIDs, threshold, unrecognizedFields } = options;
if (hasDuplicates(keyIDs)) {
throw new error_1.ValueError('duplicate key IDs found');
}
if (threshold < 1) {
throw new error_1.ValueError('threshold must be at least 1');
}
this.keyIDs = keyIDs;
this.threshold = threshold;
this.unrecognizedFields = unrecognizedFields || {};
}
equals(other) {
if (!(other instanceof Role)) {
return false;
}
return (this.threshold === other.threshold &&
util_1.default.isDeepStrictEqual(this.keyIDs, other.keyIDs) &&
util_1.default.isDeepStrictEqual(this.unrecognizedFields, other.unrecognizedFields));
}
toJSON() {
return {
keyids: this.keyIDs,
threshold: this.threshold,
...this.unrecognizedFields,
};
}
static fromJSON(data) {
const { keyids, threshold, ...rest } = data;
if (!utils_1.guard.isStringArray(keyids)) {
throw new TypeError('keyids must be an array');
}
if (typeof threshold !== 'number') {
throw new TypeError('threshold must be a number');
}
return new Role({
keyIDs: keyids,
threshold,
unrecognizedFields: rest,
});
}
}
exports.Role = Role;
function hasDuplicates(array) {
return new Set(array).size !== array.length;
}
/**
* A container with information about a delegated role.
*
* A delegation can happen in two ways:
* - ``paths`` is set: delegates targets matching any path pattern in ``paths``
* - ``pathHashPrefixes`` is set: delegates targets whose target path hash
* starts with any of the prefixes in ``pathHashPrefixes``
*
* ``paths`` and ``pathHashPrefixes`` are mutually exclusive: both cannot be
* set, at least one of them must be set.
*/
class DelegatedRole extends Role {
constructor(opts) {
super(opts);
const { name, terminating, paths, pathHashPrefixes } = opts;
this.name = name;
this.terminating = terminating;
if (opts.paths && opts.pathHashPrefixes) {
throw new error_1.ValueError('paths and pathHashPrefixes are mutually exclusive');
}
this.paths = paths;
this.pathHashPrefixes = pathHashPrefixes;
}
equals(other) {
if (!(other instanceof DelegatedRole)) {
return false;
}
return (super.equals(other) &&
this.name === other.name &&
this.terminating === other.terminating &&
util_1.default.isDeepStrictEqual(this.paths, other.paths) &&
util_1.default.isDeepStrictEqual(this.pathHashPrefixes, other.pathHashPrefixes));
}
isDelegatedPath(targetFilepath) {
if (this.paths) {
return this.paths.some((pathPattern) => isTargetInPathPattern(targetFilepath, pathPattern));
}
if (this.pathHashPrefixes) {
const hasher = crypto_1.default.createHash('sha256');
const pathHash = hasher.update(targetFilepath).digest('hex');
return this.pathHashPrefixes.some((pathHashPrefix) => pathHash.startsWith(pathHashPrefix));
}
return false;
}
toJSON() {
const json = {
...super.toJSON(),
name: this.name,
terminating: this.terminating,
};
if (this.paths) {
json.paths = this.paths;
}
if (this.pathHashPrefixes) {
json.path_hash_prefixes = this.pathHashPrefixes;
}
return json;
}
static fromJSON(data) {
const { keyids, threshold, name, terminating, paths, path_hash_prefixes, ...rest } = data;
if (!utils_1.guard.isStringArray(keyids)) {
throw new TypeError('keyids must be an array of strings');
}
if (typeof threshold !== 'number') {
throw new TypeError('threshold must be a number');
}
if (typeof name !== 'string') {
throw new TypeError('name must be a string');
}
if (typeof terminating !== 'boolean') {
throw new TypeError('terminating must be a boolean');
}
if (utils_1.guard.isDefined(paths) && !utils_1.guard.isStringArray(paths)) {
throw new TypeError('paths must be an array of strings');
}
if (utils_1.guard.isDefined(path_hash_prefixes) &&
!utils_1.guard.isStringArray(path_hash_prefixes)) {
throw new TypeError('path_hash_prefixes must be an array of strings');
}
return new DelegatedRole({
keyIDs: keyids,
threshold,
name,
terminating,
paths,
pathHashPrefixes: path_hash_prefixes,
unrecognizedFields: rest,
});
}
}
exports.DelegatedRole = DelegatedRole;
// JS version of Ruby's Array#zip
const zip = (a, b) => a.map((k, i) => [k, b[i]]);
function isTargetInPathPattern(target, pattern) {
const targetParts = target.split('/');
const patternParts = pattern.split('/');
if (patternParts.length != targetParts.length) {
return false;
}
return zip(targetParts, patternParts).every(([targetPart, patternPart]) => (0, minimatch_1.minimatch)(targetPart, patternPart));
}
/**
* Succinctly defines a hash bin delegation graph.
*
* A ``SuccinctRoles`` object describes a delegation graph that covers all
* targets, distributing them uniformly over the delegated roles (i.e. bins)
* in the graph.
*
* The total number of bins is 2 to the power of the passed ``bit_length``.
*
* Bin names are the concatenation of the passed ``name_prefix`` and a
* zero-padded hex representation of the bin index separated by a hyphen.
*
* The passed ``keyids`` and ``threshold`` is used for each bin, and each bin
* is 'terminating'.
*
* For details: https://github.com/theupdateframework/taps/blob/master/tap15.md
*/
class SuccinctRoles extends Role {
constructor(opts) {
super(opts);
const { bitLength, namePrefix } = opts;
if (bitLength <= 0 || bitLength > 32) {
throw new error_1.ValueError('bitLength must be between 1 and 32');
}
this.bitLength = bitLength;
this.namePrefix = namePrefix;
// Calculate the suffix_len value based on the total number of bins in
// hex. If bit_length = 10 then number_of_bins = 1024 or bin names will
// have a suffix between "000" and "3ff" in hex and suffix_len will be 3
// meaning the third bin will have a suffix of "003".
this.numberOfBins = Math.pow(2, bitLength);
// suffix_len is calculated based on "number_of_bins - 1" as the name
// of the last bin contains the number "number_of_bins -1" as a suffix.
this.suffixLen = (this.numberOfBins - 1).toString(16).length;
}
equals(other) {
if (!(other instanceof SuccinctRoles)) {
return false;
}
return (super.equals(other) &&
this.bitLength === other.bitLength &&
this.namePrefix === other.namePrefix);
}
/***
* Calculates the name of the delegated role responsible for 'target_filepath'.
*
* The target at path ''target_filepath' is assigned to a bin by casting
* the left-most 'bit_length' of bits of the file path hash digest to
* int, using it as bin index between 0 and '2**bit_length - 1'.
*
* Args:
* target_filepath: URL path to a target file, relative to a base
* targets URL.
*/
getRoleForTarget(targetFilepath) {
const hasher = crypto_1.default.createHash('sha256');
const hasherBuffer = hasher.update(targetFilepath).digest();
// can't ever need more than 4 bytes (32 bits).
const hashBytes = hasherBuffer.subarray(0, 4);
// Right shift hash bytes, so that we only have the leftmost
// bit_length bits that we care about.
const shiftValue = 32 - this.bitLength;
const binNumber = hashBytes.readUInt32BE() >>> shiftValue;
// Add zero padding if necessary and cast to hex the suffix.
const suffix = binNumber.toString(16).padStart(this.suffixLen, '0');
return `${this.namePrefix}-${suffix}`;
}
*getRoles() {
for (let i = 0; i < this.numberOfBins; i++) {
const suffix = i.toString(16).padStart(this.suffixLen, '0');
yield `${this.namePrefix}-${suffix}`;
}
}
/***
* Determines whether the given ``role_name`` is in one of
* the delegated roles that ``SuccinctRoles`` represents.
*
* Args:
* role_name: The name of the role to check against.
*/
isDelegatedRole(roleName) {
const desiredPrefix = this.namePrefix + '-';
if (!roleName.startsWith(desiredPrefix)) {
return false;
}
const suffix = roleName.slice(desiredPrefix.length, roleName.length);
if (suffix.length != this.suffixLen) {
return false;
}
// make sure the suffix is a hex string
if (!suffix.match(/^[0-9a-fA-F]+$/)) {
return false;
}
const num = parseInt(suffix, 16);
return 0 <= num && num < this.numberOfBins;
}
toJSON() {
const json = {
...super.toJSON(),
bit_length: this.bitLength,
name_prefix: this.namePrefix,
};
return json;
}
static fromJSON(data) {
const { keyids, threshold, bit_length, name_prefix, ...rest } = data;
if (!utils_1.guard.isStringArray(keyids)) {
throw new TypeError('keyids must be an array of strings');
}
if (typeof threshold !== 'number') {
throw new TypeError('threshold must be a number');
}
if (typeof bit_length !== 'number') {
throw new TypeError('bit_length must be a number');
}
if (typeof name_prefix !== 'string') {
throw new TypeError('name_prefix must be a string');
}
return new SuccinctRoles({
keyIDs: keyids,
threshold,
bitLength: bit_length,
namePrefix: name_prefix,
unrecognizedFields: rest,
});
}
}
exports.SuccinctRoles = SuccinctRoles;

29
my-app/node_modules/@tufjs/models/dist/root.d.ts generated vendored Executable file
View file

@ -0,0 +1,29 @@
import { MetadataKind, Signed, SignedOptions } from './base';
import { Key } from './key';
import { Role } from './role';
import { JSONObject } from './utils';
type KeyMap = Record<string, Key>;
type RoleMap = Record<string, Role>;
export interface RootOptions extends SignedOptions {
keys?: Record<string, Key>;
roles?: Record<string, Role>;
consistentSnapshot?: boolean;
}
/**
* A container for the signed part of root metadata.
*
* The top-level role and metadata file signed by the root keys.
* This role specifies trusted keys for all other top-level roles, which may further delegate trust.
*/
export declare class Root extends Signed {
readonly type = MetadataKind.Root;
readonly keys: KeyMap;
readonly roles: RoleMap;
readonly consistentSnapshot: boolean;
constructor(options: RootOptions);
addKey(key: Key, role: string): void;
equals(other: Root): boolean;
toJSON(): JSONObject;
static fromJSON(data: JSONObject): Root;
}
export {};

116
my-app/node_modules/@tufjs/models/dist/root.js generated vendored Executable file
View file

@ -0,0 +1,116 @@
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.Root = void 0;
const util_1 = __importDefault(require("util"));
const base_1 = require("./base");
const error_1 = require("./error");
const key_1 = require("./key");
const role_1 = require("./role");
const utils_1 = require("./utils");
/**
* A container for the signed part of root metadata.
*
* The top-level role and metadata file signed by the root keys.
* This role specifies trusted keys for all other top-level roles, which may further delegate trust.
*/
class Root extends base_1.Signed {
constructor(options) {
super(options);
this.type = base_1.MetadataKind.Root;
this.keys = options.keys || {};
this.consistentSnapshot = options.consistentSnapshot ?? true;
if (!options.roles) {
this.roles = role_1.TOP_LEVEL_ROLE_NAMES.reduce((acc, role) => ({
...acc,
[role]: new role_1.Role({ keyIDs: [], threshold: 1 }),
}), {});
}
else {
const roleNames = new Set(Object.keys(options.roles));
if (!role_1.TOP_LEVEL_ROLE_NAMES.every((role) => roleNames.has(role))) {
throw new error_1.ValueError('missing top-level role');
}
this.roles = options.roles;
}
}
addKey(key, role) {
if (!this.roles[role]) {
throw new error_1.ValueError(`role ${role} does not exist`);
}
if (!this.roles[role].keyIDs.includes(key.keyID)) {
this.roles[role].keyIDs.push(key.keyID);
}
this.keys[key.keyID] = key;
}
equals(other) {
if (!(other instanceof Root)) {
return false;
}
return (super.equals(other) &&
this.consistentSnapshot === other.consistentSnapshot &&
util_1.default.isDeepStrictEqual(this.keys, other.keys) &&
util_1.default.isDeepStrictEqual(this.roles, other.roles));
}
toJSON() {
return {
_type: this.type,
spec_version: this.specVersion,
version: this.version,
expires: this.expires,
keys: keysToJSON(this.keys),
roles: rolesToJSON(this.roles),
consistent_snapshot: this.consistentSnapshot,
...this.unrecognizedFields,
};
}
static fromJSON(data) {
const { unrecognizedFields, ...commonFields } = base_1.Signed.commonFieldsFromJSON(data);
const { keys, roles, consistent_snapshot, ...rest } = unrecognizedFields;
if (typeof consistent_snapshot !== 'boolean') {
throw new TypeError('consistent_snapshot must be a boolean');
}
return new Root({
...commonFields,
keys: keysFromJSON(keys),
roles: rolesFromJSON(roles),
consistentSnapshot: consistent_snapshot,
unrecognizedFields: rest,
});
}
}
exports.Root = Root;
function keysToJSON(keys) {
return Object.entries(keys).reduce((acc, [keyID, key]) => ({ ...acc, [keyID]: key.toJSON() }), {});
}
function rolesToJSON(roles) {
return Object.entries(roles).reduce((acc, [roleName, role]) => ({ ...acc, [roleName]: role.toJSON() }), {});
}
function keysFromJSON(data) {
let keys;
if (utils_1.guard.isDefined(data)) {
if (!utils_1.guard.isObjectRecord(data)) {
throw new TypeError('keys must be an object');
}
keys = Object.entries(data).reduce((acc, [keyID, keyData]) => ({
...acc,
[keyID]: key_1.Key.fromJSON(keyID, keyData),
}), {});
}
return keys;
}
function rolesFromJSON(data) {
let roles;
if (utils_1.guard.isDefined(data)) {
if (!utils_1.guard.isObjectRecord(data)) {
throw new TypeError('roles must be an object');
}
roles = Object.entries(data).reduce((acc, [roleName, roleData]) => ({
...acc,
[roleName]: role_1.Role.fromJSON(roleData),
}), {});
}
return roles;
}

20
my-app/node_modules/@tufjs/models/dist/signature.d.ts generated vendored Executable file
View file

@ -0,0 +1,20 @@
import { JSONObject } from './utils';
export interface SignatureOptions {
keyID: string;
sig: string;
}
/**
* A container class containing information about a signature.
*
* Contains a signature and the keyid uniquely identifying the key used
* to generate the signature.
*
* Provide a `fromJSON` method to create a Signature from a JSON object.
*/
export declare class Signature {
readonly keyID: string;
readonly sig: string;
constructor(options: SignatureOptions);
toJSON(): JSONObject;
static fromJSON(data: JSONObject): Signature;
}

38
my-app/node_modules/@tufjs/models/dist/signature.js generated vendored Executable file
View file

@ -0,0 +1,38 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.Signature = void 0;
/**
* A container class containing information about a signature.
*
* Contains a signature and the keyid uniquely identifying the key used
* to generate the signature.
*
* Provide a `fromJSON` method to create a Signature from a JSON object.
*/
class Signature {
constructor(options) {
const { keyID, sig } = options;
this.keyID = keyID;
this.sig = sig;
}
toJSON() {
return {
keyid: this.keyID,
sig: this.sig,
};
}
static fromJSON(data) {
const { keyid, sig } = data;
if (typeof keyid !== 'string') {
throw new TypeError('keyid must be a string');
}
if (typeof sig !== 'string') {
throw new TypeError('sig must be a string');
}
return new Signature({
keyID: keyid,
sig: sig,
});
}
}
exports.Signature = Signature;

23
my-app/node_modules/@tufjs/models/dist/snapshot.d.ts generated vendored Executable file
View file

@ -0,0 +1,23 @@
import { MetadataKind, Signed, SignedOptions } from './base';
import { MetaFile } from './file';
import { JSONObject } from './utils';
type MetaFileMap = Record<string, MetaFile>;
export interface SnapshotOptions extends SignedOptions {
meta?: MetaFileMap;
}
/**
* A container for the signed part of snapshot metadata.
*
* Snapshot contains information about all target Metadata files.
* A top-level role that specifies the latest versions of all targets metadata files,
* and hence the latest versions of all targets (including any dependencies between them) on the repository.
*/
export declare class Snapshot extends Signed {
readonly type = MetadataKind.Snapshot;
readonly meta: MetaFileMap;
constructor(opts: SnapshotOptions);
equals(other: Snapshot): boolean;
toJSON(): JSONObject;
static fromJSON(data: JSONObject): Snapshot;
}
export {};

71
my-app/node_modules/@tufjs/models/dist/snapshot.js generated vendored Executable file
View file

@ -0,0 +1,71 @@
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.Snapshot = void 0;
const util_1 = __importDefault(require("util"));
const base_1 = require("./base");
const file_1 = require("./file");
const utils_1 = require("./utils");
/**
* A container for the signed part of snapshot metadata.
*
* Snapshot contains information about all target Metadata files.
* A top-level role that specifies the latest versions of all targets metadata files,
* and hence the latest versions of all targets (including any dependencies between them) on the repository.
*/
class Snapshot extends base_1.Signed {
constructor(opts) {
super(opts);
this.type = base_1.MetadataKind.Snapshot;
this.meta = opts.meta || { 'targets.json': new file_1.MetaFile({ version: 1 }) };
}
equals(other) {
if (!(other instanceof Snapshot)) {
return false;
}
return super.equals(other) && util_1.default.isDeepStrictEqual(this.meta, other.meta);
}
toJSON() {
return {
_type: this.type,
meta: metaToJSON(this.meta),
spec_version: this.specVersion,
version: this.version,
expires: this.expires,
...this.unrecognizedFields,
};
}
static fromJSON(data) {
const { unrecognizedFields, ...commonFields } = base_1.Signed.commonFieldsFromJSON(data);
const { meta, ...rest } = unrecognizedFields;
return new Snapshot({
...commonFields,
meta: metaFromJSON(meta),
unrecognizedFields: rest,
});
}
}
exports.Snapshot = Snapshot;
function metaToJSON(meta) {
return Object.entries(meta).reduce((acc, [path, metadata]) => ({
...acc,
[path]: metadata.toJSON(),
}), {});
}
function metaFromJSON(data) {
let meta;
if (utils_1.guard.isDefined(data)) {
if (!utils_1.guard.isObjectRecord(data)) {
throw new TypeError('meta field is malformed');
}
else {
meta = Object.entries(data).reduce((acc, [path, metadata]) => ({
...acc,
[path]: file_1.MetaFile.fromJSON(metadata),
}), {});
}
}
return meta;
}

20
my-app/node_modules/@tufjs/models/dist/targets.d.ts generated vendored Executable file
View file

@ -0,0 +1,20 @@
import { MetadataKind, Signed, SignedOptions } from './base';
import { Delegations } from './delegations';
import { TargetFile } from './file';
import { JSONObject } from './utils';
type TargetFileMap = Record<string, TargetFile>;
interface TargetsOptions extends SignedOptions {
targets?: TargetFileMap;
delegations?: Delegations;
}
export declare class Targets extends Signed {
readonly type = MetadataKind.Targets;
readonly targets: TargetFileMap;
readonly delegations?: Delegations;
constructor(options: TargetsOptions);
addTarget(target: TargetFile): void;
equals(other: Targets): boolean;
toJSON(): JSONObject;
static fromJSON(data: JSONObject): Targets;
}
export {};

92
my-app/node_modules/@tufjs/models/dist/targets.js generated vendored Executable file
View file

@ -0,0 +1,92 @@
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.Targets = void 0;
const util_1 = __importDefault(require("util"));
const base_1 = require("./base");
const delegations_1 = require("./delegations");
const file_1 = require("./file");
const utils_1 = require("./utils");
// Container for the signed part of targets metadata.
//
// Targets contains verifying information about target files and also delegates
// responsible to other Targets roles.
class Targets extends base_1.Signed {
constructor(options) {
super(options);
this.type = base_1.MetadataKind.Targets;
this.targets = options.targets || {};
this.delegations = options.delegations;
}
addTarget(target) {
this.targets[target.path] = target;
}
equals(other) {
if (!(other instanceof Targets)) {
return false;
}
return (super.equals(other) &&
util_1.default.isDeepStrictEqual(this.targets, other.targets) &&
util_1.default.isDeepStrictEqual(this.delegations, other.delegations));
}
toJSON() {
const json = {
_type: this.type,
spec_version: this.specVersion,
version: this.version,
expires: this.expires,
targets: targetsToJSON(this.targets),
...this.unrecognizedFields,
};
if (this.delegations) {
json.delegations = this.delegations.toJSON();
}
return json;
}
static fromJSON(data) {
const { unrecognizedFields, ...commonFields } = base_1.Signed.commonFieldsFromJSON(data);
const { targets, delegations, ...rest } = unrecognizedFields;
return new Targets({
...commonFields,
targets: targetsFromJSON(targets),
delegations: delegationsFromJSON(delegations),
unrecognizedFields: rest,
});
}
}
exports.Targets = Targets;
function targetsToJSON(targets) {
return Object.entries(targets).reduce((acc, [path, target]) => ({
...acc,
[path]: target.toJSON(),
}), {});
}
function targetsFromJSON(data) {
let targets;
if (utils_1.guard.isDefined(data)) {
if (!utils_1.guard.isObjectRecord(data)) {
throw new TypeError('targets must be an object');
}
else {
targets = Object.entries(data).reduce((acc, [path, target]) => ({
...acc,
[path]: file_1.TargetFile.fromJSON(path, target),
}), {});
}
}
return targets;
}
function delegationsFromJSON(data) {
let delegations;
if (utils_1.guard.isDefined(data)) {
if (!utils_1.guard.isObject(data)) {
throw new TypeError('delegations must be an object');
}
else {
delegations = delegations_1.Delegations.fromJSON(data);
}
}
return delegations;
}

21
my-app/node_modules/@tufjs/models/dist/timestamp.d.ts generated vendored Executable file
View file

@ -0,0 +1,21 @@
import { MetadataKind, Signed, SignedOptions } from './base';
import { MetaFile } from './file';
import { JSONObject } from './utils';
interface TimestampOptions extends SignedOptions {
snapshotMeta?: MetaFile;
}
/**
* A container for the signed part of timestamp metadata.
*
* A top-level that specifies the latest version of the snapshot role metadata file,
* and hence the latest versions of all metadata and targets on the repository.
*/
export declare class Timestamp extends Signed {
readonly type = MetadataKind.Timestamp;
readonly snapshotMeta: MetaFile;
constructor(options: TimestampOptions);
equals(other: Timestamp): boolean;
toJSON(): JSONObject;
static fromJSON(data: JSONObject): Timestamp;
}
export {};

58
my-app/node_modules/@tufjs/models/dist/timestamp.js generated vendored Executable file
View file

@ -0,0 +1,58 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.Timestamp = void 0;
const base_1 = require("./base");
const file_1 = require("./file");
const utils_1 = require("./utils");
/**
* A container for the signed part of timestamp metadata.
*
* A top-level that specifies the latest version of the snapshot role metadata file,
* and hence the latest versions of all metadata and targets on the repository.
*/
class Timestamp extends base_1.Signed {
constructor(options) {
super(options);
this.type = base_1.MetadataKind.Timestamp;
this.snapshotMeta = options.snapshotMeta || new file_1.MetaFile({ version: 1 });
}
equals(other) {
if (!(other instanceof Timestamp)) {
return false;
}
return super.equals(other) && this.snapshotMeta.equals(other.snapshotMeta);
}
toJSON() {
return {
_type: this.type,
spec_version: this.specVersion,
version: this.version,
expires: this.expires,
meta: { 'snapshot.json': this.snapshotMeta.toJSON() },
...this.unrecognizedFields,
};
}
static fromJSON(data) {
const { unrecognizedFields, ...commonFields } = base_1.Signed.commonFieldsFromJSON(data);
const { meta, ...rest } = unrecognizedFields;
return new Timestamp({
...commonFields,
snapshotMeta: snapshotMetaFromJSON(meta),
unrecognizedFields: rest,
});
}
}
exports.Timestamp = Timestamp;
function snapshotMetaFromJSON(data) {
let snapshotMeta;
if (utils_1.guard.isDefined(data)) {
const snapshotData = data['snapshot.json'];
if (!utils_1.guard.isDefined(snapshotData) || !utils_1.guard.isObject(snapshotData)) {
throw new TypeError('missing snapshot.json in meta');
}
else {
snapshotMeta = file_1.MetaFile.fromJSON(snapshotData);
}
}
return snapshotMeta;
}

7
my-app/node_modules/@tufjs/models/dist/utils/guard.d.ts generated vendored Executable file
View file

@ -0,0 +1,7 @@
import { JSONObject } from './types';
export declare function isDefined<T>(val: T | undefined): val is T;
export declare function isObject(value: unknown): value is JSONObject;
export declare function isStringArray(value: unknown): value is string[];
export declare function isObjectArray(value: unknown): value is JSONObject[];
export declare function isStringRecord(value: unknown): value is Record<string, string>;
export declare function isObjectRecord(value: unknown): value is Record<string, JSONObject>;

33
my-app/node_modules/@tufjs/models/dist/utils/guard.js generated vendored Executable file
View file

@ -0,0 +1,33 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.isObjectRecord = exports.isStringRecord = exports.isObjectArray = exports.isStringArray = exports.isObject = exports.isDefined = void 0;
function isDefined(val) {
return val !== undefined;
}
exports.isDefined = isDefined;
function isObject(value) {
return typeof value === 'object' && value !== null;
}
exports.isObject = isObject;
function isStringArray(value) {
return Array.isArray(value) && value.every((v) => typeof v === 'string');
}
exports.isStringArray = isStringArray;
function isObjectArray(value) {
return Array.isArray(value) && value.every(isObject);
}
exports.isObjectArray = isObjectArray;
function isStringRecord(value) {
return (typeof value === 'object' &&
value !== null &&
Object.keys(value).every((k) => typeof k === 'string') &&
Object.values(value).every((v) => typeof v === 'string'));
}
exports.isStringRecord = isStringRecord;
function isObjectRecord(value) {
return (typeof value === 'object' &&
value !== null &&
Object.keys(value).every((k) => typeof k === 'string') &&
Object.values(value).every((v) => typeof v === 'object' && v !== null));
}
exports.isObjectRecord = isObjectRecord;

3
my-app/node_modules/@tufjs/models/dist/utils/index.d.ts generated vendored Executable file
View file

@ -0,0 +1,3 @@
export * as guard from './guard';
export { JSONObject, JSONValue } from './types';
export * as crypto from './verify';

28
my-app/node_modules/@tufjs/models/dist/utils/index.js generated vendored Executable file
View file

@ -0,0 +1,28 @@
"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
__setModuleDefault(result, mod);
return result;
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.crypto = exports.guard = void 0;
exports.guard = __importStar(require("./guard"));
exports.crypto = __importStar(require("./verify"));

9
my-app/node_modules/@tufjs/models/dist/utils/key.d.ts generated vendored Executable file
View file

@ -0,0 +1,9 @@
/// <reference types="node" />
import { VerifyKeyObjectInput } from 'crypto';
interface KeyInfo {
keyType: string;
scheme: string;
keyVal: string;
}
export declare function getPublicKey(keyInfo: KeyInfo): VerifyKeyObjectInput;
export {};

143
my-app/node_modules/@tufjs/models/dist/utils/key.js generated vendored Executable file
View file

@ -0,0 +1,143 @@
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.getPublicKey = void 0;
const crypto_1 = __importDefault(require("crypto"));
const error_1 = require("../error");
const oid_1 = require("./oid");
const ASN1_TAG_SEQUENCE = 0x30;
const ANS1_TAG_BIT_STRING = 0x03;
const NULL_BYTE = 0x00;
const OID_EDDSA = '1.3.101.112';
const OID_EC_PUBLIC_KEY = '1.2.840.10045.2.1';
const OID_EC_CURVE_P256V1 = '1.2.840.10045.3.1.7';
const PEM_HEADER = '-----BEGIN PUBLIC KEY-----';
function getPublicKey(keyInfo) {
switch (keyInfo.keyType) {
case 'rsa':
return getRSAPublicKey(keyInfo);
case 'ed25519':
return getED25519PublicKey(keyInfo);
case 'ecdsa':
case 'ecdsa-sha2-nistp256':
case 'ecdsa-sha2-nistp384':
return getECDCSAPublicKey(keyInfo);
default:
throw new error_1.UnsupportedAlgorithmError(`Unsupported key type: ${keyInfo.keyType}`);
}
}
exports.getPublicKey = getPublicKey;
function getRSAPublicKey(keyInfo) {
// Only support PEM-encoded RSA keys
if (!keyInfo.keyVal.startsWith(PEM_HEADER)) {
throw new error_1.CryptoError('Invalid key format');
}
const key = crypto_1.default.createPublicKey(keyInfo.keyVal);
switch (keyInfo.scheme) {
case 'rsassa-pss-sha256':
return {
key: key,
padding: crypto_1.default.constants.RSA_PKCS1_PSS_PADDING,
};
default:
throw new error_1.UnsupportedAlgorithmError(`Unsupported RSA scheme: ${keyInfo.scheme}`);
}
}
function getED25519PublicKey(keyInfo) {
let key;
// If key is already PEM-encoded we can just parse it
if (keyInfo.keyVal.startsWith(PEM_HEADER)) {
key = crypto_1.default.createPublicKey(keyInfo.keyVal);
}
else {
// If key is not PEM-encoded it had better be hex
if (!isHex(keyInfo.keyVal)) {
throw new error_1.CryptoError('Invalid key format');
}
key = crypto_1.default.createPublicKey({
key: ed25519.hexToDER(keyInfo.keyVal),
format: 'der',
type: 'spki',
});
}
return { key };
}
function getECDCSAPublicKey(keyInfo) {
let key;
// If key is already PEM-encoded we can just parse it
if (keyInfo.keyVal.startsWith(PEM_HEADER)) {
key = crypto_1.default.createPublicKey(keyInfo.keyVal);
}
else {
// If key is not PEM-encoded it had better be hex
if (!isHex(keyInfo.keyVal)) {
throw new error_1.CryptoError('Invalid key format');
}
key = crypto_1.default.createPublicKey({
key: ecdsa.hexToDER(keyInfo.keyVal),
format: 'der',
type: 'spki',
});
}
return { key };
}
const ed25519 = {
// Translates a hex key into a crypto KeyObject
// https://keygen.sh/blog/how-to-use-hexadecimal-ed25519-keys-in-node/
hexToDER: (hex) => {
const key = Buffer.from(hex, 'hex');
const oid = (0, oid_1.encodeOIDString)(OID_EDDSA);
// Create a byte sequence containing the OID and key
const elements = Buffer.concat([
Buffer.concat([
Buffer.from([ASN1_TAG_SEQUENCE]),
Buffer.from([oid.length]),
oid,
]),
Buffer.concat([
Buffer.from([ANS1_TAG_BIT_STRING]),
Buffer.from([key.length + 1]),
Buffer.from([NULL_BYTE]),
key,
]),
]);
// Wrap up by creating a sequence of elements
const der = Buffer.concat([
Buffer.from([ASN1_TAG_SEQUENCE]),
Buffer.from([elements.length]),
elements,
]);
return der;
},
};
const ecdsa = {
hexToDER: (hex) => {
const key = Buffer.from(hex, 'hex');
const bitString = Buffer.concat([
Buffer.from([ANS1_TAG_BIT_STRING]),
Buffer.from([key.length + 1]),
Buffer.from([NULL_BYTE]),
key,
]);
const oids = Buffer.concat([
(0, oid_1.encodeOIDString)(OID_EC_PUBLIC_KEY),
(0, oid_1.encodeOIDString)(OID_EC_CURVE_P256V1),
]);
const oidSequence = Buffer.concat([
Buffer.from([ASN1_TAG_SEQUENCE]),
Buffer.from([oids.length]),
oids,
]);
// Wrap up by creating a sequence of elements
const der = Buffer.concat([
Buffer.from([ASN1_TAG_SEQUENCE]),
Buffer.from([oidSequence.length + bitString.length]),
oidSequence,
bitString,
]);
return der;
},
};
const isHex = (key) => /^[0-9a-fA-F]+$/.test(key);

2
my-app/node_modules/@tufjs/models/dist/utils/oid.d.ts generated vendored Executable file
View file

@ -0,0 +1,2 @@
/// <reference types="node" />
export declare function encodeOIDString(oid: string): Buffer;

27
my-app/node_modules/@tufjs/models/dist/utils/oid.js generated vendored Executable file
View file

@ -0,0 +1,27 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.encodeOIDString = void 0;
const ANS1_TAG_OID = 0x06;
function encodeOIDString(oid) {
const parts = oid.split('.');
// The first two subidentifiers are encoded into the first byte
const first = parseInt(parts[0], 10) * 40 + parseInt(parts[1], 10);
const rest = [];
parts.slice(2).forEach((part) => {
const bytes = encodeVariableLengthInteger(parseInt(part, 10));
rest.push(...bytes);
});
const der = Buffer.from([first, ...rest]);
return Buffer.from([ANS1_TAG_OID, der.length, ...der]);
}
exports.encodeOIDString = encodeOIDString;
function encodeVariableLengthInteger(value) {
const bytes = [];
let mask = 0x00;
while (value > 0) {
bytes.unshift((value & 0x7f) | mask);
value >>= 7;
mask = 0x80;
}
return bytes;
}

4
my-app/node_modules/@tufjs/models/dist/utils/types.d.ts generated vendored Executable file
View file

@ -0,0 +1,4 @@
export type JSONObject = {
[key: string]: JSONValue;
};
export type JSONValue = null | boolean | number | string | JSONValue[] | JSONObject;

2
my-app/node_modules/@tufjs/models/dist/utils/types.js generated vendored Executable file
View file

@ -0,0 +1,2 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });

3
my-app/node_modules/@tufjs/models/dist/utils/verify.d.ts generated vendored Executable file
View file

@ -0,0 +1,3 @@
import crypto from 'crypto';
import { JSONObject } from '../utils/types';
export declare const verifySignature: (metaDataSignedData: JSONObject, key: crypto.VerifyKeyObjectInput, signature: string) => boolean;

13
my-app/node_modules/@tufjs/models/dist/utils/verify.js generated vendored Executable file
View file

@ -0,0 +1,13 @@
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.verifySignature = void 0;
const canonical_json_1 = require("@tufjs/canonical-json");
const crypto_1 = __importDefault(require("crypto"));
const verifySignature = (metaDataSignedData, key, signature) => {
const canonicalData = Buffer.from((0, canonical_json_1.canonicalize)(metaDataSignedData));
return crypto_1.default.verify(undefined, canonicalData, key, Buffer.from(signature, 'hex'));
};
exports.verifySignature = verifySignature;

View file

@ -0,0 +1,2 @@
tidelift: "npm/brace-expansion"
patreon: juliangruber

View file

@ -0,0 +1,21 @@
MIT License
Copyright (c) 2013 Julian Gruber <julian@juliangruber.com>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View file

@ -0,0 +1,135 @@
# brace-expansion
[Brace expansion](https://www.gnu.org/software/bash/manual/html_node/Brace-Expansion.html),
as known from sh/bash, in JavaScript.
[![build status](https://secure.travis-ci.org/juliangruber/brace-expansion.svg)](http://travis-ci.org/juliangruber/brace-expansion)
[![downloads](https://img.shields.io/npm/dm/brace-expansion.svg)](https://www.npmjs.org/package/brace-expansion)
[![Greenkeeper badge](https://badges.greenkeeper.io/juliangruber/brace-expansion.svg)](https://greenkeeper.io/)
[![testling badge](https://ci.testling.com/juliangruber/brace-expansion.png)](https://ci.testling.com/juliangruber/brace-expansion)
## Example
```js
var expand = require('brace-expansion');
expand('file-{a,b,c}.jpg')
// => ['file-a.jpg', 'file-b.jpg', 'file-c.jpg']
expand('-v{,,}')
// => ['-v', '-v', '-v']
expand('file{0..2}.jpg')
// => ['file0.jpg', 'file1.jpg', 'file2.jpg']
expand('file-{a..c}.jpg')
// => ['file-a.jpg', 'file-b.jpg', 'file-c.jpg']
expand('file{2..0}.jpg')
// => ['file2.jpg', 'file1.jpg', 'file0.jpg']
expand('file{0..4..2}.jpg')
// => ['file0.jpg', 'file2.jpg', 'file4.jpg']
expand('file-{a..e..2}.jpg')
// => ['file-a.jpg', 'file-c.jpg', 'file-e.jpg']
expand('file{00..10..5}.jpg')
// => ['file00.jpg', 'file05.jpg', 'file10.jpg']
expand('{{A..C},{a..c}}')
// => ['A', 'B', 'C', 'a', 'b', 'c']
expand('ppp{,config,oe{,conf}}')
// => ['ppp', 'pppconfig', 'pppoe', 'pppoeconf']
```
## API
```js
var expand = require('brace-expansion');
```
### var expanded = expand(str)
Return an array of all possible and valid expansions of `str`. If none are
found, `[str]` is returned.
Valid expansions are:
```js
/^(.*,)+(.+)?$/
// {a,b,...}
```
A comma separated list of options, like `{a,b}` or `{a,{b,c}}` or `{,a,}`.
```js
/^-?\d+\.\.-?\d+(\.\.-?\d+)?$/
// {x..y[..incr]}
```
A numeric sequence from `x` to `y` inclusive, with optional increment.
If `x` or `y` start with a leading `0`, all the numbers will be padded
to have equal length. Negative numbers and backwards iteration work too.
```js
/^-?\d+\.\.-?\d+(\.\.-?\d+)?$/
// {x..y[..incr]}
```
An alphabetic sequence from `x` to `y` inclusive, with optional increment.
`x` and `y` must be exactly one character, and if given, `incr` must be a
number.
For compatibility reasons, the string `${` is not eligible for brace expansion.
## Installation
With [npm](https://npmjs.org) do:
```bash
npm install brace-expansion
```
## Contributors
- [Julian Gruber](https://github.com/juliangruber)
- [Isaac Z. Schlueter](https://github.com/isaacs)
## Sponsors
This module is proudly supported by my [Sponsors](https://github.com/juliangruber/sponsors)!
Do you want to support modules like this to improve their quality, stability and weigh in on new features? Then please consider donating to my [Patreon](https://www.patreon.com/juliangruber). Not sure how much of my modules you're using? Try [feross/thanks](https://github.com/feross/thanks)!
## Security contact information
To report a security vulnerability, please use the
[Tidelift security contact](https://tidelift.com/security).
Tidelift will coordinate the fix and disclosure.
## License
(MIT)
Copyright (c) 2013 Julian Gruber &lt;julian@juliangruber.com&gt;
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
of the Software, and to permit persons to whom the Software is furnished to do
so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View file

@ -0,0 +1,203 @@
var balanced = require('balanced-match');
module.exports = expandTop;
var escSlash = '\0SLASH'+Math.random()+'\0';
var escOpen = '\0OPEN'+Math.random()+'\0';
var escClose = '\0CLOSE'+Math.random()+'\0';
var escComma = '\0COMMA'+Math.random()+'\0';
var escPeriod = '\0PERIOD'+Math.random()+'\0';
function numeric(str) {
return parseInt(str, 10) == str
? parseInt(str, 10)
: str.charCodeAt(0);
}
function escapeBraces(str) {
return str.split('\\\\').join(escSlash)
.split('\\{').join(escOpen)
.split('\\}').join(escClose)
.split('\\,').join(escComma)
.split('\\.').join(escPeriod);
}
function unescapeBraces(str) {
return str.split(escSlash).join('\\')
.split(escOpen).join('{')
.split(escClose).join('}')
.split(escComma).join(',')
.split(escPeriod).join('.');
}
// Basically just str.split(","), but handling cases
// where we have nested braced sections, which should be
// treated as individual members, like {a,{b,c},d}
function parseCommaParts(str) {
if (!str)
return [''];
var parts = [];
var m = balanced('{', '}', str);
if (!m)
return str.split(',');
var pre = m.pre;
var body = m.body;
var post = m.post;
var p = pre.split(',');
p[p.length-1] += '{' + body + '}';
var postParts = parseCommaParts(post);
if (post.length) {
p[p.length-1] += postParts.shift();
p.push.apply(p, postParts);
}
parts.push.apply(parts, p);
return parts;
}
function expandTop(str) {
if (!str)
return [];
// I don't know why Bash 4.3 does this, but it does.
// Anything starting with {} will have the first two bytes preserved
// but *only* at the top level, so {},a}b will not expand to anything,
// but a{},b}c will be expanded to [a}c,abc].
// One could argue that this is a bug in Bash, but since the goal of
// this module is to match Bash's rules, we escape a leading {}
if (str.substr(0, 2) === '{}') {
str = '\\{\\}' + str.substr(2);
}
return expand(escapeBraces(str), true).map(unescapeBraces);
}
function embrace(str) {
return '{' + str + '}';
}
function isPadded(el) {
return /^-?0\d/.test(el);
}
function lte(i, y) {
return i <= y;
}
function gte(i, y) {
return i >= y;
}
function expand(str, isTop) {
var expansions = [];
var m = balanced('{', '}', str);
if (!m) return [str];
// no need to expand pre, since it is guaranteed to be free of brace-sets
var pre = m.pre;
var post = m.post.length
? expand(m.post, false)
: [''];
if (/\$$/.test(m.pre)) {
for (var k = 0; k < post.length; k++) {
var expansion = pre+ '{' + m.body + '}' + post[k];
expansions.push(expansion);
}
} else {
var isNumericSequence = /^-?\d+\.\.-?\d+(?:\.\.-?\d+)?$/.test(m.body);
var isAlphaSequence = /^[a-zA-Z]\.\.[a-zA-Z](?:\.\.-?\d+)?$/.test(m.body);
var isSequence = isNumericSequence || isAlphaSequence;
var isOptions = m.body.indexOf(',') >= 0;
if (!isSequence && !isOptions) {
// {a},b}
if (m.post.match(/,.*\}/)) {
str = m.pre + '{' + m.body + escClose + m.post;
return expand(str);
}
return [str];
}
var n;
if (isSequence) {
n = m.body.split(/\.\./);
} else {
n = parseCommaParts(m.body);
if (n.length === 1) {
// x{{a,b}}y ==> x{a}y x{b}y
n = expand(n[0], false).map(embrace);
if (n.length === 1) {
return post.map(function(p) {
return m.pre + n[0] + p;
});
}
}
}
// at this point, n is the parts, and we know it's not a comma set
// with a single entry.
var N;
if (isSequence) {
var x = numeric(n[0]);
var y = numeric(n[1]);
var width = Math.max(n[0].length, n[1].length)
var incr = n.length == 3
? Math.abs(numeric(n[2]))
: 1;
var test = lte;
var reverse = y < x;
if (reverse) {
incr *= -1;
test = gte;
}
var pad = n.some(isPadded);
N = [];
for (var i = x; test(i, y); i += incr) {
var c;
if (isAlphaSequence) {
c = String.fromCharCode(i);
if (c === '\\')
c = '';
} else {
c = String(i);
if (pad) {
var need = width - c.length;
if (need > 0) {
var z = new Array(need + 1).join('0');
if (i < 0)
c = '-' + z + c.slice(1);
else
c = z + c;
}
}
}
N.push(c);
}
} else {
N = [];
for (var j = 0; j < n.length; j++) {
N.push.apply(N, expand(n[j], false));
}
}
for (var j = 0; j < N.length; j++) {
for (var k = 0; k < post.length; k++) {
var expansion = pre + N[j] + post[k];
if (!isTop || isSequence || expansion)
expansions.push(expansion);
}
}
}
return expansions;
}

View file

@ -0,0 +1,46 @@
{
"name": "brace-expansion",
"description": "Brace expansion as known from sh/bash",
"version": "2.0.1",
"repository": {
"type": "git",
"url": "git://github.com/juliangruber/brace-expansion.git"
},
"homepage": "https://github.com/juliangruber/brace-expansion",
"main": "index.js",
"scripts": {
"test": "tape test/*.js",
"gentest": "bash test/generate.sh",
"bench": "matcha test/perf/bench.js"
},
"dependencies": {
"balanced-match": "^1.0.0"
},
"devDependencies": {
"@c4312/matcha": "^1.3.1",
"tape": "^4.6.0"
},
"keywords": [],
"author": {
"name": "Julian Gruber",
"email": "mail@juliangruber.com",
"url": "http://juliangruber.com"
},
"license": "MIT",
"testling": {
"files": "test/*.js",
"browsers": [
"ie/8..latest",
"firefox/20..latest",
"firefox/nightly",
"chrome/25..latest",
"chrome/canary",
"opera/12..latest",
"opera/next",
"safari/5.1..latest",
"ipad/6.0..latest",
"iphone/6.0..latest",
"android-browser/4.2..latest"
]
}
}

View file

@ -0,0 +1,15 @@
The ISC License
Copyright (c) 2011-2023 Isaac Z. Schlueter and Contributors
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.

View file

@ -0,0 +1,454 @@
# minimatch
A minimal matching utility.
This is the matching library used internally by npm.
It works by converting glob expressions into JavaScript `RegExp`
objects.
## Usage
```js
// hybrid module, load with require() or import
import { minimatch } from 'minimatch'
// or:
const { minimatch } = require('minimatch')
minimatch('bar.foo', '*.foo') // true!
minimatch('bar.foo', '*.bar') // false!
minimatch('bar.foo', '*.+(bar|foo)', { debug: true }) // true, and noisy!
```
## Features
Supports these glob features:
- Brace Expansion
- Extended glob matching
- "Globstar" `**` matching
- [Posix character
classes](https://www.gnu.org/software/bash/manual/html_node/Pattern-Matching.html),
like `[[:alpha:]]`, supporting the full range of Unicode
characters. For example, `[[:alpha:]]` will match against
`'é'`, though `[a-zA-Z]` will not. Collating symbol and set
matching is not supported, so `[[=e=]]` will _not_ match `'é'`
and `[[.ch.]]` will not match `'ch'` in locales where `ch` is
considered a single character.
See:
- `man sh`
- `man bash` [Pattern
Matching](https://www.gnu.org/software/bash/manual/html_node/Pattern-Matching.html)
- `man 3 fnmatch`
- `man 5 gitignore`
## Windows
**Please only use forward-slashes in glob expressions.**
Though windows uses either `/` or `\` as its path separator, only `/`
characters are used by this glob implementation. You must use
forward-slashes **only** in glob expressions. Back-slashes in patterns
will always be interpreted as escape characters, not path separators.
Note that `\` or `/` _will_ be interpreted as path separators in paths on
Windows, and will match against `/` in glob expressions.
So just always use `/` in patterns.
### UNC Paths
On Windows, UNC paths like `//?/c:/...` or
`//ComputerName/Share/...` are handled specially.
- Patterns starting with a double-slash followed by some
non-slash characters will preserve their double-slash. As a
result, a pattern like `//*` will match `//x`, but not `/x`.
- Patterns staring with `//?/<drive letter>:` will _not_ treat
the `?` as a wildcard character. Instead, it will be treated
as a normal string.
- Patterns starting with `//?/<drive letter>:/...` will match
file paths starting with `<drive letter>:/...`, and vice versa,
as if the `//?/` was not present. This behavior only is
present when the drive letters are a case-insensitive match to
one another. The remaining portions of the path/pattern are
compared case sensitively, unless `nocase:true` is set.
Note that specifying a UNC path using `\` characters as path
separators is always allowed in the file path argument, but only
allowed in the pattern argument when `windowsPathsNoEscape: true`
is set in the options.
## Minimatch Class
Create a minimatch object by instantiating the `minimatch.Minimatch` class.
```javascript
var Minimatch = require('minimatch').Minimatch
var mm = new Minimatch(pattern, options)
```
### Properties
- `pattern` The original pattern the minimatch object represents.
- `options` The options supplied to the constructor.
- `set` A 2-dimensional array of regexp or string expressions.
Each row in the
array corresponds to a brace-expanded pattern. Each item in the row
corresponds to a single path-part. For example, the pattern
`{a,b/c}/d` would expand to a set of patterns like:
[ [ a, d ]
, [ b, c, d ] ]
If a portion of the pattern doesn't have any "magic" in it
(that is, it's something like `"foo"` rather than `fo*o?`), then it
will be left as a string rather than converted to a regular
expression.
- `regexp` Created by the `makeRe` method. A single regular expression
expressing the entire pattern. This is useful in cases where you wish
to use the pattern somewhat like `fnmatch(3)` with `FNM_PATH` enabled.
- `negate` True if the pattern is negated.
- `comment` True if the pattern is a comment.
- `empty` True if the pattern is `""`.
### Methods
- `makeRe()` Generate the `regexp` member if necessary, and return it.
Will return `false` if the pattern is invalid.
- `match(fname)` Return true if the filename matches the pattern, or
false otherwise.
- `matchOne(fileArray, patternArray, partial)` Take a `/`-split
filename, and match it against a single row in the `regExpSet`. This
method is mainly for internal use, but is exposed so that it can be
used by a glob-walker that needs to avoid excessive filesystem calls.
- `hasMagic()` Returns true if the parsed pattern contains any
magic characters. Returns false if all comparator parts are
string literals. If the `magicalBraces` option is set on the
constructor, then it will consider brace expansions which are
not otherwise magical to be magic. If not set, then a pattern
like `a{b,c}d` will return `false`, because neither `abd` nor
`acd` contain any special glob characters.
This does **not** mean that the pattern string can be used as a
literal filename, as it may contain magic glob characters that
are escaped. For example, the pattern `\\*` or `[*]` would not
be considered to have magic, as the matching portion parses to
the literal string `'*'` and would match a path named `'*'`,
not `'\\*'` or `'[*]'`. The `minimatch.unescape()` method may
be used to remove escape characters.
All other methods are internal, and will be called as necessary.
### minimatch(path, pattern, options)
Main export. Tests a path against the pattern using the options.
```javascript
var isJS = minimatch(file, '*.js', { matchBase: true })
```
### minimatch.filter(pattern, options)
Returns a function that tests its
supplied argument, suitable for use with `Array.filter`. Example:
```javascript
var javascripts = fileList.filter(minimatch.filter('*.js', { matchBase: true }))
```
### minimatch.escape(pattern, options = {})
Escape all magic characters in a glob pattern, so that it will
only ever match literal strings
If the `windowsPathsNoEscape` option is used, then characters are
escaped by wrapping in `[]`, because a magic character wrapped in
a character class can only be satisfied by that exact character.
Slashes (and backslashes in `windowsPathsNoEscape` mode) cannot
be escaped or unescaped.
### minimatch.unescape(pattern, options = {})
Un-escape a glob string that may contain some escaped characters.
If the `windowsPathsNoEscape` option is used, then square-brace
escapes are removed, but not backslash escapes. For example, it
will turn the string `'[*]'` into `*`, but it will not turn
`'\\*'` into `'*'`, because `\` is a path separator in
`windowsPathsNoEscape` mode.
When `windowsPathsNoEscape` is not set, then both brace escapes
and backslash escapes are removed.
Slashes (and backslashes in `windowsPathsNoEscape` mode) cannot
be escaped or unescaped.
### minimatch.match(list, pattern, options)
Match against the list of
files, in the style of fnmatch or glob. If nothing is matched, and
options.nonull is set, then return a list containing the pattern itself.
```javascript
var javascripts = minimatch.match(fileList, '*.js', { matchBase: true })
```
### minimatch.makeRe(pattern, options)
Make a regular expression object from the pattern.
## Options
All options are `false` by default.
### debug
Dump a ton of stuff to stderr.
### nobrace
Do not expand `{a,b}` and `{1..3}` brace sets.
### noglobstar
Disable `**` matching against multiple folder names.
### dot
Allow patterns to match filenames starting with a period, even if
the pattern does not explicitly have a period in that spot.
Note that by default, `a/**/b` will **not** match `a/.d/b`, unless `dot`
is set.
### noext
Disable "extglob" style patterns like `+(a|b)`.
### nocase
Perform a case-insensitive match.
### nocaseMagicOnly
When used with `{nocase: true}`, create regular expressions that
are case-insensitive, but leave string match portions untouched.
Has no effect when used without `{nocase: true}`
Useful when some other form of case-insensitive matching is used,
or if the original string representation is useful in some other
way.
### nonull
When a match is not found by `minimatch.match`, return a list containing
the pattern itself if this option is set. When not set, an empty list
is returned if there are no matches.
### magicalBraces
This only affects the results of the `Minimatch.hasMagic` method.
If the pattern contains brace expansions, such as `a{b,c}d`, but
no other magic characters, then the `Minimatch.hasMagic()` method
will return `false` by default. When this option set, it will
return `true` for brace expansion as well as other magic glob
characters.
### matchBase
If set, then patterns without slashes will be matched
against the basename of the path if it contains slashes. For example,
`a?b` would match the path `/xyz/123/acb`, but not `/xyz/acb/123`.
### nocomment
Suppress the behavior of treating `#` at the start of a pattern as a
comment.
### nonegate
Suppress the behavior of treating a leading `!` character as negation.
### flipNegate
Returns from negate expressions the same as if they were not negated.
(Ie, true on a hit, false on a miss.)
### partial
Compare a partial path to a pattern. As long as the parts of the path that
are present are not contradicted by the pattern, it will be treated as a
match. This is useful in applications where you're walking through a
folder structure, and don't yet have the full path, but want to ensure that
you do not walk down paths that can never be a match.
For example,
```js
minimatch('/a/b', '/a/*/c/d', { partial: true }) // true, might be /a/b/c/d
minimatch('/a/b', '/**/d', { partial: true }) // true, might be /a/b/.../d
minimatch('/x/y/z', '/a/**/z', { partial: true }) // false, because x !== a
```
### windowsPathsNoEscape
Use `\\` as a path separator _only_, and _never_ as an escape
character. If set, all `\\` characters are replaced with `/` in
the pattern. Note that this makes it **impossible** to match
against paths containing literal glob pattern characters, but
allows matching with patterns constructed using `path.join()` and
`path.resolve()` on Windows platforms, mimicking the (buggy!)
behavior of earlier versions on Windows. Please use with
caution, and be mindful of [the caveat about Windows
paths](#windows).
For legacy reasons, this is also set if
`options.allowWindowsEscape` is set to the exact value `false`.
### windowsNoMagicRoot
When a pattern starts with a UNC path or drive letter, and in
`nocase:true` mode, do not convert the root portions of the
pattern into a case-insensitive regular expression, and instead
leave them as strings.
This is the default when the platform is `win32` and
`nocase:true` is set.
### preserveMultipleSlashes
By default, multiple `/` characters (other than the leading `//`
in a UNC path, see "UNC Paths" above) are treated as a single
`/`.
That is, a pattern like `a///b` will match the file path `a/b`.
Set `preserveMultipleSlashes: true` to suppress this behavior.
### optimizationLevel
A number indicating the level of optimization that should be done
to the pattern prior to parsing and using it for matches.
Globstar parts `**` are always converted to `*` when `noglobstar`
is set, and multiple adjascent `**` parts are converted into a
single `**` (ie, `a/**/**/b` will be treated as `a/**/b`, as this
is equivalent in all cases).
- `0` - Make no further changes. In this mode, `.` and `..` are
maintained in the pattern, meaning that they must also appear
in the same position in the test path string. Eg, a pattern
like `a/*/../c` will match the string `a/b/../c` but not the
string `a/c`.
- `1` - (default) Remove cases where a double-dot `..` follows a
pattern portion that is not `**`, `.`, `..`, or empty `''`. For
example, the pattern `./a/b/../*` is converted to `./a/*`, and
so it will match the path string `./a/c`, but not the path
string `./a/b/../c`. Dots and empty path portions in the
pattern are preserved.
- `2` (or higher) - Much more aggressive optimizations, suitable
for use with file-walking cases:
- Remove cases where a double-dot `..` follows a pattern
portion that is not `**`, `.`, or empty `''`. Remove empty
and `.` portions of the pattern, where safe to do so (ie,
anywhere other than the last position, the first position, or
the second position in a pattern starting with `/`, as this
may indicate a UNC path on Windows).
- Convert patterns containing `<pre>/**/../<p>/<rest>` into the
equivalent `<pre>/{..,**}/<p>/<rest>`, where `<p>` is a
a pattern portion other than `.`, `..`, `**`, or empty
`''`.
- Dedupe patterns where a `**` portion is present in one and
omitted in another, and it is not the final path portion, and
they are otherwise equivalent. So `{a/**/b,a/b}` becomes
`a/**/b`, because `**` matches against an empty path portion.
- Dedupe patterns where a `*` portion is present in one, and a
non-dot pattern other than `**`, `.`, `..`, or `''` is in the
same position in the other. So `a/{*,x}/b` becomes `a/*/b`,
because `*` can match against `x`.
While these optimizations improve the performance of
file-walking use cases such as [glob](http://npm.im/glob) (ie,
the reason this module exists), there are cases where it will
fail to match a literal string that would have been matched in
optimization level 1 or 0.
Specifically, while the `Minimatch.match()` method will
optimize the file path string in the same ways, resulting in
the same matches, it will fail when tested with the regular
expression provided by `Minimatch.makeRe()`, unless the path
string is first processed with
`minimatch.levelTwoFileOptimize()` or similar.
### platform
When set to `win32`, this will trigger all windows-specific
behaviors (special handling for UNC paths, and treating `\` as
separators in file paths for comparison.)
Defaults to the value of `process.platform`.
## Comparisons to other fnmatch/glob implementations
While strict compliance with the existing standards is a
worthwhile goal, some discrepancies exist between minimatch and
other implementations. Some are intentional, and some are
unavoidable.
If the pattern starts with a `!` character, then it is negated. Set the
`nonegate` flag to suppress this behavior, and treat leading `!`
characters normally. This is perhaps relevant if you wish to start the
pattern with a negative extglob pattern like `!(a|B)`. Multiple `!`
characters at the start of a pattern will negate the pattern multiple
times.
If a pattern starts with `#`, then it is treated as a comment, and
will not match anything. Use `\#` to match a literal `#` at the
start of a line, or set the `nocomment` flag to suppress this behavior.
The double-star character `**` is supported by default, unless the
`noglobstar` flag is set. This is supported in the manner of bsdglob
and bash 4.1, where `**` only has special significance if it is the only
thing in a path part. That is, `a/**/b` will match `a/x/y/b`, but
`a/**b` will not.
If an escaped pattern has no matches, and the `nonull` flag is set,
then minimatch.match returns the pattern as-provided, rather than
interpreting the character escapes. For example,
`minimatch.match([], "\\*a\\?")` will return `"\\*a\\?"` rather than
`"*a?"`. This is akin to setting the `nullglob` option in bash, except
that it does not resolve escaped pattern characters.
If brace expansion is not disabled, then it is performed before any
other interpretation of the glob pattern. Thus, a pattern like
`+(a|{b),c)}`, which would not be valid in bash or zsh, is expanded
**first** into the set of `+(a|b)` and `+(a|c)`, and those patterns are
checked for validity. Since those two are valid, matching proceeds.
Negated extglob patterns are handled as closely as possible to
Bash semantics, but there are some cases with negative extglobs
which are exceedingly difficult to express in a JavaScript
regular expression. In particular the negated pattern
`<start>!(<pattern>*|)*` will in bash match anything that does
not start with `<start><pattern>`. However,
`<start>!(<pattern>*)*` _will_ match paths starting with
`<start><pattern>`, because the empty string can match against
the negated portion. In this library, `<start>!(<pattern>*|)*`
will _not_ match any pattern starting with `<start>`, due to a
difference in precisely which patterns are considered "greedy" in
Regular Expressions vs bash path expansion. This may be fixable,
but not without incurring some complexity and performance costs,
and the trade-off seems to not be worth pursuing.
Note that `fnmatch(3)` in libc is an extremely naive string comparison
matcher, which does not do anything special for slashes. This library is
designed to be used in glob searching and file walkers, and so it does do
special things with `/`. Thus, `foo*` will not match `foo/bar` in this
library, even though it would in `fnmatch(3)`.

View file

@ -0,0 +1,2 @@
export declare const assertValidPattern: (pattern: any) => void;
//# sourceMappingURL=assert-valid-pattern.d.ts.map

View file

@ -0,0 +1 @@
{"version":3,"file":"assert-valid-pattern.d.ts","sourceRoot":"","sources":["../../src/assert-valid-pattern.ts"],"names":[],"mappings":"AACA,eAAO,MAAM,kBAAkB,EAAE,CAAC,OAAO,EAAE,GAAG,KAAK,IAUlD,CAAA"}

View file

@ -0,0 +1,14 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.assertValidPattern = void 0;
const MAX_PATTERN_LENGTH = 1024 * 64;
const assertValidPattern = (pattern) => {
if (typeof pattern !== 'string') {
throw new TypeError('invalid pattern');
}
if (pattern.length > MAX_PATTERN_LENGTH) {
throw new TypeError('pattern is too long');
}
};
exports.assertValidPattern = assertValidPattern;
//# sourceMappingURL=assert-valid-pattern.js.map

View file

@ -0,0 +1 @@
{"version":3,"file":"assert-valid-pattern.js","sourceRoot":"","sources":["../../src/assert-valid-pattern.ts"],"names":[],"mappings":";;;AAAA,MAAM,kBAAkB,GAAG,IAAI,GAAG,EAAE,CAAA;AAC7B,MAAM,kBAAkB,GAA2B,CACxD,OAAY,EACe,EAAE;IAC7B,IAAI,OAAO,OAAO,KAAK,QAAQ,EAAE;QAC/B,MAAM,IAAI,SAAS,CAAC,iBAAiB,CAAC,CAAA;KACvC;IAED,IAAI,OAAO,CAAC,MAAM,GAAG,kBAAkB,EAAE;QACvC,MAAM,IAAI,SAAS,CAAC,qBAAqB,CAAC,CAAA;KAC3C;AACH,CAAC,CAAA;AAVY,QAAA,kBAAkB,sBAU9B","sourcesContent":["const MAX_PATTERN_LENGTH = 1024 * 64\nexport const assertValidPattern: (pattern: any) => void = (\n pattern: any\n): asserts pattern is string => {\n if (typeof pattern !== 'string') {\n throw new TypeError('invalid pattern')\n }\n\n if (pattern.length > MAX_PATTERN_LENGTH) {\n throw new TypeError('pattern is too long')\n }\n}\n"]}

View file

@ -0,0 +1,19 @@
import { MinimatchOptions, MMRegExp } from './index.js';
export type ExtglobType = '!' | '?' | '+' | '*' | '@';
export declare class AST {
#private;
type: ExtglobType | null;
constructor(type: ExtglobType | null, parent?: AST, options?: MinimatchOptions);
get hasMagic(): boolean | undefined;
toString(): string;
push(...parts: (string | AST)[]): void;
toJSON(): any[];
isStart(): boolean;
isEnd(): boolean;
copyIn(part: AST | string): void;
clone(parent: AST): AST;
static fromGlob(pattern: string, options?: MinimatchOptions): AST;
toMMPattern(): MMRegExp | string;
toRegExpSource(allowDot?: boolean): [re: string, body: string, hasMagic: boolean, uflag: boolean];
}
//# sourceMappingURL=ast.d.ts.map

View file

@ -0,0 +1 @@
{"version":3,"file":"ast.d.ts","sourceRoot":"","sources":["../../src/ast.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,gBAAgB,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAA;AAwCvD,MAAM,MAAM,WAAW,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,CAAA;AAkCrD,qBAAa,GAAG;;IACd,IAAI,EAAE,WAAW,GAAG,IAAI,CAAA;gBAiBtB,IAAI,EAAE,WAAW,GAAG,IAAI,EACxB,MAAM,CAAC,EAAE,GAAG,EACZ,OAAO,GAAE,gBAAqB;IAahC,IAAI,QAAQ,IAAI,OAAO,GAAG,SAAS,CAUlC;IAGD,QAAQ,IAAI,MAAM;IA+ClB,IAAI,CAAC,GAAG,KAAK,EAAE,CAAC,MAAM,GAAG,GAAG,CAAC,EAAE;IAY/B,MAAM;IAgBN,OAAO,IAAI,OAAO;IAgBlB,KAAK,IAAI,OAAO;IAYhB,MAAM,CAAC,IAAI,EAAE,GAAG,GAAG,MAAM;IAKzB,KAAK,CAAC,MAAM,EAAE,GAAG;IAsIjB,MAAM,CAAC,QAAQ,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,GAAE,gBAAqB;IAQ/D,WAAW,IAAI,QAAQ,GAAG,MAAM;IAgGhC,cAAc,CACZ,QAAQ,CAAC,EAAE,OAAO,GACjB,CAAC,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,OAAO,EAAE,KAAK,EAAE,OAAO,CAAC;CAiMjE"}

View file

@ -0,0 +1,589 @@
"use strict";
// parse a single path portion
Object.defineProperty(exports, "__esModule", { value: true });
exports.AST = void 0;
const brace_expressions_js_1 = require("./brace-expressions.js");
const unescape_js_1 = require("./unescape.js");
const types = new Set(['!', '?', '+', '*', '@']);
const isExtglobType = (c) => types.has(c);
// Patterns that get prepended to bind to the start of either the
// entire string, or just a single path portion, to prevent dots
// and/or traversal patterns, when needed.
// Exts don't need the ^ or / bit, because the root binds that already.
const startNoTraversal = '(?!(?:^|/)\\.\\.?(?:$|/))';
const startNoDot = '(?!\\.)';
// characters that indicate a start of pattern needs the "no dots" bit,
// because a dot *might* be matched. ( is not in the list, because in
// the case of a child extglob, it will handle the prevention itself.
const addPatternStart = new Set(['[', '.']);
// cases where traversal is A-OK, no dot prevention needed
const justDots = new Set(['..', '.']);
const reSpecials = new Set('().*{}+?[]^$\\!');
const regExpEscape = (s) => s.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, '\\$&');
// any single thing other than /
const qmark = '[^/]';
// * => any number of characters
const star = qmark + '*?';
// use + when we need to ensure that *something* matches, because the * is
// the only thing in the path portion.
const starNoEmpty = qmark + '+?';
// remove the \ chars that we added if we end up doing a nonmagic compare
// const deslash = (s: string) => s.replace(/\\(.)/g, '$1')
class AST {
type;
#root;
#hasMagic;
#uflag = false;
#parts = [];
#parent;
#parentIndex;
#negs;
#filledNegs = false;
#options;
#toString;
// set to true if it's an extglob with no children
// (which really means one child of '')
#emptyExt = false;
constructor(type, parent, options = {}) {
this.type = type;
// extglobs are inherently magical
if (type)
this.#hasMagic = true;
this.#parent = parent;
this.#root = this.#parent ? this.#parent.#root : this;
this.#options = this.#root === this ? options : this.#root.#options;
this.#negs = this.#root === this ? [] : this.#root.#negs;
if (type === '!' && !this.#root.#filledNegs)
this.#negs.push(this);
this.#parentIndex = this.#parent ? this.#parent.#parts.length : 0;
}
get hasMagic() {
/* c8 ignore start */
if (this.#hasMagic !== undefined)
return this.#hasMagic;
/* c8 ignore stop */
for (const p of this.#parts) {
if (typeof p === 'string')
continue;
if (p.type || p.hasMagic)
return (this.#hasMagic = true);
}
// note: will be undefined until we generate the regexp src and find out
return this.#hasMagic;
}
// reconstructs the pattern
toString() {
if (this.#toString !== undefined)
return this.#toString;
if (!this.type) {
return (this.#toString = this.#parts.map(p => String(p)).join(''));
}
else {
return (this.#toString =
this.type + '(' + this.#parts.map(p => String(p)).join('|') + ')');
}
}
#fillNegs() {
/* c8 ignore start */
if (this !== this.#root)
throw new Error('should only call on root');
if (this.#filledNegs)
return this;
/* c8 ignore stop */
// call toString() once to fill this out
this.toString();
this.#filledNegs = true;
let n;
while ((n = this.#negs.pop())) {
if (n.type !== '!')
continue;
// walk up the tree, appending everthing that comes AFTER parentIndex
let p = n;
let pp = p.#parent;
while (pp) {
for (let i = p.#parentIndex + 1; !pp.type && i < pp.#parts.length; i++) {
for (const part of n.#parts) {
/* c8 ignore start */
if (typeof part === 'string') {
throw new Error('string part in extglob AST??');
}
/* c8 ignore stop */
part.copyIn(pp.#parts[i]);
}
}
p = pp;
pp = p.#parent;
}
}
return this;
}
push(...parts) {
for (const p of parts) {
if (p === '')
continue;
/* c8 ignore start */
if (typeof p !== 'string' && !(p instanceof AST && p.#parent === this)) {
throw new Error('invalid part: ' + p);
}
/* c8 ignore stop */
this.#parts.push(p);
}
}
toJSON() {
const ret = this.type === null
? this.#parts.slice().map(p => (typeof p === 'string' ? p : p.toJSON()))
: [this.type, ...this.#parts.map(p => p.toJSON())];
if (this.isStart() && !this.type)
ret.unshift([]);
if (this.isEnd() &&
(this === this.#root ||
(this.#root.#filledNegs && this.#parent?.type === '!'))) {
ret.push({});
}
return ret;
}
isStart() {
if (this.#root === this)
return true;
// if (this.type) return !!this.#parent?.isStart()
if (!this.#parent?.isStart())
return false;
if (this.#parentIndex === 0)
return true;
// if everything AHEAD of this is a negation, then it's still the "start"
const p = this.#parent;
for (let i = 0; i < this.#parentIndex; i++) {
const pp = p.#parts[i];
if (!(pp instanceof AST && pp.type === '!')) {
return false;
}
}
return true;
}
isEnd() {
if (this.#root === this)
return true;
if (this.#parent?.type === '!')
return true;
if (!this.#parent?.isEnd())
return false;
if (!this.type)
return this.#parent?.isEnd();
// if not root, it'll always have a parent
/* c8 ignore start */
const pl = this.#parent ? this.#parent.#parts.length : 0;
/* c8 ignore stop */
return this.#parentIndex === pl - 1;
}
copyIn(part) {
if (typeof part === 'string')
this.push(part);
else
this.push(part.clone(this));
}
clone(parent) {
const c = new AST(this.type, parent);
for (const p of this.#parts) {
c.copyIn(p);
}
return c;
}
static #parseAST(str, ast, pos, opt) {
let escaping = false;
let inBrace = false;
let braceStart = -1;
let braceNeg = false;
if (ast.type === null) {
// outside of a extglob, append until we find a start
let i = pos;
let acc = '';
while (i < str.length) {
const c = str.charAt(i++);
// still accumulate escapes at this point, but we do ignore
// starts that are escaped
if (escaping || c === '\\') {
escaping = !escaping;
acc += c;
continue;
}
if (inBrace) {
if (i === braceStart + 1) {
if (c === '^' || c === '!') {
braceNeg = true;
}
}
else if (c === ']' && !(i === braceStart + 2 && braceNeg)) {
inBrace = false;
}
acc += c;
continue;
}
else if (c === '[') {
inBrace = true;
braceStart = i;
braceNeg = false;
acc += c;
continue;
}
if (!opt.noext && isExtglobType(c) && str.charAt(i) === '(') {
ast.push(acc);
acc = '';
const ext = new AST(c, ast);
i = AST.#parseAST(str, ext, i, opt);
ast.push(ext);
continue;
}
acc += c;
}
ast.push(acc);
return i;
}
// some kind of extglob, pos is at the (
// find the next | or )
let i = pos + 1;
let part = new AST(null, ast);
const parts = [];
let acc = '';
while (i < str.length) {
const c = str.charAt(i++);
// still accumulate escapes at this point, but we do ignore
// starts that are escaped
if (escaping || c === '\\') {
escaping = !escaping;
acc += c;
continue;
}
if (inBrace) {
if (i === braceStart + 1) {
if (c === '^' || c === '!') {
braceNeg = true;
}
}
else if (c === ']' && !(i === braceStart + 2 && braceNeg)) {
inBrace = false;
}
acc += c;
continue;
}
else if (c === '[') {
inBrace = true;
braceStart = i;
braceNeg = false;
acc += c;
continue;
}
if (isExtglobType(c) && str.charAt(i) === '(') {
part.push(acc);
acc = '';
const ext = new AST(c, part);
part.push(ext);
i = AST.#parseAST(str, ext, i, opt);
continue;
}
if (c === '|') {
part.push(acc);
acc = '';
parts.push(part);
part = new AST(null, ast);
continue;
}
if (c === ')') {
if (acc === '' && ast.#parts.length === 0) {
ast.#emptyExt = true;
}
part.push(acc);
acc = '';
ast.push(...parts, part);
return i;
}
acc += c;
}
// unfinished extglob
// if we got here, it was a malformed extglob! not an extglob, but
// maybe something else in there.
ast.type = null;
ast.#hasMagic = undefined;
ast.#parts = [str.substring(pos - 1)];
return i;
}
static fromGlob(pattern, options = {}) {
const ast = new AST(null, undefined, options);
AST.#parseAST(pattern, ast, 0, options);
return ast;
}
// returns the regular expression if there's magic, or the unescaped
// string if not.
toMMPattern() {
// should only be called on root
/* c8 ignore start */
if (this !== this.#root)
return this.#root.toMMPattern();
/* c8 ignore stop */
const glob = this.toString();
const [re, body, hasMagic, uflag] = this.toRegExpSource();
// if we're in nocase mode, and not nocaseMagicOnly, then we do
// still need a regular expression if we have to case-insensitively
// match capital/lowercase characters.
const anyMagic = hasMagic ||
this.#hasMagic ||
(this.#options.nocase &&
!this.#options.nocaseMagicOnly &&
glob.toUpperCase() !== glob.toLowerCase());
if (!anyMagic) {
return body;
}
const flags = (this.#options.nocase ? 'i' : '') + (uflag ? 'u' : '');
return Object.assign(new RegExp(`^${re}$`, flags), {
_src: re,
_glob: glob,
});
}
// returns the string match, the regexp source, whether there's magic
// in the regexp (so a regular expression is required) and whether or
// not the uflag is needed for the regular expression (for posix classes)
// TODO: instead of injecting the start/end at this point, just return
// the BODY of the regexp, along with the start/end portions suitable
// for binding the start/end in either a joined full-path makeRe context
// (where we bind to (^|/), or a standalone matchPart context (where
// we bind to ^, and not /). Otherwise slashes get duped!
//
// In part-matching mode, the start is:
// - if not isStart: nothing
// - if traversal possible, but not allowed: ^(?!\.\.?$)
// - if dots allowed or not possible: ^
// - if dots possible and not allowed: ^(?!\.)
// end is:
// - if not isEnd(): nothing
// - else: $
//
// In full-path matching mode, we put the slash at the START of the
// pattern, so start is:
// - if first pattern: same as part-matching mode
// - if not isStart(): nothing
// - if traversal possible, but not allowed: /(?!\.\.?(?:$|/))
// - if dots allowed or not possible: /
// - if dots possible and not allowed: /(?!\.)
// end is:
// - if last pattern, same as part-matching mode
// - else nothing
//
// Always put the (?:$|/) on negated tails, though, because that has to be
// there to bind the end of the negated pattern portion, and it's easier to
// just stick it in now rather than try to inject it later in the middle of
// the pattern.
//
// We can just always return the same end, and leave it up to the caller
// to know whether it's going to be used joined or in parts.
// And, if the start is adjusted slightly, can do the same there:
// - if not isStart: nothing
// - if traversal possible, but not allowed: (?:/|^)(?!\.\.?$)
// - if dots allowed or not possible: (?:/|^)
// - if dots possible and not allowed: (?:/|^)(?!\.)
//
// But it's better to have a simpler binding without a conditional, for
// performance, so probably better to return both start options.
//
// Then the caller just ignores the end if it's not the first pattern,
// and the start always gets applied.
//
// But that's always going to be $ if it's the ending pattern, or nothing,
// so the caller can just attach $ at the end of the pattern when building.
//
// So the todo is:
// - better detect what kind of start is needed
// - return both flavors of starting pattern
// - attach $ at the end of the pattern when creating the actual RegExp
//
// Ah, but wait, no, that all only applies to the root when the first pattern
// is not an extglob. If the first pattern IS an extglob, then we need all
// that dot prevention biz to live in the extglob portions, because eg
// +(*|.x*) can match .xy but not .yx.
//
// So, return the two flavors if it's #root and the first child is not an
// AST, otherwise leave it to the child AST to handle it, and there,
// use the (?:^|/) style of start binding.
//
// Even simplified further:
// - Since the start for a join is eg /(?!\.) and the start for a part
// is ^(?!\.), we can just prepend (?!\.) to the pattern (either root
// or start or whatever) and prepend ^ or / at the Regexp construction.
toRegExpSource(allowDot) {
const dot = allowDot ?? !!this.#options.dot;
if (this.#root === this)
this.#fillNegs();
if (!this.type) {
const noEmpty = this.isStart() && this.isEnd();
const src = this.#parts
.map(p => {
const [re, _, hasMagic, uflag] = typeof p === 'string'
? AST.#parseGlob(p, this.#hasMagic, noEmpty)
: p.toRegExpSource(allowDot);
this.#hasMagic = this.#hasMagic || hasMagic;
this.#uflag = this.#uflag || uflag;
return re;
})
.join('');
let start = '';
if (this.isStart()) {
if (typeof this.#parts[0] === 'string') {
// this is the string that will match the start of the pattern,
// so we need to protect against dots and such.
// '.' and '..' cannot match unless the pattern is that exactly,
// even if it starts with . or dot:true is set.
const dotTravAllowed = this.#parts.length === 1 && justDots.has(this.#parts[0]);
if (!dotTravAllowed) {
const aps = addPatternStart;
// check if we have a possibility of matching . or ..,
// and prevent that.
const needNoTrav =
// dots are allowed, and the pattern starts with [ or .
(dot && aps.has(src.charAt(0))) ||
// the pattern starts with \., and then [ or .
(src.startsWith('\\.') && aps.has(src.charAt(2))) ||
// the pattern starts with \.\., and then [ or .
(src.startsWith('\\.\\.') && aps.has(src.charAt(4)));
// no need to prevent dots if it can't match a dot, or if a
// sub-pattern will be preventing it anyway.
const needNoDot = !dot && !allowDot && aps.has(src.charAt(0));
start = needNoTrav ? startNoTraversal : needNoDot ? startNoDot : '';
}
}
}
// append the "end of path portion" pattern to negation tails
let end = '';
if (this.isEnd() &&
this.#root.#filledNegs &&
this.#parent?.type === '!') {
end = '(?:$|\\/)';
}
const final = start + src + end;
return [
final,
(0, unescape_js_1.unescape)(src),
(this.#hasMagic = !!this.#hasMagic),
this.#uflag,
];
}
// We need to calculate the body *twice* if it's a repeat pattern
// at the start, once in nodot mode, then again in dot mode, so a
// pattern like *(?) can match 'x.y'
const repeated = this.type === '*' || this.type === '+';
// some kind of extglob
const start = this.type === '!' ? '(?:(?!(?:' : '(?:';
let body = this.#partsToRegExp(dot);
if (this.isStart() && this.isEnd() && !body && this.type !== '!') {
// invalid extglob, has to at least be *something* present, if it's
// the entire path portion.
const s = this.toString();
this.#parts = [s];
this.type = null;
this.#hasMagic = undefined;
return [s, (0, unescape_js_1.unescape)(this.toString()), false, false];
}
// XXX abstract out this map method
let bodyDotAllowed = !repeated || allowDot || dot || !startNoDot
? ''
: this.#partsToRegExp(true);
if (bodyDotAllowed === body) {
bodyDotAllowed = '';
}
if (bodyDotAllowed) {
body = `(?:${body})(?:${bodyDotAllowed})*?`;
}
// an empty !() is exactly equivalent to a starNoEmpty
let final = '';
if (this.type === '!' && this.#emptyExt) {
final = (this.isStart() && !dot ? startNoDot : '') + starNoEmpty;
}
else {
const close = this.type === '!'
? // !() must match something,but !(x) can match ''
'))' +
(this.isStart() && !dot && !allowDot ? startNoDot : '') +
star +
')'
: this.type === '@'
? ')'
: this.type === '?'
? ')?'
: this.type === '+' && bodyDotAllowed
? ')'
: this.type === '*' && bodyDotAllowed
? `)?`
: `)${this.type}`;
final = start + body + close;
}
return [
final,
(0, unescape_js_1.unescape)(body),
(this.#hasMagic = !!this.#hasMagic),
this.#uflag,
];
}
#partsToRegExp(dot) {
return this.#parts
.map(p => {
// extglob ASTs should only contain parent ASTs
/* c8 ignore start */
if (typeof p === 'string') {
throw new Error('string type in extglob ast??');
}
/* c8 ignore stop */
// can ignore hasMagic, because extglobs are already always magic
const [re, _, _hasMagic, uflag] = p.toRegExpSource(dot);
this.#uflag = this.#uflag || uflag;
return re;
})
.filter(p => !(this.isStart() && this.isEnd()) || !!p)
.join('|');
}
static #parseGlob(glob, hasMagic, noEmpty = false) {
let escaping = false;
let re = '';
let uflag = false;
for (let i = 0; i < glob.length; i++) {
const c = glob.charAt(i);
if (escaping) {
escaping = false;
re += (reSpecials.has(c) ? '\\' : '') + c;
continue;
}
if (c === '\\') {
if (i === glob.length - 1) {
re += '\\\\';
}
else {
escaping = true;
}
continue;
}
if (c === '[') {
const [src, needUflag, consumed, magic] = (0, brace_expressions_js_1.parseClass)(glob, i);
if (consumed) {
re += src;
uflag = uflag || needUflag;
i += consumed - 1;
hasMagic = hasMagic || magic;
continue;
}
}
if (c === '*') {
if (noEmpty && glob === '*')
re += starNoEmpty;
else
re += star;
hasMagic = true;
continue;
}
if (c === '?') {
re += qmark;
hasMagic = true;
continue;
}
re += regExpEscape(c);
}
return [re, (0, unescape_js_1.unescape)(glob), !!hasMagic, uflag];
}
}
exports.AST = AST;
//# sourceMappingURL=ast.js.map

File diff suppressed because one or more lines are too long

View file

@ -0,0 +1,8 @@
export type ParseClassResult = [
src: string,
uFlag: boolean,
consumed: number,
hasMagic: boolean
];
export declare const parseClass: (glob: string, position: number) => ParseClassResult;
//# sourceMappingURL=brace-expressions.d.ts.map

View file

@ -0,0 +1 @@
{"version":3,"file":"brace-expressions.d.ts","sourceRoot":"","sources":["../../src/brace-expressions.ts"],"names":[],"mappings":"AA+BA,MAAM,MAAM,gBAAgB,GAAG;IAC7B,GAAG,EAAE,MAAM;IACX,KAAK,EAAE,OAAO;IACd,QAAQ,EAAE,MAAM;IAChB,QAAQ,EAAE,OAAO;CAClB,CAAA;AAQD,eAAO,MAAM,UAAU,SACf,MAAM,YACF,MAAM,qBA8HjB,CAAA"}

View file

@ -0,0 +1,152 @@
"use strict";
// translate the various posix character classes into unicode properties
// this works across all unicode locales
Object.defineProperty(exports, "__esModule", { value: true });
exports.parseClass = void 0;
// { <posix class>: [<translation>, /u flag required, negated]
const posixClasses = {
'[:alnum:]': ['\\p{L}\\p{Nl}\\p{Nd}', true],
'[:alpha:]': ['\\p{L}\\p{Nl}', true],
'[:ascii:]': ['\\x' + '00-\\x' + '7f', false],
'[:blank:]': ['\\p{Zs}\\t', true],
'[:cntrl:]': ['\\p{Cc}', true],
'[:digit:]': ['\\p{Nd}', true],
'[:graph:]': ['\\p{Z}\\p{C}', true, true],
'[:lower:]': ['\\p{Ll}', true],
'[:print:]': ['\\p{C}', true],
'[:punct:]': ['\\p{P}', true],
'[:space:]': ['\\p{Z}\\t\\r\\n\\v\\f', true],
'[:upper:]': ['\\p{Lu}', true],
'[:word:]': ['\\p{L}\\p{Nl}\\p{Nd}\\p{Pc}', true],
'[:xdigit:]': ['A-Fa-f0-9', false],
};
// only need to escape a few things inside of brace expressions
// escapes: [ \ ] -
const braceEscape = (s) => s.replace(/[[\]\\-]/g, '\\$&');
// escape all regexp magic characters
const regexpEscape = (s) => s.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, '\\$&');
// everything has already been escaped, we just have to join
const rangesToString = (ranges) => ranges.join('');
// takes a glob string at a posix brace expression, and returns
// an equivalent regular expression source, and boolean indicating
// whether the /u flag needs to be applied, and the number of chars
// consumed to parse the character class.
// This also removes out of order ranges, and returns ($.) if the
// entire class just no good.
const parseClass = (glob, position) => {
const pos = position;
/* c8 ignore start */
if (glob.charAt(pos) !== '[') {
throw new Error('not in a brace expression');
}
/* c8 ignore stop */
const ranges = [];
const negs = [];
let i = pos + 1;
let sawStart = false;
let uflag = false;
let escaping = false;
let negate = false;
let endPos = pos;
let rangeStart = '';
WHILE: while (i < glob.length) {
const c = glob.charAt(i);
if ((c === '!' || c === '^') && i === pos + 1) {
negate = true;
i++;
continue;
}
if (c === ']' && sawStart && !escaping) {
endPos = i + 1;
break;
}
sawStart = true;
if (c === '\\') {
if (!escaping) {
escaping = true;
i++;
continue;
}
// escaped \ char, fall through and treat like normal char
}
if (c === '[' && !escaping) {
// either a posix class, a collation equivalent, or just a [
for (const [cls, [unip, u, neg]] of Object.entries(posixClasses)) {
if (glob.startsWith(cls, i)) {
// invalid, [a-[] is fine, but not [a-[:alpha]]
if (rangeStart) {
return ['$.', false, glob.length - pos, true];
}
i += cls.length;
if (neg)
negs.push(unip);
else
ranges.push(unip);
uflag = uflag || u;
continue WHILE;
}
}
}
// now it's just a normal character, effectively
escaping = false;
if (rangeStart) {
// throw this range away if it's not valid, but others
// can still match.
if (c > rangeStart) {
ranges.push(braceEscape(rangeStart) + '-' + braceEscape(c));
}
else if (c === rangeStart) {
ranges.push(braceEscape(c));
}
rangeStart = '';
i++;
continue;
}
// now might be the start of a range.
// can be either c-d or c-] or c<more...>] or c] at this point
if (glob.startsWith('-]', i + 1)) {
ranges.push(braceEscape(c + '-'));
i += 2;
continue;
}
if (glob.startsWith('-', i + 1)) {
rangeStart = c;
i += 2;
continue;
}
// not the start of a range, just a single character
ranges.push(braceEscape(c));
i++;
}
if (endPos < i) {
// didn't see the end of the class, not a valid class,
// but might still be valid as a literal match.
return ['', false, 0, false];
}
// if we got no ranges and no negates, then we have a range that
// cannot possibly match anything, and that poisons the whole glob
if (!ranges.length && !negs.length) {
return ['$.', false, glob.length - pos, true];
}
// if we got one positive range, and it's a single character, then that's
// not actually a magic pattern, it's just that one literal character.
// we should not treat that as "magic", we should just return the literal
// character. [_] is a perfectly valid way to escape glob magic chars.
if (negs.length === 0 &&
ranges.length === 1 &&
/^\\?.$/.test(ranges[0]) &&
!negate) {
const r = ranges[0].length === 2 ? ranges[0].slice(-1) : ranges[0];
return [regexpEscape(r), false, endPos - pos, false];
}
const sranges = '[' + (negate ? '^' : '') + rangesToString(ranges) + ']';
const snegs = '[' + (negate ? '' : '^') + rangesToString(negs) + ']';
const comb = ranges.length && negs.length
? '(' + sranges + '|' + snegs + ')'
: ranges.length
? sranges
: snegs;
return [comb, uflag, endPos - pos, true];
};
exports.parseClass = parseClass;
//# sourceMappingURL=brace-expressions.js.map

File diff suppressed because one or more lines are too long

View file

@ -0,0 +1,12 @@
import { MinimatchOptions } from './index.js';
/**
* Escape all magic characters in a glob pattern.
*
* If the {@link windowsPathsNoEscape | GlobOptions.windowsPathsNoEscape}
* option is used, then characters are escaped by wrapping in `[]`, because
* a magic character wrapped in a character class can only be satisfied by
* that exact character. In this mode, `\` is _not_ escaped, because it is
* not interpreted as a magic character, but instead as a path separator.
*/
export declare const escape: (s: string, { windowsPathsNoEscape, }?: Pick<MinimatchOptions, 'windowsPathsNoEscape'>) => string;
//# sourceMappingURL=escape.d.ts.map

View file

@ -0,0 +1 @@
{"version":3,"file":"escape.d.ts","sourceRoot":"","sources":["../../src/escape.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAA;AAC7C;;;;;;;;GAQG;AACH,eAAO,MAAM,MAAM,MACd,MAAM,8BAGN,KAAK,gBAAgB,EAAE,sBAAsB,CAAC,WAQlD,CAAA"}

View file

@ -0,0 +1,22 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.escape = void 0;
/**
* Escape all magic characters in a glob pattern.
*
* If the {@link windowsPathsNoEscape | GlobOptions.windowsPathsNoEscape}
* option is used, then characters are escaped by wrapping in `[]`, because
* a magic character wrapped in a character class can only be satisfied by
* that exact character. In this mode, `\` is _not_ escaped, because it is
* not interpreted as a magic character, but instead as a path separator.
*/
const escape = (s, { windowsPathsNoEscape = false, } = {}) => {
// don't need to escape +@! because we escape the parens
// that make those magic, and escaping ! as [!] isn't valid,
// because [!]] is a valid glob class meaning not ']'.
return windowsPathsNoEscape
? s.replace(/[?*()[\]]/g, '[$&]')
: s.replace(/[?*()[\]\\]/g, '\\$&');
};
exports.escape = escape;
//# sourceMappingURL=escape.js.map

View file

@ -0,0 +1 @@
{"version":3,"file":"escape.js","sourceRoot":"","sources":["../../src/escape.ts"],"names":[],"mappings":";;;AACA;;;;;;;;GAQG;AACI,MAAM,MAAM,GAAG,CACpB,CAAS,EACT,EACE,oBAAoB,GAAG,KAAK,MACsB,EAAE,EACtD,EAAE;IACF,wDAAwD;IACxD,4DAA4D;IAC5D,sDAAsD;IACtD,OAAO,oBAAoB;QACzB,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,YAAY,EAAE,MAAM,CAAC;QACjC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,cAAc,EAAE,MAAM,CAAC,CAAA;AACvC,CAAC,CAAA;AAZY,QAAA,MAAM,UAYlB","sourcesContent":["import { MinimatchOptions } from './index.js'\n/**\n * Escape all magic characters in a glob pattern.\n *\n * If the {@link windowsPathsNoEscape | GlobOptions.windowsPathsNoEscape}\n * option is used, then characters are escaped by wrapping in `[]`, because\n * a magic character wrapped in a character class can only be satisfied by\n * that exact character. In this mode, `\\` is _not_ escaped, because it is\n * not interpreted as a magic character, but instead as a path separator.\n */\nexport const escape = (\n s: string,\n {\n windowsPathsNoEscape = false,\n }: Pick<MinimatchOptions, 'windowsPathsNoEscape'> = {}\n) => {\n // don't need to escape +@! because we escape the parens\n // that make those magic, and escaping ! as [!] isn't valid,\n // because [!]] is a valid glob class meaning not ']'.\n return windowsPathsNoEscape\n ? s.replace(/[?*()[\\]]/g, '[$&]')\n : s.replace(/[?*()[\\]\\\\]/g, '\\\\$&')\n}\n"]}

View file

@ -0,0 +1,94 @@
import { AST } from './ast.js';
type Platform = 'aix' | 'android' | 'darwin' | 'freebsd' | 'haiku' | 'linux' | 'openbsd' | 'sunos' | 'win32' | 'cygwin' | 'netbsd';
export interface MinimatchOptions {
nobrace?: boolean;
nocomment?: boolean;
nonegate?: boolean;
debug?: boolean;
noglobstar?: boolean;
noext?: boolean;
nonull?: boolean;
windowsPathsNoEscape?: boolean;
allowWindowsEscape?: boolean;
partial?: boolean;
dot?: boolean;
nocase?: boolean;
nocaseMagicOnly?: boolean;
magicalBraces?: boolean;
matchBase?: boolean;
flipNegate?: boolean;
preserveMultipleSlashes?: boolean;
optimizationLevel?: number;
platform?: Platform;
windowsNoMagicRoot?: boolean;
}
export declare const minimatch: {
(p: string, pattern: string, options?: MinimatchOptions): boolean;
sep: Sep;
GLOBSTAR: typeof GLOBSTAR;
filter: (pattern: string, options?: MinimatchOptions) => (p: string) => boolean;
defaults: (def: MinimatchOptions) => typeof minimatch;
braceExpand: (pattern: string, options?: MinimatchOptions) => string[];
makeRe: (pattern: string, options?: MinimatchOptions) => false | MMRegExp;
match: (list: string[], pattern: string, options?: MinimatchOptions) => string[];
AST: typeof AST;
Minimatch: typeof Minimatch;
escape: (s: string, { windowsPathsNoEscape, }?: Pick<MinimatchOptions, "windowsPathsNoEscape">) => string;
unescape: (s: string, { windowsPathsNoEscape, }?: Pick<MinimatchOptions, "windowsPathsNoEscape">) => string;
};
type Sep = '\\' | '/';
export declare const sep: Sep;
export declare const GLOBSTAR: unique symbol;
export declare const filter: (pattern: string, options?: MinimatchOptions) => (p: string) => boolean;
export declare const defaults: (def: MinimatchOptions) => typeof minimatch;
export declare const braceExpand: (pattern: string, options?: MinimatchOptions) => string[];
export declare const makeRe: (pattern: string, options?: MinimatchOptions) => false | MMRegExp;
export declare const match: (list: string[], pattern: string, options?: MinimatchOptions) => string[];
export type MMRegExp = RegExp & {
_src?: string;
_glob?: string;
};
export type ParseReturnFiltered = string | MMRegExp | typeof GLOBSTAR;
export type ParseReturn = ParseReturnFiltered | false;
export declare class Minimatch {
options: MinimatchOptions;
set: ParseReturnFiltered[][];
pattern: string;
windowsPathsNoEscape: boolean;
nonegate: boolean;
negate: boolean;
comment: boolean;
empty: boolean;
preserveMultipleSlashes: boolean;
partial: boolean;
globSet: string[];
globParts: string[][];
nocase: boolean;
isWindows: boolean;
platform: Platform;
windowsNoMagicRoot: boolean;
regexp: false | null | MMRegExp;
constructor(pattern: string, options?: MinimatchOptions);
hasMagic(): boolean;
debug(..._: any[]): void;
make(): void;
preprocess(globParts: string[][]): string[][];
adjascentGlobstarOptimize(globParts: string[][]): string[][];
levelOneOptimize(globParts: string[][]): string[][];
levelTwoFileOptimize(parts: string | string[]): string[];
firstPhasePreProcess(globParts: string[][]): string[][];
secondPhasePreProcess(globParts: string[][]): string[][];
partsMatch(a: string[], b: string[], emptyGSMatch?: boolean): false | string[];
parseNegate(): void;
matchOne(file: string[], pattern: ParseReturn[], partial?: boolean): boolean;
braceExpand(): string[];
parse(pattern: string): ParseReturn;
makeRe(): false | MMRegExp;
slashSplit(p: string): string[];
match(f: string, partial?: boolean): boolean;
static defaults(def: MinimatchOptions): typeof Minimatch;
}
export { AST } from './ast.js';
export { escape } from './escape.js';
export { unescape } from './unescape.js';
//# sourceMappingURL=index.d.ts.map

View file

@ -0,0 +1 @@
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,GAAG,EAAe,MAAM,UAAU,CAAA;AAI3C,KAAK,QAAQ,GACT,KAAK,GACL,SAAS,GACT,QAAQ,GACR,SAAS,GACT,OAAO,GACP,OAAO,GACP,SAAS,GACT,OAAO,GACP,OAAO,GACP,QAAQ,GACR,QAAQ,CAAA;AAEZ,MAAM,WAAW,gBAAgB;IAC/B,OAAO,CAAC,EAAE,OAAO,CAAA;IACjB,SAAS,CAAC,EAAE,OAAO,CAAA;IACnB,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB,KAAK,CAAC,EAAE,OAAO,CAAA;IACf,UAAU,CAAC,EAAE,OAAO,CAAA;IACpB,KAAK,CAAC,EAAE,OAAO,CAAA;IACf,MAAM,CAAC,EAAE,OAAO,CAAA;IAChB,oBAAoB,CAAC,EAAE,OAAO,CAAA;IAC9B,kBAAkB,CAAC,EAAE,OAAO,CAAA;IAC5B,OAAO,CAAC,EAAE,OAAO,CAAA;IACjB,GAAG,CAAC,EAAE,OAAO,CAAA;IACb,MAAM,CAAC,EAAE,OAAO,CAAA;IAChB,eAAe,CAAC,EAAE,OAAO,CAAA;IACzB,aAAa,CAAC,EAAE,OAAO,CAAA;IACvB,SAAS,CAAC,EAAE,OAAO,CAAA;IACnB,UAAU,CAAC,EAAE,OAAO,CAAA;IACpB,uBAAuB,CAAC,EAAE,OAAO,CAAA;IACjC,iBAAiB,CAAC,EAAE,MAAM,CAAA;IAC1B,QAAQ,CAAC,EAAE,QAAQ,CAAA;IACnB,kBAAkB,CAAC,EAAE,OAAO,CAAA;CAC7B;AAED,eAAO,MAAM,SAAS;QACjB,MAAM,WACA,MAAM,YACN,gBAAgB;;;sBAuGf,MAAM,YAAW,gBAAgB,SACvC,MAAM;oBAOkB,gBAAgB,KAAG,gBAAgB;2BA6EtD,MAAM,YACN,gBAAgB;sBA2BK,MAAM,YAAW,gBAAgB;kBAKzD,MAAM,EAAE,WACL,MAAM,YACN,gBAAgB;;;;;CArN1B,CAAA;AA+DD,KAAK,GAAG,GAAG,IAAI,GAAG,GAAG,CAAA;AAOrB,eAAO,MAAM,GAAG,KAAgE,CAAA;AAGhF,eAAO,MAAM,QAAQ,eAAwB,CAAA;AAmB7C,eAAO,MAAM,MAAM,YACP,MAAM,YAAW,gBAAgB,SACvC,MAAM,YACsB,CAAA;AAMlC,eAAO,MAAM,QAAQ,QAAS,gBAAgB,KAAG,gBA+DhD,CAAA;AAaD,eAAO,MAAM,WAAW,YACb,MAAM,YACN,gBAAgB,aAY1B,CAAA;AAeD,eAAO,MAAM,MAAM,YAAa,MAAM,YAAW,gBAAgB,qBACvB,CAAA;AAG1C,eAAO,MAAM,KAAK,SACV,MAAM,EAAE,WACL,MAAM,YACN,gBAAgB,aAQ1B,CAAA;AAQD,MAAM,MAAM,QAAQ,GAAG,MAAM,GAAG;IAC9B,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,KAAK,CAAC,EAAE,MAAM,CAAA;CACf,CAAA;AAED,MAAM,MAAM,mBAAmB,GAAG,MAAM,GAAG,QAAQ,GAAG,OAAO,QAAQ,CAAA;AACrE,MAAM,MAAM,WAAW,GAAG,mBAAmB,GAAG,KAAK,CAAA;AAErD,qBAAa,SAAS;IACpB,OAAO,EAAE,gBAAgB,CAAA;IACzB,GAAG,EAAE,mBAAmB,EAAE,EAAE,CAAA;IAC5B,OAAO,EAAE,MAAM,CAAA;IAEf,oBAAoB,EAAE,OAAO,CAAA;IAC7B,QAAQ,EAAE,OAAO,CAAA;IACjB,MAAM,EAAE,OAAO,CAAA;IACf,OAAO,EAAE,OAAO,CAAA;IAChB,KAAK,EAAE,OAAO,CAAA;IACd,uBAAuB,EAAE,OAAO,CAAA;IAChC,OAAO,EAAE,OAAO,CAAA;IAChB,OAAO,EAAE,MAAM,EAAE,CAAA;IACjB,SAAS,EAAE,MAAM,EAAE,EAAE,CAAA;IACrB,MAAM,EAAE,OAAO,CAAA;IAEf,SAAS,EAAE,OAAO,CAAA;IAClB,QAAQ,EAAE,QAAQ,CAAA;IAClB,kBAAkB,EAAE,OAAO,CAAA;IAE3B,MAAM,EAAE,KAAK,GAAG,IAAI,GAAG,QAAQ,CAAA;gBACnB,OAAO,EAAE,MAAM,EAAE,OAAO,GAAE,gBAAqB;IAkC3D,QAAQ,IAAI,OAAO;IAYnB,KAAK,CAAC,GAAG,CAAC,EAAE,GAAG,EAAE;IAEjB,IAAI;IA0FJ,UAAU,CAAC,SAAS,EAAE,MAAM,EAAE,EAAE;IA6BhC,yBAAyB,CAAC,SAAS,EAAE,MAAM,EAAE,EAAE;IAiB/C,gBAAgB,CAAC,SAAS,EAAE,MAAM,EAAE,EAAE;IAoBtC,oBAAoB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,EAAE;IA6D7C,oBAAoB,CAAC,SAAS,EAAE,MAAM,EAAE,EAAE;IA0F1C,qBAAqB,CAAC,SAAS,EAAE,MAAM,EAAE,EAAE,GAAG,MAAM,EAAE,EAAE;IAgBxD,UAAU,CACR,CAAC,EAAE,MAAM,EAAE,EACX,CAAC,EAAE,MAAM,EAAE,EACX,YAAY,GAAE,OAAe,GAC5B,KAAK,GAAG,MAAM,EAAE;IA+CnB,WAAW;IAqBX,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,OAAO,EAAE,WAAW,EAAE,EAAE,OAAO,GAAE,OAAe;IAiNzE,WAAW;IAIX,KAAK,CAAC,OAAO,EAAE,MAAM,GAAG,WAAW;IA6CnC,MAAM;IAsFN,UAAU,CAAC,CAAC,EAAE,MAAM;IAepB,KAAK,CAAC,CAAC,EAAE,MAAM,EAAE,OAAO,UAAe;IAiEvC,MAAM,CAAC,QAAQ,CAAC,GAAG,EAAE,gBAAgB;CAGtC;AAED,OAAO,EAAE,GAAG,EAAE,MAAM,UAAU,CAAA;AAC9B,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAA;AACpC,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAA"}

File diff suppressed because it is too large Load diff

File diff suppressed because one or more lines are too long

View file

@ -0,0 +1,3 @@
{
"type": "commonjs"
}

View file

@ -0,0 +1,17 @@
import { MinimatchOptions } from './index.js';
/**
* Un-escape a string that has been escaped with {@link escape}.
*
* If the {@link windowsPathsNoEscape} option is used, then square-brace
* escapes are removed, but not backslash escapes. For example, it will turn
* the string `'[*]'` into `*`, but it will not turn `'\\*'` into `'*'`,
* becuase `\` is a path separator in `windowsPathsNoEscape` mode.
*
* When `windowsPathsNoEscape` is not set, then both brace escapes and
* backslash escapes are removed.
*
* Slashes (and backslashes in `windowsPathsNoEscape` mode) cannot be escaped
* or unescaped.
*/
export declare const unescape: (s: string, { windowsPathsNoEscape, }?: Pick<MinimatchOptions, 'windowsPathsNoEscape'>) => string;
//# sourceMappingURL=unescape.d.ts.map

View file

@ -0,0 +1 @@
{"version":3,"file":"unescape.d.ts","sourceRoot":"","sources":["../../src/unescape.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAA;AAC7C;;;;;;;;;;;;;GAaG;AACH,eAAO,MAAM,QAAQ,MAChB,MAAM,8BAGN,KAAK,gBAAgB,EAAE,sBAAsB,CAAC,WAKlD,CAAA"}

View file

@ -0,0 +1,24 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.unescape = void 0;
/**
* Un-escape a string that has been escaped with {@link escape}.
*
* If the {@link windowsPathsNoEscape} option is used, then square-brace
* escapes are removed, but not backslash escapes. For example, it will turn
* the string `'[*]'` into `*`, but it will not turn `'\\*'` into `'*'`,
* becuase `\` is a path separator in `windowsPathsNoEscape` mode.
*
* When `windowsPathsNoEscape` is not set, then both brace escapes and
* backslash escapes are removed.
*
* Slashes (and backslashes in `windowsPathsNoEscape` mode) cannot be escaped
* or unescaped.
*/
const unescape = (s, { windowsPathsNoEscape = false, } = {}) => {
return windowsPathsNoEscape
? s.replace(/\[([^\/\\])\]/g, '$1')
: s.replace(/((?!\\).|^)\[([^\/\\])\]/g, '$1$2').replace(/\\([^\/])/g, '$1');
};
exports.unescape = unescape;
//# sourceMappingURL=unescape.js.map

View file

@ -0,0 +1 @@
{"version":3,"file":"unescape.js","sourceRoot":"","sources":["../../src/unescape.ts"],"names":[],"mappings":";;;AACA;;;;;;;;;;;;;GAaG;AACI,MAAM,QAAQ,GAAG,CACtB,CAAS,EACT,EACE,oBAAoB,GAAG,KAAK,MACsB,EAAE,EACtD,EAAE;IACF,OAAO,oBAAoB;QACzB,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,gBAAgB,EAAE,IAAI,CAAC;QACnC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,2BAA2B,EAAE,MAAM,CAAC,CAAC,OAAO,CAAC,YAAY,EAAE,IAAI,CAAC,CAAA;AAChF,CAAC,CAAA;AATY,QAAA,QAAQ,YASpB","sourcesContent":["import { MinimatchOptions } from './index.js'\n/**\n * Un-escape a string that has been escaped with {@link escape}.\n *\n * If the {@link windowsPathsNoEscape} option is used, then square-brace\n * escapes are removed, but not backslash escapes. For example, it will turn\n * the string `'[*]'` into `*`, but it will not turn `'\\\\*'` into `'*'`,\n * becuase `\\` is a path separator in `windowsPathsNoEscape` mode.\n *\n * When `windowsPathsNoEscape` is not set, then both brace escapes and\n * backslash escapes are removed.\n *\n * Slashes (and backslashes in `windowsPathsNoEscape` mode) cannot be escaped\n * or unescaped.\n */\nexport const unescape = (\n s: string,\n {\n windowsPathsNoEscape = false,\n }: Pick<MinimatchOptions, 'windowsPathsNoEscape'> = {}\n) => {\n return windowsPathsNoEscape\n ? s.replace(/\\[([^\\/\\\\])\\]/g, '$1')\n : s.replace(/((?!\\\\).|^)\\[([^\\/\\\\])\\]/g, '$1$2').replace(/\\\\([^\\/])/g, '$1')\n}\n"]}

View file

@ -0,0 +1,2 @@
export declare const assertValidPattern: (pattern: any) => void;
//# sourceMappingURL=assert-valid-pattern.d.ts.map

View file

@ -0,0 +1 @@
{"version":3,"file":"assert-valid-pattern.d.ts","sourceRoot":"","sources":["../../src/assert-valid-pattern.ts"],"names":[],"mappings":"AACA,eAAO,MAAM,kBAAkB,EAAE,CAAC,OAAO,EAAE,GAAG,KAAK,IAUlD,CAAA"}

View file

@ -0,0 +1,10 @@
const MAX_PATTERN_LENGTH = 1024 * 64;
export const assertValidPattern = (pattern) => {
if (typeof pattern !== 'string') {
throw new TypeError('invalid pattern');
}
if (pattern.length > MAX_PATTERN_LENGTH) {
throw new TypeError('pattern is too long');
}
};
//# sourceMappingURL=assert-valid-pattern.js.map

View file

@ -0,0 +1 @@
{"version":3,"file":"assert-valid-pattern.js","sourceRoot":"","sources":["../../src/assert-valid-pattern.ts"],"names":[],"mappings":"AAAA,MAAM,kBAAkB,GAAG,IAAI,GAAG,EAAE,CAAA;AACpC,MAAM,CAAC,MAAM,kBAAkB,GAA2B,CACxD,OAAY,EACe,EAAE;IAC7B,IAAI,OAAO,OAAO,KAAK,QAAQ,EAAE;QAC/B,MAAM,IAAI,SAAS,CAAC,iBAAiB,CAAC,CAAA;KACvC;IAED,IAAI,OAAO,CAAC,MAAM,GAAG,kBAAkB,EAAE;QACvC,MAAM,IAAI,SAAS,CAAC,qBAAqB,CAAC,CAAA;KAC3C;AACH,CAAC,CAAA","sourcesContent":["const MAX_PATTERN_LENGTH = 1024 * 64\nexport const assertValidPattern: (pattern: any) => void = (\n pattern: any\n): asserts pattern is string => {\n if (typeof pattern !== 'string') {\n throw new TypeError('invalid pattern')\n }\n\n if (pattern.length > MAX_PATTERN_LENGTH) {\n throw new TypeError('pattern is too long')\n }\n}\n"]}

View file

@ -0,0 +1,19 @@
import { MinimatchOptions, MMRegExp } from './index.js';
export type ExtglobType = '!' | '?' | '+' | '*' | '@';
export declare class AST {
#private;
type: ExtglobType | null;
constructor(type: ExtglobType | null, parent?: AST, options?: MinimatchOptions);
get hasMagic(): boolean | undefined;
toString(): string;
push(...parts: (string | AST)[]): void;
toJSON(): any[];
isStart(): boolean;
isEnd(): boolean;
copyIn(part: AST | string): void;
clone(parent: AST): AST;
static fromGlob(pattern: string, options?: MinimatchOptions): AST;
toMMPattern(): MMRegExp | string;
toRegExpSource(allowDot?: boolean): [re: string, body: string, hasMagic: boolean, uflag: boolean];
}
//# sourceMappingURL=ast.d.ts.map

View file

@ -0,0 +1 @@
{"version":3,"file":"ast.d.ts","sourceRoot":"","sources":["../../src/ast.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,gBAAgB,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAA;AAwCvD,MAAM,MAAM,WAAW,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,CAAA;AAkCrD,qBAAa,GAAG;;IACd,IAAI,EAAE,WAAW,GAAG,IAAI,CAAA;gBAiBtB,IAAI,EAAE,WAAW,GAAG,IAAI,EACxB,MAAM,CAAC,EAAE,GAAG,EACZ,OAAO,GAAE,gBAAqB;IAahC,IAAI,QAAQ,IAAI,OAAO,GAAG,SAAS,CAUlC;IAGD,QAAQ,IAAI,MAAM;IA+ClB,IAAI,CAAC,GAAG,KAAK,EAAE,CAAC,MAAM,GAAG,GAAG,CAAC,EAAE;IAY/B,MAAM;IAgBN,OAAO,IAAI,OAAO;IAgBlB,KAAK,IAAI,OAAO;IAYhB,MAAM,CAAC,IAAI,EAAE,GAAG,GAAG,MAAM;IAKzB,KAAK,CAAC,MAAM,EAAE,GAAG;IAsIjB,MAAM,CAAC,QAAQ,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,GAAE,gBAAqB;IAQ/D,WAAW,IAAI,QAAQ,GAAG,MAAM;IAgGhC,cAAc,CACZ,QAAQ,CAAC,EAAE,OAAO,GACjB,CAAC,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,OAAO,EAAE,KAAK,EAAE,OAAO,CAAC;CAiMjE"}

View file

@ -0,0 +1,585 @@
// parse a single path portion
import { parseClass } from './brace-expressions.js';
import { unescape } from './unescape.js';
const types = new Set(['!', '?', '+', '*', '@']);
const isExtglobType = (c) => types.has(c);
// Patterns that get prepended to bind to the start of either the
// entire string, or just a single path portion, to prevent dots
// and/or traversal patterns, when needed.
// Exts don't need the ^ or / bit, because the root binds that already.
const startNoTraversal = '(?!(?:^|/)\\.\\.?(?:$|/))';
const startNoDot = '(?!\\.)';
// characters that indicate a start of pattern needs the "no dots" bit,
// because a dot *might* be matched. ( is not in the list, because in
// the case of a child extglob, it will handle the prevention itself.
const addPatternStart = new Set(['[', '.']);
// cases where traversal is A-OK, no dot prevention needed
const justDots = new Set(['..', '.']);
const reSpecials = new Set('().*{}+?[]^$\\!');
const regExpEscape = (s) => s.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, '\\$&');
// any single thing other than /
const qmark = '[^/]';
// * => any number of characters
const star = qmark + '*?';
// use + when we need to ensure that *something* matches, because the * is
// the only thing in the path portion.
const starNoEmpty = qmark + '+?';
// remove the \ chars that we added if we end up doing a nonmagic compare
// const deslash = (s: string) => s.replace(/\\(.)/g, '$1')
export class AST {
type;
#root;
#hasMagic;
#uflag = false;
#parts = [];
#parent;
#parentIndex;
#negs;
#filledNegs = false;
#options;
#toString;
// set to true if it's an extglob with no children
// (which really means one child of '')
#emptyExt = false;
constructor(type, parent, options = {}) {
this.type = type;
// extglobs are inherently magical
if (type)
this.#hasMagic = true;
this.#parent = parent;
this.#root = this.#parent ? this.#parent.#root : this;
this.#options = this.#root === this ? options : this.#root.#options;
this.#negs = this.#root === this ? [] : this.#root.#negs;
if (type === '!' && !this.#root.#filledNegs)
this.#negs.push(this);
this.#parentIndex = this.#parent ? this.#parent.#parts.length : 0;
}
get hasMagic() {
/* c8 ignore start */
if (this.#hasMagic !== undefined)
return this.#hasMagic;
/* c8 ignore stop */
for (const p of this.#parts) {
if (typeof p === 'string')
continue;
if (p.type || p.hasMagic)
return (this.#hasMagic = true);
}
// note: will be undefined until we generate the regexp src and find out
return this.#hasMagic;
}
// reconstructs the pattern
toString() {
if (this.#toString !== undefined)
return this.#toString;
if (!this.type) {
return (this.#toString = this.#parts.map(p => String(p)).join(''));
}
else {
return (this.#toString =
this.type + '(' + this.#parts.map(p => String(p)).join('|') + ')');
}
}
#fillNegs() {
/* c8 ignore start */
if (this !== this.#root)
throw new Error('should only call on root');
if (this.#filledNegs)
return this;
/* c8 ignore stop */
// call toString() once to fill this out
this.toString();
this.#filledNegs = true;
let n;
while ((n = this.#negs.pop())) {
if (n.type !== '!')
continue;
// walk up the tree, appending everthing that comes AFTER parentIndex
let p = n;
let pp = p.#parent;
while (pp) {
for (let i = p.#parentIndex + 1; !pp.type && i < pp.#parts.length; i++) {
for (const part of n.#parts) {
/* c8 ignore start */
if (typeof part === 'string') {
throw new Error('string part in extglob AST??');
}
/* c8 ignore stop */
part.copyIn(pp.#parts[i]);
}
}
p = pp;
pp = p.#parent;
}
}
return this;
}
push(...parts) {
for (const p of parts) {
if (p === '')
continue;
/* c8 ignore start */
if (typeof p !== 'string' && !(p instanceof AST && p.#parent === this)) {
throw new Error('invalid part: ' + p);
}
/* c8 ignore stop */
this.#parts.push(p);
}
}
toJSON() {
const ret = this.type === null
? this.#parts.slice().map(p => (typeof p === 'string' ? p : p.toJSON()))
: [this.type, ...this.#parts.map(p => p.toJSON())];
if (this.isStart() && !this.type)
ret.unshift([]);
if (this.isEnd() &&
(this === this.#root ||
(this.#root.#filledNegs && this.#parent?.type === '!'))) {
ret.push({});
}
return ret;
}
isStart() {
if (this.#root === this)
return true;
// if (this.type) return !!this.#parent?.isStart()
if (!this.#parent?.isStart())
return false;
if (this.#parentIndex === 0)
return true;
// if everything AHEAD of this is a negation, then it's still the "start"
const p = this.#parent;
for (let i = 0; i < this.#parentIndex; i++) {
const pp = p.#parts[i];
if (!(pp instanceof AST && pp.type === '!')) {
return false;
}
}
return true;
}
isEnd() {
if (this.#root === this)
return true;
if (this.#parent?.type === '!')
return true;
if (!this.#parent?.isEnd())
return false;
if (!this.type)
return this.#parent?.isEnd();
// if not root, it'll always have a parent
/* c8 ignore start */
const pl = this.#parent ? this.#parent.#parts.length : 0;
/* c8 ignore stop */
return this.#parentIndex === pl - 1;
}
copyIn(part) {
if (typeof part === 'string')
this.push(part);
else
this.push(part.clone(this));
}
clone(parent) {
const c = new AST(this.type, parent);
for (const p of this.#parts) {
c.copyIn(p);
}
return c;
}
static #parseAST(str, ast, pos, opt) {
let escaping = false;
let inBrace = false;
let braceStart = -1;
let braceNeg = false;
if (ast.type === null) {
// outside of a extglob, append until we find a start
let i = pos;
let acc = '';
while (i < str.length) {
const c = str.charAt(i++);
// still accumulate escapes at this point, but we do ignore
// starts that are escaped
if (escaping || c === '\\') {
escaping = !escaping;
acc += c;
continue;
}
if (inBrace) {
if (i === braceStart + 1) {
if (c === '^' || c === '!') {
braceNeg = true;
}
}
else if (c === ']' && !(i === braceStart + 2 && braceNeg)) {
inBrace = false;
}
acc += c;
continue;
}
else if (c === '[') {
inBrace = true;
braceStart = i;
braceNeg = false;
acc += c;
continue;
}
if (!opt.noext && isExtglobType(c) && str.charAt(i) === '(') {
ast.push(acc);
acc = '';
const ext = new AST(c, ast);
i = AST.#parseAST(str, ext, i, opt);
ast.push(ext);
continue;
}
acc += c;
}
ast.push(acc);
return i;
}
// some kind of extglob, pos is at the (
// find the next | or )
let i = pos + 1;
let part = new AST(null, ast);
const parts = [];
let acc = '';
while (i < str.length) {
const c = str.charAt(i++);
// still accumulate escapes at this point, but we do ignore
// starts that are escaped
if (escaping || c === '\\') {
escaping = !escaping;
acc += c;
continue;
}
if (inBrace) {
if (i === braceStart + 1) {
if (c === '^' || c === '!') {
braceNeg = true;
}
}
else if (c === ']' && !(i === braceStart + 2 && braceNeg)) {
inBrace = false;
}
acc += c;
continue;
}
else if (c === '[') {
inBrace = true;
braceStart = i;
braceNeg = false;
acc += c;
continue;
}
if (isExtglobType(c) && str.charAt(i) === '(') {
part.push(acc);
acc = '';
const ext = new AST(c, part);
part.push(ext);
i = AST.#parseAST(str, ext, i, opt);
continue;
}
if (c === '|') {
part.push(acc);
acc = '';
parts.push(part);
part = new AST(null, ast);
continue;
}
if (c === ')') {
if (acc === '' && ast.#parts.length === 0) {
ast.#emptyExt = true;
}
part.push(acc);
acc = '';
ast.push(...parts, part);
return i;
}
acc += c;
}
// unfinished extglob
// if we got here, it was a malformed extglob! not an extglob, but
// maybe something else in there.
ast.type = null;
ast.#hasMagic = undefined;
ast.#parts = [str.substring(pos - 1)];
return i;
}
static fromGlob(pattern, options = {}) {
const ast = new AST(null, undefined, options);
AST.#parseAST(pattern, ast, 0, options);
return ast;
}
// returns the regular expression if there's magic, or the unescaped
// string if not.
toMMPattern() {
// should only be called on root
/* c8 ignore start */
if (this !== this.#root)
return this.#root.toMMPattern();
/* c8 ignore stop */
const glob = this.toString();
const [re, body, hasMagic, uflag] = this.toRegExpSource();
// if we're in nocase mode, and not nocaseMagicOnly, then we do
// still need a regular expression if we have to case-insensitively
// match capital/lowercase characters.
const anyMagic = hasMagic ||
this.#hasMagic ||
(this.#options.nocase &&
!this.#options.nocaseMagicOnly &&
glob.toUpperCase() !== glob.toLowerCase());
if (!anyMagic) {
return body;
}
const flags = (this.#options.nocase ? 'i' : '') + (uflag ? 'u' : '');
return Object.assign(new RegExp(`^${re}$`, flags), {
_src: re,
_glob: glob,
});
}
// returns the string match, the regexp source, whether there's magic
// in the regexp (so a regular expression is required) and whether or
// not the uflag is needed for the regular expression (for posix classes)
// TODO: instead of injecting the start/end at this point, just return
// the BODY of the regexp, along with the start/end portions suitable
// for binding the start/end in either a joined full-path makeRe context
// (where we bind to (^|/), or a standalone matchPart context (where
// we bind to ^, and not /). Otherwise slashes get duped!
//
// In part-matching mode, the start is:
// - if not isStart: nothing
// - if traversal possible, but not allowed: ^(?!\.\.?$)
// - if dots allowed or not possible: ^
// - if dots possible and not allowed: ^(?!\.)
// end is:
// - if not isEnd(): nothing
// - else: $
//
// In full-path matching mode, we put the slash at the START of the
// pattern, so start is:
// - if first pattern: same as part-matching mode
// - if not isStart(): nothing
// - if traversal possible, but not allowed: /(?!\.\.?(?:$|/))
// - if dots allowed or not possible: /
// - if dots possible and not allowed: /(?!\.)
// end is:
// - if last pattern, same as part-matching mode
// - else nothing
//
// Always put the (?:$|/) on negated tails, though, because that has to be
// there to bind the end of the negated pattern portion, and it's easier to
// just stick it in now rather than try to inject it later in the middle of
// the pattern.
//
// We can just always return the same end, and leave it up to the caller
// to know whether it's going to be used joined or in parts.
// And, if the start is adjusted slightly, can do the same there:
// - if not isStart: nothing
// - if traversal possible, but not allowed: (?:/|^)(?!\.\.?$)
// - if dots allowed or not possible: (?:/|^)
// - if dots possible and not allowed: (?:/|^)(?!\.)
//
// But it's better to have a simpler binding without a conditional, for
// performance, so probably better to return both start options.
//
// Then the caller just ignores the end if it's not the first pattern,
// and the start always gets applied.
//
// But that's always going to be $ if it's the ending pattern, or nothing,
// so the caller can just attach $ at the end of the pattern when building.
//
// So the todo is:
// - better detect what kind of start is needed
// - return both flavors of starting pattern
// - attach $ at the end of the pattern when creating the actual RegExp
//
// Ah, but wait, no, that all only applies to the root when the first pattern
// is not an extglob. If the first pattern IS an extglob, then we need all
// that dot prevention biz to live in the extglob portions, because eg
// +(*|.x*) can match .xy but not .yx.
//
// So, return the two flavors if it's #root and the first child is not an
// AST, otherwise leave it to the child AST to handle it, and there,
// use the (?:^|/) style of start binding.
//
// Even simplified further:
// - Since the start for a join is eg /(?!\.) and the start for a part
// is ^(?!\.), we can just prepend (?!\.) to the pattern (either root
// or start or whatever) and prepend ^ or / at the Regexp construction.
toRegExpSource(allowDot) {
const dot = allowDot ?? !!this.#options.dot;
if (this.#root === this)
this.#fillNegs();
if (!this.type) {
const noEmpty = this.isStart() && this.isEnd();
const src = this.#parts
.map(p => {
const [re, _, hasMagic, uflag] = typeof p === 'string'
? AST.#parseGlob(p, this.#hasMagic, noEmpty)
: p.toRegExpSource(allowDot);
this.#hasMagic = this.#hasMagic || hasMagic;
this.#uflag = this.#uflag || uflag;
return re;
})
.join('');
let start = '';
if (this.isStart()) {
if (typeof this.#parts[0] === 'string') {
// this is the string that will match the start of the pattern,
// so we need to protect against dots and such.
// '.' and '..' cannot match unless the pattern is that exactly,
// even if it starts with . or dot:true is set.
const dotTravAllowed = this.#parts.length === 1 && justDots.has(this.#parts[0]);
if (!dotTravAllowed) {
const aps = addPatternStart;
// check if we have a possibility of matching . or ..,
// and prevent that.
const needNoTrav =
// dots are allowed, and the pattern starts with [ or .
(dot && aps.has(src.charAt(0))) ||
// the pattern starts with \., and then [ or .
(src.startsWith('\\.') && aps.has(src.charAt(2))) ||
// the pattern starts with \.\., and then [ or .
(src.startsWith('\\.\\.') && aps.has(src.charAt(4)));
// no need to prevent dots if it can't match a dot, or if a
// sub-pattern will be preventing it anyway.
const needNoDot = !dot && !allowDot && aps.has(src.charAt(0));
start = needNoTrav ? startNoTraversal : needNoDot ? startNoDot : '';
}
}
}
// append the "end of path portion" pattern to negation tails
let end = '';
if (this.isEnd() &&
this.#root.#filledNegs &&
this.#parent?.type === '!') {
end = '(?:$|\\/)';
}
const final = start + src + end;
return [
final,
unescape(src),
(this.#hasMagic = !!this.#hasMagic),
this.#uflag,
];
}
// We need to calculate the body *twice* if it's a repeat pattern
// at the start, once in nodot mode, then again in dot mode, so a
// pattern like *(?) can match 'x.y'
const repeated = this.type === '*' || this.type === '+';
// some kind of extglob
const start = this.type === '!' ? '(?:(?!(?:' : '(?:';
let body = this.#partsToRegExp(dot);
if (this.isStart() && this.isEnd() && !body && this.type !== '!') {
// invalid extglob, has to at least be *something* present, if it's
// the entire path portion.
const s = this.toString();
this.#parts = [s];
this.type = null;
this.#hasMagic = undefined;
return [s, unescape(this.toString()), false, false];
}
// XXX abstract out this map method
let bodyDotAllowed = !repeated || allowDot || dot || !startNoDot
? ''
: this.#partsToRegExp(true);
if (bodyDotAllowed === body) {
bodyDotAllowed = '';
}
if (bodyDotAllowed) {
body = `(?:${body})(?:${bodyDotAllowed})*?`;
}
// an empty !() is exactly equivalent to a starNoEmpty
let final = '';
if (this.type === '!' && this.#emptyExt) {
final = (this.isStart() && !dot ? startNoDot : '') + starNoEmpty;
}
else {
const close = this.type === '!'
? // !() must match something,but !(x) can match ''
'))' +
(this.isStart() && !dot && !allowDot ? startNoDot : '') +
star +
')'
: this.type === '@'
? ')'
: this.type === '?'
? ')?'
: this.type === '+' && bodyDotAllowed
? ')'
: this.type === '*' && bodyDotAllowed
? `)?`
: `)${this.type}`;
final = start + body + close;
}
return [
final,
unescape(body),
(this.#hasMagic = !!this.#hasMagic),
this.#uflag,
];
}
#partsToRegExp(dot) {
return this.#parts
.map(p => {
// extglob ASTs should only contain parent ASTs
/* c8 ignore start */
if (typeof p === 'string') {
throw new Error('string type in extglob ast??');
}
/* c8 ignore stop */
// can ignore hasMagic, because extglobs are already always magic
const [re, _, _hasMagic, uflag] = p.toRegExpSource(dot);
this.#uflag = this.#uflag || uflag;
return re;
})
.filter(p => !(this.isStart() && this.isEnd()) || !!p)
.join('|');
}
static #parseGlob(glob, hasMagic, noEmpty = false) {
let escaping = false;
let re = '';
let uflag = false;
for (let i = 0; i < glob.length; i++) {
const c = glob.charAt(i);
if (escaping) {
escaping = false;
re += (reSpecials.has(c) ? '\\' : '') + c;
continue;
}
if (c === '\\') {
if (i === glob.length - 1) {
re += '\\\\';
}
else {
escaping = true;
}
continue;
}
if (c === '[') {
const [src, needUflag, consumed, magic] = parseClass(glob, i);
if (consumed) {
re += src;
uflag = uflag || needUflag;
i += consumed - 1;
hasMagic = hasMagic || magic;
continue;
}
}
if (c === '*') {
if (noEmpty && glob === '*')
re += starNoEmpty;
else
re += star;
hasMagic = true;
continue;
}
if (c === '?') {
re += qmark;
hasMagic = true;
continue;
}
re += regExpEscape(c);
}
return [re, unescape(glob), !!hasMagic, uflag];
}
}
//# sourceMappingURL=ast.js.map

File diff suppressed because one or more lines are too long

View file

@ -0,0 +1,8 @@
export type ParseClassResult = [
src: string,
uFlag: boolean,
consumed: number,
hasMagic: boolean
];
export declare const parseClass: (glob: string, position: number) => ParseClassResult;
//# sourceMappingURL=brace-expressions.d.ts.map

View file

@ -0,0 +1 @@
{"version":3,"file":"brace-expressions.d.ts","sourceRoot":"","sources":["../../src/brace-expressions.ts"],"names":[],"mappings":"AA+BA,MAAM,MAAM,gBAAgB,GAAG;IAC7B,GAAG,EAAE,MAAM;IACX,KAAK,EAAE,OAAO;IACd,QAAQ,EAAE,MAAM;IAChB,QAAQ,EAAE,OAAO;CAClB,CAAA;AAQD,eAAO,MAAM,UAAU,SACf,MAAM,YACF,MAAM,qBA8HjB,CAAA"}

View file

@ -0,0 +1,148 @@
// translate the various posix character classes into unicode properties
// this works across all unicode locales
// { <posix class>: [<translation>, /u flag required, negated]
const posixClasses = {
'[:alnum:]': ['\\p{L}\\p{Nl}\\p{Nd}', true],
'[:alpha:]': ['\\p{L}\\p{Nl}', true],
'[:ascii:]': ['\\x' + '00-\\x' + '7f', false],
'[:blank:]': ['\\p{Zs}\\t', true],
'[:cntrl:]': ['\\p{Cc}', true],
'[:digit:]': ['\\p{Nd}', true],
'[:graph:]': ['\\p{Z}\\p{C}', true, true],
'[:lower:]': ['\\p{Ll}', true],
'[:print:]': ['\\p{C}', true],
'[:punct:]': ['\\p{P}', true],
'[:space:]': ['\\p{Z}\\t\\r\\n\\v\\f', true],
'[:upper:]': ['\\p{Lu}', true],
'[:word:]': ['\\p{L}\\p{Nl}\\p{Nd}\\p{Pc}', true],
'[:xdigit:]': ['A-Fa-f0-9', false],
};
// only need to escape a few things inside of brace expressions
// escapes: [ \ ] -
const braceEscape = (s) => s.replace(/[[\]\\-]/g, '\\$&');
// escape all regexp magic characters
const regexpEscape = (s) => s.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, '\\$&');
// everything has already been escaped, we just have to join
const rangesToString = (ranges) => ranges.join('');
// takes a glob string at a posix brace expression, and returns
// an equivalent regular expression source, and boolean indicating
// whether the /u flag needs to be applied, and the number of chars
// consumed to parse the character class.
// This also removes out of order ranges, and returns ($.) if the
// entire class just no good.
export const parseClass = (glob, position) => {
const pos = position;
/* c8 ignore start */
if (glob.charAt(pos) !== '[') {
throw new Error('not in a brace expression');
}
/* c8 ignore stop */
const ranges = [];
const negs = [];
let i = pos + 1;
let sawStart = false;
let uflag = false;
let escaping = false;
let negate = false;
let endPos = pos;
let rangeStart = '';
WHILE: while (i < glob.length) {
const c = glob.charAt(i);
if ((c === '!' || c === '^') && i === pos + 1) {
negate = true;
i++;
continue;
}
if (c === ']' && sawStart && !escaping) {
endPos = i + 1;
break;
}
sawStart = true;
if (c === '\\') {
if (!escaping) {
escaping = true;
i++;
continue;
}
// escaped \ char, fall through and treat like normal char
}
if (c === '[' && !escaping) {
// either a posix class, a collation equivalent, or just a [
for (const [cls, [unip, u, neg]] of Object.entries(posixClasses)) {
if (glob.startsWith(cls, i)) {
// invalid, [a-[] is fine, but not [a-[:alpha]]
if (rangeStart) {
return ['$.', false, glob.length - pos, true];
}
i += cls.length;
if (neg)
negs.push(unip);
else
ranges.push(unip);
uflag = uflag || u;
continue WHILE;
}
}
}
// now it's just a normal character, effectively
escaping = false;
if (rangeStart) {
// throw this range away if it's not valid, but others
// can still match.
if (c > rangeStart) {
ranges.push(braceEscape(rangeStart) + '-' + braceEscape(c));
}
else if (c === rangeStart) {
ranges.push(braceEscape(c));
}
rangeStart = '';
i++;
continue;
}
// now might be the start of a range.
// can be either c-d or c-] or c<more...>] or c] at this point
if (glob.startsWith('-]', i + 1)) {
ranges.push(braceEscape(c + '-'));
i += 2;
continue;
}
if (glob.startsWith('-', i + 1)) {
rangeStart = c;
i += 2;
continue;
}
// not the start of a range, just a single character
ranges.push(braceEscape(c));
i++;
}
if (endPos < i) {
// didn't see the end of the class, not a valid class,
// but might still be valid as a literal match.
return ['', false, 0, false];
}
// if we got no ranges and no negates, then we have a range that
// cannot possibly match anything, and that poisons the whole glob
if (!ranges.length && !negs.length) {
return ['$.', false, glob.length - pos, true];
}
// if we got one positive range, and it's a single character, then that's
// not actually a magic pattern, it's just that one literal character.
// we should not treat that as "magic", we should just return the literal
// character. [_] is a perfectly valid way to escape glob magic chars.
if (negs.length === 0 &&
ranges.length === 1 &&
/^\\?.$/.test(ranges[0]) &&
!negate) {
const r = ranges[0].length === 2 ? ranges[0].slice(-1) : ranges[0];
return [regexpEscape(r), false, endPos - pos, false];
}
const sranges = '[' + (negate ? '^' : '') + rangesToString(ranges) + ']';
const snegs = '[' + (negate ? '' : '^') + rangesToString(negs) + ']';
const comb = ranges.length && negs.length
? '(' + sranges + '|' + snegs + ')'
: ranges.length
? sranges
: snegs;
return [comb, uflag, endPos - pos, true];
};
//# sourceMappingURL=brace-expressions.js.map

File diff suppressed because one or more lines are too long

View file

@ -0,0 +1,12 @@
import { MinimatchOptions } from './index.js';
/**
* Escape all magic characters in a glob pattern.
*
* If the {@link windowsPathsNoEscape | GlobOptions.windowsPathsNoEscape}
* option is used, then characters are escaped by wrapping in `[]`, because
* a magic character wrapped in a character class can only be satisfied by
* that exact character. In this mode, `\` is _not_ escaped, because it is
* not interpreted as a magic character, but instead as a path separator.
*/
export declare const escape: (s: string, { windowsPathsNoEscape, }?: Pick<MinimatchOptions, 'windowsPathsNoEscape'>) => string;
//# sourceMappingURL=escape.d.ts.map

View file

@ -0,0 +1 @@
{"version":3,"file":"escape.d.ts","sourceRoot":"","sources":["../../src/escape.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAA;AAC7C;;;;;;;;GAQG;AACH,eAAO,MAAM,MAAM,MACd,MAAM,8BAGN,KAAK,gBAAgB,EAAE,sBAAsB,CAAC,WAQlD,CAAA"}

View file

@ -0,0 +1,18 @@
/**
* Escape all magic characters in a glob pattern.
*
* If the {@link windowsPathsNoEscape | GlobOptions.windowsPathsNoEscape}
* option is used, then characters are escaped by wrapping in `[]`, because
* a magic character wrapped in a character class can only be satisfied by
* that exact character. In this mode, `\` is _not_ escaped, because it is
* not interpreted as a magic character, but instead as a path separator.
*/
export const escape = (s, { windowsPathsNoEscape = false, } = {}) => {
// don't need to escape +@! because we escape the parens
// that make those magic, and escaping ! as [!] isn't valid,
// because [!]] is a valid glob class meaning not ']'.
return windowsPathsNoEscape
? s.replace(/[?*()[\]]/g, '[$&]')
: s.replace(/[?*()[\]\\]/g, '\\$&');
};
//# sourceMappingURL=escape.js.map

View file

@ -0,0 +1 @@
{"version":3,"file":"escape.js","sourceRoot":"","sources":["../../src/escape.ts"],"names":[],"mappings":"AACA;;;;;;;;GAQG;AACH,MAAM,CAAC,MAAM,MAAM,GAAG,CACpB,CAAS,EACT,EACE,oBAAoB,GAAG,KAAK,MACsB,EAAE,EACtD,EAAE;IACF,wDAAwD;IACxD,4DAA4D;IAC5D,sDAAsD;IACtD,OAAO,oBAAoB;QACzB,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,YAAY,EAAE,MAAM,CAAC;QACjC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,cAAc,EAAE,MAAM,CAAC,CAAA;AACvC,CAAC,CAAA","sourcesContent":["import { MinimatchOptions } from './index.js'\n/**\n * Escape all magic characters in a glob pattern.\n *\n * If the {@link windowsPathsNoEscape | GlobOptions.windowsPathsNoEscape}\n * option is used, then characters are escaped by wrapping in `[]`, because\n * a magic character wrapped in a character class can only be satisfied by\n * that exact character. In this mode, `\\` is _not_ escaped, because it is\n * not interpreted as a magic character, but instead as a path separator.\n */\nexport const escape = (\n s: string,\n {\n windowsPathsNoEscape = false,\n }: Pick<MinimatchOptions, 'windowsPathsNoEscape'> = {}\n) => {\n // don't need to escape +@! because we escape the parens\n // that make those magic, and escaping ! as [!] isn't valid,\n // because [!]] is a valid glob class meaning not ']'.\n return windowsPathsNoEscape\n ? s.replace(/[?*()[\\]]/g, '[$&]')\n : s.replace(/[?*()[\\]\\\\]/g, '\\\\$&')\n}\n"]}

View file

@ -0,0 +1,94 @@
import { AST } from './ast.js';
type Platform = 'aix' | 'android' | 'darwin' | 'freebsd' | 'haiku' | 'linux' | 'openbsd' | 'sunos' | 'win32' | 'cygwin' | 'netbsd';
export interface MinimatchOptions {
nobrace?: boolean;
nocomment?: boolean;
nonegate?: boolean;
debug?: boolean;
noglobstar?: boolean;
noext?: boolean;
nonull?: boolean;
windowsPathsNoEscape?: boolean;
allowWindowsEscape?: boolean;
partial?: boolean;
dot?: boolean;
nocase?: boolean;
nocaseMagicOnly?: boolean;
magicalBraces?: boolean;
matchBase?: boolean;
flipNegate?: boolean;
preserveMultipleSlashes?: boolean;
optimizationLevel?: number;
platform?: Platform;
windowsNoMagicRoot?: boolean;
}
export declare const minimatch: {
(p: string, pattern: string, options?: MinimatchOptions): boolean;
sep: Sep;
GLOBSTAR: typeof GLOBSTAR;
filter: (pattern: string, options?: MinimatchOptions) => (p: string) => boolean;
defaults: (def: MinimatchOptions) => typeof minimatch;
braceExpand: (pattern: string, options?: MinimatchOptions) => string[];
makeRe: (pattern: string, options?: MinimatchOptions) => false | MMRegExp;
match: (list: string[], pattern: string, options?: MinimatchOptions) => string[];
AST: typeof AST;
Minimatch: typeof Minimatch;
escape: (s: string, { windowsPathsNoEscape, }?: Pick<MinimatchOptions, "windowsPathsNoEscape">) => string;
unescape: (s: string, { windowsPathsNoEscape, }?: Pick<MinimatchOptions, "windowsPathsNoEscape">) => string;
};
type Sep = '\\' | '/';
export declare const sep: Sep;
export declare const GLOBSTAR: unique symbol;
export declare const filter: (pattern: string, options?: MinimatchOptions) => (p: string) => boolean;
export declare const defaults: (def: MinimatchOptions) => typeof minimatch;
export declare const braceExpand: (pattern: string, options?: MinimatchOptions) => string[];
export declare const makeRe: (pattern: string, options?: MinimatchOptions) => false | MMRegExp;
export declare const match: (list: string[], pattern: string, options?: MinimatchOptions) => string[];
export type MMRegExp = RegExp & {
_src?: string;
_glob?: string;
};
export type ParseReturnFiltered = string | MMRegExp | typeof GLOBSTAR;
export type ParseReturn = ParseReturnFiltered | false;
export declare class Minimatch {
options: MinimatchOptions;
set: ParseReturnFiltered[][];
pattern: string;
windowsPathsNoEscape: boolean;
nonegate: boolean;
negate: boolean;
comment: boolean;
empty: boolean;
preserveMultipleSlashes: boolean;
partial: boolean;
globSet: string[];
globParts: string[][];
nocase: boolean;
isWindows: boolean;
platform: Platform;
windowsNoMagicRoot: boolean;
regexp: false | null | MMRegExp;
constructor(pattern: string, options?: MinimatchOptions);
hasMagic(): boolean;
debug(..._: any[]): void;
make(): void;
preprocess(globParts: string[][]): string[][];
adjascentGlobstarOptimize(globParts: string[][]): string[][];
levelOneOptimize(globParts: string[][]): string[][];
levelTwoFileOptimize(parts: string | string[]): string[];
firstPhasePreProcess(globParts: string[][]): string[][];
secondPhasePreProcess(globParts: string[][]): string[][];
partsMatch(a: string[], b: string[], emptyGSMatch?: boolean): false | string[];
parseNegate(): void;
matchOne(file: string[], pattern: ParseReturn[], partial?: boolean): boolean;
braceExpand(): string[];
parse(pattern: string): ParseReturn;
makeRe(): false | MMRegExp;
slashSplit(p: string): string[];
match(f: string, partial?: boolean): boolean;
static defaults(def: MinimatchOptions): typeof Minimatch;
}
export { AST } from './ast.js';
export { escape } from './escape.js';
export { unescape } from './unescape.js';
//# sourceMappingURL=index.d.ts.map

View file

@ -0,0 +1 @@
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,GAAG,EAAe,MAAM,UAAU,CAAA;AAI3C,KAAK,QAAQ,GACT,KAAK,GACL,SAAS,GACT,QAAQ,GACR,SAAS,GACT,OAAO,GACP,OAAO,GACP,SAAS,GACT,OAAO,GACP,OAAO,GACP,QAAQ,GACR,QAAQ,CAAA;AAEZ,MAAM,WAAW,gBAAgB;IAC/B,OAAO,CAAC,EAAE,OAAO,CAAA;IACjB,SAAS,CAAC,EAAE,OAAO,CAAA;IACnB,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB,KAAK,CAAC,EAAE,OAAO,CAAA;IACf,UAAU,CAAC,EAAE,OAAO,CAAA;IACpB,KAAK,CAAC,EAAE,OAAO,CAAA;IACf,MAAM,CAAC,EAAE,OAAO,CAAA;IAChB,oBAAoB,CAAC,EAAE,OAAO,CAAA;IAC9B,kBAAkB,CAAC,EAAE,OAAO,CAAA;IAC5B,OAAO,CAAC,EAAE,OAAO,CAAA;IACjB,GAAG,CAAC,EAAE,OAAO,CAAA;IACb,MAAM,CAAC,EAAE,OAAO,CAAA;IAChB,eAAe,CAAC,EAAE,OAAO,CAAA;IACzB,aAAa,CAAC,EAAE,OAAO,CAAA;IACvB,SAAS,CAAC,EAAE,OAAO,CAAA;IACnB,UAAU,CAAC,EAAE,OAAO,CAAA;IACpB,uBAAuB,CAAC,EAAE,OAAO,CAAA;IACjC,iBAAiB,CAAC,EAAE,MAAM,CAAA;IAC1B,QAAQ,CAAC,EAAE,QAAQ,CAAA;IACnB,kBAAkB,CAAC,EAAE,OAAO,CAAA;CAC7B;AAED,eAAO,MAAM,SAAS;QACjB,MAAM,WACA,MAAM,YACN,gBAAgB;;;sBAuGf,MAAM,YAAW,gBAAgB,SACvC,MAAM;oBAOkB,gBAAgB,KAAG,gBAAgB;2BA6EtD,MAAM,YACN,gBAAgB;sBA2BK,MAAM,YAAW,gBAAgB;kBAKzD,MAAM,EAAE,WACL,MAAM,YACN,gBAAgB;;;;;CArN1B,CAAA;AA+DD,KAAK,GAAG,GAAG,IAAI,GAAG,GAAG,CAAA;AAOrB,eAAO,MAAM,GAAG,KAAgE,CAAA;AAGhF,eAAO,MAAM,QAAQ,eAAwB,CAAA;AAmB7C,eAAO,MAAM,MAAM,YACP,MAAM,YAAW,gBAAgB,SACvC,MAAM,YACsB,CAAA;AAMlC,eAAO,MAAM,QAAQ,QAAS,gBAAgB,KAAG,gBA+DhD,CAAA;AAaD,eAAO,MAAM,WAAW,YACb,MAAM,YACN,gBAAgB,aAY1B,CAAA;AAeD,eAAO,MAAM,MAAM,YAAa,MAAM,YAAW,gBAAgB,qBACvB,CAAA;AAG1C,eAAO,MAAM,KAAK,SACV,MAAM,EAAE,WACL,MAAM,YACN,gBAAgB,aAQ1B,CAAA;AAQD,MAAM,MAAM,QAAQ,GAAG,MAAM,GAAG;IAC9B,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,KAAK,CAAC,EAAE,MAAM,CAAA;CACf,CAAA;AAED,MAAM,MAAM,mBAAmB,GAAG,MAAM,GAAG,QAAQ,GAAG,OAAO,QAAQ,CAAA;AACrE,MAAM,MAAM,WAAW,GAAG,mBAAmB,GAAG,KAAK,CAAA;AAErD,qBAAa,SAAS;IACpB,OAAO,EAAE,gBAAgB,CAAA;IACzB,GAAG,EAAE,mBAAmB,EAAE,EAAE,CAAA;IAC5B,OAAO,EAAE,MAAM,CAAA;IAEf,oBAAoB,EAAE,OAAO,CAAA;IAC7B,QAAQ,EAAE,OAAO,CAAA;IACjB,MAAM,EAAE,OAAO,CAAA;IACf,OAAO,EAAE,OAAO,CAAA;IAChB,KAAK,EAAE,OAAO,CAAA;IACd,uBAAuB,EAAE,OAAO,CAAA;IAChC,OAAO,EAAE,OAAO,CAAA;IAChB,OAAO,EAAE,MAAM,EAAE,CAAA;IACjB,SAAS,EAAE,MAAM,EAAE,EAAE,CAAA;IACrB,MAAM,EAAE,OAAO,CAAA;IAEf,SAAS,EAAE,OAAO,CAAA;IAClB,QAAQ,EAAE,QAAQ,CAAA;IAClB,kBAAkB,EAAE,OAAO,CAAA;IAE3B,MAAM,EAAE,KAAK,GAAG,IAAI,GAAG,QAAQ,CAAA;gBACnB,OAAO,EAAE,MAAM,EAAE,OAAO,GAAE,gBAAqB;IAkC3D,QAAQ,IAAI,OAAO;IAYnB,KAAK,CAAC,GAAG,CAAC,EAAE,GAAG,EAAE;IAEjB,IAAI;IA0FJ,UAAU,CAAC,SAAS,EAAE,MAAM,EAAE,EAAE;IA6BhC,yBAAyB,CAAC,SAAS,EAAE,MAAM,EAAE,EAAE;IAiB/C,gBAAgB,CAAC,SAAS,EAAE,MAAM,EAAE,EAAE;IAoBtC,oBAAoB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,EAAE;IA6D7C,oBAAoB,CAAC,SAAS,EAAE,MAAM,EAAE,EAAE;IA0F1C,qBAAqB,CAAC,SAAS,EAAE,MAAM,EAAE,EAAE,GAAG,MAAM,EAAE,EAAE;IAgBxD,UAAU,CACR,CAAC,EAAE,MAAM,EAAE,EACX,CAAC,EAAE,MAAM,EAAE,EACX,YAAY,GAAE,OAAe,GAC5B,KAAK,GAAG,MAAM,EAAE;IA+CnB,WAAW;IAqBX,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,OAAO,EAAE,WAAW,EAAE,EAAE,OAAO,GAAE,OAAe;IAiNzE,WAAW;IAIX,KAAK,CAAC,OAAO,EAAE,MAAM,GAAG,WAAW;IA6CnC,MAAM;IAsFN,UAAU,CAAC,CAAC,EAAE,MAAM;IAepB,KAAK,CAAC,CAAC,EAAE,MAAM,EAAE,OAAO,UAAe;IAiEvC,MAAM,CAAC,QAAQ,CAAC,GAAG,EAAE,gBAAgB;CAGtC;AAED,OAAO,EAAE,GAAG,EAAE,MAAM,UAAU,CAAA;AAC9B,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAA;AACpC,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAA"}

View file

@ -0,0 +1,995 @@
import expand from 'brace-expansion';
import { assertValidPattern } from './assert-valid-pattern.js';
import { AST } from './ast.js';
import { escape } from './escape.js';
import { unescape } from './unescape.js';
export const minimatch = (p, pattern, options = {}) => {
assertValidPattern(pattern);
// shortcut: comments match nothing.
if (!options.nocomment && pattern.charAt(0) === '#') {
return false;
}
return new Minimatch(pattern, options).match(p);
};
// Optimized checking for the most common glob patterns.
const starDotExtRE = /^\*+([^+@!?\*\[\(]*)$/;
const starDotExtTest = (ext) => (f) => !f.startsWith('.') && f.endsWith(ext);
const starDotExtTestDot = (ext) => (f) => f.endsWith(ext);
const starDotExtTestNocase = (ext) => {
ext = ext.toLowerCase();
return (f) => !f.startsWith('.') && f.toLowerCase().endsWith(ext);
};
const starDotExtTestNocaseDot = (ext) => {
ext = ext.toLowerCase();
return (f) => f.toLowerCase().endsWith(ext);
};
const starDotStarRE = /^\*+\.\*+$/;
const starDotStarTest = (f) => !f.startsWith('.') && f.includes('.');
const starDotStarTestDot = (f) => f !== '.' && f !== '..' && f.includes('.');
const dotStarRE = /^\.\*+$/;
const dotStarTest = (f) => f !== '.' && f !== '..' && f.startsWith('.');
const starRE = /^\*+$/;
const starTest = (f) => f.length !== 0 && !f.startsWith('.');
const starTestDot = (f) => f.length !== 0 && f !== '.' && f !== '..';
const qmarksRE = /^\?+([^+@!?\*\[\(]*)?$/;
const qmarksTestNocase = ([$0, ext = '']) => {
const noext = qmarksTestNoExt([$0]);
if (!ext)
return noext;
ext = ext.toLowerCase();
return (f) => noext(f) && f.toLowerCase().endsWith(ext);
};
const qmarksTestNocaseDot = ([$0, ext = '']) => {
const noext = qmarksTestNoExtDot([$0]);
if (!ext)
return noext;
ext = ext.toLowerCase();
return (f) => noext(f) && f.toLowerCase().endsWith(ext);
};
const qmarksTestDot = ([$0, ext = '']) => {
const noext = qmarksTestNoExtDot([$0]);
return !ext ? noext : (f) => noext(f) && f.endsWith(ext);
};
const qmarksTest = ([$0, ext = '']) => {
const noext = qmarksTestNoExt([$0]);
return !ext ? noext : (f) => noext(f) && f.endsWith(ext);
};
const qmarksTestNoExt = ([$0]) => {
const len = $0.length;
return (f) => f.length === len && !f.startsWith('.');
};
const qmarksTestNoExtDot = ([$0]) => {
const len = $0.length;
return (f) => f.length === len && f !== '.' && f !== '..';
};
/* c8 ignore start */
const defaultPlatform = (typeof process === 'object' && process
? (typeof process.env === 'object' &&
process.env &&
process.env.__MINIMATCH_TESTING_PLATFORM__) ||
process.platform
: 'posix');
const path = {
win32: { sep: '\\' },
posix: { sep: '/' },
};
/* c8 ignore stop */
export const sep = defaultPlatform === 'win32' ? path.win32.sep : path.posix.sep;
minimatch.sep = sep;
export const GLOBSTAR = Symbol('globstar **');
minimatch.GLOBSTAR = GLOBSTAR;
// any single thing other than /
// don't need to escape / when using new RegExp()
const qmark = '[^/]';
// * => any number of characters
const star = qmark + '*?';
// ** when dots are allowed. Anything goes, except .. and .
// not (^ or / followed by one or two dots followed by $ or /),
// followed by anything, any number of times.
const twoStarDot = '(?:(?!(?:\\/|^)(?:\\.{1,2})($|\\/)).)*?';
// not a ^ or / followed by a dot,
// followed by anything, any number of times.
const twoStarNoDot = '(?:(?!(?:\\/|^)\\.).)*?';
export const filter = (pattern, options = {}) => (p) => minimatch(p, pattern, options);
minimatch.filter = filter;
const ext = (a, b = {}) => Object.assign({}, a, b);
export const defaults = (def) => {
if (!def || typeof def !== 'object' || !Object.keys(def).length) {
return minimatch;
}
const orig = minimatch;
const m = (p, pattern, options = {}) => orig(p, pattern, ext(def, options));
return Object.assign(m, {
Minimatch: class Minimatch extends orig.Minimatch {
constructor(pattern, options = {}) {
super(pattern, ext(def, options));
}
static defaults(options) {
return orig.defaults(ext(def, options)).Minimatch;
}
},
AST: class AST extends orig.AST {
/* c8 ignore start */
constructor(type, parent, options = {}) {
super(type, parent, ext(def, options));
}
/* c8 ignore stop */
static fromGlob(pattern, options = {}) {
return orig.AST.fromGlob(pattern, ext(def, options));
}
},
unescape: (s, options = {}) => orig.unescape(s, ext(def, options)),
escape: (s, options = {}) => orig.escape(s, ext(def, options)),
filter: (pattern, options = {}) => orig.filter(pattern, ext(def, options)),
defaults: (options) => orig.defaults(ext(def, options)),
makeRe: (pattern, options = {}) => orig.makeRe(pattern, ext(def, options)),
braceExpand: (pattern, options = {}) => orig.braceExpand(pattern, ext(def, options)),
match: (list, pattern, options = {}) => orig.match(list, pattern, ext(def, options)),
sep: orig.sep,
GLOBSTAR: GLOBSTAR,
});
};
minimatch.defaults = defaults;
// Brace expansion:
// a{b,c}d -> abd acd
// a{b,}c -> abc ac
// a{0..3}d -> a0d a1d a2d a3d
// a{b,c{d,e}f}g -> abg acdfg acefg
// a{b,c}d{e,f}g -> abdeg acdeg abdeg abdfg
//
// Invalid sets are not expanded.
// a{2..}b -> a{2..}b
// a{b}c -> a{b}c
export const braceExpand = (pattern, options = {}) => {
assertValidPattern(pattern);
// Thanks to Yeting Li <https://github.com/yetingli> for
// improving this regexp to avoid a ReDOS vulnerability.
if (options.nobrace || !/\{(?:(?!\{).)*\}/.test(pattern)) {
// shortcut. no need to expand.
return [pattern];
}
return expand(pattern);
};
minimatch.braceExpand = braceExpand;
// parse a component of the expanded set.
// At this point, no pattern may contain "/" in it
// so we're going to return a 2d array, where each entry is the full
// pattern, split on '/', and then turned into a regular expression.
// A regexp is made at the end which joins each array with an
// escaped /, and another full one which joins each regexp with |.
//
// Following the lead of Bash 4.1, note that "**" only has special meaning
// when it is the *only* thing in a path portion. Otherwise, any series
// of * is equivalent to a single *. Globstar behavior is enabled by
// default, and can be disabled by setting options.noglobstar.
export const makeRe = (pattern, options = {}) => new Minimatch(pattern, options).makeRe();
minimatch.makeRe = makeRe;
export const match = (list, pattern, options = {}) => {
const mm = new Minimatch(pattern, options);
list = list.filter(f => mm.match(f));
if (mm.options.nonull && !list.length) {
list.push(pattern);
}
return list;
};
minimatch.match = match;
// replace stuff like \* with *
const globMagic = /[?*]|[+@!]\(.*?\)|\[|\]/;
const regExpEscape = (s) => s.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, '\\$&');
export class Minimatch {
options;
set;
pattern;
windowsPathsNoEscape;
nonegate;
negate;
comment;
empty;
preserveMultipleSlashes;
partial;
globSet;
globParts;
nocase;
isWindows;
platform;
windowsNoMagicRoot;
regexp;
constructor(pattern, options = {}) {
assertValidPattern(pattern);
options = options || {};
this.options = options;
this.pattern = pattern;
this.platform = options.platform || defaultPlatform;
this.isWindows = this.platform === 'win32';
this.windowsPathsNoEscape =
!!options.windowsPathsNoEscape || options.allowWindowsEscape === false;
if (this.windowsPathsNoEscape) {
this.pattern = this.pattern.replace(/\\/g, '/');
}
this.preserveMultipleSlashes = !!options.preserveMultipleSlashes;
this.regexp = null;
this.negate = false;
this.nonegate = !!options.nonegate;
this.comment = false;
this.empty = false;
this.partial = !!options.partial;
this.nocase = !!this.options.nocase;
this.windowsNoMagicRoot =
options.windowsNoMagicRoot !== undefined
? options.windowsNoMagicRoot
: !!(this.isWindows && this.nocase);
this.globSet = [];
this.globParts = [];
this.set = [];
// make the set of regexps etc.
this.make();
}
hasMagic() {
if (this.options.magicalBraces && this.set.length > 1) {
return true;
}
for (const pattern of this.set) {
for (const part of pattern) {
if (typeof part !== 'string')
return true;
}
}
return false;
}
debug(..._) { }
make() {
const pattern = this.pattern;
const options = this.options;
// empty patterns and comments match nothing.
if (!options.nocomment && pattern.charAt(0) === '#') {
this.comment = true;
return;
}
if (!pattern) {
this.empty = true;
return;
}
// step 1: figure out negation, etc.
this.parseNegate();
// step 2: expand braces
this.globSet = [...new Set(this.braceExpand())];
if (options.debug) {
this.debug = (...args) => console.error(...args);
}
this.debug(this.pattern, this.globSet);
// step 3: now we have a set, so turn each one into a series of
// path-portion matching patterns.
// These will be regexps, except in the case of "**", which is
// set to the GLOBSTAR object for globstar behavior,
// and will not contain any / characters
//
// First, we preprocess to make the glob pattern sets a bit simpler
// and deduped. There are some perf-killing patterns that can cause
// problems with a glob walk, but we can simplify them down a bit.
const rawGlobParts = this.globSet.map(s => this.slashSplit(s));
this.globParts = this.preprocess(rawGlobParts);
this.debug(this.pattern, this.globParts);
// glob --> regexps
let set = this.globParts.map((s, _, __) => {
if (this.isWindows && this.windowsNoMagicRoot) {
// check if it's a drive or unc path.
const isUNC = s[0] === '' &&
s[1] === '' &&
(s[2] === '?' || !globMagic.test(s[2])) &&
!globMagic.test(s[3]);
const isDrive = /^[a-z]:/i.test(s[0]);
if (isUNC) {
return [...s.slice(0, 4), ...s.slice(4).map(ss => this.parse(ss))];
}
else if (isDrive) {
return [s[0], ...s.slice(1).map(ss => this.parse(ss))];
}
}
return s.map(ss => this.parse(ss));
});
this.debug(this.pattern, set);
// filter out everything that didn't compile properly.
this.set = set.filter(s => s.indexOf(false) === -1);
// do not treat the ? in UNC paths as magic
if (this.isWindows) {
for (let i = 0; i < this.set.length; i++) {
const p = this.set[i];
if (p[0] === '' &&
p[1] === '' &&
this.globParts[i][2] === '?' &&
typeof p[3] === 'string' &&
/^[a-z]:$/i.test(p[3])) {
p[2] = '?';
}
}
}
this.debug(this.pattern, this.set);
}
// various transforms to equivalent pattern sets that are
// faster to process in a filesystem walk. The goal is to
// eliminate what we can, and push all ** patterns as far
// to the right as possible, even if it increases the number
// of patterns that we have to process.
preprocess(globParts) {
// if we're not in globstar mode, then turn all ** into *
if (this.options.noglobstar) {
for (let i = 0; i < globParts.length; i++) {
for (let j = 0; j < globParts[i].length; j++) {
if (globParts[i][j] === '**') {
globParts[i][j] = '*';
}
}
}
}
const { optimizationLevel = 1 } = this.options;
if (optimizationLevel >= 2) {
// aggressive optimization for the purpose of fs walking
globParts = this.firstPhasePreProcess(globParts);
globParts = this.secondPhasePreProcess(globParts);
}
else if (optimizationLevel >= 1) {
// just basic optimizations to remove some .. parts
globParts = this.levelOneOptimize(globParts);
}
else {
globParts = this.adjascentGlobstarOptimize(globParts);
}
return globParts;
}
// just get rid of adjascent ** portions
adjascentGlobstarOptimize(globParts) {
return globParts.map(parts => {
let gs = -1;
while (-1 !== (gs = parts.indexOf('**', gs + 1))) {
let i = gs;
while (parts[i + 1] === '**') {
i++;
}
if (i !== gs) {
parts.splice(gs, i - gs);
}
}
return parts;
});
}
// get rid of adjascent ** and resolve .. portions
levelOneOptimize(globParts) {
return globParts.map(parts => {
parts = parts.reduce((set, part) => {
const prev = set[set.length - 1];
if (part === '**' && prev === '**') {
return set;
}
if (part === '..') {
if (prev && prev !== '..' && prev !== '.' && prev !== '**') {
set.pop();
return set;
}
}
set.push(part);
return set;
}, []);
return parts.length === 0 ? [''] : parts;
});
}
levelTwoFileOptimize(parts) {
if (!Array.isArray(parts)) {
parts = this.slashSplit(parts);
}
let didSomething = false;
do {
didSomething = false;
// <pre>/<e>/<rest> -> <pre>/<rest>
if (!this.preserveMultipleSlashes) {
for (let i = 1; i < parts.length - 1; i++) {
const p = parts[i];
// don't squeeze out UNC patterns
if (i === 1 && p === '' && parts[0] === '')
continue;
if (p === '.' || p === '') {
didSomething = true;
parts.splice(i, 1);
i--;
}
}
if (parts[0] === '.' &&
parts.length === 2 &&
(parts[1] === '.' || parts[1] === '')) {
didSomething = true;
parts.pop();
}
}
// <pre>/<p>/../<rest> -> <pre>/<rest>
let dd = 0;
while (-1 !== (dd = parts.indexOf('..', dd + 1))) {
const p = parts[dd - 1];
if (p && p !== '.' && p !== '..' && p !== '**') {
didSomething = true;
parts.splice(dd - 1, 2);
dd -= 2;
}
}
} while (didSomething);
return parts.length === 0 ? [''] : parts;
}
// First phase: single-pattern processing
// <pre> is 1 or more portions
// <rest> is 1 or more portions
// <p> is any portion other than ., .., '', or **
// <e> is . or ''
//
// **/.. is *brutal* for filesystem walking performance, because
// it effectively resets the recursive walk each time it occurs,
// and ** cannot be reduced out by a .. pattern part like a regexp
// or most strings (other than .., ., and '') can be.
//
// <pre>/**/../<p>/<p>/<rest> -> {<pre>/../<p>/<p>/<rest>,<pre>/**/<p>/<p>/<rest>}
// <pre>/<e>/<rest> -> <pre>/<rest>
// <pre>/<p>/../<rest> -> <pre>/<rest>
// **/**/<rest> -> **/<rest>
//
// **/*/<rest> -> */**/<rest> <== not valid because ** doesn't follow
// this WOULD be allowed if ** did follow symlinks, or * didn't
firstPhasePreProcess(globParts) {
let didSomething = false;
do {
didSomething = false;
// <pre>/**/../<p>/<p>/<rest> -> {<pre>/../<p>/<p>/<rest>,<pre>/**/<p>/<p>/<rest>}
for (let parts of globParts) {
let gs = -1;
while (-1 !== (gs = parts.indexOf('**', gs + 1))) {
let gss = gs;
while (parts[gss + 1] === '**') {
// <pre>/**/**/<rest> -> <pre>/**/<rest>
gss++;
}
// eg, if gs is 2 and gss is 4, that means we have 3 **
// parts, and can remove 2 of them.
if (gss > gs) {
parts.splice(gs + 1, gss - gs);
}
let next = parts[gs + 1];
const p = parts[gs + 2];
const p2 = parts[gs + 3];
if (next !== '..')
continue;
if (!p ||
p === '.' ||
p === '..' ||
!p2 ||
p2 === '.' ||
p2 === '..') {
continue;
}
didSomething = true;
// edit parts in place, and push the new one
parts.splice(gs, 1);
const other = parts.slice(0);
other[gs] = '**';
globParts.push(other);
gs--;
}
// <pre>/<e>/<rest> -> <pre>/<rest>
if (!this.preserveMultipleSlashes) {
for (let i = 1; i < parts.length - 1; i++) {
const p = parts[i];
// don't squeeze out UNC patterns
if (i === 1 && p === '' && parts[0] === '')
continue;
if (p === '.' || p === '') {
didSomething = true;
parts.splice(i, 1);
i--;
}
}
if (parts[0] === '.' &&
parts.length === 2 &&
(parts[1] === '.' || parts[1] === '')) {
didSomething = true;
parts.pop();
}
}
// <pre>/<p>/../<rest> -> <pre>/<rest>
let dd = 0;
while (-1 !== (dd = parts.indexOf('..', dd + 1))) {
const p = parts[dd - 1];
if (p && p !== '.' && p !== '..' && p !== '**') {
didSomething = true;
const needDot = dd === 1 && parts[dd + 1] === '**';
const splin = needDot ? ['.'] : [];
parts.splice(dd - 1, 2, ...splin);
if (parts.length === 0)
parts.push('');
dd -= 2;
}
}
}
} while (didSomething);
return globParts;
}
// second phase: multi-pattern dedupes
// {<pre>/*/<rest>,<pre>/<p>/<rest>} -> <pre>/*/<rest>
// {<pre>/<rest>,<pre>/<rest>} -> <pre>/<rest>
// {<pre>/**/<rest>,<pre>/<rest>} -> <pre>/**/<rest>
//
// {<pre>/**/<rest>,<pre>/**/<p>/<rest>} -> <pre>/**/<rest>
// ^-- not valid because ** doens't follow symlinks
secondPhasePreProcess(globParts) {
for (let i = 0; i < globParts.length - 1; i++) {
for (let j = i + 1; j < globParts.length; j++) {
const matched = this.partsMatch(globParts[i], globParts[j], !this.preserveMultipleSlashes);
if (!matched)
continue;
globParts[i] = matched;
globParts[j] = [];
}
}
return globParts.filter(gs => gs.length);
}
partsMatch(a, b, emptyGSMatch = false) {
let ai = 0;
let bi = 0;
let result = [];
let which = '';
while (ai < a.length && bi < b.length) {
if (a[ai] === b[bi]) {
result.push(which === 'b' ? b[bi] : a[ai]);
ai++;
bi++;
}
else if (emptyGSMatch && a[ai] === '**' && b[bi] === a[ai + 1]) {
result.push(a[ai]);
ai++;
}
else if (emptyGSMatch && b[bi] === '**' && a[ai] === b[bi + 1]) {
result.push(b[bi]);
bi++;
}
else if (a[ai] === '*' &&
b[bi] &&
(this.options.dot || !b[bi].startsWith('.')) &&
b[bi] !== '**') {
if (which === 'b')
return false;
which = 'a';
result.push(a[ai]);
ai++;
bi++;
}
else if (b[bi] === '*' &&
a[ai] &&
(this.options.dot || !a[ai].startsWith('.')) &&
a[ai] !== '**') {
if (which === 'a')
return false;
which = 'b';
result.push(b[bi]);
ai++;
bi++;
}
else {
return false;
}
}
// if we fall out of the loop, it means they two are identical
// as long as their lengths match
return a.length === b.length && result;
}
parseNegate() {
if (this.nonegate)
return;
const pattern = this.pattern;
let negate = false;
let negateOffset = 0;
for (let i = 0; i < pattern.length && pattern.charAt(i) === '!'; i++) {
negate = !negate;
negateOffset++;
}
if (negateOffset)
this.pattern = pattern.slice(negateOffset);
this.negate = negate;
}
// set partial to true to test if, for example,
// "/a/b" matches the start of "/*/b/*/d"
// Partial means, if you run out of file before you run
// out of pattern, then that's fine, as long as all
// the parts match.
matchOne(file, pattern, partial = false) {
const options = this.options;
// UNC paths like //?/X:/... can match X:/... and vice versa
// Drive letters in absolute drive or unc paths are always compared
// case-insensitively.
if (this.isWindows) {
const fileDrive = typeof file[0] === 'string' && /^[a-z]:$/i.test(file[0]);
const fileUNC = !fileDrive &&
file[0] === '' &&
file[1] === '' &&
file[2] === '?' &&
/^[a-z]:$/i.test(file[3]);
const patternDrive = typeof pattern[0] === 'string' && /^[a-z]:$/i.test(pattern[0]);
const patternUNC = !patternDrive &&
pattern[0] === '' &&
pattern[1] === '' &&
pattern[2] === '?' &&
typeof pattern[3] === 'string' &&
/^[a-z]:$/i.test(pattern[3]);
const fdi = fileUNC ? 3 : fileDrive ? 0 : undefined;
const pdi = patternUNC ? 3 : patternDrive ? 0 : undefined;
if (typeof fdi === 'number' && typeof pdi === 'number') {
const [fd, pd] = [file[fdi], pattern[pdi]];
if (fd.toLowerCase() === pd.toLowerCase()) {
pattern[pdi] = fd;
if (pdi > fdi) {
pattern = pattern.slice(pdi);
}
else if (fdi > pdi) {
file = file.slice(fdi);
}
}
}
}
// resolve and reduce . and .. portions in the file as well.
// dont' need to do the second phase, because it's only one string[]
const { optimizationLevel = 1 } = this.options;
if (optimizationLevel >= 2) {
file = this.levelTwoFileOptimize(file);
}
this.debug('matchOne', this, { file, pattern });
this.debug('matchOne', file.length, pattern.length);
for (var fi = 0, pi = 0, fl = file.length, pl = pattern.length; fi < fl && pi < pl; fi++, pi++) {
this.debug('matchOne loop');
var p = pattern[pi];
var f = file[fi];
this.debug(pattern, p, f);
// should be impossible.
// some invalid regexp stuff in the set.
/* c8 ignore start */
if (p === false) {
return false;
}
/* c8 ignore stop */
if (p === GLOBSTAR) {
this.debug('GLOBSTAR', [pattern, p, f]);
// "**"
// a/**/b/**/c would match the following:
// a/b/x/y/z/c
// a/x/y/z/b/c
// a/b/x/b/x/c
// a/b/c
// To do this, take the rest of the pattern after
// the **, and see if it would match the file remainder.
// If so, return success.
// If not, the ** "swallows" a segment, and try again.
// This is recursively awful.
//
// a/**/b/**/c matching a/b/x/y/z/c
// - a matches a
// - doublestar
// - matchOne(b/x/y/z/c, b/**/c)
// - b matches b
// - doublestar
// - matchOne(x/y/z/c, c) -> no
// - matchOne(y/z/c, c) -> no
// - matchOne(z/c, c) -> no
// - matchOne(c, c) yes, hit
var fr = fi;
var pr = pi + 1;
if (pr === pl) {
this.debug('** at the end');
// a ** at the end will just swallow the rest.
// We have found a match.
// however, it will not swallow /.x, unless
// options.dot is set.
// . and .. are *never* matched by **, for explosively
// exponential reasons.
for (; fi < fl; fi++) {
if (file[fi] === '.' ||
file[fi] === '..' ||
(!options.dot && file[fi].charAt(0) === '.'))
return false;
}
return true;
}
// ok, let's see if we can swallow whatever we can.
while (fr < fl) {
var swallowee = file[fr];
this.debug('\nglobstar while', file, fr, pattern, pr, swallowee);
// XXX remove this slice. Just pass the start index.
if (this.matchOne(file.slice(fr), pattern.slice(pr), partial)) {
this.debug('globstar found match!', fr, fl, swallowee);
// found a match.
return true;
}
else {
// can't swallow "." or ".." ever.
// can only swallow ".foo" when explicitly asked.
if (swallowee === '.' ||
swallowee === '..' ||
(!options.dot && swallowee.charAt(0) === '.')) {
this.debug('dot detected!', file, fr, pattern, pr);
break;
}
// ** swallows a segment, and continue.
this.debug('globstar swallow a segment, and continue');
fr++;
}
}
// no match was found.
// However, in partial mode, we can't say this is necessarily over.
/* c8 ignore start */
if (partial) {
// ran out of file
this.debug('\n>>> no match, partial?', file, fr, pattern, pr);
if (fr === fl) {
return true;
}
}
/* c8 ignore stop */
return false;
}
// something other than **
// non-magic patterns just have to match exactly
// patterns with magic have been turned into regexps.
let hit;
if (typeof p === 'string') {
hit = f === p;
this.debug('string match', p, f, hit);
}
else {
hit = p.test(f);
this.debug('pattern match', p, f, hit);
}
if (!hit)
return false;
}
// Note: ending in / means that we'll get a final ""
// at the end of the pattern. This can only match a
// corresponding "" at the end of the file.
// If the file ends in /, then it can only match a
// a pattern that ends in /, unless the pattern just
// doesn't have any more for it. But, a/b/ should *not*
// match "a/b/*", even though "" matches against the
// [^/]*? pattern, except in partial mode, where it might
// simply not be reached yet.
// However, a/b/ should still satisfy a/*
// now either we fell off the end of the pattern, or we're done.
if (fi === fl && pi === pl) {
// ran out of pattern and filename at the same time.
// an exact hit!
return true;
}
else if (fi === fl) {
// ran out of file, but still had pattern left.
// this is ok if we're doing the match as part of
// a glob fs traversal.
return partial;
}
else if (pi === pl) {
// ran out of pattern, still have file left.
// this is only acceptable if we're on the very last
// empty segment of a file with a trailing slash.
// a/* should match a/b/
return fi === fl - 1 && file[fi] === '';
/* c8 ignore start */
}
else {
// should be unreachable.
throw new Error('wtf?');
}
/* c8 ignore stop */
}
braceExpand() {
return braceExpand(this.pattern, this.options);
}
parse(pattern) {
assertValidPattern(pattern);
const options = this.options;
// shortcuts
if (pattern === '**')
return GLOBSTAR;
if (pattern === '')
return '';
// far and away, the most common glob pattern parts are
// *, *.*, and *.<ext> Add a fast check method for those.
let m;
let fastTest = null;
if ((m = pattern.match(starRE))) {
fastTest = options.dot ? starTestDot : starTest;
}
else if ((m = pattern.match(starDotExtRE))) {
fastTest = (options.nocase
? options.dot
? starDotExtTestNocaseDot
: starDotExtTestNocase
: options.dot
? starDotExtTestDot
: starDotExtTest)(m[1]);
}
else if ((m = pattern.match(qmarksRE))) {
fastTest = (options.nocase
? options.dot
? qmarksTestNocaseDot
: qmarksTestNocase
: options.dot
? qmarksTestDot
: qmarksTest)(m);
}
else if ((m = pattern.match(starDotStarRE))) {
fastTest = options.dot ? starDotStarTestDot : starDotStarTest;
}
else if ((m = pattern.match(dotStarRE))) {
fastTest = dotStarTest;
}
const re = AST.fromGlob(pattern, this.options).toMMPattern();
return fastTest ? Object.assign(re, { test: fastTest }) : re;
}
makeRe() {
if (this.regexp || this.regexp === false)
return this.regexp;
// at this point, this.set is a 2d array of partial
// pattern strings, or "**".
//
// It's better to use .match(). This function shouldn't
// be used, really, but it's pretty convenient sometimes,
// when you just want to work with a regex.
const set = this.set;
if (!set.length) {
this.regexp = false;
return this.regexp;
}
const options = this.options;
const twoStar = options.noglobstar
? star
: options.dot
? twoStarDot
: twoStarNoDot;
const flags = new Set(options.nocase ? ['i'] : []);
// regexpify non-globstar patterns
// if ** is only item, then we just do one twoStar
// if ** is first, and there are more, prepend (\/|twoStar\/)? to next
// if ** is last, append (\/twoStar|) to previous
// if ** is in the middle, append (\/|\/twoStar\/) to previous
// then filter out GLOBSTAR symbols
let re = set
.map(pattern => {
const pp = pattern.map(p => {
if (p instanceof RegExp) {
for (const f of p.flags.split(''))
flags.add(f);
}
return typeof p === 'string'
? regExpEscape(p)
: p === GLOBSTAR
? GLOBSTAR
: p._src;
});
pp.forEach((p, i) => {
const next = pp[i + 1];
const prev = pp[i - 1];
if (p !== GLOBSTAR || prev === GLOBSTAR) {
return;
}
if (prev === undefined) {
if (next !== undefined && next !== GLOBSTAR) {
pp[i + 1] = '(?:\\/|' + twoStar + '\\/)?' + next;
}
else {
pp[i] = twoStar;
}
}
else if (next === undefined) {
pp[i - 1] = prev + '(?:\\/|' + twoStar + ')?';
}
else if (next !== GLOBSTAR) {
pp[i - 1] = prev + '(?:\\/|\\/' + twoStar + '\\/)' + next;
pp[i + 1] = GLOBSTAR;
}
});
return pp.filter(p => p !== GLOBSTAR).join('/');
})
.join('|');
// need to wrap in parens if we had more than one thing with |,
// otherwise only the first will be anchored to ^ and the last to $
const [open, close] = set.length > 1 ? ['(?:', ')'] : ['', ''];
// must match entire pattern
// ending in a * or ** will make it less strict.
re = '^' + open + re + close + '$';
// can match anything, as long as it's not this.
if (this.negate)
re = '^(?!' + re + ').+$';
try {
this.regexp = new RegExp(re, [...flags].join(''));
/* c8 ignore start */
}
catch (ex) {
// should be impossible
this.regexp = false;
}
/* c8 ignore stop */
return this.regexp;
}
slashSplit(p) {
// if p starts with // on windows, we preserve that
// so that UNC paths aren't broken. Otherwise, any number of
// / characters are coalesced into one, unless
// preserveMultipleSlashes is set to true.
if (this.preserveMultipleSlashes) {
return p.split('/');
}
else if (this.isWindows && /^\/\/[^\/]+/.test(p)) {
// add an extra '' for the one we lose
return ['', ...p.split(/\/+/)];
}
else {
return p.split(/\/+/);
}
}
match(f, partial = this.partial) {
this.debug('match', f, this.pattern);
// short-circuit in the case of busted things.
// comments, etc.
if (this.comment) {
return false;
}
if (this.empty) {
return f === '';
}
if (f === '/' && partial) {
return true;
}
const options = this.options;
// windows: need to use /, not \
if (this.isWindows) {
f = f.split('\\').join('/');
}
// treat the test path as a set of pathparts.
const ff = this.slashSplit(f);
this.debug(this.pattern, 'split', ff);
// just ONE of the pattern sets in this.set needs to match
// in order for it to be valid. If negating, then just one
// match means that we have failed.
// Either way, return on the first hit.
const set = this.set;
this.debug(this.pattern, 'set', set);
// Find the basename of the path by looking for the last non-empty segment
let filename = ff[ff.length - 1];
if (!filename) {
for (let i = ff.length - 2; !filename && i >= 0; i--) {
filename = ff[i];
}
}
for (let i = 0; i < set.length; i++) {
const pattern = set[i];
let file = ff;
if (options.matchBase && pattern.length === 1) {
file = [filename];
}
const hit = this.matchOne(file, pattern, partial);
if (hit) {
if (options.flipNegate) {
return true;
}
return !this.negate;
}
}
// didn't get any hits. this is success if it's a negative
// pattern, failure otherwise.
if (options.flipNegate) {
return false;
}
return this.negate;
}
static defaults(def) {
return minimatch.defaults(def).Minimatch;
}
}
/* c8 ignore start */
export { AST } from './ast.js';
export { escape } from './escape.js';
export { unescape } from './unescape.js';
/* c8 ignore stop */
minimatch.AST = AST;
minimatch.Minimatch = Minimatch;
minimatch.escape = escape;
minimatch.unescape = unescape;
//# sourceMappingURL=index.js.map

File diff suppressed because one or more lines are too long

View file

@ -0,0 +1,3 @@
{
"type": "module"
}

View file

@ -0,0 +1,17 @@
import { MinimatchOptions } from './index.js';
/**
* Un-escape a string that has been escaped with {@link escape}.
*
* If the {@link windowsPathsNoEscape} option is used, then square-brace
* escapes are removed, but not backslash escapes. For example, it will turn
* the string `'[*]'` into `*`, but it will not turn `'\\*'` into `'*'`,
* becuase `\` is a path separator in `windowsPathsNoEscape` mode.
*
* When `windowsPathsNoEscape` is not set, then both brace escapes and
* backslash escapes are removed.
*
* Slashes (and backslashes in `windowsPathsNoEscape` mode) cannot be escaped
* or unescaped.
*/
export declare const unescape: (s: string, { windowsPathsNoEscape, }?: Pick<MinimatchOptions, 'windowsPathsNoEscape'>) => string;
//# sourceMappingURL=unescape.d.ts.map

View file

@ -0,0 +1 @@
{"version":3,"file":"unescape.d.ts","sourceRoot":"","sources":["../../src/unescape.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAA;AAC7C;;;;;;;;;;;;;GAaG;AACH,eAAO,MAAM,QAAQ,MAChB,MAAM,8BAGN,KAAK,gBAAgB,EAAE,sBAAsB,CAAC,WAKlD,CAAA"}

Some files were not shown because too many files have changed in this diff Show more