@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.
Files changed (154) hide show
  1. package/README.md +571 -0
  2. package/dist/automaton/complement.d.ts +20 -0
  3. package/dist/automaton/complement.d.ts.map +1 -0
  4. package/dist/automaton/complement.js +36 -0
  5. package/dist/automaton/complement.js.map +1 -0
  6. package/dist/automaton/complement.test.d.ts +2 -0
  7. package/dist/automaton/complement.test.d.ts.map +1 -0
  8. package/dist/automaton/complement.test.js +114 -0
  9. package/dist/automaton/complement.test.js.map +1 -0
  10. package/dist/automaton/determinize.d.ts +41 -0
  11. package/dist/automaton/determinize.d.ts.map +1 -0
  12. package/dist/automaton/determinize.js +310 -0
  13. package/dist/automaton/determinize.js.map +1 -0
  14. package/dist/automaton/determinize.test.d.ts +2 -0
  15. package/dist/automaton/determinize.test.d.ts.map +1 -0
  16. package/dist/automaton/determinize.test.js +134 -0
  17. package/dist/automaton/determinize.test.js.map +1 -0
  18. package/dist/automaton/emptiness.d.ts +41 -0
  19. package/dist/automaton/emptiness.d.ts.map +1 -0
  20. package/dist/automaton/emptiness.js +262 -0
  21. package/dist/automaton/emptiness.js.map +1 -0
  22. package/dist/automaton/emptiness.test.d.ts +2 -0
  23. package/dist/automaton/emptiness.test.d.ts.map +1 -0
  24. package/dist/automaton/emptiness.test.js +154 -0
  25. package/dist/automaton/emptiness.test.js.map +1 -0
  26. package/dist/automaton/index.d.ts +10 -0
  27. package/dist/automaton/index.d.ts.map +1 -0
  28. package/dist/automaton/index.js +11 -0
  29. package/dist/automaton/index.js.map +1 -0
  30. package/dist/automaton/intersect.d.ts +35 -0
  31. package/dist/automaton/intersect.d.ts.map +1 -0
  32. package/dist/automaton/intersect.js +302 -0
  33. package/dist/automaton/intersect.js.map +1 -0
  34. package/dist/automaton/pattern-algebra.d.ts +62 -0
  35. package/dist/automaton/pattern-algebra.d.ts.map +1 -0
  36. package/dist/automaton/pattern-algebra.js +309 -0
  37. package/dist/automaton/pattern-algebra.js.map +1 -0
  38. package/dist/automaton/pattern-algebra.test.d.ts +2 -0
  39. package/dist/automaton/pattern-algebra.test.d.ts.map +1 -0
  40. package/dist/automaton/pattern-algebra.test.js +223 -0
  41. package/dist/automaton/pattern-algebra.test.js.map +1 -0
  42. package/dist/compile/automaton-builder.d.ts +47 -0
  43. package/dist/compile/automaton-builder.d.ts.map +1 -0
  44. package/dist/compile/automaton-builder.js +211 -0
  45. package/dist/compile/automaton-builder.js.map +1 -0
  46. package/dist/compile/compiler.d.ts +32 -0
  47. package/dist/compile/compiler.d.ts.map +1 -0
  48. package/dist/compile/compiler.js +47 -0
  49. package/dist/compile/compiler.js.map +1 -0
  50. package/dist/compile/index.d.ts +8 -0
  51. package/dist/compile/index.d.ts.map +1 -0
  52. package/dist/compile/index.js +8 -0
  53. package/dist/compile/index.js.map +1 -0
  54. package/dist/compile/quick-reject.d.ts +28 -0
  55. package/dist/compile/quick-reject.d.ts.map +1 -0
  56. package/dist/compile/quick-reject.js +147 -0
  57. package/dist/compile/quick-reject.js.map +1 -0
  58. package/dist/containment/analysis.d.ts +60 -0
  59. package/dist/containment/analysis.d.ts.map +1 -0
  60. package/dist/containment/analysis.js +378 -0
  61. package/dist/containment/analysis.js.map +1 -0
  62. package/dist/containment/containment.d.ts +23 -0
  63. package/dist/containment/containment.d.ts.map +1 -0
  64. package/dist/containment/containment.js +681 -0
  65. package/dist/containment/containment.js.map +1 -0
  66. package/dist/containment/containment.test.d.ts +2 -0
  67. package/dist/containment/containment.test.d.ts.map +1 -0
  68. package/dist/containment/containment.test.js +209 -0
  69. package/dist/containment/containment.test.js.map +1 -0
  70. package/dist/containment/index.d.ts +7 -0
  71. package/dist/containment/index.d.ts.map +1 -0
  72. package/dist/containment/index.js +7 -0
  73. package/dist/containment/index.js.map +1 -0
  74. package/dist/core-alpha.d.ts +1253 -0
  75. package/dist/core-beta.d.ts +1253 -0
  76. package/dist/core-public.d.ts +1253 -0
  77. package/dist/core-unstripped.d.ts +1253 -0
  78. package/dist/index.d.ts +32 -0
  79. package/dist/index.d.ts.map +1 -0
  80. package/dist/index.js +49 -0
  81. package/dist/index.js.map +1 -0
  82. package/dist/match/index.d.ts +8 -0
  83. package/dist/match/index.d.ts.map +1 -0
  84. package/dist/match/index.js +8 -0
  85. package/dist/match/index.js.map +1 -0
  86. package/dist/match/matcher.d.ts +40 -0
  87. package/dist/match/matcher.d.ts.map +1 -0
  88. package/dist/match/matcher.js +256 -0
  89. package/dist/match/matcher.js.map +1 -0
  90. package/dist/match/matcher.test.d.ts +2 -0
  91. package/dist/match/matcher.test.d.ts.map +1 -0
  92. package/dist/match/matcher.test.js +185 -0
  93. package/dist/match/matcher.test.js.map +1 -0
  94. package/dist/match/path-utils.d.ts +132 -0
  95. package/dist/match/path-utils.d.ts.map +1 -0
  96. package/dist/match/path-utils.js +223 -0
  97. package/dist/match/path-utils.js.map +1 -0
  98. package/dist/match/path-utils.test.d.ts +2 -0
  99. package/dist/match/path-utils.test.d.ts.map +1 -0
  100. package/dist/match/path-utils.test.js +193 -0
  101. package/dist/match/path-utils.test.js.map +1 -0
  102. package/dist/match/segment-matcher.d.ts +25 -0
  103. package/dist/match/segment-matcher.d.ts.map +1 -0
  104. package/dist/match/segment-matcher.js +267 -0
  105. package/dist/match/segment-matcher.js.map +1 -0
  106. package/dist/parse/brace-expansion.d.ts +34 -0
  107. package/dist/parse/brace-expansion.d.ts.map +1 -0
  108. package/dist/parse/brace-expansion.js +294 -0
  109. package/dist/parse/brace-expansion.js.map +1 -0
  110. package/dist/parse/brace-expansion.test.d.ts +2 -0
  111. package/dist/parse/brace-expansion.test.d.ts.map +1 -0
  112. package/dist/parse/brace-expansion.test.js +105 -0
  113. package/dist/parse/brace-expansion.test.js.map +1 -0
  114. package/dist/parse/index.d.ts +8 -0
  115. package/dist/parse/index.d.ts.map +1 -0
  116. package/dist/parse/index.js +8 -0
  117. package/dist/parse/index.js.map +1 -0
  118. package/dist/parse/parser.d.ts +15 -0
  119. package/dist/parse/parser.d.ts.map +1 -0
  120. package/dist/parse/parser.js +526 -0
  121. package/dist/parse/parser.js.map +1 -0
  122. package/dist/parse/parser.test.d.ts +2 -0
  123. package/dist/parse/parser.test.d.ts.map +1 -0
  124. package/dist/parse/parser.test.js +266 -0
  125. package/dist/parse/parser.test.js.map +1 -0
  126. package/dist/parse/validator.d.ts +30 -0
  127. package/dist/parse/validator.d.ts.map +1 -0
  128. package/dist/parse/validator.js +115 -0
  129. package/dist/parse/validator.js.map +1 -0
  130. package/dist/parse/validator.test.d.ts +2 -0
  131. package/dist/parse/validator.test.d.ts.map +1 -0
  132. package/dist/parse/validator.test.js +45 -0
  133. package/dist/parse/validator.test.js.map +1 -0
  134. package/dist/types/ast.d.ts +158 -0
  135. package/dist/types/ast.d.ts.map +1 -0
  136. package/dist/types/ast.js +2 -0
  137. package/dist/types/ast.js.map +1 -0
  138. package/dist/types/automaton.d.ts +150 -0
  139. package/dist/types/automaton.d.ts.map +1 -0
  140. package/dist/types/automaton.js +2 -0
  141. package/dist/types/automaton.js.map +1 -0
  142. package/dist/types/containment.d.ts +257 -0
  143. package/dist/types/containment.d.ts.map +1 -0
  144. package/dist/types/containment.js +5 -0
  145. package/dist/types/containment.js.map +1 -0
  146. package/dist/types/errors.d.ts +37 -0
  147. package/dist/types/errors.d.ts.map +1 -0
  148. package/dist/types/errors.js +24 -0
  149. package/dist/types/errors.js.map +1 -0
  150. package/dist/types/index.d.ts +10 -0
  151. package/dist/types/index.d.ts.map +1 -0
  152. package/dist/types/index.js +6 -0
  153. package/dist/types/index.js.map +1 -0
  154. 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,2 @@
1
+ export {};
2
+ //# sourceMappingURL=complement.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"complement.test.d.ts","sourceRoot":"","sources":["../../src/automaton/complement.test.ts"],"names":[],"mappings":""}