@blamejs/blamejs-shop 0.5.19 → 0.5.21

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 (194) hide show
  1. package/CHANGELOG.md +4 -0
  2. package/README.md +1 -0
  3. package/SECURITY.md +17 -0
  4. package/lib/addresses.js +2 -2
  5. package/lib/admin.js +49 -45
  6. package/lib/affiliates.js +7 -23
  7. package/lib/announcement-bar.js +5 -22
  8. package/lib/api-keys.js +4 -13
  9. package/lib/asset-manifest.json +1 -1
  10. package/lib/banner-ab-tests.js +5 -9
  11. package/lib/blog-articles.js +8 -26
  12. package/lib/captcha-gate.js +7 -17
  13. package/lib/carrier-accounts.js +3 -10
  14. package/lib/catalog-drafts.js +5 -12
  15. package/lib/category-navigation.js +4 -20
  16. package/lib/cms-blocks.js +4 -7
  17. package/lib/compliance-export.js +4 -11
  18. package/lib/consent-ledger.js +2 -4
  19. package/lib/cost-layers.js +56 -21
  20. package/lib/customer-import.js +2 -2
  21. package/lib/customer-notes.js +7 -23
  22. package/lib/customer-roles.js +3 -5
  23. package/lib/customer-segments.js +17 -14
  24. package/lib/customer-surveys.js +7 -14
  25. package/lib/customers.js +2 -4
  26. package/lib/dispute-resolution.js +5 -13
  27. package/lib/dropship-forwarding.js +3 -5
  28. package/lib/dunning.js +2 -7
  29. package/lib/email-ab-tests.js +4 -8
  30. package/lib/email-campaigns.js +25 -14
  31. package/lib/email-warmup.js +2 -7
  32. package/lib/experiments.js +6 -10
  33. package/lib/externaldb-d1.js +78 -44
  34. package/lib/geolocation.js +18 -28
  35. package/lib/gift-options.js +5 -23
  36. package/lib/knowledge-base.js +7 -22
  37. package/lib/line-gift-wrap.js +3 -17
  38. package/lib/live-chat.js +5 -18
  39. package/lib/metered-usage.js +3 -8
  40. package/lib/notifications.js +3 -7
  41. package/lib/operator-accounts.js +3 -5
  42. package/lib/operator-approvals.js +6 -8
  43. package/lib/operator-audit-log.js +2 -7
  44. package/lib/operator-help-center.js +5 -21
  45. package/lib/operator-inbox.js +4 -11
  46. package/lib/operator-roles.js +5 -7
  47. package/lib/order-escalation.js +2 -2
  48. package/lib/order-export.js +24 -20
  49. package/lib/order-notes.js +4 -14
  50. package/lib/order-ratings.js +4 -10
  51. package/lib/payment-methods.js +5 -13
  52. package/lib/payment-retries.js +2 -7
  53. package/lib/payment.js +103 -58
  54. package/lib/pixel-events.js +8 -23
  55. package/lib/plan-changes.js +2 -7
  56. package/lib/product-qa.js +4 -8
  57. package/lib/promo-banners.js +7 -15
  58. package/lib/purchase-orders.js +3 -6
  59. package/lib/push-notifications.js +7 -32
  60. package/lib/pwa-manifest.js +6 -11
  61. package/lib/quotes.js +5 -9
  62. package/lib/referrals.js +10 -6
  63. package/lib/reviews.js +4 -10
  64. package/lib/robots-config.js +6 -14
  65. package/lib/sales-tax-filings.js +2 -2
  66. package/lib/search-facets.js +5 -5
  67. package/lib/search-ranking.js +4 -7
  68. package/lib/search-synonyms.js +7 -21
  69. package/lib/security-middleware.js +125 -70
  70. package/lib/seller-signup.js +10 -26
  71. package/lib/sidebar-widgets.js +6 -15
  72. package/lib/site-redirects.js +49 -137
  73. package/lib/sitemap-generator.js +3 -7
  74. package/lib/sms-dispatcher.js +7 -23
  75. package/lib/stock-receipts.js +4 -10
  76. package/lib/storefront-dashboards.js +2 -10
  77. package/lib/storefront-forms.js +9 -22
  78. package/lib/storefront-pages.js +6 -20
  79. package/lib/storefront.js +21 -23
  80. package/lib/subscription-billing.js +3 -8
  81. package/lib/subscription-controls.js +2 -7
  82. package/lib/suggestion-box.js +9 -26
  83. package/lib/support-tickets.js +6 -24
  84. package/lib/tax-remittance.js +2 -2
  85. package/lib/tenants.js +2 -7
  86. package/lib/text-guard.js +137 -35
  87. package/lib/theme-assets.js +4 -16
  88. package/lib/translations.js +3 -6
  89. package/lib/trust-badges.js +4 -7
  90. package/lib/vendor/MANIFEST.json +107 -97
  91. package/lib/vendor/blamejs/CHANGELOG.md +180 -0
  92. package/lib/vendor/blamejs/NOTICE +14 -10
  93. package/lib/vendor/blamejs/README.md +17 -5
  94. package/lib/vendor/blamejs/index.js +2 -0
  95. package/lib/vendor/blamejs/lib/acme.js +9 -9
  96. package/lib/vendor/blamejs/lib/api-snapshot.js +35 -16
  97. package/lib/vendor/blamejs/lib/app-shutdown.js +1 -1
  98. package/lib/vendor/blamejs/lib/archive-adapters.js +1 -1
  99. package/lib/vendor/blamejs/lib/archive.js +13 -24
  100. package/lib/vendor/blamejs/lib/auth/access-lock.js +2 -1
  101. package/lib/vendor/blamejs/lib/auth/bot-challenge.js +3 -6
  102. package/lib/vendor/blamejs/lib/auth/ciba.js +32 -3
  103. package/lib/vendor/blamejs/lib/auth/oauth.js +65 -8
  104. package/lib/vendor/blamejs/lib/auth/password.js +1 -1
  105. package/lib/vendor/blamejs/lib/auth/saml.js +21 -3
  106. package/lib/vendor/blamejs/lib/auth-bot-challenge.js +3 -3
  107. package/lib/vendor/blamejs/lib/backup/index.js +5 -5
  108. package/lib/vendor/blamejs/lib/case-fold-classes.js +491 -0
  109. package/lib/vendor/blamejs/lib/codepoint-class.js +131 -0
  110. package/lib/vendor/blamejs/lib/constants.js +79 -0
  111. package/lib/vendor/blamejs/lib/db-declare-view.js +8 -2
  112. package/lib/vendor/blamejs/lib/error-page.js +20 -1
  113. package/lib/vendor/blamejs/lib/external-db.js +5 -1
  114. package/lib/vendor/blamejs/lib/flag-evaluation-context.js +24 -6
  115. package/lib/vendor/blamejs/lib/flag.js +28 -2
  116. package/lib/vendor/blamejs/lib/framework-schema.js +14 -3
  117. package/lib/vendor/blamejs/lib/guard-regex.js +2345 -89
  118. package/lib/vendor/blamejs/lib/http-client.js +217 -40
  119. package/lib/vendor/blamejs/lib/mail-bimi.js +1 -1
  120. package/lib/vendor/blamejs/lib/mail-crypto-pgp.js +3 -2
  121. package/lib/vendor/blamejs/lib/mail-dav.js +26 -26
  122. package/lib/vendor/blamejs/lib/mail-deploy.js +10 -10
  123. package/lib/vendor/blamejs/lib/mail-scan.js +3 -0
  124. package/lib/vendor/blamejs/lib/mail-store.js +7 -1
  125. package/lib/vendor/blamejs/lib/mail.js +5 -1
  126. package/lib/vendor/blamejs/lib/middleware/age-gate.js +3 -2
  127. package/lib/vendor/blamejs/lib/middleware/api-encrypt.js +1 -1
  128. package/lib/vendor/blamejs/lib/middleware/assetlinks.js +2 -1
  129. package/lib/vendor/blamejs/lib/middleware/asyncapi-serve.js +3 -2
  130. package/lib/vendor/blamejs/lib/middleware/bearer-auth.js +2 -1
  131. package/lib/vendor/blamejs/lib/middleware/compression.js +1 -1
  132. package/lib/vendor/blamejs/lib/middleware/cors.js +1 -1
  133. package/lib/vendor/blamejs/lib/middleware/csp-report.js +5 -5
  134. package/lib/vendor/blamejs/lib/middleware/csrf-protect.js +2 -1
  135. package/lib/vendor/blamejs/lib/middleware/daily-byte-quota.js +2 -2
  136. package/lib/vendor/blamejs/lib/middleware/deny-response.js +2 -1
  137. package/lib/vendor/blamejs/lib/middleware/dpop.js +2 -2
  138. package/lib/vendor/blamejs/lib/middleware/fetch-metadata.js +2 -1
  139. package/lib/vendor/blamejs/lib/middleware/flag-context.js +17 -0
  140. package/lib/vendor/blamejs/lib/middleware/idempotency-key.js +10 -8
  141. package/lib/vendor/blamejs/lib/middleware/openapi-serve.js +3 -2
  142. package/lib/vendor/blamejs/lib/middleware/require-aal.js +3 -2
  143. package/lib/vendor/blamejs/lib/middleware/require-auth.js +4 -3
  144. package/lib/vendor/blamejs/lib/middleware/require-bound-key.js +4 -3
  145. package/lib/vendor/blamejs/lib/middleware/require-content-type.js +3 -2
  146. package/lib/vendor/blamejs/lib/middleware/require-methods.js +3 -2
  147. package/lib/vendor/blamejs/lib/middleware/require-mtls.js +3 -2
  148. package/lib/vendor/blamejs/lib/middleware/security-txt.js +2 -1
  149. package/lib/vendor/blamejs/lib/middleware/web-app-manifest.js +2 -1
  150. package/lib/vendor/blamejs/lib/network-dns-resolver.js +1 -1
  151. package/lib/vendor/blamejs/lib/network-dns.js +3 -3
  152. package/lib/vendor/blamejs/lib/network-nts.js +35 -14
  153. package/lib/vendor/blamejs/lib/network-proxy.js +42 -4
  154. package/lib/vendor/blamejs/lib/network-smtp-policy.js +1 -1
  155. package/lib/vendor/blamejs/lib/network-tls.js +341 -11
  156. package/lib/vendor/blamejs/lib/nist-crosswalk.js +2 -2
  157. package/lib/vendor/blamejs/lib/ntp-check.js +30 -19
  158. package/lib/vendor/blamejs/lib/object-store/azure-blob.js +1 -1
  159. package/lib/vendor/blamejs/lib/object-store/gcs.js +1 -1
  160. package/lib/vendor/blamejs/lib/object-store/http-put.js +2 -1
  161. package/lib/vendor/blamejs/lib/object-store/sigv4-bucket-ops.js +5 -5
  162. package/lib/vendor/blamejs/lib/object-store/sigv4.js +2 -2
  163. package/lib/vendor/blamejs/lib/observability-otlp-exporter.js +4 -2
  164. package/lib/vendor/blamejs/lib/openapi.js +1 -1
  165. package/lib/vendor/blamejs/lib/pqc-agent.js +4 -7
  166. package/lib/vendor/blamejs/lib/problem-details.js +2 -1
  167. package/lib/vendor/blamejs/lib/redis-client.js +12 -1
  168. package/lib/vendor/blamejs/lib/regex-linear.js +1349 -0
  169. package/lib/vendor/blamejs/lib/render.js +531 -7
  170. package/lib/vendor/blamejs/lib/request-helpers.js +181 -5
  171. package/lib/vendor/blamejs/lib/router.js +37 -13
  172. package/lib/vendor/blamejs/lib/safe-async.js +142 -0
  173. package/lib/vendor/blamejs/lib/safe-icap.js +3 -0
  174. package/lib/vendor/blamejs/lib/safe-sql.js +15 -3
  175. package/lib/vendor/blamejs/lib/self-update.js +2 -2
  176. package/lib/vendor/blamejs/lib/testing.js +54 -7
  177. package/lib/vendor/blamejs/lib/validate-opts.js +56 -0
  178. package/lib/vendor/blamejs/lib/vendor/MANIFEST.json +62 -25
  179. package/lib/vendor/blamejs/lib/vendor/blamejs-pki.cjs +2004 -142
  180. package/lib/vendor/blamejs/lib/vendor/browser/noble-ciphers.mjs +727 -0
  181. package/lib/vendor/blamejs/lib/vendor/browser/noble-hashes.mjs +1070 -0
  182. package/lib/vendor/blamejs/lib/vendor/browser/noble-post-quantum.mjs +1090 -0
  183. package/lib/vendor/blamejs/lib/vendor/noble-post-quantum.cjs +510 -227
  184. package/lib/vendor/blamejs/lib/webhook-hmac.js +19 -2
  185. package/lib/vendor/blamejs/lib/webhook.js +1 -1
  186. package/lib/vendor/blamejs/lib/ws-client.js +24 -2
  187. package/lib/vendor/blamejs/package.json +1 -1
  188. package/lib/vendor/blamejs/sbom.cdx.json +6 -6
  189. package/lib/vendor-invoices.js +3 -6
  190. package/lib/vendors.js +5 -19
  191. package/lib/webhook-receiver.js +46 -83
  192. package/lib/webhook-subscriptions.js +3 -13
  193. package/lib/wishlist-sharing.js +5 -10
  194. package/package.json +1 -1
@@ -0,0 +1,1349 @@
1
+ // SPDX-License-Identifier: Apache-2.0
2
+ // Copyright (c) blamejs contributors
3
+ "use strict";
4
+ /**
5
+ * @module b.regexLinear
6
+ * @nav Security
7
+ * @title Linear regex
8
+ * @order 70
9
+ * @slug regex-linear
10
+ *
11
+ * @intro
12
+ * Run an operator-supplied pattern in time proportional to the subject,
13
+ * whatever the pattern is.
14
+ *
15
+ * The backtracking engine every JavaScript runtime ships explores one path at
16
+ * a time and reconsiders when a path fails. For most patterns that is quick
17
+ * and for some it is catastrophic: `(a+)+$` against forty `a`s and a `!`
18
+ * takes longer than the request that carried it, and the pattern that does it
19
+ * is a few characters long. Where the pattern comes from an operator's
20
+ * configuration and the subject from a request, that is a denial of service
21
+ * with no payload.
22
+ *
23
+ * Screening the pattern first — `b.guardRegex` — decides whether a pattern
24
+ * LOOKS dangerous. It is a conservative screen, and conservative screens have
25
+ * two failure modes: they turn away patterns that were fine, and they cannot
26
+ * promise to catch every shape that is not.
27
+ *
28
+ * This runs the pattern instead. It simulates every path at once, one
29
+ * character of the subject at a time, keeping at most one live position per
30
+ * instruction. A path that dies costs nothing to abandon because it was never
31
+ * the only one being followed, and no input makes the work grow faster than
32
+ * the length of the subject times the size of the pattern. There is nothing
33
+ * to tune, no budget to exhaust and no shape to special-case.
34
+ *
35
+ * What it will not do is guess. Backreferences and lookaround cannot be
36
+ * simulated this way, so a pattern using them is refused by name at compile
37
+ * time rather than quietly handed to the engine that can be made to hang.
38
+ *
39
+ * @card
40
+ * Run an operator's regex in time proportional to the subject, whatever the
41
+ * pattern.
42
+ */
43
+
44
+ var C = require("./constants");
45
+ var codepointClass = require("./codepoint-class");
46
+ var frameworkError = require("./framework-error");
47
+
48
+ var RegexLinearError = frameworkError.defineClass("RegexLinearError", {
49
+ alwaysPermanent: true,
50
+ });
51
+
52
+ var MAX_CODE_POINT = 0x10FFFF;
53
+
54
+ // A pattern longer than this is refused unread. Operators write patterns; this
55
+ // is far above any of them, and a compiled program grows with the source.
56
+ var MAX_SOURCE_BYTES = C.BYTES.kib(64);
57
+
58
+ // Bounded repeats are compiled by copying the body, so the counts an operator
59
+ // writes bound the program's size. `{1000000}` is a pattern that would build a
60
+ // program larger than the subject it screens.
61
+ var MAX_REPEAT_EXPANSION = 4096;
62
+
63
+ // And a ceiling on the compiled program however the counts are arranged, since
64
+ // nesting multiplies them. Far above any pattern an operator writes.
65
+ var MAX_PROGRAM_LENGTH = 65536;
66
+
67
+ // ---- character sets, as exact code-point ranges -----------------------------
68
+ //
69
+ // A set is a sorted, non-overlapping list of `[lo, hi]` code-point ranges. The
70
+ // analysis parser in `b.guardRegex` enumerates class members instead and widens
71
+ // to "anything" when a range is too large to enumerate, which is the right
72
+ // answer for an over-approximating screen and the wrong one here: an engine
73
+ // that matches a widened set matches a different language than the operator
74
+ // wrote. So this keeps ranges, and a class spanning the whole code space costs
75
+ // one of them rather than a member each.
76
+
77
+ // Sorted, non-overlapping, and — this is the part that matters — built out of
78
+ // PAIRS OF ITS OWN. A class is assembled by concatenating the members' range
79
+ // lists, and a positive shorthand contributes the shared `DIGIT` / `WORD` /
80
+ // `SPACE` table itself rather than a copy of it. Widening a pair in place to
81
+ // absorb a neighbour therefore edited that table for the whole process: after
82
+ // one `[\d:]`, every `\d` compiled anywhere in the program matched a colon, and
83
+ // the operator's other validators quietly widened with it.
84
+ function _norm(ranges) {
85
+ var sorted = ranges.map(function (r) { return [r[0], r[1]]; });
86
+ if (sorted.length < 2) return sorted;
87
+ sorted.sort(function (a, b) { return a[0] - b[0] || a[1] - b[1]; });
88
+ var out = [sorted[0]];
89
+ for (var i = 1; i < sorted.length; i += 1) {
90
+ var last = out[out.length - 1];
91
+ var next = sorted[i];
92
+ if (next[0] <= last[1] + 1) {
93
+ if (next[1] > last[1]) last[1] = next[1];
94
+ } else out.push(next);
95
+ }
96
+ return out;
97
+ }
98
+
99
+ // Deep-frozen so that a future write through a shared table fails loudly at the
100
+ // line that made it, rather than silently widening a shorthand everywhere.
101
+ function _sealed(ranges) {
102
+ ranges.forEach(function (r) { Object.freeze(r); });
103
+ return Object.freeze(ranges);
104
+ }
105
+
106
+ function _one(cp) { return [[cp, cp]]; }
107
+
108
+ function _union(a, b) { return _norm(a.concat(b)); }
109
+
110
+ function _negate(ranges) {
111
+ var sorted = _norm(ranges);
112
+ var out = [];
113
+ var at = 0;
114
+ for (var i = 0; i < sorted.length; i += 1) {
115
+ if (sorted[i][0] > at) out.push([at, sorted[i][0] - 1]);
116
+ at = Math.max(at, sorted[i][1] + 1);
117
+ }
118
+ if (at <= MAX_CODE_POINT) out.push([at, MAX_CODE_POINT]);
119
+ return out;
120
+ }
121
+
122
+ // Everything that shares a case with a member of these ranges. A set that is
123
+ // COMPLEMENTED under `i` has to be complemented on its case closure rather than
124
+ // on itself: under `iu` a Kelvin sign folds to `k`, so it is a word character
125
+ // and belongs outside `\W`. Complementing the bare list put it in `\W` as well,
126
+ // and then the fold test found `k` there and `[\W]` matched a letter.
127
+ //
128
+ // Only the shorthand sets are ever complemented, so this walks a hundred or so
129
+ // code points; it is not a general-purpose closure over an arbitrary class.
130
+ function _caseClosure(ranges, icase, unicode) {
131
+ if (!icase) return ranges;
132
+ var out = ranges.slice();
133
+ for (var i = 0; i < ranges.length; i += 1) {
134
+ for (var cp = ranges[i][0]; cp <= ranges[i][1]; cp += 1) {
135
+ var partners = codepointClass.caseFoldPartners(cp, unicode);
136
+ for (var p = 0; p < partners.length; p += 1) {
137
+ out.push([partners[p], partners[p]]);
138
+ }
139
+ }
140
+ }
141
+ return _norm(out);
142
+ }
143
+
144
+ // Does the pattern declare a named group anywhere in it? Read with an eye to
145
+ // where a `(` does NOT open a group: behind a backslash, or inside a class,
146
+ // where `(?<` is four ordinary members. A lookbehind head — `(?<=` or `(?<!` —
147
+ // names nothing either.
148
+ function _declaresNamedGroup(text) {
149
+ var inClass = false;
150
+ for (var i = 0; i < text.length; i += 1) {
151
+ var ch = text.charAt(i);
152
+ if (ch === "\\") { i += 1; continue; }
153
+ if (inClass) { if (ch === "]") inClass = false; continue; }
154
+ if (ch === "[") { inClass = true; continue; }
155
+ if (ch === "(" && text.charAt(i + 1) === "?" && text.charAt(i + 2) === "<" &&
156
+ text.charAt(i + 3) !== "=" && text.charAt(i + 3) !== "!") {
157
+ return true;
158
+ }
159
+ }
160
+ return false;
161
+ }
162
+
163
+ function _has(ranges, cp) {
164
+ var lo = 0;
165
+ var hi = ranges.length - 1;
166
+ while (lo <= hi) {
167
+ var mid = (lo + hi) >> 1;
168
+ if (cp < ranges[mid][0]) hi = mid - 1;
169
+ else if (cp > ranges[mid][1]) lo = mid + 1;
170
+ else return true;
171
+ }
172
+ return false;
173
+ }
174
+
175
+ var DIGIT = _sealed([[0x30, 0x39]]);
176
+ var WORD = _sealed([[0x30, 0x39], [0x41, 0x5A], [0x5F, 0x5F], [0x61, 0x7A]]);
177
+ var SPACE = _sealed(_norm([
178
+ [0x09, 0x0D], [0x20, 0x20], [0xA0, 0xA0], [0x1680, 0x1680], [0x2000, 0x200A],
179
+ [0x2028, 0x2029], [0x202F, 0x202F], [0x205F, 0x205F], [0x3000, 0x3000],
180
+ [0xFEFF, 0xFEFF],
181
+ ]));
182
+ var LINE_TERMINATORS = _sealed([[0x0A, 0x0A], [0x0D, 0x0D], [0x2028, 0x2029]]);
183
+
184
+ var CONTROL_ESCAPES = {
185
+ n: 0x0A, r: 0x0D, t: 0x09, f: 0x0C, v: 0x0B, 0: 0x00,
186
+ };
187
+
188
+ // ---- parsing ----------------------------------------------------------------
189
+ //
190
+ // Its own parser, for the reason above: this one has to be exact where the
191
+ // screen's may approximate, and it has to refuse rather than widen.
192
+
193
+ function _fail(message, code) {
194
+ throw new RegexLinearError(code, "regexLinear.compile: " + message);
195
+ }
196
+
197
+ // Scanning helpers, spelled out. A pattern is read one character code at a
198
+ // time — not matched against patterns of its own, and never by slicing the
199
+ // source at every position, which would cost more than reading it.
200
+
201
+ function _isDigit(code) { return code >= 0x30 && code <= 0x39; }
202
+
203
+ function _hexDigit(code) {
204
+ if (code >= 0x30 && code <= 0x39) return code - 0x30;
205
+ if (code >= 0x41 && code <= 0x46) return code - 0x37;
206
+ if (code >= 0x61 && code <= 0x66) return code - 0x57;
207
+ return -1;
208
+ }
209
+
210
+ // `min`..`max` hex digits from `at`, or null.
211
+ function _readHex(src, at, min, max) {
212
+ var value = 0;
213
+ var count = 0;
214
+ while (count < max && at + count < src.length) {
215
+ var digit = _hexDigit(src.charCodeAt(at + count));
216
+ if (digit < 0) break;
217
+ value = value * 16 + digit;
218
+ count += 1;
219
+ }
220
+ if (count < min) return null;
221
+ return { value: value, end: at + count };
222
+ }
223
+
224
+ // A repetition count. Anything with more digits than a real count could have is
225
+ // answered as one past what the compiler will expand, so it is refused there
226
+ // with the message about program size rather than silently becoming Infinity.
227
+ function _readCount(src, from, to) {
228
+ if (to - from > 9) return MAX_REPEAT_EXPANSION + 1;
229
+ var value = 0;
230
+ for (var at = from; at < to; at += 1) value = value * 10 + (src.charCodeAt(at) - 0x30);
231
+ return value;
232
+ }
233
+
234
+ // The characters a capture name is SPELLED WITH, with the identifier escapes
235
+ // the language allows resolved: a name written as an escape for the letter `a`
236
+ // is the name `a`, and `groups` carries it under that key. Returns null where
237
+ // the spelling is not one the language would read, so the caller can refuse it
238
+ // by the same route as any other bad name.
239
+ function _decodeCaptureName(spelling) {
240
+ var out = "";
241
+ var i = 0;
242
+ while (i < spelling.length) {
243
+ var ch = spelling.charAt(i);
244
+ if (ch !== "\\") { out += ch; i += 1; continue; }
245
+ if (spelling.charAt(i + 1) !== "u") return null; // only `\u` names a character
246
+ if (spelling.charAt(i + 2) === "{") {
247
+ var braced = _readHex(spelling, i + 3, 1, 6);
248
+ if (braced === null || spelling.charAt(braced.end) !== "}") return null;
249
+ if (braced.value > MAX_CODE_POINT) return null;
250
+ out += String.fromCodePoint(braced.value);
251
+ i = braced.end + 1;
252
+ continue;
253
+ }
254
+ var four = _readHex(spelling, i + 2, 4, 4);
255
+ if (four === null) return null;
256
+ out += String.fromCharCode(four.value);
257
+ i = four.end;
258
+ }
259
+ return out;
260
+ }
261
+
262
+ // A capture name is an identifier: it does not start with a digit and carries
263
+ // no punctuation. `(?<1>a)` and `(?<->a)` are patterns the platform refuses, so
264
+ // they are refused here rather than quietly given a meaning of their own.
265
+ function _isCaptureName(name) {
266
+ if (name.length === 0) return false;
267
+ for (var i = 0; i < name.length; i += 1) {
268
+ var code = name.charCodeAt(i);
269
+ var isLetter = (code >= 0x41 && code <= 0x5A) || (code >= 0x61 && code <= 0x7A);
270
+ var isDigit = code >= 0x30 && code <= 0x39;
271
+ var isNameSign = code === 0x24 || code === 0x5F; // `$` and `_`
272
+ // Deliberately ASCII. Which characters beyond it may start or continue an
273
+ // identifier is a Unicode property, and admitting everything above ASCII
274
+ // would take names the platform refuses — `(?<{co}py>a)` is not a name. A
275
+ // name outside ASCII is refused with a message saying so, rather than given
276
+ // a meaning here that `RegExp` would not give it.
277
+ if (isLetter || isNameSign) continue;
278
+ if (isDigit && i > 0) continue;
279
+ return false;
280
+ }
281
+ return true;
282
+ }
283
+
284
+ // The same rules `RegExp` applies: known letters, each at most once, and never
285
+ // both Unicode modes. A flag string this refuses is one the platform refuses.
286
+ function _badFlags(flags) {
287
+ var seen = "";
288
+ for (var i = 0; i < flags.length; i += 1) {
289
+ var ch = flags.charAt(i);
290
+ if ("dgimsuvy".indexOf(ch) === -1) return "unknown flag " + JSON.stringify(ch);
291
+ if (seen.indexOf(ch) !== -1) return "repeated flag " + JSON.stringify(ch);
292
+ seen += ch;
293
+ }
294
+ if (seen.indexOf("u") !== -1 && seen.indexOf("v") !== -1) {
295
+ return "the u and v flags cannot both be set";
296
+ }
297
+ return null;
298
+ }
299
+
300
+ function _parse(src, flags) {
301
+ var pos = 0;
302
+ var captureCount = 0;
303
+ // Without a prototype, because a group may legitimately be named
304
+ // `__proto__` and assigning that to an ordinary object sets the prototype
305
+ // instead of recording the name.
306
+ var names = Object.create(null);
307
+ var unicode = flags.indexOf("u") !== -1 || flags.indexOf("v") !== -1;
308
+ var icase = flags.indexOf("i") !== -1;
309
+ // Answered once, from the whole source, because it is a property of the
310
+ // pattern rather than of the position being read. Asked at each `\k` instead,
311
+ // a source carrying many of them would be re-scanned once per escape, and a
312
+ // matcher that promises linear MATCHING would have taken time quadratic in
313
+ // the pattern to compile.
314
+ var namesAGroup = _declaresNamedGroup(src);
315
+
316
+ function peek() { return src.charAt(pos); }
317
+ function eat(ch) { if (src.charAt(pos) === ch) { pos += 1; return true; } return false; }
318
+
319
+ function parseAlt() {
320
+ var branches = [parseSeq()];
321
+ while (eat("|")) branches.push(parseSeq());
322
+ return { t: "alt", bs: branches };
323
+ }
324
+
325
+ function parseSeq() {
326
+ var terms = [];
327
+ for (;;) {
328
+ var ch = peek();
329
+ if (ch === "" || ch === "|" || ch === ")") break;
330
+ var capLo = captureCount;
331
+ var atom = parseAtom(); // refuses rather than returning nothing
332
+ var capHi = captureCount;
333
+ var quant = parseQuantifier();
334
+ if (quant.min > quant.max) {
335
+ _fail("numbers out of order in {} quantifier", "regex/bad-quantifier");
336
+ }
337
+ // Nothing repeats an assertion. `^{1}a` and `\b{0}a` are syntax errors in
338
+ // JavaScript, and reading them as "the assertion, once" or "the assertion,
339
+ // never" would give an invalid pattern a meaning of its own — the second
340
+ // one silently dropping the boundary the operator asked for.
341
+ if (quant.present && (atom.t === "bol" || atom.t === "eol" ||
342
+ atom.t === "wb" || atom.t === "nwb")) {
343
+ _fail("nothing to repeat — a quantifier cannot be applied to an assertion",
344
+ "regex/bad-quantifier");
345
+ }
346
+ terms.push({
347
+ n: atom, min: quant.min, max: quant.max, lazy: quant.lazy,
348
+ capLo: capLo, capHi: capHi,
349
+ });
350
+ }
351
+ return { t: "seq", xs: terms };
352
+ }
353
+
354
+ // `present` says whether a quantifier was WRITTEN, which is not the same as
355
+ // whether it changes anything: `^{1}` is a syntax error even though repeating
356
+ // once would have meant nothing.
357
+ function parseQuantifier() {
358
+ var plain = { min: 1, max: 1, lazy: false, present: false };
359
+ var ch = peek();
360
+ var min, max;
361
+ if (ch === "*") { pos += 1; min = 0; max = Infinity; }
362
+ else if (ch === "+") { pos += 1; min = 1; max = Infinity; }
363
+ else if (ch === "?") { pos += 1; min = 0; max = 1; }
364
+ else if (ch === "{") {
365
+ var scanned = scanBraceQuantifier(pos);
366
+ if (scanned === null) return plain; // a literal `{`
367
+ pos = scanned.end;
368
+ min = scanned.min;
369
+ max = scanned.max;
370
+ } else return plain;
371
+ var lazy = eat("?");
372
+ // `a++` is a syntax error in JavaScript. Swallowing it as a possessive
373
+ // quantifier would accept a pattern the platform refuses, and quietly.
374
+ if (!lazy && peek() === "+") _fail("nothing to repeat", "regex/bad-quantifier");
375
+ return { min: min, max: max, lazy: lazy, present: true };
376
+ }
377
+
378
+ // `{n}` / `{n,}` / `{n,m}`, read one character at a time.
379
+ function scanBraceQuantifier(from) {
380
+ var at = from + 1; // past the `{`
381
+ var loStart = at;
382
+ while (at < src.length && _isDigit(src.charCodeAt(at))) at += 1;
383
+ if (at === loStart) return null;
384
+ var lo = _readCount(src, loStart, at);
385
+ var hi = lo;
386
+ if (src.charAt(at) === ",") {
387
+ at += 1;
388
+ var hiStart = at;
389
+ while (at < src.length && _isDigit(src.charCodeAt(at))) at += 1;
390
+ hi = at === hiStart ? Infinity : _readCount(src, hiStart, at);
391
+ }
392
+ if (src.charAt(at) !== "}") return null;
393
+ return { min: lo, max: hi, end: at + 1 };
394
+ }
395
+
396
+ function parseAtom() {
397
+ var ch = peek();
398
+ if (ch === "(") return parseGroup();
399
+ if (ch === "[") return parseClass();
400
+ if (ch === "^") { pos += 1; return { t: "bol" }; }
401
+ if (ch === "$") { pos += 1; return { t: "eol" }; }
402
+ if (ch === ".") {
403
+ pos += 1;
404
+ return flags.indexOf("s") !== -1
405
+ ? { t: "set", r: [[0, MAX_CODE_POINT]], neg: false }
406
+ : { t: "set", r: LINE_TERMINATORS, neg: true };
407
+ }
408
+ if (ch === "\\") return parseEscape();
409
+ if (ch === "*" || ch === "+" || ch === "?") {
410
+ _fail("nothing to repeat", "regex/bad-quantifier");
411
+ }
412
+ // A COMPLETE `{n}` / `{n,}` / `{n,m}` standing where an atom belongs is a
413
+ // syntax error rather than literal text — `{1}` and `a{0}{1}` are both
414
+ // refused by the platform. Reading them as the characters they are spelled
415
+ // with would give a pattern nobody can run a private meaning here. An
416
+ // incomplete brace — `{1`, `{a}` — really is literal, and stays so.
417
+ if (ch === "{" && scanBraceQuantifier(pos) !== null) {
418
+ _fail("nothing to repeat", "regex/bad-quantifier");
419
+ }
420
+ refuseLoneSyntaxChar(ch);
421
+ pos += 1;
422
+ if (unicode) {
423
+ var cp = src.codePointAt(pos - 1);
424
+ if (cp > 0xFFFF) pos += 1;
425
+ return { t: "set", r: _one(cp), neg: false };
426
+ }
427
+ return { t: "set", r: _one(ch.charCodeAt(0)), neg: false };
428
+ }
429
+
430
+ // Under `u` a bare `{`, `}` or `]` outside a class is a syntax error rather
431
+ // than a literal. Reading them as literals would take a pattern the platform
432
+ // refuses and give it a meaning of its own.
433
+ function refuseLoneSyntaxChar(ch) {
434
+ if (!unicode) return;
435
+ if (ch === "{" || ch === "}" || ch === "]") {
436
+ _fail("lone " + JSON.stringify(ch) + " is a syntax error under the u flag — " +
437
+ "escape it as \\" + ch, "regex/bad-escape");
438
+ }
439
+ }
440
+
441
+ function parseGroup() {
442
+ pos += 1; // the `(`
443
+ var capture = null;
444
+ var name = null;
445
+ if (peek() === "?") {
446
+ var rest = src.slice(pos, pos + 3);
447
+ if (rest.charAt(1) === "=" || rest.charAt(1) === "!" ||
448
+ (rest.charAt(1) === "<" && (rest.charAt(2) === "=" || rest.charAt(2) === "!"))) {
449
+ _fail("lookaround cannot be run in linear time — screen it with " +
450
+ "b.guardRegex.assertSafe and run it with the platform engine, or " +
451
+ "rewrite the pattern without it", "regex/unsupported-lookaround");
452
+ }
453
+ if (rest.charAt(1) === "<") { // named group
454
+ pos += 2;
455
+ var end = src.indexOf(">", pos);
456
+ if (end === -1) _fail("invalid capture group name", "regex/bad-group");
457
+ var spelling = src.slice(pos, end);
458
+ // A name may spell its characters with the identifier escapes the
459
+ // language allows, so it is decoded before it is judged: a group whose
460
+ // name is written as an escape for the letter `a` IS named `a`. Reading
461
+ // the source spelling refused a name the platform accepts, and would
462
+ // have recorded the escape itself as the key in `groups`.
463
+ name = _decodeCaptureName(spelling);
464
+ if (name === null || !_isCaptureName(name)) {
465
+ _fail("capture group name " + JSON.stringify(spelling) + " — a name here is " +
466
+ "ASCII letters, digits, `$` and `_`, not starting with a digit. " +
467
+ "Which characters beyond ASCII may name a group is a Unicode " +
468
+ "property this does not carry, so such a name is refused rather " +
469
+ "than given a meaning the platform would not give it",
470
+ "regex/bad-group");
471
+ }
472
+ pos = end + 1;
473
+ captureCount += 1;
474
+ capture = captureCount;
475
+ if (Object.prototype.hasOwnProperty.call(names, name)) {
476
+ _fail("duplicate capture group name " + JSON.stringify(name), "regex/bad-group");
477
+ }
478
+ names[name] = capture;
479
+ } else if (rest.charAt(1) === ":") {
480
+ pos += 2;
481
+ } else {
482
+ _fail("unsupported group " + JSON.stringify(src.slice(pos - 1, pos + 3)),
483
+ "regex/unsupported-group");
484
+ }
485
+ } else {
486
+ captureCount += 1;
487
+ capture = captureCount;
488
+ }
489
+ var body = parseAlt();
490
+ if (!eat(")")) _fail("unterminated group", "regex/bad-group");
491
+ return { t: "grp", b: body, cap: capture, name: name };
492
+ }
493
+
494
+ // A shorthand outside a class, or a single escaped character.
495
+ function parseEscape() {
496
+ pos += 1; // the backslash
497
+ var ch = peek();
498
+ if (ch === "") _fail("trailing backslash", "regex/bad-escape");
499
+ if (ch === "b") { pos += 1; return { t: "wb" }; }
500
+ if (ch === "B") { pos += 1; return { t: "nwb" }; }
501
+ if (ch >= "1" && ch <= "9") {
502
+ // `\1` is a backreference where a first group exists and a legacy octal
503
+ // escape where none does — a distinction that cannot be made until the
504
+ // whole pattern has been read, and one the operator can make at a glance.
505
+ // A backreference cannot be simulated in linear time, so both are refused
506
+ // and the message names the escape that says what was meant.
507
+ _fail("a digit escape is a backreference, which cannot be run in linear " +
508
+ "time — screen it with b.guardRegex.assertSafe and run it with the " +
509
+ "platform engine, or rewrite it without the backreference. If a " +
510
+ "character was meant, write it as \\xNN or \\uNNNN rather than as a " +
511
+ "legacy octal escape", "regex/unsupported-backreference");
512
+ }
513
+ // `\k` names a group where the pattern declares one and is an identity
514
+ // escape where it declares none — `/\k/` matches a `k`. The group may be
515
+ // written after the `\k` that names it, so the answer comes from the whole
516
+ // pattern rather than from what has been read so far. Under `u` a `\k` that
517
+ // names nothing is a syntax error, which the identity path already reports.
518
+ if (ch === "k" && namesAGroup) {
519
+ _fail("a named backreference cannot be run in linear time — screen it " +
520
+ "with b.guardRegex.assertSafe and run it with the platform engine, " +
521
+ "or rewrite the pattern without it", "regex/unsupported-backreference");
522
+ }
523
+ var shorthand = _shorthand(ch);
524
+ if (shorthand !== null) { pos += 1; return { t: "set", r: shorthand.r, neg: shorthand.neg }; }
525
+ return { t: "set", r: _one(_escapedCodePoint(false)), neg: false };
526
+ }
527
+
528
+ function _shorthand(ch) {
529
+ if (ch === "d") return { r: DIGIT, neg: false };
530
+ if (ch === "D") return { r: DIGIT, neg: true };
531
+ if (ch === "w") return { r: WORD, neg: false };
532
+ if (ch === "W") return { r: WORD, neg: true };
533
+ if (ch === "s") return { r: SPACE, neg: false };
534
+ if (ch === "S") return { r: SPACE, neg: true };
535
+ return null;
536
+ }
537
+
538
+ // The code point an escape stands for, having consumed it. `inClass` matters
539
+ // because the legacy grammar reads several escapes differently between the two
540
+ // places: `\c1` names a control inside a class and is a literal backslash
541
+ // outside one.
542
+ function _escapedCodePoint(inClass) {
543
+ var ch = src.charAt(pos);
544
+ // Outside Unicode mode a run of octal digits after a backslash is ONE
545
+ // character: `\07` is a bell, not a NUL followed by a seven. Reading only
546
+ // the `\0` left the digit behind as an atom of its own, which compiles
547
+ // happily and matches something else.
548
+ if (ch >= "0" && ch <= "7" && !unicode) {
549
+ var value = 0;
550
+ var digits = 0;
551
+ while (digits < 3 && pos < src.length) {
552
+ var digit = src.charCodeAt(pos) - 0x30;
553
+ if (digit < 0 || digit > 7) break;
554
+ if (value * 8 + digit > 0xFF) break; // three digits only up to \377
555
+ value = value * 8 + digit;
556
+ pos += 1;
557
+ digits += 1;
558
+ }
559
+ return value;
560
+ }
561
+ if (Object.prototype.hasOwnProperty.call(CONTROL_ESCAPES, ch)) {
562
+ // Under `u`, `\0` stands alone: `\01` and `\08` are invalid decimal
563
+ // escapes, not a NUL beside a digit.
564
+ if (ch === "0" && unicode && _isDigit(src.charCodeAt(pos + 1))) {
565
+ _fail("a digit cannot follow \\0 under the u flag", "regex/bad-escape");
566
+ }
567
+ pos += 1;
568
+ return CONTROL_ESCAPES[ch];
569
+ }
570
+ if (ch === "x") {
571
+ var hex = _readHex(src, pos + 1, 2, 2);
572
+ if (hex !== null) { pos = hex.end; return hex.value; }
573
+ // Without `u`, an `\x` that is not followed by two hex digits is an
574
+ // identity escape rather than an error: `\xZZ` matches an `x` and two
575
+ // `Z`s. It falls through to the identity path below.
576
+ if (unicode) _fail("invalid \\x escape", "regex/bad-escape");
577
+ }
578
+ if (ch === "u") {
579
+ // `\u{...}` is Unicode-mode syntax. Without `u` the platform reads it as
580
+ // a `u` followed by a brace, so accepting it here would match a different
581
+ // language than the same pattern handed to `RegExp`.
582
+ if (src.charAt(pos + 1) === "{" && unicode) {
583
+ var braced = _readHex(src, pos + 2, 1, 6);
584
+ if (braced === null || src.charAt(braced.end) !== "}") {
585
+ _fail("invalid \\u escape", "regex/bad-escape");
586
+ }
587
+ if (braced.value > MAX_CODE_POINT) _fail("invalid \\u escape", "regex/bad-escape");
588
+ pos = braced.end + 1;
589
+ return braced.value;
590
+ }
591
+ var four = _readHex(src, pos + 1, 4, 4);
592
+ if (four === null) {
593
+ // Outside Unicode mode a `\u` that is not followed by four hex digits
594
+ // is just an escaped `u`, and what comes after it is read on its own —
595
+ // `\u{61}` is a `u` repeated. Refusing it would turn away a pattern the
596
+ // platform accepts.
597
+ if (unicode) _fail("invalid \\u escape", "regex/bad-escape");
598
+ pos += 1;
599
+ return 0x75; // the letter `u`
600
+ }
601
+ pos = four.end;
602
+ // Under `u`, a surrogate pair spells ONE character. Left as two halves it
603
+ // would never match a subject that is read a code point at a time.
604
+ if (unicode && four.value >= 0xD800 && four.value <= 0xDBFF &&
605
+ src.charAt(pos) === "\\" && src.charAt(pos + 1) === "u") {
606
+ var low = _readHex(src, pos + 2, 4, 4);
607
+ if (low !== null && low.value >= 0xDC00 && low.value <= 0xDFFF) {
608
+ pos = low.end;
609
+ return (four.value - 0xD800) * 0x400 + (low.value - 0xDC00) + 0x10000;
610
+ }
611
+ }
612
+ return four.value;
613
+ }
614
+ if (ch === "c") {
615
+ var letter = src.charCodeAt(pos + 1);
616
+ var isLetter = (letter >= 0x41 && letter <= 0x5A) || (letter >= 0x61 && letter <= 0x7A);
617
+ // Inside a class and without `u`, a digit or an underscore names a control
618
+ // character as well: `[\c1]` is U+0011 and `[\c_]` is U+001F.
619
+ var isClassControl = !unicode && inClass &&
620
+ ((letter >= 0x30 && letter <= 0x39) || letter === 0x5F);
621
+ if (isLetter || isClassControl) {
622
+ pos += 2;
623
+ return (letter & 0x1F); // the control it names
624
+ }
625
+ if (unicode) _fail("invalid \\c escape", "regex/bad-escape");
626
+ // Without `u`, a `\c` that names no control is a LITERAL BACKSLASH and the
627
+ // `c` behind it is read on its own — `/\c1/` matches the three characters
628
+ // `\c1`, and `[\c]` holds a backslash and a `c`. Leaving `pos` on the `c`
629
+ // is what hands it back to the caller as an ordinary character.
630
+ return 0x5C;
631
+ }
632
+ // A property escape is Unicode-mode syntax. Without `u` the platform reads
633
+ // `\p{L}` as a `p`, a brace, an `L` and a brace, so refusing it there turned
634
+ // away a pattern that runs perfectly well.
635
+ if ((ch === "p" || ch === "P") && unicode) {
636
+ _fail("a unicode property escape is not supported — name the characters " +
637
+ "in a class instead", "regex/unsupported-property");
638
+ }
639
+ // An identity escape. Outside Unicode mode the platform lets a backslash
640
+ // stand in front of anything; under `u` only the syntax characters may be
641
+ // escaped, and `\a` is a syntax error. Accepting it here would take a
642
+ // pattern the platform refuses.
643
+ // Inside a class the hyphen joins them, because that is where escaping one
644
+ // means something: `[A-Za-z0-9_\-]` says the hyphen is a member and not the
645
+ // start of a range. Outside a class there is no range to disambiguate and
646
+ // `\-` stays a syntax error under `u`.
647
+ var escapable = unicode && inClass ? "^$\\.*+?()[]{}|/-" : "^$\\.*+?()[]{}|/";
648
+ if (unicode && escapable.indexOf(ch) === -1) {
649
+ _fail("invalid escape \\" + ch + " under the u flag — only a syntax " +
650
+ "character may be escaped there", "regex/bad-escape");
651
+ }
652
+ pos += 1;
653
+ // Outside Unicode mode the subject is read a UTF-16 unit at a time, so an
654
+ // escaped astral character is TWO units and has to compile as two. Taking
655
+ // the whole code point here would build a matcher that never matches it.
656
+ if (!unicode) return src.charCodeAt(pos - 1);
657
+ var cp = src.codePointAt(pos - 1);
658
+ if (cp > 0xFFFF) pos += 1;
659
+ return cp;
660
+ }
661
+
662
+ function parseClass() {
663
+ pos += 1; // the `[`
664
+ var negated = eat("^");
665
+ var ranges = [];
666
+ var closed = false;
667
+ while (pos < src.length) {
668
+ if (eat("]")) { closed = true; break; }
669
+ var lo = classMember();
670
+ // A shorthand is a set, not a bound: `[\d-x]` is the digits, a hyphen
671
+ // and an `x`, and treating it as a range start would be a different class.
672
+ if (lo.set !== null) {
673
+ if (peek() === "-" && src.charAt(pos + 1) !== "]" && pos + 1 < src.length) {
674
+ // `[\d-x]` is a syntax error under `u` for the same reason `[a-\d]` is:
675
+ // a shorthand cannot stand at either end of a range.
676
+ if (unicode) {
677
+ _fail("a shorthand cannot be an end of a range under the u flag",
678
+ "regex/bad-class");
679
+ }
680
+ // Without `u` those are three members — the shorthand, a hyphen, and
681
+ // whatever followed the hyphen — and all three are taken HERE. Left
682
+ // for the next turn of the loop, the tail of `[\d-a-z]` reads as an
683
+ // `a`-to-`z` range and the class matches every letter between them;
684
+ // the platform takes only the `a` and the `z`.
685
+ pos += 1; // the `-`
686
+ var tail = classMember();
687
+ ranges = ranges.concat(lo.set);
688
+ ranges.push([0x2D, 0x2D]);
689
+ if (tail.set !== null) ranges = ranges.concat(tail.set);
690
+ else ranges.push([tail.cp, tail.cp]);
691
+ continue;
692
+ }
693
+ ranges = ranges.concat(lo.set);
694
+ continue;
695
+ }
696
+ if (peek() === "-" && src.charAt(pos + 1) !== "]" && pos + 1 < src.length) {
697
+ pos += 1;
698
+ var hi = classMember();
699
+ if (hi.set !== null) { // `[a-\d]` — not a range
700
+ // Outside Unicode mode that is three members: an `a`, a hyphen and
701
+ // the digits. Under `u` it is a syntax error, and reading it as the
702
+ // three would take a pattern the platform refuses.
703
+ if (unicode) {
704
+ _fail("a shorthand cannot be the end of a range under the u flag",
705
+ "regex/bad-class");
706
+ }
707
+ ranges.push([lo.cp, lo.cp]);
708
+ ranges.push([0x2D, 0x2D]);
709
+ ranges = ranges.concat(hi.set);
710
+ continue;
711
+ }
712
+ if (hi.cp < lo.cp) _fail("range out of order in character class", "regex/bad-class");
713
+ ranges.push([lo.cp, hi.cp]);
714
+ continue;
715
+ }
716
+ ranges.push([lo.cp, lo.cp]);
717
+ }
718
+ if (!closed) _fail("unterminated character class", "regex/bad-class");
719
+ // The `^` is carried rather than applied. Under `i` a class matches when
720
+ // any MEMBER shares a case with the character, and a negated class matches
721
+ // when none does — so the negation has to come after that test, not before
722
+ // it. Pre-negating let `[^abc]` match a `b`, because `B` was in the
723
+ // complement.
724
+ return { t: "set", r: _norm(ranges), neg: negated };
725
+ }
726
+
727
+ function classMember() {
728
+ if (peek() === "\\") {
729
+ pos += 1;
730
+ var ch = peek();
731
+ if (ch === "") _fail("trailing backslash", "regex/bad-escape");
732
+ // `\b` is a backspace inside a class, not a boundary.
733
+ if (ch === "b") { pos += 1; return { cp: 0x08, set: null }; }
734
+ // Once a pattern names a group, `\k` stops being an ordinary character
735
+ // ANYWHERE in it — including inside a class, where the grammar has no
736
+ // production for it at all, so `/(?<n>a)[\k]/` is a syntax error. Without
737
+ // a named group the same class holds a `k`.
738
+ if (ch === "k" && namesAGroup) {
739
+ _fail("invalid escape \\k inside a character class — a pattern that " +
740
+ "names a group cannot also use \\k as an ordinary character",
741
+ "regex/bad-escape");
742
+ }
743
+ var shorthand = _shorthand(ch);
744
+ if (shorthand !== null) {
745
+ pos += 1;
746
+ return {
747
+ cp: -1,
748
+ set: shorthand.neg
749
+ ? _negate(_caseClosure(shorthand.r, icase, unicode))
750
+ : shorthand.r,
751
+ };
752
+ }
753
+ return { cp: _escapedCodePoint(true), set: null };
754
+ }
755
+ // Inside a class as outside it: without `u` an astral character is its two
756
+ // surrogate units, and reading the whole code point while advancing one
757
+ // unit records a character the subject never presents.
758
+ if (!unicode) {
759
+ pos += 1;
760
+ return { cp: src.charCodeAt(pos - 1), set: null };
761
+ }
762
+ var cp = src.codePointAt(pos);
763
+ pos += cp > 0xFFFF ? 2 : 1;
764
+ return { cp: cp, set: null };
765
+ }
766
+
767
+ var ast = parseAlt();
768
+ if (pos < src.length) {
769
+ _fail("unmatched " + JSON.stringify(src.charAt(pos)), "regex/unbalanced");
770
+ }
771
+ return { ast: ast, captureCount: captureCount, names: names };
772
+ }
773
+
774
+ // ---- compiling to a program -------------------------------------------------
775
+ //
776
+ // Instructions: `c` consumes a character, `s` splits into two paths in priority
777
+ // order, `j` jumps, `v` records a capture boundary, `a` tests a zero-width
778
+ // assertion, `m` matches. Nothing in this set can backtrack, because nothing in
779
+ // it ever un-does a step.
780
+
781
+ // Can this match without consuming anything?
782
+ function _isNullable(n) {
783
+ if (n.t === "set") return false;
784
+ if (n.t === "bol" || n.t === "eol" || n.t === "wb" || n.t === "nwb") return true;
785
+ if (n.t === "grp") return _isNullable(n.b);
786
+ if (n.t === "alt") {
787
+ for (var b = 0; b < n.bs.length; b += 1) if (_isNullable(n.bs[b])) return true;
788
+ return false;
789
+ }
790
+ for (var i = 0; i < n.xs.length; i += 1) {
791
+ var t = n.xs[i];
792
+ if (t.min > 0 && !_isNullable(t.n)) return false;
793
+ }
794
+ return true;
795
+ }
796
+
797
+ function _compile(ast, captureCount) {
798
+ var prog = [];
799
+ // The whole program, not one quantifier at a time. Counts multiply when they
800
+ // nest — `(a{4096}){4096}` passes any per-term cap and asks for sixteen
801
+ // million instructions — so what is bounded is the total, which is the thing
802
+ // that actually costs memory.
803
+ function emit(instruction) {
804
+ if (prog.length >= MAX_PROGRAM_LENGTH) {
805
+ _fail("the pattern expands to more than " + MAX_PROGRAM_LENGTH + " steps — " +
806
+ "repetition counts multiply where they nest, so a short pattern can " +
807
+ "ask for a very large program", "regex/repeat-too-large");
808
+ }
809
+ prog.push(instruction);
810
+ return prog.length - 1;
811
+ }
812
+
813
+ function node(n) {
814
+ if (n.t === "set") { emit({ op: "c", set: { r: n.r, neg: !!n.neg } }); return; }
815
+ if (n.t === "bol" || n.t === "eol" || n.t === "wb" || n.t === "nwb") {
816
+ emit({ op: "a", k: n.t });
817
+ return;
818
+ }
819
+ if (n.t === "grp") {
820
+ if (n.cap !== null) emit({ op: "v", i: n.cap * 2 });
821
+ alt(n.b);
822
+ if (n.cap !== null) emit({ op: "v", i: n.cap * 2 + 1 });
823
+ return;
824
+ }
825
+ if (n.t === "alt") { alt(n); return; }
826
+ if (n.t === "seq") { seq(n); return; }
827
+ _fail("unsupported construct", "regex/unsupported");
828
+ }
829
+
830
+ function seq(s) {
831
+ for (var i = 0; i < s.xs.length; i += 1) term(s.xs[i]);
832
+ }
833
+
834
+ // One pass through a repeated body. Each pass starts with the groups inside
835
+ // it forgotten: a group that took part in an earlier repetition and not in
836
+ // the last one reads as absent, which is what `(?:(a)|b)+` against "ab" says
837
+ // about its group. Carrying the earlier value forward would report a capture
838
+ // the match did not make.
839
+ function iteration(t) {
840
+ if ((t.min !== 1 || t.max !== 1) && t.capHi > t.capLo) {
841
+ emit({ op: "clr", lo: t.capLo + 1, hi: t.capHi });
842
+ }
843
+ node(t.n);
844
+ }
845
+
846
+ function alt(a) {
847
+ if (a.bs.length === 1) { seq(a.bs[0]); return; }
848
+ var jumps = [];
849
+ for (var i = 0; i < a.bs.length; i += 1) {
850
+ if (i === a.bs.length - 1) { seq(a.bs[i]); break; }
851
+ var split = emit({ op: "s", x: 0, y: 0 });
852
+ prog[split].x = prog.length;
853
+ seq(a.bs[i]);
854
+ jumps.push(emit({ op: "j", x: 0 }));
855
+ prog[split].y = prog.length;
856
+ }
857
+ for (var j = 0; j < jumps.length; j += 1) prog[jumps[j]].x = prog.length;
858
+ }
859
+
860
+ // A repeat is compiled by writing the body out: `n` mandatory copies, then
861
+ // the optional ones. That is what bounds the program by the counts the
862
+ // operator wrote, and why those counts are capped.
863
+ function term(t) {
864
+ var min = t.min;
865
+ var max = t.max;
866
+ if (max !== Infinity && max - min > MAX_REPEAT_EXPANSION) {
867
+ _fail("a repetition of more than " + MAX_REPEAT_EXPANSION + " is refused — " +
868
+ "the program would be larger than the subject", "regex/repeat-too-large");
869
+ }
870
+ if (min > MAX_REPEAT_EXPANSION) {
871
+ _fail("a repetition of more than " + MAX_REPEAT_EXPANSION + " is refused — " +
872
+ "the program would be larger than the subject", "regex/repeat-too-large");
873
+ }
874
+ // A repetition over a body that can match nothing is refused, and refused
875
+ // rather than approximated. Simulating every path at once is what makes
876
+ // this linear, and it works by keeping ONE live position per instruction —
877
+ // so two paths that reach the same instruction at the same place are the
878
+ // same path from here on. That is true of everything except an empty
879
+ // iteration, where whether the repetition may go round again depends on
880
+ // where its body began rather than on where it is now. Telling those paths
881
+ // apart means keeping a live position per instruction PER STARTING POINT,
882
+ // which is the quadratic behaviour this exists to avoid.
883
+ //
884
+ // Nothing is lost by saying so. `x*` where `x` can match nothing means
885
+ // exactly what `x` means, and the shapes this turns away — `(a*)*`,
886
+ // `(a*b*?)*` — are the ones that hang the platform engine.
887
+ if (max > min && _isNullable(t.n)) {
888
+ _fail("a repetition of something that can match nothing — rewrite the body " +
889
+ "so each repetition must consume, or drop the repetition, which " +
890
+ "means the same thing", "regex/nullable-repetition");
891
+ }
892
+ var i;
893
+ for (i = 0; i < min; i += 1) iteration(t);
894
+ if (max === Infinity) {
895
+ if (min === 0) star(t);
896
+ else plusTail(t); // the copies above were the `min`
897
+ return;
898
+ }
899
+ // Every optional copy skips to the END of the whole construct, not into the
900
+ // next copy. Landing in the next one matches the same language and gets the
901
+ // ORDER wrong — `x{0,3}` would try one `x` before three — and order is what
902
+ // decides which capture an operator sees.
903
+ var skips = [];
904
+ for (i = min; i < max; i += 1) {
905
+ var split = emit({ op: "s", x: 0, y: 0 });
906
+ var body = prog.length;
907
+ iteration(t);
908
+ skips.push(split);
909
+ if (t.lazy) prog[split].y = body; else prog[split].x = body;
910
+ }
911
+ var end = prog.length;
912
+ for (var k = 0; k < skips.length; k += 1) {
913
+ if (t.lazy) prog[skips[k]].x = end; else prog[skips[k]].y = end;
914
+ }
915
+ }
916
+
917
+ function star(t) {
918
+ var lazy = t.lazy;
919
+ var split = emit({ op: "s", x: 0, y: 0 });
920
+ var body = prog.length;
921
+ iteration(t);
922
+ emit({ op: "j", x: split });
923
+ var after = prog.length;
924
+ prog[split].x = lazy ? after : body;
925
+ prog[split].y = lazy ? body : after;
926
+ }
927
+
928
+ // `x+` after its mandatory copy: loop back over one more, or fall through.
929
+ function plusTail(t) {
930
+ var lazy = t.lazy;
931
+ var back = prog.length;
932
+ var split = emit({ op: "s", x: 0, y: 0 });
933
+ var body = prog.length;
934
+ iteration(t);
935
+ emit({ op: "j", x: back });
936
+ var after = prog.length;
937
+ prog[split].x = lazy ? after : body;
938
+ prog[split].y = lazy ? body : after;
939
+ }
940
+
941
+ emit({ op: "v", i: 0 });
942
+ node(ast);
943
+ emit({ op: "v", i: 1 });
944
+ emit({ op: "m" });
945
+ return { prog: prog, slots: (captureCount + 1) * 2 };
946
+ }
947
+
948
+ // ---- running ----------------------------------------------------------------
949
+
950
+ // A class matches when one of its MEMBERS is the character, or shares a case
951
+ // with it. Its `^` is applied to that answer, not to the members: `[^abc]`
952
+ // under `i` refuses `B` because `b` is a member, and negating the member list
953
+ // first would have said the opposite.
954
+ function _matchesSet(set, cp, icase, unicode) {
955
+ var member = _has(set.r, cp);
956
+ if (!member && icase) member = _sharesCase(set.r, cp, unicode);
957
+ return set.neg ? !member : member;
958
+ }
959
+
960
+ // Two characters are the same under `i` when they CANONICALIZE alike, which is
961
+ // not the same as one being the other's upper or lower case. The language folds
962
+ // through upper case, keeps a character whose upper case is more than one
963
+ // character, and — the rule that catches people out — refuses to fold a
964
+ // non-ASCII character onto an ASCII one. That last is why `/k/i` does not match
965
+ // a Kelvin sign, and why converting both ways and comparing gets it wrong.
966
+ //
967
+ // Under `u` the rule is different again: characters are folded rather than
968
+ // upper-cased, and the ASCII guard does not apply — which is why `/s/iu` DOES
969
+ // match a long s, and `/s/i` does not.
970
+ function _canonicalize(cp, unicode) {
971
+ return codepointClass.canonicalizeForCase(cp, unicode);
972
+ }
973
+
974
+ // Is some member of these ranges the same character as `cp` under `i`? The
975
+ // class is asked for in full rather than guessed at from `cp`'s own upper and
976
+ // lower forms: a final sigma is an ordinary sigma, and no amount of casing the
977
+ // ordinary one leads to it.
978
+ function _sharesCase(ranges, cp, unicode) {
979
+ var partners = codepointClass.caseFoldPartners(cp, unicode);
980
+ for (var i = 0; i < partners.length; i += 1) {
981
+ if (_has(ranges, partners[i])) return true;
982
+ }
983
+ return false;
984
+ }
985
+
986
+ function _run(compiled, subject, startAt, opts) {
987
+ var prog = compiled.prog;
988
+ var slots = compiled.slots;
989
+ var icase = opts.icase;
990
+ var multiline = opts.multiline;
991
+ var unicode = opts.unicode;
992
+ var sticky = opts.sticky;
993
+ var len = subject.length;
994
+
995
+ // One live position per instruction, which is what makes the whole thing
996
+ // linear: a path that arrives where another already is has nothing new to
997
+ // explore, so it is dropped rather than followed.
998
+ //
999
+ // Each list keeps its OWN record of what it has admitted. Sharing one between
1000
+ // the current step and the next lets a position already taken in this step
1001
+ // turn away the path that reaches it in the next, and the loop back over a
1002
+ // repetition is exactly such a path — `a*` came out empty for want of this.
1003
+ var marksC = { seen: new Array(prog.length), gen: 1 };
1004
+ var marksN = { seen: new Array(prog.length), gen: 1 };
1005
+ var clist = [];
1006
+ var nlist = [];
1007
+ var matched = null;
1008
+
1009
+ function codePointAt(at) {
1010
+ if (at >= len) return -1;
1011
+ if (!unicode) return subject.charCodeAt(at);
1012
+ return subject.codePointAt(at);
1013
+ }
1014
+
1015
+ function widthAt(at) {
1016
+ if (!unicode) return 1;
1017
+ var cp = subject.codePointAt(at);
1018
+ return cp > 0xFFFF ? 2 : 1;
1019
+ }
1020
+
1021
+ // A character that folds onto a word character IS one, for boundaries as much
1022
+ // as for `\w`: under `iu` a Kelvin sign folds to `k`, so it stands inside a
1023
+ // word rather than beside one.
1024
+ function isWordAt(cp) {
1025
+ if (cp < 0) return false;
1026
+ if (_has(WORD, cp)) return true;
1027
+ return icase && _sharesCase(WORD, cp, unicode);
1028
+ }
1029
+
1030
+ function assertionHolds(kind, at) {
1031
+ var before = at > 0 ? subject.charCodeAt(at - 1) : -1;
1032
+ var here = at < len ? subject.charCodeAt(at) : -1;
1033
+ if (kind === "bol") {
1034
+ return at === 0 || (multiline && _has(LINE_TERMINATORS, before));
1035
+ }
1036
+ if (kind === "eol") {
1037
+ return at === len || (multiline && _has(LINE_TERMINATORS, here));
1038
+ }
1039
+ var wordBefore = at > 0 && isWordAt(before);
1040
+ var wordHere = at < len && isWordAt(here);
1041
+ if (kind === "wb") return wordBefore !== wordHere;
1042
+ return wordBefore === wordHere; // `\B`
1043
+ }
1044
+
1045
+ // Where the capture boundaries recorded so far are kept: a chain, one link
1046
+ // per boundary, shared by every path that passed through it. Recording one
1047
+ // costs a link rather than a copy of every slot — with an array, a pattern of
1048
+ // N groups copied 2N slots at each of its 2N boundaries, so matching grew
1049
+ // with the SQUARE of the pattern and the promised bound held only for
1050
+ // patterns that were small anyway. The chain is read once, when something
1051
+ // matches.
1052
+ //
1053
+ // Following the zero-width steps to the characters they lead to, with an
1054
+ // explicit stack rather than the call stack. A pattern can chain thousands of
1055
+ // them — `(){2000}` is nothing but zero-width steps — and recursion would
1056
+ // overflow on a pattern the platform matches without trouble.
1057
+ //
1058
+ // The stack keeps priority: a split's preferred side is pushed LAST so it
1059
+ // comes off first, which is what makes greedy greedy.
1060
+ function add(list, marks, startPc, at, startCaps) {
1061
+ var stack = [{ pc: startPc, caps: startCaps }];
1062
+ while (stack.length !== 0) {
1063
+ var step = stack.pop();
1064
+ var pc = step.pc;
1065
+ if (marks.seen[pc] === marks.gen) continue;
1066
+ marks.seen[pc] = marks.gen;
1067
+ var caps = step.caps;
1068
+ var instruction = prog[pc];
1069
+ if (instruction.op === "j") { stack.push({ pc: instruction.x, caps: caps }); continue; }
1070
+ if (instruction.op === "s") {
1071
+ stack.push({ pc: instruction.y, caps: caps });
1072
+ stack.push({ pc: instruction.x, caps: caps });
1073
+ continue;
1074
+ }
1075
+ if (instruction.op === "v") {
1076
+ stack.push({ pc: pc + 1, caps: { slot: instruction.i, at: at, prev: caps } });
1077
+ continue;
1078
+ }
1079
+ if (instruction.op === "a") {
1080
+ if (assertionHolds(instruction.k, at)) stack.push({ pc: pc + 1, caps: caps });
1081
+ continue;
1082
+ }
1083
+ if (instruction.op === "clr") {
1084
+ stack.push({ pc: pc + 1, caps: { lo: instruction.lo, hi: instruction.hi, prev: caps } });
1085
+ continue;
1086
+ }
1087
+ list.push({ pc: pc, caps: caps });
1088
+ }
1089
+ }
1090
+
1091
+ var at = startAt;
1092
+
1093
+ for (;;) {
1094
+ // A new attempt from this position, at the lowest priority, so an earlier
1095
+ // start always wins — which is what "leftmost" means. Once something has
1096
+ // matched, no later start can improve on it.
1097
+ if (matched === null && (!sticky || at === startAt)) add(clist, marksC, 0, at, null);
1098
+ // An empty list means nothing survived FROM HERE — `\b` refusing at the
1099
+ // first position, say. The search still moves on, because a later position
1100
+ // may start an attempt that does survive. Two things make that pointless: a
1101
+ // match already in hand, since no later start could be further left, and the
1102
+ // `y` flag, which allows exactly one start — once its attempt has died there
1103
+ // is nothing left to seed and walking the rest of the subject would be pure
1104
+ // cost. A sticky pattern refused twenty million characters in about a second
1105
+ // before this, reading every one of them to reach an answer it already had.
1106
+ if (clist.length === 0 && (matched !== null || sticky)) break;
1107
+
1108
+ var cp = codePointAt(at);
1109
+ var width = at < len ? widthAt(at) : 0;
1110
+ for (var i = 0; i < clist.length; i += 1) {
1111
+ var thread = clist[i];
1112
+ var instruction = prog[thread.pc];
1113
+ if (instruction.op === "m") {
1114
+ matched = thread.caps;
1115
+ break; // lower-priority threads lose
1116
+ }
1117
+ if (instruction.op === "c" && cp >= 0 && _matchesSet(instruction.set, cp, icase, unicode)) {
1118
+ add(nlist, marksN, thread.pc + 1, at + width, thread.caps);
1119
+ }
1120
+ }
1121
+
1122
+ // Between the halves of an astral character there is a position the engine
1123
+ // still looks at, and a zero-width pattern still matches there: `/\B/u`
1124
+ // against "a<astral>b" reports an empty match at the index INSIDE the pair.
1125
+ // Nothing that consumes can match there — the character does not begin at
1126
+ // that index — so only an empty match is looked for, and only after the
1127
+ // position before it has been settled, which keeps the leftmost one.
1128
+ if (matched === null && unicode && width === 2 && (!sticky || at + 1 === startAt)) {
1129
+ var splitList = [];
1130
+ var splitMarks = { seen: new Array(prog.length), gen: 1 };
1131
+ add(splitList, splitMarks, 0, at + 1, null);
1132
+ for (var sp = 0; sp < splitList.length; sp += 1) {
1133
+ if (prog[splitList[sp].pc].op === "m") { matched = splitList[sp].caps; break; }
1134
+ }
1135
+ }
1136
+
1137
+ if (at >= len) break;
1138
+ at += width;
1139
+ var swapList = clist;
1140
+ clist = nlist;
1141
+ nlist = swapList;
1142
+ nlist.length = 0;
1143
+ var swapMarks = marksC;
1144
+ marksC = marksN;
1145
+ marksN = swapMarks;
1146
+ marksN.gen += 1; // clear it for the next step
1147
+ }
1148
+ return matched === null ? null : _readCaptures(matched, slots);
1149
+ }
1150
+
1151
+ // Read the chain back into slots, once, for the path that matched. Walking it
1152
+ // from the end means the FIRST value found for a slot is the most recent one
1153
+ // written, which is the one that counts; a clearing link answers for every slot
1154
+ // in its range that nothing later has already answered for.
1155
+ function _readCaptures(node, slots) {
1156
+ var out = new Array(slots);
1157
+ var known = new Array(slots);
1158
+ var i;
1159
+ for (i = 0; i < slots; i += 1) { out[i] = -1; known[i] = false; }
1160
+ var link = node;
1161
+ while (link !== null) {
1162
+ if (link.slot !== undefined) {
1163
+ if (!known[link.slot]) { out[link.slot] = link.at; known[link.slot] = true; }
1164
+ } else {
1165
+ for (var g = link.lo; g <= link.hi; g += 1) {
1166
+ var open = g * 2;
1167
+ var close = open + 1;
1168
+ if (!known[open]) { out[open] = -1; known[open] = true; }
1169
+ if (!known[close]) { out[close] = -1; known[close] = true; }
1170
+ }
1171
+ }
1172
+ link = link.prev;
1173
+ }
1174
+ return out;
1175
+ }
1176
+
1177
+ /**
1178
+ * @primitive b.regexLinear.compile
1179
+ * @signature b.regexLinear.compile(source, flags?)
1180
+ * @since 0.18.19
1181
+ * @status stable
1182
+ * @related b.guardRegex.assertSafe, b.guardRegex.validate
1183
+ *
1184
+ * Compile a pattern into a matcher that runs in time proportional to the
1185
+ * subject, whatever the pattern is.
1186
+ *
1187
+ * `source` is the pattern without delimiters, as `RegExp` takes it, or a
1188
+ * `RegExp` whose source and flags are used. Flags `i`, `m`, `s`, `u` and `y`
1189
+ * are honoured. `g` is not, because the matcher returns one match and the
1190
+ * caller decides what to do next.
1191
+ *
1192
+ * Two are refused by name rather than ignored, both under
1193
+ * `regex/unsupported-flag`. `v` brings class set operations — intersection,
1194
+ * subtraction, string properties — that this does not implement, and reading it
1195
+ * as a `u` would quietly match a different language; use `u`, or name the
1196
+ * characters in a plain class. `d` asks for the index of every capture, which
1197
+ * this does not record; drop it, or run the pattern with the platform engine
1198
+ * once it has been screened.
1199
+ *
1200
+ * The returned matcher exposes `test(subject)` and `exec(subject, from?)`.
1201
+ * `exec` returns `null` or a result shaped like the platform's: index 0 is the
1202
+ * whole match, higher indices are the capture groups, `index` is where the
1203
+ * match began, and `groups` carries the named ones.
1204
+ *
1205
+ * Backreferences and lookaround are refused at compile time, by name, with the
1206
+ * code `regex/unsupported-backreference` or `regex/unsupported-lookaround`.
1207
+ * They cannot be simulated without exploring paths one at a time, which is the
1208
+ * thing being avoided — so the choice is made where an operator can see it,
1209
+ * rather than by handing the pattern to an engine that can be made to hang.
1210
+ *
1211
+ * Four more are refused for narrower reasons, each under its own code and each
1212
+ * with a message saying what to write instead: a repetition whose body can
1213
+ * match nothing (`regex/nullable-repetition` — `(a*)*` means what `a*` means);
1214
+ * a group named outside ASCII, and a name reused across alternatives
1215
+ * (`regex/bad-group`); an inline flag modifier such as `(?i:...)`
1216
+ * (`regex/unsupported-group`), whose scoped flags this does not yet carry; and
1217
+ * a Unicode property escape under `u`, such as `\p{L}` (
1218
+ * `regex/unsupported-property`) — name the characters in a class instead.
1219
+ * Without `u` there is no property escape to refuse: `\p{L}` is a `p`, a brace,
1220
+ * an `L` and a brace there, and it compiles.
1221
+ * These are patterns the platform accepts, so a pattern using one has to be
1222
+ * screened and run with the platform engine, or rewritten. Everything else
1223
+ * accepted here returns exactly what `RegExp` returns, with one exception that
1224
+ * belongs to the platform rather than to this matcher: under `u`, an unanchored
1225
+ * pattern ending in a CHARACTER CLASS that can match at most one code point,
1226
+ * then `$`, misses a subject ending in an astral character.
1227
+ * `new RegExp("[^a]$", "u")` finds nothing in a single emoji, while
1228
+ * `new RegExp("^[^a]$", "u")` finds it — and a `^` can only take match
1229
+ * positions away, never add one. This returns the match, which is what the
1230
+ * language specifies, so on that one shape the two answers differ. A class that
1231
+ * may run on (`[^a]+$`, `[^a]*$`), a shorthand (`\W$`), the dot, and the
1232
+ * character written out are all unaffected.
1233
+ *
1234
+ * @opts
1235
+ * (none — flags are passed as the second argument, as `RegExp` takes them)
1236
+ *
1237
+ * @example
1238
+ * var m = b.regexLinear.compile("^(\\w+)@([\\w.]+)$");
1239
+ * m.test("ada@example.com"); // → true
1240
+ * m.exec("ada@example.com")[2]; // → "example.com"
1241
+ *
1242
+ * // The shape that hangs the platform engine runs in linear time here.
1243
+ * b.regexLinear.compile("(a+)+$").test("a".repeat(40) + "!"); // → false
1244
+ */
1245
+ function compile(source, flags) {
1246
+ if (source instanceof RegExp) {
1247
+ flags = flags === undefined ? source.flags : flags;
1248
+ source = source.source;
1249
+ }
1250
+ if (typeof source !== "string") {
1251
+ throw new TypeError("regexLinear.compile: source must be a string or a RegExp");
1252
+ }
1253
+ if (flags === undefined || flags === null) flags = "";
1254
+ if (typeof flags !== "string") {
1255
+ throw new TypeError("regexLinear.compile: flags must be a string");
1256
+ }
1257
+ var flagProblem = _badFlags(flags);
1258
+ if (flagProblem !== null) {
1259
+ throw new TypeError("regexLinear.compile: " + flagProblem);
1260
+ }
1261
+ // (2) `v` brings set intersection, subtraction and nested classes. Reading
1262
+ // `[a&&b]` as ordinary members would match a different language than the
1263
+ // operator wrote, so the flag is refused until those are implemented rather
1264
+ // than accepted and approximated.
1265
+ if (flags.indexOf("d") !== -1) {
1266
+ throw new RegexLinearError("regex/unsupported-flag",
1267
+ "regexLinear.compile: the d flag promises match indices this does not " +
1268
+ "produce — drop it, or use the platform engine for that result");
1269
+ }
1270
+ if (flags.indexOf("v") !== -1) {
1271
+ throw new RegexLinearError("regex/unsupported-flag",
1272
+ "regexLinear.compile: the v flag brings class set operations that are not " +
1273
+ "implemented — use u, or name the characters in a plain class");
1274
+ }
1275
+ if (Buffer.byteLength(source, "utf8") > MAX_SOURCE_BYTES) {
1276
+ throw new RegexLinearError("regex/pattern-too-large",
1277
+ "regexLinear.compile: pattern longer than " + MAX_SOURCE_BYTES + " bytes");
1278
+ }
1279
+
1280
+ var parsed = _parse(source, flags);
1281
+ var compiled = _compile(parsed.ast, parsed.captureCount);
1282
+ var runOpts = {
1283
+ icase: flags.indexOf("i") !== -1,
1284
+ multiline: flags.indexOf("m") !== -1,
1285
+ unicode: flags.indexOf("u") !== -1 || flags.indexOf("v") !== -1,
1286
+ sticky: flags.indexOf("y") !== -1,
1287
+ };
1288
+
1289
+ function exec(subject, from) {
1290
+ if (typeof subject !== "string") {
1291
+ throw new TypeError("regexLinear.exec: subject must be a string");
1292
+ }
1293
+ var startAt = from === undefined ? 0 : from;
1294
+ // A whole number of characters. A fraction reaches the string operations,
1295
+ // which truncate it, while the capture slots keep it — and the match comes
1296
+ // back claiming to start at index 1.5.
1297
+ if (typeof startAt !== "number" || !Number.isInteger(startAt) || startAt < 0) {
1298
+ throw new TypeError("regexLinear.exec: from must be a non-negative integer");
1299
+ }
1300
+ if (startAt > subject.length) return null;
1301
+ // Under `u` an offset that splits a surrogate pair does not name a place a
1302
+ // character starts. The platform's own answer there is not one rule — a
1303
+ // zero-width assertion can match at the split index while a consuming
1304
+ // pattern reports the index before it — so rather than pick one and differ
1305
+ // from `RegExp` silently, the offset is refused. A caller stepping through
1306
+ // matches under `u` advances by code point, and one that lands here has a
1307
+ // bug worth seeing.
1308
+ if (runOpts.unicode && startAt > 0 && startAt < subject.length) {
1309
+ var high = subject.charCodeAt(startAt - 1);
1310
+ var low = subject.charCodeAt(startAt);
1311
+ if (high >= 0xD800 && high <= 0xDBFF && low >= 0xDC00 && low <= 0xDFFF) {
1312
+ throw new RangeError("regexLinear.exec: from " + startAt + " splits a surrogate " +
1313
+ "pair — under the u flag an offset must fall where a character starts");
1314
+ }
1315
+ }
1316
+ var caps = _run(compiled, subject, startAt, runOpts);
1317
+ if (caps === null) return null;
1318
+ var result = [];
1319
+ for (var g = 0; g <= parsed.captureCount; g += 1) {
1320
+ var lo = caps[g * 2];
1321
+ var hi = caps[g * 2 + 1];
1322
+ result[g] = (lo < 0 || hi < 0) ? undefined : subject.slice(lo, hi);
1323
+ }
1324
+ result.index = caps[0];
1325
+ result.input = subject;
1326
+ var named = Object.keys(parsed.names);
1327
+ if (named.length === 0) result.groups = undefined;
1328
+ else {
1329
+ var groups = Object.create(null);
1330
+ for (var n = 0; n < named.length; n += 1) groups[named[n]] = result[parsed.names[named[n]]];
1331
+ result.groups = groups;
1332
+ }
1333
+ return result;
1334
+ }
1335
+
1336
+ return {
1337
+ source: source,
1338
+ flags: flags,
1339
+ groupCount: parsed.captureCount,
1340
+ groupNames: Object.keys(parsed.names),
1341
+ test: function (subject) { return exec(subject, 0) !== null; },
1342
+ exec: exec,
1343
+ };
1344
+ }
1345
+
1346
+ module.exports = {
1347
+ compile: compile,
1348
+ RegexLinearError: RegexLinearError,
1349
+ };