vinext 1.0.0-beta.1 → 1.0.0-beta.3
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 +10 -10
- package/dist/build/client-build-config.js +8 -1
- package/dist/build/report.d.ts +8 -1
- package/dist/build/report.js +12 -2
- package/dist/check.js +19 -2
- package/dist/cli.js +3 -2
- package/dist/config/config-matchers.d.ts +13 -37
- package/dist/config/config-matchers.js +35 -122
- package/dist/config/next-config.d.ts +15 -5
- package/dist/config/next-config.js +7 -1
- package/dist/config/request-context.d.ts +14 -0
- package/dist/config/request-context.js +26 -0
- package/dist/entries/app-browser-entry.d.ts +4 -10
- package/dist/entries/app-browser-entry.js +20 -4
- package/dist/entries/app-rsc-entry.js +30 -9
- package/dist/entries/app-rsc-manifest.js +36 -0
- package/dist/entries/pages-client-entry.js +0 -1
- package/dist/entries/pages-server-entry.js +13 -5
- package/dist/index.js +194 -101
- package/dist/init-cloudflare.d.ts +3 -1
- package/dist/init-cloudflare.js +59 -5
- package/dist/init.d.ts +6 -4
- package/dist/init.js +20 -22
- package/dist/plugins/extensionless-dynamic-import.js +23 -18
- package/dist/plugins/ignore-dynamic-requests.js +3 -1
- package/dist/plugins/import-meta-url.js +17 -6
- package/dist/plugins/og-assets.js +1 -1
- package/dist/plugins/require-context.js +20 -15
- package/dist/plugins/transform-cache.d.ts +19 -0
- package/dist/plugins/transform-cache.js +36 -0
- package/dist/routing/app-route-graph.d.ts +27 -11
- package/dist/routing/app-route-graph.js +158 -22
- package/dist/routing/app-router.js +12 -6
- package/dist/routing/file-matcher.d.ts +11 -22
- package/dist/routing/file-matcher.js +21 -33
- package/dist/routing/pages-router.js +23 -29
- package/dist/routing/route-matching.d.ts +14 -1
- package/dist/routing/route-matching.js +13 -1
- package/dist/routing/route-pattern.d.ts +2 -1
- package/dist/routing/route-pattern.js +7 -4
- package/dist/routing/route-trie.d.ts +5 -1
- package/dist/routing/route-trie.js +5 -2
- package/dist/server/api-handler.d.ts +2 -0
- package/dist/server/api-handler.js +3 -3
- package/dist/server/app-browser-entry.js +110 -37
- package/dist/server/app-browser-history-controller.d.ts +1 -1
- package/dist/server/app-browser-history-controller.js +3 -3
- package/dist/server/app-browser-navigation-controller.d.ts +23 -21
- package/dist/server/app-browser-navigation-controller.js +2 -2
- package/dist/server/app-browser-state.js +1 -1
- package/dist/server/app-browser-visible-commit.js +1 -1
- package/dist/server/app-fallback-renderer.d.ts +3 -0
- package/dist/server/app-fallback-renderer.js +4 -2
- package/dist/server/app-middleware.js +2 -1
- package/dist/server/app-page-boundary-render.d.ts +12 -1
- package/dist/server/app-page-boundary-render.js +84 -2
- package/dist/server/app-page-cache-finalizer.d.ts +1 -1
- package/dist/server/app-page-cache-finalizer.js +1 -1
- package/dist/server/app-page-cache.d.ts +2 -2
- package/dist/server/app-page-cache.js +16 -2
- package/dist/server/app-page-dispatch.d.ts +19 -2
- package/dist/server/app-page-dispatch.js +45 -23
- package/dist/server/app-page-element-builder.d.ts +9 -2
- package/dist/server/app-page-element-builder.js +144 -33
- package/dist/server/app-page-head.d.ts +56 -4
- package/dist/server/app-page-head.js +216 -85
- package/dist/server/app-page-http-access-fallback-metadata.d.ts +29 -0
- package/dist/server/app-page-http-access-fallback-metadata.js +114 -0
- package/dist/server/app-page-probe.d.ts +4 -0
- package/dist/server/app-page-probe.js +6 -2
- package/dist/server/app-page-render.d.ts +2 -2
- package/dist/server/app-page-render.js +1 -1
- package/dist/server/app-page-request.d.ts +15 -0
- package/dist/server/app-page-request.js +134 -14
- package/dist/server/app-page-response.js +1 -1
- package/dist/server/app-page-route-wiring.d.ts +16 -0
- package/dist/server/app-page-route-wiring.js +254 -52
- package/dist/server/app-pages-bridge.d.ts +1 -1
- package/dist/server/app-pages-bridge.js +2 -1
- package/dist/server/app-post-middleware-context.d.ts +1 -1
- package/dist/server/app-post-middleware-context.js +1 -1
- package/dist/server/app-route-handler-cache.js +4 -0
- package/dist/server/app-route-handler-dispatch.js +40 -20
- package/dist/server/app-route-handler-execution.d.ts +6 -1
- package/dist/server/app-route-handler-execution.js +42 -16
- package/dist/server/app-route-handler-policy.d.ts +2 -0
- package/dist/server/app-route-handler-policy.js +3 -3
- package/dist/server/app-route-handler-response.js +1 -1
- package/dist/server/app-route-module-loader.d.ts +9 -1
- package/dist/server/app-route-module-loader.js +4 -0
- package/dist/server/app-router-entry.js +6 -3
- package/dist/server/app-rsc-cache-busting.d.ts +1 -1
- package/dist/server/app-rsc-errors.d.ts +1 -1
- package/dist/server/app-rsc-errors.js +22 -2
- package/dist/server/app-rsc-handler.d.ts +10 -5
- package/dist/server/app-rsc-handler.js +73 -33
- package/dist/server/app-rsc-request-normalization.d.ts +3 -2
- package/dist/server/app-rsc-request-normalization.js +8 -2
- package/dist/server/app-rsc-response-finalizer.d.ts +2 -2
- package/dist/server/app-rsc-response-finalizer.js +5 -11
- package/dist/server/app-rsc-route-matching.d.ts +29 -0
- package/dist/server/app-rsc-route-matching.js +93 -11
- package/dist/server/app-segment-config.js +13 -3
- package/dist/server/app-server-action-execution.d.ts +3 -1
- package/dist/server/app-server-action-execution.js +9 -7
- package/dist/server/app-ssr-entry.js +6 -6
- package/dist/server/app-ssr-stream.d.ts +1 -7
- package/dist/server/app-ssr-stream.js +2 -10
- package/dist/server/cache-control.d.ts +2 -2
- package/dist/server/cache-control.js +2 -1
- package/dist/server/config-headers.d.ts +24 -0
- package/dist/server/config-headers.js +52 -0
- package/dist/server/dev-initial-server-error.d.ts +1 -1
- package/dist/server/dev-server.d.ts +3 -1
- package/dist/server/dev-server.js +170 -321
- package/dist/server/headers.d.ts +2 -2
- package/dist/server/headers.js +4 -3
- package/dist/server/isr-cache.d.ts +11 -34
- package/dist/server/isr-cache.js +33 -43
- package/dist/server/isr-decision.d.ts +2 -2
- package/dist/server/middleware-matcher-pattern.d.ts +22 -0
- package/dist/server/middleware-matcher-pattern.js +219 -0
- package/dist/server/middleware-matcher.d.ts +2 -7
- package/dist/server/middleware-matcher.js +13 -60
- package/dist/server/middleware-path-to-regexp.d.ts +14 -0
- package/dist/server/middleware-path-to-regexp.js +228 -0
- package/dist/server/middleware-runtime.js +4 -3
- package/dist/server/middleware.d.ts +1 -1
- package/dist/server/middleware.js +2 -1
- package/dist/server/pages-api-route.d.ts +2 -0
- package/dist/server/pages-api-route.js +4 -1
- package/dist/server/pages-data-route.d.ts +11 -1
- package/dist/server/pages-data-route.js +16 -1
- package/dist/server/pages-dev-hydration.js +0 -1
- package/dist/server/pages-get-initial-props.d.ts +16 -3
- package/dist/server/pages-get-initial-props.js +17 -10
- package/dist/server/pages-i18n.js +64 -16
- package/dist/server/pages-node-compat.d.ts +2 -0
- package/dist/server/pages-node-compat.js +7 -3
- package/dist/server/pages-page-data.d.ts +64 -10
- package/dist/server/pages-page-data.js +349 -80
- package/dist/server/pages-page-handler.d.ts +9 -3
- package/dist/server/pages-page-handler.js +131 -36
- package/dist/server/pages-page-response.d.ts +5 -3
- package/dist/server/pages-page-response.js +24 -27
- package/dist/server/pages-readiness.d.ts +3 -3
- package/dist/server/pages-request-pipeline.d.ts +8 -0
- package/dist/server/pages-request-pipeline.js +30 -12
- package/dist/server/pages-revalidate.d.ts +1 -1
- package/dist/server/pages-revalidate.js +54 -8
- package/dist/server/pages-router-entry.d.ts +1 -0
- package/dist/server/pages-router-entry.js +16 -4
- package/dist/server/prerender-route-params.js +2 -2
- package/dist/server/prod-server.d.ts +3 -5
- package/dist/server/prod-server.js +72 -35
- package/dist/server/request-pipeline.d.ts +15 -29
- package/dist/server/request-pipeline.js +24 -59
- package/dist/server/revalidation-host.d.ts +11 -0
- package/dist/server/revalidation-host.js +19 -0
- package/dist/server/rsc-stream-hints.js +122 -18
- package/dist/server/worker-revalidation-context.d.ts +13 -0
- package/dist/server/worker-revalidation-context.js +29 -0
- package/dist/shims/app.d.ts +1 -1
- package/dist/shims/cache-handler.d.ts +1 -1
- package/dist/shims/cache-handler.js +10 -7
- package/dist/shims/cache-request-state.d.ts +27 -1
- package/dist/shims/cache-request-state.js +60 -1
- package/dist/shims/cache-runtime.d.ts +18 -1
- package/dist/shims/cache-runtime.js +26 -8
- package/dist/shims/cache.d.ts +5 -5
- package/dist/shims/cache.js +27 -11
- package/dist/shims/document.d.ts +42 -83
- package/dist/shims/document.js +62 -53
- package/dist/shims/dynamic.d.ts +5 -25
- package/dist/shims/dynamic.js +29 -8
- package/dist/shims/error.d.ts +1 -1
- package/dist/shims/error.js +2 -2
- package/dist/shims/fetch-cache.js +13 -4
- package/dist/shims/font-google-base.d.ts +11 -4
- package/dist/shims/font-google-base.js +17 -8
- package/dist/shims/font-local.d.ts +10 -3
- package/dist/shims/font-local.js +41 -10
- package/dist/shims/head.d.ts +13 -2
- package/dist/shims/head.js +3 -3
- package/dist/shims/headers.d.ts +37 -43
- package/dist/shims/headers.js +183 -74
- package/dist/shims/image.d.ts +27 -37
- package/dist/shims/image.js +21 -14
- package/dist/shims/internal/cookie-serialize.d.ts +12 -9
- package/dist/shims/internal/cookie-serialize.js +10 -7
- package/dist/shims/internal/hybrid-client-route-owner.js +3 -1
- package/dist/shims/internal/interpolate-as.d.ts +15 -1
- package/dist/shims/internal/interpolate-as.js +27 -2
- package/dist/shims/internal/utils.d.ts +3 -3
- package/dist/shims/legacy-image.d.ts +3 -37
- package/dist/shims/legacy-image.js +6 -16
- package/dist/shims/link.d.ts +15 -22
- package/dist/shims/link.js +34 -13
- package/dist/shims/metadata.d.ts +7 -17
- package/dist/shims/metadata.js +74 -24
- package/dist/shims/navigation-context-state.d.ts +1 -1
- package/dist/shims/navigation.d.ts +7 -3
- package/dist/shims/navigation.js +25 -14
- package/dist/shims/navigation.react-server.js +1 -1
- package/dist/shims/next-shims-augmentations.d.ts +1 -0
- package/dist/shims/next-shims-public.d.ts +1 -0
- package/dist/shims/public-shim-map.json.js +103 -0
- package/dist/shims/readonly-url-search-params.d.ts +3 -3
- package/dist/shims/readonly-url-search-params.js +3 -3
- package/dist/shims/request-context.d.ts +4 -1
- package/dist/shims/request-state-types.d.ts +1 -1
- package/dist/shims/router.d.ts +55 -21
- package/dist/shims/router.js +180 -44
- package/dist/shims/script.d.ts +6 -10
- package/dist/shims/script.js +7 -4
- package/dist/shims/server.d.ts +50 -26
- package/dist/shims/server.js +193 -55
- package/dist/shims/unified-request-context.d.ts +22 -3
- package/dist/shims/unified-request-context.js +84 -1
- package/dist/shims/url-safety.d.ts +1 -1
- package/dist/shims/url-safety.js +2 -2
- package/dist/typegen.d.ts +8 -2
- package/dist/typegen.js +76 -15
- package/dist/utils/cache-control-metadata.d.ts +2 -1
- package/dist/utils/cache-control-metadata.js +5 -1
- package/dist/utils/domain-locale.d.ts +2 -2
- package/dist/utils/external-url.d.ts +5 -0
- package/dist/utils/external-url.js +7 -0
- package/dist/utils/html-limited-bots.js +1 -1
- package/dist/utils/protocol-headers.d.ts +7 -1
- package/dist/utils/protocol-headers.js +7 -1
- package/dist/utils/regex-safety.d.ts +8 -0
- package/dist/utils/regex-safety.js +737 -0
- package/package.json +13 -2
|
@@ -0,0 +1,737 @@
|
|
|
1
|
+
//#region src/utils/regex-safety.ts
|
|
2
|
+
const MAX_NODES = 16384;
|
|
3
|
+
const MAX_NESTING_DEPTH = 256;
|
|
4
|
+
const MAX_PATTERN_LENGTH = 65536;
|
|
5
|
+
const MAX_WORDS = 4096;
|
|
6
|
+
const MAX_WORD_SYMBOLS = 32768;
|
|
7
|
+
const MAX_OPAQUE_COMPARISONS = 4096;
|
|
8
|
+
const MAX_SEQUENCE_EXPANSIONS = 256;
|
|
9
|
+
const MAX_SAFE_OVERLAPPING_VARIABLE_BOUNDARIES = 1;
|
|
10
|
+
function canonicalizeIgnoreCase(character) {
|
|
11
|
+
const upper = character.toUpperCase();
|
|
12
|
+
if (upper.length !== 1) return character;
|
|
13
|
+
if (character.charCodeAt(0) >= 128 && upper.charCodeAt(0) < 128) return character;
|
|
14
|
+
return upper;
|
|
15
|
+
}
|
|
16
|
+
function literalSymbol(character, ignoreCase) {
|
|
17
|
+
return {
|
|
18
|
+
kind: "literal",
|
|
19
|
+
key: ignoreCase ? canonicalizeIgnoreCase(character) : character,
|
|
20
|
+
value: character
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
function createClassSymbol(values, nonAscii) {
|
|
24
|
+
return {
|
|
25
|
+
kind: "class",
|
|
26
|
+
key: `class:${nonAscii}:${[...values].sort().join("")}`,
|
|
27
|
+
values,
|
|
28
|
+
nonAscii
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
function shorthandClassSymbol(shorthand, ignoreCase) {
|
|
32
|
+
if (!"dDwWsS".includes(shorthand)) return null;
|
|
33
|
+
const regexp = new RegExp(`\\${shorthand}`);
|
|
34
|
+
const values = /* @__PURE__ */ new Set();
|
|
35
|
+
for (let code = 0; code <= 127; code++) {
|
|
36
|
+
const character = String.fromCharCode(code);
|
|
37
|
+
if (regexp.test(character)) values.add(ignoreCase ? canonicalizeIgnoreCase(character) : character);
|
|
38
|
+
}
|
|
39
|
+
return createClassSymbol(values, shorthand === "d" || shorthand === "w" ? "none" : shorthand === "s" ? "whitespace" : shorthand === "S" ? "non-whitespace" : "all");
|
|
40
|
+
}
|
|
41
|
+
function unionNonAscii(left, right) {
|
|
42
|
+
if (left === "none") return right;
|
|
43
|
+
if (right === "none" || left === right) return left;
|
|
44
|
+
return "all";
|
|
45
|
+
}
|
|
46
|
+
function simpleClassSymbol(raw, ignoreCase) {
|
|
47
|
+
const end = raw.length - 1;
|
|
48
|
+
if (raw[0] !== "[" || raw[end] !== "]" || raw[1] === "^") return null;
|
|
49
|
+
const values = /* @__PURE__ */ new Set();
|
|
50
|
+
let nonAscii = "none";
|
|
51
|
+
const add = (character) => {
|
|
52
|
+
if (character.charCodeAt(0) > 127) return false;
|
|
53
|
+
values.add(ignoreCase ? canonicalizeIgnoreCase(character) : character);
|
|
54
|
+
return true;
|
|
55
|
+
};
|
|
56
|
+
const addClass = (symbol) => {
|
|
57
|
+
if (symbol.kind !== "class") return false;
|
|
58
|
+
for (const value of symbol.values) values.add(value);
|
|
59
|
+
nonAscii = unionNonAscii(nonAscii, symbol.nonAscii);
|
|
60
|
+
return true;
|
|
61
|
+
};
|
|
62
|
+
for (let index = 1; index < end; index++) {
|
|
63
|
+
const start = raw[index];
|
|
64
|
+
if (start === "\\") {
|
|
65
|
+
const escaped = raw[++index];
|
|
66
|
+
if (escaped === void 0) return null;
|
|
67
|
+
const shorthand = shorthandClassSymbol(escaped, ignoreCase);
|
|
68
|
+
if (shorthand) {
|
|
69
|
+
if (!addClass(shorthand)) return null;
|
|
70
|
+
} else if ("\\-]".includes(escaped)) {
|
|
71
|
+
if (!add(escaped)) return null;
|
|
72
|
+
} else return null;
|
|
73
|
+
continue;
|
|
74
|
+
}
|
|
75
|
+
if (index + 2 < end && raw[index + 1] === "-") {
|
|
76
|
+
const rangeEnd = raw[index + 2];
|
|
77
|
+
if (rangeEnd === "\\") return null;
|
|
78
|
+
const startCode = start.charCodeAt(0);
|
|
79
|
+
const endCode = rangeEnd.charCodeAt(0);
|
|
80
|
+
if (startCode > endCode || endCode > 127) return null;
|
|
81
|
+
for (let code = startCode; code <= endCode; code++) if (!add(String.fromCharCode(code))) return null;
|
|
82
|
+
index += 2;
|
|
83
|
+
} else if (!add(start)) return null;
|
|
84
|
+
}
|
|
85
|
+
if (values.size === 0 && nonAscii === "none") return null;
|
|
86
|
+
return createClassSymbol(values, nonAscii);
|
|
87
|
+
}
|
|
88
|
+
var RegexParser = class {
|
|
89
|
+
pattern;
|
|
90
|
+
ignoreCase;
|
|
91
|
+
index = 0;
|
|
92
|
+
nodes = 0;
|
|
93
|
+
depth = 0;
|
|
94
|
+
exceededBudget = false;
|
|
95
|
+
constructor(pattern, ignoreCase) {
|
|
96
|
+
this.pattern = pattern;
|
|
97
|
+
this.ignoreCase = ignoreCase;
|
|
98
|
+
}
|
|
99
|
+
parse() {
|
|
100
|
+
return this.parseAlternation();
|
|
101
|
+
}
|
|
102
|
+
node(node) {
|
|
103
|
+
this.nodes++;
|
|
104
|
+
if (this.nodes > MAX_NODES) this.exceededBudget = true;
|
|
105
|
+
return node;
|
|
106
|
+
}
|
|
107
|
+
parseAlternation() {
|
|
108
|
+
const branches = [this.parseSequence()];
|
|
109
|
+
while (this.pattern[this.index] === "|") {
|
|
110
|
+
this.index++;
|
|
111
|
+
branches.push(this.parseSequence());
|
|
112
|
+
}
|
|
113
|
+
return branches.length === 1 ? branches[0] : this.node({
|
|
114
|
+
kind: "alternation",
|
|
115
|
+
branches
|
|
116
|
+
});
|
|
117
|
+
}
|
|
118
|
+
parseSequence() {
|
|
119
|
+
const children = [];
|
|
120
|
+
while (this.index < this.pattern.length) {
|
|
121
|
+
const character = this.pattern[this.index];
|
|
122
|
+
if (character === "|" || character === ")") break;
|
|
123
|
+
const term = this.parseTerm();
|
|
124
|
+
if (term.kind === "sequence") children.push(...term.children);
|
|
125
|
+
else children.push(term);
|
|
126
|
+
}
|
|
127
|
+
return children.length === 1 ? children[0] : this.node({
|
|
128
|
+
kind: "sequence",
|
|
129
|
+
children
|
|
130
|
+
});
|
|
131
|
+
}
|
|
132
|
+
parseTerm() {
|
|
133
|
+
const atom = this.parseAtom();
|
|
134
|
+
const quantifier = this.parseQuantifier();
|
|
135
|
+
if (!quantifier) return atom;
|
|
136
|
+
if (this.pattern[this.index] === "?") this.index++;
|
|
137
|
+
if (quantifier.min === 1 && quantifier.max === 1) return atom;
|
|
138
|
+
return this.node({
|
|
139
|
+
kind: "repeat",
|
|
140
|
+
child: atom,
|
|
141
|
+
...quantifier
|
|
142
|
+
});
|
|
143
|
+
}
|
|
144
|
+
parseAtom() {
|
|
145
|
+
const character = this.pattern[this.index++];
|
|
146
|
+
if (character === "(") return this.parseGroup();
|
|
147
|
+
if (character === "[") return this.parseClass();
|
|
148
|
+
if (character === "\\") return this.parseEscape();
|
|
149
|
+
if (character === "^" || character === "$") return this.node({
|
|
150
|
+
kind: "assertion",
|
|
151
|
+
child: this.node({
|
|
152
|
+
kind: "sequence",
|
|
153
|
+
children: []
|
|
154
|
+
})
|
|
155
|
+
});
|
|
156
|
+
if (character === ".") return this.node({
|
|
157
|
+
kind: "atom",
|
|
158
|
+
symbol: {
|
|
159
|
+
kind: "opaque",
|
|
160
|
+
key: ".",
|
|
161
|
+
pattern: ".",
|
|
162
|
+
ignoreCase: this.ignoreCase
|
|
163
|
+
},
|
|
164
|
+
fixedWidth: true
|
|
165
|
+
});
|
|
166
|
+
return this.node({
|
|
167
|
+
kind: "atom",
|
|
168
|
+
symbol: literalSymbol(character, this.ignoreCase),
|
|
169
|
+
fixedWidth: true
|
|
170
|
+
});
|
|
171
|
+
}
|
|
172
|
+
parseGroup() {
|
|
173
|
+
this.depth++;
|
|
174
|
+
if (this.depth > MAX_NESTING_DEPTH) {
|
|
175
|
+
this.exceededBudget = true;
|
|
176
|
+
this.skipGroup();
|
|
177
|
+
this.depth--;
|
|
178
|
+
return this.node({
|
|
179
|
+
kind: "atom",
|
|
180
|
+
symbol: null,
|
|
181
|
+
fixedWidth: false
|
|
182
|
+
});
|
|
183
|
+
}
|
|
184
|
+
let assertion = false;
|
|
185
|
+
if (this.pattern[this.index] === "?") {
|
|
186
|
+
const marker = this.pattern[this.index + 1];
|
|
187
|
+
if (marker === ":") this.index += 2;
|
|
188
|
+
else if (marker === "=" || marker === "!") {
|
|
189
|
+
assertion = true;
|
|
190
|
+
this.index += 2;
|
|
191
|
+
} else if (marker === "<" && (this.pattern[this.index + 2] === "=" || this.pattern[this.index + 2] === "!")) {
|
|
192
|
+
assertion = true;
|
|
193
|
+
this.index += 3;
|
|
194
|
+
} else if (marker === "<") {
|
|
195
|
+
const nameEnd = this.pattern.indexOf(">", this.index + 2);
|
|
196
|
+
this.index = nameEnd === -1 ? this.pattern.length : nameEnd + 1;
|
|
197
|
+
} else {
|
|
198
|
+
while (this.index < this.pattern.length && this.pattern[this.index] !== ")") this.index++;
|
|
199
|
+
if (this.pattern[this.index] === ")") this.index++;
|
|
200
|
+
this.depth--;
|
|
201
|
+
return this.node({
|
|
202
|
+
kind: "atom",
|
|
203
|
+
symbol: null,
|
|
204
|
+
fixedWidth: false
|
|
205
|
+
});
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
const child = this.parseAlternation();
|
|
209
|
+
if (this.pattern[this.index] === ")") this.index++;
|
|
210
|
+
this.depth--;
|
|
211
|
+
return assertion ? this.node({
|
|
212
|
+
kind: "assertion",
|
|
213
|
+
child
|
|
214
|
+
}) : child;
|
|
215
|
+
}
|
|
216
|
+
skipGroup() {
|
|
217
|
+
let depth = 1;
|
|
218
|
+
let inClass = false;
|
|
219
|
+
while (this.index < this.pattern.length && depth > 0) {
|
|
220
|
+
const character = this.pattern[this.index++];
|
|
221
|
+
if (character === "\\") {
|
|
222
|
+
this.index++;
|
|
223
|
+
continue;
|
|
224
|
+
}
|
|
225
|
+
if (character === "[") inClass = true;
|
|
226
|
+
else if (character === "]") inClass = false;
|
|
227
|
+
else if (!inClass && character === "(") depth++;
|
|
228
|
+
else if (!inClass && character === ")") depth--;
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
parseClass() {
|
|
232
|
+
const start = this.index - 1;
|
|
233
|
+
while (this.index < this.pattern.length) {
|
|
234
|
+
const character = this.pattern[this.index++];
|
|
235
|
+
if (character === "\\") this.index++;
|
|
236
|
+
else if (character === "]") break;
|
|
237
|
+
}
|
|
238
|
+
const raw = this.pattern.slice(start, this.index);
|
|
239
|
+
return this.node({
|
|
240
|
+
kind: "atom",
|
|
241
|
+
symbol: simpleClassSymbol(raw, this.ignoreCase) ?? {
|
|
242
|
+
kind: "opaque",
|
|
243
|
+
key: raw,
|
|
244
|
+
pattern: raw,
|
|
245
|
+
ignoreCase: this.ignoreCase
|
|
246
|
+
},
|
|
247
|
+
fixedWidth: true
|
|
248
|
+
});
|
|
249
|
+
}
|
|
250
|
+
parseEscape() {
|
|
251
|
+
const escaped = this.pattern[this.index++];
|
|
252
|
+
if (escaped === void 0) return this.node({
|
|
253
|
+
kind: "atom",
|
|
254
|
+
symbol: null,
|
|
255
|
+
fixedWidth: false
|
|
256
|
+
});
|
|
257
|
+
if (escaped === "b" || escaped === "B") return this.node({
|
|
258
|
+
kind: "assertion",
|
|
259
|
+
child: this.node({
|
|
260
|
+
kind: "sequence",
|
|
261
|
+
children: []
|
|
262
|
+
})
|
|
263
|
+
});
|
|
264
|
+
const shorthand = shorthandClassSymbol(escaped, this.ignoreCase);
|
|
265
|
+
if (shorthand) return this.node({
|
|
266
|
+
kind: "atom",
|
|
267
|
+
symbol: shorthand,
|
|
268
|
+
fixedWidth: true
|
|
269
|
+
});
|
|
270
|
+
if (/\d/.test(escaped)) return this.node({
|
|
271
|
+
kind: "atom",
|
|
272
|
+
symbol: null,
|
|
273
|
+
fixedWidth: false
|
|
274
|
+
});
|
|
275
|
+
let literal = null;
|
|
276
|
+
if (escaped === "x" && /^[\da-fA-F]{2}/.test(this.pattern.slice(this.index, this.index + 2))) {
|
|
277
|
+
literal = String.fromCharCode(Number.parseInt(this.pattern.slice(this.index, this.index + 2), 16));
|
|
278
|
+
this.index += 2;
|
|
279
|
+
} else if (escaped === "u" && /^[\da-fA-F]{4}/.test(this.pattern.slice(this.index, this.index + 4))) {
|
|
280
|
+
literal = String.fromCharCode(Number.parseInt(this.pattern.slice(this.index, this.index + 4), 16));
|
|
281
|
+
this.index += 4;
|
|
282
|
+
} else if ("nrtvf0".includes(escaped)) literal = {
|
|
283
|
+
n: "\n",
|
|
284
|
+
r: "\r",
|
|
285
|
+
t: " ",
|
|
286
|
+
v: "\v",
|
|
287
|
+
f: "\f",
|
|
288
|
+
0: "\0"
|
|
289
|
+
}[escaped];
|
|
290
|
+
else if (!/[A-Za-z]/.test(escaped)) literal = escaped;
|
|
291
|
+
if (literal !== null) return this.node({
|
|
292
|
+
kind: "atom",
|
|
293
|
+
symbol: literalSymbol(literal, this.ignoreCase),
|
|
294
|
+
fixedWidth: true
|
|
295
|
+
});
|
|
296
|
+
const raw = `\\${escaped}`;
|
|
297
|
+
return this.node({
|
|
298
|
+
kind: "atom",
|
|
299
|
+
symbol: {
|
|
300
|
+
kind: "opaque",
|
|
301
|
+
key: raw,
|
|
302
|
+
pattern: raw,
|
|
303
|
+
ignoreCase: this.ignoreCase
|
|
304
|
+
},
|
|
305
|
+
fixedWidth: true
|
|
306
|
+
});
|
|
307
|
+
}
|
|
308
|
+
parseQuantifier() {
|
|
309
|
+
const character = this.pattern[this.index];
|
|
310
|
+
if (character === "*") {
|
|
311
|
+
this.index++;
|
|
312
|
+
return {
|
|
313
|
+
min: 0,
|
|
314
|
+
max: Infinity
|
|
315
|
+
};
|
|
316
|
+
}
|
|
317
|
+
if (character === "+") {
|
|
318
|
+
this.index++;
|
|
319
|
+
return {
|
|
320
|
+
min: 1,
|
|
321
|
+
max: Infinity
|
|
322
|
+
};
|
|
323
|
+
}
|
|
324
|
+
if (character === "?") {
|
|
325
|
+
this.index++;
|
|
326
|
+
return {
|
|
327
|
+
min: 0,
|
|
328
|
+
max: 1
|
|
329
|
+
};
|
|
330
|
+
}
|
|
331
|
+
if (character !== "{") return null;
|
|
332
|
+
const start = this.index;
|
|
333
|
+
let cursor = start + 1;
|
|
334
|
+
while (/\d/.test(this.pattern[cursor] ?? "")) cursor++;
|
|
335
|
+
if (cursor === start + 1) return null;
|
|
336
|
+
const min = Number(this.pattern.slice(start + 1, cursor));
|
|
337
|
+
if (this.pattern[cursor] === "}") {
|
|
338
|
+
this.index = cursor + 1;
|
|
339
|
+
return {
|
|
340
|
+
min,
|
|
341
|
+
max: min
|
|
342
|
+
};
|
|
343
|
+
}
|
|
344
|
+
if (this.pattern[cursor] !== ",") return null;
|
|
345
|
+
cursor++;
|
|
346
|
+
const maxStart = cursor;
|
|
347
|
+
while (/\d/.test(this.pattern[cursor] ?? "")) cursor++;
|
|
348
|
+
if (this.pattern[cursor] !== "}") return null;
|
|
349
|
+
const max = cursor === maxStart ? Infinity : Number(this.pattern.slice(maxStart, cursor));
|
|
350
|
+
this.index = cursor + 1;
|
|
351
|
+
return {
|
|
352
|
+
min,
|
|
353
|
+
max
|
|
354
|
+
};
|
|
355
|
+
}
|
|
356
|
+
};
|
|
357
|
+
function exactWidth(node) {
|
|
358
|
+
switch (node.kind) {
|
|
359
|
+
case "atom": return node.fixedWidth ? 1 : null;
|
|
360
|
+
case "assertion": return 0;
|
|
361
|
+
case "sequence": {
|
|
362
|
+
let width = 0;
|
|
363
|
+
for (const child of node.children) {
|
|
364
|
+
const childWidth = exactWidth(child);
|
|
365
|
+
if (childWidth === null) return null;
|
|
366
|
+
width += childWidth;
|
|
367
|
+
}
|
|
368
|
+
return width;
|
|
369
|
+
}
|
|
370
|
+
case "alternation": {
|
|
371
|
+
let width;
|
|
372
|
+
for (const branch of node.branches) {
|
|
373
|
+
const branchWidth = exactWidth(branch);
|
|
374
|
+
if (branchWidth === null) return null;
|
|
375
|
+
if (width === void 0) width = branchWidth;
|
|
376
|
+
else if (width !== branchWidth) return null;
|
|
377
|
+
}
|
|
378
|
+
return width ?? 0;
|
|
379
|
+
}
|
|
380
|
+
case "repeat": {
|
|
381
|
+
if (node.min !== node.max || !Number.isFinite(node.max)) return null;
|
|
382
|
+
const childWidth = exactWidth(node.child);
|
|
383
|
+
return childWidth === null ? null : childWidth * node.min;
|
|
384
|
+
}
|
|
385
|
+
}
|
|
386
|
+
}
|
|
387
|
+
function containsConsumingRepetition(node) {
|
|
388
|
+
switch (node.kind) {
|
|
389
|
+
case "atom": return false;
|
|
390
|
+
case "assertion": return false;
|
|
391
|
+
case "sequence": return node.children.some(containsConsumingRepetition);
|
|
392
|
+
case "alternation": return node.branches.some(containsConsumingRepetition);
|
|
393
|
+
case "repeat": return node.min !== 1 || node.max !== 1 || containsConsumingRepetition(node.child);
|
|
394
|
+
}
|
|
395
|
+
}
|
|
396
|
+
function containsConsumingAlternation(node) {
|
|
397
|
+
switch (node.kind) {
|
|
398
|
+
case "atom": return false;
|
|
399
|
+
case "assertion": return false;
|
|
400
|
+
case "sequence": return node.children.some(containsConsumingAlternation);
|
|
401
|
+
case "alternation": return true;
|
|
402
|
+
case "repeat": return containsConsumingAlternation(node.child);
|
|
403
|
+
}
|
|
404
|
+
}
|
|
405
|
+
function fixedWords(node, budget) {
|
|
406
|
+
if (budget.exceeded) return null;
|
|
407
|
+
switch (node.kind) {
|
|
408
|
+
case "atom": return node.symbol ? [[node.symbol]] : null;
|
|
409
|
+
case "assertion": return [[]];
|
|
410
|
+
case "sequence": {
|
|
411
|
+
let words = [[]];
|
|
412
|
+
for (const child of node.children) {
|
|
413
|
+
const childWords = fixedWords(child, budget);
|
|
414
|
+
if (!childWords) return null;
|
|
415
|
+
const next = [];
|
|
416
|
+
for (const prefix of words) for (const suffix of childWords) {
|
|
417
|
+
if (++budget.words > MAX_WORDS) {
|
|
418
|
+
budget.exceeded = true;
|
|
419
|
+
return null;
|
|
420
|
+
}
|
|
421
|
+
const word = [...prefix, ...suffix];
|
|
422
|
+
budget.symbols += word.length;
|
|
423
|
+
if (budget.symbols > MAX_WORD_SYMBOLS) {
|
|
424
|
+
budget.exceeded = true;
|
|
425
|
+
return null;
|
|
426
|
+
}
|
|
427
|
+
next.push(word);
|
|
428
|
+
}
|
|
429
|
+
words = next;
|
|
430
|
+
}
|
|
431
|
+
return words;
|
|
432
|
+
}
|
|
433
|
+
case "alternation": {
|
|
434
|
+
const words = [];
|
|
435
|
+
for (const branch of node.branches) {
|
|
436
|
+
const branchWords = fixedWords(branch, budget);
|
|
437
|
+
if (!branchWords) return null;
|
|
438
|
+
words.push(...branchWords);
|
|
439
|
+
if ((budget.words += branchWords.length) > MAX_WORDS) {
|
|
440
|
+
budget.exceeded = true;
|
|
441
|
+
return null;
|
|
442
|
+
}
|
|
443
|
+
}
|
|
444
|
+
return words;
|
|
445
|
+
}
|
|
446
|
+
case "repeat": {
|
|
447
|
+
if (node.min !== node.max || !Number.isFinite(node.max)) return null;
|
|
448
|
+
let words = [[]];
|
|
449
|
+
const childWords = fixedWords(node.child, budget);
|
|
450
|
+
if (!childWords) return null;
|
|
451
|
+
for (let count = 0; count < node.min; count++) {
|
|
452
|
+
const next = [];
|
|
453
|
+
for (const prefix of words) for (const suffix of childWords) {
|
|
454
|
+
if (++budget.words > MAX_WORDS) {
|
|
455
|
+
budget.exceeded = true;
|
|
456
|
+
return null;
|
|
457
|
+
}
|
|
458
|
+
const word = [...prefix, ...suffix];
|
|
459
|
+
budget.symbols += word.length;
|
|
460
|
+
if (budget.symbols > MAX_WORD_SYMBOLS) {
|
|
461
|
+
budget.exceeded = true;
|
|
462
|
+
return null;
|
|
463
|
+
}
|
|
464
|
+
next.push(word);
|
|
465
|
+
}
|
|
466
|
+
words = next;
|
|
467
|
+
}
|
|
468
|
+
return words;
|
|
469
|
+
}
|
|
470
|
+
}
|
|
471
|
+
}
|
|
472
|
+
function createTrieNode() {
|
|
473
|
+
return {
|
|
474
|
+
terminal: false,
|
|
475
|
+
edges: /* @__PURE__ */ new Map(),
|
|
476
|
+
complexEdges: []
|
|
477
|
+
};
|
|
478
|
+
}
|
|
479
|
+
function opaqueMatchesLiteral(opaque, literal) {
|
|
480
|
+
if (opaque.kind !== "opaque" || literal.kind !== "literal") return false;
|
|
481
|
+
try {
|
|
482
|
+
return new RegExp(`^(?:${opaque.pattern})$`, opaque.ignoreCase ? "i" : "").test(literal.value);
|
|
483
|
+
} catch {
|
|
484
|
+
return true;
|
|
485
|
+
}
|
|
486
|
+
}
|
|
487
|
+
function classMatchesLiteral(characterClass, literal) {
|
|
488
|
+
if (characterClass.kind !== "class" || literal.kind !== "literal") return false;
|
|
489
|
+
if (literal.value.charCodeAt(0) <= 127) return characterClass.values.has(literal.key);
|
|
490
|
+
if (characterClass.nonAscii === "all") return true;
|
|
491
|
+
return /\s/.test(literal.value) ? characterClass.nonAscii === "whitespace" : characterClass.nonAscii === "non-whitespace";
|
|
492
|
+
}
|
|
493
|
+
function nonAsciiDomainsOverlap(left, right) {
|
|
494
|
+
if (left === "none" || right === "none") return false;
|
|
495
|
+
if (left === "all" || right === "all") return true;
|
|
496
|
+
return left === right;
|
|
497
|
+
}
|
|
498
|
+
function symbolsMayOverlap(left, right) {
|
|
499
|
+
if (left.kind === "literal" && right.kind === "literal") return left.key === right.key;
|
|
500
|
+
if (left.kind === "class" && right.kind === "literal") return classMatchesLiteral(left, right);
|
|
501
|
+
if (left.kind === "literal" && right.kind === "class") return classMatchesLiteral(right, left);
|
|
502
|
+
if (left.kind === "class" && right.kind === "class") {
|
|
503
|
+
const [smaller, larger] = left.values.size <= right.values.size ? [left.values, right.values] : [right.values, left.values];
|
|
504
|
+
for (const value of smaller) if (larger.has(value)) return true;
|
|
505
|
+
return nonAsciiDomainsOverlap(left.nonAscii, right.nonAscii);
|
|
506
|
+
}
|
|
507
|
+
if (left.kind === "opaque" && right.kind === "literal") return opaqueMatchesLiteral(left, right);
|
|
508
|
+
if (left.kind === "literal" && right.kind === "opaque") return opaqueMatchesLiteral(right, left);
|
|
509
|
+
return true;
|
|
510
|
+
}
|
|
511
|
+
function insertPrefixFreeWord(root, word, comparisons) {
|
|
512
|
+
let node = root;
|
|
513
|
+
for (const symbol of word) {
|
|
514
|
+
if (node.terminal) return false;
|
|
515
|
+
let edge = node.edges.get(symbol.key);
|
|
516
|
+
if (!edge) {
|
|
517
|
+
const candidates = symbol.kind === "literal" ? node.complexEdges : node.edges.values();
|
|
518
|
+
for (const candidate of candidates) {
|
|
519
|
+
if (++comparisons.count > MAX_OPAQUE_COMPARISONS) return false;
|
|
520
|
+
if (symbolsMayOverlap(candidate.symbol, symbol)) return false;
|
|
521
|
+
}
|
|
522
|
+
edge = {
|
|
523
|
+
symbol,
|
|
524
|
+
node: createTrieNode()
|
|
525
|
+
};
|
|
526
|
+
node.edges.set(symbol.key, edge);
|
|
527
|
+
if (symbol.kind !== "literal") node.complexEdges.push(edge);
|
|
528
|
+
}
|
|
529
|
+
node = edge.node;
|
|
530
|
+
}
|
|
531
|
+
if (node.terminal || node.edges.size > 0) return false;
|
|
532
|
+
node.terminal = true;
|
|
533
|
+
return true;
|
|
534
|
+
}
|
|
535
|
+
function hasPrefixFreeFiniteLanguage(node) {
|
|
536
|
+
const budget = {
|
|
537
|
+
words: 0,
|
|
538
|
+
symbols: 0,
|
|
539
|
+
exceeded: false
|
|
540
|
+
};
|
|
541
|
+
const words = fixedWords(node, budget);
|
|
542
|
+
if (!words) return {
|
|
543
|
+
safe: false,
|
|
544
|
+
budgetExceeded: budget.exceeded,
|
|
545
|
+
wordCount: 0
|
|
546
|
+
};
|
|
547
|
+
const root = createTrieNode();
|
|
548
|
+
const comparisons = { count: 0 };
|
|
549
|
+
for (const word of words) if (!insertPrefixFreeWord(root, word, comparisons)) return {
|
|
550
|
+
safe: false,
|
|
551
|
+
budgetExceeded: comparisons.count > MAX_OPAQUE_COMPARISONS,
|
|
552
|
+
wordCount: words.length
|
|
553
|
+
};
|
|
554
|
+
return {
|
|
555
|
+
safe: true,
|
|
556
|
+
budgetExceeded: false,
|
|
557
|
+
wordCount: words.length
|
|
558
|
+
};
|
|
559
|
+
}
|
|
560
|
+
function ambiguousExpansionFactor(node) {
|
|
561
|
+
switch (node.kind) {
|
|
562
|
+
case "atom":
|
|
563
|
+
case "assertion": return 1;
|
|
564
|
+
case "alternation": {
|
|
565
|
+
const result = hasPrefixFreeFiniteLanguage(node);
|
|
566
|
+
if (result.safe) return 1;
|
|
567
|
+
if (result.budgetExceeded || result.wordCount === 0) return 257;
|
|
568
|
+
return result.wordCount;
|
|
569
|
+
}
|
|
570
|
+
case "sequence": {
|
|
571
|
+
let factor = 1;
|
|
572
|
+
for (const child of node.children) {
|
|
573
|
+
factor *= ambiguousExpansionFactor(child);
|
|
574
|
+
if (factor > MAX_SEQUENCE_EXPANSIONS) return factor;
|
|
575
|
+
}
|
|
576
|
+
return factor;
|
|
577
|
+
}
|
|
578
|
+
case "repeat": {
|
|
579
|
+
if (node.min !== node.max || !Number.isFinite(node.max)) return 1;
|
|
580
|
+
const childFactor = ambiguousExpansionFactor(node.child);
|
|
581
|
+
let factor = 1;
|
|
582
|
+
for (let count = 0; count < node.max; count++) {
|
|
583
|
+
factor *= childFactor;
|
|
584
|
+
if (factor > MAX_SEQUENCE_EXPANSIONS) return factor;
|
|
585
|
+
}
|
|
586
|
+
return factor;
|
|
587
|
+
}
|
|
588
|
+
}
|
|
589
|
+
}
|
|
590
|
+
function isNullable(node) {
|
|
591
|
+
switch (node.kind) {
|
|
592
|
+
case "atom": return !node.fixedWidth;
|
|
593
|
+
case "assertion": return true;
|
|
594
|
+
case "sequence": return node.children.every(isNullable);
|
|
595
|
+
case "alternation": return node.branches.some(isNullable);
|
|
596
|
+
case "repeat": return node.min === 0 || isNullable(node.child);
|
|
597
|
+
}
|
|
598
|
+
}
|
|
599
|
+
function firstSymbols(node) {
|
|
600
|
+
switch (node.kind) {
|
|
601
|
+
case "atom": return node.symbol ? [node.symbol] : null;
|
|
602
|
+
case "assertion": return [];
|
|
603
|
+
case "repeat": return firstSymbols(node.child);
|
|
604
|
+
case "alternation": {
|
|
605
|
+
const symbols = [];
|
|
606
|
+
for (const branch of node.branches) {
|
|
607
|
+
const branchSymbols = firstSymbols(branch);
|
|
608
|
+
if (!branchSymbols) return null;
|
|
609
|
+
symbols.push(...branchSymbols);
|
|
610
|
+
}
|
|
611
|
+
return symbols;
|
|
612
|
+
}
|
|
613
|
+
case "sequence": {
|
|
614
|
+
const symbols = [];
|
|
615
|
+
for (const child of node.children) {
|
|
616
|
+
const childSymbols = firstSymbols(child);
|
|
617
|
+
if (!childSymbols) return null;
|
|
618
|
+
symbols.push(...childSymbols);
|
|
619
|
+
if (!isNullable(child)) break;
|
|
620
|
+
}
|
|
621
|
+
return symbols;
|
|
622
|
+
}
|
|
623
|
+
}
|
|
624
|
+
}
|
|
625
|
+
function lastSymbols(node) {
|
|
626
|
+
switch (node.kind) {
|
|
627
|
+
case "atom": return node.symbol ? [node.symbol] : null;
|
|
628
|
+
case "assertion": return [];
|
|
629
|
+
case "repeat": return lastSymbols(node.child);
|
|
630
|
+
case "alternation": {
|
|
631
|
+
const symbols = [];
|
|
632
|
+
for (const branch of node.branches) {
|
|
633
|
+
const branchSymbols = lastSymbols(branch);
|
|
634
|
+
if (!branchSymbols) return null;
|
|
635
|
+
symbols.push(...branchSymbols);
|
|
636
|
+
}
|
|
637
|
+
return symbols;
|
|
638
|
+
}
|
|
639
|
+
case "sequence": {
|
|
640
|
+
const symbols = [];
|
|
641
|
+
for (let index = node.children.length - 1; index >= 0; index--) {
|
|
642
|
+
const child = node.children[index];
|
|
643
|
+
const childSymbols = lastSymbols(child);
|
|
644
|
+
if (!childSymbols) return null;
|
|
645
|
+
symbols.push(...childSymbols);
|
|
646
|
+
if (!isNullable(child)) break;
|
|
647
|
+
}
|
|
648
|
+
return symbols;
|
|
649
|
+
}
|
|
650
|
+
}
|
|
651
|
+
}
|
|
652
|
+
function boundariesMayOverlap(left, right, comparisons) {
|
|
653
|
+
if (!left || !right) return true;
|
|
654
|
+
for (const leftSymbol of left) for (const rightSymbol of right) {
|
|
655
|
+
if (++comparisons.count > MAX_OPAQUE_COMPARISONS) return true;
|
|
656
|
+
if (symbolsMayOverlap(leftSymbol, rightSymbol)) return true;
|
|
657
|
+
}
|
|
658
|
+
return false;
|
|
659
|
+
}
|
|
660
|
+
function findSequenceIssue(node) {
|
|
661
|
+
if (ambiguousExpansionFactor(node) > MAX_SEQUENCE_EXPANSIONS) return "ambiguous sequence expansion";
|
|
662
|
+
let pendingRepetitionEnds = [];
|
|
663
|
+
const comparisons = { count: 0 };
|
|
664
|
+
let overlappingBoundaryCount = 0;
|
|
665
|
+
for (const child of node.children) if (child.kind === "repeat" && (child.min !== child.max || !Number.isFinite(child.max))) {
|
|
666
|
+
const starts = firstSymbols(child);
|
|
667
|
+
if (pendingRepetitionEnds.filter((ends) => boundariesMayOverlap(ends, starts, comparisons)).length > 0) overlappingBoundaryCount++;
|
|
668
|
+
else if (!isNullable(child)) overlappingBoundaryCount = 0;
|
|
669
|
+
if (overlappingBoundaryCount > MAX_SAFE_OVERLAPPING_VARIABLE_BOUNDARIES) return "overlapping sequential repetition";
|
|
670
|
+
const ends = lastSymbols(child);
|
|
671
|
+
pendingRepetitionEnds = isNullable(child) ? [...pendingRepetitionEnds, ends] : [ends];
|
|
672
|
+
} else if (!isNullable(child)) {
|
|
673
|
+
pendingRepetitionEnds = [];
|
|
674
|
+
overlappingBoundaryCount = 0;
|
|
675
|
+
}
|
|
676
|
+
return null;
|
|
677
|
+
}
|
|
678
|
+
function findSafetyIssue(node) {
|
|
679
|
+
switch (node.kind) {
|
|
680
|
+
case "atom": return null;
|
|
681
|
+
case "assertion": return findSafetyIssue(node.child);
|
|
682
|
+
case "sequence": {
|
|
683
|
+
const sequenceIssue = findSequenceIssue(node);
|
|
684
|
+
if (sequenceIssue) return sequenceIssue;
|
|
685
|
+
for (const child of node.children) {
|
|
686
|
+
const issue = findSafetyIssue(child);
|
|
687
|
+
if (issue) return issue;
|
|
688
|
+
}
|
|
689
|
+
return null;
|
|
690
|
+
}
|
|
691
|
+
case "alternation":
|
|
692
|
+
for (const branch of node.branches) {
|
|
693
|
+
const issue = findSafetyIssue(branch);
|
|
694
|
+
if (issue) return issue;
|
|
695
|
+
}
|
|
696
|
+
return null;
|
|
697
|
+
case "repeat": {
|
|
698
|
+
const nestedRepetition = containsConsumingRepetition(node.child);
|
|
699
|
+
if (node.max > 1 && nestedRepetition && exactWidth(node.child) === null) return "nested repetition";
|
|
700
|
+
if (node.max > 1 && containsConsumingAlternation(node.child)) {
|
|
701
|
+
const prefixFree = hasPrefixFreeFiniteLanguage(node.child);
|
|
702
|
+
if (!prefixFree.safe) return prefixFree.budgetExceeded ? "analysis budget exceeded" : "ambiguous alternatives under repetition";
|
|
703
|
+
}
|
|
704
|
+
return findSafetyIssue(node.child);
|
|
705
|
+
}
|
|
706
|
+
}
|
|
707
|
+
}
|
|
708
|
+
function analyzeRegexSafety(pattern, options = {}) {
|
|
709
|
+
if (pattern.length > MAX_PATTERN_LENGTH) return "analysis budget exceeded";
|
|
710
|
+
const parser = new RegexParser(pattern, options.ignoreCase === true);
|
|
711
|
+
const node = parser.parse();
|
|
712
|
+
if (parser.exceededBudget) return "analysis budget exceeded";
|
|
713
|
+
return findSafetyIssue(node);
|
|
714
|
+
}
|
|
715
|
+
function regexAtomsMayOverlap(left, right, ignoreCase = false) {
|
|
716
|
+
const leftParser = new RegexParser(left, ignoreCase);
|
|
717
|
+
const rightParser = new RegexParser(right, ignoreCase);
|
|
718
|
+
const leftNode = leftParser.parse();
|
|
719
|
+
const rightNode = rightParser.parse();
|
|
720
|
+
const leftWords = fixedWords(leftNode, {
|
|
721
|
+
words: 0,
|
|
722
|
+
symbols: 0,
|
|
723
|
+
exceeded: false
|
|
724
|
+
});
|
|
725
|
+
const rightWords = fixedWords(rightNode, {
|
|
726
|
+
words: 0,
|
|
727
|
+
symbols: 0,
|
|
728
|
+
exceeded: false
|
|
729
|
+
});
|
|
730
|
+
if (!leftWords || !rightWords || leftWords.length !== 1 || rightWords.length !== 1) return true;
|
|
731
|
+
const leftSymbol = leftWords[0][0];
|
|
732
|
+
const rightSymbol = rightWords[0][0];
|
|
733
|
+
if (!leftSymbol || !rightSymbol) return true;
|
|
734
|
+
return symbolsMayOverlap(leftSymbol, rightSymbol);
|
|
735
|
+
}
|
|
736
|
+
//#endregion
|
|
737
|
+
export { analyzeRegexSafety, regexAtomsMayOverlap };
|