@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/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,43 @@
|
|
|
1
1
|
# @viu/emporix-sdk
|
|
2
2
|
|
|
3
|
+
## 2.0.0
|
|
4
|
+
|
|
5
|
+
### Major Changes
|
|
6
|
+
|
|
7
|
+
- [#52](https://github.com/viuteam/emporix-sdk/pull/52) [`26640fe`](https://github.com/viuteam/emporix-sdk/commit/26640fe281083e6ce0475a547e292ac82ba7d9bf) Thanks [@amnael1](https://github.com/amnael1)! - Add `client.media.download(assetId, auth?)` for retrieving asset content via `GET /media/{tenant}/assets/{id}/download`. Returns a discriminated union:
|
|
8
|
+
- `{ kind: "redirect", url }` for `PUBLIC` assets — the SDK captures the server's 30x `Location` header so the caller can redirect the user to the storage URL without proxying bytes.
|
|
9
|
+
- `{ kind: "bytes", data, etag?, contentType? }` for `PRIVATE` assets — the SDK returns the response body as an `ArrayBuffer`. When the server uses the OpenAPI-documented `text/plain` + base64 wire format, the SDK decodes it transparently; binary content types are passed through.
|
|
10
|
+
|
|
11
|
+
Also adds a low-level `HttpClient.requestRaw` escape hatch (used internally by `download`) for endpoints whose responses are not JSON. Auth resolution + timeout + logging are applied; the retry-on-5xx and 401-reauth-once paths from `request` are intentionally skipped (callers of `requestRaw` handle their own response shape).
|
|
12
|
+
|
|
13
|
+
Browser note: `download()` uses `redirect: "manual"` so it can observe `PUBLIC` redirect URLs. In Node this works as documented; in a browser the redirect `Location` is hidden by the fetch spec — there, `PUBLIC` downloads will surface as an error. Browser code should use the asset's `url` field (for `LINK` assets) or render `PUBLIC` `BLOB` assets via the storage URL directly.
|
|
14
|
+
|
|
15
|
+
**Breaking — `media.list()`** now returns `PaginatedItems<Asset>` instead of `Asset[]`. Callers must read `.items` for the array. The new shape includes `pageNumber`, `pageSize`, and the standard `hasNextPage` heuristic so paginated listings behave like every other list endpoint in the SDK. The previous shape silently truncated at the server-default page size (60); the new shape makes pagination explicit.
|
|
16
|
+
|
|
17
|
+
```ts
|
|
18
|
+
// before
|
|
19
|
+
const assets = await client.media.list(); // Asset[], page 1 only
|
|
20
|
+
// after
|
|
21
|
+
const { items, hasNextPage } = await client.media.list({ pageSize: 100 });
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
`client.media.listForProduct(productId)` shares the same envelope change.
|
|
25
|
+
|
|
26
|
+
**Breaking — `media.update()`** now takes a discriminated input matching `create()`:
|
|
27
|
+
|
|
28
|
+
```ts
|
|
29
|
+
// before
|
|
30
|
+
await client.media.update(id, patch);
|
|
31
|
+
// after — JSON path (refIds, details, url, metadata)
|
|
32
|
+
await client.media.update(id, { kind: "json", body: patch });
|
|
33
|
+
// after — BLOB file-replacement (multipart, up to 10MB)
|
|
34
|
+
await client.media.update(id, { kind: "blob", file, body });
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
The previous signature only supported the JSON path; the new BLOB path closes the gap where re-uploading bytes required `remove` + `create` (and lost the asset id). A new `client.media.replaceFile(assetId, { file, access, ... })` sugar wraps the common case.
|
|
38
|
+
|
|
39
|
+
New named type exports: `AssetUpdateBlobInput`, `AssetUpdateLinkInput`, `ListAssetsQuery`.
|
|
40
|
+
|
|
3
41
|
## 1.0.0
|
|
4
42
|
|
|
5
43
|
### Minor Changes
|
package/README.md
CHANGED
|
@@ -70,6 +70,7 @@ await sdk.customers.me(auth.raw(externalJwt));
|
|
|
70
70
|
| `carts.*` | — | explicit `customer` or `anonymous` |
|
|
71
71
|
| `carts.merge` | — | `customer` |
|
|
72
72
|
| `companies.*` / `contacts.*` / `locations.*` / `customerGroups.*` (B2B) | — | `customer` (reads need `*_read_own`; mutations need `*_manage`) |
|
|
73
|
+
| `media.*` (Asset CRUD + download) | `service` | `service` — server-only (`media.asset_read` / `media.asset_manage`); never call from the browser |
|
|
73
74
|
|
|
74
75
|
`AuthContext` is **per call, never stored** — one client safely serves many
|
|
75
76
|
concurrent shoppers (SSR/edge/multi-tenant). SDK-managed (`service`/`anonymous`)
|
|
@@ -98,6 +99,18 @@ customer's role lacks scope for surface as `EmporixInsufficientScopeError`
|
|
|
98
99
|
(extends `EmporixForbiddenError`, carries `requiredScope`). See
|
|
99
100
|
[`../../docs/b2b.md`](../../docs/b2b.md).
|
|
100
101
|
|
|
102
|
+
## Media
|
|
103
|
+
|
|
104
|
+
`sdk.media` covers the full `/media/{tenant}/assets/*` surface: create
|
|
105
|
+
(`uploadFile` / `link` / `create`), list (paginated, `PaginatedItems<Asset>`),
|
|
106
|
+
get, update (JSON metadata or BLOB multipart file-replacement via the
|
|
107
|
+
`replaceFile` sugar), remove, and download (resolves to either a redirect
|
|
108
|
+
URL for `PUBLIC` assets or an `ArrayBuffer` for `PRIVATE`). All endpoints
|
|
109
|
+
require a server-only scope — every call defaults to a `service`
|
|
110
|
+
`AuthContext`. Storefronts read media via `product.productMedia` (denormalised
|
|
111
|
+
on the product) or the `useProductMedia(productId)` hook, not by calling
|
|
112
|
+
the Media service directly. See [`../../docs/media.md`](../../docs/media.md).
|
|
113
|
+
|
|
101
114
|
## Subpath exports
|
|
102
115
|
|
|
103
116
|
`@viu/emporix-sdk` (everything) plus `./customer`, `./product`, `./category`,
|
package/dist/cart.d.cts
CHANGED
package/dist/cart.d.ts
CHANGED
package/dist/category.d.cts
CHANGED
package/dist/category.d.ts
CHANGED
package/dist/checkout.d.cts
CHANGED
package/dist/checkout.d.ts
CHANGED
|
@@ -0,0 +1,248 @@
|
|
|
1
|
+
import { EmporixError, errorFromResponse } from './chunk-KTODT6DV.js';
|
|
2
|
+
|
|
3
|
+
// src/services/media.ts
|
|
4
|
+
var SERVICE = { kind: "service" };
|
|
5
|
+
function isProductRef(r, productId) {
|
|
6
|
+
return r.type === "PRODUCT" && r.id === productId;
|
|
7
|
+
}
|
|
8
|
+
var MediaService = class {
|
|
9
|
+
constructor(ctx) {
|
|
10
|
+
this.ctx = ctx;
|
|
11
|
+
}
|
|
12
|
+
ctx;
|
|
13
|
+
base() {
|
|
14
|
+
return `/media/${this.ctx.tenant}/assets`;
|
|
15
|
+
}
|
|
16
|
+
/** Create an asset. BLOB uploads via multipart; LINK via JSON. */
|
|
17
|
+
async create(input, auth = SERVICE) {
|
|
18
|
+
if (input.kind === "blob") {
|
|
19
|
+
const fd = new FormData();
|
|
20
|
+
fd.set("file", input.file);
|
|
21
|
+
fd.set("body", JSON.stringify(input.body));
|
|
22
|
+
return this.ctx.http.request({
|
|
23
|
+
method: "POST",
|
|
24
|
+
path: this.base(),
|
|
25
|
+
auth,
|
|
26
|
+
body: fd
|
|
27
|
+
});
|
|
28
|
+
}
|
|
29
|
+
return this.ctx.http.request({
|
|
30
|
+
method: "POST",
|
|
31
|
+
path: this.base(),
|
|
32
|
+
auth,
|
|
33
|
+
body: input.body
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* List assets, wrapped in the shared {@link PaginatedItems} envelope so
|
|
38
|
+
* callers can iterate over pages consistently with the rest of the SDK.
|
|
39
|
+
* `hasNextPage` is the standard SDK heuristic: `true` when the returned
|
|
40
|
+
* page is full (`items.length === requested pageSize`). Pagination
|
|
41
|
+
* defaults match Emporix's server defaults (`pageNumber: 1`,
|
|
42
|
+
* `pageSize: 60`) so an empty `query` produces stable expectations.
|
|
43
|
+
*/
|
|
44
|
+
async list(query = {}, auth = SERVICE) {
|
|
45
|
+
const pageNumber = query.pageNumber ?? 1;
|
|
46
|
+
const pageSize = query.pageSize ?? 60;
|
|
47
|
+
const items = await this.ctx.http.request({
|
|
48
|
+
method: "GET",
|
|
49
|
+
path: this.base(),
|
|
50
|
+
auth,
|
|
51
|
+
query: { ...query, pageNumber, pageSize }
|
|
52
|
+
});
|
|
53
|
+
return {
|
|
54
|
+
items,
|
|
55
|
+
pageNumber,
|
|
56
|
+
pageSize,
|
|
57
|
+
hasNextPage: items.length === pageSize
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
/** Fetch an asset by id. */
|
|
61
|
+
async get(assetId, auth = SERVICE) {
|
|
62
|
+
return this.ctx.http.request({
|
|
63
|
+
method: "GET",
|
|
64
|
+
path: `${this.base()}/${assetId}`,
|
|
65
|
+
auth
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Update an asset. The input is a discriminated union mirroring
|
|
70
|
+
* {@link create}:
|
|
71
|
+
* - `{ kind: "json", body }` — metadata-only patch (BLOB or LINK).
|
|
72
|
+
* Sends `application/json`. Used for `refIds`, `details`, `metadata`,
|
|
73
|
+
* or `url` changes.
|
|
74
|
+
* - `{ kind: "blob", file, body }` — replaces the BLOB file content
|
|
75
|
+
* (max 10MB) AND patches metadata in the same request. Sends
|
|
76
|
+
* `multipart/form-data`.
|
|
77
|
+
*
|
|
78
|
+
* `type` and `access` are immutable per Emporix — they must match the
|
|
79
|
+
* existing asset's values. Optimistic-locking is via `body.metadata.version`;
|
|
80
|
+
* the server returns 409 Conflict on a stale version.
|
|
81
|
+
*/
|
|
82
|
+
async update(assetId, input, auth = SERVICE) {
|
|
83
|
+
if (input.kind === "blob") {
|
|
84
|
+
const fd = new FormData();
|
|
85
|
+
fd.set("file", input.file);
|
|
86
|
+
fd.set("body", JSON.stringify(input.body));
|
|
87
|
+
return this.ctx.http.request({
|
|
88
|
+
method: "PUT",
|
|
89
|
+
path: `${this.base()}/${assetId}`,
|
|
90
|
+
auth,
|
|
91
|
+
body: fd
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
return this.ctx.http.request({
|
|
95
|
+
method: "PUT",
|
|
96
|
+
path: `${this.base()}/${assetId}`,
|
|
97
|
+
auth,
|
|
98
|
+
body: input.body
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
/** Remove an asset. */
|
|
102
|
+
async remove(assetId, auth = SERVICE) {
|
|
103
|
+
await this.ctx.http.request({
|
|
104
|
+
method: "DELETE",
|
|
105
|
+
path: `${this.base()}/${assetId}`,
|
|
106
|
+
auth
|
|
107
|
+
});
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* Download an asset by id. Returns a discriminated union:
|
|
111
|
+
* - `{ kind: "redirect", url }` for `PUBLIC` assets (server-side 30x with
|
|
112
|
+
* the storage URL in `Location`).
|
|
113
|
+
* - `{ kind: "bytes", data }` for `PRIVATE` assets (server-side 200 with
|
|
114
|
+
* the asset bytes; `etag` + `contentType` headers are exposed for
|
|
115
|
+
* caching). When the server reports `text/plain` Content-Type (the
|
|
116
|
+
* OpenAPI-documented format), the SDK decodes the base64 stream into
|
|
117
|
+
* an `ArrayBuffer` transparently; binary Content-Types are passed
|
|
118
|
+
* through verbatim.
|
|
119
|
+
*
|
|
120
|
+
* Implementation note: uses `redirect: "manual"` so the redirect-location
|
|
121
|
+
* is observable. In Node.js this works; in a browser the redirect Location
|
|
122
|
+
* is intentionally hidden by fetch — there, `PUBLIC` downloads will
|
|
123
|
+
* surface as an opaque-redirect and the SDK throws. Browser code should
|
|
124
|
+
* use the asset's `url` field from `get()` for `LINK` assets, or the
|
|
125
|
+
* direct storage URL for `PUBLIC` `BLOB` assets (typically delivered via
|
|
126
|
+
* an `<img>` tag rather than `download()`).
|
|
127
|
+
*/
|
|
128
|
+
async download(assetId, auth = SERVICE) {
|
|
129
|
+
const path = `${this.base()}/${assetId}/download`;
|
|
130
|
+
const res = await this.ctx.http.requestRaw(
|
|
131
|
+
{ method: "GET", path, auth },
|
|
132
|
+
{ redirect: "manual" }
|
|
133
|
+
);
|
|
134
|
+
if (res.status >= 300 && res.status < 400) {
|
|
135
|
+
const url = res.headers.get("location");
|
|
136
|
+
if (!url) {
|
|
137
|
+
throw new EmporixError(
|
|
138
|
+
`media.download: ${res.status} response without a Location header`,
|
|
139
|
+
res.status
|
|
140
|
+
);
|
|
141
|
+
}
|
|
142
|
+
return { kind: "redirect", url };
|
|
143
|
+
}
|
|
144
|
+
if (res.ok) {
|
|
145
|
+
const etag = res.headers.get("etag") ?? void 0;
|
|
146
|
+
const contentType = res.headers.get("content-type") ?? void 0;
|
|
147
|
+
let data;
|
|
148
|
+
if (contentType?.startsWith("text/plain")) {
|
|
149
|
+
const text2 = await res.text();
|
|
150
|
+
const bin = atob(text2);
|
|
151
|
+
const arr = new Uint8Array(bin.length);
|
|
152
|
+
for (let i = 0; i < bin.length; i++) arr[i] = bin.charCodeAt(i);
|
|
153
|
+
data = arr.buffer;
|
|
154
|
+
} else {
|
|
155
|
+
data = await res.arrayBuffer();
|
|
156
|
+
}
|
|
157
|
+
return {
|
|
158
|
+
kind: "bytes",
|
|
159
|
+
data,
|
|
160
|
+
...etag !== void 0 ? { etag } : {},
|
|
161
|
+
...contentType !== void 0 ? { contentType } : {}
|
|
162
|
+
};
|
|
163
|
+
}
|
|
164
|
+
const text = await res.text();
|
|
165
|
+
let parsed = void 0;
|
|
166
|
+
if (text) {
|
|
167
|
+
try {
|
|
168
|
+
parsed = JSON.parse(text);
|
|
169
|
+
} catch {
|
|
170
|
+
parsed = text;
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
throw errorFromResponse(res.status, `GET ${path} \u2192 ${res.status}`, parsed);
|
|
174
|
+
}
|
|
175
|
+
/** Multipart upload sugar: builds `AssetCreateBlob` from input. */
|
|
176
|
+
async uploadFile(input, auth = SERVICE) {
|
|
177
|
+
const body = {
|
|
178
|
+
type: "BLOB",
|
|
179
|
+
access: input.access ?? "PUBLIC",
|
|
180
|
+
...input.productId ? { refIds: [{ type: "PRODUCT", id: input.productId }] } : {},
|
|
181
|
+
...input.filename || input.mimeType ? {
|
|
182
|
+
details: {
|
|
183
|
+
...input.filename ? { filename: input.filename } : {},
|
|
184
|
+
...input.mimeType ? { mimeType: input.mimeType } : {}
|
|
185
|
+
}
|
|
186
|
+
} : {}
|
|
187
|
+
};
|
|
188
|
+
return this.create({ kind: "blob", file: input.file, body }, auth);
|
|
189
|
+
}
|
|
190
|
+
/**
|
|
191
|
+
* Replace the file content of an existing BLOB asset. Sugar over
|
|
192
|
+
* {@link update} with `kind: "blob"`. `access` is required because the
|
|
193
|
+
* field is immutable and the server validates that the patch matches.
|
|
194
|
+
* Pass `version` from `asset.metadata.version` if you want optimistic
|
|
195
|
+
* locking (recommended when concurrent writers are possible).
|
|
196
|
+
*/
|
|
197
|
+
async replaceFile(assetId, input, auth = SERVICE) {
|
|
198
|
+
const body = {
|
|
199
|
+
type: "BLOB",
|
|
200
|
+
access: input.access,
|
|
201
|
+
...input.filename || input.mimeType ? {
|
|
202
|
+
details: {
|
|
203
|
+
...input.filename ? { filename: input.filename } : {},
|
|
204
|
+
...input.mimeType ? { mimeType: input.mimeType } : {}
|
|
205
|
+
}
|
|
206
|
+
} : {},
|
|
207
|
+
...input.version !== void 0 ? { metadata: { version: input.version } } : {}
|
|
208
|
+
};
|
|
209
|
+
return this.update(assetId, { kind: "blob", file: input.file, body }, auth);
|
|
210
|
+
}
|
|
211
|
+
/** External-URL sugar: builds `AssetCreateLink`. */
|
|
212
|
+
async link(input, auth = SERVICE) {
|
|
213
|
+
const body = {
|
|
214
|
+
type: "LINK",
|
|
215
|
+
access: input.access ?? "PUBLIC",
|
|
216
|
+
url: input.url,
|
|
217
|
+
...input.productId ? { refIds: [{ type: "PRODUCT", id: input.productId }] } : {}
|
|
218
|
+
};
|
|
219
|
+
return this.create({ kind: "link", body }, auth);
|
|
220
|
+
}
|
|
221
|
+
/** Idempotently add a PRODUCT refId to an asset. */
|
|
222
|
+
async attachToProduct(assetId, productId, auth = SERVICE) {
|
|
223
|
+
const a = await this.get(assetId, auth);
|
|
224
|
+
const refIds = a.refIds ?? [];
|
|
225
|
+
if (refIds.some((r) => isProductRef(r, productId))) return a;
|
|
226
|
+
const next = [...refIds, { type: "PRODUCT", id: productId }];
|
|
227
|
+
const patch = { type: a.type, refIds: next };
|
|
228
|
+
return this.update(assetId, { kind: "json", body: patch }, auth);
|
|
229
|
+
}
|
|
230
|
+
/** Remove a PRODUCT refId from an asset (no-op if absent). */
|
|
231
|
+
async detachFromProduct(assetId, productId, auth = SERVICE) {
|
|
232
|
+
const a = await this.get(assetId, auth);
|
|
233
|
+
const refIds = a.refIds ?? [];
|
|
234
|
+
const next = refIds.filter((r) => !isProductRef(r, productId));
|
|
235
|
+
if (next.length === refIds.length) return a;
|
|
236
|
+
const patch = { type: a.type, refIds: next };
|
|
237
|
+
return this.update(assetId, { kind: "json", body: patch }, auth);
|
|
238
|
+
}
|
|
239
|
+
/** Convenience: list assets attached to a product (server-side filter). */
|
|
240
|
+
async listForProduct(productId, auth = SERVICE) {
|
|
241
|
+
return this.list({ "refIds.id": productId }, auth);
|
|
242
|
+
}
|
|
243
|
+
};
|
|
244
|
+
var _internalMedia = { isProductRef };
|
|
245
|
+
|
|
246
|
+
export { MediaService, _internalMedia };
|
|
247
|
+
//# sourceMappingURL=chunk-WSXRUYBE.js.map
|
|
248
|
+
//# sourceMappingURL=chunk-WSXRUYBE.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/services/media.ts"],"names":["text"],"mappings":"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","file":"chunk-WSXRUYBE.js","sourcesContent":["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/companies.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 } from './context-mMTqsW1Z.cjs';
|
|
2
2
|
import { L as LegalEntity, f as LegalEntityCreate, h as LegalEntityUpdate } from './types.gen-D1QuqPLs.cjs';
|
|
3
3
|
export { A as AccountLimit, e as CustomerGroupRef, g as LegalEntityType, i as LegalInfo } from './types.gen-D1QuqPLs.cjs';
|
|
4
4
|
|
package/dist/companies.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { C as ClientContext, b as AuthContext } from './context-
|
|
1
|
+
import { C as ClientContext, b as AuthContext } from './context-mMTqsW1Z.js';
|
|
2
2
|
import { L as LegalEntity, f as LegalEntityCreate, h as LegalEntityUpdate } from './types.gen-D1QuqPLs.js';
|
|
3
3
|
export { A as AccountLimit, e as CustomerGroupRef, g as LegalEntityType, i as LegalInfo } from './types.gen-D1QuqPLs.js';
|
|
4
4
|
|
package/dist/contacts.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 } from './context-mMTqsW1Z.cjs';
|
|
2
2
|
import { C as ContactAssignment, a as ContactAssignmentCreate, c as ContactAssignmentUpdate } from './types.gen-D1QuqPLs.cjs';
|
|
3
3
|
export { b as ContactAssignmentType } from './types.gen-D1QuqPLs.cjs';
|
|
4
4
|
|
package/dist/contacts.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { C as ClientContext, b as AuthContext } from './context-
|
|
1
|
+
import { C as ClientContext, b as AuthContext } from './context-mMTqsW1Z.js';
|
|
2
2
|
import { C as ContactAssignment, a as ContactAssignmentCreate, c as ContactAssignmentUpdate } from './types.gen-D1QuqPLs.js';
|
|
3
3
|
export { b as ContactAssignmentType } from './types.gen-D1QuqPLs.js';
|
|
4
4
|
|
|
@@ -297,6 +297,21 @@ declare class HttpClient {
|
|
|
297
297
|
private readonly sleep;
|
|
298
298
|
constructor(opts: HttpClientOptions);
|
|
299
299
|
request<T = unknown>(o: RequestOptions): Promise<T>;
|
|
300
|
+
/**
|
|
301
|
+
* Single-shot fetch that returns the raw {@link Response} unparsed. Used by
|
|
302
|
+
* endpoints whose responses are not JSON (binary downloads, redirect-only
|
|
303
|
+
* endpoints). Skips the retry-on-5xx and 401-reauth-once paths intentionally
|
|
304
|
+
* — those depend on parsing the JSON error body, which a raw consumer must
|
|
305
|
+
* handle itself if needed. Auth resolution + timeout + logging are still
|
|
306
|
+
* applied.
|
|
307
|
+
*
|
|
308
|
+
* `extra.redirect` is forwarded to `fetch` so callers can use `'manual'` to
|
|
309
|
+
* capture 30x responses with their `Location` header (Node fetch exposes
|
|
310
|
+
* the header in this mode; browser fetch returns an opaque response).
|
|
311
|
+
*/
|
|
312
|
+
requestRaw(o: RequestOptions, extra?: {
|
|
313
|
+
redirect?: "follow" | "manual" | "error";
|
|
314
|
+
}): Promise<Response>;
|
|
300
315
|
}
|
|
301
316
|
|
|
302
317
|
/** Shared dependencies every service facade receives. */
|
|
@@ -297,6 +297,21 @@ declare class HttpClient {
|
|
|
297
297
|
private readonly sleep;
|
|
298
298
|
constructor(opts: HttpClientOptions);
|
|
299
299
|
request<T = unknown>(o: RequestOptions): Promise<T>;
|
|
300
|
+
/**
|
|
301
|
+
* Single-shot fetch that returns the raw {@link Response} unparsed. Used by
|
|
302
|
+
* endpoints whose responses are not JSON (binary downloads, redirect-only
|
|
303
|
+
* endpoints). Skips the retry-on-5xx and 401-reauth-once paths intentionally
|
|
304
|
+
* — those depend on parsing the JSON error body, which a raw consumer must
|
|
305
|
+
* handle itself if needed. Auth resolution + timeout + logging are still
|
|
306
|
+
* applied.
|
|
307
|
+
*
|
|
308
|
+
* `extra.redirect` is forwarded to `fetch` so callers can use `'manual'` to
|
|
309
|
+
* capture 30x responses with their `Location` header (Node fetch exposes
|
|
310
|
+
* the header in this mode; browser fetch returns an opaque response).
|
|
311
|
+
*/
|
|
312
|
+
requestRaw(o: RequestOptions, extra?: {
|
|
313
|
+
redirect?: "follow" | "manual" | "error";
|
|
314
|
+
}): Promise<Response>;
|
|
300
315
|
}
|
|
301
316
|
|
|
302
317
|
/** Shared dependencies every service facade receives. */
|
package/dist/customer.d.cts
CHANGED
package/dist/customer.d.ts
CHANGED