@classytic/commerce-client 0.1.0 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +18 -0
- package/README.md +42 -0
- package/dist/category-tree.d.ts +1 -3
- package/dist/category-tree.js +1 -2
- package/dist/delivery-types.d.ts +1 -3
- package/dist/index.d.ts +4 -4
- package/dist/index.js +6 -2
- package/dist/management-types.d.ts +27 -4
- package/dist/management.d.ts +11 -6
- package/dist/management.js +11 -10
- package/dist/platform.d.ts +34 -0
- package/dist/platform.js +28 -0
- package/dist/{types-C0cSi6Pk.d.ts → types-CwAQqf0Z.d.ts} +23 -3
- package/dist/types.d.ts +2 -2
- package/management.md +41 -0
- package/package.json +6 -2
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,21 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.2.0 — 2026-10-23
|
|
4
|
+
|
|
5
|
+
Add typed merchant collection CRUD and public collection list/detail methods with
|
|
6
|
+
cursor pagination over Arc Next. Add the collections media folder. Browser
|
|
7
|
+
entry points remain independent of React, MongoDB and server domain packages.
|
|
8
|
+
|
|
9
|
+
Dependencies are already at the latest published (`arc-next` 0.18.0, `media-transform` 0.3.0, `primitives` 0.34.0). Dev toolchain: `typescript@7.1.0-beta`, which tsdown 0.23.2 needs to emit declarations with TypeScript 7.
|
|
10
|
+
|
|
11
|
+
## 0.1.1 — 2026-10-20
|
|
12
|
+
|
|
13
|
+
Add tenant-scoped media removal through the guarded media resource, and type media folder, size and display metadata for Fluid library bindings. Report unsupported source formats and browser decoding failures before starting storage writes.
|
|
14
|
+
|
|
15
|
+
Add `@classytic/commerce-client/platform`: `createPlatformClient({ apiUrl, token })` for platform administration with a bearer token and no merchant organization. `organizations.list` pages organizations and `organizations.decide(id, { action, expectedStatus, note })` applies an approve, reject, suspend or reactivate decision (matches `@spinekit/auth` 0.17.0 `./organizations`).
|
|
16
|
+
|
|
17
|
+
Dependencies are already at the latest published (`arc-next` 0.18.0, `media-transform` 0.3.0, `primitives` 0.34.0).
|
|
18
|
+
|
|
3
19
|
## 0.1.0 — 2026-10-20
|
|
4
20
|
|
|
5
21
|
First publish.
|
|
@@ -19,3 +35,5 @@ Typed store profiles, catalog reads, guest checkout with explicit retry keys, an
|
|
|
19
35
|
The isolated `/management` entry supports merchant catalog/media, order commands
|
|
20
36
|
and cash collection, store settings, Telegram connections and delivery history,
|
|
21
37
|
plus API-key options, creation, rotation and revocation.
|
|
38
|
+
|
|
39
|
+
Platform administration: `./platform` exposes bearer-only organization lists and guarded status decisions without requiring a merchant organization.
|
package/README.md
CHANGED
|
@@ -128,3 +128,45 @@ and sort. Restart pagination when filters change. Public calls always enforce th
|
|
|
128
128
|
resolved store and published/priced visibility, and reject unknown query fields.
|
|
129
129
|
Merchant `products.getAll({ params })` continues to use Arc's normal allowed query
|
|
130
130
|
syntax, including tags, collections and base-price bracket filters.
|
|
131
|
+
|
|
132
|
+
### Managed collections
|
|
133
|
+
|
|
134
|
+
A collection is a store-owned merchandising group, independent of the category
|
|
135
|
+
hierarchy. Enable `modules: { collections: true }` on the catalog engine and mount
|
|
136
|
+
`createStorefrontCollectionResource` from `@spinekit/catalog/collections-storefront`.
|
|
137
|
+
Disabled engines register no collection model or indexes. The SDK stays browser-safe
|
|
138
|
+
and does not import catalog, MongoDB, React or the server modules.
|
|
139
|
+
|
|
140
|
+
```ts
|
|
141
|
+
const groups = await storefront.collections({ featured: true, limit: 8 });
|
|
142
|
+
const collection = await storefront.collection('eid-picks');
|
|
143
|
+
const products = await storefront.products({
|
|
144
|
+
collection: collection.slug,
|
|
145
|
+
category: 'children', // optional; intersects the collection
|
|
146
|
+
tag: 'cotton', // optional
|
|
147
|
+
sort: 'newest',
|
|
148
|
+
limit: 20,
|
|
149
|
+
});
|
|
150
|
+
// Pass groups.next / products.next as `after`, retaining the same filters.
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
Only visible collections are public. Hidden or missing collection filters return
|
|
154
|
+
404. `featured` and `displayOrder` are editorial settings, not sales rankings.
|
|
155
|
+
A collection does not belong to a category: its products can span categories, or a
|
|
156
|
+
frontend can intersect both filters. Storefront sections and page layout stay in
|
|
157
|
+
the frontend.
|
|
158
|
+
|
|
159
|
+
Merchant collection edits use the version returned by the management API:
|
|
160
|
+
|
|
161
|
+
```ts
|
|
162
|
+
await merchant.collections.update({
|
|
163
|
+
id: collection._id,
|
|
164
|
+
data: { name: 'Summer favourites' },
|
|
165
|
+
version: collection.__v,
|
|
166
|
+
});
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
Arc Next sends `If-Match` for this version. A 412 response means another editor
|
|
170
|
+
changed the collection: reload and review the latest record before saving again.
|
|
171
|
+
Keep the original version with an open draft; do not silently retry with a newer
|
|
172
|
+
version.
|
package/dist/category-tree.d.ts
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
//#region src/category-tree.d.ts
|
|
2
1
|
export interface CategoryBranch {
|
|
3
2
|
name: string;
|
|
4
3
|
slug: string;
|
|
@@ -16,5 +15,4 @@ export declare function categoryNavigation(rows: readonly {
|
|
|
16
15
|
name: string;
|
|
17
16
|
slug: string;
|
|
18
17
|
parent?: string | null;
|
|
19
|
-
}[]): CategoryBranch[];
|
|
20
|
-
//#endregion
|
|
18
|
+
}[]): CategoryBranch[];
|
package/dist/category-tree.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
|
|
1
|
+
// src/category-tree.ts
|
|
2
2
|
/** Flatten a category tree into breadcrumb choices, excluding a complete subtree when editing its parent. */
|
|
3
3
|
function categoryChoices(nodes, excludeSlug) {
|
|
4
4
|
function visit(items, path, seen) {
|
|
@@ -35,5 +35,4 @@ function categoryNavigation(rows) {
|
|
|
35
35
|
}
|
|
36
36
|
return roots;
|
|
37
37
|
}
|
|
38
|
-
//#endregion
|
|
39
38
|
export { categoryChoices, categoryNavigation };
|
package/dist/delivery-types.d.ts
CHANGED
package/dist/index.d.ts
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { DeliveryArea, DeliveryDistrict, DeliveryQuote, DeliveryQuoteInput } from "./delivery-types.js";
|
|
2
|
-
import { a as StorefrontCategory, c as
|
|
2
|
+
import { a as StorefrontCategory, c as StorefrontCollectionQuery, d as StorefrontProductPage, f as StorefrontProductQuery, i as GuestOrderAccess, l as StorefrontOrder, m as StorefrontRequestOptions, n as GuestCheckoutOptions, o as StorefrontCollection, p as StorefrontProfile, r as GuestCheckoutResult, s as StorefrontCollectionPage, t as GuestCheckoutInput, u as StorefrontProduct } from "./types-CwAQqf0Z.js";
|
|
3
3
|
import { ArcClientConfig } from "@classytic/arc-next/client";
|
|
4
|
-
//#region src/index.d.ts
|
|
5
4
|
export interface StorefrontClientOptions {
|
|
6
5
|
/** Full API mount, for example https://api.example.com/api/v1. */
|
|
7
6
|
apiUrl: string;
|
|
@@ -14,6 +13,8 @@ export declare function createStorefrontClient(options: StorefrontClientOptions)
|
|
|
14
13
|
profile: (request?: StorefrontRequestOptions) => Promise<StorefrontProfile>;
|
|
15
14
|
products: (query?: StorefrontProductQuery, request?: StorefrontRequestOptions) => Promise<StorefrontProductPage>;
|
|
16
15
|
product: (slug: string, request?: StorefrontRequestOptions) => Promise<StorefrontProduct>;
|
|
16
|
+
collections: (query?: StorefrontCollectionQuery, request?: StorefrontRequestOptions) => Promise<StorefrontCollectionPage>;
|
|
17
|
+
collection: (slug: string, request?: StorefrontRequestOptions) => Promise<StorefrontCollection>;
|
|
17
18
|
categories: (request?: StorefrontRequestOptions) => Promise<StorefrontCategory[]>;
|
|
18
19
|
deliveryDistricts: (request?: StorefrontRequestOptions) => Promise<{
|
|
19
20
|
requiresArea: boolean;
|
|
@@ -26,5 +27,4 @@ export declare function createStorefrontClient(options: StorefrontClientOptions)
|
|
|
26
27
|
checkout: (input: GuestCheckoutInput, request: GuestCheckoutOptions) => Promise<GuestCheckoutResult>;
|
|
27
28
|
orderStatus: (access: GuestOrderAccess, request?: StorefrontRequestOptions) => Promise<StorefrontOrder>;
|
|
28
29
|
};
|
|
29
|
-
export type StorefrontClient = ReturnType<typeof createStorefrontClient>;
|
|
30
|
-
//#endregion
|
|
30
|
+
export type StorefrontClient = ReturnType<typeof createStorefrontClient>;
|
package/dist/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { BaseApi } from "@classytic/arc-next/api";
|
|
2
2
|
import { createClient } from "@classytic/arc-next/client";
|
|
3
|
-
|
|
3
|
+
// src/index.ts
|
|
4
4
|
function segment(value) {
|
|
5
5
|
if (!value || value === "." || value === "..") throw new Error("A nonempty resource identifier is required.");
|
|
6
6
|
return encodeURIComponent(value);
|
|
@@ -33,6 +33,11 @@ function createStorefrontClient(options) {
|
|
|
33
33
|
options: request
|
|
34
34
|
}),
|
|
35
35
|
product: (slug, request = {}) => catalog.request("GET", `${catalog.baseUrl}/products/${segment(slug)}`, { options: request }),
|
|
36
|
+
collections: (query = {}, request = {}) => catalog.request("GET", `${catalog.baseUrl}/collections`, {
|
|
37
|
+
params: { ...query },
|
|
38
|
+
options: request
|
|
39
|
+
}),
|
|
40
|
+
collection: (slug, request = {}) => catalog.request("GET", `${catalog.baseUrl}/collections/${segment(slug)}`, { options: request }),
|
|
36
41
|
categories: async (request = {}) => {
|
|
37
42
|
return (await catalog.request("GET", `${catalog.baseUrl}/categories`, { options: request })).data;
|
|
38
43
|
},
|
|
@@ -62,5 +67,4 @@ function createStorefrontClient(options) {
|
|
|
62
67
|
} })
|
|
63
68
|
};
|
|
64
69
|
}
|
|
65
|
-
//#endregion
|
|
66
70
|
export { createStorefrontClient };
|
|
@@ -1,7 +1,6 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { l as StorefrontOrder, u as StorefrontProduct } from "./types-CwAQqf0Z.js";
|
|
2
2
|
import { Address } from "@classytic/primitives/address";
|
|
3
3
|
import { Money } from "@classytic/primitives/money";
|
|
4
|
-
//#region src/management-types.d.ts
|
|
5
4
|
export interface ProductInput {
|
|
6
5
|
name: string;
|
|
7
6
|
slug: string;
|
|
@@ -63,6 +62,26 @@ export interface MerchantCategoryTree extends MerchantCategory {
|
|
|
63
62
|
children: MerchantCategoryTree[];
|
|
64
63
|
totalProductCount: number;
|
|
65
64
|
}
|
|
65
|
+
export interface CollectionInput {
|
|
66
|
+
name: string;
|
|
67
|
+
/** Omit to derive from the name. Permanent after creation. */
|
|
68
|
+
slug?: string;
|
|
69
|
+
description?: string;
|
|
70
|
+
image?: {
|
|
71
|
+
url: string;
|
|
72
|
+
alt?: string;
|
|
73
|
+
} | null;
|
|
74
|
+
isActive?: boolean;
|
|
75
|
+
featured?: boolean;
|
|
76
|
+
displayOrder?: number;
|
|
77
|
+
}
|
|
78
|
+
export type CollectionUpdate = Partial<Omit<CollectionInput, 'slug'>>;
|
|
79
|
+
export interface MerchantCollection extends CollectionInput {
|
|
80
|
+
_id: string;
|
|
81
|
+
/** Pass the version read by the editor to collections.update({ version }). */
|
|
82
|
+
__v: number;
|
|
83
|
+
slug: string;
|
|
84
|
+
}
|
|
66
85
|
export interface MerchantOrder extends StorefrontOrder {
|
|
67
86
|
_id: string;
|
|
68
87
|
customerSnapshot?: {
|
|
@@ -98,6 +117,11 @@ export interface StoreSettings {
|
|
|
98
117
|
}
|
|
99
118
|
export interface MerchantMedia {
|
|
100
119
|
_id: string;
|
|
120
|
+
folder: string;
|
|
121
|
+
size: number;
|
|
122
|
+
title?: string;
|
|
123
|
+
alt?: string;
|
|
124
|
+
createdAt?: string;
|
|
101
125
|
url: string;
|
|
102
126
|
filename: string;
|
|
103
127
|
mimeType?: string;
|
|
@@ -221,5 +245,4 @@ export interface MerchantOverview {
|
|
|
221
245
|
count: number;
|
|
222
246
|
orderValue: number;
|
|
223
247
|
}>;
|
|
224
|
-
}
|
|
225
|
-
//#endregion
|
|
248
|
+
}
|
package/dist/management.d.ts
CHANGED
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
import { DeliveryDistrict, DeliverySettings, DeliverySettingsUpdate } from "./delivery-types.js";
|
|
2
|
-
import { AnalyticsWindow, ApiKeyInput, ApiKeyOptions, CategoryInput, IntegrationProvider, IssuedApiKey, MerchantApiKey, MerchantCategory, MerchantCategoryTree, MerchantIntegration, MerchantMedia, MerchantOrder, MerchantOverview, MerchantProduct, NotificationDelivery, OrderCommand, ProductInput, StoreSettings } from "./management-types.js";
|
|
2
|
+
import { AnalyticsWindow, ApiKeyInput, ApiKeyOptions, CategoryInput, CollectionInput, IntegrationProvider, IssuedApiKey, MerchantApiKey, MerchantCategory, MerchantCategoryTree, MerchantCollection, MerchantIntegration, MerchantMedia, MerchantOrder, MerchantOverview, MerchantProduct, NotificationDelivery, OrderCommand, ProductInput, StoreSettings } from "./management-types.js";
|
|
3
3
|
import { BaseApi } from "@classytic/arc-next/api";
|
|
4
4
|
import { ArcClientConfig } from "@classytic/arc-next/client";
|
|
5
|
-
//#region src/management.d.ts
|
|
6
5
|
export interface MerchantClientOptions {
|
|
7
6
|
apiUrl: string;
|
|
8
7
|
token: string;
|
|
@@ -45,6 +44,7 @@ export declare function createMerchantClient(options: MerchantClientOptions): {
|
|
|
45
44
|
districts(signal?: AbortSignal): Promise<DeliveryDistrict[]>;
|
|
46
45
|
};
|
|
47
46
|
products: BaseApi<MerchantProduct, ProductInput, Partial<ProductInput>>;
|
|
47
|
+
collections: BaseApi<MerchantCollection, CollectionInput, Partial<Omit<CollectionInput, "slug">>>;
|
|
48
48
|
categories: BaseApi<MerchantCategory, CategoryInput, Partial<Omit<CategoryInput, "slug">>> & {
|
|
49
49
|
tree: (options?: {
|
|
50
50
|
signal?: AbortSignal;
|
|
@@ -86,9 +86,14 @@ export declare function createMerchantClient(options: MerchantClientOptions): {
|
|
|
86
86
|
params?: import("@classytic/arc-next/api").QueryParams;
|
|
87
87
|
options?: Omit<import("@classytic/arc-next/api").RequestOptions, "token" | "organizationId">;
|
|
88
88
|
}) => Promise<import("@classytic/repo-core/pagination").PaginatedResult<MerchantMedia>>;
|
|
89
|
-
|
|
90
|
-
|
|
89
|
+
remove: ({ token, organizationId, id, options }: {
|
|
90
|
+
token?: string | null;
|
|
91
|
+
organizationId?: string | null;
|
|
92
|
+
id: string;
|
|
93
|
+
options?: Omit<import("@classytic/arc-next/api").RequestOptions, "token" | "organizationId">;
|
|
94
|
+
}) => Promise<import("@classytic/repo-core/repository").DeleteResult>;
|
|
95
|
+
upload: (file: File, folder?: 'products' | 'categories' | 'collections') => Promise<MerchantMedia>;
|
|
96
|
+
uploadImage(file: File, folder?: 'products' | 'categories' | 'collections'): Promise<MerchantMedia>;
|
|
91
97
|
};
|
|
92
98
|
};
|
|
93
|
-
export type MerchantClient = ReturnType<typeof createMerchantClient>;
|
|
94
|
-
//#endregion
|
|
99
|
+
export type MerchantClient = ReturnType<typeof createMerchantClient>;
|
package/dist/management.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { BaseApi } from "@classytic/arc-next/api";
|
|
2
2
|
import { createClient } from "@classytic/arc-next/client";
|
|
3
|
-
|
|
3
|
+
// src/management-delivery.ts
|
|
4
4
|
function createMerchantDelivery(client) {
|
|
5
5
|
const api = new BaseApi("stores/delivery", {
|
|
6
6
|
client,
|
|
@@ -15,8 +15,7 @@ function createMerchantDelivery(client) {
|
|
|
15
15
|
}
|
|
16
16
|
} };
|
|
17
17
|
}
|
|
18
|
-
|
|
19
|
-
//#region src/management-access.ts
|
|
18
|
+
// src/management-access.ts
|
|
20
19
|
function key(value) {
|
|
21
20
|
if (!value || value === "." || value === "..") throw new Error("An identifier is required.");
|
|
22
21
|
return encodeURIComponent(value);
|
|
@@ -60,8 +59,7 @@ function createMerchantAccess(client) {
|
|
|
60
59
|
}
|
|
61
60
|
};
|
|
62
61
|
}
|
|
63
|
-
|
|
64
|
-
//#region src/management-media.ts
|
|
62
|
+
// src/management-media.ts
|
|
65
63
|
function createMerchantMedia(client, fetcher) {
|
|
66
64
|
const media = new BaseApi("media", {
|
|
67
65
|
client,
|
|
@@ -124,10 +122,12 @@ function createMerchantMedia(client, fetcher) {
|
|
|
124
122
|
}
|
|
125
123
|
return {
|
|
126
124
|
list: media.getAll.bind(media),
|
|
125
|
+
remove: media.delete.bind(media),
|
|
127
126
|
upload: (file, folder = "products") => upload(file, folder),
|
|
128
127
|
async uploadImage(file, folder = "products") {
|
|
129
128
|
const policy = await media.request("GET", `${media.baseUrl}/transform-policy`);
|
|
130
|
-
const { resolveTransformPlan } = await import("@classytic/media-transform/policy");
|
|
129
|
+
const { resolveTransformPlan, isTransformableImage } = await import("@classytic/media-transform/policy");
|
|
130
|
+
if (!isTransformableImage(file.type)) throw new Error("Choose a JPEG, PNG, WebP, AVIF or BMP image that your browser can open.");
|
|
131
131
|
const plan = resolveTransformPlan({
|
|
132
132
|
folder,
|
|
133
133
|
mimeType: file.type,
|
|
@@ -142,13 +142,14 @@ function createMerchantMedia(client, fetcher) {
|
|
|
142
142
|
import("@classytic/media-transform/image/compressImageSet"),
|
|
143
143
|
import("@classytic/media-transform/image/uploadHints")
|
|
144
144
|
]);
|
|
145
|
-
const result = await compressImageSet(await createBrowserImageAdapter(), file, plan.options)
|
|
145
|
+
const result = await compressImageSet(await createBrowserImageAdapter(), file, plan.options).catch(() => {
|
|
146
|
+
throw new Error("Your browser could not process this image. Try a JPEG or PNG copy.");
|
|
147
|
+
});
|
|
146
148
|
return upload(new File([result.blob], storedFilename(file.name, result.format), { type: result.blob.type }), folder, result, file.name);
|
|
147
149
|
}
|
|
148
150
|
};
|
|
149
151
|
}
|
|
150
|
-
|
|
151
|
-
//#region src/management.ts
|
|
152
|
+
// src/management.ts
|
|
152
153
|
function orderKey(value) {
|
|
153
154
|
if (!value || value === "." || value === "..") throw new Error("An order number is required.");
|
|
154
155
|
return encodeURIComponent(value);
|
|
@@ -181,6 +182,7 @@ function createMerchantClient(options) {
|
|
|
181
182
|
...createMerchantAccess(client),
|
|
182
183
|
...createMerchantDelivery(client),
|
|
183
184
|
products,
|
|
185
|
+
collections: new BaseApi("collections", config),
|
|
184
186
|
categories: Object.assign(categories, { tree: (options = {}) => categories.request("GET", `${categories.baseUrl}/tree`, { options }) }),
|
|
185
187
|
analytics: { async overview(window, options = {}) {
|
|
186
188
|
const from = new Date(window.from);
|
|
@@ -257,5 +259,4 @@ function createMerchantClient(options) {
|
|
|
257
259
|
media: createMerchantMedia(client, fetcher)
|
|
258
260
|
};
|
|
259
261
|
}
|
|
260
|
-
//#endregion
|
|
261
262
|
export { createMerchantClient };
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
export type OrganizationStatus = 'pending' | 'active' | 'rejected' | 'suspended';
|
|
2
|
+
export type OrganizationAction = 'approve' | 'reject' | 'suspend' | 'reactivate';
|
|
3
|
+
export interface PlatformOrganization {
|
|
4
|
+
_id: string;
|
|
5
|
+
name: string;
|
|
6
|
+
slug: string;
|
|
7
|
+
status: OrganizationStatus;
|
|
8
|
+
createdAt?: string;
|
|
9
|
+
statusHistory?: Array<{
|
|
10
|
+
status: OrganizationStatus;
|
|
11
|
+
occurredAt: string;
|
|
12
|
+
by?: string;
|
|
13
|
+
note?: string;
|
|
14
|
+
}>;
|
|
15
|
+
}
|
|
16
|
+
/** Platform administration uses a bearer identity without selecting a merchant organization. */
|
|
17
|
+
export declare function createPlatformClient({ apiUrl, token }: {
|
|
18
|
+
apiUrl: string;
|
|
19
|
+
token: string;
|
|
20
|
+
}): {
|
|
21
|
+
organizations: {
|
|
22
|
+
list: ({ token, organizationId, params, options }?: {
|
|
23
|
+
token?: string | null;
|
|
24
|
+
organizationId?: string | null;
|
|
25
|
+
params?: import("@classytic/arc-next/api").QueryParams;
|
|
26
|
+
options?: Omit<import("@classytic/arc-next/api").RequestOptions, "token" | "organizationId">;
|
|
27
|
+
}) => Promise<import("@classytic/repo-core/pagination").PaginatedResult<PlatformOrganization>>;
|
|
28
|
+
decide(id: string, data: {
|
|
29
|
+
action: OrganizationAction;
|
|
30
|
+
expectedStatus: OrganizationStatus;
|
|
31
|
+
note: string;
|
|
32
|
+
}): Promise<PlatformOrganization>;
|
|
33
|
+
};
|
|
34
|
+
};
|
package/dist/platform.js
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { BaseApi } from "@classytic/arc-next/api";
|
|
2
|
+
import { createClient } from "@classytic/arc-next/client";
|
|
3
|
+
// src/platform.ts
|
|
4
|
+
/** Platform administration uses a bearer identity without selecting a merchant organization. */
|
|
5
|
+
function createPlatformClient({ apiUrl, token }) {
|
|
6
|
+
if (!token.trim()) throw new Error("A platform bearer token is required.");
|
|
7
|
+
const url = new URL(apiUrl);
|
|
8
|
+
if (url.username || url.password || url.search || url.hash) throw new Error("Use a plain API URL.");
|
|
9
|
+
const client = createClient({
|
|
10
|
+
baseUrl: url.href.replace(/\/+$/, ""),
|
|
11
|
+
getToken: () => token,
|
|
12
|
+
credentials: "omit",
|
|
13
|
+
autoIdempotency: false,
|
|
14
|
+
timeoutMs: 15e3
|
|
15
|
+
});
|
|
16
|
+
const organizations = new BaseApi("admin/organizations", {
|
|
17
|
+
client,
|
|
18
|
+
basePath: "",
|
|
19
|
+
cache: "no-store"
|
|
20
|
+
});
|
|
21
|
+
return { organizations: {
|
|
22
|
+
list: organizations.getAll.bind(organizations),
|
|
23
|
+
decide(id, data) {
|
|
24
|
+
return organizations.request("POST", `${organizations.baseUrl}/${encodeURIComponent(id)}/action`, { data });
|
|
25
|
+
}
|
|
26
|
+
} };
|
|
27
|
+
}
|
|
28
|
+
export { createPlatformClient };
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { Address } from "@classytic/primitives/address";
|
|
2
2
|
import { Money } from "@classytic/primitives/money";
|
|
3
|
-
//#region src/types.d.ts
|
|
4
3
|
/** Public wire projections; merchant-only catalog and buyer fields are absent. */
|
|
5
4
|
interface StorefrontProduct {
|
|
6
5
|
id: string;
|
|
@@ -45,6 +44,28 @@ interface StorefrontCategory {
|
|
|
45
44
|
} | null;
|
|
46
45
|
displayOrder?: number;
|
|
47
46
|
}
|
|
47
|
+
interface StorefrontCollection {
|
|
48
|
+
id: string;
|
|
49
|
+
slug: string;
|
|
50
|
+
name: string;
|
|
51
|
+
description?: string;
|
|
52
|
+
image?: {
|
|
53
|
+
url: string;
|
|
54
|
+
alt?: string;
|
|
55
|
+
} | null;
|
|
56
|
+
featured: boolean;
|
|
57
|
+
displayOrder: number;
|
|
58
|
+
}
|
|
59
|
+
interface StorefrontCollectionQuery {
|
|
60
|
+
limit?: number;
|
|
61
|
+
/** Opaque cursor; retain the same featured filter between pages. */
|
|
62
|
+
after?: string;
|
|
63
|
+
featured?: boolean;
|
|
64
|
+
}
|
|
65
|
+
interface StorefrontCollectionPage {
|
|
66
|
+
data: StorefrontCollection[];
|
|
67
|
+
next: string | null;
|
|
68
|
+
}
|
|
48
69
|
interface StorefrontProductPage {
|
|
49
70
|
data: StorefrontProduct[];
|
|
50
71
|
next: string | null;
|
|
@@ -124,5 +145,4 @@ interface GuestCheckoutOptions extends StorefrontRequestOptions {
|
|
|
124
145
|
/** Create once per checkout attempt; preserve across retries and lost responses. */
|
|
125
146
|
idempotencyKey: string;
|
|
126
147
|
}
|
|
127
|
-
|
|
128
|
-
export { StorefrontCategory as a, StorefrontProductPage as c, StorefrontRequestOptions as d, GuestOrderAccess as i, StorefrontProductQuery as l, GuestCheckoutOptions as n, StorefrontOrder as o, GuestCheckoutResult as r, StorefrontProduct as s, GuestCheckoutInput as t, StorefrontProfile as u };
|
|
148
|
+
export { StorefrontCategory as a, StorefrontCollectionQuery as c, StorefrontProductPage as d, StorefrontProductQuery as f, GuestOrderAccess as i, StorefrontOrder as l, StorefrontRequestOptions as m, GuestCheckoutOptions as n, StorefrontCollection as o, StorefrontProfile as p, GuestCheckoutResult as r, StorefrontCollectionPage as s, GuestCheckoutInput as t, StorefrontProduct as u };
|
package/dist/types.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { a as StorefrontCategory, c as
|
|
2
|
-
export { GuestCheckoutInput, GuestCheckoutOptions, GuestCheckoutResult, GuestOrderAccess, StorefrontCategory, StorefrontOrder, StorefrontProduct, StorefrontProductPage, StorefrontProductQuery, StorefrontProfile, StorefrontRequestOptions };
|
|
1
|
+
import { a as StorefrontCategory, c as StorefrontCollectionQuery, d as StorefrontProductPage, f as StorefrontProductQuery, i as GuestOrderAccess, l as StorefrontOrder, m as StorefrontRequestOptions, n as GuestCheckoutOptions, o as StorefrontCollection, p as StorefrontProfile, r as GuestCheckoutResult, s as StorefrontCollectionPage, t as GuestCheckoutInput, u as StorefrontProduct } from "./types-CwAQqf0Z.js";
|
|
2
|
+
export { GuestCheckoutInput, GuestCheckoutOptions, GuestCheckoutResult, GuestOrderAccess, StorefrontCategory, StorefrontCollection, StorefrontCollectionPage, StorefrontCollectionQuery, StorefrontOrder, StorefrontProduct, StorefrontProductPage, StorefrontProductQuery, StorefrontProfile, StorefrontRequestOptions };
|
package/management.md
CHANGED
|
@@ -86,3 +86,44 @@ Product writes also accept `tags`, `collections` and
|
|
|
86
86
|
`defaultMonetization.pricing.compareAtPrice`. The catalog derives `onSale`; clients
|
|
87
87
|
never set it. Tags and collections use the existing catalog arrays, with no
|
|
88
88
|
additional collection-management service.
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
### Media library
|
|
92
|
+
|
|
93
|
+
`client.media.list({ params: { page: 1, limit: 20, folder: 'products' } })`
|
|
94
|
+
returns tenant-scoped media and pagination. `client.media.remove({ id })` uses
|
|
95
|
+
the media resource's guarded delete; an in-use image is refused by the server.
|
|
96
|
+
|
|
97
|
+
`client.media.uploadImage(file, folder)` resolves the server's transform policy
|
|
98
|
+
through media-transform before requesting storage writes. The server must accept
|
|
99
|
+
every output format in its policy, including the browser encoder's fallback.
|
|
100
|
+
Passthrough thresholds and dimensions belong to that policy, rather than the UI.
|
|
101
|
+
|
|
102
|
+
## Managed collections
|
|
103
|
+
|
|
104
|
+
```ts
|
|
105
|
+
const collection = await merchant.collections.create({ data: {
|
|
106
|
+
name: 'Eid picks', // slug is generated when omitted
|
|
107
|
+
description: 'Pieces for the celebrations.',
|
|
108
|
+
isActive: true,
|
|
109
|
+
featured: true,
|
|
110
|
+
displayOrder: 10,
|
|
111
|
+
} });
|
|
112
|
+
await merchant.products.update({ id: productId, data: {
|
|
113
|
+
collections: [collection.slug], // replaces this product's memberships
|
|
114
|
+
} });
|
|
115
|
+
await merchant.collections.update({ id: collection._id, data: {
|
|
116
|
+
name: 'Our Eid edit', // preserves its permanent slug
|
|
117
|
+
} });
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
Collections use the same store-bound bearer client and Arc Next CRUD/pagination
|
|
121
|
+
contract as products. Create a new client when switching stores. Slug uniqueness
|
|
122
|
+
is per store. An unknown or foreign collection cannot be assigned. A collection
|
|
123
|
+
cannot be deleted until products release its slug; hide it instead when keeping
|
|
124
|
+
memberships. Upload cover images with `merchant.media.uploadImage(file,
|
|
125
|
+
'collections')` and pass `{ url, alt }` as `image`.
|
|
126
|
+
|
|
127
|
+
Existing string labels require the Brihot server's `collections:migrate` script
|
|
128
|
+
before switching an existing storefront to managed collection navigation. It is a
|
|
129
|
+
per-store dry run by default; it creates metadata without rewriting memberships.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@classytic/commerce-client",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"description": "Typed storefront and merchant clients for the Classytic Commerce API",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"sideEffects": false,
|
|
@@ -30,6 +30,10 @@
|
|
|
30
30
|
"./category-tree": {
|
|
31
31
|
"types": "./dist/category-tree.d.ts",
|
|
32
32
|
"default": "./dist/category-tree.js"
|
|
33
|
+
},
|
|
34
|
+
"./platform": {
|
|
35
|
+
"types": "./dist/platform.d.ts",
|
|
36
|
+
"default": "./dist/platform.js"
|
|
33
37
|
}
|
|
34
38
|
},
|
|
35
39
|
"files": [
|
|
@@ -59,7 +63,7 @@
|
|
|
59
63
|
"esbuild": "^0.28.1",
|
|
60
64
|
"publint": "^0.3.25",
|
|
61
65
|
"tsdown": "^0.23.1",
|
|
62
|
-
"typescript": "^7.0
|
|
66
|
+
"typescript": "^7.1.0-beta",
|
|
63
67
|
"vitest": "^5.0.5"
|
|
64
68
|
},
|
|
65
69
|
"publishConfig": {
|