@viu/emporix-sdk 1.0.0 → 2.0.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/CHANGELOG.md +38 -0
- package/README.md +13 -0
- package/dist/cart.d.cts +1 -1
- package/dist/cart.d.ts +1 -1
- package/dist/category.d.cts +1 -1
- package/dist/category.d.ts +1 -1
- package/dist/checkout.d.cts +1 -1
- package/dist/checkout.d.ts +1 -1
- package/dist/chunk-WSXRUYBE.js +248 -0
- package/dist/chunk-WSXRUYBE.js.map +1 -0
- package/dist/companies.d.cts +1 -1
- package/dist/companies.d.ts +1 -1
- package/dist/contacts.d.cts +1 -1
- package/dist/contacts.d.ts +1 -1
- package/dist/{context-vKgzZ-5f.d.cts → context-mMTqsW1Z.d.cts} +15 -0
- package/dist/{context-vKgzZ-5f.d.ts → context-mMTqsW1Z.d.ts} +15 -0
- package/dist/customer-groups.d.cts +1 -1
- package/dist/customer-groups.d.ts +1 -1
- package/dist/customer.d.cts +1 -1
- package/dist/customer.d.ts +1 -1
- package/dist/index.cjs +186 -9
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +3 -3
- package/dist/index.d.ts +3 -3
- package/dist/index.js +53 -2
- package/dist/index.js.map +1 -1
- package/dist/locations.d.cts +1 -1
- package/dist/locations.d.ts +1 -1
- package/dist/media.cjs +218 -9
- package/dist/media.cjs.map +1 -1
- package/dist/media.d.cts +103 -7
- package/dist/media.d.ts +103 -7
- package/dist/media.js +2 -1
- package/dist/orders.d.cts +1 -1
- package/dist/orders.d.ts +1 -1
- package/dist/payment.d.cts +1 -1
- package/dist/payment.d.ts +1 -1
- package/dist/price.d.cts +1 -1
- package/dist/price.d.ts +1 -1
- package/dist/product.d.cts +1 -1
- package/dist/product.d.ts +1 -1
- package/dist/segment.d.cts +1 -1
- package/dist/segment.d.ts +1 -1
- package/package.json +2 -2
- package/dist/chunk-F3WAQH2E.js +0 -120
- package/dist/chunk-F3WAQH2E.js.map +0 -1
package/dist/media.cjs
CHANGED
|
@@ -1,5 +1,88 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
+
// src/core/errors.ts
|
|
4
|
+
var REDACTED_BODY_KEYS = /* @__PURE__ */ new Set([
|
|
5
|
+
"access_token",
|
|
6
|
+
"refresh_token",
|
|
7
|
+
"token",
|
|
8
|
+
"customertoken",
|
|
9
|
+
"saastoken",
|
|
10
|
+
"secret",
|
|
11
|
+
"client_secret",
|
|
12
|
+
"authorization",
|
|
13
|
+
"password"
|
|
14
|
+
]);
|
|
15
|
+
function scrub(body) {
|
|
16
|
+
if (Array.isArray(body)) return body.map(scrub);
|
|
17
|
+
if (body && typeof body === "object") {
|
|
18
|
+
const out = {};
|
|
19
|
+
for (const [k, v] of Object.entries(body)) {
|
|
20
|
+
out[k] = REDACTED_BODY_KEYS.has(k.toLowerCase()) ? "***redacted***" : scrub(v);
|
|
21
|
+
}
|
|
22
|
+
return out;
|
|
23
|
+
}
|
|
24
|
+
return body;
|
|
25
|
+
}
|
|
26
|
+
var EmporixError = class extends Error {
|
|
27
|
+
status;
|
|
28
|
+
body;
|
|
29
|
+
constructor(message, status, body) {
|
|
30
|
+
super(message);
|
|
31
|
+
this.name = new.target.name;
|
|
32
|
+
this.status = status;
|
|
33
|
+
this.body = body;
|
|
34
|
+
Object.setPrototypeOf(this, new.target.prototype);
|
|
35
|
+
}
|
|
36
|
+
/** Safe serialisation — token-like body fields are redacted. */
|
|
37
|
+
toJSON() {
|
|
38
|
+
return { name: this.name, message: this.message, status: this.status, body: scrub(this.body) };
|
|
39
|
+
}
|
|
40
|
+
};
|
|
41
|
+
var EmporixAuthError = class extends EmporixError {
|
|
42
|
+
};
|
|
43
|
+
var EmporixForbiddenError = class extends EmporixError {
|
|
44
|
+
};
|
|
45
|
+
var EmporixNotFoundError = class extends EmporixError {
|
|
46
|
+
};
|
|
47
|
+
var EmporixValidationError = class extends EmporixError {
|
|
48
|
+
};
|
|
49
|
+
var EmporixServerError = class extends EmporixError {
|
|
50
|
+
};
|
|
51
|
+
var EmporixInsufficientScopeError = class extends EmporixForbiddenError {
|
|
52
|
+
requiredScope;
|
|
53
|
+
constructor(message, status, body, requiredScope) {
|
|
54
|
+
super(message, status, body);
|
|
55
|
+
this.requiredScope = requiredScope;
|
|
56
|
+
}
|
|
57
|
+
};
|
|
58
|
+
var SCOPE_HINT_RE = /missing scope[: ]+([a-z0-9._-]+)/i;
|
|
59
|
+
function extractRequiredScope(body) {
|
|
60
|
+
if (body && typeof body === "object" && "details" in body) {
|
|
61
|
+
const details = body.details;
|
|
62
|
+
if (Array.isArray(details)) {
|
|
63
|
+
for (const d of details) {
|
|
64
|
+
if (typeof d === "string") {
|
|
65
|
+
const m = d.match(SCOPE_HINT_RE);
|
|
66
|
+
if (m) return m[1];
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
return void 0;
|
|
72
|
+
}
|
|
73
|
+
function errorFromResponse(status, message, body) {
|
|
74
|
+
if (status === 401) return new EmporixAuthError(message, status, body);
|
|
75
|
+
if (status === 403) {
|
|
76
|
+
const scope = extractRequiredScope(body);
|
|
77
|
+
if (scope) return new EmporixInsufficientScopeError(message, status, body, scope);
|
|
78
|
+
return new EmporixForbiddenError(message, status, body);
|
|
79
|
+
}
|
|
80
|
+
if (status === 404) return new EmporixNotFoundError(message, status, body);
|
|
81
|
+
if (status === 400 || status === 422) return new EmporixValidationError(message, status, body);
|
|
82
|
+
if (status >= 500) return new EmporixServerError(message, status, body);
|
|
83
|
+
return new EmporixError(message, status, body);
|
|
84
|
+
}
|
|
85
|
+
|
|
3
86
|
// src/services/media.ts
|
|
4
87
|
var SERVICE = { kind: "service" };
|
|
5
88
|
function isProductRef(r, productId) {
|
|
@@ -33,14 +116,29 @@ var MediaService = class {
|
|
|
33
116
|
body: input.body
|
|
34
117
|
});
|
|
35
118
|
}
|
|
36
|
-
/**
|
|
37
|
-
|
|
38
|
-
|
|
119
|
+
/**
|
|
120
|
+
* List assets, wrapped in the shared {@link PaginatedItems} envelope so
|
|
121
|
+
* callers can iterate over pages consistently with the rest of the SDK.
|
|
122
|
+
* `hasNextPage` is the standard SDK heuristic: `true` when the returned
|
|
123
|
+
* page is full (`items.length === requested pageSize`). Pagination
|
|
124
|
+
* defaults match Emporix's server defaults (`pageNumber: 1`,
|
|
125
|
+
* `pageSize: 60`) so an empty `query` produces stable expectations.
|
|
126
|
+
*/
|
|
127
|
+
async list(query = {}, auth = SERVICE) {
|
|
128
|
+
const pageNumber = query.pageNumber ?? 1;
|
|
129
|
+
const pageSize = query.pageSize ?? 60;
|
|
130
|
+
const items = await this.ctx.http.request({
|
|
39
131
|
method: "GET",
|
|
40
132
|
path: this.base(),
|
|
41
133
|
auth,
|
|
42
|
-
|
|
134
|
+
query: { ...query, pageNumber, pageSize }
|
|
43
135
|
});
|
|
136
|
+
return {
|
|
137
|
+
items,
|
|
138
|
+
pageNumber,
|
|
139
|
+
pageSize,
|
|
140
|
+
hasNextPage: items.length === pageSize
|
|
141
|
+
};
|
|
44
142
|
}
|
|
45
143
|
/** Fetch an asset by id. */
|
|
46
144
|
async get(assetId, auth = SERVICE) {
|
|
@@ -50,13 +148,37 @@ var MediaService = class {
|
|
|
50
148
|
auth
|
|
51
149
|
});
|
|
52
150
|
}
|
|
53
|
-
/**
|
|
54
|
-
|
|
151
|
+
/**
|
|
152
|
+
* Update an asset. The input is a discriminated union mirroring
|
|
153
|
+
* {@link create}:
|
|
154
|
+
* - `{ kind: "json", body }` — metadata-only patch (BLOB or LINK).
|
|
155
|
+
* Sends `application/json`. Used for `refIds`, `details`, `metadata`,
|
|
156
|
+
* or `url` changes.
|
|
157
|
+
* - `{ kind: "blob", file, body }` — replaces the BLOB file content
|
|
158
|
+
* (max 10MB) AND patches metadata in the same request. Sends
|
|
159
|
+
* `multipart/form-data`.
|
|
160
|
+
*
|
|
161
|
+
* `type` and `access` are immutable per Emporix — they must match the
|
|
162
|
+
* existing asset's values. Optimistic-locking is via `body.metadata.version`;
|
|
163
|
+
* the server returns 409 Conflict on a stale version.
|
|
164
|
+
*/
|
|
165
|
+
async update(assetId, input, auth = SERVICE) {
|
|
166
|
+
if (input.kind === "blob") {
|
|
167
|
+
const fd = new FormData();
|
|
168
|
+
fd.set("file", input.file);
|
|
169
|
+
fd.set("body", JSON.stringify(input.body));
|
|
170
|
+
return this.ctx.http.request({
|
|
171
|
+
method: "PUT",
|
|
172
|
+
path: `${this.base()}/${assetId}`,
|
|
173
|
+
auth,
|
|
174
|
+
body: fd
|
|
175
|
+
});
|
|
176
|
+
}
|
|
55
177
|
return this.ctx.http.request({
|
|
56
178
|
method: "PUT",
|
|
57
179
|
path: `${this.base()}/${assetId}`,
|
|
58
180
|
auth,
|
|
59
|
-
body:
|
|
181
|
+
body: input.body
|
|
60
182
|
});
|
|
61
183
|
}
|
|
62
184
|
/** Remove an asset. */
|
|
@@ -67,6 +189,72 @@ var MediaService = class {
|
|
|
67
189
|
auth
|
|
68
190
|
});
|
|
69
191
|
}
|
|
192
|
+
/**
|
|
193
|
+
* Download an asset by id. Returns a discriminated union:
|
|
194
|
+
* - `{ kind: "redirect", url }` for `PUBLIC` assets (server-side 30x with
|
|
195
|
+
* the storage URL in `Location`).
|
|
196
|
+
* - `{ kind: "bytes", data }` for `PRIVATE` assets (server-side 200 with
|
|
197
|
+
* the asset bytes; `etag` + `contentType` headers are exposed for
|
|
198
|
+
* caching). When the server reports `text/plain` Content-Type (the
|
|
199
|
+
* OpenAPI-documented format), the SDK decodes the base64 stream into
|
|
200
|
+
* an `ArrayBuffer` transparently; binary Content-Types are passed
|
|
201
|
+
* through verbatim.
|
|
202
|
+
*
|
|
203
|
+
* Implementation note: uses `redirect: "manual"` so the redirect-location
|
|
204
|
+
* is observable. In Node.js this works; in a browser the redirect Location
|
|
205
|
+
* is intentionally hidden by fetch — there, `PUBLIC` downloads will
|
|
206
|
+
* surface as an opaque-redirect and the SDK throws. Browser code should
|
|
207
|
+
* use the asset's `url` field from `get()` for `LINK` assets, or the
|
|
208
|
+
* direct storage URL for `PUBLIC` `BLOB` assets (typically delivered via
|
|
209
|
+
* an `<img>` tag rather than `download()`).
|
|
210
|
+
*/
|
|
211
|
+
async download(assetId, auth = SERVICE) {
|
|
212
|
+
const path = `${this.base()}/${assetId}/download`;
|
|
213
|
+
const res = await this.ctx.http.requestRaw(
|
|
214
|
+
{ method: "GET", path, auth },
|
|
215
|
+
{ redirect: "manual" }
|
|
216
|
+
);
|
|
217
|
+
if (res.status >= 300 && res.status < 400) {
|
|
218
|
+
const url = res.headers.get("location");
|
|
219
|
+
if (!url) {
|
|
220
|
+
throw new EmporixError(
|
|
221
|
+
`media.download: ${res.status} response without a Location header`,
|
|
222
|
+
res.status
|
|
223
|
+
);
|
|
224
|
+
}
|
|
225
|
+
return { kind: "redirect", url };
|
|
226
|
+
}
|
|
227
|
+
if (res.ok) {
|
|
228
|
+
const etag = res.headers.get("etag") ?? void 0;
|
|
229
|
+
const contentType = res.headers.get("content-type") ?? void 0;
|
|
230
|
+
let data;
|
|
231
|
+
if (contentType?.startsWith("text/plain")) {
|
|
232
|
+
const text2 = await res.text();
|
|
233
|
+
const bin = atob(text2);
|
|
234
|
+
const arr = new Uint8Array(bin.length);
|
|
235
|
+
for (let i = 0; i < bin.length; i++) arr[i] = bin.charCodeAt(i);
|
|
236
|
+
data = arr.buffer;
|
|
237
|
+
} else {
|
|
238
|
+
data = await res.arrayBuffer();
|
|
239
|
+
}
|
|
240
|
+
return {
|
|
241
|
+
kind: "bytes",
|
|
242
|
+
data,
|
|
243
|
+
...etag !== void 0 ? { etag } : {},
|
|
244
|
+
...contentType !== void 0 ? { contentType } : {}
|
|
245
|
+
};
|
|
246
|
+
}
|
|
247
|
+
const text = await res.text();
|
|
248
|
+
let parsed = void 0;
|
|
249
|
+
if (text) {
|
|
250
|
+
try {
|
|
251
|
+
parsed = JSON.parse(text);
|
|
252
|
+
} catch {
|
|
253
|
+
parsed = text;
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
throw errorFromResponse(res.status, `GET ${path} \u2192 ${res.status}`, parsed);
|
|
257
|
+
}
|
|
70
258
|
/** Multipart upload sugar: builds `AssetCreateBlob` from input. */
|
|
71
259
|
async uploadFile(input, auth = SERVICE) {
|
|
72
260
|
const body = {
|
|
@@ -82,6 +270,27 @@ var MediaService = class {
|
|
|
82
270
|
};
|
|
83
271
|
return this.create({ kind: "blob", file: input.file, body }, auth);
|
|
84
272
|
}
|
|
273
|
+
/**
|
|
274
|
+
* Replace the file content of an existing BLOB asset. Sugar over
|
|
275
|
+
* {@link update} with `kind: "blob"`. `access` is required because the
|
|
276
|
+
* field is immutable and the server validates that the patch matches.
|
|
277
|
+
* Pass `version` from `asset.metadata.version` if you want optimistic
|
|
278
|
+
* locking (recommended when concurrent writers are possible).
|
|
279
|
+
*/
|
|
280
|
+
async replaceFile(assetId, input, auth = SERVICE) {
|
|
281
|
+
const body = {
|
|
282
|
+
type: "BLOB",
|
|
283
|
+
access: input.access,
|
|
284
|
+
...input.filename || input.mimeType ? {
|
|
285
|
+
details: {
|
|
286
|
+
...input.filename ? { filename: input.filename } : {},
|
|
287
|
+
...input.mimeType ? { mimeType: input.mimeType } : {}
|
|
288
|
+
}
|
|
289
|
+
} : {},
|
|
290
|
+
...input.version !== void 0 ? { metadata: { version: input.version } } : {}
|
|
291
|
+
};
|
|
292
|
+
return this.update(assetId, { kind: "blob", file: input.file, body }, auth);
|
|
293
|
+
}
|
|
85
294
|
/** External-URL sugar: builds `AssetCreateLink`. */
|
|
86
295
|
async link(input, auth = SERVICE) {
|
|
87
296
|
const body = {
|
|
@@ -99,7 +308,7 @@ var MediaService = class {
|
|
|
99
308
|
if (refIds.some((r) => isProductRef(r, productId))) return a;
|
|
100
309
|
const next = [...refIds, { type: "PRODUCT", id: productId }];
|
|
101
310
|
const patch = { type: a.type, refIds: next };
|
|
102
|
-
return this.update(assetId, patch, auth);
|
|
311
|
+
return this.update(assetId, { kind: "json", body: patch }, auth);
|
|
103
312
|
}
|
|
104
313
|
/** Remove a PRODUCT refId from an asset (no-op if absent). */
|
|
105
314
|
async detachFromProduct(assetId, productId, auth = SERVICE) {
|
|
@@ -108,7 +317,7 @@ var MediaService = class {
|
|
|
108
317
|
const next = refIds.filter((r) => !isProductRef(r, productId));
|
|
109
318
|
if (next.length === refIds.length) return a;
|
|
110
319
|
const patch = { type: a.type, refIds: next };
|
|
111
|
-
return this.update(assetId, patch, auth);
|
|
320
|
+
return this.update(assetId, { kind: "json", body: patch }, auth);
|
|
112
321
|
}
|
|
113
322
|
/** Convenience: list assets attached to a product (server-side filter). */
|
|
114
323
|
async listForProduct(productId, auth = SERVICE) {
|
package/dist/media.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/services/media.ts"],"names":[],"mappings":"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","file":"media.cjs","sourcesContent":["import type { ClientContext } from \"../core/context\";\nimport type { AuthContext } from \"../core/auth\";\nimport type {\n AssetCreateBlob,\n AssetCreateLink,\n AssetUpdateBlob,\n AssetUpdateLink,\n GetAsset,\n RefId,\n} from \"../generated/media\";\n\n/** Generated media types (caller sends the exact wire shape). */\nexport type AssetCreateBlobInput = AssetCreateBlob;\nexport type AssetCreateLinkInput = AssetCreateLink;\nexport type AssetUpdateInput = AssetUpdateBlob | AssetUpdateLink;\nexport type Asset = GetAsset;\nexport type AssetRefId = RefId;\n\nconst SERVICE: AuthContext = { kind: \"service\" };\n\nfunction isProductRef(r: AssetRefId, productId: string): boolean {\n return r.type === \"PRODUCT\" && r.id === productId;\n}\n\n/**\n * Media assets (BLOB/LINK). All endpoints require a backend-only scope\n * (`media.asset_manage` / `media.asset_read`) — default auth: service.\n */\nexport class MediaService {\n constructor(private readonly ctx: ClientContext) {}\n\n private base(): string {\n return `/media/${this.ctx.tenant}/assets`;\n }\n\n /** Create an asset. BLOB uploads via multipart; LINK via JSON. */\n async create(\n input:\n | { kind: \"blob\"; file: Blob; body: AssetCreateBlobInput }\n | { kind: \"link\"; body: AssetCreateLinkInput },\n auth: AuthContext = SERVICE,\n ): Promise<{ id: string }> {\n if (input.kind === \"blob\") {\n const fd = new FormData();\n fd.set(\"file\", input.file);\n fd.set(\"body\", JSON.stringify(input.body));\n return this.ctx.http.request<{ id: string }>({\n method: \"POST\",\n path: this.base(),\n auth,\n body: fd,\n });\n }\n return this.ctx.http.request<{ id: string }>({\n method: \"POST\",\n path: this.base(),\n auth,\n body: input.body,\n });\n }\n\n /** List assets. Optional `query` is passed through to Emporix verbatim. */\n async list(\n query?: Record<string, string | number | undefined>,\n auth: AuthContext = SERVICE,\n ): Promise<Asset[]> {\n return this.ctx.http.request<Asset[]>({\n method: \"GET\",\n path: this.base(),\n auth,\n ...(query ? { query } : {}),\n });\n }\n\n /** Fetch an asset by id. */\n async get(assetId: string, auth: AuthContext = SERVICE): Promise<Asset> {\n return this.ctx.http.request<Asset>({\n method: \"GET\",\n path: `${this.base()}/${assetId}`,\n auth,\n });\n }\n\n /** Update an asset (e.g. swap `refIds` or `access`). */\n async update(\n assetId: string,\n patch: AssetUpdateInput,\n auth: AuthContext = SERVICE,\n ): Promise<Asset> {\n return this.ctx.http.request<Asset>({\n method: \"PUT\",\n path: `${this.base()}/${assetId}`,\n auth,\n body: patch,\n });\n }\n\n /** Remove an asset. */\n async remove(assetId: string, auth: AuthContext = SERVICE): Promise<void> {\n await this.ctx.http.request<void>({\n method: \"DELETE\",\n path: `${this.base()}/${assetId}`,\n auth,\n });\n }\n\n /** Multipart upload sugar: builds `AssetCreateBlob` from input. */\n async uploadFile(\n input: {\n file: Blob;\n productId?: string;\n filename?: string;\n mimeType?: string;\n access?: \"PUBLIC\" | \"PRIVATE\";\n },\n auth: AuthContext = SERVICE,\n ): Promise<{ id: string }> {\n const body: AssetCreateBlobInput = {\n type: \"BLOB\",\n access: input.access ?? \"PUBLIC\",\n ...(input.productId\n ? { refIds: [{ type: \"PRODUCT\", id: input.productId }] }\n : {}),\n ...(input.filename || input.mimeType\n ? {\n details: {\n ...(input.filename ? { filename: input.filename } : {}),\n ...(input.mimeType ? { mimeType: input.mimeType } : {}),\n },\n }\n : {}),\n };\n return this.create({ kind: \"blob\", file: input.file, body }, auth);\n }\n\n /** External-URL sugar: builds `AssetCreateLink`. */\n async link(\n input: { url: string; productId?: string; access?: \"PUBLIC\" | \"PRIVATE\" },\n auth: AuthContext = SERVICE,\n ): Promise<{ id: string }> {\n const body: AssetCreateLinkInput = {\n type: \"LINK\",\n access: input.access ?? \"PUBLIC\",\n url: input.url,\n ...(input.productId\n ? { refIds: [{ type: \"PRODUCT\", id: input.productId }] }\n : {}),\n };\n return this.create({ kind: \"link\", body }, auth);\n }\n\n /** Idempotently add a PRODUCT refId to an asset. */\n async attachToProduct(\n assetId: string,\n productId: string,\n auth: AuthContext = SERVICE,\n ): Promise<Asset> {\n const a = await this.get(assetId, auth);\n const refIds: AssetRefId[] = a.refIds ?? [];\n if (refIds.some((r) => isProductRef(r, productId))) return a;\n const next: AssetRefId[] = [...refIds, { type: \"PRODUCT\", id: productId }];\n // Preserve the asset's type discriminator so the update body satisfies\n // the AssetUpdateBlob | AssetUpdateLink union.\n const patch = { type: a.type, refIds: next } as unknown as AssetUpdateInput;\n return this.update(assetId, patch, auth);\n }\n\n /** Remove a PRODUCT refId from an asset (no-op if absent). */\n async detachFromProduct(\n assetId: string,\n productId: string,\n auth: AuthContext = SERVICE,\n ): Promise<Asset> {\n const a = await this.get(assetId, auth);\n const refIds: AssetRefId[] = a.refIds ?? [];\n const next = refIds.filter((r) => !isProductRef(r, productId));\n if (next.length === refIds.length) return a;\n const patch = { type: a.type, refIds: next } as unknown as AssetUpdateInput;\n return this.update(assetId, patch, auth);\n }\n\n /** Convenience: list assets attached to a product (server-side filter). */\n async listForProduct(productId: string, auth: AuthContext = SERVICE): Promise<Asset[]> {\n return this.list({ \"refIds.id\": productId }, auth);\n }\n}\n\n// Re-export so consumers can `import { isProductRef } from \"@viu/emporix-sdk/media\"`?\n// Internal helper — kept module-private; convenience methods in Task 4 use it.\nexport const _internalMedia = { isProductRef };\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/core/errors.ts","../src/services/media.ts"],"names":["text"],"mappings":"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","file":"media.cjs","sourcesContent":["/** Keys whose values are stripped from any serialised error body. */\nconst REDACTED_BODY_KEYS = new Set([\n \"access_token\", \"refresh_token\", \"token\", \"customertoken\", \"saastoken\",\n \"secret\", \"client_secret\", \"authorization\", \"password\",\n]);\n\nfunction scrub(body: unknown): unknown {\n if (Array.isArray(body)) return body.map(scrub);\n if (body && typeof body === \"object\") {\n const out: Record<string, unknown> = {};\n for (const [k, v] of Object.entries(body as Record<string, unknown>)) {\n out[k] = REDACTED_BODY_KEYS.has(k.toLowerCase()) ? \"***redacted***\" : scrub(v);\n }\n return out;\n }\n return body;\n}\n\n/** Base class for every error thrown by the SDK. */\nexport class EmporixError extends Error {\n readonly status: number | undefined;\n readonly body: unknown;\n constructor(message: string, status?: number, body?: unknown) {\n super(message);\n this.name = new.target.name;\n this.status = status;\n this.body = body;\n Object.setPrototypeOf(this, new.target.prototype);\n }\n /** Safe serialisation — token-like body fields are redacted. */\n toJSON(): Record<string, unknown> {\n return { name: this.name, message: this.message, status: this.status, body: scrub(this.body) };\n }\n}\n\n/** 401 — authentication failed or token expired. */\nexport class EmporixAuthError extends EmporixError {}\n/** 403 — authenticated but not permitted. */\nexport class EmporixForbiddenError extends EmporixError {}\n/** 404 — resource not found. */\nexport class EmporixNotFoundError extends EmporixError {}\n/** 400/422 — request validation failed. */\nexport class EmporixValidationError extends EmporixError {}\n/** 5xx — server-side failure. */\nexport class EmporixServerError extends EmporixError {}\n\n/**\n * 403 with a `missing scope: <name>` hint in the body's `details` array.\n * Subclasses {@link EmporixForbiddenError} so existing 403 catches still work.\n */\nexport class EmporixInsufficientScopeError extends EmporixForbiddenError {\n readonly requiredScope: string | undefined;\n constructor(message: string, status: number, body: unknown, requiredScope?: string) {\n super(message, status, body);\n this.requiredScope = requiredScope;\n }\n}\n\nconst SCOPE_HINT_RE = /missing scope[: ]+([a-z0-9._-]+)/i;\n\nfunction extractRequiredScope(body: unknown): string | undefined {\n if (body && typeof body === \"object\" && \"details\" in body) {\n const details = (body as { details?: unknown }).details;\n if (Array.isArray(details)) {\n for (const d of details) {\n if (typeof d === \"string\") {\n const m = d.match(SCOPE_HINT_RE);\n if (m) return m[1];\n }\n }\n }\n }\n return undefined;\n}\n\n/** Maps an HTTP status to the matching {@link EmporixError} subclass. */\nexport function errorFromResponse(status: number, message: string, body: unknown): EmporixError {\n if (status === 401) return new EmporixAuthError(message, status, body);\n if (status === 403) {\n const scope = extractRequiredScope(body);\n if (scope) return new EmporixInsufficientScopeError(message, status, body, scope);\n return new EmporixForbiddenError(message, status, body);\n }\n if (status === 404) return new EmporixNotFoundError(message, status, body);\n if (status === 400 || status === 422) return new EmporixValidationError(message, status, body);\n if (status >= 500) return new EmporixServerError(message, status, body);\n return new EmporixError(message, status, body);\n}\n","import type { ClientContext, PaginatedItems } from \"../core/context\";\nimport type { AuthContext } from \"../core/auth\";\nimport { EmporixError, errorFromResponse } from \"../core/errors\";\nimport type {\n AssetCreateBlob,\n AssetCreateLink,\n AssetUpdateBlob,\n AssetUpdateLink,\n GetAsset,\n RefId,\n} from \"../generated/media\";\n\n/** Generated media types (caller sends the exact wire shape). */\nexport type AssetCreateBlobInput = AssetCreateBlob;\nexport type AssetCreateLinkInput = AssetCreateLink;\nexport type AssetUpdateBlobInput = AssetUpdateBlob;\nexport type AssetUpdateLinkInput = AssetUpdateLink;\nexport type AssetUpdateInput = AssetUpdateBlob | AssetUpdateLink;\nexport type Asset = GetAsset;\nexport type AssetRefId = RefId;\n\n/**\n * Filter / pagination options for {@link MediaService.list}. The explicit\n * fields are typed for autocomplete; the index signature stays open so\n * Emporix `q`-syntax filters like `\"refIds.id\"` can be passed verbatim.\n */\nexport interface ListAssetsQuery {\n pageNumber?: number;\n pageSize?: number;\n /** Emporix sort syntax, e.g. `\"name,metadata.createdAt:desc\"`. */\n sort?: string;\n /** Emporix `q`-syntax filter, e.g. `\"name:hero\"`. */\n q?: string;\n [key: string]: string | number | undefined;\n}\n\n/**\n * Result of {@link MediaService.download}. The endpoint's behaviour depends\n * on the asset's `access`:\n * - `PUBLIC` assets respond with a 30x redirect whose `Location` header\n * carries the externally-cacheable storage URL — useful when the\n * storefront wants to send the user there directly.\n * - `PRIVATE` assets respond with the raw bytes (plus an `ETag` header\n * for caching). The asset is delivered through Cloudinary behind the\n * server-side service token, never publicly addressable.\n */\nexport type DownloadResult =\n | { kind: \"redirect\"; url: string }\n | {\n kind: \"bytes\";\n data: ArrayBuffer;\n etag?: string;\n contentType?: string;\n };\n\nconst SERVICE: AuthContext = { kind: \"service\" };\n\nfunction isProductRef(r: AssetRefId, productId: string): boolean {\n return r.type === \"PRODUCT\" && r.id === productId;\n}\n\n/**\n * Media assets (BLOB/LINK). All endpoints require a backend-only scope\n * (`media.asset_manage` / `media.asset_read`) — default auth: service.\n */\nexport class MediaService {\n constructor(private readonly ctx: ClientContext) {}\n\n private base(): string {\n return `/media/${this.ctx.tenant}/assets`;\n }\n\n /** Create an asset. BLOB uploads via multipart; LINK via JSON. */\n async create(\n input:\n | { kind: \"blob\"; file: Blob; body: AssetCreateBlobInput }\n | { kind: \"link\"; body: AssetCreateLinkInput },\n auth: AuthContext = SERVICE,\n ): Promise<{ id: string }> {\n if (input.kind === \"blob\") {\n const fd = new FormData();\n fd.set(\"file\", input.file);\n fd.set(\"body\", JSON.stringify(input.body));\n return this.ctx.http.request<{ id: string }>({\n method: \"POST\",\n path: this.base(),\n auth,\n body: fd,\n });\n }\n return this.ctx.http.request<{ id: string }>({\n method: \"POST\",\n path: this.base(),\n auth,\n body: input.body,\n });\n }\n\n /**\n * List assets, wrapped in the shared {@link PaginatedItems} envelope so\n * callers can iterate over pages consistently with the rest of the SDK.\n * `hasNextPage` is the standard SDK heuristic: `true` when the returned\n * page is full (`items.length === requested pageSize`). Pagination\n * defaults match Emporix's server defaults (`pageNumber: 1`,\n * `pageSize: 60`) so an empty `query` produces stable expectations.\n */\n async list(\n query: ListAssetsQuery = {},\n auth: AuthContext = SERVICE,\n ): Promise<PaginatedItems<Asset>> {\n const pageNumber = query.pageNumber ?? 1;\n const pageSize = query.pageSize ?? 60;\n const items = await this.ctx.http.request<Asset[]>({\n method: \"GET\",\n path: this.base(),\n auth,\n query: { ...query, pageNumber, pageSize },\n });\n return {\n items,\n pageNumber,\n pageSize,\n hasNextPage: items.length === pageSize,\n };\n }\n\n /** Fetch an asset by id. */\n async get(assetId: string, auth: AuthContext = SERVICE): Promise<Asset> {\n return this.ctx.http.request<Asset>({\n method: \"GET\",\n path: `${this.base()}/${assetId}`,\n auth,\n });\n }\n\n /**\n * Update an asset. The input is a discriminated union mirroring\n * {@link create}:\n * - `{ kind: \"json\", body }` — metadata-only patch (BLOB or LINK).\n * Sends `application/json`. Used for `refIds`, `details`, `metadata`,\n * or `url` changes.\n * - `{ kind: \"blob\", file, body }` — replaces the BLOB file content\n * (max 10MB) AND patches metadata in the same request. Sends\n * `multipart/form-data`.\n *\n * `type` and `access` are immutable per Emporix — they must match the\n * existing asset's values. Optimistic-locking is via `body.metadata.version`;\n * the server returns 409 Conflict on a stale version.\n */\n async update(\n assetId: string,\n input:\n | { kind: \"json\"; body: AssetUpdateInput }\n | { kind: \"blob\"; file: Blob; body: AssetUpdateBlobInput },\n auth: AuthContext = SERVICE,\n ): Promise<Asset> {\n if (input.kind === \"blob\") {\n const fd = new FormData();\n fd.set(\"file\", input.file);\n fd.set(\"body\", JSON.stringify(input.body));\n return this.ctx.http.request<Asset>({\n method: \"PUT\",\n path: `${this.base()}/${assetId}`,\n auth,\n body: fd,\n });\n }\n return this.ctx.http.request<Asset>({\n method: \"PUT\",\n path: `${this.base()}/${assetId}`,\n auth,\n body: input.body,\n });\n }\n\n /** Remove an asset. */\n async remove(assetId: string, auth: AuthContext = SERVICE): Promise<void> {\n await this.ctx.http.request<void>({\n method: \"DELETE\",\n path: `${this.base()}/${assetId}`,\n auth,\n });\n }\n\n /**\n * Download an asset by id. Returns a discriminated union:\n * - `{ kind: \"redirect\", url }` for `PUBLIC` assets (server-side 30x with\n * the storage URL in `Location`).\n * - `{ kind: \"bytes\", data }` for `PRIVATE` assets (server-side 200 with\n * the asset bytes; `etag` + `contentType` headers are exposed for\n * caching). When the server reports `text/plain` Content-Type (the\n * OpenAPI-documented format), the SDK decodes the base64 stream into\n * an `ArrayBuffer` transparently; binary Content-Types are passed\n * through verbatim.\n *\n * Implementation note: uses `redirect: \"manual\"` so the redirect-location\n * is observable. In Node.js this works; in a browser the redirect Location\n * is intentionally hidden by fetch — there, `PUBLIC` downloads will\n * surface as an opaque-redirect and the SDK throws. Browser code should\n * use the asset's `url` field from `get()` for `LINK` assets, or the\n * direct storage URL for `PUBLIC` `BLOB` assets (typically delivered via\n * an `<img>` tag rather than `download()`).\n */\n async download(\n assetId: string,\n auth: AuthContext = SERVICE,\n ): Promise<DownloadResult> {\n const path = `${this.base()}/${assetId}/download`;\n const res = await this.ctx.http.requestRaw(\n { method: \"GET\", path, auth },\n { redirect: \"manual\" },\n );\n\n // PUBLIC: server-side redirect — capture Location.\n if (res.status >= 300 && res.status < 400) {\n const url = res.headers.get(\"location\");\n if (!url) {\n throw new EmporixError(\n `media.download: ${res.status} response without a Location header`,\n res.status,\n );\n }\n return { kind: \"redirect\", url };\n }\n\n // PRIVATE: bytes.\n if (res.ok) {\n const etag = res.headers.get(\"etag\") ?? undefined;\n const contentType = res.headers.get(\"content-type\") ?? undefined;\n let data: ArrayBuffer;\n if (contentType?.startsWith(\"text/plain\")) {\n // Per OpenAPI spec the server returns the byte stream as a\n // base64-encoded text/plain body. Decode once into raw bytes\n // so callers always see an ArrayBuffer.\n const text = await res.text();\n const bin = atob(text);\n const arr = new Uint8Array(bin.length);\n for (let i = 0; i < bin.length; i++) arr[i] = bin.charCodeAt(i);\n data = arr.buffer;\n } else {\n data = await res.arrayBuffer();\n }\n return {\n kind: \"bytes\",\n data,\n ...(etag !== undefined ? { etag } : {}),\n ...(contentType !== undefined ? { contentType } : {}),\n };\n }\n\n // Error: surface via the standard EmporixError hierarchy.\n const text = await res.text();\n let parsed: unknown = undefined;\n if (text) {\n try {\n parsed = JSON.parse(text);\n } catch {\n parsed = text;\n }\n }\n throw errorFromResponse(res.status, `GET ${path} → ${res.status}`, parsed);\n }\n\n /** Multipart upload sugar: builds `AssetCreateBlob` from input. */\n async uploadFile(\n input: {\n file: Blob;\n productId?: string;\n filename?: string;\n mimeType?: string;\n access?: \"PUBLIC\" | \"PRIVATE\";\n },\n auth: AuthContext = SERVICE,\n ): Promise<{ id: string }> {\n const body: AssetCreateBlobInput = {\n type: \"BLOB\",\n access: input.access ?? \"PUBLIC\",\n ...(input.productId\n ? { refIds: [{ type: \"PRODUCT\", id: input.productId }] }\n : {}),\n ...(input.filename || input.mimeType\n ? {\n details: {\n ...(input.filename ? { filename: input.filename } : {}),\n ...(input.mimeType ? { mimeType: input.mimeType } : {}),\n },\n }\n : {}),\n };\n return this.create({ kind: \"blob\", file: input.file, body }, auth);\n }\n\n /**\n * Replace the file content of an existing BLOB asset. Sugar over\n * {@link update} with `kind: \"blob\"`. `access` is required because the\n * field is immutable and the server validates that the patch matches.\n * Pass `version` from `asset.metadata.version` if you want optimistic\n * locking (recommended when concurrent writers are possible).\n */\n async replaceFile(\n assetId: string,\n input: {\n file: Blob;\n access: \"PUBLIC\" | \"PRIVATE\";\n filename?: string;\n mimeType?: string;\n version?: number;\n },\n auth: AuthContext = SERVICE,\n ): Promise<Asset> {\n const body: AssetUpdateBlobInput = {\n type: \"BLOB\",\n access: input.access,\n ...(input.filename || input.mimeType\n ? {\n details: {\n ...(input.filename ? { filename: input.filename } : {}),\n ...(input.mimeType ? { mimeType: input.mimeType } : {}),\n },\n }\n : {}),\n ...(input.version !== undefined\n ? { metadata: { version: input.version } }\n : {}),\n };\n return this.update(assetId, { kind: \"blob\", file: input.file, body }, auth);\n }\n\n /** External-URL sugar: builds `AssetCreateLink`. */\n async link(\n input: { url: string; productId?: string; access?: \"PUBLIC\" | \"PRIVATE\" },\n auth: AuthContext = SERVICE,\n ): Promise<{ id: string }> {\n const body: AssetCreateLinkInput = {\n type: \"LINK\",\n access: input.access ?? \"PUBLIC\",\n url: input.url,\n ...(input.productId\n ? { refIds: [{ type: \"PRODUCT\", id: input.productId }] }\n : {}),\n };\n return this.create({ kind: \"link\", body }, auth);\n }\n\n /** Idempotently add a PRODUCT refId to an asset. */\n async attachToProduct(\n assetId: string,\n productId: string,\n auth: AuthContext = SERVICE,\n ): Promise<Asset> {\n const a = await this.get(assetId, auth);\n const refIds: AssetRefId[] = a.refIds ?? [];\n if (refIds.some((r) => isProductRef(r, productId))) return a;\n const next: AssetRefId[] = [...refIds, { type: \"PRODUCT\", id: productId }];\n // Preserve the asset's type discriminator so the update body satisfies\n // the AssetUpdateBlob | AssetUpdateLink union.\n const patch = { type: a.type, refIds: next } as unknown as AssetUpdateInput;\n return this.update(assetId, { kind: \"json\", body: patch }, auth);\n }\n\n /** Remove a PRODUCT refId from an asset (no-op if absent). */\n async detachFromProduct(\n assetId: string,\n productId: string,\n auth: AuthContext = SERVICE,\n ): Promise<Asset> {\n const a = await this.get(assetId, auth);\n const refIds: AssetRefId[] = a.refIds ?? [];\n const next = refIds.filter((r) => !isProductRef(r, productId));\n if (next.length === refIds.length) return a;\n const patch = { type: a.type, refIds: next } as unknown as AssetUpdateInput;\n return this.update(assetId, { kind: \"json\", body: patch }, auth);\n }\n\n /** Convenience: list assets attached to a product (server-side filter). */\n async listForProduct(\n productId: string,\n auth: AuthContext = SERVICE,\n ): Promise<PaginatedItems<Asset>> {\n return this.list({ \"refIds.id\": productId }, auth);\n }\n}\n\n// Re-export so consumers can `import { isProductRef } from \"@viu/emporix-sdk/media\"`?\n// Internal helper — kept module-private; convenience methods in Task 4 use it.\nexport const _internalMedia = { isProductRef };\n"]}
|
package/dist/media.d.cts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { C as ClientContext, b as AuthContext } from './context-
|
|
1
|
+
import { C as ClientContext, b as AuthContext, P as PaginatedItems } from './context-mMTqsW1Z.cjs';
|
|
2
2
|
|
|
3
3
|
type MetadataUpdate = {
|
|
4
4
|
/**
|
|
@@ -156,9 +156,44 @@ type AssetUpdateBlob = Asset$1 & {
|
|
|
156
156
|
/** Generated media types (caller sends the exact wire shape). */
|
|
157
157
|
type AssetCreateBlobInput = AssetCreateBlob;
|
|
158
158
|
type AssetCreateLinkInput = AssetCreateLink;
|
|
159
|
+
type AssetUpdateBlobInput = AssetUpdateBlob;
|
|
160
|
+
type AssetUpdateLinkInput = AssetUpdateLink;
|
|
159
161
|
type AssetUpdateInput = AssetUpdateBlob | AssetUpdateLink;
|
|
160
162
|
type Asset = GetAsset;
|
|
161
163
|
type AssetRefId = RefId;
|
|
164
|
+
/**
|
|
165
|
+
* Filter / pagination options for {@link MediaService.list}. The explicit
|
|
166
|
+
* fields are typed for autocomplete; the index signature stays open so
|
|
167
|
+
* Emporix `q`-syntax filters like `"refIds.id"` can be passed verbatim.
|
|
168
|
+
*/
|
|
169
|
+
interface ListAssetsQuery {
|
|
170
|
+
pageNumber?: number;
|
|
171
|
+
pageSize?: number;
|
|
172
|
+
/** Emporix sort syntax, e.g. `"name,metadata.createdAt:desc"`. */
|
|
173
|
+
sort?: string;
|
|
174
|
+
/** Emporix `q`-syntax filter, e.g. `"name:hero"`. */
|
|
175
|
+
q?: string;
|
|
176
|
+
[key: string]: string | number | undefined;
|
|
177
|
+
}
|
|
178
|
+
/**
|
|
179
|
+
* Result of {@link MediaService.download}. The endpoint's behaviour depends
|
|
180
|
+
* on the asset's `access`:
|
|
181
|
+
* - `PUBLIC` assets respond with a 30x redirect whose `Location` header
|
|
182
|
+
* carries the externally-cacheable storage URL — useful when the
|
|
183
|
+
* storefront wants to send the user there directly.
|
|
184
|
+
* - `PRIVATE` assets respond with the raw bytes (plus an `ETag` header
|
|
185
|
+
* for caching). The asset is delivered through Cloudinary behind the
|
|
186
|
+
* server-side service token, never publicly addressable.
|
|
187
|
+
*/
|
|
188
|
+
type DownloadResult = {
|
|
189
|
+
kind: "redirect";
|
|
190
|
+
url: string;
|
|
191
|
+
} | {
|
|
192
|
+
kind: "bytes";
|
|
193
|
+
data: ArrayBuffer;
|
|
194
|
+
etag?: string;
|
|
195
|
+
contentType?: string;
|
|
196
|
+
};
|
|
162
197
|
declare function isProductRef(r: AssetRefId, productId: string): boolean;
|
|
163
198
|
/**
|
|
164
199
|
* Media assets (BLOB/LINK). All endpoints require a backend-only scope
|
|
@@ -179,14 +214,61 @@ declare class MediaService {
|
|
|
179
214
|
}, auth?: AuthContext): Promise<{
|
|
180
215
|
id: string;
|
|
181
216
|
}>;
|
|
182
|
-
/**
|
|
183
|
-
|
|
217
|
+
/**
|
|
218
|
+
* List assets, wrapped in the shared {@link PaginatedItems} envelope so
|
|
219
|
+
* callers can iterate over pages consistently with the rest of the SDK.
|
|
220
|
+
* `hasNextPage` is the standard SDK heuristic: `true` when the returned
|
|
221
|
+
* page is full (`items.length === requested pageSize`). Pagination
|
|
222
|
+
* defaults match Emporix's server defaults (`pageNumber: 1`,
|
|
223
|
+
* `pageSize: 60`) so an empty `query` produces stable expectations.
|
|
224
|
+
*/
|
|
225
|
+
list(query?: ListAssetsQuery, auth?: AuthContext): Promise<PaginatedItems<Asset>>;
|
|
184
226
|
/** Fetch an asset by id. */
|
|
185
227
|
get(assetId: string, auth?: AuthContext): Promise<Asset>;
|
|
186
|
-
/**
|
|
187
|
-
|
|
228
|
+
/**
|
|
229
|
+
* Update an asset. The input is a discriminated union mirroring
|
|
230
|
+
* {@link create}:
|
|
231
|
+
* - `{ kind: "json", body }` — metadata-only patch (BLOB or LINK).
|
|
232
|
+
* Sends `application/json`. Used for `refIds`, `details`, `metadata`,
|
|
233
|
+
* or `url` changes.
|
|
234
|
+
* - `{ kind: "blob", file, body }` — replaces the BLOB file content
|
|
235
|
+
* (max 10MB) AND patches metadata in the same request. Sends
|
|
236
|
+
* `multipart/form-data`.
|
|
237
|
+
*
|
|
238
|
+
* `type` and `access` are immutable per Emporix — they must match the
|
|
239
|
+
* existing asset's values. Optimistic-locking is via `body.metadata.version`;
|
|
240
|
+
* the server returns 409 Conflict on a stale version.
|
|
241
|
+
*/
|
|
242
|
+
update(assetId: string, input: {
|
|
243
|
+
kind: "json";
|
|
244
|
+
body: AssetUpdateInput;
|
|
245
|
+
} | {
|
|
246
|
+
kind: "blob";
|
|
247
|
+
file: Blob;
|
|
248
|
+
body: AssetUpdateBlobInput;
|
|
249
|
+
}, auth?: AuthContext): Promise<Asset>;
|
|
188
250
|
/** Remove an asset. */
|
|
189
251
|
remove(assetId: string, auth?: AuthContext): Promise<void>;
|
|
252
|
+
/**
|
|
253
|
+
* Download an asset by id. Returns a discriminated union:
|
|
254
|
+
* - `{ kind: "redirect", url }` for `PUBLIC` assets (server-side 30x with
|
|
255
|
+
* the storage URL in `Location`).
|
|
256
|
+
* - `{ kind: "bytes", data }` for `PRIVATE` assets (server-side 200 with
|
|
257
|
+
* the asset bytes; `etag` + `contentType` headers are exposed for
|
|
258
|
+
* caching). When the server reports `text/plain` Content-Type (the
|
|
259
|
+
* OpenAPI-documented format), the SDK decodes the base64 stream into
|
|
260
|
+
* an `ArrayBuffer` transparently; binary Content-Types are passed
|
|
261
|
+
* through verbatim.
|
|
262
|
+
*
|
|
263
|
+
* Implementation note: uses `redirect: "manual"` so the redirect-location
|
|
264
|
+
* is observable. In Node.js this works; in a browser the redirect Location
|
|
265
|
+
* is intentionally hidden by fetch — there, `PUBLIC` downloads will
|
|
266
|
+
* surface as an opaque-redirect and the SDK throws. Browser code should
|
|
267
|
+
* use the asset's `url` field from `get()` for `LINK` assets, or the
|
|
268
|
+
* direct storage URL for `PUBLIC` `BLOB` assets (typically delivered via
|
|
269
|
+
* an `<img>` tag rather than `download()`).
|
|
270
|
+
*/
|
|
271
|
+
download(assetId: string, auth?: AuthContext): Promise<DownloadResult>;
|
|
190
272
|
/** Multipart upload sugar: builds `AssetCreateBlob` from input. */
|
|
191
273
|
uploadFile(input: {
|
|
192
274
|
file: Blob;
|
|
@@ -197,6 +279,20 @@ declare class MediaService {
|
|
|
197
279
|
}, auth?: AuthContext): Promise<{
|
|
198
280
|
id: string;
|
|
199
281
|
}>;
|
|
282
|
+
/**
|
|
283
|
+
* Replace the file content of an existing BLOB asset. Sugar over
|
|
284
|
+
* {@link update} with `kind: "blob"`. `access` is required because the
|
|
285
|
+
* field is immutable and the server validates that the patch matches.
|
|
286
|
+
* Pass `version` from `asset.metadata.version` if you want optimistic
|
|
287
|
+
* locking (recommended when concurrent writers are possible).
|
|
288
|
+
*/
|
|
289
|
+
replaceFile(assetId: string, input: {
|
|
290
|
+
file: Blob;
|
|
291
|
+
access: "PUBLIC" | "PRIVATE";
|
|
292
|
+
filename?: string;
|
|
293
|
+
mimeType?: string;
|
|
294
|
+
version?: number;
|
|
295
|
+
}, auth?: AuthContext): Promise<Asset>;
|
|
200
296
|
/** External-URL sugar: builds `AssetCreateLink`. */
|
|
201
297
|
link(input: {
|
|
202
298
|
url: string;
|
|
@@ -210,10 +306,10 @@ declare class MediaService {
|
|
|
210
306
|
/** Remove a PRODUCT refId from an asset (no-op if absent). */
|
|
211
307
|
detachFromProduct(assetId: string, productId: string, auth?: AuthContext): Promise<Asset>;
|
|
212
308
|
/** Convenience: list assets attached to a product (server-side filter). */
|
|
213
|
-
listForProduct(productId: string, auth?: AuthContext): Promise<Asset
|
|
309
|
+
listForProduct(productId: string, auth?: AuthContext): Promise<PaginatedItems<Asset>>;
|
|
214
310
|
}
|
|
215
311
|
declare const _internalMedia: {
|
|
216
312
|
isProductRef: typeof isProductRef;
|
|
217
313
|
};
|
|
218
314
|
|
|
219
|
-
export { type Asset, type AssetCreateBlobInput, type AssetCreateLinkInput, type AssetRefId, type AssetUpdateInput, MediaService, _internalMedia };
|
|
315
|
+
export { type Asset, type AssetCreateBlobInput, type AssetCreateLinkInput, type AssetRefId, type AssetUpdateBlobInput, type AssetUpdateInput, type AssetUpdateLinkInput, type DownloadResult, type ListAssetsQuery, MediaService, _internalMedia };
|