@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
@@ -0,0 +1,105 @@
1
+ import { describe, it, expect } from 'vitest';
2
+ import { parsePattern } from './parser';
3
+ import { expandBraces, countBraceExpansions } from './brace-expansion';
4
+ describe('expandBraces', () => {
5
+ it('expands simple brace expression', () => {
6
+ const pattern = parsePattern('{src,lib}');
7
+ const expanded = expandBraces(pattern);
8
+ expect(expanded).toHaveLength(2);
9
+ expect(expanded[0].source).toBe('src');
10
+ expect(expanded[1].source).toBe('lib');
11
+ });
12
+ it('expands braces with suffix', () => {
13
+ const pattern = parsePattern('{src,lib}/**/*.ts');
14
+ const expanded = expandBraces(pattern);
15
+ expect(expanded).toHaveLength(2);
16
+ expect(expanded[0].source).toBe('src/**/*.ts');
17
+ expect(expanded[1].source).toBe('lib/**/*.ts');
18
+ });
19
+ it('expands braces with prefix', () => {
20
+ const pattern = parsePattern('packages/{core,cli}');
21
+ const expanded = expandBraces(pattern);
22
+ expect(expanded).toHaveLength(2);
23
+ expect(expanded[0].source).toBe('packages/core');
24
+ expect(expanded[1].source).toBe('packages/cli');
25
+ });
26
+ it('expands multiple braces', () => {
27
+ const pattern = parsePattern('{a,b}/{x,y}');
28
+ const expanded = expandBraces(pattern);
29
+ expect(expanded).toHaveLength(4);
30
+ const sources = expanded.map((p) => p.source);
31
+ expect(sources).toContain('a/x');
32
+ expect(sources).toContain('a/y');
33
+ expect(sources).toContain('b/x');
34
+ expect(sources).toContain('b/y');
35
+ });
36
+ it('expands numeric range', () => {
37
+ const pattern = parsePattern('file{1..5}.txt');
38
+ const expanded = expandBraces(pattern);
39
+ expect(expanded).toHaveLength(5);
40
+ expect(expanded[0].source).toBe('file1.txt');
41
+ expect(expanded[4].source).toBe('file5.txt');
42
+ });
43
+ it('expands descending numeric range', () => {
44
+ const pattern = parsePattern('v{3..1}');
45
+ const expanded = expandBraces(pattern);
46
+ expect(expanded).toHaveLength(3);
47
+ expect(expanded[0].source).toBe('v3');
48
+ expect(expanded[1].source).toBe('v2');
49
+ expect(expanded[2].source).toBe('v1');
50
+ });
51
+ it('returns pattern unchanged if no braces', () => {
52
+ const pattern = parsePattern('src/**/*.ts');
53
+ const expanded = expandBraces(pattern);
54
+ expect(expanded).toHaveLength(1);
55
+ expect(expanded[0].source).toBe('src/**/*.ts');
56
+ });
57
+ it('preserves negation', () => {
58
+ const pattern = parsePattern('!{node_modules,dist}/**');
59
+ const expanded = expandBraces(pattern);
60
+ expect(expanded).toHaveLength(2);
61
+ expect(expanded[0].source).toBe('!node_modules/**');
62
+ expect(expanded[1].source).toBe('!dist/**');
63
+ });
64
+ it('limits expansion count', () => {
65
+ const pattern = parsePattern('{a,b,c,d,e}');
66
+ const expanded = expandBraces(pattern, 3);
67
+ expect(expanded.length).toBeLessThanOrEqual(3);
68
+ expect(expanded.some((p) => p.errors?.some((e) => e.code === 'EXPANSION_LIMIT'))).toBe(true);
69
+ });
70
+ it('limits numeric range', () => {
71
+ const pattern = parsePattern('{1..100}');
72
+ const expanded = expandBraces(pattern);
73
+ // Should error because range exceeds 50
74
+ expect(expanded.some((p) => p.errors?.some((e) => e.code === 'EXPANSION_LIMIT'))).toBe(true);
75
+ });
76
+ it('errors on nested braces', () => {
77
+ const pattern = parsePattern('{a,{b,c}}');
78
+ const expanded = expandBraces(pattern);
79
+ expect(expanded.some((p) => p.errors?.some((e) => e.code === 'NESTED_BRACES'))).toBe(true);
80
+ });
81
+ it('handles braces inside brackets (treated as literal)', () => {
82
+ // {a,b} inside [] is not brace expansion
83
+ const pattern = parsePattern('[{a,b}]');
84
+ const expanded = expandBraces(pattern);
85
+ expect(expanded).toHaveLength(1);
86
+ });
87
+ });
88
+ describe('countBraceExpansions', () => {
89
+ it('counts simple expansion', () => {
90
+ expect(countBraceExpansions('{a,b}')).toBe(2);
91
+ expect(countBraceExpansions('{a,b,c}')).toBe(3);
92
+ });
93
+ it('counts multiple braces as multiplication', () => {
94
+ expect(countBraceExpansions('{a,b}/{x,y}')).toBe(4);
95
+ expect(countBraceExpansions('{a,b}/{x,y}/{1,2}')).toBe(8);
96
+ });
97
+ it('returns 1 for no braces', () => {
98
+ expect(countBraceExpansions('src/**/*.ts')).toBe(1);
99
+ });
100
+ it('returns Infinity for excessive expansion', () => {
101
+ // Create a pattern that would expand to > 10000 (10^5 = 100,000)
102
+ expect(countBraceExpansions(`{a,b,c,d,e,f,g,h,i,j}{a,b,c,d,e,f,g,h,i,j}{a,b,c,d,e,f,g,h,i,j}{a,b,c,d,e,f,g,h,i,j}{a,b,c,d,e,f,g,h,i,j}`)).toBe(Infinity);
103
+ });
104
+ });
105
+ //# sourceMappingURL=brace-expansion.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"brace-expansion.test.js","sourceRoot":"","sources":["../../src/parse/brace-expansion.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Pattern parsing utilities.
3
+ * @packageDocumentation
4
+ */
5
+ export { parsePattern } from './parser';
6
+ export { validatePattern, isValidPattern } from './validator';
7
+ export { expandBraces, countBraceExpansions } from './brace-expansion';
8
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/parse/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,YAAY,EAAE,MAAM,UAAU,CAAA;AACvC,OAAO,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,aAAa,CAAA;AAC7D,OAAO,EAAE,YAAY,EAAE,oBAAoB,EAAE,MAAM,mBAAmB,CAAA"}
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Pattern parsing utilities.
3
+ * @packageDocumentation
4
+ */
5
+ export { parsePattern } from './parser';
6
+ export { validatePattern, isValidPattern } from './validator';
7
+ export { expandBraces, countBraceExpansions } from './brace-expansion';
8
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/parse/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,YAAY,EAAE,MAAM,UAAU,CAAA;AACvC,OAAO,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,aAAa,CAAA;AAC7D,OAAO,EAAE,YAAY,EAAE,oBAAoB,EAAE,MAAM,mBAAmB,CAAA"}
@@ -0,0 +1,15 @@
1
+ /**
2
+ * Pattern parser - converts glob pattern strings to AST.
3
+ * @packageDocumentation
4
+ */
5
+ import type { PathPattern } from '../types';
6
+ /**
7
+ * Parse a pattern string into an AST.
8
+ *
9
+ * @param source - The pattern string to parse
10
+ * @returns Parsed PathPattern with AST and any errors
11
+ *
12
+ * @public
13
+ */
14
+ export declare function parsePattern(source: string): PathPattern;
15
+ //# sourceMappingURL=parser.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"parser.d.ts","sourceRoot":"","sources":["../../src/parse/parser.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EACV,WAAW,EASZ,MAAM,UAAU,CAAA;AAWjB;;;;;;;GAOG;AACH,wBAAgB,YAAY,CAAC,MAAM,EAAE,MAAM,GAAG,WAAW,CA+CxD"}
@@ -0,0 +1,526 @@
1
+ /**
2
+ * Pattern parser - converts glob pattern strings to AST.
3
+ * @packageDocumentation
4
+ */
5
+ /**
6
+ * Parse a pattern string into an AST.
7
+ *
8
+ * @param source - The pattern string to parse
9
+ * @returns Parsed PathPattern with AST and any errors
10
+ *
11
+ * @public
12
+ */
13
+ export function parsePattern(source) {
14
+ const state = {
15
+ source,
16
+ position: 0,
17
+ errors: [],
18
+ };
19
+ // Handle negation prefix
20
+ let isNegation = false;
21
+ let patternToParse = source;
22
+ if (source.startsWith('!')) {
23
+ isNegation = true;
24
+ patternToParse = source.slice(1);
25
+ state.position = 1;
26
+ }
27
+ // Determine if absolute
28
+ const isAbsolute = patternToParse.startsWith('/') || patternToParse.startsWith('~');
29
+ // Check for brace expansion at top level
30
+ const hasBraces = containsTopLevelBraces(patternToParse);
31
+ let root;
32
+ if (hasBraces) {
33
+ // Parse as alternation
34
+ const branches = expandBracesOnce(patternToParse, state);
35
+ if (branches.length === 1) {
36
+ root = parseSegmentSequence(branches[0], state);
37
+ }
38
+ else {
39
+ root = {
40
+ type: 'alternation',
41
+ branches: branches.map((branch) => parseSegmentSequence(branch, state)),
42
+ };
43
+ }
44
+ }
45
+ else {
46
+ root = parseSegmentSequence(patternToParse, state);
47
+ }
48
+ return {
49
+ source,
50
+ root,
51
+ isAbsolute,
52
+ isNegation,
53
+ errors: state.errors.length > 0 ? state.errors : undefined,
54
+ };
55
+ }
56
+ /**
57
+ * Check if a pattern contains top-level braces (not nested in brackets).
58
+ */
59
+ function containsTopLevelBraces(pattern) {
60
+ let inBracket = false;
61
+ let braceDepth = 0;
62
+ for (let i = 0; i < pattern.length; i++) {
63
+ const char = pattern[i];
64
+ const prevChar = i > 0 ? pattern[i - 1] : '';
65
+ // Skip escaped characters
66
+ if (prevChar === '\\')
67
+ continue;
68
+ if (char === '[' && !inBracket) {
69
+ inBracket = true;
70
+ }
71
+ else if (char === ']' && inBracket) {
72
+ inBracket = false;
73
+ }
74
+ else if (char === '{' && !inBracket) {
75
+ braceDepth++;
76
+ }
77
+ else if (char === '}' && !inBracket && braceDepth > 0) {
78
+ braceDepth--;
79
+ }
80
+ }
81
+ // Check again for actual braces
82
+ braceDepth = 0;
83
+ inBracket = false;
84
+ for (let i = 0; i < pattern.length; i++) {
85
+ const char = pattern[i];
86
+ const prevChar = i > 0 ? pattern[i - 1] : '';
87
+ if (prevChar === '\\')
88
+ continue;
89
+ if (char === '[' && !inBracket) {
90
+ inBracket = true;
91
+ }
92
+ else if (char === ']' && inBracket) {
93
+ inBracket = false;
94
+ }
95
+ else if (char === '{' && !inBracket) {
96
+ return true;
97
+ }
98
+ }
99
+ return false;
100
+ }
101
+ /**
102
+ * Expand braces one level (non-recursive, nested braces are an error).
103
+ */
104
+ function expandBracesOnce(pattern, state) {
105
+ // Find the first top-level brace
106
+ let inBracket = false;
107
+ let braceStart = -1;
108
+ let braceEnd = -1;
109
+ let braceDepth = 0;
110
+ for (let i = 0; i < pattern.length; i++) {
111
+ const char = pattern[i];
112
+ const prevChar = i > 0 ? pattern[i - 1] : '';
113
+ if (prevChar === '\\')
114
+ continue;
115
+ if (char === '[' && !inBracket) {
116
+ inBracket = true;
117
+ }
118
+ else if (char === ']' && inBracket) {
119
+ inBracket = false;
120
+ }
121
+ else if (char === '{' && !inBracket) {
122
+ if (braceDepth === 0) {
123
+ braceStart = i;
124
+ }
125
+ braceDepth++;
126
+ // Check for nested braces
127
+ if (braceDepth > 1) {
128
+ state.errors.push({
129
+ code: 'NESTED_BRACES',
130
+ message: 'Nested braces are not allowed',
131
+ position: i,
132
+ length: 1,
133
+ });
134
+ // Continue parsing as if it weren't nested
135
+ }
136
+ }
137
+ else if (char === '}' && !inBracket) {
138
+ if (braceDepth > 0) {
139
+ braceDepth--;
140
+ if (braceDepth === 0) {
141
+ braceEnd = i;
142
+ break;
143
+ }
144
+ }
145
+ }
146
+ }
147
+ if (braceStart === -1) {
148
+ return [pattern];
149
+ }
150
+ if (braceEnd === -1) {
151
+ state.errors.push({
152
+ code: 'UNCLOSED_BRACE',
153
+ message: 'Unclosed brace in pattern',
154
+ position: braceStart,
155
+ length: 1,
156
+ });
157
+ return [pattern];
158
+ }
159
+ const prefix = pattern.slice(0, braceStart);
160
+ const braceContent = pattern.slice(braceStart + 1, braceEnd);
161
+ const suffix = pattern.slice(braceEnd + 1);
162
+ // Split brace content by commas (not inside nested structures)
163
+ const alternatives = splitByComma(braceContent);
164
+ // Check for numeric range like {1..10}
165
+ if (alternatives.length === 1 && alternatives[0].includes('..')) {
166
+ const rangeMatch = /^(-?\d+)\.\.(-?\d+)$/.exec(alternatives[0]);
167
+ if (rangeMatch) {
168
+ const start = parseInt(rangeMatch[1], 10);
169
+ const end = parseInt(rangeMatch[2], 10);
170
+ const step = start <= end ? 1 : -1;
171
+ const count = Math.abs(end - start) + 1;
172
+ if (count > 50) {
173
+ state.errors.push({
174
+ code: 'EXPANSION_LIMIT',
175
+ message: `Numeric range {${start}..${end}} exceeds limit of 50 elements`,
176
+ position: braceStart,
177
+ length: braceEnd - braceStart + 1,
178
+ });
179
+ // Truncate to 50
180
+ const truncated = [];
181
+ for (let i = 0; i < 50; i++) {
182
+ truncated.push(String(start + i * step));
183
+ }
184
+ return expandBracesOnce(truncated.map((n) => prefix + n + suffix).join('|SPLIT|'), state).flatMap((p) => p.split('|SPLIT|'));
185
+ }
186
+ const expanded = [];
187
+ for (let n = start; step > 0 ? n <= end : n >= end; n += step) {
188
+ expanded.push(prefix + n + suffix);
189
+ }
190
+ // Recursively expand if more braces in suffix
191
+ if (containsTopLevelBraces(suffix)) {
192
+ return expanded.flatMap((p) => expandBracesOnce(p, state));
193
+ }
194
+ return expanded;
195
+ }
196
+ }
197
+ // Standard brace expansion
198
+ const expanded = alternatives.map((alt) => prefix + alt + suffix);
199
+ // Recursively expand if more braces
200
+ return expanded.flatMap((p) => {
201
+ if (containsTopLevelBraces(p)) {
202
+ return expandBracesOnce(p, state);
203
+ }
204
+ return [p];
205
+ });
206
+ }
207
+ /**
208
+ * Split a string by top-level commas.
209
+ */
210
+ function splitByComma(content) {
211
+ const parts = [];
212
+ let current = '';
213
+ let depth = 0;
214
+ let inBracket = false;
215
+ for (let i = 0; i < content.length; i++) {
216
+ const char = content[i];
217
+ const prevChar = i > 0 ? content[i - 1] : '';
218
+ if (prevChar === '\\') {
219
+ current += char;
220
+ continue;
221
+ }
222
+ if (char === '[' && !inBracket) {
223
+ inBracket = true;
224
+ current += char;
225
+ }
226
+ else if (char === ']' && inBracket) {
227
+ inBracket = false;
228
+ current += char;
229
+ }
230
+ else if (char === '{' && !inBracket) {
231
+ depth++;
232
+ current += char;
233
+ }
234
+ else if (char === '}' && !inBracket && depth > 0) {
235
+ depth--;
236
+ current += char;
237
+ }
238
+ else if (char === ',' && depth === 0 && !inBracket) {
239
+ parts.push(current);
240
+ current = '';
241
+ }
242
+ else {
243
+ current += char;
244
+ }
245
+ }
246
+ parts.push(current);
247
+ return parts;
248
+ }
249
+ /**
250
+ * Parse a segment sequence (path split by /).
251
+ */
252
+ function parseSegmentSequence(pattern, state) {
253
+ // Remove leading / for absolute paths (we track absoluteness separately)
254
+ let toParse = pattern;
255
+ if (toParse.startsWith('/')) {
256
+ toParse = toParse.slice(1);
257
+ }
258
+ else if (toParse.startsWith('~/')) {
259
+ toParse = toParse.slice(2);
260
+ }
261
+ else if (toParse === '~') {
262
+ toParse = '';
263
+ }
264
+ if (toParse === '') {
265
+ return { type: 'sequence', segments: [] };
266
+ }
267
+ // Split by / but not inside brackets
268
+ const segmentStrings = splitBySlash(toParse);
269
+ const segments = segmentStrings.map((seg) => parseSegment(seg, state));
270
+ return { type: 'sequence', segments };
271
+ }
272
+ /**
273
+ * Split pattern by forward slashes (not inside brackets).
274
+ */
275
+ function splitBySlash(pattern) {
276
+ const parts = [];
277
+ let current = '';
278
+ let inBracket = false;
279
+ for (let i = 0; i < pattern.length; i++) {
280
+ const char = pattern[i];
281
+ const prevChar = i > 0 ? pattern[i - 1] : '';
282
+ if (prevChar === '\\') {
283
+ current += char;
284
+ continue;
285
+ }
286
+ if (char === '[' && !inBracket) {
287
+ inBracket = true;
288
+ current += char;
289
+ }
290
+ else if (char === ']' && inBracket) {
291
+ inBracket = false;
292
+ current += char;
293
+ }
294
+ else if (char === '/' && !inBracket) {
295
+ if (current !== '') {
296
+ parts.push(current);
297
+ }
298
+ current = '';
299
+ }
300
+ else {
301
+ current += char;
302
+ }
303
+ }
304
+ if (current !== '') {
305
+ parts.push(current);
306
+ }
307
+ return parts;
308
+ }
309
+ /**
310
+ * Check if a segment contains unescaped special characters.
311
+ */
312
+ function hasUnescapedSpecial(segment, chars) {
313
+ for (let i = 0; i < segment.length; i++) {
314
+ const char = segment[i];
315
+ // Skip escaped characters
316
+ if (char === '\\' && i + 1 < segment.length) {
317
+ i++; // Skip next character
318
+ continue;
319
+ }
320
+ if (chars.includes(char)) {
321
+ return true;
322
+ }
323
+ }
324
+ return false;
325
+ }
326
+ /**
327
+ * Parse a single segment string into a Segment node.
328
+ */
329
+ function parseSegment(segment, state) {
330
+ // Check for globstar
331
+ if (segment === '**') {
332
+ return { type: 'globstar' };
333
+ }
334
+ // Check for invalid globstar usage (unescaped **)
335
+ if (hasUnescapedSpecial(segment, '*') && segment !== '**') {
336
+ // Check if there's an actual ** sequence (not escaped)
337
+ for (let i = 0; i < segment.length - 1; i++) {
338
+ if (segment[i] === '\\') {
339
+ i++; // Skip escaped char
340
+ continue;
341
+ }
342
+ if (segment[i] === '*' && segment[i + 1] === '*') {
343
+ state.errors.push({
344
+ code: 'INVALID_GLOBSTAR',
345
+ message: '** must be a complete path segment, not part of a larger pattern',
346
+ position: state.source.indexOf(segment),
347
+ length: segment.length,
348
+ });
349
+ break;
350
+ }
351
+ }
352
+ }
353
+ // Analyze segment content - check for UNESCAPED special chars
354
+ const hasWildcard = hasUnescapedSpecial(segment, '*?');
355
+ const hasBracket = hasUnescapedSpecial(segment, '[');
356
+ // Pure literal (no unescaped wildcards or brackets)
357
+ if (!hasWildcard && !hasBracket) {
358
+ return {
359
+ type: 'literal',
360
+ value: unescapeSegment(segment),
361
+ };
362
+ }
363
+ // Parse into parts
364
+ const parts = parseSegmentParts(segment, state);
365
+ // If only wildcards (no charclass), use WildcardSegment
366
+ if (!hasBracket) {
367
+ const wildcardParts = parts.map((p) => {
368
+ if (p.type === 'literal')
369
+ return { type: 'literal', value: p.value };
370
+ if (p.type === 'star')
371
+ return { type: 'star' };
372
+ if (p.type === 'question')
373
+ return { type: 'question' };
374
+ // Should not happen for non-bracket segments
375
+ throw new Error(`Unexpected part type: ${p.type}`);
376
+ });
377
+ return {
378
+ type: 'wildcard',
379
+ pattern: segment,
380
+ parts: wildcardParts,
381
+ };
382
+ }
383
+ // Has character classes - use CompositeSegment
384
+ return {
385
+ type: 'composite',
386
+ parts,
387
+ };
388
+ }
389
+ /**
390
+ * Parse segment into component parts (literals, wildcards, charclasses).
391
+ */
392
+ function parseSegmentParts(segment, state) {
393
+ const parts = [];
394
+ let i = 0;
395
+ let literalBuffer = '';
396
+ const flushLiteral = () => {
397
+ if (literalBuffer !== '') {
398
+ parts.push({ type: 'literal', value: literalBuffer });
399
+ literalBuffer = '';
400
+ }
401
+ };
402
+ while (i < segment.length) {
403
+ const char = segment[i];
404
+ // Handle escapes
405
+ if (char === '\\' && i + 1 < segment.length) {
406
+ literalBuffer += segment[i + 1];
407
+ i += 2;
408
+ continue;
409
+ }
410
+ if (char === '*') {
411
+ flushLiteral();
412
+ parts.push({ type: 'star' });
413
+ i++;
414
+ }
415
+ else if (char === '?') {
416
+ flushLiteral();
417
+ parts.push({ type: 'question' });
418
+ i++;
419
+ }
420
+ else if (char === '[') {
421
+ flushLiteral();
422
+ const { charclass, endIndex } = parseCharClass(segment, i, state);
423
+ parts.push({ type: 'charclass', spec: charclass });
424
+ i = endIndex;
425
+ }
426
+ else {
427
+ literalBuffer += char;
428
+ i++;
429
+ }
430
+ }
431
+ flushLiteral();
432
+ return parts;
433
+ }
434
+ /**
435
+ * Parse a character class [abc] or [a-z] or [!...].
436
+ */
437
+ function parseCharClass(segment, startIndex, state) {
438
+ let i = startIndex + 1; // Skip opening [
439
+ let negated = false;
440
+ const ranges = [];
441
+ let chars = '';
442
+ // Check for negation
443
+ if (i < segment.length && (segment[i] === '!' || segment[i] === '^')) {
444
+ negated = true;
445
+ i++;
446
+ }
447
+ // Handle ] as first char (literal)
448
+ if (i < segment.length && segment[i] === ']') {
449
+ chars += ']';
450
+ i++;
451
+ }
452
+ while (i < segment.length) {
453
+ const char = segment[i];
454
+ if (char === ']') {
455
+ // End of character class
456
+ if (chars === '' && ranges.length === 0) {
457
+ state.errors.push({
458
+ code: 'EMPTY_CHARCLASS',
459
+ message: 'Empty character class',
460
+ position: startIndex,
461
+ length: i - startIndex + 1,
462
+ });
463
+ }
464
+ return {
465
+ charclass: { type: 'charclass', negated, ranges, chars },
466
+ endIndex: i + 1,
467
+ };
468
+ }
469
+ if (char === '\\' && i + 1 < segment.length) {
470
+ // Escaped character
471
+ chars += segment[i + 1];
472
+ i += 2;
473
+ continue;
474
+ }
475
+ // Check for range
476
+ if (i + 2 < segment.length && segment[i + 1] === '-' && segment[i + 2] !== ']') {
477
+ const start = char;
478
+ const end = segment[i + 2];
479
+ // Validate range
480
+ if (start.charCodeAt(0) > end.charCodeAt(0)) {
481
+ state.errors.push({
482
+ code: 'INVALID_RANGE',
483
+ message: `Invalid range [${start}-${end}]: start > end`,
484
+ position: startIndex + i,
485
+ length: 3,
486
+ });
487
+ }
488
+ ranges.push({ start, end });
489
+ i += 3;
490
+ }
491
+ else {
492
+ chars += char;
493
+ i++;
494
+ }
495
+ }
496
+ // Unclosed bracket
497
+ state.errors.push({
498
+ code: 'UNCLOSED_BRACKET',
499
+ message: 'Unclosed character class',
500
+ position: startIndex,
501
+ length: segment.length - startIndex,
502
+ });
503
+ return {
504
+ charclass: { type: 'charclass', negated, ranges, chars },
505
+ endIndex: segment.length,
506
+ };
507
+ }
508
+ /**
509
+ * Unescape a literal segment (remove backslashes).
510
+ */
511
+ function unescapeSegment(segment) {
512
+ let result = '';
513
+ let i = 0;
514
+ while (i < segment.length) {
515
+ if (segment[i] === '\\' && i + 1 < segment.length) {
516
+ result += segment[i + 1];
517
+ i += 2;
518
+ }
519
+ else {
520
+ result += segment[i];
521
+ i++;
522
+ }
523
+ }
524
+ return result;
525
+ }
526
+ //# sourceMappingURL=parser.js.map