@apitap/core 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (236) hide show
  1. package/LICENSE +60 -0
  2. package/README.md +362 -0
  3. package/SKILL.md +270 -0
  4. package/dist/auth/crypto.d.ts +31 -0
  5. package/dist/auth/crypto.js +66 -0
  6. package/dist/auth/crypto.js.map +1 -0
  7. package/dist/auth/handoff.d.ts +29 -0
  8. package/dist/auth/handoff.js +180 -0
  9. package/dist/auth/handoff.js.map +1 -0
  10. package/dist/auth/manager.d.ts +46 -0
  11. package/dist/auth/manager.js +127 -0
  12. package/dist/auth/manager.js.map +1 -0
  13. package/dist/auth/oauth-refresh.d.ts +16 -0
  14. package/dist/auth/oauth-refresh.js +91 -0
  15. package/dist/auth/oauth-refresh.js.map +1 -0
  16. package/dist/auth/refresh.d.ts +43 -0
  17. package/dist/auth/refresh.js +217 -0
  18. package/dist/auth/refresh.js.map +1 -0
  19. package/dist/capture/anti-bot.d.ts +15 -0
  20. package/dist/capture/anti-bot.js +43 -0
  21. package/dist/capture/anti-bot.js.map +1 -0
  22. package/dist/capture/blocklist.d.ts +6 -0
  23. package/dist/capture/blocklist.js +70 -0
  24. package/dist/capture/blocklist.js.map +1 -0
  25. package/dist/capture/body-diff.d.ts +8 -0
  26. package/dist/capture/body-diff.js +102 -0
  27. package/dist/capture/body-diff.js.map +1 -0
  28. package/dist/capture/body-variables.d.ts +13 -0
  29. package/dist/capture/body-variables.js +142 -0
  30. package/dist/capture/body-variables.js.map +1 -0
  31. package/dist/capture/domain.d.ts +8 -0
  32. package/dist/capture/domain.js +34 -0
  33. package/dist/capture/domain.js.map +1 -0
  34. package/dist/capture/entropy.d.ts +33 -0
  35. package/dist/capture/entropy.js +100 -0
  36. package/dist/capture/entropy.js.map +1 -0
  37. package/dist/capture/filter.d.ts +11 -0
  38. package/dist/capture/filter.js +49 -0
  39. package/dist/capture/filter.js.map +1 -0
  40. package/dist/capture/graphql.d.ts +21 -0
  41. package/dist/capture/graphql.js +99 -0
  42. package/dist/capture/graphql.js.map +1 -0
  43. package/dist/capture/idle.d.ts +23 -0
  44. package/dist/capture/idle.js +44 -0
  45. package/dist/capture/idle.js.map +1 -0
  46. package/dist/capture/monitor.d.ts +26 -0
  47. package/dist/capture/monitor.js +183 -0
  48. package/dist/capture/monitor.js.map +1 -0
  49. package/dist/capture/oauth-detector.d.ts +18 -0
  50. package/dist/capture/oauth-detector.js +96 -0
  51. package/dist/capture/oauth-detector.js.map +1 -0
  52. package/dist/capture/pagination.d.ts +9 -0
  53. package/dist/capture/pagination.js +40 -0
  54. package/dist/capture/pagination.js.map +1 -0
  55. package/dist/capture/parameterize.d.ts +17 -0
  56. package/dist/capture/parameterize.js +63 -0
  57. package/dist/capture/parameterize.js.map +1 -0
  58. package/dist/capture/scrubber.d.ts +5 -0
  59. package/dist/capture/scrubber.js +38 -0
  60. package/dist/capture/scrubber.js.map +1 -0
  61. package/dist/capture/session.d.ts +46 -0
  62. package/dist/capture/session.js +445 -0
  63. package/dist/capture/session.js.map +1 -0
  64. package/dist/capture/token-detector.d.ts +16 -0
  65. package/dist/capture/token-detector.js +62 -0
  66. package/dist/capture/token-detector.js.map +1 -0
  67. package/dist/capture/verifier.d.ts +17 -0
  68. package/dist/capture/verifier.js +147 -0
  69. package/dist/capture/verifier.js.map +1 -0
  70. package/dist/cli.d.ts +2 -0
  71. package/dist/cli.js +930 -0
  72. package/dist/cli.js.map +1 -0
  73. package/dist/discovery/auth.d.ts +17 -0
  74. package/dist/discovery/auth.js +81 -0
  75. package/dist/discovery/auth.js.map +1 -0
  76. package/dist/discovery/fetch.d.ts +17 -0
  77. package/dist/discovery/fetch.js +59 -0
  78. package/dist/discovery/fetch.js.map +1 -0
  79. package/dist/discovery/frameworks.d.ts +11 -0
  80. package/dist/discovery/frameworks.js +249 -0
  81. package/dist/discovery/frameworks.js.map +1 -0
  82. package/dist/discovery/index.d.ts +21 -0
  83. package/dist/discovery/index.js +219 -0
  84. package/dist/discovery/index.js.map +1 -0
  85. package/dist/discovery/openapi.d.ts +13 -0
  86. package/dist/discovery/openapi.js +175 -0
  87. package/dist/discovery/openapi.js.map +1 -0
  88. package/dist/discovery/probes.d.ts +9 -0
  89. package/dist/discovery/probes.js +70 -0
  90. package/dist/discovery/probes.js.map +1 -0
  91. package/dist/index.d.ts +25 -0
  92. package/dist/index.js +25 -0
  93. package/dist/index.js.map +1 -0
  94. package/dist/inspect/report.d.ts +52 -0
  95. package/dist/inspect/report.js +191 -0
  96. package/dist/inspect/report.js.map +1 -0
  97. package/dist/mcp.d.ts +8 -0
  98. package/dist/mcp.js +526 -0
  99. package/dist/mcp.js.map +1 -0
  100. package/dist/orchestration/browse.d.ts +38 -0
  101. package/dist/orchestration/browse.js +198 -0
  102. package/dist/orchestration/browse.js.map +1 -0
  103. package/dist/orchestration/cache.d.ts +15 -0
  104. package/dist/orchestration/cache.js +24 -0
  105. package/dist/orchestration/cache.js.map +1 -0
  106. package/dist/plugin.d.ts +17 -0
  107. package/dist/plugin.js +158 -0
  108. package/dist/plugin.js.map +1 -0
  109. package/dist/read/decoders/deepwiki.d.ts +2 -0
  110. package/dist/read/decoders/deepwiki.js +148 -0
  111. package/dist/read/decoders/deepwiki.js.map +1 -0
  112. package/dist/read/decoders/grokipedia.d.ts +2 -0
  113. package/dist/read/decoders/grokipedia.js +210 -0
  114. package/dist/read/decoders/grokipedia.js.map +1 -0
  115. package/dist/read/decoders/hackernews.d.ts +2 -0
  116. package/dist/read/decoders/hackernews.js +168 -0
  117. package/dist/read/decoders/hackernews.js.map +1 -0
  118. package/dist/read/decoders/index.d.ts +2 -0
  119. package/dist/read/decoders/index.js +12 -0
  120. package/dist/read/decoders/index.js.map +1 -0
  121. package/dist/read/decoders/reddit.d.ts +2 -0
  122. package/dist/read/decoders/reddit.js +142 -0
  123. package/dist/read/decoders/reddit.js.map +1 -0
  124. package/dist/read/decoders/twitter.d.ts +12 -0
  125. package/dist/read/decoders/twitter.js +187 -0
  126. package/dist/read/decoders/twitter.js.map +1 -0
  127. package/dist/read/decoders/wikipedia.d.ts +2 -0
  128. package/dist/read/decoders/wikipedia.js +66 -0
  129. package/dist/read/decoders/wikipedia.js.map +1 -0
  130. package/dist/read/decoders/youtube.d.ts +2 -0
  131. package/dist/read/decoders/youtube.js +69 -0
  132. package/dist/read/decoders/youtube.js.map +1 -0
  133. package/dist/read/extract.d.ts +25 -0
  134. package/dist/read/extract.js +320 -0
  135. package/dist/read/extract.js.map +1 -0
  136. package/dist/read/index.d.ts +14 -0
  137. package/dist/read/index.js +66 -0
  138. package/dist/read/index.js.map +1 -0
  139. package/dist/read/peek.d.ts +9 -0
  140. package/dist/read/peek.js +137 -0
  141. package/dist/read/peek.js.map +1 -0
  142. package/dist/read/types.d.ts +44 -0
  143. package/dist/read/types.js +3 -0
  144. package/dist/read/types.js.map +1 -0
  145. package/dist/replay/engine.d.ts +53 -0
  146. package/dist/replay/engine.js +441 -0
  147. package/dist/replay/engine.js.map +1 -0
  148. package/dist/replay/truncate.d.ts +16 -0
  149. package/dist/replay/truncate.js +92 -0
  150. package/dist/replay/truncate.js.map +1 -0
  151. package/dist/serve.d.ts +31 -0
  152. package/dist/serve.js +149 -0
  153. package/dist/serve.js.map +1 -0
  154. package/dist/skill/generator.d.ts +44 -0
  155. package/dist/skill/generator.js +419 -0
  156. package/dist/skill/generator.js.map +1 -0
  157. package/dist/skill/importer.d.ts +26 -0
  158. package/dist/skill/importer.js +80 -0
  159. package/dist/skill/importer.js.map +1 -0
  160. package/dist/skill/search.d.ts +19 -0
  161. package/dist/skill/search.js +51 -0
  162. package/dist/skill/search.js.map +1 -0
  163. package/dist/skill/signing.d.ts +16 -0
  164. package/dist/skill/signing.js +34 -0
  165. package/dist/skill/signing.js.map +1 -0
  166. package/dist/skill/ssrf.d.ts +27 -0
  167. package/dist/skill/ssrf.js +210 -0
  168. package/dist/skill/ssrf.js.map +1 -0
  169. package/dist/skill/store.d.ts +7 -0
  170. package/dist/skill/store.js +93 -0
  171. package/dist/skill/store.js.map +1 -0
  172. package/dist/stats/report.d.ts +26 -0
  173. package/dist/stats/report.js +157 -0
  174. package/dist/stats/report.js.map +1 -0
  175. package/dist/types.d.ts +214 -0
  176. package/dist/types.js +3 -0
  177. package/dist/types.js.map +1 -0
  178. package/package.json +58 -0
  179. package/src/auth/crypto.ts +92 -0
  180. package/src/auth/handoff.ts +229 -0
  181. package/src/auth/manager.ts +140 -0
  182. package/src/auth/oauth-refresh.ts +120 -0
  183. package/src/auth/refresh.ts +300 -0
  184. package/src/capture/anti-bot.ts +63 -0
  185. package/src/capture/blocklist.ts +75 -0
  186. package/src/capture/body-diff.ts +109 -0
  187. package/src/capture/body-variables.ts +156 -0
  188. package/src/capture/domain.ts +34 -0
  189. package/src/capture/entropy.ts +121 -0
  190. package/src/capture/filter.ts +56 -0
  191. package/src/capture/graphql.ts +124 -0
  192. package/src/capture/idle.ts +45 -0
  193. package/src/capture/monitor.ts +224 -0
  194. package/src/capture/oauth-detector.ts +106 -0
  195. package/src/capture/pagination.ts +49 -0
  196. package/src/capture/parameterize.ts +68 -0
  197. package/src/capture/scrubber.ts +49 -0
  198. package/src/capture/session.ts +502 -0
  199. package/src/capture/token-detector.ts +76 -0
  200. package/src/capture/verifier.ts +171 -0
  201. package/src/cli.ts +1031 -0
  202. package/src/discovery/auth.ts +99 -0
  203. package/src/discovery/fetch.ts +85 -0
  204. package/src/discovery/frameworks.ts +231 -0
  205. package/src/discovery/index.ts +256 -0
  206. package/src/discovery/openapi.ts +230 -0
  207. package/src/discovery/probes.ts +76 -0
  208. package/src/index.ts +26 -0
  209. package/src/inspect/report.ts +247 -0
  210. package/src/mcp.ts +618 -0
  211. package/src/orchestration/browse.ts +250 -0
  212. package/src/orchestration/cache.ts +37 -0
  213. package/src/plugin.ts +188 -0
  214. package/src/read/decoders/deepwiki.ts +180 -0
  215. package/src/read/decoders/grokipedia.ts +246 -0
  216. package/src/read/decoders/hackernews.ts +198 -0
  217. package/src/read/decoders/index.ts +15 -0
  218. package/src/read/decoders/reddit.ts +158 -0
  219. package/src/read/decoders/twitter.ts +211 -0
  220. package/src/read/decoders/wikipedia.ts +75 -0
  221. package/src/read/decoders/youtube.ts +75 -0
  222. package/src/read/extract.ts +396 -0
  223. package/src/read/index.ts +78 -0
  224. package/src/read/peek.ts +175 -0
  225. package/src/read/types.ts +37 -0
  226. package/src/replay/engine.ts +559 -0
  227. package/src/replay/truncate.ts +116 -0
  228. package/src/serve.ts +189 -0
  229. package/src/skill/generator.ts +473 -0
  230. package/src/skill/importer.ts +107 -0
  231. package/src/skill/search.ts +76 -0
  232. package/src/skill/signing.ts +36 -0
  233. package/src/skill/ssrf.ts +238 -0
  234. package/src/skill/store.ts +107 -0
  235. package/src/stats/report.ts +208 -0
  236. package/src/types.ts +233 -0
@@ -0,0 +1,106 @@
1
+ // src/capture/oauth-detector.ts
2
+
3
+ export interface OAuthInfo {
4
+ tokenEndpoint: string;
5
+ clientId: string;
6
+ grantType: 'refresh_token' | 'client_credentials';
7
+ scope?: string;
8
+ clientSecret?: string;
9
+ }
10
+
11
+ /**
12
+ * Detect OAuth2 token endpoint requests from captured traffic.
13
+ * Only recognizes refreshable flows (refresh_token, client_credentials).
14
+ * Ignores authorization_code (initial auth, not refreshable in isolation).
15
+ */
16
+ export function isOAuthTokenRequest(req: {
17
+ url: string;
18
+ method: string;
19
+ headers: Record<string, string>;
20
+ postData?: string;
21
+ }): OAuthInfo | null {
22
+ // Only POST requests
23
+ if (req.method.toUpperCase() !== 'POST') return null;
24
+
25
+ // URL heuristic: must contain /token or /oauth
26
+ const urlLower = req.url.toLowerCase();
27
+ if (!urlLower.includes('/token') && !urlLower.includes('/oauth')) return null;
28
+
29
+ if (!req.postData) return null;
30
+
31
+ // Parse body — support URL-encoded and JSON
32
+ const params = parseBody(req.postData, req.headers['content-type'] ?? '');
33
+ if (!params) return null;
34
+
35
+ const grantType = params.get('grant_type');
36
+ if (!grantType) return null;
37
+
38
+ // Only refreshable flows
39
+ if (grantType !== 'refresh_token' && grantType !== 'client_credentials') return null;
40
+
41
+ // Extract client_id — may also be in Basic auth header
42
+ let clientId = params.get('client_id') ?? '';
43
+ let clientSecret = params.get('client_secret');
44
+
45
+ if (!clientId) {
46
+ const basic = parseBasicAuth(req.headers['authorization'] ?? '');
47
+ if (basic) {
48
+ clientId = basic.username;
49
+ if (!clientSecret) clientSecret = basic.password;
50
+ }
51
+ }
52
+
53
+ if (!clientId) return null;
54
+
55
+ const result: OAuthInfo = {
56
+ tokenEndpoint: req.url.split('?')[0]!, // strip query params
57
+ clientId,
58
+ grantType: grantType as 'refresh_token' | 'client_credentials',
59
+ };
60
+
61
+ const scope = params.get('scope');
62
+ if (scope) result.scope = scope;
63
+ if (clientSecret) result.clientSecret = clientSecret;
64
+
65
+ return result;
66
+ }
67
+
68
+ function parseBody(body: string, contentType: string): Map<string, string> | null {
69
+ try {
70
+ if (contentType.includes('application/json')) {
71
+ const obj = JSON.parse(body);
72
+ if (typeof obj !== 'object' || obj === null) return null;
73
+ const map = new Map<string, string>();
74
+ for (const [k, v] of Object.entries(obj)) {
75
+ if (typeof v === 'string') map.set(k, v);
76
+ }
77
+ return map;
78
+ }
79
+
80
+ // Default: URL-encoded (+ is space in application/x-www-form-urlencoded)
81
+ const map = new Map<string, string>();
82
+ const pairs = body.split('&');
83
+ for (const pair of pairs) {
84
+ const idx = pair.indexOf('=');
85
+ if (idx === -1) continue;
86
+ const key = decodeURIComponent(pair.slice(0, idx).replace(/\+/g, ' '));
87
+ const val = decodeURIComponent(pair.slice(idx + 1).replace(/\+/g, ' '));
88
+ map.set(key, val);
89
+ }
90
+ return map.size > 0 ? map : null;
91
+ } catch {
92
+ return null;
93
+ }
94
+ }
95
+
96
+ function parseBasicAuth(header: string): { username: string; password: string } | null {
97
+ if (!header.startsWith('Basic ')) return null;
98
+ try {
99
+ const decoded = Buffer.from(header.slice(6), 'base64').toString('utf-8');
100
+ const idx = decoded.indexOf(':');
101
+ if (idx === -1) return null;
102
+ return { username: decoded.slice(0, idx), password: decoded.slice(idx + 1) };
103
+ } catch {
104
+ return null;
105
+ }
106
+ }
@@ -0,0 +1,49 @@
1
+ // src/capture/pagination.ts
2
+ import type { PaginationInfo } from '../types.js';
3
+
4
+ const OFFSET_PARAMS = new Set(['offset', 'skip']);
5
+ const CURSOR_PARAMS = new Set(['cursor', 'after', 'before', 'next_cursor', 'starting_after']);
6
+ const PAGE_PARAMS = new Set(['page', 'p', 'page_number']);
7
+ const LIMIT_PARAMS = new Set(['limit', 'per_page', 'page_size', 'count', 'size']);
8
+
9
+ /**
10
+ * Detect pagination patterns from query parameters.
11
+ * Returns null if no pagination pattern is detected.
12
+ */
13
+ export function detectPagination(
14
+ queryParams: Record<string, { type: string; example: string }>,
15
+ ): PaginationInfo | null {
16
+ const paramNames = Object.keys(queryParams);
17
+ const limitParam = paramNames.find(p => LIMIT_PARAMS.has(p.toLowerCase()));
18
+
19
+ // Check offset-based (offset/skip + optional limit)
20
+ for (const name of paramNames) {
21
+ if (OFFSET_PARAMS.has(name.toLowerCase())) {
22
+ return {
23
+ type: 'offset',
24
+ paramName: name,
25
+ ...(limitParam ? { limitParam } : {}),
26
+ };
27
+ }
28
+ }
29
+
30
+ // Check cursor-based
31
+ for (const name of paramNames) {
32
+ if (CURSOR_PARAMS.has(name.toLowerCase())) {
33
+ return { type: 'cursor', paramName: name };
34
+ }
35
+ }
36
+
37
+ // Check page-based
38
+ for (const name of paramNames) {
39
+ if (PAGE_PARAMS.has(name.toLowerCase())) {
40
+ return {
41
+ type: 'page',
42
+ paramName: name,
43
+ ...(limitParam && limitParam !== name ? { limitParam } : {}),
44
+ };
45
+ }
46
+ }
47
+
48
+ return null;
49
+ }
@@ -0,0 +1,68 @@
1
+ // src/capture/parameterize.ts
2
+
3
+ const UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
4
+ const PURE_NUMERIC_RE = /^\d+$/;
5
+ const LONG_DIGITS_RE = /\d{8,}/;
6
+ const NEXT_DATA_PREFIX_RE = /^\/_next\/data\/[^/]+\//;
7
+
8
+ /**
9
+ * Check if a path segment is a dynamic value that should be parameterized.
10
+ * Returns the parameter name (:id, :hash, :slug) or null if static.
11
+ */
12
+ function classifySegment(segment: string): string | null {
13
+ // Pure numeric → :id
14
+ if (PURE_NUMERIC_RE.test(segment)) return ':id';
15
+
16
+ // UUID → :id
17
+ if (UUID_RE.test(segment)) return ':id';
18
+
19
+ // Slug with embedded long number (8+ consecutive digits) — check before hash
20
+ // because slugs like "btc-updown-15m-1770254100" would also match the hash rule
21
+ if (LONG_DIGITS_RE.test(segment)) {
22
+ return ':slug';
23
+ }
24
+
25
+ // Strip hyphens/underscores for character analysis
26
+ const stripped = segment.replace(/[-_]/g, '');
27
+
28
+ // Hash-like: 12+ alphanumeric chars with both letters and digits
29
+ if (stripped.length >= 12 && /[a-zA-Z]/.test(stripped) && /\d/.test(stripped)) {
30
+ return ':hash';
31
+ }
32
+
33
+ return null;
34
+ }
35
+
36
+ /**
37
+ * Replace dynamic path segments with :param placeholders.
38
+ *
39
+ * Rules:
40
+ * - Pure numeric → :id
41
+ * - UUID → :id
42
+ * - 12+ alphanum with mixed letters+digits → :hash
43
+ * - Contains 8+ consecutive digits → :slug
44
+ */
45
+ export function parameterizePath(path: string): string {
46
+ const segments = path.split('/');
47
+ const result = segments.map(seg => {
48
+ if (seg === '') return seg;
49
+ return classifySegment(seg) ?? seg;
50
+ });
51
+ return result.join('/');
52
+ }
53
+
54
+ /**
55
+ * Strip framework-specific path noise for clean endpoint IDs.
56
+ *
57
+ * - Strips /_next/data/<hash>/ prefix (Next.js data routes)
58
+ * - Strips .json suffix
59
+ */
60
+ export function cleanFrameworkPath(path: string): string {
61
+ let cleaned = path;
62
+ // Strip _next/data/<hash>/ prefix
63
+ cleaned = cleaned.replace(NEXT_DATA_PREFIX_RE, '/');
64
+ // Strip .json suffix
65
+ cleaned = cleaned.replace(/\.json$/, '');
66
+ // Ensure we have at least /
67
+ return cleaned || '/';
68
+ }
@@ -0,0 +1,49 @@
1
+ // src/capture/scrubber.ts
2
+
3
+ // Email: standard pattern
4
+ const EMAIL_RE = /[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}/g;
5
+
6
+ // Phone (international): requires + prefix
7
+ const PHONE_INTL_RE = /\+[1-9]\d{7,14}/g;
8
+
9
+ // Phone (US): requires separators — (123) 456-7890 or 123-456-7890 or 123.456.7890
10
+ const PHONE_US_RE = /\(\d{3}\)[-.\s]\d{3}[-.\s]\d{4}|\d{3}[-.\s]\d{3}[-.\s]\d{4}/g;
11
+
12
+ // IPv4: four octets, each 0-255, validated programmatically
13
+ const IPV4_RE = /\b(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})\b/g;
14
+
15
+ // Credit card: 16 digits with optional dashes or spaces every 4
16
+ const CARD_RE = /\b\d{4}[- ]?\d{4}[- ]?\d{4}[- ]?\d{4}\b/g;
17
+
18
+ // US SSN: 123-45-6789
19
+ const SSN_RE = /\b\d{3}-\d{2}-\d{4}\b/g;
20
+
21
+ /**
22
+ * Scrub PII from a string. Returns the string with PII replaced by placeholders.
23
+ * Order matters: SSN before phone (SSN is more specific).
24
+ */
25
+ export function scrubPII(input: string): string {
26
+ let result = input;
27
+
28
+ // Email first (most distinctive pattern)
29
+ result = result.replace(EMAIL_RE, '[email]');
30
+
31
+ // SSN before phone (SSN pattern 123-45-6789 could be confused)
32
+ result = result.replace(SSN_RE, '[ssn]');
33
+
34
+ // Credit cards
35
+ result = result.replace(CARD_RE, '[card]');
36
+
37
+ // IPv4 with octet validation
38
+ result = result.replace(IPV4_RE, (_match, o1, o2, o3, o4) => {
39
+ const octets = [o1, o2, o3, o4].map(Number);
40
+ if (octets.every(o => o <= 255)) return '[ip]';
41
+ return _match;
42
+ });
43
+
44
+ // Phone (international, then US)
45
+ result = result.replace(PHONE_INTL_RE, '[phone]');
46
+ result = result.replace(PHONE_US_RE, '[phone]');
47
+
48
+ return result;
49
+ }