@stackshift-cloud/sdk 1.0.0 → 1.2.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.
- package/README.md +117 -0
- package/dist/asset-media-v2.d.ts +65 -0
- package/dist/asset-media-v2.js +1 -0
- package/dist/asset-workflows.d.ts +269 -0
- package/dist/asset-workflows.js +69 -0
- package/dist/assets-dam-types.d.ts +225 -0
- package/dist/assets-dam-types.js +1 -0
- package/dist/assets-dam.d.ts +96 -0
- package/dist/assets-dam.js +47 -0
- package/dist/assets-platform-types.d.ts +218 -0
- package/dist/assets-platform-types.js +1 -0
- package/dist/assets-platform.d.ts +55 -0
- package/dist/assets-platform.js +90 -0
- package/dist/assets-video-types.d.ts +116 -0
- package/dist/assets-video-types.js +1 -0
- package/dist/assets-video.d.ts +25 -0
- package/dist/assets-video.js +19 -0
- package/dist/assets.d.ts +21 -1
- package/dist/assets.js +85 -132
- package/dist/client.d.ts +10 -0
- package/dist/client.js +26 -6
- package/dist/generated/assets-contract.d.ts +512 -2
- package/dist/generated/assets-contract.js +102 -0
- package/dist/index.d.ts +21 -0
- package/dist/index.js +11 -0
- package/dist/mail-advanced-types.d.ts +9 -0
- package/dist/mail-billing.d.ts +34 -0
- package/dist/mail-billing.js +7 -0
- package/dist/mail-brands.d.ts +35 -0
- package/dist/mail-brands.js +17 -0
- package/dist/mail-campaigns.d.ts +72 -10
- package/dist/mail-campaigns.js +25 -2
- package/dist/mail-marketing.d.ts +84 -0
- package/dist/mail-marketing.js +24 -0
- package/dist/mail-migration.d.ts +16 -0
- package/dist/mail-migration.js +10 -0
- package/dist/mail-otp-types.d.ts +4 -0
- package/dist/mail-simulation.d.ts +4 -0
- package/dist/mail-simulation.js +1 -0
- package/dist/mail-subscribers.d.ts +39 -0
- package/dist/mail-subscribers.js +1 -0
- package/dist/mail-template-types.d.ts +8 -0
- package/dist/mail-tracking.d.ts +59 -0
- package/dist/mail-tracking.js +14 -0
- package/dist/mail-webhook-types.d.ts +4 -1
- package/dist/mail.d.ts +12 -0
- package/dist/mail.js +19 -0
- package/dist/types.d.ts +19 -0
- package/dist/upload-controller.d.ts +70 -0
- package/dist/upload-controller.js +223 -0
- package/dist/workload-security.d.ts +172 -0
- package/dist/workload-security.js +50 -0
- package/package.json +2 -2
|
@@ -0,0 +1,223 @@
|
|
|
1
|
+
export class UploadResumeInvalidError extends Error {
|
|
2
|
+
constructor(message = 'The saved upload session is no longer valid') {
|
|
3
|
+
super(message);
|
|
4
|
+
this.name = 'UploadResumeInvalidError';
|
|
5
|
+
}
|
|
6
|
+
}
|
|
7
|
+
export class ChunkedUploadController {
|
|
8
|
+
transport;
|
|
9
|
+
store;
|
|
10
|
+
persistence = Promise.resolve();
|
|
11
|
+
constructor(transport, store) {
|
|
12
|
+
this.transport = transport;
|
|
13
|
+
this.store = store;
|
|
14
|
+
}
|
|
15
|
+
async upload(file, request, options = {}) {
|
|
16
|
+
throwIfAborted(options.signal);
|
|
17
|
+
const resumeKey = options.resumeKey;
|
|
18
|
+
const fingerprint = fileFingerprint(file, request);
|
|
19
|
+
let session = await this.restore(resumeKey, fingerprint);
|
|
20
|
+
if (!session)
|
|
21
|
+
session = await this.transport.create(file, request);
|
|
22
|
+
validateSession(session, file.size);
|
|
23
|
+
options.onSession?.(session);
|
|
24
|
+
await this.persist(resumeKey, fingerprint, session);
|
|
25
|
+
const completed = await verifiedCompletedParts(file, session);
|
|
26
|
+
const loadedByPart = new Map(completed);
|
|
27
|
+
const tasks = [];
|
|
28
|
+
for (let offset = 0, partNumber = 1; offset < file.size; offset += session.partSize, partNumber += 1) {
|
|
29
|
+
if (!completed.has(partNumber))
|
|
30
|
+
tasks.push({ partNumber, chunk: file.slice(offset, offset + session.partSize) });
|
|
31
|
+
}
|
|
32
|
+
const report = () => {
|
|
33
|
+
const loaded = Math.min(file.size, [...loadedByPart.values()].reduce((sum, size) => sum + size, 0));
|
|
34
|
+
options.onProgress?.({ loaded, total: file.size, percent: file.size ? Math.round(loaded / file.size * 100) : 100 });
|
|
35
|
+
};
|
|
36
|
+
report();
|
|
37
|
+
const concurrency = boundedInteger(options.concurrency, 4, 1, 8);
|
|
38
|
+
const attempts = boundedInteger(options.retryAttempts, 4, 1, 8);
|
|
39
|
+
let next = 0;
|
|
40
|
+
const worker = async () => {
|
|
41
|
+
while (next < tasks.length) {
|
|
42
|
+
throwIfAborted(options.signal);
|
|
43
|
+
const task = tasks[next++];
|
|
44
|
+
const checksum = await sha256Blob(task.chunk);
|
|
45
|
+
await retryWithBackoff(async () => {
|
|
46
|
+
await this.transport.uploadPart(session, task.partNumber, task.chunk, checksum, {
|
|
47
|
+
signal: options.signal,
|
|
48
|
+
onProgress: (progress) => {
|
|
49
|
+
loadedByPart.set(task.partNumber, progress.loaded);
|
|
50
|
+
options.onPartProgress?.(task.partNumber, progress);
|
|
51
|
+
report();
|
|
52
|
+
},
|
|
53
|
+
});
|
|
54
|
+
}, attempts, options.signal);
|
|
55
|
+
loadedByPart.set(task.partNumber, task.chunk.size);
|
|
56
|
+
session.parts = [...session.parts.filter((part) => part.partNumber !== task.partNumber), {
|
|
57
|
+
partNumber: task.partNumber, size: task.chunk.size, checksumSHA256: checksum,
|
|
58
|
+
}];
|
|
59
|
+
await this.persist(resumeKey, fingerprint, session);
|
|
60
|
+
report();
|
|
61
|
+
}
|
|
62
|
+
};
|
|
63
|
+
try {
|
|
64
|
+
await Promise.all(Array.from({ length: Math.min(concurrency, tasks.length || 1) }, worker));
|
|
65
|
+
const result = await retryWithBackoff(() => this.transport.complete(session), attempts, options.signal);
|
|
66
|
+
if (resumeKey)
|
|
67
|
+
await this.store?.delete(resumeKey);
|
|
68
|
+
return result;
|
|
69
|
+
}
|
|
70
|
+
catch (error) {
|
|
71
|
+
if (isAbortError(error) && options.cancelOnAbort) {
|
|
72
|
+
await this.transport.cancel(session).catch(() => undefined);
|
|
73
|
+
if (resumeKey)
|
|
74
|
+
await this.store?.delete(resumeKey);
|
|
75
|
+
}
|
|
76
|
+
throw error;
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
async cancel(resumeKey) {
|
|
80
|
+
const record = await this.store?.get(resumeKey);
|
|
81
|
+
if (!record)
|
|
82
|
+
return;
|
|
83
|
+
try {
|
|
84
|
+
await this.transport.cancel(record.session);
|
|
85
|
+
}
|
|
86
|
+
finally {
|
|
87
|
+
await this.store?.delete(resumeKey);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
async restore(resumeKey, fingerprint) {
|
|
91
|
+
if (!resumeKey || !this.store)
|
|
92
|
+
return undefined;
|
|
93
|
+
const record = await this.store.get(resumeKey);
|
|
94
|
+
if (!record)
|
|
95
|
+
return undefined;
|
|
96
|
+
if (record.fingerprint !== fingerprint || Date.parse(record.session.expiresAt) <= Date.now()) {
|
|
97
|
+
await this.transport.cancel(record.session).catch(() => undefined);
|
|
98
|
+
await this.store.delete(resumeKey);
|
|
99
|
+
return undefined;
|
|
100
|
+
}
|
|
101
|
+
try {
|
|
102
|
+
return await this.transport.resume(record.session.resumeContext ?? record.session.id);
|
|
103
|
+
}
|
|
104
|
+
catch (error) {
|
|
105
|
+
if (!(error instanceof UploadResumeInvalidError))
|
|
106
|
+
throw error;
|
|
107
|
+
await this.store.delete(resumeKey);
|
|
108
|
+
return undefined;
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
persist(resumeKey, fingerprint, session) {
|
|
112
|
+
if (!resumeKey || !this.store)
|
|
113
|
+
return Promise.resolve();
|
|
114
|
+
const record = {
|
|
115
|
+
key: resumeKey,
|
|
116
|
+
fingerprint,
|
|
117
|
+
session: { ...session, parts: session.parts.map((part) => ({ ...part })) },
|
|
118
|
+
updatedAt: new Date().toISOString(),
|
|
119
|
+
};
|
|
120
|
+
this.persistence = this.persistence.then(() => this.store.set(record));
|
|
121
|
+
return this.persistence;
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
export function putBlob(url, blob, callbacks = {}, headers = {}, fetcher = fetch) {
|
|
125
|
+
if (typeof XMLHttpRequest === 'undefined') {
|
|
126
|
+
return fetcher(url, { method: 'PUT', body: blob, signal: callbacks.signal, headers }).then((response) => {
|
|
127
|
+
if (!response.ok)
|
|
128
|
+
throw new Error(`Upload failed with ${response.status}`);
|
|
129
|
+
callbacks.onProgress?.({ loaded: blob.size, total: blob.size, percent: 100 });
|
|
130
|
+
});
|
|
131
|
+
}
|
|
132
|
+
return new Promise((resolve, reject) => {
|
|
133
|
+
const xhr = new XMLHttpRequest();
|
|
134
|
+
xhr.open('PUT', url);
|
|
135
|
+
for (const [name, value] of Object.entries(headers))
|
|
136
|
+
xhr.setRequestHeader(name, value);
|
|
137
|
+
const abort = () => xhr.abort();
|
|
138
|
+
const cleanup = () => callbacks.signal?.removeEventListener('abort', abort);
|
|
139
|
+
callbacks.signal?.addEventListener('abort', abort, { once: true });
|
|
140
|
+
xhr.upload.onprogress = (event) => {
|
|
141
|
+
const total = event.total || blob.size;
|
|
142
|
+
callbacks.onProgress?.({ loaded: event.loaded, total, percent: total ? Math.round(event.loaded / total * 100) : 0 });
|
|
143
|
+
};
|
|
144
|
+
xhr.onload = () => { cleanup(); xhr.status >= 200 && xhr.status < 300 ? resolve() : reject(new Error(`Upload failed with ${xhr.status}`)); };
|
|
145
|
+
xhr.onerror = () => { cleanup(); reject(new Error('Upload failed')); };
|
|
146
|
+
xhr.onabort = () => { cleanup(); reject(abortError()); };
|
|
147
|
+
xhr.send(blob);
|
|
148
|
+
});
|
|
149
|
+
}
|
|
150
|
+
export async function sha256Blob(blob) {
|
|
151
|
+
if (!globalThis.crypto?.subtle)
|
|
152
|
+
throw new Error('Web Crypto SHA-256 is required for multipart uploads');
|
|
153
|
+
const digest = await globalThis.crypto.subtle.digest('SHA-256', await blob.arrayBuffer());
|
|
154
|
+
return [...new Uint8Array(digest)].map((byte) => byte.toString(16).padStart(2, '0')).join('');
|
|
155
|
+
}
|
|
156
|
+
function validateSession(session, size) {
|
|
157
|
+
if (!session.id || !session.uploadToken || session.partSize <= 0)
|
|
158
|
+
throw new Error('Upload session is incomplete');
|
|
159
|
+
if (Date.parse(session.expiresAt) <= Date.now())
|
|
160
|
+
throw new Error('Upload session has expired');
|
|
161
|
+
if (size > session.maxBytes)
|
|
162
|
+
throw new Error(`File exceeds upload limit of ${session.maxBytes} bytes`);
|
|
163
|
+
}
|
|
164
|
+
function expectedPartSize(fileSize, partSize, partNumber) {
|
|
165
|
+
const offset = (partNumber - 1) * partSize;
|
|
166
|
+
if (partNumber < 1 || offset >= fileSize)
|
|
167
|
+
return -1;
|
|
168
|
+
return Math.min(partSize, fileSize - offset);
|
|
169
|
+
}
|
|
170
|
+
async function verifiedCompletedParts(file, session) {
|
|
171
|
+
const completed = new Map();
|
|
172
|
+
for (const part of session.parts) {
|
|
173
|
+
const expectedSize = expectedPartSize(file.size, session.partSize, part.partNumber);
|
|
174
|
+
if (part.size !== expectedSize || !part.checksumSHA256)
|
|
175
|
+
continue;
|
|
176
|
+
const offset = (part.partNumber - 1) * session.partSize;
|
|
177
|
+
const checksum = await sha256Blob(file.slice(offset, offset + expectedSize));
|
|
178
|
+
if (checksum.toLowerCase() === part.checksumSHA256.toLowerCase()) {
|
|
179
|
+
completed.set(part.partNumber, part.size);
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
return completed;
|
|
183
|
+
}
|
|
184
|
+
function fileFingerprint(file, request) {
|
|
185
|
+
const modified = file.lastModified ?? 0;
|
|
186
|
+
return [request.key, request.fileName, request.mimeType, file.size, modified].join(':');
|
|
187
|
+
}
|
|
188
|
+
function boundedInteger(value, fallback, minimum, maximum) {
|
|
189
|
+
if (value === undefined)
|
|
190
|
+
return fallback;
|
|
191
|
+
if (!Number.isSafeInteger(value))
|
|
192
|
+
throw new Error('Upload concurrency and retry attempts must be integers');
|
|
193
|
+
return Math.min(maximum, Math.max(minimum, value));
|
|
194
|
+
}
|
|
195
|
+
async function retryWithBackoff(operation, attempts, signal) {
|
|
196
|
+
let lastError;
|
|
197
|
+
for (let attempt = 0; attempt < attempts; attempt += 1) {
|
|
198
|
+
try {
|
|
199
|
+
return await operation();
|
|
200
|
+
}
|
|
201
|
+
catch (error) {
|
|
202
|
+
if (isAbortError(error) || signal?.aborted)
|
|
203
|
+
throw abortError();
|
|
204
|
+
lastError = error;
|
|
205
|
+
if (attempt + 1 < attempts)
|
|
206
|
+
await abortableDelay(250 * 2 ** attempt, signal);
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
throw lastError;
|
|
210
|
+
}
|
|
211
|
+
function abortableDelay(milliseconds, signal) {
|
|
212
|
+
throwIfAborted(signal);
|
|
213
|
+
return new Promise((resolve, reject) => {
|
|
214
|
+
const timer = setTimeout(() => { cleanup(); resolve(); }, milliseconds);
|
|
215
|
+
const abort = () => { clearTimeout(timer); cleanup(); reject(abortError()); };
|
|
216
|
+
const cleanup = () => signal?.removeEventListener('abort', abort);
|
|
217
|
+
signal?.addEventListener('abort', abort, { once: true });
|
|
218
|
+
});
|
|
219
|
+
}
|
|
220
|
+
function throwIfAborted(signal) { if (signal?.aborted)
|
|
221
|
+
throw abortError(); }
|
|
222
|
+
function abortError() { return typeof DOMException === 'undefined' ? Object.assign(new Error('Upload cancelled'), { name: 'AbortError' }) : new DOMException('Upload cancelled', 'AbortError'); }
|
|
223
|
+
function isAbortError(error) { return error instanceof Error && error.name === 'AbortError'; }
|
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
import type { StackShift } from './client.js';
|
|
2
|
+
export type AdmissionDecision = 'allow' | 'review' | 'block';
|
|
3
|
+
export type SecurityResourceStatus = 'pending' | 'approved' | 'rejected' | 'revoked' | 'expired';
|
|
4
|
+
export type SecurityIncidentStatus = 'open' | 'reviewing' | 'quarantined' | 'released' | 'dismissed';
|
|
5
|
+
export interface WorkloadAdmission {
|
|
6
|
+
id: string;
|
|
7
|
+
project_id?: string;
|
|
8
|
+
deployment_id?: string;
|
|
9
|
+
environment: string;
|
|
10
|
+
execution_kind: string;
|
|
11
|
+
image_reference: string;
|
|
12
|
+
image_digest: string;
|
|
13
|
+
policy_version: string;
|
|
14
|
+
decision: AdmissionDecision;
|
|
15
|
+
sanitized_evidence: unknown;
|
|
16
|
+
receipt_digest: string;
|
|
17
|
+
issued_at: string;
|
|
18
|
+
expires_at: string;
|
|
19
|
+
revoked_at?: string;
|
|
20
|
+
}
|
|
21
|
+
export interface ExternalDependency {
|
|
22
|
+
id: string;
|
|
23
|
+
project_id: string;
|
|
24
|
+
environment: string;
|
|
25
|
+
hostname: string;
|
|
26
|
+
port: number;
|
|
27
|
+
protocol: string;
|
|
28
|
+
purpose: string;
|
|
29
|
+
tls_required: boolean;
|
|
30
|
+
private_route_required: boolean;
|
|
31
|
+
detected: boolean;
|
|
32
|
+
status: string;
|
|
33
|
+
created_at: string;
|
|
34
|
+
updated_at: string;
|
|
35
|
+
}
|
|
36
|
+
export interface EgressGrant {
|
|
37
|
+
id: string;
|
|
38
|
+
project_id: string;
|
|
39
|
+
dependency_id?: string;
|
|
40
|
+
environment: string;
|
|
41
|
+
protocol: string;
|
|
42
|
+
hostname?: string;
|
|
43
|
+
cidr?: string;
|
|
44
|
+
port_start: number;
|
|
45
|
+
port_end: number;
|
|
46
|
+
purpose: string;
|
|
47
|
+
status: SecurityResourceStatus;
|
|
48
|
+
broad_public_egress: boolean;
|
|
49
|
+
expires_at: string;
|
|
50
|
+
resolved_cidrs?: string[];
|
|
51
|
+
created_at: string;
|
|
52
|
+
updated_at: string;
|
|
53
|
+
}
|
|
54
|
+
export interface PrivateRoute {
|
|
55
|
+
id: string;
|
|
56
|
+
project_id: string;
|
|
57
|
+
node_id: string;
|
|
58
|
+
environment: string;
|
|
59
|
+
route_kind: string;
|
|
60
|
+
cidr: string;
|
|
61
|
+
hostname?: string;
|
|
62
|
+
purpose: string;
|
|
63
|
+
status: SecurityResourceStatus;
|
|
64
|
+
expires_at: string;
|
|
65
|
+
created_at: string;
|
|
66
|
+
updated_at: string;
|
|
67
|
+
}
|
|
68
|
+
export interface NetworkDestination {
|
|
69
|
+
protocol: string;
|
|
70
|
+
hostname?: string;
|
|
71
|
+
cidrs?: string[];
|
|
72
|
+
port_start: number;
|
|
73
|
+
port_end: number;
|
|
74
|
+
purpose: string;
|
|
75
|
+
grant_id?: string;
|
|
76
|
+
expires_at?: string;
|
|
77
|
+
}
|
|
78
|
+
export interface NetworkPolicy {
|
|
79
|
+
version: string;
|
|
80
|
+
mode: 'restricted' | 'quarantined';
|
|
81
|
+
allow_stackshift_dns: boolean;
|
|
82
|
+
allow_public_web: boolean;
|
|
83
|
+
destinations: NetworkDestination[];
|
|
84
|
+
block_private_ranges: boolean;
|
|
85
|
+
block_metadata: boolean;
|
|
86
|
+
block_alternate_dns: boolean;
|
|
87
|
+
block_unexpected_udp: boolean;
|
|
88
|
+
}
|
|
89
|
+
export interface PolicyInspection {
|
|
90
|
+
effective: boolean;
|
|
91
|
+
policy?: NetworkPolicy;
|
|
92
|
+
pending_dependencies?: ExternalDependency[];
|
|
93
|
+
}
|
|
94
|
+
export interface SecurityIncident {
|
|
95
|
+
id: string;
|
|
96
|
+
project_id: string;
|
|
97
|
+
deployment_id?: string;
|
|
98
|
+
environment: string;
|
|
99
|
+
category: string;
|
|
100
|
+
confidence: 'low' | 'medium' | 'high';
|
|
101
|
+
status: SecurityIncidentStatus;
|
|
102
|
+
sanitized_summary: string;
|
|
103
|
+
sanitized_evidence: unknown;
|
|
104
|
+
quarantined_at?: string;
|
|
105
|
+
enforcement_status: string;
|
|
106
|
+
released_at?: string;
|
|
107
|
+
resolution_note?: string;
|
|
108
|
+
created_at: string;
|
|
109
|
+
updated_at: string;
|
|
110
|
+
}
|
|
111
|
+
export interface DeclareDependencyInput {
|
|
112
|
+
environment: string;
|
|
113
|
+
hostname: string;
|
|
114
|
+
port: number;
|
|
115
|
+
protocol: string;
|
|
116
|
+
purpose: string;
|
|
117
|
+
tls_required?: boolean;
|
|
118
|
+
}
|
|
119
|
+
export interface RequestGrantInput {
|
|
120
|
+
dependency_id?: string;
|
|
121
|
+
environment: string;
|
|
122
|
+
protocol: string;
|
|
123
|
+
hostname?: string;
|
|
124
|
+
cidr?: string;
|
|
125
|
+
port_start: number;
|
|
126
|
+
port_end: number;
|
|
127
|
+
purpose: string;
|
|
128
|
+
broad_public_egress?: boolean;
|
|
129
|
+
expires_at: string;
|
|
130
|
+
}
|
|
131
|
+
export interface RequestPrivateRouteInput {
|
|
132
|
+
node_id: string;
|
|
133
|
+
environment: string;
|
|
134
|
+
route_kind: string;
|
|
135
|
+
cidr: string;
|
|
136
|
+
hostname?: string;
|
|
137
|
+
purpose: string;
|
|
138
|
+
expires_at: string;
|
|
139
|
+
}
|
|
140
|
+
export declare class WorkloadSecurityClient {
|
|
141
|
+
private readonly client;
|
|
142
|
+
constructor(client: StackShift);
|
|
143
|
+
private root;
|
|
144
|
+
admissions(projectId: string, limit?: number): Promise<{
|
|
145
|
+
admissions: WorkloadAdmission[];
|
|
146
|
+
}>;
|
|
147
|
+
policy(projectId: string, environment?: string): Promise<PolicyInspection>;
|
|
148
|
+
dependencies(projectId: string, environment?: string): Promise<{
|
|
149
|
+
dependencies: ExternalDependency[];
|
|
150
|
+
}>;
|
|
151
|
+
declareDependency(projectId: string, input: DeclareDependencyInput): Promise<{
|
|
152
|
+
dependencies: ExternalDependency[];
|
|
153
|
+
}>;
|
|
154
|
+
refreshDependencies(projectId: string, environment: string): Promise<{
|
|
155
|
+
dependencies: ExternalDependency[];
|
|
156
|
+
}>;
|
|
157
|
+
grants(projectId: string, environment?: string): Promise<{
|
|
158
|
+
grants: EgressGrant[];
|
|
159
|
+
}>;
|
|
160
|
+
requestGrant(projectId: string, input: RequestGrantInput): Promise<EgressGrant>;
|
|
161
|
+
privateRoutes(projectId: string, environment?: string): Promise<{
|
|
162
|
+
private_routes: PrivateRoute[];
|
|
163
|
+
}>;
|
|
164
|
+
requestPrivateRoute(projectId: string, input: RequestPrivateRouteInput): Promise<PrivateRoute>;
|
|
165
|
+
incidents(projectId: string, status?: SecurityIncidentStatus, limit?: number): Promise<{
|
|
166
|
+
incidents: SecurityIncident[];
|
|
167
|
+
}>;
|
|
168
|
+
incident(projectId: string, incidentId: string): Promise<{
|
|
169
|
+
incident: SecurityIncident;
|
|
170
|
+
notifications: unknown[];
|
|
171
|
+
}>;
|
|
172
|
+
}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
export class WorkloadSecurityClient {
|
|
2
|
+
client;
|
|
3
|
+
constructor(client) {
|
|
4
|
+
this.client = client;
|
|
5
|
+
}
|
|
6
|
+
root(projectId) {
|
|
7
|
+
return `/projects/${encodeURIComponent(projectId)}/security`;
|
|
8
|
+
}
|
|
9
|
+
admissions(projectId, limit) {
|
|
10
|
+
return this.client.request(withQuery(`${this.root(projectId)}/admissions`, { limit }));
|
|
11
|
+
}
|
|
12
|
+
policy(projectId, environment) {
|
|
13
|
+
return this.client.request(withQuery(`${this.root(projectId)}/policy`, { environment }));
|
|
14
|
+
}
|
|
15
|
+
dependencies(projectId, environment) {
|
|
16
|
+
return this.client.request(withQuery(`${this.root(projectId)}/dependencies`, { environment }));
|
|
17
|
+
}
|
|
18
|
+
declareDependency(projectId, input) {
|
|
19
|
+
return this.client.request(`${this.root(projectId)}/dependencies`, { method: 'POST', body: input });
|
|
20
|
+
}
|
|
21
|
+
refreshDependencies(projectId, environment) {
|
|
22
|
+
return this.client.request(`${this.root(projectId)}/dependencies/refresh`, { method: 'POST', body: { environment } });
|
|
23
|
+
}
|
|
24
|
+
grants(projectId, environment) {
|
|
25
|
+
return this.client.request(withQuery(`${this.root(projectId)}/grants`, { environment }));
|
|
26
|
+
}
|
|
27
|
+
requestGrant(projectId, input) {
|
|
28
|
+
return this.client.request(`${this.root(projectId)}/grants`, { method: 'POST', body: input });
|
|
29
|
+
}
|
|
30
|
+
privateRoutes(projectId, environment) {
|
|
31
|
+
return this.client.request(withQuery(`${this.root(projectId)}/private-routes`, { environment }));
|
|
32
|
+
}
|
|
33
|
+
requestPrivateRoute(projectId, input) {
|
|
34
|
+
return this.client.request(`${this.root(projectId)}/private-routes`, { method: 'POST', body: input });
|
|
35
|
+
}
|
|
36
|
+
incidents(projectId, status, limit) {
|
|
37
|
+
return this.client.request(withQuery(`${this.root(projectId)}/incidents`, { status, limit }));
|
|
38
|
+
}
|
|
39
|
+
incident(projectId, incidentId) {
|
|
40
|
+
return this.client.request(`${this.root(projectId)}/incidents/${encodeURIComponent(incidentId)}`);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
function withQuery(path, values) {
|
|
44
|
+
const query = new URLSearchParams();
|
|
45
|
+
for (const [key, value] of Object.entries(values)) {
|
|
46
|
+
if (value !== undefined && value !== '')
|
|
47
|
+
query.set(key, String(value));
|
|
48
|
+
}
|
|
49
|
+
return query.size ? `${path}?${query}` : path;
|
|
50
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@stackshift-cloud/sdk",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.2.0",
|
|
4
4
|
"description": "Official JavaScript and TypeScript SDK for StackShift.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
],
|
|
18
18
|
"scripts": {
|
|
19
19
|
"build": "tsc -p tsconfig.json",
|
|
20
|
-
"test": "npm run build && node test/mail.test.mjs && node test/mail-webhooks.test.mjs && node test/mail-advanced.test.mjs && node test/mail-exports.test.mjs && node test/mail-diagnostics.test.mjs && node test/assets.test.mjs"
|
|
20
|
+
"test": "npm run build && node test/mail.test.mjs && node test/mail-simulation.test.mjs && node test/mail-platform.test.mjs && node test/mail-webhooks.test.mjs && node test/mail-advanced.test.mjs && node test/mail-tracking.test.mjs && node test/mail-subscribers.test.mjs && node test/mail-exports.test.mjs && node test/mail-diagnostics.test.mjs && node test/assets.test.mjs && node test/assets-platform.test.mjs && node test/assets-video.test.mjs && node test/assets-dam.test.mjs"
|
|
21
21
|
},
|
|
22
22
|
"devDependencies": {
|
|
23
23
|
"typescript": "^5.4.0"
|