@computesdk/gateway 0.0.2
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/LICENSE +21 -0
- package/README.md +77 -0
- package/dist/index.d.mts +36 -0
- package/dist/index.d.ts +36 -0
- package/dist/index.js +213 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +186 -0
- package/dist/index.mjs.map +1 -0
- package/package.json +59 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 computesdk
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
# @computesdk/gateway
|
|
2
|
+
|
|
3
|
+
Infrastructure provider implementations for the ComputeSDK gateway server.
|
|
4
|
+
|
|
5
|
+
## Purpose
|
|
6
|
+
|
|
7
|
+
This package contains infrastructure-only provider implementations used by the ComputeSDK gateway server to provision compute resources.
|
|
8
|
+
|
|
9
|
+
**⚠️ This package is intended for internal use by the gateway server.** End users should import from provider-specific packages like `@computesdk/railway` instead.
|
|
10
|
+
|
|
11
|
+
## What's in here?
|
|
12
|
+
|
|
13
|
+
- **Railway** - GraphQL API for creating/destroying Railway services
|
|
14
|
+
|
|
15
|
+
Additional providers (Vercel, Render, Namespace, etc.) will be added as needed.
|
|
16
|
+
|
|
17
|
+
Each provider exposes only infrastructure methods:
|
|
18
|
+
- `create()` - Provision compute resource
|
|
19
|
+
- `destroy()` - Tear down resource
|
|
20
|
+
- `getById()` - Get resource by ID
|
|
21
|
+
- `list()` - List resources
|
|
22
|
+
|
|
23
|
+
These methods do NOT include sandbox operations like `runCode()` or `filesystem` access. The gateway server adds those capabilities by installing the ComputeSDK daemon.
|
|
24
|
+
|
|
25
|
+
## Usage (Gateway Server Only)
|
|
26
|
+
|
|
27
|
+
```typescript
|
|
28
|
+
import { railway } from '@computesdk/gateway';
|
|
29
|
+
|
|
30
|
+
const provider = railway({
|
|
31
|
+
apiKey: 'railway_xxx',
|
|
32
|
+
projectId: 'project_xxx',
|
|
33
|
+
environmentId: 'env_xxx'
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
// Create infrastructure with daemon pre-installed
|
|
37
|
+
const instance = await provider.create({
|
|
38
|
+
daemonConfig: {
|
|
39
|
+
accessToken: 'token_xxx',
|
|
40
|
+
gatewayUrl: 'https://gateway.computesdk.com'
|
|
41
|
+
}
|
|
42
|
+
});
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
## For End Users
|
|
46
|
+
|
|
47
|
+
If you're an end user looking to use Railway (or other infrastructure providers) as a sandbox, import from the provider-specific package instead:
|
|
48
|
+
|
|
49
|
+
```typescript
|
|
50
|
+
import { railway } from '@computesdk/railway';
|
|
51
|
+
|
|
52
|
+
const compute = railway({
|
|
53
|
+
apiKey: 'railway_xxx',
|
|
54
|
+
projectId: 'project_xxx',
|
|
55
|
+
environmentId: 'env_xxx'
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
// Full sandbox API available (routes through gateway)
|
|
59
|
+
const sandbox = await compute.sandbox.create();
|
|
60
|
+
await sandbox.runCode('console.log("hello")');
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
## Architecture
|
|
64
|
+
|
|
65
|
+
```
|
|
66
|
+
User App Gateway Server Infrastructure
|
|
67
|
+
┌─────────────┐ ┌──────────────┐ ┌──────────────┐
|
|
68
|
+
│ @computesdk/│ │ @computesdk/ │ │ Railway │
|
|
69
|
+
│ railway │───────────>│ gateway │───────────>│ GraphQL │
|
|
70
|
+
│ │ HTTP API │ │ Direct │ API │
|
|
71
|
+
│ (wrapper) │ │ (infra impl) │ │ │
|
|
72
|
+
└─────────────┘ └──────────────┘ └──────────────┘
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
## License
|
|
76
|
+
|
|
77
|
+
MIT
|
package/dist/index.d.mts
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import * as _computesdk_provider from '@computesdk/provider';
|
|
2
|
+
export { DaemonConfig, InfraProvider } from '@computesdk/provider';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Railway Infrastructure Provider
|
|
6
|
+
*
|
|
7
|
+
* Provides infrastructure-only methods for creating/destroying Railway services.
|
|
8
|
+
* Used by the gateway server to provision compute resources with daemon pre-installed.
|
|
9
|
+
*/
|
|
10
|
+
/**
|
|
11
|
+
* Railway service instance
|
|
12
|
+
*/
|
|
13
|
+
interface RailwayInstance {
|
|
14
|
+
serviceId: string;
|
|
15
|
+
projectId: string;
|
|
16
|
+
environmentId: string;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Railway provider configuration
|
|
20
|
+
*/
|
|
21
|
+
interface RailwayConfig {
|
|
22
|
+
/** Railway API key - if not provided, will fallback to RAILWAY_API_KEY environment variable */
|
|
23
|
+
apiKey?: string;
|
|
24
|
+
/** Railway Project ID */
|
|
25
|
+
projectId?: string;
|
|
26
|
+
/** Railway Environment ID - if not provided, will fallback to RAILWAY_ENVIRONMENT_ID environment variable */
|
|
27
|
+
environmentId?: string;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Railway infrastructure provider
|
|
31
|
+
*
|
|
32
|
+
* Creates Railway services with ComputeSDK daemon pre-installed via Docker image.
|
|
33
|
+
*/
|
|
34
|
+
declare const railway: (providerConfig: RailwayConfig) => _computesdk_provider.InfraProvider<RailwayInstance>;
|
|
35
|
+
|
|
36
|
+
export { type RailwayConfig, type RailwayInstance, railway };
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import * as _computesdk_provider from '@computesdk/provider';
|
|
2
|
+
export { DaemonConfig, InfraProvider } from '@computesdk/provider';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Railway Infrastructure Provider
|
|
6
|
+
*
|
|
7
|
+
* Provides infrastructure-only methods for creating/destroying Railway services.
|
|
8
|
+
* Used by the gateway server to provision compute resources with daemon pre-installed.
|
|
9
|
+
*/
|
|
10
|
+
/**
|
|
11
|
+
* Railway service instance
|
|
12
|
+
*/
|
|
13
|
+
interface RailwayInstance {
|
|
14
|
+
serviceId: string;
|
|
15
|
+
projectId: string;
|
|
16
|
+
environmentId: string;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Railway provider configuration
|
|
20
|
+
*/
|
|
21
|
+
interface RailwayConfig {
|
|
22
|
+
/** Railway API key - if not provided, will fallback to RAILWAY_API_KEY environment variable */
|
|
23
|
+
apiKey?: string;
|
|
24
|
+
/** Railway Project ID */
|
|
25
|
+
projectId?: string;
|
|
26
|
+
/** Railway Environment ID - if not provided, will fallback to RAILWAY_ENVIRONMENT_ID environment variable */
|
|
27
|
+
environmentId?: string;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Railway infrastructure provider
|
|
31
|
+
*
|
|
32
|
+
* Creates Railway services with ComputeSDK daemon pre-installed via Docker image.
|
|
33
|
+
*/
|
|
34
|
+
declare const railway: (providerConfig: RailwayConfig) => _computesdk_provider.InfraProvider<RailwayInstance>;
|
|
35
|
+
|
|
36
|
+
export { type RailwayConfig, type RailwayInstance, railway };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,213 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
|
|
20
|
+
// src/index.ts
|
|
21
|
+
var index_exports = {};
|
|
22
|
+
__export(index_exports, {
|
|
23
|
+
railway: () => railway
|
|
24
|
+
});
|
|
25
|
+
module.exports = __toCommonJS(index_exports);
|
|
26
|
+
|
|
27
|
+
// src/railway.ts
|
|
28
|
+
var import_provider = require("@computesdk/provider");
|
|
29
|
+
var getAndValidateCredentials = (config) => {
|
|
30
|
+
const apiKey = config.apiKey || typeof process !== "undefined" && process.env?.RAILWAY_API_KEY || "";
|
|
31
|
+
const projectId = config.projectId || typeof process !== "undefined" && process.env?.RAILWAY_PROJECT_ID || "";
|
|
32
|
+
const environmentId = config.environmentId || typeof process !== "undefined" && process.env?.RAILWAY_ENVIRONMENT_ID || "";
|
|
33
|
+
if (!apiKey) {
|
|
34
|
+
throw new Error(
|
|
35
|
+
"Missing Railway API key. Provide apiKey in config or set RAILWAY_API_KEY environment variable."
|
|
36
|
+
);
|
|
37
|
+
}
|
|
38
|
+
if (!projectId) {
|
|
39
|
+
throw new Error(
|
|
40
|
+
"Missing Railway Project ID. Provide projectId in config or set RAILWAY_PROJECT_ID environment variable."
|
|
41
|
+
);
|
|
42
|
+
}
|
|
43
|
+
if (!environmentId) {
|
|
44
|
+
throw new Error(
|
|
45
|
+
"Missing Railway Environment ID. Provide environmentId in config or set RAILWAY_ENVIRONMENT_ID environment variable."
|
|
46
|
+
);
|
|
47
|
+
}
|
|
48
|
+
return { apiKey, projectId, environmentId };
|
|
49
|
+
};
|
|
50
|
+
var GRAPHQL_QUERIES = {
|
|
51
|
+
CREATE_SERVICE: `mutation ServiceCreate($input: ServiceCreateInput!) { serviceCreate(input: $input) { id name } }`,
|
|
52
|
+
GET_SERVICE: `query Service($serviceId: String!) { service(id: $serviceId) { id name createdAt } }`,
|
|
53
|
+
LIST_SERVICES: `query Project($projectId: String!) { project(id: $projectId) { services { edges { node { id name createdAt updatedAt } } } } }`,
|
|
54
|
+
DELETE_SERVICE: `mutation ServiceDelete($id: String!) { serviceDelete(id: $id) }`
|
|
55
|
+
};
|
|
56
|
+
var handleGraphQLErrors = (data) => {
|
|
57
|
+
if (data.errors) {
|
|
58
|
+
throw new Error(`Railway GraphQL error: ${data.errors.map((e) => e.message).join(", ")}`);
|
|
59
|
+
}
|
|
60
|
+
};
|
|
61
|
+
var fetchRailway = async (apiKey, mutation) => {
|
|
62
|
+
const response = await fetch("https://backboard.railway.com/graphql/v2", {
|
|
63
|
+
method: "POST",
|
|
64
|
+
headers: {
|
|
65
|
+
"Content-Type": "application/json",
|
|
66
|
+
"Authorization": `Bearer ${apiKey}`
|
|
67
|
+
},
|
|
68
|
+
body: JSON.stringify(mutation)
|
|
69
|
+
});
|
|
70
|
+
if (!response.ok) {
|
|
71
|
+
throw new Error(`Railway API error: ${response.status} ${response.statusText}`);
|
|
72
|
+
}
|
|
73
|
+
const data = await response.json();
|
|
74
|
+
handleGraphQLErrors(data);
|
|
75
|
+
return data.data;
|
|
76
|
+
};
|
|
77
|
+
function buildDaemonEnvVars(daemonConfig) {
|
|
78
|
+
if (!daemonConfig) {
|
|
79
|
+
return {};
|
|
80
|
+
}
|
|
81
|
+
return {
|
|
82
|
+
COMPUTESDK_ACCESS_TOKEN: daemonConfig.accessToken,
|
|
83
|
+
...daemonConfig.gatewayUrl && { COMPUTESDK_GATEWAY_URL: daemonConfig.gatewayUrl },
|
|
84
|
+
...daemonConfig.env
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
var railway = (0, import_provider.defineInfraProvider)({
|
|
88
|
+
name: "railway",
|
|
89
|
+
methods: {
|
|
90
|
+
create: async (config, options) => {
|
|
91
|
+
const { apiKey, projectId, environmentId } = getAndValidateCredentials(config);
|
|
92
|
+
try {
|
|
93
|
+
const envVars = buildDaemonEnvVars(options?.daemonConfig);
|
|
94
|
+
const mutation = {
|
|
95
|
+
query: GRAPHQL_QUERIES.CREATE_SERVICE,
|
|
96
|
+
variables: {
|
|
97
|
+
input: {
|
|
98
|
+
projectId,
|
|
99
|
+
environmentId,
|
|
100
|
+
source: {
|
|
101
|
+
image: options?.image ?? "computesdk/compute:latest"
|
|
102
|
+
},
|
|
103
|
+
...Object.keys(envVars).length > 0 && {
|
|
104
|
+
variables: envVars
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
};
|
|
109
|
+
const responseData = await fetchRailway(apiKey, mutation);
|
|
110
|
+
const service = responseData?.serviceCreate;
|
|
111
|
+
if (!service) {
|
|
112
|
+
throw new Error("No service returned from Railway API - responseData.serviceCreate is undefined");
|
|
113
|
+
}
|
|
114
|
+
if (!service.id) {
|
|
115
|
+
throw new Error(`Service ID is undefined. Full service object: ${JSON.stringify(service, null, 2)}`);
|
|
116
|
+
}
|
|
117
|
+
const instance = {
|
|
118
|
+
serviceId: service.id,
|
|
119
|
+
projectId,
|
|
120
|
+
environmentId
|
|
121
|
+
};
|
|
122
|
+
return {
|
|
123
|
+
instance,
|
|
124
|
+
instanceId: service.id
|
|
125
|
+
};
|
|
126
|
+
} catch (error) {
|
|
127
|
+
throw new Error(
|
|
128
|
+
`Failed to create Railway instance: ${error instanceof Error ? error.message : String(error)}`
|
|
129
|
+
);
|
|
130
|
+
}
|
|
131
|
+
},
|
|
132
|
+
getById: async (config, instanceId) => {
|
|
133
|
+
const { apiKey, projectId, environmentId } = getAndValidateCredentials(config);
|
|
134
|
+
try {
|
|
135
|
+
const mutation = {
|
|
136
|
+
query: GRAPHQL_QUERIES.GET_SERVICE,
|
|
137
|
+
variables: {
|
|
138
|
+
serviceId: instanceId
|
|
139
|
+
}
|
|
140
|
+
};
|
|
141
|
+
const responseData = await fetchRailway(apiKey, mutation);
|
|
142
|
+
if (responseData === null) {
|
|
143
|
+
return null;
|
|
144
|
+
}
|
|
145
|
+
const service = responseData?.service;
|
|
146
|
+
if (!service) {
|
|
147
|
+
throw new Error("Service data is missing from Railway response");
|
|
148
|
+
}
|
|
149
|
+
const instance = {
|
|
150
|
+
serviceId: service.id,
|
|
151
|
+
projectId,
|
|
152
|
+
environmentId
|
|
153
|
+
};
|
|
154
|
+
return {
|
|
155
|
+
instance,
|
|
156
|
+
instanceId: service.id
|
|
157
|
+
};
|
|
158
|
+
} catch (error) {
|
|
159
|
+
throw new Error(
|
|
160
|
+
`Failed to get Railway instance: ${error instanceof Error ? error.message : String(error)}`
|
|
161
|
+
);
|
|
162
|
+
}
|
|
163
|
+
},
|
|
164
|
+
list: async (config) => {
|
|
165
|
+
const { apiKey, projectId, environmentId } = getAndValidateCredentials(config);
|
|
166
|
+
try {
|
|
167
|
+
const mutation = {
|
|
168
|
+
query: GRAPHQL_QUERIES.LIST_SERVICES,
|
|
169
|
+
variables: {
|
|
170
|
+
projectId
|
|
171
|
+
}
|
|
172
|
+
};
|
|
173
|
+
const responseData = await fetchRailway(apiKey, mutation);
|
|
174
|
+
const services = responseData?.project?.services?.edges || [];
|
|
175
|
+
return services.map((edge) => {
|
|
176
|
+
const service = edge.node;
|
|
177
|
+
const instance = {
|
|
178
|
+
serviceId: service.id,
|
|
179
|
+
projectId,
|
|
180
|
+
environmentId
|
|
181
|
+
};
|
|
182
|
+
return {
|
|
183
|
+
instance,
|
|
184
|
+
instanceId: service.id
|
|
185
|
+
};
|
|
186
|
+
});
|
|
187
|
+
} catch (error) {
|
|
188
|
+
throw new Error(
|
|
189
|
+
`Failed to list Railway instances: ${error instanceof Error ? error.message : String(error)}`
|
|
190
|
+
);
|
|
191
|
+
}
|
|
192
|
+
},
|
|
193
|
+
destroy: async (config, instanceId) => {
|
|
194
|
+
const { apiKey } = getAndValidateCredentials(config);
|
|
195
|
+
try {
|
|
196
|
+
const mutation = {
|
|
197
|
+
query: GRAPHQL_QUERIES.DELETE_SERVICE,
|
|
198
|
+
variables: {
|
|
199
|
+
id: instanceId
|
|
200
|
+
}
|
|
201
|
+
};
|
|
202
|
+
await fetchRailway(apiKey, mutation);
|
|
203
|
+
} catch (error) {
|
|
204
|
+
console.warn(`Railway destroy warning: ${error instanceof Error ? error.message : String(error)}`);
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
});
|
|
209
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
210
|
+
0 && (module.exports = {
|
|
211
|
+
railway
|
|
212
|
+
});
|
|
213
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/railway.ts"],"sourcesContent":["/**\n * @computesdk/gateway - Infrastructure Provider Implementations\n * \n * Infrastructure-only providers for the ComputeSDK gateway server.\n * These providers handle resource provisioning but don't include native sandbox capabilities.\n * The gateway server installs the ComputeSDK daemon to add sandbox features.\n */\n\n// Export Railway infrastructure provider\nexport { railway } from './railway.js';\nexport type { RailwayConfig, RailwayInstance } from './railway.js';\n\n// Re-export infrastructure provider types from @computesdk/provider\nexport type { InfraProvider, DaemonConfig } from '@computesdk/provider';\n","/**\n * Railway Infrastructure Provider\n * \n * Provides infrastructure-only methods for creating/destroying Railway services.\n * Used by the gateway server to provision compute resources with daemon pre-installed.\n */\n\nimport { defineInfraProvider } from '@computesdk/provider';\nimport type { DaemonConfig, CreateSandboxOptions } from '@computesdk/provider';\n\n/**\n * Railway service instance\n */\nexport interface RailwayInstance {\n serviceId: string;\n projectId: string;\n environmentId: string;\n}\n\n/**\n * Railway provider configuration\n */\nexport interface RailwayConfig {\n /** Railway API key - if not provided, will fallback to RAILWAY_API_KEY environment variable */\n apiKey?: string;\n /** Railway Project ID */\n projectId?: string;\n /** Railway Environment ID - if not provided, will fallback to RAILWAY_ENVIRONMENT_ID environment variable */\n environmentId?: string;\n}\n\n/**\n * Get and validate Railway credentials from config and environment\n */\nexport const getAndValidateCredentials = (config: RailwayConfig) => {\n const apiKey = config.apiKey || (typeof process !== 'undefined' && process.env?.RAILWAY_API_KEY) || '';\n const projectId = config.projectId || (typeof process !== 'undefined' && process.env?.RAILWAY_PROJECT_ID) || '';\n const environmentId = config.environmentId || (typeof process !== 'undefined' && process.env?.RAILWAY_ENVIRONMENT_ID) || '';\n\n if (!apiKey) {\n throw new Error(\n 'Missing Railway API key. Provide apiKey in config or set RAILWAY_API_KEY environment variable.'\n );\n }\n\n if (!projectId) {\n throw new Error(\n 'Missing Railway Project ID. Provide projectId in config or set RAILWAY_PROJECT_ID environment variable.'\n );\n }\n\n if (!environmentId) {\n throw new Error(\n 'Missing Railway Environment ID. Provide environmentId in config or set RAILWAY_ENVIRONMENT_ID environment variable.'\n );\n }\n\n return { apiKey, projectId, environmentId };\n};\n\nconst GRAPHQL_QUERIES = {\n CREATE_SERVICE: `mutation ServiceCreate($input: ServiceCreateInput!) { serviceCreate(input: $input) { id name } }`,\n GET_SERVICE: `query Service($serviceId: String!) { service(id: $serviceId) { id name createdAt } }`,\n LIST_SERVICES: `query Project($projectId: String!) { project(id: $projectId) { services { edges { node { id name createdAt updatedAt } } } } }`,\n DELETE_SERVICE: `mutation ServiceDelete($id: String!) { serviceDelete(id: $id) }`\n};\n\nconst handleGraphQLErrors = (data: any) => {\n if (data.errors) {\n throw new Error(`Railway GraphQL error: ${data.errors.map((e: any) => e.message).join(', ')}`);\n }\n};\n\nexport const fetchRailway = async (\n apiKey: string, \n mutation: any,\n) => {\n const response = await fetch('https://backboard.railway.com/graphql/v2', {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n 'Authorization': `Bearer ${apiKey}`\n },\n body: JSON.stringify(mutation)\n });\n\n if (!response.ok) {\n throw new Error(`Railway API error: ${response.status} ${response.statusText}`);\n }\n\n const data = await response.json();\n handleGraphQLErrors(data);\n\n return data.data;\n};\n\n/**\n * Convert daemon config to Railway environment variables\n */\nfunction buildDaemonEnvVars(daemonConfig?: DaemonConfig): Record<string, string> {\n if (!daemonConfig) {\n return {};\n }\n\n return {\n COMPUTESDK_ACCESS_TOKEN: daemonConfig.accessToken,\n ...(daemonConfig.gatewayUrl && { COMPUTESDK_GATEWAY_URL: daemonConfig.gatewayUrl }),\n ...daemonConfig.env,\n };\n}\n\n/**\n * Railway infrastructure provider\n * \n * Creates Railway services with ComputeSDK daemon pre-installed via Docker image.\n */\nexport const railway = defineInfraProvider<RailwayInstance, RailwayConfig>({\n name: 'railway',\n \n methods: {\n create: async (config: RailwayConfig, options?: CreateSandboxOptions & { daemonConfig?: DaemonConfig }) => {\n const { apiKey, projectId, environmentId } = getAndValidateCredentials(config);\n\n try {\n // Build environment variables for daemon\n const envVars = buildDaemonEnvVars(options?.daemonConfig);\n\n const mutation = {\n query: GRAPHQL_QUERIES.CREATE_SERVICE,\n variables: {\n input: {\n projectId,\n environmentId,\n source: {\n image: options?.image ?? 'computesdk/compute:latest'\n },\n ...(Object.keys(envVars).length > 0 && {\n variables: envVars\n })\n }\n }\n };\n\n const responseData = await fetchRailway(apiKey, mutation);\n const service = responseData?.serviceCreate;\n \n if (!service) {\n throw new Error('No service returned from Railway API - responseData.serviceCreate is undefined');\n }\n \n if (!service.id) {\n throw new Error(`Service ID is undefined. Full service object: ${JSON.stringify(service, null, 2)}`);\n }\n\n const instance: RailwayInstance = {\n serviceId: service.id,\n projectId,\n environmentId,\n };\n\n return {\n instance,\n instanceId: service.id\n };\n } catch (error) {\n throw new Error(\n `Failed to create Railway instance: ${error instanceof Error ? error.message : String(error)}`\n );\n }\n },\n\n getById: async (config: RailwayConfig, instanceId: string) => {\n const { apiKey, projectId, environmentId } = getAndValidateCredentials(config);\n\n try {\n const mutation = {\n query: GRAPHQL_QUERIES.GET_SERVICE,\n variables: {\n serviceId: instanceId\n }\n };\n\n const responseData = await fetchRailway(apiKey, mutation);\n \n if (responseData === null) {\n return null;\n }\n \n const service = responseData?.service;\n \n if (!service) {\n throw new Error('Service data is missing from Railway response');\n }\n\n const instance: RailwayInstance = {\n serviceId: service.id,\n projectId,\n environmentId,\n };\n\n return {\n instance,\n instanceId: service.id\n };\n } catch (error) {\n throw new Error(\n `Failed to get Railway instance: ${error instanceof Error ? error.message : String(error)}`\n );\n }\n },\n \n list: async (config: RailwayConfig) => {\n const { apiKey, projectId, environmentId } = getAndValidateCredentials(config);\n\n try {\n const mutation = {\n query: GRAPHQL_QUERIES.LIST_SERVICES,\n variables: {\n projectId\n }\n };\n\n const responseData = await fetchRailway(apiKey, mutation);\n const services = responseData?.project?.services?.edges || [];\n \n return services.map((edge: any) => {\n const service = edge.node;\n const instance: RailwayInstance = {\n serviceId: service.id,\n projectId,\n environmentId,\n };\n\n return {\n instance,\n instanceId: service.id\n };\n });\n } catch (error) {\n throw new Error(\n `Failed to list Railway instances: ${error instanceof Error ? error.message : String(error)}`\n );\n }\n },\n\n destroy: async (config: RailwayConfig, instanceId: string) => {\n const { apiKey } = getAndValidateCredentials(config);\n\n try {\n const mutation = {\n query: GRAPHQL_QUERIES.DELETE_SERVICE,\n variables: {\n id: instanceId\n }\n };\n\n await fetchRailway(apiKey, mutation);\n } catch (error) {\n // For destroy operations, we log warnings rather than throwing\n // since the resource may already be gone\n console.warn(`Railway destroy warning: ${error instanceof Error ? error.message : String(error)}`);\n }\n },\n },\n});\n"],"mappings":"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","names":[]}
|
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
// src/railway.ts
|
|
2
|
+
import { defineInfraProvider } from "@computesdk/provider";
|
|
3
|
+
var getAndValidateCredentials = (config) => {
|
|
4
|
+
const apiKey = config.apiKey || typeof process !== "undefined" && process.env?.RAILWAY_API_KEY || "";
|
|
5
|
+
const projectId = config.projectId || typeof process !== "undefined" && process.env?.RAILWAY_PROJECT_ID || "";
|
|
6
|
+
const environmentId = config.environmentId || typeof process !== "undefined" && process.env?.RAILWAY_ENVIRONMENT_ID || "";
|
|
7
|
+
if (!apiKey) {
|
|
8
|
+
throw new Error(
|
|
9
|
+
"Missing Railway API key. Provide apiKey in config or set RAILWAY_API_KEY environment variable."
|
|
10
|
+
);
|
|
11
|
+
}
|
|
12
|
+
if (!projectId) {
|
|
13
|
+
throw new Error(
|
|
14
|
+
"Missing Railway Project ID. Provide projectId in config or set RAILWAY_PROJECT_ID environment variable."
|
|
15
|
+
);
|
|
16
|
+
}
|
|
17
|
+
if (!environmentId) {
|
|
18
|
+
throw new Error(
|
|
19
|
+
"Missing Railway Environment ID. Provide environmentId in config or set RAILWAY_ENVIRONMENT_ID environment variable."
|
|
20
|
+
);
|
|
21
|
+
}
|
|
22
|
+
return { apiKey, projectId, environmentId };
|
|
23
|
+
};
|
|
24
|
+
var GRAPHQL_QUERIES = {
|
|
25
|
+
CREATE_SERVICE: `mutation ServiceCreate($input: ServiceCreateInput!) { serviceCreate(input: $input) { id name } }`,
|
|
26
|
+
GET_SERVICE: `query Service($serviceId: String!) { service(id: $serviceId) { id name createdAt } }`,
|
|
27
|
+
LIST_SERVICES: `query Project($projectId: String!) { project(id: $projectId) { services { edges { node { id name createdAt updatedAt } } } } }`,
|
|
28
|
+
DELETE_SERVICE: `mutation ServiceDelete($id: String!) { serviceDelete(id: $id) }`
|
|
29
|
+
};
|
|
30
|
+
var handleGraphQLErrors = (data) => {
|
|
31
|
+
if (data.errors) {
|
|
32
|
+
throw new Error(`Railway GraphQL error: ${data.errors.map((e) => e.message).join(", ")}`);
|
|
33
|
+
}
|
|
34
|
+
};
|
|
35
|
+
var fetchRailway = async (apiKey, mutation) => {
|
|
36
|
+
const response = await fetch("https://backboard.railway.com/graphql/v2", {
|
|
37
|
+
method: "POST",
|
|
38
|
+
headers: {
|
|
39
|
+
"Content-Type": "application/json",
|
|
40
|
+
"Authorization": `Bearer ${apiKey}`
|
|
41
|
+
},
|
|
42
|
+
body: JSON.stringify(mutation)
|
|
43
|
+
});
|
|
44
|
+
if (!response.ok) {
|
|
45
|
+
throw new Error(`Railway API error: ${response.status} ${response.statusText}`);
|
|
46
|
+
}
|
|
47
|
+
const data = await response.json();
|
|
48
|
+
handleGraphQLErrors(data);
|
|
49
|
+
return data.data;
|
|
50
|
+
};
|
|
51
|
+
function buildDaemonEnvVars(daemonConfig) {
|
|
52
|
+
if (!daemonConfig) {
|
|
53
|
+
return {};
|
|
54
|
+
}
|
|
55
|
+
return {
|
|
56
|
+
COMPUTESDK_ACCESS_TOKEN: daemonConfig.accessToken,
|
|
57
|
+
...daemonConfig.gatewayUrl && { COMPUTESDK_GATEWAY_URL: daemonConfig.gatewayUrl },
|
|
58
|
+
...daemonConfig.env
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
var railway = defineInfraProvider({
|
|
62
|
+
name: "railway",
|
|
63
|
+
methods: {
|
|
64
|
+
create: async (config, options) => {
|
|
65
|
+
const { apiKey, projectId, environmentId } = getAndValidateCredentials(config);
|
|
66
|
+
try {
|
|
67
|
+
const envVars = buildDaemonEnvVars(options?.daemonConfig);
|
|
68
|
+
const mutation = {
|
|
69
|
+
query: GRAPHQL_QUERIES.CREATE_SERVICE,
|
|
70
|
+
variables: {
|
|
71
|
+
input: {
|
|
72
|
+
projectId,
|
|
73
|
+
environmentId,
|
|
74
|
+
source: {
|
|
75
|
+
image: options?.image ?? "computesdk/compute:latest"
|
|
76
|
+
},
|
|
77
|
+
...Object.keys(envVars).length > 0 && {
|
|
78
|
+
variables: envVars
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
};
|
|
83
|
+
const responseData = await fetchRailway(apiKey, mutation);
|
|
84
|
+
const service = responseData?.serviceCreate;
|
|
85
|
+
if (!service) {
|
|
86
|
+
throw new Error("No service returned from Railway API - responseData.serviceCreate is undefined");
|
|
87
|
+
}
|
|
88
|
+
if (!service.id) {
|
|
89
|
+
throw new Error(`Service ID is undefined. Full service object: ${JSON.stringify(service, null, 2)}`);
|
|
90
|
+
}
|
|
91
|
+
const instance = {
|
|
92
|
+
serviceId: service.id,
|
|
93
|
+
projectId,
|
|
94
|
+
environmentId
|
|
95
|
+
};
|
|
96
|
+
return {
|
|
97
|
+
instance,
|
|
98
|
+
instanceId: service.id
|
|
99
|
+
};
|
|
100
|
+
} catch (error) {
|
|
101
|
+
throw new Error(
|
|
102
|
+
`Failed to create Railway instance: ${error instanceof Error ? error.message : String(error)}`
|
|
103
|
+
);
|
|
104
|
+
}
|
|
105
|
+
},
|
|
106
|
+
getById: async (config, instanceId) => {
|
|
107
|
+
const { apiKey, projectId, environmentId } = getAndValidateCredentials(config);
|
|
108
|
+
try {
|
|
109
|
+
const mutation = {
|
|
110
|
+
query: GRAPHQL_QUERIES.GET_SERVICE,
|
|
111
|
+
variables: {
|
|
112
|
+
serviceId: instanceId
|
|
113
|
+
}
|
|
114
|
+
};
|
|
115
|
+
const responseData = await fetchRailway(apiKey, mutation);
|
|
116
|
+
if (responseData === null) {
|
|
117
|
+
return null;
|
|
118
|
+
}
|
|
119
|
+
const service = responseData?.service;
|
|
120
|
+
if (!service) {
|
|
121
|
+
throw new Error("Service data is missing from Railway response");
|
|
122
|
+
}
|
|
123
|
+
const instance = {
|
|
124
|
+
serviceId: service.id,
|
|
125
|
+
projectId,
|
|
126
|
+
environmentId
|
|
127
|
+
};
|
|
128
|
+
return {
|
|
129
|
+
instance,
|
|
130
|
+
instanceId: service.id
|
|
131
|
+
};
|
|
132
|
+
} catch (error) {
|
|
133
|
+
throw new Error(
|
|
134
|
+
`Failed to get Railway instance: ${error instanceof Error ? error.message : String(error)}`
|
|
135
|
+
);
|
|
136
|
+
}
|
|
137
|
+
},
|
|
138
|
+
list: async (config) => {
|
|
139
|
+
const { apiKey, projectId, environmentId } = getAndValidateCredentials(config);
|
|
140
|
+
try {
|
|
141
|
+
const mutation = {
|
|
142
|
+
query: GRAPHQL_QUERIES.LIST_SERVICES,
|
|
143
|
+
variables: {
|
|
144
|
+
projectId
|
|
145
|
+
}
|
|
146
|
+
};
|
|
147
|
+
const responseData = await fetchRailway(apiKey, mutation);
|
|
148
|
+
const services = responseData?.project?.services?.edges || [];
|
|
149
|
+
return services.map((edge) => {
|
|
150
|
+
const service = edge.node;
|
|
151
|
+
const instance = {
|
|
152
|
+
serviceId: service.id,
|
|
153
|
+
projectId,
|
|
154
|
+
environmentId
|
|
155
|
+
};
|
|
156
|
+
return {
|
|
157
|
+
instance,
|
|
158
|
+
instanceId: service.id
|
|
159
|
+
};
|
|
160
|
+
});
|
|
161
|
+
} catch (error) {
|
|
162
|
+
throw new Error(
|
|
163
|
+
`Failed to list Railway instances: ${error instanceof Error ? error.message : String(error)}`
|
|
164
|
+
);
|
|
165
|
+
}
|
|
166
|
+
},
|
|
167
|
+
destroy: async (config, instanceId) => {
|
|
168
|
+
const { apiKey } = getAndValidateCredentials(config);
|
|
169
|
+
try {
|
|
170
|
+
const mutation = {
|
|
171
|
+
query: GRAPHQL_QUERIES.DELETE_SERVICE,
|
|
172
|
+
variables: {
|
|
173
|
+
id: instanceId
|
|
174
|
+
}
|
|
175
|
+
};
|
|
176
|
+
await fetchRailway(apiKey, mutation);
|
|
177
|
+
} catch (error) {
|
|
178
|
+
console.warn(`Railway destroy warning: ${error instanceof Error ? error.message : String(error)}`);
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
});
|
|
183
|
+
export {
|
|
184
|
+
railway
|
|
185
|
+
};
|
|
186
|
+
//# sourceMappingURL=index.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/railway.ts"],"sourcesContent":["/**\n * Railway Infrastructure Provider\n * \n * Provides infrastructure-only methods for creating/destroying Railway services.\n * Used by the gateway server to provision compute resources with daemon pre-installed.\n */\n\nimport { defineInfraProvider } from '@computesdk/provider';\nimport type { DaemonConfig, CreateSandboxOptions } from '@computesdk/provider';\n\n/**\n * Railway service instance\n */\nexport interface RailwayInstance {\n serviceId: string;\n projectId: string;\n environmentId: string;\n}\n\n/**\n * Railway provider configuration\n */\nexport interface RailwayConfig {\n /** Railway API key - if not provided, will fallback to RAILWAY_API_KEY environment variable */\n apiKey?: string;\n /** Railway Project ID */\n projectId?: string;\n /** Railway Environment ID - if not provided, will fallback to RAILWAY_ENVIRONMENT_ID environment variable */\n environmentId?: string;\n}\n\n/**\n * Get and validate Railway credentials from config and environment\n */\nexport const getAndValidateCredentials = (config: RailwayConfig) => {\n const apiKey = config.apiKey || (typeof process !== 'undefined' && process.env?.RAILWAY_API_KEY) || '';\n const projectId = config.projectId || (typeof process !== 'undefined' && process.env?.RAILWAY_PROJECT_ID) || '';\n const environmentId = config.environmentId || (typeof process !== 'undefined' && process.env?.RAILWAY_ENVIRONMENT_ID) || '';\n\n if (!apiKey) {\n throw new Error(\n 'Missing Railway API key. Provide apiKey in config or set RAILWAY_API_KEY environment variable.'\n );\n }\n\n if (!projectId) {\n throw new Error(\n 'Missing Railway Project ID. Provide projectId in config or set RAILWAY_PROJECT_ID environment variable.'\n );\n }\n\n if (!environmentId) {\n throw new Error(\n 'Missing Railway Environment ID. Provide environmentId in config or set RAILWAY_ENVIRONMENT_ID environment variable.'\n );\n }\n\n return { apiKey, projectId, environmentId };\n};\n\nconst GRAPHQL_QUERIES = {\n CREATE_SERVICE: `mutation ServiceCreate($input: ServiceCreateInput!) { serviceCreate(input: $input) { id name } }`,\n GET_SERVICE: `query Service($serviceId: String!) { service(id: $serviceId) { id name createdAt } }`,\n LIST_SERVICES: `query Project($projectId: String!) { project(id: $projectId) { services { edges { node { id name createdAt updatedAt } } } } }`,\n DELETE_SERVICE: `mutation ServiceDelete($id: String!) { serviceDelete(id: $id) }`\n};\n\nconst handleGraphQLErrors = (data: any) => {\n if (data.errors) {\n throw new Error(`Railway GraphQL error: ${data.errors.map((e: any) => e.message).join(', ')}`);\n }\n};\n\nexport const fetchRailway = async (\n apiKey: string, \n mutation: any,\n) => {\n const response = await fetch('https://backboard.railway.com/graphql/v2', {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n 'Authorization': `Bearer ${apiKey}`\n },\n body: JSON.stringify(mutation)\n });\n\n if (!response.ok) {\n throw new Error(`Railway API error: ${response.status} ${response.statusText}`);\n }\n\n const data = await response.json();\n handleGraphQLErrors(data);\n\n return data.data;\n};\n\n/**\n * Convert daemon config to Railway environment variables\n */\nfunction buildDaemonEnvVars(daemonConfig?: DaemonConfig): Record<string, string> {\n if (!daemonConfig) {\n return {};\n }\n\n return {\n COMPUTESDK_ACCESS_TOKEN: daemonConfig.accessToken,\n ...(daemonConfig.gatewayUrl && { COMPUTESDK_GATEWAY_URL: daemonConfig.gatewayUrl }),\n ...daemonConfig.env,\n };\n}\n\n/**\n * Railway infrastructure provider\n * \n * Creates Railway services with ComputeSDK daemon pre-installed via Docker image.\n */\nexport const railway = defineInfraProvider<RailwayInstance, RailwayConfig>({\n name: 'railway',\n \n methods: {\n create: async (config: RailwayConfig, options?: CreateSandboxOptions & { daemonConfig?: DaemonConfig }) => {\n const { apiKey, projectId, environmentId } = getAndValidateCredentials(config);\n\n try {\n // Build environment variables for daemon\n const envVars = buildDaemonEnvVars(options?.daemonConfig);\n\n const mutation = {\n query: GRAPHQL_QUERIES.CREATE_SERVICE,\n variables: {\n input: {\n projectId,\n environmentId,\n source: {\n image: options?.image ?? 'computesdk/compute:latest'\n },\n ...(Object.keys(envVars).length > 0 && {\n variables: envVars\n })\n }\n }\n };\n\n const responseData = await fetchRailway(apiKey, mutation);\n const service = responseData?.serviceCreate;\n \n if (!service) {\n throw new Error('No service returned from Railway API - responseData.serviceCreate is undefined');\n }\n \n if (!service.id) {\n throw new Error(`Service ID is undefined. Full service object: ${JSON.stringify(service, null, 2)}`);\n }\n\n const instance: RailwayInstance = {\n serviceId: service.id,\n projectId,\n environmentId,\n };\n\n return {\n instance,\n instanceId: service.id\n };\n } catch (error) {\n throw new Error(\n `Failed to create Railway instance: ${error instanceof Error ? error.message : String(error)}`\n );\n }\n },\n\n getById: async (config: RailwayConfig, instanceId: string) => {\n const { apiKey, projectId, environmentId } = getAndValidateCredentials(config);\n\n try {\n const mutation = {\n query: GRAPHQL_QUERIES.GET_SERVICE,\n variables: {\n serviceId: instanceId\n }\n };\n\n const responseData = await fetchRailway(apiKey, mutation);\n \n if (responseData === null) {\n return null;\n }\n \n const service = responseData?.service;\n \n if (!service) {\n throw new Error('Service data is missing from Railway response');\n }\n\n const instance: RailwayInstance = {\n serviceId: service.id,\n projectId,\n environmentId,\n };\n\n return {\n instance,\n instanceId: service.id\n };\n } catch (error) {\n throw new Error(\n `Failed to get Railway instance: ${error instanceof Error ? error.message : String(error)}`\n );\n }\n },\n \n list: async (config: RailwayConfig) => {\n const { apiKey, projectId, environmentId } = getAndValidateCredentials(config);\n\n try {\n const mutation = {\n query: GRAPHQL_QUERIES.LIST_SERVICES,\n variables: {\n projectId\n }\n };\n\n const responseData = await fetchRailway(apiKey, mutation);\n const services = responseData?.project?.services?.edges || [];\n \n return services.map((edge: any) => {\n const service = edge.node;\n const instance: RailwayInstance = {\n serviceId: service.id,\n projectId,\n environmentId,\n };\n\n return {\n instance,\n instanceId: service.id\n };\n });\n } catch (error) {\n throw new Error(\n `Failed to list Railway instances: ${error instanceof Error ? error.message : String(error)}`\n );\n }\n },\n\n destroy: async (config: RailwayConfig, instanceId: string) => {\n const { apiKey } = getAndValidateCredentials(config);\n\n try {\n const mutation = {\n query: GRAPHQL_QUERIES.DELETE_SERVICE,\n variables: {\n id: instanceId\n }\n };\n\n await fetchRailway(apiKey, mutation);\n } catch (error) {\n // For destroy operations, we log warnings rather than throwing\n // since the resource may already be gone\n console.warn(`Railway destroy warning: ${error instanceof Error ? error.message : String(error)}`);\n }\n },\n },\n});\n"],"mappings":"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","names":[]}
|
package/package.json
ADDED
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@computesdk/gateway",
|
|
3
|
+
"version": "0.0.2",
|
|
4
|
+
"description": "Infrastructure provider implementations for ComputeSDK gateway server",
|
|
5
|
+
"author": "ComputeSDK Team",
|
|
6
|
+
"license": "MIT",
|
|
7
|
+
"main": "./dist/index.js",
|
|
8
|
+
"module": "./dist/index.mjs",
|
|
9
|
+
"types": "./dist/index.d.ts",
|
|
10
|
+
"exports": {
|
|
11
|
+
".": {
|
|
12
|
+
"types": "./dist/index.d.ts",
|
|
13
|
+
"import": "./dist/index.mjs",
|
|
14
|
+
"require": "./dist/index.js"
|
|
15
|
+
}
|
|
16
|
+
},
|
|
17
|
+
"files": [
|
|
18
|
+
"dist"
|
|
19
|
+
],
|
|
20
|
+
"dependencies": {
|
|
21
|
+
"@computesdk/provider": "1.0.8"
|
|
22
|
+
},
|
|
23
|
+
"keywords": [
|
|
24
|
+
"computesdk",
|
|
25
|
+
"gateway",
|
|
26
|
+
"infrastructure",
|
|
27
|
+
"providers",
|
|
28
|
+
"internal"
|
|
29
|
+
],
|
|
30
|
+
"repository": {
|
|
31
|
+
"type": "git",
|
|
32
|
+
"url": "https://github.com/computesdk/computesdk.git",
|
|
33
|
+
"directory": "packages/gateway"
|
|
34
|
+
},
|
|
35
|
+
"homepage": "https://www.computesdk.com",
|
|
36
|
+
"bugs": {
|
|
37
|
+
"url": "https://github.com/computesdk/computesdk/issues"
|
|
38
|
+
},
|
|
39
|
+
"devDependencies": {
|
|
40
|
+
"@types/node": "^20.0.0",
|
|
41
|
+
"@vitest/coverage-v8": "^1.0.0",
|
|
42
|
+
"eslint": "^8.37.0",
|
|
43
|
+
"rimraf": "^5.0.0",
|
|
44
|
+
"tsup": "^8.0.0",
|
|
45
|
+
"typescript": "^5.0.0",
|
|
46
|
+
"vitest": "^1.0.0",
|
|
47
|
+
"@computesdk/test-utils": "1.5.1"
|
|
48
|
+
},
|
|
49
|
+
"scripts": {
|
|
50
|
+
"build": "tsup",
|
|
51
|
+
"clean": "rimraf dist",
|
|
52
|
+
"dev": "tsup --watch",
|
|
53
|
+
"test": "vitest run",
|
|
54
|
+
"test:watch": "vitest watch",
|
|
55
|
+
"test:coverage": "vitest run --coverage",
|
|
56
|
+
"typecheck": "tsc --noEmit",
|
|
57
|
+
"lint": "eslint"
|
|
58
|
+
}
|
|
59
|
+
}
|