@pattern-algebra/core 0.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +571 -0
- package/dist/automaton/complement.d.ts +20 -0
- package/dist/automaton/complement.d.ts.map +1 -0
- package/dist/automaton/complement.js +36 -0
- package/dist/automaton/complement.js.map +1 -0
- package/dist/automaton/complement.test.d.ts +2 -0
- package/dist/automaton/complement.test.d.ts.map +1 -0
- package/dist/automaton/complement.test.js +114 -0
- package/dist/automaton/complement.test.js.map +1 -0
- package/dist/automaton/determinize.d.ts +41 -0
- package/dist/automaton/determinize.d.ts.map +1 -0
- package/dist/automaton/determinize.js +310 -0
- package/dist/automaton/determinize.js.map +1 -0
- package/dist/automaton/determinize.test.d.ts +2 -0
- package/dist/automaton/determinize.test.d.ts.map +1 -0
- package/dist/automaton/determinize.test.js +134 -0
- package/dist/automaton/determinize.test.js.map +1 -0
- package/dist/automaton/emptiness.d.ts +41 -0
- package/dist/automaton/emptiness.d.ts.map +1 -0
- package/dist/automaton/emptiness.js +262 -0
- package/dist/automaton/emptiness.js.map +1 -0
- package/dist/automaton/emptiness.test.d.ts +2 -0
- package/dist/automaton/emptiness.test.d.ts.map +1 -0
- package/dist/automaton/emptiness.test.js +154 -0
- package/dist/automaton/emptiness.test.js.map +1 -0
- package/dist/automaton/index.d.ts +10 -0
- package/dist/automaton/index.d.ts.map +1 -0
- package/dist/automaton/index.js +11 -0
- package/dist/automaton/index.js.map +1 -0
- package/dist/automaton/intersect.d.ts +35 -0
- package/dist/automaton/intersect.d.ts.map +1 -0
- package/dist/automaton/intersect.js +302 -0
- package/dist/automaton/intersect.js.map +1 -0
- package/dist/automaton/pattern-algebra.d.ts +62 -0
- package/dist/automaton/pattern-algebra.d.ts.map +1 -0
- package/dist/automaton/pattern-algebra.js +309 -0
- package/dist/automaton/pattern-algebra.js.map +1 -0
- package/dist/automaton/pattern-algebra.test.d.ts +2 -0
- package/dist/automaton/pattern-algebra.test.d.ts.map +1 -0
- package/dist/automaton/pattern-algebra.test.js +223 -0
- package/dist/automaton/pattern-algebra.test.js.map +1 -0
- package/dist/compile/automaton-builder.d.ts +47 -0
- package/dist/compile/automaton-builder.d.ts.map +1 -0
- package/dist/compile/automaton-builder.js +211 -0
- package/dist/compile/automaton-builder.js.map +1 -0
- package/dist/compile/compiler.d.ts +32 -0
- package/dist/compile/compiler.d.ts.map +1 -0
- package/dist/compile/compiler.js +47 -0
- package/dist/compile/compiler.js.map +1 -0
- package/dist/compile/index.d.ts +8 -0
- package/dist/compile/index.d.ts.map +1 -0
- package/dist/compile/index.js +8 -0
- package/dist/compile/index.js.map +1 -0
- package/dist/compile/quick-reject.d.ts +28 -0
- package/dist/compile/quick-reject.d.ts.map +1 -0
- package/dist/compile/quick-reject.js +147 -0
- package/dist/compile/quick-reject.js.map +1 -0
- package/dist/containment/analysis.d.ts +60 -0
- package/dist/containment/analysis.d.ts.map +1 -0
- package/dist/containment/analysis.js +378 -0
- package/dist/containment/analysis.js.map +1 -0
- package/dist/containment/containment.d.ts +23 -0
- package/dist/containment/containment.d.ts.map +1 -0
- package/dist/containment/containment.js +681 -0
- package/dist/containment/containment.js.map +1 -0
- package/dist/containment/containment.test.d.ts +2 -0
- package/dist/containment/containment.test.d.ts.map +1 -0
- package/dist/containment/containment.test.js +209 -0
- package/dist/containment/containment.test.js.map +1 -0
- package/dist/containment/index.d.ts +7 -0
- package/dist/containment/index.d.ts.map +1 -0
- package/dist/containment/index.js +7 -0
- package/dist/containment/index.js.map +1 -0
- package/dist/core-alpha.d.ts +1253 -0
- package/dist/core-beta.d.ts +1253 -0
- package/dist/core-public.d.ts +1253 -0
- package/dist/core-unstripped.d.ts +1253 -0
- package/dist/index.d.ts +32 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +49 -0
- package/dist/index.js.map +1 -0
- package/dist/match/index.d.ts +8 -0
- package/dist/match/index.d.ts.map +1 -0
- package/dist/match/index.js +8 -0
- package/dist/match/index.js.map +1 -0
- package/dist/match/matcher.d.ts +40 -0
- package/dist/match/matcher.d.ts.map +1 -0
- package/dist/match/matcher.js +256 -0
- package/dist/match/matcher.js.map +1 -0
- package/dist/match/matcher.test.d.ts +2 -0
- package/dist/match/matcher.test.d.ts.map +1 -0
- package/dist/match/matcher.test.js +185 -0
- package/dist/match/matcher.test.js.map +1 -0
- package/dist/match/path-utils.d.ts +132 -0
- package/dist/match/path-utils.d.ts.map +1 -0
- package/dist/match/path-utils.js +223 -0
- package/dist/match/path-utils.js.map +1 -0
- package/dist/match/path-utils.test.d.ts +2 -0
- package/dist/match/path-utils.test.d.ts.map +1 -0
- package/dist/match/path-utils.test.js +193 -0
- package/dist/match/path-utils.test.js.map +1 -0
- package/dist/match/segment-matcher.d.ts +25 -0
- package/dist/match/segment-matcher.d.ts.map +1 -0
- package/dist/match/segment-matcher.js +267 -0
- package/dist/match/segment-matcher.js.map +1 -0
- package/dist/parse/brace-expansion.d.ts +34 -0
- package/dist/parse/brace-expansion.d.ts.map +1 -0
- package/dist/parse/brace-expansion.js +294 -0
- package/dist/parse/brace-expansion.js.map +1 -0
- package/dist/parse/brace-expansion.test.d.ts +2 -0
- package/dist/parse/brace-expansion.test.d.ts.map +1 -0
- package/dist/parse/brace-expansion.test.js +105 -0
- package/dist/parse/brace-expansion.test.js.map +1 -0
- package/dist/parse/index.d.ts +8 -0
- package/dist/parse/index.d.ts.map +1 -0
- package/dist/parse/index.js +8 -0
- package/dist/parse/index.js.map +1 -0
- package/dist/parse/parser.d.ts +15 -0
- package/dist/parse/parser.d.ts.map +1 -0
- package/dist/parse/parser.js +526 -0
- package/dist/parse/parser.js.map +1 -0
- package/dist/parse/parser.test.d.ts +2 -0
- package/dist/parse/parser.test.d.ts.map +1 -0
- package/dist/parse/parser.test.js +266 -0
- package/dist/parse/parser.test.js.map +1 -0
- package/dist/parse/validator.d.ts +30 -0
- package/dist/parse/validator.d.ts.map +1 -0
- package/dist/parse/validator.js +115 -0
- package/dist/parse/validator.js.map +1 -0
- package/dist/parse/validator.test.d.ts +2 -0
- package/dist/parse/validator.test.d.ts.map +1 -0
- package/dist/parse/validator.test.js +45 -0
- package/dist/parse/validator.test.js.map +1 -0
- package/dist/types/ast.d.ts +158 -0
- package/dist/types/ast.d.ts.map +1 -0
- package/dist/types/ast.js +2 -0
- package/dist/types/ast.js.map +1 -0
- package/dist/types/automaton.d.ts +150 -0
- package/dist/types/automaton.d.ts.map +1 -0
- package/dist/types/automaton.js +2 -0
- package/dist/types/automaton.js.map +1 -0
- package/dist/types/containment.d.ts +257 -0
- package/dist/types/containment.d.ts.map +1 -0
- package/dist/types/containment.js +5 -0
- package/dist/types/containment.js.map +1 -0
- package/dist/types/errors.d.ts +37 -0
- package/dist/types/errors.d.ts.map +1 -0
- package/dist/types/errors.js +24 -0
- package/dist/types/errors.js.map +1 -0
- package/dist/types/index.d.ts +10 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/index.js +6 -0
- package/dist/types/index.js.map +1 -0
- package/package.json +48 -0
package/README.md
ADDED
|
@@ -0,0 +1,571 @@
|
|
|
1
|
+
# @pattern-algebra/core
|
|
2
|
+
|
|
3
|
+
A TypeScript library for path pattern algebra - parsing, compiling, matching, and performing set operations on glob patterns. Designed for policy systems, build tools, and any application that needs to reason about path patterns mathematically.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- **Pattern Parsing**: Parse glob patterns with support for `*`, `**`, `?`, `[...]`, `{a,b}`, and negation
|
|
8
|
+
- **Pattern Matching**: Efficiently match file paths against compiled patterns
|
|
9
|
+
- **Pattern Algebra**: Perform set operations (intersection, union, complement, difference) on patterns
|
|
10
|
+
- **Containment Checking**: Determine if one pattern contains, overlaps with, or is disjoint from another
|
|
11
|
+
- **Path Utilities**: Normalize paths, extract segments, and perform common path operations
|
|
12
|
+
- **Type-Safe**: Written in TypeScript with comprehensive type definitions
|
|
13
|
+
|
|
14
|
+
## Installation
|
|
15
|
+
|
|
16
|
+
```bash
|
|
17
|
+
# pnpm
|
|
18
|
+
pnpm add @pattern-algebra/core
|
|
19
|
+
|
|
20
|
+
# npm
|
|
21
|
+
npm install @pattern-algebra/core
|
|
22
|
+
|
|
23
|
+
# yarn
|
|
24
|
+
yarn add @pattern-algebra/core
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
## Quick Start
|
|
28
|
+
|
|
29
|
+
```typescript
|
|
30
|
+
import {
|
|
31
|
+
parsePattern,
|
|
32
|
+
compilePattern,
|
|
33
|
+
matchPath,
|
|
34
|
+
patternIntersect,
|
|
35
|
+
patternUnion,
|
|
36
|
+
checkContainment,
|
|
37
|
+
} from '@pattern-algebra/core'
|
|
38
|
+
|
|
39
|
+
// Parse and compile a pattern
|
|
40
|
+
const pattern = compilePattern(parsePattern('src/**/*.ts'))
|
|
41
|
+
|
|
42
|
+
// Match paths against the pattern
|
|
43
|
+
matchPath('/src/index.ts', pattern) // true
|
|
44
|
+
matchPath('/src/utils/helper.ts', pattern) // true
|
|
45
|
+
matchPath('/lib/index.ts', pattern) // false
|
|
46
|
+
|
|
47
|
+
// Combine patterns with set operations
|
|
48
|
+
const srcFiles = compilePattern(parsePattern('src/**'))
|
|
49
|
+
const tsFiles = compilePattern(parsePattern('**/*.ts'))
|
|
50
|
+
|
|
51
|
+
// Intersection: files matching BOTH patterns
|
|
52
|
+
const srcTsFiles = patternIntersect(srcFiles, tsFiles)
|
|
53
|
+
matchPath('/src/index.ts', srcTsFiles) // true
|
|
54
|
+
matchPath('/lib/index.ts', srcTsFiles) // false
|
|
55
|
+
|
|
56
|
+
// Union: files matching EITHER pattern
|
|
57
|
+
const combined = patternUnion(compilePattern(parsePattern('**/*.js')), compilePattern(parsePattern('**/*.ts')))
|
|
58
|
+
matchPath('/src/index.js', combined) // true
|
|
59
|
+
matchPath('/src/index.ts', combined) // true
|
|
60
|
+
|
|
61
|
+
// Check containment relationships
|
|
62
|
+
const result = checkContainment(compilePattern(parsePattern('src/index.ts')), compilePattern(parsePattern('src/*.ts')))
|
|
63
|
+
result.isSubset // true
|
|
64
|
+
result.relationship // 'subset'
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
## Pattern Syntax
|
|
68
|
+
|
|
69
|
+
### Basic Wildcards
|
|
70
|
+
|
|
71
|
+
- `*` - Matches any characters within a single path segment (does not cross `/`)
|
|
72
|
+
|
|
73
|
+
```typescript
|
|
74
|
+
'*.ts' // matches: index.ts, helper.ts
|
|
75
|
+
// does NOT match: src/index.ts
|
|
76
|
+
|
|
77
|
+
'test-*-spec.js' // matches: test-unit-spec.js, test-integration-spec.js
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
- `?` - Matches exactly one character
|
|
81
|
+
|
|
82
|
+
```typescript
|
|
83
|
+
'file?.ts' // matches: file1.ts, fileA.ts
|
|
84
|
+
// does NOT match: file10.ts
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
- `**` - Globstar: matches zero or more path segments
|
|
88
|
+
|
|
89
|
+
```typescript
|
|
90
|
+
'src/**/*.ts' // matches: src/index.ts, src/lib/util/helper.ts
|
|
91
|
+
|
|
92
|
+
'**/*.test.ts' // matches: foo.test.ts, src/bar.test.ts, a/b/c/baz.test.ts
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
### Character Classes
|
|
96
|
+
|
|
97
|
+
- `[abc]` - Matches any character in the set
|
|
98
|
+
|
|
99
|
+
```typescript
|
|
100
|
+
'[aeiou]*.txt' // matches: apple.txt, index.txt
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
- `[a-z]` - Matches any character in the range
|
|
104
|
+
|
|
105
|
+
```typescript
|
|
106
|
+
'file[0-9].ts' // matches: file1.ts, file9.ts
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
- `[!abc]` - Matches any character NOT in the set
|
|
110
|
+
```typescript
|
|
111
|
+
'[!.]*.ts' // matches files not starting with a dot
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
### Brace Expansion
|
|
115
|
+
|
|
116
|
+
- `{a,b,c}` - Matches any of the comma-separated alternatives
|
|
117
|
+
|
|
118
|
+
```typescript
|
|
119
|
+
'*.{js,ts}' // matches: index.js, helper.ts
|
|
120
|
+
|
|
121
|
+
'src/{lib,test}/**' // matches: src/lib/*, src/test/*
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
### Negation
|
|
125
|
+
|
|
126
|
+
- `!pattern` - Negates a pattern (typically used in arrays of patterns)
|
|
127
|
+
```typescript
|
|
128
|
+
const pattern = parsePattern('!node_modules/**')
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
## API Reference
|
|
132
|
+
|
|
133
|
+
### Parsing
|
|
134
|
+
|
|
135
|
+
#### `parsePattern(source: string): PathPattern`
|
|
136
|
+
|
|
137
|
+
Parses a pattern string into an Abstract Syntax Tree (AST).
|
|
138
|
+
|
|
139
|
+
```typescript
|
|
140
|
+
import { parsePattern } from '@pattern-algebra/core'
|
|
141
|
+
|
|
142
|
+
const ast = parsePattern('src/**/*.ts')
|
|
143
|
+
// Returns AST representation of the pattern
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
#### `validatePattern(source: string): PatternError[]`
|
|
147
|
+
|
|
148
|
+
Validates a pattern and returns any errors found.
|
|
149
|
+
|
|
150
|
+
```typescript
|
|
151
|
+
import { validatePattern } from '@pattern-algebra/core'
|
|
152
|
+
|
|
153
|
+
const errors = validatePattern('src/[invalid')
|
|
154
|
+
if (errors.length > 0) {
|
|
155
|
+
console.error('Invalid pattern:', errors)
|
|
156
|
+
}
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
#### `isValidPattern(source: string): boolean`
|
|
160
|
+
|
|
161
|
+
Checks if a pattern is valid.
|
|
162
|
+
|
|
163
|
+
```typescript
|
|
164
|
+
import { isValidPattern } from '@pattern-algebra/core'
|
|
165
|
+
|
|
166
|
+
if (isValidPattern('src/**/*.ts')) {
|
|
167
|
+
// Pattern is valid
|
|
168
|
+
}
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
#### `expandBraces(source: string): string[]`
|
|
172
|
+
|
|
173
|
+
Expands brace patterns into multiple patterns.
|
|
174
|
+
|
|
175
|
+
```typescript
|
|
176
|
+
import { expandBraces } from '@pattern-algebra/core'
|
|
177
|
+
|
|
178
|
+
expandBraces('*.{js,ts}')
|
|
179
|
+
// Returns: ['*.js', '*.ts']
|
|
180
|
+
|
|
181
|
+
expandBraces('src/{lib,test}/**')
|
|
182
|
+
// Returns: ['src/lib/**', 'src/test/**']
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
### Compilation
|
|
186
|
+
|
|
187
|
+
#### `compilePattern(ast: PathPattern): CompiledPattern`
|
|
188
|
+
|
|
189
|
+
Compiles a parsed pattern into an efficient matching automaton.
|
|
190
|
+
|
|
191
|
+
```typescript
|
|
192
|
+
import { parsePattern, compilePattern } from '@pattern-algebra/core'
|
|
193
|
+
|
|
194
|
+
const pattern = compilePattern(parsePattern('src/**/*.ts'))
|
|
195
|
+
// Pattern is now ready for matching
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
### Matching
|
|
199
|
+
|
|
200
|
+
#### `matchPath(path: string, pattern: CompiledPattern): boolean`
|
|
201
|
+
|
|
202
|
+
Matches a file path against a compiled pattern.
|
|
203
|
+
|
|
204
|
+
```typescript
|
|
205
|
+
import { parsePattern, compilePattern, matchPath } from '@pattern-algebra/core'
|
|
206
|
+
|
|
207
|
+
const pattern = compilePattern(parsePattern('src/**/*.ts'))
|
|
208
|
+
|
|
209
|
+
matchPath('/src/index.ts', pattern) // true
|
|
210
|
+
matchPath('/lib/index.ts', pattern) // false
|
|
211
|
+
```
|
|
212
|
+
|
|
213
|
+
**Note:** Paths should be normalized (use forward slashes, no leading `./`). Use `normalizePath()` if needed.
|
|
214
|
+
|
|
215
|
+
### Path Utilities
|
|
216
|
+
|
|
217
|
+
#### `PathContext`
|
|
218
|
+
|
|
219
|
+
The `PathContext` interface provides context for path resolution operations. It is used with `normalizePath()` to handle home directory expansion, relative path resolution, and project-relative paths.
|
|
220
|
+
|
|
221
|
+
```typescript
|
|
222
|
+
import type { PathContext } from '@pattern-algebra/core'
|
|
223
|
+
|
|
224
|
+
const context: PathContext = {
|
|
225
|
+
homeDir: '/home/user', // User's home directory (for ~ expansion)
|
|
226
|
+
cwd: '/home/user/project', // Current working directory
|
|
227
|
+
projectRoot: '/home/user/project', // Optional: project root for project-relative patterns
|
|
228
|
+
}
|
|
229
|
+
```
|
|
230
|
+
|
|
231
|
+
**Properties:**
|
|
232
|
+
|
|
233
|
+
- `homeDir` (required): The user's home directory, used to expand `~` in paths
|
|
234
|
+
- `cwd` (required): The current working directory, used to resolve relative paths
|
|
235
|
+
- `projectRoot` (optional): The project root directory for project-relative pattern resolution
|
|
236
|
+
|
|
237
|
+
#### `normalizePath(path: string, context: PathContext): string`
|
|
238
|
+
|
|
239
|
+
Normalizes a file path by resolving `~`, `.`, and `..` segments, converting backslashes to forward slashes, and handling relative paths.
|
|
240
|
+
|
|
241
|
+
```typescript
|
|
242
|
+
import { normalizePath } from '@pattern-algebra/core'
|
|
243
|
+
|
|
244
|
+
const context = {
|
|
245
|
+
homeDir: '/home/user',
|
|
246
|
+
cwd: '/home/user/project',
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
normalizePath('./src/../lib/index.ts', context) // '/home/user/project/lib/index.ts'
|
|
250
|
+
normalizePath('src\\utils\\helper.ts', context) // '/home/user/project/src/utils/helper.ts'
|
|
251
|
+
normalizePath('~/Documents/file.txt', context) // '/home/user/Documents/file.txt'
|
|
252
|
+
```
|
|
253
|
+
|
|
254
|
+
#### `pathToSegments(path: string): string[]`
|
|
255
|
+
|
|
256
|
+
Splits a path into segments.
|
|
257
|
+
|
|
258
|
+
```typescript
|
|
259
|
+
import { pathToSegments } from '@pattern-algebra/core'
|
|
260
|
+
|
|
261
|
+
pathToSegments('/src/lib/index.ts') // ['src', 'lib', 'index.ts']
|
|
262
|
+
```
|
|
263
|
+
|
|
264
|
+
#### `segmentsToPath(segments: string[]): string`
|
|
265
|
+
|
|
266
|
+
Joins segments into a path.
|
|
267
|
+
|
|
268
|
+
```typescript
|
|
269
|
+
import { segmentsToPath } from '@pattern-algebra/core'
|
|
270
|
+
|
|
271
|
+
segmentsToPath(['src', 'lib', 'index.ts']) // 'src/lib/index.ts'
|
|
272
|
+
```
|
|
273
|
+
|
|
274
|
+
### Pattern Algebra (Set Operations)
|
|
275
|
+
|
|
276
|
+
#### `patternIntersect(a: CompiledPattern, b: CompiledPattern): CompiledPattern`
|
|
277
|
+
|
|
278
|
+
Creates a pattern matching paths that match BOTH input patterns.
|
|
279
|
+
|
|
280
|
+
```typescript
|
|
281
|
+
import { parsePattern, compilePattern, patternIntersect, matchPath } from '@pattern-algebra/core'
|
|
282
|
+
|
|
283
|
+
const srcFiles = compilePattern(parsePattern('src/**'))
|
|
284
|
+
const tsFiles = compilePattern(parsePattern('**/*.ts'))
|
|
285
|
+
const srcTsFiles = patternIntersect(srcFiles, tsFiles)
|
|
286
|
+
|
|
287
|
+
matchPath('/src/index.ts', srcTsFiles) // true (matches both)
|
|
288
|
+
matchPath('/lib/index.ts', srcTsFiles) // false (only matches tsFiles)
|
|
289
|
+
matchPath('/src/index.js', srcTsFiles) // false (only matches srcFiles)
|
|
290
|
+
```
|
|
291
|
+
|
|
292
|
+
#### `patternUnion(a: CompiledPattern, b: CompiledPattern): CompiledPattern`
|
|
293
|
+
|
|
294
|
+
Creates a pattern matching paths that match EITHER input pattern.
|
|
295
|
+
|
|
296
|
+
```typescript
|
|
297
|
+
import { parsePattern, compilePattern, patternUnion, matchPath } from '@pattern-algebra/core'
|
|
298
|
+
|
|
299
|
+
const jsFiles = compilePattern(parsePattern('**/*.js'))
|
|
300
|
+
const tsFiles = compilePattern(parsePattern('**/*.ts'))
|
|
301
|
+
const scriptFiles = patternUnion(jsFiles, tsFiles)
|
|
302
|
+
|
|
303
|
+
matchPath('/src/index.js', scriptFiles) // true
|
|
304
|
+
matchPath('/src/index.ts', scriptFiles) // true
|
|
305
|
+
matchPath('/src/style.css', scriptFiles) // false
|
|
306
|
+
```
|
|
307
|
+
|
|
308
|
+
#### `patternComplement(pattern: CompiledPattern): CompiledPattern`
|
|
309
|
+
|
|
310
|
+
Creates a pattern matching paths that do NOT match the input pattern.
|
|
311
|
+
|
|
312
|
+
```typescript
|
|
313
|
+
import { parsePattern, compilePattern, patternComplement, matchPath } from '@pattern-algebra/core'
|
|
314
|
+
|
|
315
|
+
const testFiles = compilePattern(parsePattern('**/*.test.ts'))
|
|
316
|
+
const nonTestFiles = patternComplement(testFiles)
|
|
317
|
+
|
|
318
|
+
matchPath('/src/index.ts', nonTestFiles) // true
|
|
319
|
+
matchPath('/src/index.test.ts', nonTestFiles) // false
|
|
320
|
+
```
|
|
321
|
+
|
|
322
|
+
**Note:** Complement patterns may have infinite representations. Use with care.
|
|
323
|
+
|
|
324
|
+
#### `patternDifference(a: CompiledPattern, b: CompiledPattern): CompiledPattern`
|
|
325
|
+
|
|
326
|
+
Creates a pattern matching paths that match pattern A but NOT pattern B.
|
|
327
|
+
|
|
328
|
+
```typescript
|
|
329
|
+
import { parsePattern, compilePattern, patternDifference, matchPath } from '@pattern-algebra/core'
|
|
330
|
+
|
|
331
|
+
const allTs = compilePattern(parsePattern('**/*.ts'))
|
|
332
|
+
const testTs = compilePattern(parsePattern('**/*.test.ts'))
|
|
333
|
+
const nonTestTs = patternDifference(allTs, testTs)
|
|
334
|
+
|
|
335
|
+
matchPath('/src/index.ts', nonTestTs) // true
|
|
336
|
+
matchPath('/src/index.test.ts', nonTestTs) // false
|
|
337
|
+
```
|
|
338
|
+
|
|
339
|
+
### Containment Checking
|
|
340
|
+
|
|
341
|
+
#### `checkContainment(a: CompiledPattern, b: CompiledPattern): ContainmentResult`
|
|
342
|
+
|
|
343
|
+
Determines the relationship between two patterns.
|
|
344
|
+
|
|
345
|
+
```typescript
|
|
346
|
+
import { parsePattern, compilePattern, checkContainment } from '@pattern-algebra/core'
|
|
347
|
+
|
|
348
|
+
const specific = compilePattern(parsePattern('src/index.ts'))
|
|
349
|
+
const general = compilePattern(parsePattern('src/*.ts'))
|
|
350
|
+
|
|
351
|
+
const result = checkContainment(specific, general)
|
|
352
|
+
|
|
353
|
+
result.isSubset // true (every path matching specific also matches general)
|
|
354
|
+
result.isSuperset // false
|
|
355
|
+
result.isEqual // false
|
|
356
|
+
result.relationship // 'subset'
|
|
357
|
+
```
|
|
358
|
+
|
|
359
|
+
**Possible relationships:**
|
|
360
|
+
|
|
361
|
+
- `'equal'` - Patterns match exactly the same set of paths
|
|
362
|
+
- `'subset'` - Pattern A matches a subset of pattern B's paths
|
|
363
|
+
- `'superset'` - Pattern A matches a superset of pattern B's paths
|
|
364
|
+
- `'overlap'` - Patterns have some common paths but neither contains the other
|
|
365
|
+
- `'disjoint'` - Patterns have no common paths
|
|
366
|
+
|
|
367
|
+
#### `areEquivalent(a: CompiledPattern, b: CompiledPattern): boolean`
|
|
368
|
+
|
|
369
|
+
Checks if two patterns match exactly the same set of paths.
|
|
370
|
+
|
|
371
|
+
```typescript
|
|
372
|
+
import { parsePattern, compilePattern, areEquivalent } from '@pattern-algebra/core'
|
|
373
|
+
|
|
374
|
+
const p1 = compilePattern(parsePattern('src/**/*.ts'))
|
|
375
|
+
const p2 = compilePattern(parsePattern('src/**/*.ts'))
|
|
376
|
+
|
|
377
|
+
areEquivalent(p1, p2) // true
|
|
378
|
+
```
|
|
379
|
+
|
|
380
|
+
#### `hasOverlap(a: CompiledPattern, b: CompiledPattern): boolean`
|
|
381
|
+
|
|
382
|
+
Checks if two patterns have any common paths.
|
|
383
|
+
|
|
384
|
+
```typescript
|
|
385
|
+
import { parsePattern, compilePattern, hasOverlap } from '@pattern-algebra/core'
|
|
386
|
+
|
|
387
|
+
const ts = compilePattern(parsePattern('**/*.ts'))
|
|
388
|
+
const js = compilePattern(parsePattern('**/*.js'))
|
|
389
|
+
|
|
390
|
+
hasOverlap(ts, js) // false (disjoint file extensions)
|
|
391
|
+
```
|
|
392
|
+
|
|
393
|
+
#### `areDisjoint(a: CompiledPattern, b: CompiledPattern): boolean`
|
|
394
|
+
|
|
395
|
+
Checks if two patterns have no common paths.
|
|
396
|
+
|
|
397
|
+
```typescript
|
|
398
|
+
import { parsePattern, compilePattern, areDisjoint } from '@pattern-algebra/core'
|
|
399
|
+
|
|
400
|
+
const src = compilePattern(parsePattern('src/**'))
|
|
401
|
+
const lib = compilePattern(parsePattern('lib/**'))
|
|
402
|
+
|
|
403
|
+
areDisjoint(src, lib) // true (different directories)
|
|
404
|
+
```
|
|
405
|
+
|
|
406
|
+
#### `analyzePatterns(patterns: CompiledPattern[]): PatternAnalysis`
|
|
407
|
+
|
|
408
|
+
Analyzes multiple patterns to find overlaps, redundancies, and gaps.
|
|
409
|
+
|
|
410
|
+
```typescript
|
|
411
|
+
import { parsePattern, compilePattern, analyzePatterns } from '@pattern-algebra/core'
|
|
412
|
+
|
|
413
|
+
const patterns = [
|
|
414
|
+
compilePattern(parsePattern('src/**/*.ts')),
|
|
415
|
+
compilePattern(parsePattern('src/**/*.js')),
|
|
416
|
+
compilePattern(parsePattern('lib/**')),
|
|
417
|
+
]
|
|
418
|
+
|
|
419
|
+
const analysis = analyzePatterns(patterns)
|
|
420
|
+
// Returns detailed analysis of pattern relationships
|
|
421
|
+
```
|
|
422
|
+
|
|
423
|
+
## Advanced Usage
|
|
424
|
+
|
|
425
|
+
### Working with Automata
|
|
426
|
+
|
|
427
|
+
For advanced use cases, you can work directly with the underlying automaton operations:
|
|
428
|
+
|
|
429
|
+
```typescript
|
|
430
|
+
import {
|
|
431
|
+
parsePattern,
|
|
432
|
+
compilePattern,
|
|
433
|
+
buildAutomaton,
|
|
434
|
+
determinize,
|
|
435
|
+
intersect,
|
|
436
|
+
union,
|
|
437
|
+
complement,
|
|
438
|
+
isEmpty,
|
|
439
|
+
findWitness,
|
|
440
|
+
} from '@pattern-algebra/core'
|
|
441
|
+
|
|
442
|
+
const pattern = compilePattern(parsePattern('src/**/*.ts'))
|
|
443
|
+
|
|
444
|
+
// Build NFA (Non-deterministic Finite Automaton)
|
|
445
|
+
const nfa = buildAutomaton(pattern.ast)
|
|
446
|
+
|
|
447
|
+
// Convert to DFA for faster matching
|
|
448
|
+
const dfa = determinize(nfa)
|
|
449
|
+
|
|
450
|
+
// Check if a pattern matches nothing
|
|
451
|
+
const empty = compilePattern(parsePattern('*.{js,ts}'))
|
|
452
|
+
const intersection = intersect(empty.automaton, compilePattern(parsePattern('*.css')).automaton)
|
|
453
|
+
isEmpty(intersection) // true (no files can be both .js/.ts AND .css)
|
|
454
|
+
|
|
455
|
+
// Find a witness path (example matching path)
|
|
456
|
+
const witness = findWitness(pattern.automaton)
|
|
457
|
+
// Returns a path that matches the pattern, or null if none exists
|
|
458
|
+
```
|
|
459
|
+
|
|
460
|
+
### Quick Reject Filters
|
|
461
|
+
|
|
462
|
+
Compiled patterns include quick-reject filters for fast path elimination:
|
|
463
|
+
|
|
464
|
+
```typescript
|
|
465
|
+
import { compilePattern, parsePattern, applyQuickReject } from '@pattern-algebra/core'
|
|
466
|
+
|
|
467
|
+
const pattern = compilePattern(parsePattern('src/**/*.ts'))
|
|
468
|
+
|
|
469
|
+
// Quick check before full automaton matching
|
|
470
|
+
const path = '/lib/index.js'
|
|
471
|
+
if (applyQuickReject(path, pattern.quickReject)) {
|
|
472
|
+
// Path rejected - definitely doesn't match
|
|
473
|
+
} else {
|
|
474
|
+
// Path might match - use full matching
|
|
475
|
+
}
|
|
476
|
+
```
|
|
477
|
+
|
|
478
|
+
Quick reject checks:
|
|
479
|
+
|
|
480
|
+
- Minimum/maximum segment count
|
|
481
|
+
- Required file extensions
|
|
482
|
+
- Required prefixes/suffixes
|
|
483
|
+
- Required path segments
|
|
484
|
+
|
|
485
|
+
## Use Cases
|
|
486
|
+
|
|
487
|
+
### Policy Systems
|
|
488
|
+
|
|
489
|
+
```typescript
|
|
490
|
+
// Define access control policies as patterns
|
|
491
|
+
const readPolicy = compilePattern(parsePattern('public/**'))
|
|
492
|
+
const writePolicy = compilePattern(parsePattern('public/{uploads,temp}/**'))
|
|
493
|
+
|
|
494
|
+
// Check if a path is allowed
|
|
495
|
+
function canRead(path: string): boolean {
|
|
496
|
+
return matchPath(path, readPolicy)
|
|
497
|
+
}
|
|
498
|
+
|
|
499
|
+
function canWrite(path: string): boolean {
|
|
500
|
+
return matchPath(path, writePolicy)
|
|
501
|
+
}
|
|
502
|
+
|
|
503
|
+
// Find overlapping permissions
|
|
504
|
+
const overlap = patternIntersect(readPolicy, writePolicy)
|
|
505
|
+
```
|
|
506
|
+
|
|
507
|
+
### Build Systems
|
|
508
|
+
|
|
509
|
+
```typescript
|
|
510
|
+
// Define source patterns
|
|
511
|
+
const sourceFiles = patternUnion(
|
|
512
|
+
compilePattern(parsePattern('src/**/*.ts')),
|
|
513
|
+
compilePattern(parsePattern('lib/**/*.ts')),
|
|
514
|
+
)
|
|
515
|
+
|
|
516
|
+
const testFiles = compilePattern(parsePattern('**/*.test.ts'))
|
|
517
|
+
|
|
518
|
+
// Production files = source files - test files
|
|
519
|
+
const prodFiles = patternDifference(sourceFiles, testFiles)
|
|
520
|
+
|
|
521
|
+
// Check which files to include in build
|
|
522
|
+
function shouldBuild(path: string): boolean {
|
|
523
|
+
return matchPath(path, prodFiles)
|
|
524
|
+
}
|
|
525
|
+
```
|
|
526
|
+
|
|
527
|
+
### Linter Configuration Analysis
|
|
528
|
+
|
|
529
|
+
```typescript
|
|
530
|
+
// Check if .eslintignore patterns overlap with lint targets
|
|
531
|
+
const lintTargets = compilePattern(parsePattern('src/**/*.{js,ts}'))
|
|
532
|
+
const ignored = compilePattern(parsePattern('src/generated/**'))
|
|
533
|
+
|
|
534
|
+
const result = checkContainment(ignored, lintTargets)
|
|
535
|
+
|
|
536
|
+
if (result.relationship === 'subset') {
|
|
537
|
+
console.log('Some lint targets are ignored')
|
|
538
|
+
} else if (result.relationship === 'disjoint') {
|
|
539
|
+
console.log('No overlap between targets and ignored files')
|
|
540
|
+
}
|
|
541
|
+
```
|
|
542
|
+
|
|
543
|
+
## How It Works
|
|
544
|
+
|
|
545
|
+
@pattern-algebra/core uses segment-level automata to efficiently represent and operate on path patterns:
|
|
546
|
+
|
|
547
|
+
1. **Parsing**: Patterns are parsed into an Abstract Syntax Tree (AST)
|
|
548
|
+
2. **Compilation**: The AST is compiled into a Non-deterministic Finite Automaton (NFA) that operates on path segments (not characters)
|
|
549
|
+
3. **Matching**: Paths are split into segments and matched against the NFA
|
|
550
|
+
4. **Set Operations**: Automata are combined using standard automaton operations (intersection, union, complement)
|
|
551
|
+
5. **Containment**: Patterns are determinized into DFAs and compared to determine subset/superset relationships
|
|
552
|
+
|
|
553
|
+
### Performance Optimizations
|
|
554
|
+
|
|
555
|
+
- **Lazy Determinization**: DFAs are only constructed when needed for containment checking
|
|
556
|
+
- **Quick Reject Filters**: Fast pre-filtering eliminates non-matching paths without full automaton traversal
|
|
557
|
+
- **Segment-Level Operations**: Operating on path segments (not characters) reduces state space
|
|
558
|
+
- **Epsilon Transition Elimination**: NFA-to-DFA conversion eliminates epsilon transitions for faster matching
|
|
559
|
+
|
|
560
|
+
## License
|
|
561
|
+
|
|
562
|
+
MIT
|
|
563
|
+
|
|
564
|
+
## Contributing
|
|
565
|
+
|
|
566
|
+
Contributions are welcome! Please see the [repository](https://github.com/mike-north/pattern-algebra) for guidelines.
|
|
567
|
+
|
|
568
|
+
## Related Projects
|
|
569
|
+
|
|
570
|
+
- [@pattern-algebra/cli](https://github.com/mike-north/pattern-algebra) - Command-line interface for pattern operations
|
|
571
|
+
- [@pattern-algebra/policy](https://github.com/mike-north/pattern-algebra) - Policy evaluation engine built on @pattern-algebra/core
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* DFA complement operation.
|
|
3
|
+
* @packageDocumentation
|
|
4
|
+
*/
|
|
5
|
+
import type { SegmentAutomaton } from '../types';
|
|
6
|
+
/**
|
|
7
|
+
* Complement a DFA by swapping accepting and non-accepting states.
|
|
8
|
+
*
|
|
9
|
+
* The complemented automaton accepts exactly the strings that the
|
|
10
|
+
* original automaton rejects, and vice versa.
|
|
11
|
+
*
|
|
12
|
+
* Note: Input must be deterministic. NFAs are automatically converted.
|
|
13
|
+
*
|
|
14
|
+
* @param automaton - The automaton to complement
|
|
15
|
+
* @returns Complemented automaton
|
|
16
|
+
*
|
|
17
|
+
* @public
|
|
18
|
+
*/
|
|
19
|
+
export declare function complement(automaton: SegmentAutomaton): SegmentAutomaton;
|
|
20
|
+
//# sourceMappingURL=complement.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"complement.d.ts","sourceRoot":"","sources":["../../src/automaton/complement.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EAAE,gBAAgB,EAAkB,MAAM,UAAU,CAAA;AAGhE;;;;;;;;;;;;GAYG;AACH,wBAAgB,UAAU,CAAC,SAAS,EAAE,gBAAgB,GAAG,gBAAgB,CAmBxE"}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* DFA complement operation.
|
|
3
|
+
* @packageDocumentation
|
|
4
|
+
*/
|
|
5
|
+
import { determinize } from './determinize';
|
|
6
|
+
/**
|
|
7
|
+
* Complement a DFA by swapping accepting and non-accepting states.
|
|
8
|
+
*
|
|
9
|
+
* The complemented automaton accepts exactly the strings that the
|
|
10
|
+
* original automaton rejects, and vice versa.
|
|
11
|
+
*
|
|
12
|
+
* Note: Input must be deterministic. NFAs are automatically converted.
|
|
13
|
+
*
|
|
14
|
+
* @param automaton - The automaton to complement
|
|
15
|
+
* @returns Complemented automaton
|
|
16
|
+
*
|
|
17
|
+
* @public
|
|
18
|
+
*/
|
|
19
|
+
export function complement(automaton) {
|
|
20
|
+
// Ensure the automaton is deterministic
|
|
21
|
+
const dfa = automaton.isDeterministic ? automaton : determinize(automaton);
|
|
22
|
+
// Swap accepting and non-accepting states
|
|
23
|
+
const complementedStates = dfa.states.map((state) => ({
|
|
24
|
+
...state,
|
|
25
|
+
accepting: !state.accepting,
|
|
26
|
+
}));
|
|
27
|
+
// New accepting states are the old non-accepting states
|
|
28
|
+
const acceptingStates = complementedStates.filter((s) => s.accepting).map((s) => s.id);
|
|
29
|
+
return {
|
|
30
|
+
states: complementedStates,
|
|
31
|
+
initialState: dfa.initialState,
|
|
32
|
+
acceptingStates,
|
|
33
|
+
isDeterministic: true,
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
//# sourceMappingURL=complement.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"complement.js","sourceRoot":"","sources":["../../src/automaton/complement.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAGH,OAAO,EAAE,WAAW,EAAE,MAAM,eAAe,CAAA;AAE3C;;;;;;;;;;;;GAYG;AACH,MAAM,UAAU,UAAU,CAAC,SAA2B;IACpD,wCAAwC;IACxC,MAAM,GAAG,GAAG,SAAS,CAAC,eAAe,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,WAAW,CAAC,SAAS,CAAC,CAAA;IAE1E,0CAA0C;IAC1C,MAAM,kBAAkB,GAAqB,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;QACtE,GAAG,KAAK;QACR,SAAS,EAAE,CAAC,KAAK,CAAC,SAAS;KAC5B,CAAC,CAAC,CAAA;IAEH,wDAAwD;IACxD,MAAM,eAAe,GAAG,kBAAkB,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,CAAA;IAEtF,OAAO;QACL,MAAM,EAAE,kBAAkB;QAC1B,YAAY,EAAE,GAAG,CAAC,YAAY;QAC9B,eAAe;QACf,eAAe,EAAE,IAAI;KACtB,CAAA;AACH,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"complement.test.d.ts","sourceRoot":"","sources":["../../src/automaton/complement.test.ts"],"names":[],"mappings":""}
|