@adobe/aio-lib-sandbox 0.1.0-alpha.10

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.
@@ -0,0 +1,22 @@
1
+ /*
2
+ Copyright 2026 Adobe. All rights reserved.
3
+ This file is licensed to you under the Apache License, Version 2.0 (the "License");
4
+ you may not use this file except in compliance with the License. You may obtain a copy
5
+ of the License at http://www.apache.org/licenses/LICENSE-2.0
6
+ Unless required by applicable law or agreed to in writing, software distributed under
7
+ the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
8
+ OF ANY KIND, either express or implied. See the License for the specific language
9
+ governing permissions and limitations under the License.
10
+ */
11
+
12
+ const SANDBOX_SIZES = Object.freeze({
13
+ SMALL: { cpu: '500m', memory: '512Mi', gpu: 0 },
14
+ MEDIUM: { cpu: '2000m', memory: '4Gi', gpu: 0 },
15
+ LARGE: { cpu: '4000m', memory: '16Gi', gpu: 0 },
16
+ XLARGE: { cpu: '8000m', memory: '32Gi', gpu: 1 }
17
+ })
18
+
19
+ const PROTOCOL_VERSION = '1'
20
+ const API_PREFIX = `/api/v${PROTOCOL_VERSION}`
21
+
22
+ module.exports = { SANDBOX_SIZES, PROTOCOL_VERSION, API_PREFIX }
package/src/errors.js ADDED
@@ -0,0 +1,47 @@
1
+ /*
2
+ Copyright 2026 Adobe. All rights reserved.
3
+ This file is licensed to you under the Apache License, Version 2.0 (the "License");
4
+ you may not use this file except in compliance with the License. You may obtain a copy
5
+ of the License at http://www.apache.org/licenses/LICENSE-2.0
6
+ Unless required by applicable law or agreed to in writing, software distributed under
7
+ the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
8
+ OF ANY KIND, either express or implied. See the License for the specific language
9
+ governing permissions and limitations under the License.
10
+ */
11
+
12
+ class SandboxSDKError extends Error {
13
+ constructor (message) {
14
+ super(message)
15
+ this.name = this.constructor.name
16
+ if (Error.captureStackTrace) {
17
+ Error.captureStackTrace(this, this.constructor)
18
+ }
19
+ }
20
+ }
21
+
22
+ class SandboxInitializationError extends SandboxSDKError {}
23
+ class SandboxClientError extends SandboxSDKError {}
24
+ class SandboxNotFoundError extends SandboxSDKError {}
25
+ class SandboxUnauthorizedError extends SandboxSDKError {}
26
+ class SandboxTimeoutError extends SandboxSDKError {}
27
+ class SandboxWebSocketError extends SandboxSDKError {}
28
+ class SandboxCommandNotFoundError extends SandboxSDKError {}
29
+ class SandboxPortNotProvisionedError extends SandboxSDKError {}
30
+ class SandboxInvalidPortError extends SandboxClientError {}
31
+ class ProtocolVersionMismatchError extends SandboxClientError {}
32
+ class SandboxMalformedFrameError extends SandboxClientError {}
33
+
34
+ module.exports = {
35
+ SandboxSDKError,
36
+ SandboxInitializationError,
37
+ SandboxClientError,
38
+ SandboxNotFoundError,
39
+ SandboxUnauthorizedError,
40
+ SandboxTimeoutError,
41
+ SandboxWebSocketError,
42
+ SandboxCommandNotFoundError,
43
+ SandboxPortNotProvisionedError,
44
+ SandboxInvalidPortError,
45
+ ProtocolVersionMismatchError,
46
+ SandboxMalformedFrameError
47
+ }
package/src/index.js ADDED
@@ -0,0 +1,44 @@
1
+ /*
2
+ Copyright 2026 Adobe. All rights reserved.
3
+ This file is licensed to you under the Apache License, Version 2.0 (the "License");
4
+ you may not use this file except in compliance with the License. You may obtain a copy
5
+ of the License at http://www.apache.org/licenses/LICENSE-2.0
6
+ Unless required by applicable law or agreed to in writing, software distributed under
7
+ the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
8
+ OF ANY KIND, either express or implied. See the License for the specific language
9
+ governing permissions and limitations under the License.
10
+ */
11
+
12
+ const Sandbox = require('./Sandbox')
13
+ const { PROTOCOL_VERSION } = require('./constants')
14
+ const {
15
+ SandboxSDKError,
16
+ SandboxInitializationError,
17
+ SandboxClientError,
18
+ SandboxNotFoundError,
19
+ SandboxUnauthorizedError,
20
+ SandboxTimeoutError,
21
+ SandboxWebSocketError,
22
+ SandboxCommandNotFoundError,
23
+ SandboxPortNotProvisionedError,
24
+ SandboxInvalidPortError,
25
+ ProtocolVersionMismatchError,
26
+ SandboxMalformedFrameError
27
+ } = require('./errors')
28
+
29
+ module.exports = {
30
+ Sandbox,
31
+ SANDBOX_PROTOCOL_VERSION: PROTOCOL_VERSION,
32
+ SandboxSDKError,
33
+ SandboxInitializationError,
34
+ SandboxClientError,
35
+ SandboxNotFoundError,
36
+ SandboxUnauthorizedError,
37
+ SandboxTimeoutError,
38
+ SandboxWebSocketError,
39
+ SandboxCommandNotFoundError,
40
+ SandboxPortNotProvisionedError,
41
+ SandboxInvalidPortError,
42
+ ProtocolVersionMismatchError,
43
+ SandboxMalformedFrameError
44
+ }
package/src/utils.js ADDED
@@ -0,0 +1,170 @@
1
+ /*
2
+ Copyright 2026 Adobe. All rights reserved.
3
+ This file is licensed to you under the Apache License, Version 2.0 (the "License");
4
+ you may not use this file except in compliance with the License. You may obtain a copy
5
+ of the License at http://www.apache.org/licenses/LICENSE-2.0
6
+ Unless required by applicable law or agreed to in writing, software distributed under
7
+ the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
8
+ OF ANY KIND, either express or implied. See the License for the specific language
9
+ governing permissions and limitations under the License.
10
+ */
11
+
12
+ const {
13
+ SandboxClientError,
14
+ SandboxInitializationError,
15
+ SandboxNotFoundError,
16
+ SandboxUnauthorizedError,
17
+ SandboxTimeoutError
18
+ } = require('./errors')
19
+ const { SANDBOX_SIZES, API_PREFIX } = require('./constants')
20
+
21
+ /**
22
+ * Builds a Basic authorization header from a Runtime API key.
23
+ *
24
+ * @param {string} apiKey Runtime API key
25
+ * @returns {string} Basic authorization header value
26
+ */
27
+ function buildAuthorizationHeader (apiKey) {
28
+ return `Basic ${Buffer.from(apiKey).toString('base64')}`
29
+ }
30
+
31
+ /**
32
+ * Maps sandbox management API status codes to SDK error classes.
33
+ *
34
+ * @param {number} status HTTP response status
35
+ * @param {string} message error message
36
+ * @returns {SandboxClientError} SDK error matching the response status
37
+ */
38
+ function createSandboxHttpError (status, message) {
39
+ if (status === 401 || status === 403) {
40
+ return new SandboxUnauthorizedError(message)
41
+ }
42
+ if (status === 404) {
43
+ return new SandboxNotFoundError(message)
44
+ }
45
+ if (status === 504) {
46
+ return new SandboxTimeoutError(message)
47
+ }
48
+ return new SandboxClientError(message)
49
+ }
50
+
51
+ /**
52
+ * Ensures the Runtime API host has a URL scheme.
53
+ *
54
+ * @param {string} host Runtime API host
55
+ * @returns {string} API host with a URL scheme
56
+ */
57
+ function normalizeApiHost (host) {
58
+ if (!host.match(/^https?:\/\//)) {
59
+ return `https://${host}`
60
+ }
61
+ return host
62
+ }
63
+
64
+ /**
65
+ * Builds the sandbox execution WebSocket endpoint from Runtime API details.
66
+ *
67
+ * @param {string} apiHost Runtime API host
68
+ * @param {string} namespace Runtime namespace
69
+ * @param {string} sandboxId sandbox id
70
+ * @returns {string} sandbox WebSocket endpoint
71
+ */
72
+ function buildWebSocketEndpoint (apiHost, namespace, sandboxId) {
73
+ const url = new URL(apiHost)
74
+ url.protocol = url.protocol === 'http:' ? 'ws:' : 'wss:'
75
+ url.pathname = `${API_PREFIX}/namespaces/${namespace}/sandboxes/${sandboxId}/exec`
76
+ url.search = ''
77
+ return url.toString()
78
+ }
79
+
80
+ /**
81
+ * Reads Runtime credentials from environment variables, merged with any
82
+ * explicit overrides. Throws `SandboxInitializationError` for missing values.
83
+ *
84
+ * @param {object} overrides explicit credential overrides
85
+ * @returns {{ apiHost: string, namespace: string, apiKey: string }} resolved Runtime credentials
86
+ */
87
+ function resolveCredentials (overrides = {}) {
88
+ const apiHost = overrides.apiHost || process.env.__OW_API_HOST
89
+ const namespace = overrides.namespace || process.env.__OW_NAMESPACE
90
+ const apiKey = overrides.auth || process.env.__OW_API_KEY
91
+
92
+ const missing = []
93
+ if (!apiHost) missing.push('apiHost')
94
+ if (!namespace) missing.push('namespace')
95
+ if (!apiKey) missing.push('auth')
96
+
97
+ if (missing.length > 0) {
98
+ throw new SandboxInitializationError(
99
+ `Missing required credentials: ${missing.join(', ')}. ` +
100
+ 'Pass them explicitly or set __OW_API_HOST, __OW_NAMESPACE, __OW_API_KEY in the environment.'
101
+ )
102
+ }
103
+
104
+ return { apiHost: normalizeApiHost(apiHost), namespace, apiKey }
105
+ }
106
+
107
+ /**
108
+ * @param {string|object|undefined} size size name or spec object
109
+ * @returns {string} normalised size name
110
+ */
111
+ function normalizeSize (size) {
112
+ if (!size) return 'MEDIUM'
113
+
114
+ if (typeof size === 'string' && SANDBOX_SIZES[size]) return size
115
+
116
+ if (typeof size === 'object') {
117
+ const entry = Object.entries(SANDBOX_SIZES).find(
118
+ ([, v]) => v.cpu === size.cpu && v.memory === size.memory && v.gpu === size.gpu
119
+ )
120
+ if (entry) return entry[0]
121
+ }
122
+
123
+ throw new SandboxClientError('Invalid sandbox size provided')
124
+ }
125
+
126
+ /**
127
+ * Thin fetch wrapper around the management REST API.
128
+ *
129
+ * Uses the Node.js global `fetch` (available since Node 18).
130
+ *
131
+ * @param {string} method HTTP method
132
+ * @param {string} url full request URL
133
+ * @param {string} apiKey API key for Basic auth
134
+ * @param {object|undefined} body request body (JSON-encoded when present)
135
+ * @returns {Promise<object>} parsed response JSON
136
+ */
137
+ async function apiRequest (method, url, apiKey, body) {
138
+ const headers = { Authorization: buildAuthorizationHeader(apiKey) }
139
+ const init = { method, headers }
140
+
141
+ if (body !== undefined) {
142
+ headers['Content-Type'] = 'application/json'
143
+ init.body = JSON.stringify(body)
144
+ }
145
+
146
+ let response
147
+ try {
148
+ response = await fetch(url, init)
149
+ } catch (error) {
150
+ throw new SandboxClientError(`Sandbox API request failed: ${error.message}`)
151
+ }
152
+
153
+ if (!response.ok) {
154
+ const text = await response.text()
155
+ const detail = `${response.status}${text ? ` ${text}` : ''}`
156
+ throw createSandboxHttpError(response.status, detail)
157
+ }
158
+
159
+ return response.json()
160
+ }
161
+
162
+ module.exports = {
163
+ buildAuthorizationHeader,
164
+ createSandboxHttpError,
165
+ normalizeApiHost,
166
+ buildWebSocketEndpoint,
167
+ resolveCredentials,
168
+ normalizeSize,
169
+ apiRequest
170
+ }