@cloudflare/sandbox 0.0.0-153e416 → 0.0.0-1558ec9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +98 -16
- package/Dockerfile +79 -32
- package/LICENSE +176 -0
- package/README.md +10 -3
- package/dist/index.d.ts +1953 -9
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +3278 -65
- package/dist/index.js.map +1 -1
- package/package.json +5 -5
- package/src/clients/base-client.ts +39 -24
- package/src/clients/command-client.ts +8 -8
- package/src/clients/file-client.ts +31 -26
- package/src/clients/git-client.ts +9 -3
- package/src/clients/index.ts +16 -16
- package/src/clients/interpreter-client.ts +51 -47
- package/src/clients/port-client.ts +10 -10
- package/src/clients/process-client.ts +11 -8
- package/src/clients/sandbox-client.ts +2 -4
- package/src/clients/types.ts +6 -2
- package/src/clients/utility-client.ts +43 -6
- package/src/errors/adapter.ts +90 -32
- package/src/errors/classes.ts +189 -64
- package/src/errors/index.ts +9 -5
- package/src/file-stream.ts +11 -6
- package/src/index.ts +28 -17
- package/src/interpreter.ts +50 -41
- package/src/request-handler.ts +24 -21
- package/src/sandbox.ts +403 -148
- package/src/security.ts +21 -6
- package/src/sse-parser.ts +4 -3
- package/src/version.ts +1 -1
- package/startup.sh +1 -1
- package/tests/base-client.test.ts +116 -80
- package/tests/command-client.test.ts +149 -112
- package/tests/file-client.test.ts +309 -197
- package/tests/file-stream.test.ts +24 -20
- package/tests/get-sandbox.test.ts +45 -6
- package/tests/git-client.test.ts +260 -101
- package/tests/port-client.test.ts +100 -108
- package/tests/process-client.test.ts +204 -179
- package/tests/request-handler.test.ts +117 -65
- package/tests/sandbox.test.ts +253 -68
- package/tests/sse-parser.test.ts +17 -16
- package/tests/utility-client.test.ts +79 -72
- package/tsdown.config.ts +12 -0
- package/vitest.config.ts +6 -6
- package/dist/chunk-BFVUNTP4.js +0 -104
- package/dist/chunk-BFVUNTP4.js.map +0 -1
- package/dist/chunk-EKSWCBCA.js +0 -86
- package/dist/chunk-EKSWCBCA.js.map +0 -1
- package/dist/chunk-FE4PJSRB.js +0 -7
- package/dist/chunk-FE4PJSRB.js.map +0 -1
- package/dist/chunk-JXZMAU2C.js +0 -559
- package/dist/chunk-JXZMAU2C.js.map +0 -1
- package/dist/chunk-SVWLTRHD.js +0 -2456
- package/dist/chunk-SVWLTRHD.js.map +0 -1
- package/dist/chunk-Z532A7QC.js +0 -78
- package/dist/chunk-Z532A7QC.js.map +0 -1
- package/dist/file-stream.d.ts +0 -43
- package/dist/file-stream.js +0 -9
- package/dist/file-stream.js.map +0 -1
- package/dist/interpreter.d.ts +0 -33
- package/dist/interpreter.js +0 -8
- package/dist/interpreter.js.map +0 -1
- package/dist/request-handler.d.ts +0 -18
- package/dist/request-handler.js +0 -13
- package/dist/request-handler.js.map +0 -1
- package/dist/sandbox-DWQVgVTY.d.ts +0 -603
- package/dist/sandbox.d.ts +0 -4
- package/dist/sandbox.js +0 -13
- package/dist/sandbox.js.map +0 -1
- package/dist/security.d.ts +0 -31
- package/dist/security.js +0 -13
- package/dist/security.js.map +0 -1
- package/dist/sse-parser.d.ts +0 -28
- package/dist/sse-parser.js +0 -11
- package/dist/sse-parser.js.map +0 -1
- package/dist/version.d.ts +0 -8
- package/dist/version.js +0 -7
- package/dist/version.js.map +0 -1
package/src/errors/index.ts
CHANGED
|
@@ -39,11 +39,13 @@
|
|
|
39
39
|
*/
|
|
40
40
|
|
|
41
41
|
// Re-export context types for advanced usage
|
|
42
|
-
export type {
|
|
42
|
+
export type {
|
|
43
43
|
CodeExecutionContext,
|
|
44
44
|
CommandErrorContext,
|
|
45
45
|
CommandNotFoundContext,
|
|
46
|
-
ContextNotFoundContext,
|
|
46
|
+
ContextNotFoundContext,
|
|
47
|
+
ErrorCodeType,
|
|
48
|
+
ErrorResponse,
|
|
47
49
|
FileExistsContext,
|
|
48
50
|
FileNotFoundContext,
|
|
49
51
|
FileSystemContext,
|
|
@@ -53,13 +55,15 @@ export type {
|
|
|
53
55
|
GitRepositoryNotFoundContext,
|
|
54
56
|
InternalErrorContext,
|
|
55
57
|
InterpreterNotReadyContext,
|
|
56
|
-
InvalidPortContext,
|
|
58
|
+
InvalidPortContext,
|
|
59
|
+
OperationType,
|
|
57
60
|
PortAlreadyExposedContext,
|
|
58
61
|
PortErrorContext,
|
|
59
62
|
PortNotExposedContext,
|
|
60
63
|
ProcessErrorContext,
|
|
61
64
|
ProcessNotFoundContext,
|
|
62
|
-
ValidationFailedContext
|
|
65
|
+
ValidationFailedContext
|
|
66
|
+
} from '@repo/shared/errors';
|
|
63
67
|
// Re-export shared types and constants
|
|
64
68
|
export { ErrorCode, Operation } from '@repo/shared/errors';
|
|
65
69
|
|
|
@@ -101,5 +105,5 @@ export {
|
|
|
101
105
|
SandboxError,
|
|
102
106
|
ServiceNotRespondingError,
|
|
103
107
|
// Validation Errors
|
|
104
|
-
ValidationFailedError
|
|
108
|
+
ValidationFailedError
|
|
105
109
|
} from './classes';
|
package/src/file-stream.ts
CHANGED
|
@@ -3,7 +3,9 @@ import type { FileChunk, FileMetadata, FileStreamEvent } from '@repo/shared';
|
|
|
3
3
|
/**
|
|
4
4
|
* Parse SSE (Server-Sent Events) lines from a stream
|
|
5
5
|
*/
|
|
6
|
-
async function* parseSSE(
|
|
6
|
+
async function* parseSSE(
|
|
7
|
+
stream: ReadableStream<Uint8Array>
|
|
8
|
+
): AsyncGenerator<FileStreamEvent> {
|
|
7
9
|
const reader = stream.getReader();
|
|
8
10
|
const decoder = new TextDecoder();
|
|
9
11
|
let buffer = '';
|
|
@@ -59,7 +61,9 @@ async function* parseSSE(stream: ReadableStream<Uint8Array>): AsyncGenerator<Fil
|
|
|
59
61
|
* }
|
|
60
62
|
* ```
|
|
61
63
|
*/
|
|
62
|
-
export async function* streamFile(
|
|
64
|
+
export async function* streamFile(
|
|
65
|
+
stream: ReadableStream<Uint8Array>
|
|
66
|
+
): AsyncGenerator<FileChunk, FileMetadata> {
|
|
63
67
|
let metadata: FileMetadata | null = null;
|
|
64
68
|
|
|
65
69
|
for await (const event of parseSSE(stream)) {
|
|
@@ -69,7 +73,7 @@ export async function* streamFile(stream: ReadableStream<Uint8Array>): AsyncGene
|
|
|
69
73
|
mimeType: event.mimeType,
|
|
70
74
|
size: event.size,
|
|
71
75
|
isBinary: event.isBinary,
|
|
72
|
-
encoding: event.encoding
|
|
76
|
+
encoding: event.encoding
|
|
73
77
|
};
|
|
74
78
|
break;
|
|
75
79
|
|
|
@@ -144,8 +148,9 @@ export async function collectFile(stream: ReadableStream<Uint8Array>): Promise<{
|
|
|
144
148
|
// Combine chunks based on type
|
|
145
149
|
if (metadata.isBinary) {
|
|
146
150
|
// Binary file - combine Uint8Arrays
|
|
147
|
-
const totalLength = chunks.reduce(
|
|
148
|
-
sum + (chunk instanceof Uint8Array ? chunk.length : 0),
|
|
151
|
+
const totalLength = chunks.reduce(
|
|
152
|
+
(sum, chunk) => sum + (chunk instanceof Uint8Array ? chunk.length : 0),
|
|
153
|
+
0
|
|
149
154
|
);
|
|
150
155
|
const combined = new Uint8Array(totalLength);
|
|
151
156
|
let offset = 0;
|
|
@@ -158,7 +163,7 @@ export async function collectFile(stream: ReadableStream<Uint8Array>): Promise<{
|
|
|
158
163
|
return { content: combined, metadata };
|
|
159
164
|
} else {
|
|
160
165
|
// Text file - combine strings
|
|
161
|
-
const combined = chunks.filter(c => typeof c === 'string').join('');
|
|
166
|
+
const combined = chunks.filter((c) => typeof c === 'string').join('');
|
|
162
167
|
return { content: combined, metadata };
|
|
163
168
|
}
|
|
164
169
|
}
|
package/src/index.ts
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
// Export the main Sandbox class and utilities
|
|
2
2
|
|
|
3
|
-
|
|
4
3
|
// Export the new client architecture
|
|
5
4
|
export {
|
|
6
5
|
CommandClient,
|
|
@@ -10,8 +9,8 @@ export {
|
|
|
10
9
|
ProcessClient,
|
|
11
10
|
SandboxClient,
|
|
12
11
|
UtilityClient
|
|
13
|
-
} from
|
|
14
|
-
export { getSandbox, Sandbox } from
|
|
12
|
+
} from './clients';
|
|
13
|
+
export { getSandbox, Sandbox } from './sandbox';
|
|
15
14
|
|
|
16
15
|
// Legacy types are now imported from the new client architecture
|
|
17
16
|
|
|
@@ -20,26 +19,31 @@ export type {
|
|
|
20
19
|
BaseExecOptions,
|
|
21
20
|
ExecEvent,
|
|
22
21
|
ExecOptions,
|
|
23
|
-
ExecResult,
|
|
22
|
+
ExecResult,
|
|
23
|
+
FileChunk,
|
|
24
|
+
FileMetadata,
|
|
25
|
+
FileStreamEvent,
|
|
24
26
|
ISandbox,
|
|
25
27
|
LogEvent,
|
|
26
28
|
Process,
|
|
27
29
|
ProcessOptions,
|
|
28
30
|
ProcessStatus,
|
|
29
|
-
StreamOptions
|
|
30
|
-
} from
|
|
31
|
+
StreamOptions
|
|
32
|
+
} from '@repo/shared';
|
|
31
33
|
export * from '@repo/shared';
|
|
32
34
|
// Export type guards for runtime validation
|
|
33
|
-
export {
|
|
34
|
-
isExecResult,
|
|
35
|
-
isProcess,
|
|
36
|
-
isProcessStatus
|
|
37
|
-
} from "@repo/shared";
|
|
35
|
+
export { isExecResult, isProcess, isProcessStatus } from '@repo/shared';
|
|
38
36
|
// Export all client types from new architecture
|
|
39
37
|
export type {
|
|
40
38
|
BaseApiResponse,
|
|
41
39
|
CommandsResponse,
|
|
42
40
|
ContainerStub,
|
|
41
|
+
|
|
42
|
+
// Utility client types
|
|
43
|
+
CreateSessionRequest,
|
|
44
|
+
CreateSessionResponse,
|
|
45
|
+
DeleteSessionRequest,
|
|
46
|
+
DeleteSessionResponse,
|
|
43
47
|
ErrorResponse,
|
|
44
48
|
|
|
45
49
|
// Command client types
|
|
@@ -58,8 +62,6 @@ export type {
|
|
|
58
62
|
|
|
59
63
|
// File client types
|
|
60
64
|
MkdirRequest,
|
|
61
|
-
|
|
62
|
-
// Utility client types
|
|
63
65
|
PingResponse,
|
|
64
66
|
PortCloseResult,
|
|
65
67
|
PortExposeResult,
|
|
@@ -79,15 +81,24 @@ export type {
|
|
|
79
81
|
StartProcessRequest,
|
|
80
82
|
UnexposePortRequest,
|
|
81
83
|
WriteFileRequest
|
|
82
|
-
} from
|
|
83
|
-
export type {
|
|
84
|
+
} from './clients';
|
|
85
|
+
export type {
|
|
86
|
+
ExecutionCallbacks,
|
|
87
|
+
InterpreterClient
|
|
88
|
+
} from './clients/interpreter-client.js';
|
|
84
89
|
// Export file streaming utilities for binary file support
|
|
85
90
|
export { collectFile, streamFile } from './file-stream';
|
|
86
91
|
// Export interpreter functionality
|
|
87
92
|
export { CodeInterpreter } from './interpreter.js';
|
|
88
93
|
// Re-export request handler utilities
|
|
89
94
|
export {
|
|
90
|
-
proxyToSandbox,
|
|
95
|
+
proxyToSandbox,
|
|
96
|
+
type RouteInfo,
|
|
97
|
+
type SandboxEnv
|
|
91
98
|
} from './request-handler';
|
|
92
99
|
// Export SSE parser for converting ReadableStream to AsyncIterable
|
|
93
|
-
export {
|
|
100
|
+
export {
|
|
101
|
+
asyncIterableToSSEStream,
|
|
102
|
+
parseSSEStream,
|
|
103
|
+
responseToAsyncIterable
|
|
104
|
+
} from './sse-parser';
|
package/src/interpreter.ts
CHANGED
|
@@ -6,11 +6,11 @@ import {
|
|
|
6
6
|
type OutputMessage,
|
|
7
7
|
type Result,
|
|
8
8
|
ResultImpl,
|
|
9
|
-
type RunCodeOptions
|
|
10
|
-
} from
|
|
11
|
-
import type { InterpreterClient } from
|
|
12
|
-
import type { Sandbox } from
|
|
13
|
-
import { validateLanguage } from
|
|
9
|
+
type RunCodeOptions
|
|
10
|
+
} from '@repo/shared';
|
|
11
|
+
import type { InterpreterClient } from './clients/interpreter-client.js';
|
|
12
|
+
import type { Sandbox } from './sandbox.js';
|
|
13
|
+
import { validateLanguage } from './security.js';
|
|
14
14
|
|
|
15
15
|
export class CodeInterpreter {
|
|
16
16
|
private interpreterClient: InterpreterClient;
|
|
@@ -18,7 +18,8 @@ export class CodeInterpreter {
|
|
|
18
18
|
|
|
19
19
|
constructor(sandbox: Sandbox) {
|
|
20
20
|
// In init-testing architecture, client is a SandboxClient with an interpreter property
|
|
21
|
-
this.interpreterClient = (sandbox.client as any)
|
|
21
|
+
this.interpreterClient = (sandbox.client as any)
|
|
22
|
+
.interpreter as InterpreterClient;
|
|
22
23
|
}
|
|
23
24
|
|
|
24
25
|
/**
|
|
@@ -46,7 +47,7 @@ export class CodeInterpreter {
|
|
|
46
47
|
let context = options.context;
|
|
47
48
|
if (!context) {
|
|
48
49
|
// Try to find or create a default context for the language
|
|
49
|
-
const language = options.language ||
|
|
50
|
+
const language = options.language || 'python';
|
|
50
51
|
context = await this.getOrCreateDefaultContext(language);
|
|
51
52
|
}
|
|
52
53
|
|
|
@@ -54,24 +55,29 @@ export class CodeInterpreter {
|
|
|
54
55
|
const execution = new Execution(code, context);
|
|
55
56
|
|
|
56
57
|
// Stream execution
|
|
57
|
-
await this.interpreterClient.runCodeStream(
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
58
|
+
await this.interpreterClient.runCodeStream(
|
|
59
|
+
context.id,
|
|
60
|
+
code,
|
|
61
|
+
options.language,
|
|
62
|
+
{
|
|
63
|
+
onStdout: (output: OutputMessage) => {
|
|
64
|
+
execution.logs.stdout.push(output.text);
|
|
65
|
+
if (options.onStdout) return options.onStdout(output);
|
|
66
|
+
},
|
|
67
|
+
onStderr: (output: OutputMessage) => {
|
|
68
|
+
execution.logs.stderr.push(output.text);
|
|
69
|
+
if (options.onStderr) return options.onStderr(output);
|
|
70
|
+
},
|
|
71
|
+
onResult: async (result: Result) => {
|
|
72
|
+
execution.results.push(new ResultImpl(result) as any);
|
|
73
|
+
if (options.onResult) return options.onResult(result);
|
|
74
|
+
},
|
|
75
|
+
onError: (error: ExecutionError) => {
|
|
76
|
+
execution.error = error;
|
|
77
|
+
if (options.onError) return options.onError(error);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
);
|
|
75
81
|
|
|
76
82
|
return execution;
|
|
77
83
|
}
|
|
@@ -86,36 +92,39 @@ export class CodeInterpreter {
|
|
|
86
92
|
// Get or create context
|
|
87
93
|
let context = options.context;
|
|
88
94
|
if (!context) {
|
|
89
|
-
const language = options.language ||
|
|
95
|
+
const language = options.language || 'python';
|
|
90
96
|
context = await this.getOrCreateDefaultContext(language);
|
|
91
97
|
}
|
|
92
98
|
|
|
93
99
|
// Create streaming response
|
|
94
100
|
// Note: doFetch is protected but we need direct access for raw stream response
|
|
95
|
-
const response = await (this.interpreterClient as any).doFetch(
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
101
|
+
const response = await (this.interpreterClient as any).doFetch(
|
|
102
|
+
'/api/execute/code',
|
|
103
|
+
{
|
|
104
|
+
method: 'POST',
|
|
105
|
+
headers: {
|
|
106
|
+
'Content-Type': 'application/json',
|
|
107
|
+
Accept: 'text/event-stream'
|
|
108
|
+
},
|
|
109
|
+
body: JSON.stringify({
|
|
110
|
+
context_id: context.id,
|
|
111
|
+
code,
|
|
112
|
+
language: options.language
|
|
113
|
+
})
|
|
114
|
+
}
|
|
115
|
+
);
|
|
107
116
|
|
|
108
117
|
if (!response.ok) {
|
|
109
118
|
const errorData = (await response
|
|
110
119
|
.json()
|
|
111
|
-
.catch(() => ({ error:
|
|
120
|
+
.catch(() => ({ error: 'Unknown error' }))) as { error?: string };
|
|
112
121
|
throw new Error(
|
|
113
122
|
errorData.error || `Failed to execute code: ${response.status}`
|
|
114
123
|
);
|
|
115
124
|
}
|
|
116
125
|
|
|
117
126
|
if (!response.body) {
|
|
118
|
-
throw new Error(
|
|
127
|
+
throw new Error('No response body for streaming execution');
|
|
119
128
|
}
|
|
120
129
|
|
|
121
130
|
return response.body;
|
|
@@ -144,7 +153,7 @@ export class CodeInterpreter {
|
|
|
144
153
|
}
|
|
145
154
|
|
|
146
155
|
private async getOrCreateDefaultContext(
|
|
147
|
-
language:
|
|
156
|
+
language: 'python' | 'javascript' | 'typescript'
|
|
148
157
|
): Promise<CodeContext> {
|
|
149
158
|
// Check if we have a cached context for this language
|
|
150
159
|
for (const context of this.contexts.values()) {
|
package/src/request-handler.ts
CHANGED
|
@@ -1,10 +1,7 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import { getSandbox, type Sandbox } from
|
|
4
|
-
import {
|
|
5
|
-
sanitizeSandboxId,
|
|
6
|
-
validatePort
|
|
7
|
-
} from "./security";
|
|
1
|
+
import { switchPort } from '@cloudflare/containers';
|
|
2
|
+
import { createLogger, type LogContext, TraceContext } from '@repo/shared';
|
|
3
|
+
import { getSandbox, type Sandbox } from './sandbox';
|
|
4
|
+
import { sanitizeSandboxId, validatePort } from './security';
|
|
8
5
|
|
|
9
6
|
export interface SandboxEnv {
|
|
10
7
|
Sandbox: DurableObjectNamespace<Sandbox>;
|
|
@@ -22,7 +19,8 @@ export async function proxyToSandbox<E extends SandboxEnv>(
|
|
|
22
19
|
env: E
|
|
23
20
|
): Promise<Response | null> {
|
|
24
21
|
// Create logger context for this request
|
|
25
|
-
const traceId =
|
|
22
|
+
const traceId =
|
|
23
|
+
TraceContext.fromHeaders(request.headers) || TraceContext.generate();
|
|
26
24
|
const logger = createLogger({
|
|
27
25
|
component: 'sandbox-do',
|
|
28
26
|
traceId,
|
|
@@ -98,16 +96,19 @@ export async function proxyToSandbox<E extends SandboxEnv>(
|
|
|
98
96
|
'X-Original-URL': request.url,
|
|
99
97
|
'X-Forwarded-Host': url.hostname,
|
|
100
98
|
'X-Forwarded-Proto': url.protocol.replace(':', ''),
|
|
101
|
-
'X-Sandbox-Name': sandboxId
|
|
99
|
+
'X-Sandbox-Name': sandboxId // Pass the friendly name
|
|
102
100
|
},
|
|
103
101
|
body: request.body,
|
|
104
102
|
// @ts-expect-error - duplex required for body streaming in modern runtimes
|
|
105
|
-
duplex: 'half'
|
|
103
|
+
duplex: 'half'
|
|
106
104
|
});
|
|
107
105
|
|
|
108
106
|
return await sandbox.containerFetch(proxyRequest, port);
|
|
109
107
|
} catch (error) {
|
|
110
|
-
logger.error(
|
|
108
|
+
logger.error(
|
|
109
|
+
'Proxy routing error',
|
|
110
|
+
error instanceof Error ? error : new Error(String(error))
|
|
111
|
+
);
|
|
111
112
|
return new Response('Proxy routing error', { status: 500 });
|
|
112
113
|
}
|
|
113
114
|
}
|
|
@@ -115,7 +116,9 @@ export async function proxyToSandbox<E extends SandboxEnv>(
|
|
|
115
116
|
function extractSandboxRoute(url: URL): RouteInfo | null {
|
|
116
117
|
// Parse subdomain pattern: port-sandboxId-token.domain (tokens mandatory)
|
|
117
118
|
// Token is always exactly 16 chars (generated by generatePortToken)
|
|
118
|
-
const subdomainMatch = url.hostname.match(
|
|
119
|
+
const subdomainMatch = url.hostname.match(
|
|
120
|
+
/^(\d{4,5})-([^.-][^.]*?[^.-]|[^.-])-([a-z0-9_-]{16})\.(.+)$/
|
|
121
|
+
);
|
|
119
122
|
|
|
120
123
|
if (!subdomainMatch) {
|
|
121
124
|
return null;
|
|
@@ -146,8 +149,8 @@ function extractSandboxRoute(url: URL): RouteInfo | null {
|
|
|
146
149
|
return {
|
|
147
150
|
port,
|
|
148
151
|
sandboxId: sanitizedSandboxId,
|
|
149
|
-
path: url.pathname ||
|
|
150
|
-
token
|
|
152
|
+
path: url.pathname || '/',
|
|
153
|
+
token
|
|
151
154
|
};
|
|
152
155
|
}
|
|
153
156
|
|
|
@@ -163,18 +166,18 @@ export function isLocalhostPattern(hostname: string): boolean {
|
|
|
163
166
|
return hostname === '[::1]';
|
|
164
167
|
}
|
|
165
168
|
}
|
|
166
|
-
|
|
169
|
+
|
|
167
170
|
// Handle bare IPv6 without brackets
|
|
168
171
|
if (hostname === '::1') {
|
|
169
172
|
return true;
|
|
170
173
|
}
|
|
171
|
-
|
|
174
|
+
|
|
172
175
|
// For IPv4 and regular hostnames, split on colon to remove port
|
|
173
|
-
const hostPart = hostname.split(
|
|
174
|
-
|
|
176
|
+
const hostPart = hostname.split(':')[0];
|
|
177
|
+
|
|
175
178
|
return (
|
|
176
|
-
hostPart ===
|
|
177
|
-
hostPart ===
|
|
178
|
-
hostPart ===
|
|
179
|
+
hostPart === 'localhost' ||
|
|
180
|
+
hostPart === '127.0.0.1' ||
|
|
181
|
+
hostPart === '0.0.0.0'
|
|
179
182
|
);
|
|
180
183
|
}
|