@immagin/client 0.2.2 → 0.2.5
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 +71 -49
- package/dist/index.d.mts +120 -8
- package/dist/index.mjs +51 -30
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -17,12 +17,12 @@ import { Immagin } from '@immagin/client'
|
|
|
17
17
|
|
|
18
18
|
const client = new Immagin({ apiKey: 'imk_...' })
|
|
19
19
|
|
|
20
|
-
//
|
|
21
|
-
const url = await client.images.url('photos/hero.jpg',
|
|
22
|
-
|
|
23
|
-
|
|
20
|
+
// Generate a signed image URL (auto-fetches tenant credentials)
|
|
21
|
+
const url = await client.images.url('photos/hero.jpg', [
|
|
22
|
+
{ resize: { width: 800, height: 600 } },
|
|
23
|
+
])
|
|
24
24
|
|
|
25
|
-
// Upload an image
|
|
25
|
+
// Upload an image (auto-detects MIME type)
|
|
26
26
|
import { readFileSync } from 'node:fs'
|
|
27
27
|
const buffer = readFileSync('photo.jpg')
|
|
28
28
|
await client.images.upload(buffer, 'photos/hero.jpg')
|
|
@@ -30,62 +30,65 @@ await client.images.upload(buffer, 'photos/hero.jpg')
|
|
|
30
30
|
|
|
31
31
|
## Images
|
|
32
32
|
|
|
33
|
-
###
|
|
33
|
+
### Generate image URLs
|
|
34
34
|
|
|
35
|
-
|
|
35
|
+
Generate signed image URLs for serving processed images. All URLs are automatically signed — the signature locks transformation parameters so they can't be tampered with.
|
|
36
36
|
|
|
37
37
|
```ts
|
|
38
|
+
// Original image
|
|
38
39
|
const url = await client.images.url('photo.jpg')
|
|
39
|
-
```
|
|
40
|
-
|
|
41
|
-
With transformations:
|
|
42
|
-
|
|
43
|
-
```ts
|
|
44
|
-
const url = await client.images.url('photo.jpg', {
|
|
45
|
-
size: [400, 300],
|
|
46
|
-
text: {
|
|
47
|
-
text: 'Hello',
|
|
48
|
-
position: 'bottom-right', // top-left, top-right, bottom-left, bottom-right, center
|
|
49
|
-
fontSize: 24,
|
|
50
|
-
color: '#ffffff',
|
|
51
|
-
opacity: 0.8,
|
|
52
|
-
},
|
|
53
|
-
})
|
|
54
|
-
```
|
|
55
|
-
|
|
56
|
-
### Sign URLs locally (without API call)
|
|
57
|
-
|
|
58
|
-
Generate signed image URLs on the server without making an API request. Requires `tenantId` and `tenantSecret` in the constructor.
|
|
59
|
-
|
|
60
|
-
```ts
|
|
61
|
-
const client = new Immagin({
|
|
62
|
-
apiKey: 'imk_...',
|
|
63
|
-
tenantId: 'your-tenant-id',
|
|
64
|
-
tenantSecret: 'your-tenant-secret',
|
|
65
|
-
})
|
|
66
|
-
|
|
67
|
-
// Synchronous - no network request
|
|
68
|
-
const url = client.images.signedUrl('photo.jpg')
|
|
69
40
|
|
|
70
41
|
// With transformations
|
|
71
|
-
const thumb = client.images.
|
|
72
|
-
|
|
73
|
-
text: { text: '© My Company', position: 'bottom-right', opacity: 0.5 },
|
|
74
|
-
|
|
42
|
+
const thumb = await client.images.url('photo.jpg', [
|
|
43
|
+
{ resize: { width: 800, height: 600 } },
|
|
44
|
+
{ text: { text: '© My Company', position: 'bottom-right', opacity: 0.5 } },
|
|
45
|
+
])
|
|
46
|
+
|
|
47
|
+
// Rotate, blur, grayscale
|
|
48
|
+
const edited = await client.images.url('photo.jpg', [
|
|
49
|
+
{ rotate: { angle: 90 } },
|
|
50
|
+
{ blur: 3 },
|
|
51
|
+
{ grayscale: true },
|
|
52
|
+
])
|
|
53
|
+
|
|
54
|
+
// Crop a region
|
|
55
|
+
const cropped = await client.images.url('photo.jpg', [
|
|
56
|
+
{ crop: { left: 100, top: 50, width: 400, height: 300 } },
|
|
57
|
+
])
|
|
58
|
+
|
|
59
|
+
// Flip, sharpen, adjust colors
|
|
60
|
+
const adjusted = await client.images.url('photo.jpg', [
|
|
61
|
+
{ flip: true },
|
|
62
|
+
{ sharpen: 2 },
|
|
63
|
+
{ modulate: { brightness: 1.2, saturation: 0.8 } },
|
|
64
|
+
])
|
|
65
|
+
|
|
66
|
+
// Disable auto-orient (enabled by default)
|
|
67
|
+
const raw = await client.images.url('photo.jpg', [
|
|
68
|
+
{ autoOrient: false },
|
|
69
|
+
{ resize: { width: 800 } },
|
|
70
|
+
])
|
|
71
|
+
|
|
72
|
+
// Custom output format (default is WebP at quality 90)
|
|
73
|
+
const jpeg = await client.images.url(
|
|
74
|
+
'photo.jpg',
|
|
75
|
+
[{ resize: { width: 800 } }],
|
|
76
|
+
{ format: 'jpeg', quality: 85 },
|
|
77
|
+
)
|
|
75
78
|
```
|
|
76
79
|
|
|
77
|
-
> **Note:**
|
|
80
|
+
> **Note:** `url()` uses `node:crypto` for signing and is intended for server-side use only. Tenant credentials are auto-fetched and cached on initialization.
|
|
78
81
|
|
|
79
|
-
### Get a
|
|
82
|
+
### Get a signed upload URL
|
|
80
83
|
|
|
81
|
-
Returns a
|
|
84
|
+
Returns a CloudFront signed URL for direct upload (expires in 5 minutes). Useful for browser uploads where you don't want to expose your API key. The URL points to a CloudFront distribution (not S3 directly), so the underlying infrastructure is not exposed.
|
|
82
85
|
|
|
83
86
|
```ts
|
|
84
|
-
// Server: get the
|
|
87
|
+
// Server: get the signed upload URL
|
|
85
88
|
const { uploadUrl, key } = await client.images.signUrl('photos/hero.jpg')
|
|
86
89
|
// Return uploadUrl to the browser
|
|
87
90
|
|
|
88
|
-
// Browser: upload directly
|
|
91
|
+
// Browser: upload directly
|
|
89
92
|
await fetch(uploadUrl, {
|
|
90
93
|
method: 'PUT',
|
|
91
94
|
body: file,
|
|
@@ -94,7 +97,7 @@ await fetch(uploadUrl, {
|
|
|
94
97
|
|
|
95
98
|
### Upload (Node.js)
|
|
96
99
|
|
|
97
|
-
Convenience method that gets a signed URL and uploads in one call. The file goes directly to S3 and never passes through the API.
|
|
100
|
+
Convenience method that gets a signed URL and uploads in one call. The file goes directly to CloudFront/S3 and never passes through the API. MIME type is auto-detected from file bytes (magic bytes for JPEG, PNG, GIF, WebP, BMP, AVIF, HEIC, HEIF) or file extension.
|
|
98
101
|
|
|
99
102
|
```ts
|
|
100
103
|
import { readFileSync } from 'node:fs'
|
|
@@ -123,6 +126,27 @@ const page = await client.images.list({
|
|
|
123
126
|
await client.images.delete('uploads/photo.jpg')
|
|
124
127
|
```
|
|
125
128
|
|
|
129
|
+
### Metadata
|
|
130
|
+
|
|
131
|
+
Inspect image properties (dimensions, format, color space, etc.) without downloading the full image. The request goes through Luna, which extracts metadata using Sharp.
|
|
132
|
+
|
|
133
|
+
```ts
|
|
134
|
+
const meta = await client.images.metadata('photo.jpg')
|
|
135
|
+
// {
|
|
136
|
+
// width: 1920,
|
|
137
|
+
// height: 1080,
|
|
138
|
+
// format: 'jpeg',
|
|
139
|
+
// space: 'srgb',
|
|
140
|
+
// channels: 3,
|
|
141
|
+
// hasAlpha: false,
|
|
142
|
+
// density: 72,
|
|
143
|
+
// isProgressive: false,
|
|
144
|
+
// size: 204800,
|
|
145
|
+
// }
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
> **Note:** `metadata()` uses `node:crypto` for signing and is intended for server-side use only.
|
|
149
|
+
|
|
126
150
|
## API Keys
|
|
127
151
|
|
|
128
152
|
Manage API keys programmatically.
|
|
@@ -171,8 +195,6 @@ try {
|
|
|
171
195
|
const client = new Immagin({
|
|
172
196
|
apiKey: 'imk_...', // Required
|
|
173
197
|
baseUrl: 'https://...', // Optional, defaults to https://gateway.immag.in
|
|
174
|
-
tenantId: 'your-tenant-id', // Optional, required for signedUrl()
|
|
175
|
-
tenantSecret: 'your-secret', // Optional, required for signedUrl()
|
|
176
198
|
})
|
|
177
199
|
```
|
|
178
200
|
|
package/dist/index.d.mts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
//#region src/types.d.ts
|
|
2
|
-
type TextPosition = 'top-left' | 'top-right' | '
|
|
2
|
+
type TextPosition = 'top-left' | 'top-center' | 'top-right' | 'center-left' | 'center' | 'center-right' | 'bottom-left' | 'bottom-center' | 'bottom-right';
|
|
3
3
|
interface TextOverlay {
|
|
4
4
|
text: string;
|
|
5
5
|
fontSize?: number;
|
|
@@ -8,11 +8,113 @@ interface TextOverlay {
|
|
|
8
8
|
position?: TextPosition;
|
|
9
9
|
padding?: number;
|
|
10
10
|
}
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
11
|
+
type ResizeFit = 'cover' | 'contain' | 'fill' | 'inside' | 'outside';
|
|
12
|
+
type ResizePosition = 'top' | 'right top' | 'right' | 'right bottom' | 'bottom' | 'left bottom' | 'left' | 'left top' | 'centre' | 'center' | 'entropy' | 'attention';
|
|
13
|
+
type ResizeKernel = 'nearest' | 'cubic' | 'mitchell' | 'lanczos2' | 'lanczos3';
|
|
14
|
+
interface ResizeOptions {
|
|
15
|
+
width: number;
|
|
16
|
+
height?: number;
|
|
17
|
+
fit?: ResizeFit;
|
|
18
|
+
position?: ResizePosition;
|
|
19
|
+
kernel?: ResizeKernel;
|
|
20
|
+
background?: string;
|
|
21
|
+
upscale?: boolean;
|
|
22
|
+
downscale?: boolean;
|
|
23
|
+
}
|
|
24
|
+
interface RotateOptions {
|
|
25
|
+
angle: number;
|
|
26
|
+
background?: string;
|
|
27
|
+
}
|
|
28
|
+
interface CropOptions {
|
|
29
|
+
left: number;
|
|
30
|
+
top: number;
|
|
31
|
+
width: number;
|
|
32
|
+
height: number;
|
|
33
|
+
}
|
|
34
|
+
interface ExtendOptions {
|
|
35
|
+
top?: number;
|
|
36
|
+
bottom?: number;
|
|
37
|
+
left?: number;
|
|
38
|
+
right?: number;
|
|
39
|
+
background?: string;
|
|
40
|
+
}
|
|
41
|
+
interface ModulateOptions {
|
|
42
|
+
brightness?: number;
|
|
43
|
+
saturation?: number;
|
|
44
|
+
hue?: number;
|
|
45
|
+
lightness?: number;
|
|
46
|
+
}
|
|
47
|
+
interface FlattenOptions {
|
|
48
|
+
background?: string;
|
|
49
|
+
}
|
|
50
|
+
interface TintOptions {
|
|
51
|
+
r: number;
|
|
52
|
+
g: number;
|
|
53
|
+
b: number;
|
|
54
|
+
}
|
|
55
|
+
interface NormalizeOptions {
|
|
56
|
+
lower?: number;
|
|
57
|
+
upper?: number;
|
|
58
|
+
}
|
|
59
|
+
interface TrimOptions {
|
|
60
|
+
background?: string;
|
|
61
|
+
threshold?: number;
|
|
62
|
+
}
|
|
63
|
+
type ImageOperation = {
|
|
64
|
+
resize: ResizeOptions;
|
|
65
|
+
} | {
|
|
66
|
+
text: TextOverlay;
|
|
67
|
+
} | {
|
|
68
|
+
rotate: RotateOptions;
|
|
69
|
+
} | {
|
|
70
|
+
flip: true;
|
|
71
|
+
} | {
|
|
72
|
+
flop: true;
|
|
73
|
+
} | {
|
|
74
|
+
blur: number;
|
|
75
|
+
} | {
|
|
76
|
+
sharpen: number;
|
|
77
|
+
} | {
|
|
78
|
+
grayscale: true;
|
|
79
|
+
} | {
|
|
80
|
+
crop: CropOptions;
|
|
81
|
+
} | {
|
|
82
|
+
extend: ExtendOptions;
|
|
83
|
+
} | {
|
|
84
|
+
modulate: ModulateOptions;
|
|
85
|
+
} | {
|
|
86
|
+
flatten: FlattenOptions;
|
|
87
|
+
} | {
|
|
88
|
+
tint: TintOptions;
|
|
89
|
+
} | {
|
|
90
|
+
invert: boolean;
|
|
91
|
+
} | {
|
|
92
|
+
normalize: NormalizeOptions;
|
|
93
|
+
} | {
|
|
94
|
+
trim: TrimOptions;
|
|
95
|
+
} | {
|
|
96
|
+
autoOrient: boolean;
|
|
97
|
+
};
|
|
98
|
+
type OutputFormat = 'webp' | 'jpeg' | 'png' | 'gif' | 'jp2' | 'tiff' | 'avif' | 'heif';
|
|
99
|
+
interface OutputOptions {
|
|
100
|
+
format?: OutputFormat;
|
|
101
|
+
quality?: number;
|
|
102
|
+
progressive?: boolean;
|
|
103
|
+
lossless?: boolean;
|
|
104
|
+
}
|
|
105
|
+
interface ImageMetadata {
|
|
106
|
+
width: number;
|
|
107
|
+
height: number;
|
|
108
|
+
format: string;
|
|
109
|
+
space: string;
|
|
110
|
+
channels: number;
|
|
111
|
+
hasAlpha: boolean;
|
|
112
|
+
orientation?: number;
|
|
113
|
+
density?: number;
|
|
114
|
+
isProgressive?: boolean;
|
|
115
|
+
pages?: number;
|
|
116
|
+
size: number;
|
|
14
117
|
}
|
|
15
|
-
interface ImageUrlOptions extends ImageEdits {}
|
|
16
118
|
interface UploadResult {
|
|
17
119
|
uploadUrl: string;
|
|
18
120
|
key: string;
|
|
@@ -43,6 +145,10 @@ interface CreateKeyResult {
|
|
|
43
145
|
prefix: string;
|
|
44
146
|
name: string;
|
|
45
147
|
}
|
|
148
|
+
interface ProjectInfo {
|
|
149
|
+
tenantId: string;
|
|
150
|
+
tenantSecret: string;
|
|
151
|
+
}
|
|
46
152
|
interface ImmaginConfig {
|
|
47
153
|
apiKey: string;
|
|
48
154
|
baseUrl?: string;
|
|
@@ -54,12 +160,12 @@ interface ImmaginConfig {
|
|
|
54
160
|
declare class ImagesResource {
|
|
55
161
|
private client;
|
|
56
162
|
constructor(client: Immagin);
|
|
57
|
-
|
|
58
|
-
url(key: string, options?: ImageUrlOptions): Promise<string>;
|
|
163
|
+
url(key: string, edits?: ImageOperation | ImageOperation[], output?: OutputOptions): Promise<string>;
|
|
59
164
|
signUrl(key: string): Promise<UploadResult>;
|
|
60
165
|
upload(file: Blob | Buffer | ReadableStream, key: string): Promise<UploadResult>;
|
|
61
166
|
list(options?: ListImagesOptions): Promise<ListImagesResult>;
|
|
62
167
|
delete(key: string): Promise<void>;
|
|
168
|
+
metadata(key: string): Promise<ImageMetadata>;
|
|
63
169
|
}
|
|
64
170
|
//#endregion
|
|
65
171
|
//#region src/resources/keys.d.ts
|
|
@@ -81,10 +187,16 @@ declare class Immagin {
|
|
|
81
187
|
tenantId?: string;
|
|
82
188
|
/** @internal */
|
|
83
189
|
tenantSecret?: string;
|
|
190
|
+
private _tenantInfoPromise?;
|
|
84
191
|
images: ImagesResource;
|
|
85
192
|
keys: KeysResource;
|
|
86
193
|
constructor(config: ImmaginConfig);
|
|
87
194
|
/** @internal */
|
|
195
|
+
resolveTenantInfo(): Promise<{
|
|
196
|
+
tenantId: string;
|
|
197
|
+
tenantSecret: string;
|
|
198
|
+
}>;
|
|
199
|
+
/** @internal */
|
|
88
200
|
request<T>(method: string, path: string, options?: {
|
|
89
201
|
body?: unknown;
|
|
90
202
|
params?: Record<string, string>;
|
|
@@ -98,4 +210,4 @@ declare class ImmaginError extends Error {
|
|
|
98
210
|
constructor(message: string, status: number, body?: unknown | undefined);
|
|
99
211
|
}
|
|
100
212
|
//#endregion
|
|
101
|
-
export { type ApiKey, type CreateKeyResult, type
|
|
213
|
+
export { type ApiKey, type CreateKeyResult, type CropOptions, type ExtendOptions, type FlattenOptions, type ImageEntry, type ImageMetadata, type ImageOperation, Immagin, type ImmaginConfig, ImmaginError, type ListImagesOptions, type ListImagesResult, type ModulateOptions, type NormalizeOptions, type OutputFormat, type OutputOptions, type ProjectInfo, type ResizeFit, type ResizeKernel, type ResizeOptions, type ResizePosition, type RotateOptions, type TextOverlay, type TextPosition, type TintOptions, type TrimOptions, type UploadResult };
|
package/dist/index.mjs
CHANGED
|
@@ -1,5 +1,16 @@
|
|
|
1
1
|
import { createHmac } from "node:crypto";
|
|
2
2
|
|
|
3
|
+
//#region src/errors.ts
|
|
4
|
+
var ImmaginError = class extends Error {
|
|
5
|
+
constructor(message, status, body) {
|
|
6
|
+
super(message);
|
|
7
|
+
this.status = status;
|
|
8
|
+
this.body = body;
|
|
9
|
+
this.name = "ImmaginError";
|
|
10
|
+
}
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
//#endregion
|
|
3
14
|
//#region src/resources/images.ts
|
|
4
15
|
const MIME_TYPES = {
|
|
5
16
|
jpg: "image/jpeg",
|
|
@@ -42,29 +53,17 @@ var ImagesResource = class {
|
|
|
42
53
|
constructor(client) {
|
|
43
54
|
this.client = client;
|
|
44
55
|
}
|
|
45
|
-
|
|
46
|
-
const { tenantId, tenantSecret } = this.client;
|
|
47
|
-
if (!tenantId || !tenantSecret) throw new Error("tenantId and tenantSecret are required for signedUrl(). Pass them in the Immagin constructor.");
|
|
56
|
+
async url(key, edits, output) {
|
|
57
|
+
const { tenantId, tenantSecret } = await this.client.resolveTenantInfo();
|
|
48
58
|
const payload = { key };
|
|
49
|
-
if (edits
|
|
59
|
+
if (edits) {
|
|
60
|
+
const editsArray = Array.isArray(edits) ? edits : [edits];
|
|
61
|
+
if (editsArray.length > 0) payload.edits = editsArray;
|
|
62
|
+
}
|
|
63
|
+
if (output) payload.output = output;
|
|
50
64
|
const base64 = Buffer.from(JSON.stringify(payload)).toString("base64");
|
|
51
65
|
return `https://${tenantId}.immag.in/${base64}?sig=${createHmac("sha256", tenantSecret).update(base64).digest("hex").slice(0, 16)}`;
|
|
52
66
|
}
|
|
53
|
-
async url(key, options) {
|
|
54
|
-
const params = { key };
|
|
55
|
-
if (options?.size) {
|
|
56
|
-
params.width = String(options.size[0]);
|
|
57
|
-
if (options.size.length === 2) params.height = String(options.size[1]);
|
|
58
|
-
}
|
|
59
|
-
if (options?.text) {
|
|
60
|
-
params["text.text"] = options.text.text;
|
|
61
|
-
if (options.text.position) params["text.position"] = options.text.position;
|
|
62
|
-
if (options.text.fontSize) params["text.fontSize"] = String(options.text.fontSize);
|
|
63
|
-
if (options.text.opacity) params["text.opacity"] = String(options.text.opacity);
|
|
64
|
-
if (options.text.color) params["text.color"] = options.text.color;
|
|
65
|
-
}
|
|
66
|
-
return (await this.client.request("GET", "/v1/images/url", { params })).url;
|
|
67
|
-
}
|
|
68
67
|
async signUrl(key) {
|
|
69
68
|
const contentType = mimeFromKey(key);
|
|
70
69
|
return this.client.request("POST", "/v1/images/sign-url", { body: contentType ? {
|
|
@@ -95,6 +94,21 @@ var ImagesResource = class {
|
|
|
95
94
|
async delete(key) {
|
|
96
95
|
await this.client.request("DELETE", `/v1/images/${key}`);
|
|
97
96
|
}
|
|
97
|
+
async metadata(key) {
|
|
98
|
+
const { tenantId, tenantSecret } = await this.client.resolveTenantInfo();
|
|
99
|
+
const payload = JSON.stringify({
|
|
100
|
+
key,
|
|
101
|
+
metadata: true
|
|
102
|
+
});
|
|
103
|
+
const base64 = Buffer.from(payload).toString("base64");
|
|
104
|
+
const url = `https://${tenantId}.immag.in/${base64}?sig=${createHmac("sha256", tenantSecret).update(base64).digest("hex").slice(0, 16)}`;
|
|
105
|
+
const response = await fetch(url);
|
|
106
|
+
if (!response.ok) {
|
|
107
|
+
const body = await response.text();
|
|
108
|
+
throw new ImmaginError(`HTTP ${response.status}`, response.status, body);
|
|
109
|
+
}
|
|
110
|
+
return response.json();
|
|
111
|
+
}
|
|
98
112
|
};
|
|
99
113
|
|
|
100
114
|
//#endregion
|
|
@@ -114,17 +128,6 @@ var KeysResource = class {
|
|
|
114
128
|
}
|
|
115
129
|
};
|
|
116
130
|
|
|
117
|
-
//#endregion
|
|
118
|
-
//#region src/errors.ts
|
|
119
|
-
var ImmaginError = class extends Error {
|
|
120
|
-
constructor(message, status, body) {
|
|
121
|
-
super(message);
|
|
122
|
-
this.status = status;
|
|
123
|
-
this.body = body;
|
|
124
|
-
this.name = "ImmaginError";
|
|
125
|
-
}
|
|
126
|
-
};
|
|
127
|
-
|
|
128
131
|
//#endregion
|
|
129
132
|
//#region src/client.ts
|
|
130
133
|
const DEFAULT_BASE_URL = "https://gateway.immag.in";
|
|
@@ -135,6 +138,7 @@ var Immagin = class {
|
|
|
135
138
|
tenantId;
|
|
136
139
|
/** @internal */
|
|
137
140
|
tenantSecret;
|
|
141
|
+
_tenantInfoPromise;
|
|
138
142
|
images;
|
|
139
143
|
keys;
|
|
140
144
|
constructor(config) {
|
|
@@ -144,6 +148,23 @@ var Immagin = class {
|
|
|
144
148
|
this.tenantSecret = config.tenantSecret;
|
|
145
149
|
this.images = new ImagesResource(this);
|
|
146
150
|
this.keys = new KeysResource(this);
|
|
151
|
+
if (!this.tenantId || !this.tenantSecret) this.resolveTenantInfo().catch(() => {});
|
|
152
|
+
}
|
|
153
|
+
/** @internal */
|
|
154
|
+
async resolveTenantInfo() {
|
|
155
|
+
if (this.tenantId && this.tenantSecret) return {
|
|
156
|
+
tenantId: this.tenantId,
|
|
157
|
+
tenantSecret: this.tenantSecret
|
|
158
|
+
};
|
|
159
|
+
if (!this._tenantInfoPromise) this._tenantInfoPromise = this.request("GET", "/v1/project").then((info) => {
|
|
160
|
+
this.tenantId = info.tenantId;
|
|
161
|
+
this.tenantSecret = info.tenantSecret;
|
|
162
|
+
return info;
|
|
163
|
+
}).catch((err) => {
|
|
164
|
+
this._tenantInfoPromise = void 0;
|
|
165
|
+
throw err;
|
|
166
|
+
});
|
|
167
|
+
return this._tenantInfoPromise;
|
|
147
168
|
}
|
|
148
169
|
/** @internal */
|
|
149
170
|
async request(method, path, options) {
|