securenow 6.0.2 → 6.1.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 (87) hide show
  1. package/CONSUMING-APPS-GUIDE.md +455 -0
  2. package/NPM_README.md +2029 -0
  3. package/README.md +297 -40
  4. package/SKILL-API.md +634 -0
  5. package/SKILL-CLI.md +454 -0
  6. package/cidr.js +83 -0
  7. package/cli/apps.js +585 -0
  8. package/cli/auth.js +280 -0
  9. package/cli/client.js +115 -0
  10. package/cli/config.js +173 -0
  11. package/cli/diagnostics.js +387 -0
  12. package/cli/firewall.js +100 -0
  13. package/cli/fp.js +638 -0
  14. package/cli/init.js +201 -0
  15. package/cli/monitor.js +440 -0
  16. package/cli/run.js +148 -0
  17. package/cli/security.js +980 -0
  18. package/cli/ui.js +386 -0
  19. package/cli/utils.js +127 -0
  20. package/cli.js +466 -455
  21. package/console-instrumentation.js +147 -136
  22. package/docs/ALL-FRAMEWORKS-QUICKSTART.md +1377 -455
  23. package/docs/API-KEYS-GUIDE.md +233 -0
  24. package/docs/ARCHITECTURE.md +3 -3
  25. package/docs/AUTO-BODY-CAPTURE.md +1 -1
  26. package/docs/AUTO-SETUP-SUMMARY.md +331 -0
  27. package/docs/AUTO-SETUP.md +4 -4
  28. package/docs/AUTOMATIC-IP-CAPTURE.md +5 -5
  29. package/docs/BODY-CAPTURE-FIX.md +261 -0
  30. package/docs/BODY-CAPTURE-QUICKSTART.md +2 -2
  31. package/docs/CHANGELOG-NEXTJS.md +1 -35
  32. package/docs/COMPLETION-REPORT.md +408 -0
  33. package/docs/CUSTOMER-GUIDE.md +16 -16
  34. package/docs/EASIEST-SETUP.md +5 -5
  35. package/docs/ENVIRONMENT-VARIABLES.md +880 -652
  36. package/docs/EXPRESS-BODY-CAPTURE.md +13 -12
  37. package/docs/EXPRESS-SETUP-GUIDE.md +719 -720
  38. package/docs/FINAL-SOLUTION.md +335 -0
  39. package/docs/FIREWALL-GUIDE.md +426 -0
  40. package/docs/IMPLEMENTATION-SUMMARY.md +410 -0
  41. package/docs/INDEX.md +22 -4
  42. package/docs/LOGGING-GUIDE.md +701 -708
  43. package/docs/LOGGING-QUICKSTART.md +234 -255
  44. package/docs/NEXTJS-BODY-CAPTURE-COMPARISON.md +323 -0
  45. package/docs/NEXTJS-BODY-CAPTURE.md +2 -2
  46. package/docs/NEXTJS-GUIDE.md +14 -14
  47. package/docs/NEXTJS-QUICKSTART.md +1 -1
  48. package/docs/NEXTJS-SETUP-COMPLETE.md +795 -0
  49. package/docs/NEXTJS-WRAPPER-APPROACH.md +1 -1
  50. package/docs/NUXT-GUIDE.md +166 -0
  51. package/docs/QUICKSTART-BODY-CAPTURE.md +2 -2
  52. package/docs/REDACTION-EXAMPLES.md +1 -1
  53. package/docs/REQUEST-BODY-CAPTURE.md +19 -10
  54. package/docs/SOLUTION-SUMMARY.md +312 -0
  55. package/docs/VERCEL-OTEL-MIGRATION.md +3 -3
  56. package/examples/README.md +6 -6
  57. package/examples/instrumentation-with-auto-capture.ts +1 -1
  58. package/examples/nextjs-env-example.txt +2 -2
  59. package/examples/nextjs-instrumentation.js +1 -1
  60. package/examples/nextjs-instrumentation.ts +1 -1
  61. package/examples/nextjs-with-logging-example.md +6 -6
  62. package/examples/nextjs-with-options.ts +1 -1
  63. package/examples/test-nextjs-setup.js +1 -1
  64. package/firewall-cloud.js +212 -0
  65. package/firewall-iptables.js +139 -0
  66. package/firewall-only.js +38 -0
  67. package/firewall-tcp.js +74 -0
  68. package/firewall.js +720 -0
  69. package/free-trial-banner.js +174 -0
  70. package/nextjs-auto-capture.js +199 -207
  71. package/nextjs-middleware.js +186 -181
  72. package/nextjs-webpack-config.js +88 -53
  73. package/nextjs-wrapper.js +158 -158
  74. package/nextjs.d.ts +1 -1
  75. package/nextjs.js +639 -647
  76. package/nuxt-server-plugin.mjs +423 -0
  77. package/nuxt.d.ts +60 -0
  78. package/nuxt.mjs +75 -0
  79. package/package.json +186 -164
  80. package/postinstall.js +6 -6
  81. package/register.d.ts +1 -1
  82. package/register.js +39 -4
  83. package/resolve-ip.js +77 -0
  84. package/tracing.d.ts +2 -1
  85. package/tracing.js +295 -34
  86. package/web-vite.mjs +239 -156
  87. package/LICENSE +0 -15
@@ -1,181 +1,186 @@
1
- /**
2
- * SecureNow Next.js Middleware for Body Capture
3
- *
4
- * OPTIONAL: Import this in your Next.js app to enable automatic body capture
5
- *
6
- * Usage:
7
- *
8
- * Create middleware.ts in your Next.js app root:
9
- *
10
- * export { middleware } from 'securenow/nextjs-middleware';
11
- * export const config = {
12
- * matcher: '/api/:path*', // Apply to API routes only
13
- * };
14
- *
15
- * That's it! Bodies are now captured with sensitive data redacted.
16
- */
17
-
18
- const { trace, context, SpanStatusCode } = require('@opentelemetry/api');
19
-
20
- // Default sensitive fields to redact
21
- const DEFAULT_SENSITIVE_FIELDS = [
22
- 'password', 'passwd', 'pwd', 'secret', 'token', 'api_key', 'apikey',
23
- 'access_token', 'auth', 'credentials', 'mysql_pwd', 'stripeToken',
24
- 'card', 'cardnumber', 'ccv', 'cvc', 'cvv', 'ssn', 'pin',
25
- ];
26
-
27
- /**
28
- * Redact sensitive fields from an object
29
- */
30
- function redactSensitiveData(obj, sensitiveFields = DEFAULT_SENSITIVE_FIELDS) {
31
- if (!obj || typeof obj !== 'object') return obj;
32
-
33
- const redacted = Array.isArray(obj) ? [...obj] : { ...obj };
34
-
35
- for (const key in redacted) {
36
- const lowerKey = key.toLowerCase();
37
-
38
- if (sensitiveFields.some(field => lowerKey.includes(field.toLowerCase()))) {
39
- redacted[key] = '[REDACTED]';
40
- } else if (typeof redacted[key] === 'object' && redacted[key] !== null) {
41
- redacted[key] = redactSensitiveData(redacted[key], sensitiveFields);
42
- }
43
- }
44
-
45
- return redacted;
46
- }
47
-
48
- /**
49
- * Redact sensitive data from GraphQL query strings
50
- */
51
- function redactGraphQLQuery(query, sensitiveFields = DEFAULT_SENSITIVE_FIELDS) {
52
- if (!query || typeof query !== 'string') return query;
53
-
54
- let redacted = query;
55
-
56
- sensitiveFields.forEach(field => {
57
- const patterns = [
58
- new RegExp(`(${field}\\s*:\\s*["'])([^"']+)(["'])`, 'gi'),
59
- new RegExp(`(${field}\\s*:\\s*)([^\\s,})\n]+)`, 'gi'),
60
- ];
61
-
62
- patterns.forEach(pattern => {
63
- redacted = redacted.replace(pattern, (match, prefix, value, suffix) => {
64
- return suffix ? `${prefix}[REDACTED]${suffix}` : `${prefix}[REDACTED]`;
65
- });
66
- });
67
- });
68
-
69
- return redacted;
70
- }
71
-
72
- /**
73
- * Next.js Middleware for Body Capture
74
- */
75
- async function middleware(request) {
76
- const { NextResponse } = require('next/server');
77
-
78
- // Only capture for POST/PUT/PATCH
79
- if (!['POST', 'PUT', 'PATCH'].includes(request.method)) {
80
- return NextResponse.next();
81
- }
82
-
83
- // Get or create a tracer
84
- const tracer = trace.getTracer('securenow-middleware');
85
- let span = trace.getActiveSpan();
86
- let createdSpan = false;
87
-
88
- // If no active span, create one for this middleware
89
- if (!span) {
90
- const url = new URL(request.url);
91
- span = tracer.startSpan(`middleware ${request.method} ${url.pathname}`);
92
- createdSpan = true;
93
- }
94
-
95
- try {
96
- const contentType = request.headers.get('content-type') || '';
97
- const maxBodySize = parseInt(process.env.SECURENOW_MAX_BODY_SIZE || '10240');
98
- const customSensitiveFields = (process.env.SECURENOW_SENSITIVE_FIELDS || '').split(',').map(s => s.trim()).filter(Boolean);
99
- const allSensitiveFields = [...DEFAULT_SENSITIVE_FIELDS, ...customSensitiveFields];
100
-
101
- // Only capture supported types
102
- if (contentType.includes('application/json') ||
103
- contentType.includes('application/graphql')) {
104
-
105
- // Clone the request to read body without consuming the original
106
- const clonedRequest = request.clone();
107
- const bodyText = await clonedRequest.text();
108
-
109
- if (bodyText.length <= maxBodySize) {
110
- let redactedBody;
111
-
112
- if (contentType.includes('application/graphql')) {
113
- // GraphQL: redact query string
114
- redactedBody = redactGraphQLQuery(bodyText, allSensitiveFields);
115
- } else {
116
- // JSON: parse and redact
117
- try {
118
- const parsed = JSON.parse(bodyText);
119
- const redacted = redactSensitiveData(parsed, allSensitiveFields);
120
- redactedBody = JSON.stringify(redacted);
121
- } catch (e) {
122
- redactedBody = bodyText; // Keep as-is if parse fails
123
- }
124
- }
125
-
126
- span.setAttributes({
127
- 'http.request.body': redactedBody.substring(0, maxBodySize),
128
- 'http.request.body.type': contentType.includes('graphql') ? 'graphql' : 'json',
129
- 'http.request.body.size': bodyText.length,
130
- });
131
- } else {
132
- span.setAttribute('http.request.body', `[TOO LARGE: ${bodyText.length} bytes]`);
133
- }
134
- } else if (contentType.includes('application/x-www-form-urlencoded')) {
135
- const clonedRequest = request.clone();
136
- const formData = await clonedRequest.formData();
137
- const parsed = Object.fromEntries(formData);
138
- const redacted = redactSensitiveData(parsed, allSensitiveFields);
139
-
140
- span.setAttributes({
141
- 'http.request.body': JSON.stringify(redacted).substring(0, maxBodySize),
142
- 'http.request.body.type': 'form',
143
- 'http.request.body.size': JSON.stringify(parsed).length,
144
- });
145
- } else if (contentType.includes('multipart/form-data')) {
146
- span.setAttribute('http.request.body', '[MULTIPART - NOT CAPTURED]');
147
- span.setAttribute('http.request.body.type', 'multipart');
148
- }
149
-
150
- // End span if we created it
151
- if (createdSpan) {
152
- span.setStatus({ code: SpanStatusCode.OK });
153
- span.end();
154
- }
155
- } catch (error) {
156
- // Silently fail - don't break the request
157
- console.debug('[securenow] Body capture failed:', error.message);
158
-
159
- // End span with error if we created it
160
- if (createdSpan && span) {
161
- span.setStatus({
162
- code: SpanStatusCode.ERROR,
163
- message: error.message
164
- });
165
- span.end();
166
- }
167
- }
168
-
169
- return NextResponse.next();
170
- }
171
-
172
- module.exports = {
173
- middleware,
174
- redactSensitiveData,
175
- redactGraphQLQuery,
176
- DEFAULT_SENSITIVE_FIELDS,
177
- };
178
-
179
-
180
-
181
-
1
+ /**
2
+ * SecureNow Next.js Middleware for Body Capture
3
+ *
4
+ * OPTIONAL: Import this in your Next.js app to enable automatic body capture
5
+ *
6
+ * Usage:
7
+ *
8
+ * Create middleware.ts in your Next.js app root:
9
+ *
10
+ * export { middleware } from 'securenow/nextjs-middleware';
11
+ * export const config = {
12
+ * matcher: '/api/:path*', // Apply to API routes only
13
+ * };
14
+ *
15
+ * That's it! Bodies are now captured with sensitive data redacted.
16
+ */
17
+
18
+ const { trace, context, SpanStatusCode } = require('@opentelemetry/api');
19
+
20
+ // Default sensitive fields to redact
21
+ const DEFAULT_SENSITIVE_FIELDS = [
22
+ 'password', 'passwd', 'pwd', 'secret', 'token', 'api_key', 'apikey',
23
+ 'access_token', 'auth', 'credentials', 'mysql_pwd', 'stripeToken',
24
+ 'card', 'cardnumber', 'ccv', 'cvc', 'cvv', 'ssn', 'pin',
25
+ ];
26
+
27
+ /**
28
+ * Redact sensitive fields from an object
29
+ */
30
+ function escapeRegex(str) {
31
+ return str.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
32
+ }
33
+
34
+ function redactSensitiveData(obj, sensitiveFields = DEFAULT_SENSITIVE_FIELDS) {
35
+ if (!obj || typeof obj !== 'object') return obj;
36
+
37
+ const redacted = Array.isArray(obj) ? [...obj] : { ...obj };
38
+
39
+ for (const key of Object.keys(redacted)) {
40
+ const lowerKey = key.toLowerCase();
41
+
42
+ if (sensitiveFields.some(field => lowerKey.includes(field.toLowerCase()))) {
43
+ redacted[key] = '[REDACTED]';
44
+ } else if (typeof redacted[key] === 'object' && redacted[key] !== null) {
45
+ redacted[key] = redactSensitiveData(redacted[key], sensitiveFields);
46
+ }
47
+ }
48
+
49
+ return redacted;
50
+ }
51
+
52
+ /**
53
+ * Redact sensitive data from GraphQL query strings
54
+ */
55
+ function redactGraphQLQuery(query, sensitiveFields = DEFAULT_SENSITIVE_FIELDS) {
56
+ if (!query || typeof query !== 'string') return query;
57
+
58
+ let redacted = query;
59
+
60
+ sensitiveFields.forEach(field => {
61
+ const escaped = escapeRegex(field);
62
+ const patterns = [
63
+ new RegExp(`(${escaped}\\s*:\\s*["'])([^"']+)(["'])`, 'gi'),
64
+ new RegExp(`(${escaped}\\s*:\\s*)([^\\s,})\n]+)`, 'gi'),
65
+ ];
66
+
67
+ patterns.forEach(pattern => {
68
+ redacted = redacted.replace(pattern, (match, prefix, value, suffix) => {
69
+ return suffix ? `${prefix}[REDACTED]${suffix}` : `${prefix}[REDACTED]`;
70
+ });
71
+ });
72
+ });
73
+
74
+ return redacted;
75
+ }
76
+
77
+ /**
78
+ * Next.js Middleware for Body Capture
79
+ */
80
+ async function middleware(request) {
81
+ const { NextResponse } = require('next/server');
82
+
83
+ // Only capture for POST/PUT/PATCH
84
+ if (!['POST', 'PUT', 'PATCH'].includes(request.method)) {
85
+ return NextResponse.next();
86
+ }
87
+
88
+ // Get or create a tracer
89
+ const tracer = trace.getTracer('securenow-middleware');
90
+ let span = trace.getActiveSpan();
91
+ let createdSpan = false;
92
+
93
+ // If no active span, create one for this middleware
94
+ if (!span) {
95
+ const url = new URL(request.url);
96
+ span = tracer.startSpan(`middleware ${request.method} ${url.pathname}`);
97
+ createdSpan = true;
98
+ }
99
+
100
+ try {
101
+ const contentType = request.headers.get('content-type') || '';
102
+ const maxBodySize = Math.max(1024, parseInt(process.env.SECURENOW_MAX_BODY_SIZE, 10) || 10240);
103
+ const customSensitiveFields = (process.env.SECURENOW_SENSITIVE_FIELDS || '').split(',').map(s => s.trim()).filter(Boolean);
104
+ const allSensitiveFields = [...DEFAULT_SENSITIVE_FIELDS, ...customSensitiveFields];
105
+
106
+ // Only capture supported types
107
+ if (contentType.includes('application/json') ||
108
+ contentType.includes('application/graphql')) {
109
+
110
+ // Clone the request to read body without consuming the original
111
+ const clonedRequest = request.clone();
112
+ const bodyText = await clonedRequest.text();
113
+
114
+ if (bodyText.length <= maxBodySize) {
115
+ let redactedBody;
116
+
117
+ if (contentType.includes('application/graphql')) {
118
+ // GraphQL: redact query string
119
+ redactedBody = redactGraphQLQuery(bodyText, allSensitiveFields);
120
+ } else {
121
+ // JSON: parse and redact
122
+ try {
123
+ const parsed = JSON.parse(bodyText);
124
+ const redacted = redactSensitiveData(parsed, allSensitiveFields);
125
+ redactedBody = JSON.stringify(redacted);
126
+ } catch (e) {
127
+ redactedBody = '[UNPARSEABLE - REDACTED FOR SAFETY]';
128
+ }
129
+ }
130
+
131
+ span.setAttributes({
132
+ 'http.request.body': redactedBody.substring(0, maxBodySize),
133
+ 'http.request.body.type': contentType.includes('graphql') ? 'graphql' : 'json',
134
+ 'http.request.body.size': bodyText.length,
135
+ });
136
+ } else {
137
+ span.setAttribute('http.request.body', `[TOO LARGE: ${bodyText.length} bytes]`);
138
+ }
139
+ } else if (contentType.includes('application/x-www-form-urlencoded')) {
140
+ const clonedRequest = request.clone();
141
+ const formData = await clonedRequest.formData();
142
+ const parsed = Object.fromEntries(formData);
143
+ const redacted = redactSensitiveData(parsed, allSensitiveFields);
144
+
145
+ span.setAttributes({
146
+ 'http.request.body': JSON.stringify(redacted).substring(0, maxBodySize),
147
+ 'http.request.body.type': 'form',
148
+ 'http.request.body.size': JSON.stringify(parsed).length,
149
+ });
150
+ } else if (contentType.includes('multipart/form-data')) {
151
+ span.setAttribute('http.request.body', '[MULTIPART - NOT CAPTURED]');
152
+ span.setAttribute('http.request.body.type', 'multipart');
153
+ }
154
+
155
+ // End span if we created it
156
+ if (createdSpan) {
157
+ span.setStatus({ code: SpanStatusCode.OK });
158
+ span.end();
159
+ }
160
+ } catch (error) {
161
+ // Silently fail - don't break the request
162
+ console.debug('[securenow] Body capture failed:', error.message);
163
+
164
+ // End span with error if we created it
165
+ if (createdSpan && span) {
166
+ span.setStatus({
167
+ code: SpanStatusCode.ERROR,
168
+ message: error.message
169
+ });
170
+ span.end();
171
+ }
172
+ }
173
+
174
+ return NextResponse.next();
175
+ }
176
+
177
+ module.exports = {
178
+ middleware,
179
+ redactSensitiveData,
180
+ redactGraphQLQuery,
181
+ DEFAULT_SENSITIVE_FIELDS,
182
+ };
183
+
184
+
185
+
186
+
@@ -1,33 +1,100 @@
1
+ 'use strict';
2
+
3
+ /**
4
+ * Next.js configuration helpers for SecureNow
5
+ *
6
+ * Usage (recommended — zero-list approach):
7
+ *
8
+ * const { withSecureNow } = require('securenow/nextjs-webpack-config');
9
+ * module.exports = withSecureNow({
10
+ * // your existing next.config options
11
+ * });
12
+ *
13
+ * Legacy webpack-only helper (still exported for backwards compat):
14
+ *
15
+ * const { getSecureNowWebpackConfig } = require('securenow/nextjs-webpack-config');
16
+ * module.exports = { webpack: (config, opts) => getSecureNowWebpackConfig(config, opts) };
17
+ */
18
+
19
+ const EXTERNAL_PACKAGES = [
20
+ 'securenow',
21
+ '@opentelemetry/sdk-node',
22
+ '@opentelemetry/auto-instrumentations-node',
23
+ '@opentelemetry/instrumentation-http',
24
+ '@opentelemetry/exporter-trace-otlp-http',
25
+ '@opentelemetry/exporter-logs-otlp-http',
26
+ '@opentelemetry/sdk-logs',
27
+ '@opentelemetry/instrumentation',
28
+ '@opentelemetry/resources',
29
+ '@opentelemetry/semantic-conventions',
30
+ '@opentelemetry/api',
31
+ '@opentelemetry/api-logs',
32
+ '@vercel/otel',
33
+ ];
34
+
35
+ function detectNextMajor() {
36
+ try {
37
+ const pkg = require('next/package.json');
38
+ return parseInt(pkg.version, 10) || 14;
39
+ } catch {
40
+ return 14;
41
+ }
42
+ }
43
+
1
44
  /**
2
- * Next.js webpack configuration for SecureNow
3
- *
4
- * Add this to your next.config.js to suppress OpenTelemetry instrumentation warnings
5
- *
6
- * Usage:
7
- * const { getSecureNowWebpackConfig } = require('securenow/nextjs-webpack-config');
8
- *
9
- * module.exports = {
10
- * webpack: (config, options) => {
11
- * return getSecureNowWebpackConfig(config, options);
12
- * }
13
- * };
45
+ * Wrap a Next.js config object to auto-externalize SecureNow + OTel
46
+ * packages and enable the instrumentation hook.
47
+ *
48
+ * module.exports = withSecureNow({ reactStrictMode: true });
14
49
  */
50
+ function withSecureNow(userConfig) {
51
+ if (typeof userConfig === 'function') {
52
+ return (...args) => withSecureNow(userConfig(...args));
53
+ }
54
+
55
+ const cfg = { ...userConfig };
56
+ const major = detectNextMajor();
57
+
58
+ if (major >= 15) {
59
+ cfg.serverExternalPackages = dedup([
60
+ ...(cfg.serverExternalPackages || []),
61
+ ...EXTERNAL_PACKAGES,
62
+ ]);
63
+ } else {
64
+ cfg.experimental = { ...(cfg.experimental || {}) };
65
+ cfg.experimental.instrumentationHook = true;
66
+ cfg.experimental.serverComponentsExternalPackages = dedup([
67
+ ...(cfg.experimental.serverComponentsExternalPackages || []),
68
+ ...EXTERNAL_PACKAGES,
69
+ ]);
70
+ }
71
+
72
+ const origWebpack = cfg.webpack;
73
+ cfg.webpack = (config, options) => {
74
+ const c = origWebpack ? origWebpack(config, options) : config;
75
+ return getSecureNowWebpackConfig(c, options);
76
+ };
77
+
78
+ return cfg;
79
+ }
15
80
 
81
+ function dedup(arr) {
82
+ return [...new Set(arr)];
83
+ }
84
+
85
+ /**
86
+ * Legacy: suppress OTel webpack warnings and add externals.
87
+ */
16
88
  function getSecureNowWebpackConfig(config, options) {
17
89
  const { isServer } = options;
18
-
19
- // Only apply to server-side builds
90
+
20
91
  if (isServer) {
21
- // Suppress warnings for OpenTelemetry instrumentations
22
92
  config.ignoreWarnings = config.ignoreWarnings || [];
23
-
24
93
  config.ignoreWarnings.push(
25
- // Ignore "Critical dependency" warnings from instrumentations
26
94
  {
27
95
  module: /@opentelemetry\/instrumentation/,
28
96
  message: /Critical dependency: the request of a dependency is an expression/,
29
97
  },
30
- // Ignore missing optional peer dependencies
31
98
  {
32
99
  module: /@opentelemetry/,
33
100
  message: /Module not found.*@opentelemetry\/winston-transport/,
@@ -35,43 +102,11 @@ function getSecureNowWebpackConfig(config, options) {
35
102
  {
36
103
  module: /@opentelemetry/,
37
104
  message: /Module not found.*@opentelemetry\/exporter-jaeger/,
38
- }
105
+ },
39
106
  );
40
-
41
- // Externalize problematic packages (don't bundle them)
42
- config.externals = config.externals || [];
43
-
44
- // Add OpenTelemetry packages as externals
45
- if (typeof config.externals === 'function') {
46
- const originalExternals = config.externals;
47
- config.externals = async (...args) => {
48
- const result = await originalExternals(...args);
49
- if (result) return result;
50
-
51
- const [context, request] = args;
52
-
53
- // Externalize OpenTelemetry instrumentation packages
54
- if (request.startsWith('@opentelemetry/')) {
55
- return `commonjs ${request}`;
56
- }
57
-
58
- return undefined;
59
- };
60
- } else if (Array.isArray(config.externals)) {
61
- config.externals.push(/@opentelemetry\//);
62
- } else {
63
- config.externals = [/@opentelemetry\//];
64
- }
65
107
  }
66
-
108
+
67
109
  return config;
68
110
  }
69
111
 
70
- module.exports = { getSecureNowWebpackConfig };
71
-
72
-
73
-
74
-
75
-
76
-
77
-
112
+ module.exports = { withSecureNow, getSecureNowWebpackConfig, EXTERNAL_PACKAGES };