@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,90 @@
|
|
|
1
|
+
const encoded = encodeURIComponent;
|
|
2
|
+
export class AssetConnectionsClient {
|
|
3
|
+
client;
|
|
4
|
+
constructor(client) {
|
|
5
|
+
this.client = client;
|
|
6
|
+
}
|
|
7
|
+
list() { return this.client.request('/assets/connections'); }
|
|
8
|
+
get(connectionId) { return this.client.request(`/assets/connections/${encoded(connectionId)}`); }
|
|
9
|
+
create(input) {
|
|
10
|
+
return this.client.request('/assets/connections', { method: 'POST', body: input });
|
|
11
|
+
}
|
|
12
|
+
update(connectionId, input) {
|
|
13
|
+
return this.client.request(`/assets/connections/${encoded(connectionId)}`, { method: 'PATCH', body: input });
|
|
14
|
+
}
|
|
15
|
+
verify(connectionId) {
|
|
16
|
+
return this.client.request(`/assets/connections/${encoded(connectionId)}/verify`, { method: 'POST' });
|
|
17
|
+
}
|
|
18
|
+
delete(connectionId) {
|
|
19
|
+
return this.client.request(`/assets/connections/${encoded(connectionId)}`, { method: 'DELETE' });
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
export class AssetStorageClient {
|
|
23
|
+
client;
|
|
24
|
+
constructor(client) {
|
|
25
|
+
this.client = client;
|
|
26
|
+
}
|
|
27
|
+
configure(bucketId, revision, input) {
|
|
28
|
+
return this.client.request(`/assets/buckets/${encoded(bucketId)}/storage`, {
|
|
29
|
+
method: 'PUT', body: input, headers: { 'If-Match': `"${revision}"` },
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
listS2Buckets() { return this.client.request('/assets/s2-buckets'); }
|
|
33
|
+
createS2Bucket(input) {
|
|
34
|
+
return this.client.request('/assets/s2-buckets', { method: 'POST', body: input });
|
|
35
|
+
}
|
|
36
|
+
relocate(bucketId, input = {}) {
|
|
37
|
+
return this.client.request(`/assets/buckets/${encoded(bucketId)}/relocations`, { method: 'POST', body: input });
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
export class AssetMigrationsClient {
|
|
41
|
+
client;
|
|
42
|
+
constructor(client) {
|
|
43
|
+
this.client = client;
|
|
44
|
+
}
|
|
45
|
+
start(input) {
|
|
46
|
+
return this.client.request('/assets/migrations', { method: 'POST', body: input });
|
|
47
|
+
}
|
|
48
|
+
list(options = {}) {
|
|
49
|
+
const query = new URLSearchParams();
|
|
50
|
+
if (options.limit !== undefined)
|
|
51
|
+
query.set('limit', String(options.limit));
|
|
52
|
+
const suffix = query.toString();
|
|
53
|
+
return this.client.request(`/assets/migrations${suffix ? `?${suffix}` : ''}`);
|
|
54
|
+
}
|
|
55
|
+
get(migrationId) { return this.client.request(`/assets/migrations/${encoded(migrationId)}`); }
|
|
56
|
+
cancel(migrationId) {
|
|
57
|
+
return this.client.request(`/assets/migrations/${encoded(migrationId)}/cancel`, { method: 'POST' });
|
|
58
|
+
}
|
|
59
|
+
retryItem(migrationId, itemId) {
|
|
60
|
+
return this.client.request(`/assets/migrations/${encoded(migrationId)}/items/${encoded(itemId)}/retry`, { method: 'POST' });
|
|
61
|
+
}
|
|
62
|
+
report(migrationId, format = 'json') {
|
|
63
|
+
return this.client.request(`/assets/migrations/${encoded(migrationId)}/report?format=${format}`);
|
|
64
|
+
}
|
|
65
|
+
async downloadReport(migrationId, format = 'json') {
|
|
66
|
+
return this.client.requestRaw(`/assets/migrations/${encoded(migrationId)}/report?format=${format}`).then((response) => response.blob());
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
export class AssetTextClient {
|
|
70
|
+
client;
|
|
71
|
+
constructor(client) {
|
|
72
|
+
this.client = client;
|
|
73
|
+
}
|
|
74
|
+
start(assetId, input = {}) {
|
|
75
|
+
return this.client.request(`/assets/${encoded(assetId)}/text-extraction`, { method: 'POST', body: input });
|
|
76
|
+
}
|
|
77
|
+
get(assetId) { return this.client.request(`/assets/${encoded(assetId)}/text-extraction`); }
|
|
78
|
+
retry(extractionId) {
|
|
79
|
+
return this.client.request(`/assets/text-extractions/${encoded(extractionId)}/retry`, { method: 'POST' });
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
export class AssetUploadCapabilitiesClient {
|
|
83
|
+
client;
|
|
84
|
+
constructor(client) {
|
|
85
|
+
this.client = client;
|
|
86
|
+
}
|
|
87
|
+
create(input) {
|
|
88
|
+
return this.client.request('/assets/upload-capabilities', { method: 'POST', body: input });
|
|
89
|
+
}
|
|
90
|
+
}
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
export interface AssetVideoPackage {
|
|
2
|
+
id: string;
|
|
3
|
+
asset_space_id: string;
|
|
4
|
+
asset_id: string;
|
|
5
|
+
version_id: string;
|
|
6
|
+
source_checksum_sha256: string;
|
|
7
|
+
recipe_hash: string;
|
|
8
|
+
recipe: string;
|
|
9
|
+
processor_revision: string;
|
|
10
|
+
status: 'queued' | 'processing' | 'ready' | 'degraded' | 'failed' | 'cancelled';
|
|
11
|
+
phase: string;
|
|
12
|
+
progress_percent: number;
|
|
13
|
+
job_id?: string;
|
|
14
|
+
published_attempt?: string;
|
|
15
|
+
admitted_at?: string;
|
|
16
|
+
created_at: string;
|
|
17
|
+
updated_at: string;
|
|
18
|
+
failure_code: string;
|
|
19
|
+
metadata: {
|
|
20
|
+
asset_id: string;
|
|
21
|
+
duration_seconds: number;
|
|
22
|
+
width: number;
|
|
23
|
+
height: number;
|
|
24
|
+
codec: string;
|
|
25
|
+
bitrate: number;
|
|
26
|
+
frame_rate: number;
|
|
27
|
+
has_audio: boolean;
|
|
28
|
+
rotation: number;
|
|
29
|
+
created_at: string;
|
|
30
|
+
updated_at: string;
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
export interface AssetVideoWorkspace {
|
|
34
|
+
active?: AssetVideoPackage;
|
|
35
|
+
pending?: AssetVideoPackage;
|
|
36
|
+
source?: {
|
|
37
|
+
version_id: string;
|
|
38
|
+
scan_status: 'pending' | 'clean' | 'infected' | 'failed';
|
|
39
|
+
review_status: 'not_required' | 'pending' | 'approved' | 'rejected';
|
|
40
|
+
review_reason: string;
|
|
41
|
+
};
|
|
42
|
+
renditions: Array<{
|
|
43
|
+
id: string;
|
|
44
|
+
package_id?: string;
|
|
45
|
+
asset_id: string;
|
|
46
|
+
source_checksum_sha256: string;
|
|
47
|
+
kind: 'hls' | 'mp4' | 'poster';
|
|
48
|
+
profile: string;
|
|
49
|
+
width: number;
|
|
50
|
+
height: number;
|
|
51
|
+
size: number;
|
|
52
|
+
mime_type: string;
|
|
53
|
+
status: string;
|
|
54
|
+
created_at: string;
|
|
55
|
+
updated_at: string;
|
|
56
|
+
}>;
|
|
57
|
+
admission_enabled: boolean;
|
|
58
|
+
}
|
|
59
|
+
export interface AssetVideoCaptionInput {
|
|
60
|
+
/** Atomically replace this active track; the new reference receives a new ID. */
|
|
61
|
+
replace_caption_id?: string;
|
|
62
|
+
caption_asset_id: string;
|
|
63
|
+
caption_version_id: string;
|
|
64
|
+
language: string;
|
|
65
|
+
label: string;
|
|
66
|
+
kind: 'captions' | 'subtitles';
|
|
67
|
+
is_default?: boolean;
|
|
68
|
+
}
|
|
69
|
+
export interface AssetVideoCaption extends AssetVideoCaptionInput {
|
|
70
|
+
id: string;
|
|
71
|
+
asset_space_id: string;
|
|
72
|
+
asset_id: string;
|
|
73
|
+
version_id: string;
|
|
74
|
+
is_default: boolean;
|
|
75
|
+
}
|
|
76
|
+
export interface AssetPlaybackGrant {
|
|
77
|
+
has_audio: boolean;
|
|
78
|
+
session: {
|
|
79
|
+
id: string;
|
|
80
|
+
asset_space_id: string;
|
|
81
|
+
asset_id: string;
|
|
82
|
+
package_id: string;
|
|
83
|
+
version_id: string;
|
|
84
|
+
owner_id?: string;
|
|
85
|
+
is_private: boolean;
|
|
86
|
+
expires_at: string;
|
|
87
|
+
maximum_expires_at: string;
|
|
88
|
+
revoked_at?: string;
|
|
89
|
+
created_at: string;
|
|
90
|
+
};
|
|
91
|
+
credential: string;
|
|
92
|
+
hls: string;
|
|
93
|
+
mp4: string;
|
|
94
|
+
poster?: string;
|
|
95
|
+
captions: Array<{
|
|
96
|
+
id: string;
|
|
97
|
+
url: string;
|
|
98
|
+
language: string;
|
|
99
|
+
label: string;
|
|
100
|
+
kind: 'captions' | 'subtitles';
|
|
101
|
+
is_default: boolean;
|
|
102
|
+
}>;
|
|
103
|
+
}
|
|
104
|
+
export interface AssetPlaybackEvent {
|
|
105
|
+
sequence: number;
|
|
106
|
+
kind: 'startup' | 'rebuffer' | 'error' | 'quality' | 'watched' | 'completed';
|
|
107
|
+
value: number;
|
|
108
|
+
}
|
|
109
|
+
export interface AssetPlaybackAnalytics {
|
|
110
|
+
starts: number;
|
|
111
|
+
completions: number;
|
|
112
|
+
errors: number;
|
|
113
|
+
startup_ms: number;
|
|
114
|
+
rebuffer_ms: number;
|
|
115
|
+
watched_ms: number;
|
|
116
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import type { StackShift } from './client.js';
|
|
2
|
+
import type { AssetVideoWorkspace, AssetVideoPackage, AssetVideoCaption, AssetVideoCaptionInput, AssetPlaybackGrant, AssetPlaybackEvent, AssetPlaybackAnalytics } from './assets-video-types.js';
|
|
3
|
+
export declare class AssetVideoClient {
|
|
4
|
+
private readonly client;
|
|
5
|
+
constructor(client: StackShift);
|
|
6
|
+
workspace(assetId: string): Promise<AssetVideoWorkspace>;
|
|
7
|
+
process(assetId: string, versionId: string): Promise<AssetVideoPackage>;
|
|
8
|
+
createSession(assetId: string): Promise<AssetPlaybackGrant>;
|
|
9
|
+
renewSession(credential: string): Promise<AssetPlaybackGrant>;
|
|
10
|
+
revokeSession(assetId: string, sessionId: string): Promise<{
|
|
11
|
+
revoked: boolean;
|
|
12
|
+
}>;
|
|
13
|
+
captions(assetId: string, versionId: string): Promise<AssetVideoCaption[]>;
|
|
14
|
+
addCaption(assetId: string, versionId: string, input: AssetVideoCaptionInput): Promise<AssetVideoCaption>;
|
|
15
|
+
removeCaption(assetId: string, versionId: string, captionId: string): Promise<{
|
|
16
|
+
removed: boolean;
|
|
17
|
+
}>;
|
|
18
|
+
review(assetId: string, versionId: string, decision: 'approved' | 'rejected', reason: string): Promise<{
|
|
19
|
+
reviewed: boolean;
|
|
20
|
+
}>;
|
|
21
|
+
events(credential: string, events: AssetPlaybackEvent[]): Promise<{
|
|
22
|
+
accepted: boolean;
|
|
23
|
+
}>;
|
|
24
|
+
analytics(assetId: string): Promise<AssetPlaybackAnalytics>;
|
|
25
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
const assetPath = (id) => `/assets/${encodeURIComponent(id)}`;
|
|
2
|
+
const versionPath = (id, version) => `${assetPath(id)}/versions/${encodeURIComponent(version)}/video`;
|
|
3
|
+
export class AssetVideoClient {
|
|
4
|
+
client;
|
|
5
|
+
constructor(client) {
|
|
6
|
+
this.client = client;
|
|
7
|
+
}
|
|
8
|
+
workspace(assetId) { return this.client.request(`${assetPath(assetId)}/video`); }
|
|
9
|
+
process(assetId, versionId) { return this.client.request(versionPath(assetId, versionId), { method: 'POST' }); }
|
|
10
|
+
createSession(assetId) { return this.client.request(`${assetPath(assetId)}/playback-sessions`, { method: 'POST' }); }
|
|
11
|
+
renewSession(credential) { return this.client.playbackRequest('/sessions/renew', credential, {}); }
|
|
12
|
+
revokeSession(assetId, sessionId) { return this.client.request(`${assetPath(assetId)}/playback-sessions/${encodeURIComponent(sessionId)}`, { method: 'DELETE' }); }
|
|
13
|
+
captions(assetId, versionId) { return this.client.request(`${versionPath(assetId, versionId)}/captions`); }
|
|
14
|
+
addCaption(assetId, versionId, input) { return this.client.request(`${versionPath(assetId, versionId)}/captions`, { method: 'POST', body: input }); }
|
|
15
|
+
removeCaption(assetId, versionId, captionId) { return this.client.request(`${versionPath(assetId, versionId)}/captions/${encodeURIComponent(captionId)}`, { method: 'DELETE' }); }
|
|
16
|
+
review(assetId, versionId, decision, reason) { return this.client.request(`${versionPath(assetId, versionId)}/review`, { method: 'POST', body: { decision, reason } }); }
|
|
17
|
+
events(credential, events) { return this.client.playbackRequest('/events', credential, { events }); }
|
|
18
|
+
analytics(assetId) { return this.client.request(`${assetPath(assetId)}/video/analytics`); }
|
|
19
|
+
}
|
package/dist/assets.d.ts
CHANGED
|
@@ -1,4 +1,9 @@
|
|
|
1
|
+
import { AssetDAMClient } from './assets-dam.js';
|
|
2
|
+
import { AssetVideoClient } from './assets-video.js';
|
|
1
3
|
import type { StackShift } from './client.js';
|
|
4
|
+
import { AssetConnectionsClient, AssetMigrationsClient, AssetStorageClient, AssetTextClient, AssetUploadCapabilitiesClient } from './assets-platform.js';
|
|
5
|
+
import { AssetWorkflowsClient } from './asset-workflows.js';
|
|
6
|
+
import type { AssetDerivedAsset, AssetImageGenerationInput, AssetTransformDefinition, AssetTransformMaterializeInput } from './asset-media-v2.js';
|
|
2
7
|
import type { Asset, AssetBucket, AssetBucketInput, AssetAIAccountConfig, AssetPatchInput, AssetCustomDomain, AssetEvent, AssetJob, AssetJobSubmission, AssetLifecycleRule, AssetUsageSummary, AssetWebhook, AssetWebhookDelivery, AssetAIInfo, AssetCollection, AssetContentPolicy, AssetNamedTransformInput, AssetListOptions, AssetListResult, AssetSavedSearch, AssetTranscript, AssetUploadProgressCallbacks, AssetVersion, AssetSignedUploadUrl, AssetSignedUploadUrlOptions, AssetSignedTransformOptions, AssetSignedUrl, AssetSignedUrlOptions, AssetTransformOptions, AssetTransformPreset, AssetUploadOptions } from './types.js';
|
|
3
8
|
export declare const assetTransformPresets: AssetTransformPreset[];
|
|
4
9
|
export declare function getAssetTransformPreset(name: string): AssetTransformPreset | undefined;
|
|
@@ -6,6 +11,14 @@ export declare function assetTransformOptions(preset: AssetTransformPreset): Ass
|
|
|
6
11
|
export declare function assetTransformSpec(options: AssetTransformOptions): string;
|
|
7
12
|
export declare class AssetsClient {
|
|
8
13
|
private readonly client;
|
|
14
|
+
readonly dam: AssetDAMClient;
|
|
15
|
+
readonly video: AssetVideoClient;
|
|
16
|
+
readonly connections: AssetConnectionsClient;
|
|
17
|
+
readonly storage: AssetStorageClient;
|
|
18
|
+
readonly migrations: AssetMigrationsClient;
|
|
19
|
+
readonly text: AssetTextClient;
|
|
20
|
+
readonly uploadCapabilities: AssetUploadCapabilitiesClient;
|
|
21
|
+
readonly workflows: AssetWorkflowsClient;
|
|
9
22
|
constructor(client: StackShift);
|
|
10
23
|
upload(file: Blob, options?: AssetUploadOptions): Promise<Asset>;
|
|
11
24
|
list(options?: AssetListOptions): Promise<AssetListResult>;
|
|
@@ -70,7 +83,7 @@ export declare class AssetsClient {
|
|
|
70
83
|
description?: string;
|
|
71
84
|
asset_ids?: string[];
|
|
72
85
|
}): Promise<AssetCollection>;
|
|
73
|
-
updateCollection(collectionId: string, input: {
|
|
86
|
+
updateCollection(collectionId: string, revision: number, input: {
|
|
74
87
|
name: string;
|
|
75
88
|
description?: string;
|
|
76
89
|
asset_ids?: string[];
|
|
@@ -95,6 +108,13 @@ export declare class AssetsClient {
|
|
|
95
108
|
getPolicy(): Promise<AssetContentPolicy>;
|
|
96
109
|
updatePolicy(policy: AssetContentPolicy): Promise<AssetContentPolicy>;
|
|
97
110
|
createTransformation(input: AssetNamedTransformInput): Promise<unknown>;
|
|
111
|
+
createTransformationV2(name: string, definition: AssetTransformDefinition, eager?: boolean): Promise<unknown>;
|
|
112
|
+
materializeTransform(assetId: string, input: AssetTransformMaterializeInput, idempotencyKey?: string): Promise<AssetJobSubmission>;
|
|
113
|
+
derivatives(assetId: string): Promise<{
|
|
114
|
+
derivatives: AssetDerivedAsset[];
|
|
115
|
+
}>;
|
|
116
|
+
generate(input: AssetImageGenerationInput, idempotencyKey: string): Promise<AssetJobSubmission>;
|
|
117
|
+
automation(): Promise<import("./asset-workflows.js").AssetAutomationOverview>;
|
|
98
118
|
listTransformations(): Promise<unknown>;
|
|
99
119
|
deleteTransformation(name: string): Promise<{
|
|
100
120
|
deleted: boolean;
|
package/dist/assets.js
CHANGED
|
@@ -1,3 +1,8 @@
|
|
|
1
|
+
import { AssetDAMClient } from './assets-dam.js';
|
|
2
|
+
import { AssetVideoClient } from './assets-video.js';
|
|
3
|
+
import { AssetConnectionsClient, AssetMigrationsClient, AssetStorageClient, AssetTextClient, AssetUploadCapabilitiesClient } from './assets-platform.js';
|
|
4
|
+
import { AssetWorkflowsClient } from './asset-workflows.js';
|
|
5
|
+
import { ChunkedUploadController, putBlob, sha256Blob } from './upload-controller.js';
|
|
1
6
|
export const assetTransformPresets = [
|
|
2
7
|
preset('web-thumbnail', 'responsive-web', 'Compact grid thumbnail for libraries and dashboards.', 320, 180, 'fill', 'webp', 78),
|
|
3
8
|
preset('web-card', 'responsive-web', 'Balanced card image for product and content listings.', 640, 360, 'fill', 'webp', 82),
|
|
@@ -41,8 +46,24 @@ export function assetTransformSpec(options) {
|
|
|
41
46
|
}
|
|
42
47
|
export class AssetsClient {
|
|
43
48
|
client;
|
|
49
|
+
dam;
|
|
50
|
+
video;
|
|
51
|
+
connections;
|
|
52
|
+
storage;
|
|
53
|
+
migrations;
|
|
54
|
+
text;
|
|
55
|
+
uploadCapabilities;
|
|
56
|
+
workflows;
|
|
44
57
|
constructor(client) {
|
|
45
58
|
this.client = client;
|
|
59
|
+
this.dam = new AssetDAMClient(client);
|
|
60
|
+
this.video = new AssetVideoClient(client);
|
|
61
|
+
this.connections = new AssetConnectionsClient(client);
|
|
62
|
+
this.storage = new AssetStorageClient(client);
|
|
63
|
+
this.migrations = new AssetMigrationsClient(client);
|
|
64
|
+
this.text = new AssetTextClient(client);
|
|
65
|
+
this.uploadCapabilities = new AssetUploadCapabilitiesClient(client);
|
|
66
|
+
this.workflows = new AssetWorkflowsClient(client);
|
|
46
67
|
}
|
|
47
68
|
upload(file, options = {}) {
|
|
48
69
|
return this.client.request('/assets/upload', {
|
|
@@ -61,6 +82,9 @@ export class AssetsClient {
|
|
|
61
82
|
append(query, 'type', options.type);
|
|
62
83
|
append(query, 'folder', options.folder);
|
|
63
84
|
append(query, 'checksum', options.checksum);
|
|
85
|
+
append(query, 'collection_id', options.collectionId);
|
|
86
|
+
append(query, 'duplicates', options.duplicates === undefined ? undefined : String(options.duplicates));
|
|
87
|
+
append(query, 'security', options.security);
|
|
64
88
|
append(query, 'status', options.status);
|
|
65
89
|
append(query, 'scan_status', options.scanStatus);
|
|
66
90
|
append(query, 'replication_status', options.replicationStatus);
|
|
@@ -130,7 +154,13 @@ export class AssetsClient {
|
|
|
130
154
|
const target = uploadUrlOrToken.startsWith('http')
|
|
131
155
|
? `${uploadUrlOrToken.replace(/\/$/, '')}/parts/${partNumber}`
|
|
132
156
|
: `${this.originBaseUrl()}/uploads/${encodeURIComponent(uploadUrlOrToken)}/parts/${partNumber}`;
|
|
133
|
-
return
|
|
157
|
+
return putBlob(target, chunk, {
|
|
158
|
+
signal: callbacks.signal,
|
|
159
|
+
onProgress: (progress) => {
|
|
160
|
+
callbacks.onProgress?.(progress);
|
|
161
|
+
callbacks.onPartProgress?.(partNumber, progress);
|
|
162
|
+
},
|
|
163
|
+
}, { 'X-Content-SHA256': await sha256Blob(chunk) });
|
|
134
164
|
}
|
|
135
165
|
resumeUploadSession(sessionId) {
|
|
136
166
|
return this.client.request(`/assets/upload-sessions/${encodeURIComponent(sessionId)}`);
|
|
@@ -190,56 +220,22 @@ export class AssetsClient {
|
|
|
190
220
|
fileName: options.fileName ?? blobName(file),
|
|
191
221
|
mimeType: options.mimeType ?? file.type,
|
|
192
222
|
});
|
|
193
|
-
const
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
};
|
|
209
|
-
reportProgress();
|
|
210
|
-
const concurrency = boundedInteger(options.concurrency, 4, 1, 8);
|
|
211
|
-
const attempts = boundedInteger(options.retryAttempts, 4, 1, 8);
|
|
212
|
-
let next = 0;
|
|
213
|
-
const worker = async () => {
|
|
214
|
-
while (next < tasks.length) {
|
|
215
|
-
throwIfAborted(callbacks.signal);
|
|
216
|
-
const task = tasks[next++];
|
|
217
|
-
await retryWithBackoff(async () => {
|
|
218
|
-
throwIfAborted(callbacks.signal);
|
|
219
|
-
await this.uploadChunk(session.upload_session_id, task.part, task.chunk, {
|
|
220
|
-
signal: callbacks.signal,
|
|
221
|
-
onProgress: (progress) => {
|
|
222
|
-
loadedByPart.set(task.part, progress.loaded);
|
|
223
|
-
callbacks.onPartProgress?.(task.part, progress);
|
|
224
|
-
reportProgress();
|
|
225
|
-
},
|
|
226
|
-
});
|
|
227
|
-
loadedByPart.set(task.part, task.chunk.size);
|
|
228
|
-
reportProgress();
|
|
229
|
-
}, attempts, callbacks.signal);
|
|
230
|
-
}
|
|
231
|
-
};
|
|
232
|
-
try {
|
|
233
|
-
await Promise.all(Array.from({ length: Math.min(concurrency, tasks.length || 1) }, worker));
|
|
234
|
-
throwIfAborted(callbacks.signal);
|
|
235
|
-
return await retryWithBackoff(() => this.completeUploadSession(sessionId), attempts, callbacks.signal);
|
|
236
|
-
}
|
|
237
|
-
catch (error) {
|
|
238
|
-
if (isAbortError(error)) {
|
|
239
|
-
await this.cancelUploadSession(sessionId).catch(() => undefined);
|
|
240
|
-
}
|
|
241
|
-
throw error;
|
|
242
|
-
}
|
|
223
|
+
const controller = new ChunkedUploadController({
|
|
224
|
+
create: async () => toControllerSession(session),
|
|
225
|
+
resume: async (sessionId) => toControllerSession(await this.resumeUploadSession(sessionId)),
|
|
226
|
+
uploadPart: async (current, partNumber, chunk, checksum, progress) => {
|
|
227
|
+
const target = `${this.originBaseUrl()}/uploads/${encodeURIComponent(current.uploadToken)}/parts/${partNumber}`;
|
|
228
|
+
await putBlob(target, chunk, progress, { 'X-Content-SHA256': checksum });
|
|
229
|
+
},
|
|
230
|
+
complete: (current) => this.completeUploadSession(current.id),
|
|
231
|
+
cancel: (current) => this.cancelUploadSession(current.id).then(() => undefined),
|
|
232
|
+
});
|
|
233
|
+
return controller.upload(file, {
|
|
234
|
+
key: options.key ?? options.fileName ?? blobName(file) ?? 'upload',
|
|
235
|
+
fileName: options.fileName ?? blobName(file) ?? 'upload',
|
|
236
|
+
mimeType: options.mimeType ?? file.type,
|
|
237
|
+
metadata: options.metadata,
|
|
238
|
+
}, { ...callbacks, concurrency: options.concurrency, retryAttempts: options.retryAttempts, cancelOnAbort: true });
|
|
243
239
|
}
|
|
244
240
|
url(assetId, options) {
|
|
245
241
|
return `${this.client.cdnBaseUrl}/assets/${encodeURIComponent(assetId)}/tr/${encodeURIComponent(transformSpec(options))}`;
|
|
@@ -313,8 +309,8 @@ export class AssetsClient {
|
|
|
313
309
|
createCollection(input) {
|
|
314
310
|
return this.client.request('/assets/collections', { method: 'POST', body: input });
|
|
315
311
|
}
|
|
316
|
-
updateCollection(collectionId, input) {
|
|
317
|
-
return this.client.request(`/assets/collections/${encodeURIComponent(collectionId)}`, { method: 'PUT', body: input });
|
|
312
|
+
updateCollection(collectionId, revision, input) {
|
|
313
|
+
return this.client.request(`/assets/collections/${encodeURIComponent(collectionId)}`, { method: 'PUT', body: input, headers: { 'If-Match': `"${revision}"` } });
|
|
318
314
|
}
|
|
319
315
|
deleteCollection(collectionId) {
|
|
320
316
|
return this.client.request(`/assets/collections/${encodeURIComponent(collectionId)}`, { method: 'DELETE' });
|
|
@@ -346,6 +342,28 @@ export class AssetsClient {
|
|
|
346
342
|
body: input,
|
|
347
343
|
});
|
|
348
344
|
}
|
|
345
|
+
createTransformationV2(name, definition, eager = false) {
|
|
346
|
+
return this.client.request('/assets/transformations', {
|
|
347
|
+
method: 'POST', body: { name, definition, eager },
|
|
348
|
+
});
|
|
349
|
+
}
|
|
350
|
+
materializeTransform(assetId, input, idempotencyKey) {
|
|
351
|
+
return this.client.request(`/assets/${encodeURIComponent(assetId)}/transforms`, {
|
|
352
|
+
method: 'POST', body: input,
|
|
353
|
+
headers: idempotencyKey ? { 'Idempotency-Key': idempotencyKey } : undefined,
|
|
354
|
+
});
|
|
355
|
+
}
|
|
356
|
+
derivatives(assetId) {
|
|
357
|
+
return this.client.request(`/assets/${encodeURIComponent(assetId)}/derivatives`);
|
|
358
|
+
}
|
|
359
|
+
generate(input, idempotencyKey) {
|
|
360
|
+
return this.client.request('/assets/generations', {
|
|
361
|
+
method: 'POST', body: input, headers: { 'Idempotency-Key': idempotencyKey },
|
|
362
|
+
});
|
|
363
|
+
}
|
|
364
|
+
automation() {
|
|
365
|
+
return this.client.request('/assets/automation');
|
|
366
|
+
}
|
|
349
367
|
listTransformations() {
|
|
350
368
|
return this.client.request('/assets/transformations');
|
|
351
369
|
}
|
|
@@ -441,7 +459,7 @@ export class AssetsClient {
|
|
|
441
459
|
mode: 'single',
|
|
442
460
|
maxBytes: options.maxBytes ?? file.size,
|
|
443
461
|
});
|
|
444
|
-
await
|
|
462
|
+
await putBlob(session.url, file, callbacks);
|
|
445
463
|
return session;
|
|
446
464
|
}
|
|
447
465
|
}
|
|
@@ -495,82 +513,6 @@ const defaultPartSize = 5 * 1024 * 1024;
|
|
|
495
513
|
function blobName(file) {
|
|
496
514
|
return file.name;
|
|
497
515
|
}
|
|
498
|
-
function uploadBlob(url, blob, callbacks, partNumber, headers = {}) {
|
|
499
|
-
if (typeof XMLHttpRequest === 'undefined') {
|
|
500
|
-
return fetch(url, { method: 'PUT', body: blob, signal: callbacks.signal, headers }).then((response) => {
|
|
501
|
-
if (!response.ok)
|
|
502
|
-
throw new Error(`Upload failed with ${response.status}`);
|
|
503
|
-
const progress = { loaded: blob.size, total: blob.size, percent: 100 };
|
|
504
|
-
callbacks.onProgress?.(progress);
|
|
505
|
-
if (partNumber)
|
|
506
|
-
callbacks.onPartProgress?.(partNumber, progress);
|
|
507
|
-
});
|
|
508
|
-
}
|
|
509
|
-
return new Promise((resolve, reject) => {
|
|
510
|
-
const xhr = new XMLHttpRequest();
|
|
511
|
-
xhr.open('PUT', url);
|
|
512
|
-
for (const [name, value] of Object.entries(headers))
|
|
513
|
-
xhr.setRequestHeader(name, value);
|
|
514
|
-
const abort = () => xhr.abort();
|
|
515
|
-
callbacks.signal?.addEventListener('abort', abort, { once: true });
|
|
516
|
-
xhr.upload.onprogress = (event) => {
|
|
517
|
-
const total = event.total || blob.size;
|
|
518
|
-
const progress = { loaded: event.loaded, total, percent: total ? Math.round((event.loaded / total) * 100) : 0 };
|
|
519
|
-
callbacks.onProgress?.(progress);
|
|
520
|
-
if (partNumber)
|
|
521
|
-
callbacks.onPartProgress?.(partNumber, progress);
|
|
522
|
-
};
|
|
523
|
-
const cleanup = () => callbacks.signal?.removeEventListener('abort', abort);
|
|
524
|
-
xhr.onload = () => {
|
|
525
|
-
cleanup();
|
|
526
|
-
if (xhr.status >= 200 && xhr.status < 300)
|
|
527
|
-
resolve();
|
|
528
|
-
else
|
|
529
|
-
reject(new Error(`Upload failed with ${xhr.status}`));
|
|
530
|
-
};
|
|
531
|
-
xhr.onerror = () => { cleanup(); reject(new Error('Upload failed')); };
|
|
532
|
-
xhr.onabort = () => { cleanup(); reject(abortError()); };
|
|
533
|
-
xhr.send(blob);
|
|
534
|
-
});
|
|
535
|
-
}
|
|
536
|
-
async function sha256Blob(blob) {
|
|
537
|
-
if (!globalThis.crypto?.subtle)
|
|
538
|
-
throw new Error('Web Crypto SHA-256 is required for multipart uploads');
|
|
539
|
-
const digest = await globalThis.crypto.subtle.digest('SHA-256', await blob.arrayBuffer());
|
|
540
|
-
return Array.from(new Uint8Array(digest), (byte) => byte.toString(16).padStart(2, '0')).join('');
|
|
541
|
-
}
|
|
542
|
-
function boundedInteger(value, fallback, minimum, maximum) {
|
|
543
|
-
if (value === undefined)
|
|
544
|
-
return fallback;
|
|
545
|
-
if (!Number.isSafeInteger(value))
|
|
546
|
-
throw new Error('Upload concurrency and retry attempts must be integers');
|
|
547
|
-
return Math.min(maximum, Math.max(minimum, value));
|
|
548
|
-
}
|
|
549
|
-
async function retryWithBackoff(operation, attempts, signal) {
|
|
550
|
-
let lastError;
|
|
551
|
-
for (let attempt = 0; attempt < attempts; attempt += 1) {
|
|
552
|
-
try {
|
|
553
|
-
return await operation();
|
|
554
|
-
}
|
|
555
|
-
catch (error) {
|
|
556
|
-
if (isAbortError(error) || signal?.aborted)
|
|
557
|
-
throw abortError();
|
|
558
|
-
lastError = error;
|
|
559
|
-
if (attempt + 1 < attempts)
|
|
560
|
-
await abortableDelay(250 * 2 ** attempt, signal);
|
|
561
|
-
}
|
|
562
|
-
}
|
|
563
|
-
throw lastError;
|
|
564
|
-
}
|
|
565
|
-
function abortableDelay(milliseconds, signal) {
|
|
566
|
-
throwIfAborted(signal);
|
|
567
|
-
return new Promise((resolve, reject) => {
|
|
568
|
-
const timer = setTimeout(() => { cleanup(); resolve(); }, milliseconds);
|
|
569
|
-
const abort = () => { clearTimeout(timer); cleanup(); reject(abortError()); };
|
|
570
|
-
const cleanup = () => signal?.removeEventListener('abort', abort);
|
|
571
|
-
signal?.addEventListener('abort', abort, { once: true });
|
|
572
|
-
});
|
|
573
|
-
}
|
|
574
516
|
function throwIfAborted(signal) {
|
|
575
517
|
if (signal?.aborted)
|
|
576
518
|
throw abortError();
|
|
@@ -578,6 +520,17 @@ function throwIfAborted(signal) {
|
|
|
578
520
|
function abortError() {
|
|
579
521
|
return typeof DOMException === 'undefined' ? Object.assign(new Error('Upload cancelled'), { name: 'AbortError' }) : new DOMException('Upload cancelled', 'AbortError');
|
|
580
522
|
}
|
|
581
|
-
function
|
|
582
|
-
|
|
523
|
+
function toControllerSession(session) {
|
|
524
|
+
const id = session.session_id;
|
|
525
|
+
if (!id)
|
|
526
|
+
throw new Error('Chunked upload session did not include a resumable session id');
|
|
527
|
+
return {
|
|
528
|
+
id,
|
|
529
|
+
uploadToken: session.upload_session_id,
|
|
530
|
+
expiresAt: session.expires_at,
|
|
531
|
+
maxBytes: session.max_bytes,
|
|
532
|
+
partSize: session.part_size ?? defaultPartSize,
|
|
533
|
+
parts: (session.parts ?? []).map((part) => ({ partNumber: part.part_number, size: part.size, checksumSHA256: part.checksum_sha256 })),
|
|
534
|
+
resumeContext: id,
|
|
535
|
+
};
|
|
583
536
|
}
|
package/dist/client.d.ts
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
import { AssetsClient } from './assets.js';
|
|
2
2
|
import { MailClient } from './mail.js';
|
|
3
3
|
import { ProjectsClient } from './projects.js';
|
|
4
|
+
import { WorkloadSecurityClient } from './workload-security.js';
|
|
4
5
|
import type { StackShiftOptions } from './types.js';
|
|
5
6
|
export declare class StackShift {
|
|
7
|
+
readonly assetSpaceId?: string;
|
|
6
8
|
readonly apiKey: string;
|
|
7
9
|
readonly baseUrl: string;
|
|
8
10
|
readonly mailBaseUrl: string;
|
|
@@ -10,6 +12,7 @@ export declare class StackShift {
|
|
|
10
12
|
readonly projects: ProjectsClient;
|
|
11
13
|
readonly assets: AssetsClient;
|
|
12
14
|
readonly mail: MailClient;
|
|
15
|
+
readonly workloadSecurity: WorkloadSecurityClient;
|
|
13
16
|
private readonly fetcher;
|
|
14
17
|
constructor(options?: StackShiftOptions);
|
|
15
18
|
request<T>(path: string, init?: {
|
|
@@ -17,12 +20,19 @@ export declare class StackShift {
|
|
|
17
20
|
body?: unknown;
|
|
18
21
|
headers?: Record<string, string>;
|
|
19
22
|
}): Promise<T>;
|
|
23
|
+
requestRaw(path: string, init?: {
|
|
24
|
+
method?: string;
|
|
25
|
+
body?: unknown;
|
|
26
|
+
headers?: Record<string, string>;
|
|
27
|
+
}): Promise<Response>;
|
|
20
28
|
mailRequest<T>(path: string, init?: {
|
|
21
29
|
method?: string;
|
|
22
30
|
body?: unknown;
|
|
23
31
|
headers?: Record<string, string>;
|
|
24
32
|
}): Promise<T>;
|
|
33
|
+
playbackRequest<T>(path: '/sessions/renew' | '/events', credential: string, body: unknown): Promise<T>;
|
|
25
34
|
private requestFrom;
|
|
35
|
+
private fetchFrom;
|
|
26
36
|
}
|
|
27
37
|
export declare class StackShiftAPIError extends Error {
|
|
28
38
|
readonly status: number;
|