sharetribe-flex-build-sdk 1.15.0 → 1.15.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/package.json +1 -1
- package/src/api/client.ts +6 -8
- package/src/assets.ts +41 -2
- package/src/config.ts +58 -0
- package/src/index.ts +10 -0
- package/test/assets.test.ts +308 -0
package/package.json
CHANGED
package/src/api/client.ts
CHANGED
|
@@ -8,13 +8,11 @@ import { get, post, del, request, postTransit, HttpResponse } from './http-clien
|
|
|
8
8
|
import { createMultipartBody, MultipartField } from './multipart.js';
|
|
9
9
|
import { encodeTransit, decodeTransit } from './transit.js';
|
|
10
10
|
import { readAuth } from '../auth-storage.js';
|
|
11
|
+
import { getApiBaseUrl } from '../config.js';
|
|
11
12
|
|
|
12
13
|
// Re-export MultipartField for use in commands
|
|
13
14
|
export type { MultipartField };
|
|
14
15
|
|
|
15
|
-
// API base URL - must match flex-cli exactly
|
|
16
|
-
const API_BASE_URL = 'https://flex-build-api.sharetribe.com/v1/build-api';
|
|
17
|
-
|
|
18
16
|
export interface ApiError {
|
|
19
17
|
code: string;
|
|
20
18
|
message: string;
|
|
@@ -81,7 +79,7 @@ function handleResponse<T>(response: HttpResponse): T {
|
|
|
81
79
|
* @returns API response
|
|
82
80
|
*/
|
|
83
81
|
export async function apiGet<T>(apiKey: string | undefined, endpoint: string, queryParams?: Record<string, string>): Promise<T> {
|
|
84
|
-
const url = new URL(
|
|
82
|
+
const url = new URL(getApiBaseUrl() + endpoint);
|
|
85
83
|
if (queryParams) {
|
|
86
84
|
Object.entries(queryParams).forEach(([key, value]) => {
|
|
87
85
|
url.searchParams.append(key, value);
|
|
@@ -107,7 +105,7 @@ export async function apiPost<T>(
|
|
|
107
105
|
queryParams?: Record<string, string>,
|
|
108
106
|
body?: unknown
|
|
109
107
|
): Promise<T> {
|
|
110
|
-
const url = new URL(
|
|
108
|
+
const url = new URL(getApiBaseUrl() + endpoint);
|
|
111
109
|
if (queryParams) {
|
|
112
110
|
Object.entries(queryParams).forEach(([key, value]) => {
|
|
113
111
|
url.searchParams.append(key, value);
|
|
@@ -131,7 +129,7 @@ export async function apiDelete<T>(
|
|
|
131
129
|
endpoint: string,
|
|
132
130
|
queryParams?: Record<string, string>
|
|
133
131
|
): Promise<T> {
|
|
134
|
-
const url = new URL(
|
|
132
|
+
const url = new URL(getApiBaseUrl() + endpoint);
|
|
135
133
|
if (queryParams) {
|
|
136
134
|
Object.entries(queryParams).forEach(([key, value]) => {
|
|
137
135
|
url.searchParams.append(key, value);
|
|
@@ -157,7 +155,7 @@ export async function apiPostMultipart<T>(
|
|
|
157
155
|
queryParams: Record<string, string>,
|
|
158
156
|
fields: MultipartField[]
|
|
159
157
|
): Promise<T> {
|
|
160
|
-
const url = new URL(
|
|
158
|
+
const url = new URL(getApiBaseUrl() + endpoint);
|
|
161
159
|
Object.entries(queryParams).forEach(([key, value]) => {
|
|
162
160
|
url.searchParams.append(key, value);
|
|
163
161
|
});
|
|
@@ -194,7 +192,7 @@ export async function apiPostTransit<T>(
|
|
|
194
192
|
queryParams: Record<string, string>,
|
|
195
193
|
body: unknown
|
|
196
194
|
): Promise<T> {
|
|
197
|
-
const url = new URL(
|
|
195
|
+
const url = new URL(getApiBaseUrl() + endpoint);
|
|
198
196
|
Object.entries(queryParams).forEach(([key, value]) => {
|
|
199
197
|
url.searchParams.append(key, value);
|
|
200
198
|
});
|
package/src/assets.ts
CHANGED
|
@@ -22,6 +22,10 @@ export interface PushAssetsResult {
|
|
|
22
22
|
assets: Array<{ path: string; contentHash: string }>;
|
|
23
23
|
}
|
|
24
24
|
|
|
25
|
+
export interface StageAssetResult {
|
|
26
|
+
stagingId: string;
|
|
27
|
+
}
|
|
28
|
+
|
|
25
29
|
/**
|
|
26
30
|
* Pulls assets from remote
|
|
27
31
|
*
|
|
@@ -67,6 +71,34 @@ export async function pullAssets(
|
|
|
67
71
|
};
|
|
68
72
|
}
|
|
69
73
|
|
|
74
|
+
/**
|
|
75
|
+
* Stages an asset for upload
|
|
76
|
+
*
|
|
77
|
+
* @param apiKey - Sharetribe API key (optional, reads from auth file if not provided)
|
|
78
|
+
* @param marketplace - Marketplace ID
|
|
79
|
+
* @param fileData - File data as Buffer
|
|
80
|
+
* @param filename - Filename
|
|
81
|
+
* @returns Staging ID
|
|
82
|
+
*/
|
|
83
|
+
export async function stageAsset(
|
|
84
|
+
apiKey: string | undefined,
|
|
85
|
+
marketplace: string,
|
|
86
|
+
fileData: Buffer,
|
|
87
|
+
filename: string
|
|
88
|
+
): Promise<StageAssetResult> {
|
|
89
|
+
const fields: MultipartField[] = [
|
|
90
|
+
{ name: 'file', value: fileData, filename },
|
|
91
|
+
];
|
|
92
|
+
|
|
93
|
+
const response = await apiPostMultipart<{
|
|
94
|
+
data: { 'staging-id': string };
|
|
95
|
+
}>(apiKey, '/assets/stage', { marketplace }, fields);
|
|
96
|
+
|
|
97
|
+
return {
|
|
98
|
+
stagingId: response.data['staging-id'],
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
|
|
70
102
|
/**
|
|
71
103
|
* Pushes assets to remote
|
|
72
104
|
*
|
|
@@ -84,6 +116,8 @@ export async function pushAssets(
|
|
|
84
116
|
path: string;
|
|
85
117
|
op: 'upsert' | 'delete';
|
|
86
118
|
data?: Buffer;
|
|
119
|
+
stagingId?: string;
|
|
120
|
+
filename?: string;
|
|
87
121
|
}>
|
|
88
122
|
): Promise<PushAssetsResult> {
|
|
89
123
|
const fields: MultipartField[] = [
|
|
@@ -94,8 +128,13 @@ export async function pushAssets(
|
|
|
94
128
|
const op = operations[i];
|
|
95
129
|
fields.push({ name: `path-${i}`, value: op.path });
|
|
96
130
|
fields.push({ name: `op-${i}`, value: op.op });
|
|
97
|
-
if (op.op === 'upsert'
|
|
98
|
-
|
|
131
|
+
if (op.op === 'upsert') {
|
|
132
|
+
if (op.stagingId) {
|
|
133
|
+
fields.push({ name: `staging-id-${i}`, value: String(op.stagingId) });
|
|
134
|
+
} else if (op.data) {
|
|
135
|
+
const fname = op.filename || op.path;
|
|
136
|
+
fields.push({ name: `data-raw-${i}`, value: op.data, filename: fname });
|
|
137
|
+
}
|
|
99
138
|
}
|
|
100
139
|
}
|
|
101
140
|
|
package/src/config.ts
ADDED
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Configuration helpers matching flex-cli behavior.
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { existsSync, readFileSync } from 'node:fs';
|
|
6
|
+
import { join } from 'node:path';
|
|
7
|
+
import edn from 'jsedn';
|
|
8
|
+
|
|
9
|
+
const ENV_FILE = '.env.edn';
|
|
10
|
+
const DEFAULT_API_BASE_URL = 'https://flex-build-api.sharetribe.com/v1/build-api';
|
|
11
|
+
|
|
12
|
+
let envFileContent: any | null | undefined;
|
|
13
|
+
|
|
14
|
+
function readEnvFile(): any | null {
|
|
15
|
+
const envPath = join(process.cwd(), ENV_FILE);
|
|
16
|
+
if (!existsSync(envPath)) {
|
|
17
|
+
return null;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
try {
|
|
21
|
+
const content = readFileSync(envPath, 'utf-8');
|
|
22
|
+
return edn.parse(content);
|
|
23
|
+
} catch {
|
|
24
|
+
return null;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function getEnvValue(name: string): string | undefined {
|
|
29
|
+
const envValue = process.env[name];
|
|
30
|
+
if (envValue) {
|
|
31
|
+
return envValue;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
if (envFileContent === undefined) {
|
|
35
|
+
envFileContent = readEnvFile();
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
if (envFileContent && typeof envFileContent.at === 'function') {
|
|
39
|
+
const value = envFileContent.at(name);
|
|
40
|
+
if (typeof value === 'string') {
|
|
41
|
+
return value;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
return undefined;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export function getApiBaseUrl(): string {
|
|
49
|
+
return getEnvValue('FLEX_API_BASE_URL') || DEFAULT_API_BASE_URL;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export function getConfigMap(): Record<string, string> {
|
|
53
|
+
return {
|
|
54
|
+
'api-base-url': getApiBaseUrl(),
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
export { DEFAULT_API_BASE_URL };
|
package/src/index.ts
CHANGED
|
@@ -64,6 +64,7 @@ export {
|
|
|
64
64
|
|
|
65
65
|
// Export authentication functions
|
|
66
66
|
export {
|
|
67
|
+
readAuth,
|
|
67
68
|
writeAuth,
|
|
68
69
|
clearAuth,
|
|
69
70
|
type AuthData,
|
|
@@ -73,9 +74,11 @@ export {
|
|
|
73
74
|
export {
|
|
74
75
|
pullAssets,
|
|
75
76
|
pushAssets,
|
|
77
|
+
stageAsset,
|
|
76
78
|
type Asset,
|
|
77
79
|
type PullAssetsResult,
|
|
78
80
|
type PushAssetsResult,
|
|
81
|
+
type StageAssetResult,
|
|
79
82
|
} from './assets.js';
|
|
80
83
|
|
|
81
84
|
// Export notification management functions
|
|
@@ -99,3 +102,10 @@ export {
|
|
|
99
102
|
SUPPORTED_STRIPE_VERSIONS,
|
|
100
103
|
type StripeApiVersion,
|
|
101
104
|
} from './stripe.js';
|
|
105
|
+
|
|
106
|
+
// Export configuration helpers
|
|
107
|
+
export {
|
|
108
|
+
getApiBaseUrl,
|
|
109
|
+
getConfigMap,
|
|
110
|
+
DEFAULT_API_BASE_URL,
|
|
111
|
+
} from './config.js';
|
|
@@ -0,0 +1,308 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tests for asset management SDK functions
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
|
6
|
+
import { stageAsset, pushAssets } from '../src/assets.js';
|
|
7
|
+
import * as client from '../src/api/client.js';
|
|
8
|
+
|
|
9
|
+
// Mock the API client
|
|
10
|
+
vi.mock('../src/api/client.js', () => ({
|
|
11
|
+
apiPostMultipart: vi.fn(),
|
|
12
|
+
}));
|
|
13
|
+
|
|
14
|
+
describe('stageAsset', () => {
|
|
15
|
+
beforeEach(() => {
|
|
16
|
+
vi.clearAllMocks();
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
it('should stage an asset and return staging ID', async () => {
|
|
20
|
+
const mockApiPostMultipart = vi.mocked(client.apiPostMultipart);
|
|
21
|
+
mockApiPostMultipart.mockResolvedValue({
|
|
22
|
+
data: {
|
|
23
|
+
'staging-id': 'staging-12345',
|
|
24
|
+
},
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
const result = await stageAsset(
|
|
28
|
+
'test-api-key',
|
|
29
|
+
'test-marketplace',
|
|
30
|
+
Buffer.from('image data'),
|
|
31
|
+
'test.png'
|
|
32
|
+
);
|
|
33
|
+
|
|
34
|
+
expect(mockApiPostMultipart).toHaveBeenCalledWith(
|
|
35
|
+
'test-api-key',
|
|
36
|
+
'/assets/stage',
|
|
37
|
+
{ marketplace: 'test-marketplace' },
|
|
38
|
+
[
|
|
39
|
+
{
|
|
40
|
+
name: 'file',
|
|
41
|
+
value: Buffer.from('image data'),
|
|
42
|
+
filename: 'test.png',
|
|
43
|
+
},
|
|
44
|
+
]
|
|
45
|
+
);
|
|
46
|
+
|
|
47
|
+
expect(result.stagingId).toBe('staging-12345');
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
it('should work without API key (reads from auth file)', async () => {
|
|
51
|
+
const mockApiPostMultipart = vi.mocked(client.apiPostMultipart);
|
|
52
|
+
mockApiPostMultipart.mockResolvedValue({
|
|
53
|
+
data: {
|
|
54
|
+
'staging-id': 'staging-67890',
|
|
55
|
+
},
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
const result = await stageAsset(
|
|
59
|
+
undefined,
|
|
60
|
+
'test-marketplace',
|
|
61
|
+
Buffer.from('file data'),
|
|
62
|
+
'document.pdf'
|
|
63
|
+
);
|
|
64
|
+
|
|
65
|
+
expect(mockApiPostMultipart).toHaveBeenCalledWith(
|
|
66
|
+
undefined,
|
|
67
|
+
'/assets/stage',
|
|
68
|
+
{ marketplace: 'test-marketplace' },
|
|
69
|
+
expect.arrayContaining([
|
|
70
|
+
expect.objectContaining({
|
|
71
|
+
name: 'file',
|
|
72
|
+
filename: 'document.pdf',
|
|
73
|
+
}),
|
|
74
|
+
])
|
|
75
|
+
);
|
|
76
|
+
|
|
77
|
+
expect(result.stagingId).toBe('staging-67890');
|
|
78
|
+
});
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
describe('pushAssets', () => {
|
|
82
|
+
beforeEach(() => {
|
|
83
|
+
vi.clearAllMocks();
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
it('should push assets with staging-id when provided', async () => {
|
|
87
|
+
const mockApiPostMultipart = vi.mocked(client.apiPostMultipart);
|
|
88
|
+
mockApiPostMultipart.mockResolvedValue({
|
|
89
|
+
data: {
|
|
90
|
+
version: 'v2',
|
|
91
|
+
'asset-meta': {
|
|
92
|
+
assets: [
|
|
93
|
+
{ path: 'test.png', 'content-hash': 'hash123' },
|
|
94
|
+
],
|
|
95
|
+
},
|
|
96
|
+
},
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
const result = await pushAssets(
|
|
100
|
+
'test-api-key',
|
|
101
|
+
'test-marketplace',
|
|
102
|
+
'v1',
|
|
103
|
+
[
|
|
104
|
+
{
|
|
105
|
+
path: 'test.png',
|
|
106
|
+
op: 'upsert',
|
|
107
|
+
stagingId: 'staging-123',
|
|
108
|
+
},
|
|
109
|
+
]
|
|
110
|
+
);
|
|
111
|
+
|
|
112
|
+
expect(mockApiPostMultipart).toHaveBeenCalledWith(
|
|
113
|
+
'test-api-key',
|
|
114
|
+
'/assets/push',
|
|
115
|
+
{ marketplace: 'test-marketplace' },
|
|
116
|
+
expect.arrayContaining([
|
|
117
|
+
{ name: 'current-version', value: 'v1' },
|
|
118
|
+
{ name: 'path-0', value: 'test.png' },
|
|
119
|
+
{ name: 'op-0', value: 'upsert' },
|
|
120
|
+
{ name: 'staging-id-0', value: 'staging-123' },
|
|
121
|
+
])
|
|
122
|
+
);
|
|
123
|
+
|
|
124
|
+
expect(result.version).toBe('v2');
|
|
125
|
+
expect(result.assets).toHaveLength(1);
|
|
126
|
+
expect(result.assets[0].path).toBe('test.png');
|
|
127
|
+
expect(result.assets[0].contentHash).toBe('hash123');
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
it('should push assets with data-raw when staging-id is not provided', async () => {
|
|
131
|
+
const mockApiPostMultipart = vi.mocked(client.apiPostMultipart);
|
|
132
|
+
mockApiPostMultipart.mockResolvedValue({
|
|
133
|
+
data: {
|
|
134
|
+
version: 'v2',
|
|
135
|
+
'asset-meta': {
|
|
136
|
+
assets: [
|
|
137
|
+
{ path: 'config.json', 'content-hash': 'hash456' },
|
|
138
|
+
],
|
|
139
|
+
},
|
|
140
|
+
},
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
const fileData = Buffer.from('{"test": "data"}');
|
|
144
|
+
|
|
145
|
+
const result = await pushAssets(
|
|
146
|
+
'test-api-key',
|
|
147
|
+
'test-marketplace',
|
|
148
|
+
'v1',
|
|
149
|
+
[
|
|
150
|
+
{
|
|
151
|
+
path: 'config.json',
|
|
152
|
+
op: 'upsert',
|
|
153
|
+
data: fileData,
|
|
154
|
+
filename: 'config.json',
|
|
155
|
+
},
|
|
156
|
+
]
|
|
157
|
+
);
|
|
158
|
+
|
|
159
|
+
expect(mockApiPostMultipart).toHaveBeenCalledWith(
|
|
160
|
+
'test-api-key',
|
|
161
|
+
'/assets/push',
|
|
162
|
+
{ marketplace: 'test-marketplace' },
|
|
163
|
+
expect.arrayContaining([
|
|
164
|
+
{ name: 'current-version', value: 'v1' },
|
|
165
|
+
{ name: 'path-0', value: 'config.json' },
|
|
166
|
+
{ name: 'op-0', value: 'upsert' },
|
|
167
|
+
expect.objectContaining({
|
|
168
|
+
name: 'data-raw-0',
|
|
169
|
+
value: fileData,
|
|
170
|
+
filename: 'config.json',
|
|
171
|
+
}),
|
|
172
|
+
])
|
|
173
|
+
);
|
|
174
|
+
|
|
175
|
+
expect(result.version).toBe('v2');
|
|
176
|
+
});
|
|
177
|
+
|
|
178
|
+
it('should handle delete operations', async () => {
|
|
179
|
+
const mockApiPostMultipart = vi.mocked(client.apiPostMultipart);
|
|
180
|
+
mockApiPostMultipart.mockResolvedValue({
|
|
181
|
+
data: {
|
|
182
|
+
version: 'v2',
|
|
183
|
+
'asset-meta': {
|
|
184
|
+
assets: [],
|
|
185
|
+
},
|
|
186
|
+
},
|
|
187
|
+
});
|
|
188
|
+
|
|
189
|
+
const result = await pushAssets(
|
|
190
|
+
'test-api-key',
|
|
191
|
+
'test-marketplace',
|
|
192
|
+
'v1',
|
|
193
|
+
[
|
|
194
|
+
{
|
|
195
|
+
path: 'old-file.txt',
|
|
196
|
+
op: 'delete',
|
|
197
|
+
},
|
|
198
|
+
]
|
|
199
|
+
);
|
|
200
|
+
|
|
201
|
+
expect(mockApiPostMultipart).toHaveBeenCalledWith(
|
|
202
|
+
'test-api-key',
|
|
203
|
+
'/assets/push',
|
|
204
|
+
{ marketplace: 'test-marketplace' },
|
|
205
|
+
expect.arrayContaining([
|
|
206
|
+
{ name: 'current-version', value: 'v1' },
|
|
207
|
+
{ name: 'path-0', value: 'old-file.txt' },
|
|
208
|
+
{ name: 'op-0', value: 'delete' },
|
|
209
|
+
])
|
|
210
|
+
);
|
|
211
|
+
|
|
212
|
+
// Should not include data-raw or staging-id for delete operations
|
|
213
|
+
const callArgs = mockApiPostMultipart.mock.calls[0];
|
|
214
|
+
const fields = callArgs[3] as Array<{ name: string }>;
|
|
215
|
+
const fieldNames = fields.map(f => f.name);
|
|
216
|
+
expect(fieldNames).not.toContain('data-raw-0');
|
|
217
|
+
expect(fieldNames).not.toContain('staging-id-0');
|
|
218
|
+
|
|
219
|
+
expect(result.version).toBe('v2');
|
|
220
|
+
});
|
|
221
|
+
|
|
222
|
+
it('should handle multiple operations', async () => {
|
|
223
|
+
const mockApiPostMultipart = vi.mocked(client.apiPostMultipart);
|
|
224
|
+
mockApiPostMultipart.mockResolvedValue({
|
|
225
|
+
data: {
|
|
226
|
+
version: 'v3',
|
|
227
|
+
'asset-meta': {
|
|
228
|
+
assets: [
|
|
229
|
+
{ path: 'new.png', 'content-hash': 'hash1' },
|
|
230
|
+
{ path: 'config.json', 'content-hash': 'hash2' },
|
|
231
|
+
],
|
|
232
|
+
},
|
|
233
|
+
},
|
|
234
|
+
});
|
|
235
|
+
|
|
236
|
+
const result = await pushAssets(
|
|
237
|
+
'test-api-key',
|
|
238
|
+
'test-marketplace',
|
|
239
|
+
'v2',
|
|
240
|
+
[
|
|
241
|
+
{
|
|
242
|
+
path: 'new.png',
|
|
243
|
+
op: 'upsert',
|
|
244
|
+
stagingId: 'staging-1',
|
|
245
|
+
},
|
|
246
|
+
{
|
|
247
|
+
path: 'config.json',
|
|
248
|
+
op: 'upsert',
|
|
249
|
+
data: Buffer.from('{}'),
|
|
250
|
+
filename: 'config.json',
|
|
251
|
+
},
|
|
252
|
+
{
|
|
253
|
+
path: 'old.txt',
|
|
254
|
+
op: 'delete',
|
|
255
|
+
},
|
|
256
|
+
]
|
|
257
|
+
);
|
|
258
|
+
|
|
259
|
+
expect(mockApiPostMultipart).toHaveBeenCalledWith(
|
|
260
|
+
'test-api-key',
|
|
261
|
+
'/assets/push',
|
|
262
|
+
{ marketplace: 'test-marketplace' },
|
|
263
|
+
expect.arrayContaining([
|
|
264
|
+
{ name: 'current-version', value: 'v2' },
|
|
265
|
+
{ name: 'path-0', value: 'new.png' },
|
|
266
|
+
{ name: 'op-0', value: 'upsert' },
|
|
267
|
+
{ name: 'staging-id-0', value: 'staging-1' },
|
|
268
|
+
{ name: 'path-1', value: 'config.json' },
|
|
269
|
+
{ name: 'op-1', value: 'upsert' },
|
|
270
|
+
{ name: 'path-2', value: 'old.txt' },
|
|
271
|
+
{ name: 'op-2', value: 'delete' },
|
|
272
|
+
])
|
|
273
|
+
);
|
|
274
|
+
|
|
275
|
+
expect(result.version).toBe('v3');
|
|
276
|
+
expect(result.assets).toHaveLength(2);
|
|
277
|
+
});
|
|
278
|
+
|
|
279
|
+
it('should convert staging-id to string', async () => {
|
|
280
|
+
const mockApiPostMultipart = vi.mocked(client.apiPostMultipart);
|
|
281
|
+
mockApiPostMultipart.mockResolvedValue({
|
|
282
|
+
data: {
|
|
283
|
+
version: 'v2',
|
|
284
|
+
'asset-meta': {
|
|
285
|
+
assets: [],
|
|
286
|
+
},
|
|
287
|
+
},
|
|
288
|
+
});
|
|
289
|
+
|
|
290
|
+
await pushAssets(
|
|
291
|
+
'test-api-key',
|
|
292
|
+
'test-marketplace',
|
|
293
|
+
'v1',
|
|
294
|
+
[
|
|
295
|
+
{
|
|
296
|
+
path: 'test.png',
|
|
297
|
+
op: 'upsert',
|
|
298
|
+
stagingId: 12345 as any, // Test that it converts to string
|
|
299
|
+
},
|
|
300
|
+
]
|
|
301
|
+
);
|
|
302
|
+
|
|
303
|
+
const callArgs = mockApiPostMultipart.mock.calls[0];
|
|
304
|
+
const fields = callArgs[3] as Array<{ name: string; value: string }>;
|
|
305
|
+
const stagingIdField = fields.find(f => f.name === 'staging-id-0');
|
|
306
|
+
expect(stagingIdField?.value).toBe('12345');
|
|
307
|
+
});
|
|
308
|
+
});
|