@sixtyninecommerce/admin 0.10.0 → 0.11.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.
Files changed (247) hide show
  1. package/README.md +4347 -171
  2. package/dist/catalog/assets.d.ts +120 -8
  3. package/dist/catalog/assets.js +98 -12
  4. package/dist/catalog/collections.d.ts +279 -14
  5. package/dist/catalog/collections.js +246 -15
  6. package/dist/catalog/index.d.ts +9 -2
  7. package/dist/catalog/index.js +4 -0
  8. package/dist/catalog/products.d.ts +159 -19
  9. package/dist/catalog/products.js +88 -13
  10. package/dist/catalog/properties.d.ts +102 -14
  11. package/dist/catalog/properties.js +13 -0
  12. package/dist/catalog/recommendations.d.ts +261 -0
  13. package/dist/catalog/recommendations.js +145 -0
  14. package/dist/catalog/reviews.d.ts +368 -0
  15. package/dist/catalog/reviews.js +318 -0
  16. package/dist/catalog/types.d.ts +17 -1
  17. package/dist/catalog/types.js +10 -0
  18. package/dist/catalog/variant-find.d.ts +136 -0
  19. package/dist/catalog/variant-find.js +239 -0
  20. package/dist/catalog/variants.d.ts +233 -26
  21. package/dist/catalog/variants.js +125 -7
  22. package/dist/catalog/visibility.d.ts +20 -5
  23. package/dist/catalog/visibility.js +19 -1
  24. package/dist/client.d.ts +95 -9
  25. package/dist/client.js +35 -7
  26. package/dist/content/assets.d.ts +286 -0
  27. package/dist/content/assets.js +181 -0
  28. package/dist/content/items.d.ts +269 -0
  29. package/dist/content/items.js +170 -0
  30. package/dist/content/types.d.ts +282 -0
  31. package/dist/content/types.js +100 -0
  32. package/dist/customers/age-verification.d.ts +66 -0
  33. package/dist/customers/age-verification.js +79 -0
  34. package/dist/customers/credit.d.ts +263 -0
  35. package/dist/customers/credit.js +244 -0
  36. package/dist/customers/delivery-preferences.d.ts +120 -0
  37. package/dist/customers/delivery-preferences.js +97 -0
  38. package/dist/customers/groups.d.ts +207 -0
  39. package/dist/customers/groups.js +438 -0
  40. package/dist/customers/index.d.ts +447 -37
  41. package/dist/customers/index.js +313 -40
  42. package/dist/customers/invitation-status.d.ts +57 -0
  43. package/dist/customers/invitation-status.js +21 -0
  44. package/dist/customers/notes.d.ts +67 -0
  45. package/dist/customers/notes.js +51 -0
  46. package/dist/customers/notification-recipients.d.ts +97 -0
  47. package/dist/customers/notification-recipients.js +84 -0
  48. package/dist/customers/saved-lists.d.ts +42 -0
  49. package/dist/customers/saved-lists.js +61 -0
  50. package/dist/customers/vat.d.ts +83 -0
  51. package/dist/customers/vat.js +46 -0
  52. package/dist/customers/wire.d.ts +20 -0
  53. package/dist/customers/wire.js +35 -0
  54. package/dist/documents/ar.d.ts +307 -0
  55. package/dist/documents/ar.js +254 -0
  56. package/dist/documents/index.d.ts +731 -21
  57. package/dist/documents/index.js +436 -24
  58. package/dist/documents/pdf.d.ts +2 -0
  59. package/dist/documents/pdf.js +11 -0
  60. package/dist/fulfillments/create.d.ts +167 -0
  61. package/dist/fulfillments/create.js +216 -0
  62. package/dist/fulfillments/index.d.ts +44 -10
  63. package/dist/fulfillments/index.js +11 -2
  64. package/dist/fulfillments/payment-policy.d.ts +64 -0
  65. package/dist/fulfillments/payment-policy.js +140 -0
  66. package/dist/fulfillments/warehouse.d.ts +724 -0
  67. package/dist/fulfillments/warehouse.js +914 -0
  68. package/dist/geo/index.d.ts +119 -0
  69. package/dist/geo/index.js +107 -0
  70. package/dist/index.d.ts +123 -35
  71. package/dist/index.js +45 -12
  72. package/dist/integrations/afas-actions.d.ts +234 -0
  73. package/dist/integrations/afas-actions.js +310 -0
  74. package/dist/integrations/afas-types.d.ts +483 -0
  75. package/dist/integrations/afas-types.js +5 -0
  76. package/dist/integrations/afas.d.ts +234 -0
  77. package/dist/integrations/afas.js +290 -0
  78. package/dist/integrations/connections.d.ts +103 -0
  79. package/dist/integrations/connections.js +55 -0
  80. package/dist/integrations/esp.d.ts +313 -0
  81. package/dist/integrations/esp.js +169 -0
  82. package/dist/integrations/exact-online-types.d.ts +423 -0
  83. package/dist/integrations/exact-online-types.js +6 -0
  84. package/dist/integrations/exact-online.d.ts +248 -0
  85. package/dist/integrations/exact-online.js +376 -0
  86. package/dist/integrations/index.d.ts +29 -3
  87. package/dist/integrations/index.js +18 -3
  88. package/dist/integrations/pipeline-keys.d.ts +13 -0
  89. package/dist/integrations/pipeline-keys.js +22 -0
  90. package/dist/integrations/pipelines.d.ts +15 -14
  91. package/dist/integrations/pipelines.js +6 -22
  92. package/dist/integrations/types.d.ts +146 -0
  93. package/dist/locations/index.d.ts +66 -0
  94. package/dist/locations/index.js +31 -0
  95. package/dist/meta/index.d.ts +162 -0
  96. package/dist/orders/cancel-with-money.d.ts +227 -0
  97. package/dist/orders/cancel-with-money.js +153 -0
  98. package/dist/orders/commit-edit.d.ts +7 -1
  99. package/dist/orders/commit-edit.js +5 -1
  100. package/dist/orders/drafts.d.ts +800 -0
  101. package/dist/orders/drafts.js +771 -0
  102. package/dist/orders/fields.d.ts +245 -0
  103. package/dist/orders/fields.js +188 -0
  104. package/dist/orders/get.d.ts +29 -19
  105. package/dist/orders/get.js +17 -8
  106. package/dist/orders/index.d.ts +49 -9
  107. package/dist/orders/index.js +22 -2
  108. package/dist/orders/lifecycle.d.ts +128 -0
  109. package/dist/orders/lifecycle.js +158 -0
  110. package/dist/orders/line-price-preview.d.ts +7 -2
  111. package/dist/orders/list.d.ts +222 -0
  112. package/dist/orders/list.js +377 -0
  113. package/dist/orders/manual-pricing.d.ts +5 -0
  114. package/dist/orders/order-entry.d.ts +20 -7
  115. package/dist/orders/plan-edit.d.ts +1 -1
  116. package/dist/orders/plan-edit.js +4 -1
  117. package/dist/orders/settings.d.ts +201 -0
  118. package/dist/orders/settings.js +325 -0
  119. package/dist/orders/status.d.ts +159 -13
  120. package/dist/orders/status.js +21 -0
  121. package/dist/orders/surcharges.d.ts +8 -1
  122. package/dist/orders/surcharges.js +3 -2
  123. package/dist/payments/account-credit.d.ts +30 -0
  124. package/dist/payments/account-credit.js +19 -0
  125. package/dist/payments/ar.d.ts +292 -0
  126. package/dist/payments/ar.js +223 -0
  127. package/dist/payments/connected-account.d.ts +47 -0
  128. package/dist/payments/connected-account.js +61 -0
  129. package/dist/payments/credit-noted-fields.d.ts +12 -0
  130. package/dist/payments/credit-noted-fields.js +37 -0
  131. package/dist/payments/index.d.ts +257 -11
  132. package/dist/payments/index.js +131 -1
  133. package/dist/payments/methods.d.ts +303 -8
  134. package/dist/payments/methods.js +337 -7
  135. package/dist/payments/mollie-methods.d.ts +159 -0
  136. package/dist/payments/mollie-methods.js +141 -0
  137. package/dist/payments/payment-links.d.ts +180 -0
  138. package/dist/payments/payment-links.js +97 -0
  139. package/dist/payments/provider-keys.d.ts +150 -0
  140. package/dist/payments/provider-keys.js +118 -0
  141. package/dist/payments/providers.d.ts +86 -11
  142. package/dist/payments/providers.js +29 -0
  143. package/dist/payments/requests.d.ts +144 -0
  144. package/dist/payments/requests.js +92 -0
  145. package/dist/payments/store-credit-fields.d.ts +15 -0
  146. package/dist/payments/store-credit-fields.js +51 -0
  147. package/dist/pickups/index.d.ts +104 -0
  148. package/dist/pickups/index.js +105 -0
  149. package/dist/platform/channels.d.ts +132 -0
  150. package/dist/platform/channels.js +128 -0
  151. package/dist/platform/geo.d.ts +123 -0
  152. package/dist/platform/geo.js +144 -0
  153. package/dist/platform/tax.d.ts +154 -0
  154. package/dist/platform/tax.js +139 -0
  155. package/dist/pos/anomalies.d.ts +158 -0
  156. package/dist/pos/anomalies.js +64 -0
  157. package/dist/pos/assisted-selling.d.ts +79 -0
  158. package/dist/pos/assisted-selling.js +38 -0
  159. package/dist/pos/exchanges.d.ts +103 -0
  160. package/dist/pos/exchanges.js +22 -0
  161. package/dist/pos/gift-cards.d.ts +135 -0
  162. package/dist/pos/gift-cards.js +47 -0
  163. package/dist/pos/index.d.ts +2125 -32
  164. package/dist/pos/index.js +742 -4
  165. package/dist/pos/no-receipt.d.ts +94 -0
  166. package/dist/pos/no-receipt.js +25 -0
  167. package/dist/pos/offline-sales.d.ts +187 -0
  168. package/dist/pos/offline-sales.js +116 -0
  169. package/dist/pos/pay-by-link.d.ts +143 -0
  170. package/dist/pos/pay-by-link.js +57 -0
  171. package/dist/pos/quick-keys.d.ts +103 -0
  172. package/dist/pos/quick-keys.js +45 -0
  173. package/dist/pos/reports.d.ts +360 -0
  174. package/dist/pos/reports.js +62 -0
  175. package/dist/pos/returns.d.ts +217 -0
  176. package/dist/pos/returns.js +53 -0
  177. package/dist/pricing/bulk-coupons.d.ts +139 -0
  178. package/dist/pricing/bulk-coupons.js +112 -0
  179. package/dist/pricing/index.d.ts +174 -4
  180. package/dist/pricing/index.js +174 -1
  181. package/dist/pricing/price-lists.d.ts +604 -0
  182. package/dist/pricing/price-lists.js +600 -0
  183. package/dist/purchasing/alerts.d.ts +89 -0
  184. package/dist/purchasing/alerts.js +62 -0
  185. package/dist/purchasing/cost.d.ts +110 -0
  186. package/dist/purchasing/cost.js +86 -0
  187. package/dist/purchasing/index.d.ts +36 -0
  188. package/dist/purchasing/index.js +16 -0
  189. package/dist/purchasing/purchase-orders.d.ts +548 -0
  190. package/dist/purchasing/purchase-orders.js +491 -0
  191. package/dist/purchasing/replenishment.d.ts +281 -0
  192. package/dist/purchasing/replenishment.js +240 -0
  193. package/dist/purchasing/settings.d.ts +152 -0
  194. package/dist/purchasing/settings.js +181 -0
  195. package/dist/purchasing/suppliers.d.ts +476 -0
  196. package/dist/purchasing/suppliers.js +438 -0
  197. package/dist/quotes/index.d.ts +501 -0
  198. package/dist/quotes/index.js +439 -0
  199. package/dist/reports/index.d.ts +71 -8
  200. package/dist/reports/index.js +74 -11
  201. package/dist/returns/index.d.ts +614 -21
  202. package/dist/returns/index.js +695 -11
  203. package/dist/search/index.d.ts +76 -1
  204. package/dist/search/index.js +63 -0
  205. package/dist/shipping/definitions.d.ts +29 -0
  206. package/dist/shipping/definitions.js +47 -0
  207. package/dist/shipping/fulfillment-profiles.d.ts +174 -0
  208. package/dist/shipping/fulfillment-profiles.js +104 -0
  209. package/dist/shipping/hendriks.d.ts +97 -0
  210. package/dist/shipping/hendriks.js +50 -0
  211. package/dist/shipping/index.d.ts +48 -0
  212. package/dist/shipping/index.js +25 -0
  213. package/dist/shipping/methods.d.ts +198 -0
  214. package/dist/shipping/methods.js +240 -0
  215. package/dist/shipping/myparcel.d.ts +254 -0
  216. package/dist/shipping/myparcel.js +178 -0
  217. package/dist/shipping/parcel-count-suggestion.d.ts +2 -0
  218. package/dist/shipping/parcel-rule.d.ts +6 -5
  219. package/dist/shipping/parcel-rule.js +21 -23
  220. package/dist/shipping/rate-tables.d.ts +179 -0
  221. package/dist/shipping/rate-tables.js +184 -0
  222. package/dist/stock/index.d.ts +10 -3
  223. package/dist/stock/index.js +3 -0
  224. package/dist/stock/locations.d.ts +144 -0
  225. package/dist/stock/locations.js +168 -0
  226. package/dist/stock/transfers.d.ts +216 -43
  227. package/dist/stock/transfers.js +140 -13
  228. package/dist/stored-value/index.d.ts +214 -4
  229. package/dist/stored-value/index.js +152 -0
  230. package/dist/subscriptions/index.d.ts +682 -0
  231. package/dist/subscriptions/index.js +815 -0
  232. package/dist/tax/index.d.ts +76 -1
  233. package/dist/tax/index.js +50 -0
  234. package/dist/tenant/email-style.d.ts +102 -0
  235. package/dist/tenant/email-style.js +72 -0
  236. package/dist/tenant/email-templates.d.ts +66 -0
  237. package/dist/tenant/email-templates.js +54 -0
  238. package/dist/tenant/notifications.d.ts +23 -17
  239. package/dist/tenant/settings-sources.d.ts +1 -1
  240. package/dist/tenant/time-zone.d.ts +17 -0
  241. package/dist/tenant/time-zone.js +11 -0
  242. package/dist/types.d.ts +50 -3
  243. package/dist/users/index.d.ts +3 -0
  244. package/dist/users/index.js +2 -0
  245. package/dist/users/warehouses.d.ts +50 -0
  246. package/dist/users/warehouses.js +51 -0
  247. package/package.json +2 -2
@@ -0,0 +1,286 @@
1
+ import { type Page, type Transport } from '@sixtyninecommerce/core';
2
+ import type { Call, ListOptions } from '../types.js';
3
+ /** An image's alt text in one language. */
4
+ export interface ContentAssetTranslation {
5
+ languageCode: string;
6
+ altText: string;
7
+ }
8
+ /** One scaled copy of an image: the original format and WebP. */
9
+ export interface ContentAssetPresetUrls {
10
+ url: string;
11
+ webpUrl: string;
12
+ }
13
+ /**
14
+ * A file in the store's media library (the CMS's assets): what a content item's image, file or
15
+ * video field refers to.
16
+ *
17
+ * `storageKey` is where the bytes live, under the store's own prefix (`{store code}/content/…`):
18
+ * a content field stores it, and a storefront resolves it to a URL. The platform chooses it from
19
+ * the store the call is made for; no call here takes one.
20
+ */
21
+ export interface ContentAsset {
22
+ id: string;
23
+ channelId: string;
24
+ /** The folder it sits in; null at the top level. */
25
+ folderId: string | null;
26
+ /** The stored file name, made safe for a URL. */
27
+ filename: string;
28
+ /** The file name as uploaded. */
29
+ originalFilename: string;
30
+ mimeType: string;
31
+ /** Bytes. */
32
+ fileSize: number;
33
+ storageKey: string;
34
+ contentHash: string | null;
35
+ /** Pixels; null for a file that is not an image, or one the CMS could not measure. */
36
+ width: number | null;
37
+ height: number | null;
38
+ /** The alt text in no particular language; see `translations` for the per-language ones. */
39
+ altText: string | null;
40
+ /** Stored verbatim: its keys are never case-converted. */
41
+ metadata: Record<string, unknown> | null;
42
+ /**
43
+ * The scaled copies of a raster image, by preset name (`xxs` … `xl`); null for any other file.
44
+ * They are made in the background after an upload, so a URL can answer 404 for a few seconds.
45
+ */
46
+ presets: Record<string, ContentAssetPresetUrls> | null;
47
+ /** The alt text per language, as `update` last wrote each one. */
48
+ translations: ContentAssetTranslation[];
49
+ createdAt: string;
50
+ updatedAt: string;
51
+ [extra: string]: unknown;
52
+ }
53
+ /** A folder of the media library. */
54
+ export interface ContentAssetFolder {
55
+ id: string;
56
+ channelId: string;
57
+ name: string;
58
+ /** The folder it sits in; null at the top level. */
59
+ parentId: string | null;
60
+ createdAt: string;
61
+ updatedAt: string;
62
+ /** Its direct subfolders: answered by `folders.get` only. */
63
+ children?: ContentAssetFolder[];
64
+ [extra: string]: unknown;
65
+ }
66
+ export interface ContentAssetListOptions extends ListOptions {
67
+ /** Only the files in this folder; `null` for the ones in no folder. */
68
+ folderId?: string | null;
69
+ /** Only the files whose MIME type starts with this (`image/`, `application/pdf`). */
70
+ mimeType?: string;
71
+ /**
72
+ * Full-text search over the file names and the alt text; the results come in relevance order.
73
+ * The CMS searches with a narrower filter than a list: `mimeType` must then be a whole type
74
+ * (`image/png`), because a prefix matches nothing, and `folderId: null` is not applied, so the
75
+ * search covers every folder.
76
+ */
77
+ search?: string;
78
+ }
79
+ /** What `upload` sends: the file and where it goes. */
80
+ export interface UploadContentAssetInput {
81
+ file: Blob;
82
+ /**
83
+ * The name the file is stored under. Required unless `file` is a `File` (which carries its
84
+ * own): a bare Blob would otherwise be stored as "blob".
85
+ */
86
+ fileName?: string;
87
+ /** A folder of the store; the top level when omitted. */
88
+ folderId?: string;
89
+ altText?: string;
90
+ }
91
+ /** What `uploadFromUrl` sends. */
92
+ export interface UploadContentAssetFromUrlInput {
93
+ /** An `http` or `https` URL the CMS fetches the file from. */
94
+ url: string;
95
+ /** A folder of the store; the top level when omitted. */
96
+ folderId?: string;
97
+ altText?: string;
98
+ }
99
+ /** The members `update` acts on; an absent key leaves the stored value alone. */
100
+ export interface UpdateContentAssetInput {
101
+ /**
102
+ * Moves the file into this folder of the store; `null` to the top level. A CMS older than the
103
+ * platform release that fixed it (headless W10-6) answers the update and keeps the file where
104
+ * it was.
105
+ */
106
+ folderId?: string | null;
107
+ /** At most 255 characters; `null` clears it. */
108
+ altText?: string | null;
109
+ /** Replaces the stored metadata, sent verbatim; `null` clears it. */
110
+ metadata?: Record<string, unknown> | null;
111
+ /**
112
+ * The alt text of each named language (at most 500 characters), written as given. A language
113
+ * left out keeps the alt text it had: nothing here removes one.
114
+ */
115
+ translations?: readonly ContentAssetTranslation[];
116
+ }
117
+ export interface CreateContentAssetFolderInput {
118
+ /** At most 255 characters. */
119
+ name: string;
120
+ /** A folder of the store to create it in; the top level when omitted. */
121
+ parentId?: string;
122
+ }
123
+ export interface UpdateContentAssetFolderInput {
124
+ name?: string;
125
+ /**
126
+ * Moves the folder into another of the store's folders; `null` to the top level. A CMS older
127
+ * than the platform release of headless W10-6 moves it but answers `parentId: null`.
128
+ */
129
+ parentId?: string | null;
130
+ }
131
+ export interface ListContentAssetFoldersOptions extends Call {
132
+ /** The folders inside this one; the top-level folders when omitted. */
133
+ parentId?: string;
134
+ }
135
+ /**
136
+ * The folders of the media library. Reads need `ReadCmsAsset`; writes the permission each method
137
+ * names.
138
+ */
139
+ export interface ContentAssetFoldersModule {
140
+ /**
141
+ * Every folder directly inside `parentId` (the top level when omitted), ordered by name. The
142
+ * CMS answers them whole: there is no page.
143
+ *
144
+ * @effect read
145
+ * @permission ReadCmsAsset
146
+ */
147
+ list(options?: ListContentAssetFoldersOptions): Promise<ContentAssetFolder[]>;
148
+ /**
149
+ * One folder with its direct subfolders (`children`). `NotFoundError` for a folder this store
150
+ * does not hold.
151
+ *
152
+ * @effect read
153
+ * @permission ReadCmsAsset
154
+ */
155
+ get(id: string, options?: Call): Promise<ContentAssetFolder>;
156
+ /**
157
+ * Adds a folder. A `parentId` this store does not hold is `NotFoundError`.
158
+ *
159
+ * @effect write
160
+ * @permission CreateCmsAsset
161
+ */
162
+ create(input: CreateContentAssetFolderInput, options?: Call): Promise<ContentAssetFolder>;
163
+ /**
164
+ * Renames or moves a folder. Moving it into itself or into one of its own subfolders is refused
165
+ * (`ValidationError`).
166
+ *
167
+ * @effect write
168
+ * @permission UpdateCmsAsset
169
+ */
170
+ update(id: string, input: UpdateContentAssetFolderInput, options?: Call): Promise<ContentAssetFolder>;
171
+ /**
172
+ * Removes an empty folder. One that still holds a subfolder or a file is refused
173
+ * (`ValidationError`) and nothing is removed.
174
+ *
175
+ * @effect destructive
176
+ * @permission DeleteCmsAsset
177
+ */
178
+ delete(id: string, options?: Call): Promise<void>;
179
+ }
180
+ /**
181
+ * The store's media library (the CMS's assets and folders): the files content items show.
182
+ * Product images are a different library, `catalog.assets`.
183
+ *
184
+ * Everything is scoped to the store the client is for: another store's file or folder answers
185
+ * `NotFoundError` (404, `NOT_FOUND`), as one that never existed does, and is never listed.
186
+ *
187
+ * Refusals are the CMS's problem+json, mapped by status:
188
+ * - `ValidationError` (400): a file type the platform does not accept, a file over the size limit
189
+ * the CMS is configured with, a URL it cannot fetch or that points into a private network, a
190
+ * refused input member (`VALIDATION_FAILED`, with `details[]`).
191
+ * - 413 (`PlatformError`): a multipart body over the 200 MB the upload route reads at most.
192
+ * - `PermissionDeniedError` (403): the credential lacks the method's permission on the store.
193
+ * - `NotFoundError` (404): no such file or folder in this store.
194
+ *
195
+ * `upload` sends the file through the CMS as `multipart/form-data` and is never retried: a retry
196
+ * would store it twice. For a large file, `uploads` (spec §6.11) puts it straight into storage
197
+ * instead. No write here is deduplicated: an `idempotencyKey` is sent and acts on nothing.
198
+ */
199
+ export interface ContentAssetsModule {
200
+ folders: ContentAssetFoldersModule;
201
+ /**
202
+ * The store's files, newest first, a page at a time (relevance order with `search`). Pages are
203
+ * offset-based (`Page.total` counts the matches), so `after: offsetCursor((n - 1) * size)` opens
204
+ * page `n`, and a file uploaded while a walk runs can be seen twice or not at all.
205
+ *
206
+ * @effect read
207
+ * @permission ReadCmsAsset
208
+ */
209
+ list(options?: ContentAssetListOptions): Promise<Page<ContentAsset>>;
210
+ /**
211
+ * One file. `NotFoundError` for a file this store does not hold.
212
+ *
213
+ * @effect read
214
+ * @permission ReadCmsAsset
215
+ */
216
+ get(id: string, options?: Call): Promise<ContentAsset>;
217
+ /**
218
+ * The file stored under `storageKey` (what a content field holds), or null when this store holds
219
+ * none. The same bytes uploaded twice under the same name share a key; then the newest file
220
+ * answers.
221
+ *
222
+ * @effect read
223
+ * @permission ReadCmsAsset
224
+ */
225
+ byStorageKey(storageKey: string, options?: Call): Promise<ContentAsset | null>;
226
+ /**
227
+ * Stores a file in the media library. Raster images get their scaled copies in the background.
228
+ * A file without a name is refused before anything is sent (`ValidationError`,
229
+ * `ASSET_FILE_NAME_REQUIRED`). Never retried.
230
+ *
231
+ * @effect write
232
+ * @permission CreateCmsAsset
233
+ */
234
+ upload(input: UploadContentAssetInput, options?: Call): Promise<ContentAsset>;
235
+ /**
236
+ * Has the CMS fetch a file from a public URL and store it, as `upload` would. The CMS refuses a
237
+ * URL into a private network, one that answers an error, redirects too often or takes over 30
238
+ * seconds (`ValidationError`).
239
+ *
240
+ * @effect write
241
+ * @permission CreateCmsAsset
242
+ */
243
+ uploadFromUrl(input: UploadContentAssetFromUrlInput, options?: Call): Promise<ContentAsset>;
244
+ /**
245
+ * Changes a file's folder, alt text, metadata or per-language alt text, in one transaction.
246
+ *
247
+ * @effect write
248
+ * @permission UpdateCmsAsset
249
+ */
250
+ update(id: string, input: UpdateContentAssetInput, options?: Call): Promise<ContentAsset>;
251
+ /**
252
+ * Removes the file from the library. Its bytes and scaled copies are deleted from storage too
253
+ * unless another file of the store shares them, so a content field still holding its key then
254
+ * shows nothing. Nothing in the SDK brings it back.
255
+ *
256
+ * @effect destructive
257
+ * @permission DeleteCmsAsset
258
+ */
259
+ delete(id: string, options?: Call): Promise<void>;
260
+ /**
261
+ * A URL to download the original file with, valid for one hour.
262
+ *
263
+ * @effect read
264
+ * @permission ReadCmsAsset
265
+ */
266
+ downloadUrl(id: string, options?: Call): Promise<string>;
267
+ /**
268
+ * Has the CMS fetch a file from a public URL and hand it back, storing nothing: how a browser
269
+ * gets a file it may not fetch itself (another origin) to upload somewhere else, such as
270
+ * `catalog.assets.create`. The CMS refuses what `uploadFromUrl` refuses. The `File` is named
271
+ * as the CMS names it, else after the URL's last path segment.
272
+ *
273
+ * @effect read
274
+ * @permission CreateCmsAsset
275
+ */
276
+ proxyFetch(url: string, options?: Call): Promise<File>;
277
+ }
278
+ /**
279
+ * The file name a `Content-Disposition` header carries. The CMS percent-encodes it inside a
280
+ * quoted `filename` (`attachment; filename="logo%20dark.png"`), and RFC 6266's `filename*`
281
+ * (`UTF-8''…`) is read too.
282
+ */
283
+ export declare function fileNameOfDisposition(header: string | null): string | null;
284
+ /** The URL's last path segment when it names a file (has an extension), else a generic name. */
285
+ export declare function fileNameOfUrl(url: string, mimeType: string): string;
286
+ export declare function createContentAssetsModule(transport: Transport): ContentAssetsModule;
@@ -0,0 +1,181 @@
1
+ import { createPage, offsetCursor, PLATFORM_PASSTHROUGH_KEYS, readOffsetCursor, ValidationError, } from '@sixtyninecommerce/core';
2
+ const DEFAULT_PAGE_SIZE = 50;
3
+ /** The cursor-mode opener: `after` present and empty starts the CMS's keyset walk. */
4
+ const FIRST_PAGE = '';
5
+ /** `metadata` is a stored-verbatim bag; nothing else in an asset is merchant-keyed. */
6
+ const ASSET_KEYS = PLATFORM_PASSTHROUGH_KEYS;
7
+ function assetPath(id) {
8
+ return `/assets/${encodeURIComponent(id)}`;
9
+ }
10
+ function folderPath(id) {
11
+ return `/asset-folders/${encodeURIComponent(id)}`;
12
+ }
13
+ /** A `File` without depending on the DOM lib: anything Blob-like carrying a `name`. */
14
+ function nameOf(file) {
15
+ const name = file.name;
16
+ return typeof name === 'string' && name !== '' ? name : undefined;
17
+ }
18
+ /**
19
+ * The file name a `Content-Disposition` header carries. The CMS percent-encodes it inside a
20
+ * quoted `filename` (`attachment; filename="logo%20dark.png"`), and RFC 6266's `filename*`
21
+ * (`UTF-8''…`) is read too.
22
+ */
23
+ export function fileNameOfDisposition(header) {
24
+ if (header === null)
25
+ return null;
26
+ const extended = /filename\*\s*=\s*(?:UTF-8|utf-8)''([^;]+)/.exec(header)?.[1];
27
+ const plain = /filename\s*=\s*"([^"]*)"|filename\s*=\s*([^;\s]+)/.exec(header);
28
+ const raw = extended ?? plain?.[1] ?? plain?.[2];
29
+ if (raw === undefined || raw.trim() === '')
30
+ return null;
31
+ try {
32
+ return decodeURIComponent(raw.trim());
33
+ }
34
+ catch {
35
+ return raw.trim();
36
+ }
37
+ }
38
+ /** The URL's last path segment when it names a file (has an extension), else a generic name. */
39
+ export function fileNameOfUrl(url, mimeType) {
40
+ try {
41
+ const segment = new URL(url).pathname.split('/').pop();
42
+ if (segment?.includes('.'))
43
+ return decodeURIComponent(segment);
44
+ }
45
+ catch {
46
+ // An unparseable URL or segment falls through to the generic name.
47
+ }
48
+ const subtype = mimeType.split(';')[0]?.split('/')[1]?.trim();
49
+ return `fetched-file.${subtype !== undefined && subtype !== '' ? subtype : 'bin'}`;
50
+ }
51
+ export function createContentAssetsModule(transport) {
52
+ async function listPage(offset, limit, options) {
53
+ const body = await transport.rest('cms', {
54
+ method: 'GET',
55
+ path: '/assets',
56
+ query: {
57
+ // `offset` is always sent: it keeps the CMS out of its keyset mode, which cannot search
58
+ // or open page n. Without `limit` an offset read answers the WHOLE library.
59
+ limit,
60
+ offset,
61
+ folder_id: options?.folderId === null ? 'null' : options?.folderId,
62
+ mime_type: options?.mimeType,
63
+ search: options?.search,
64
+ },
65
+ }, { ...options, preserveKeys: ASSET_KEYS });
66
+ const items = body.data ?? [];
67
+ // An offset read echoes neither the offset nor a cursor: the page is built from what THIS
68
+ // call asked for.
69
+ const total = body.pagination?.total ?? items.length;
70
+ const advanced = offset + (items.length > 0 ? items.length : limit);
71
+ const hasNext = advanced < total;
72
+ return createPage(items, { nextCursor: hasNext ? offsetCursor(advanced) : null, hasNext, total }, (cursor) => listPage(readOffsetCursor(cursor), limit, options));
73
+ }
74
+ async function writeAsset(method, path, body, options) {
75
+ const answer = await transport.rest('cms', { method, path, body }, { ...options, preserveKeys: ASSET_KEYS });
76
+ return answer.data;
77
+ }
78
+ async function folderCall(method, path, body, options) {
79
+ const answer = await transport.rest('cms', { method, path, ...(body === undefined ? {} : { body }) }, options);
80
+ return answer.data;
81
+ }
82
+ return {
83
+ folders: {
84
+ async list(options) {
85
+ // No `after`: only then does the CMS keep the folders in name order and answer them all.
86
+ const body = await transport.rest('cms', { method: 'GET', path: '/asset-folders', query: { parent_id: options?.parentId } }, options);
87
+ return body.data ?? [];
88
+ },
89
+ get(id, options) {
90
+ return folderCall('GET', folderPath(id), undefined, options);
91
+ },
92
+ create(input, options) {
93
+ return folderCall('POST', '/asset-folders', {
94
+ name: input.name,
95
+ ...(input.parentId === undefined ? {} : { parentId: input.parentId }),
96
+ }, options);
97
+ },
98
+ update(id, input, options) {
99
+ return folderCall('PATCH', folderPath(id), input, options);
100
+ },
101
+ async delete(id, options) {
102
+ await transport.rest('cms', { method: 'DELETE', path: folderPath(id), expect: 'void' }, options);
103
+ },
104
+ },
105
+ list(options) {
106
+ const limit = options?.limit ?? DEFAULT_PAGE_SIZE;
107
+ const offset = options?.after === undefined ? 0 : readOffsetCursor(options.after);
108
+ return listPage(offset, limit, options);
109
+ },
110
+ async get(id, options) {
111
+ const body = await transport.rest('cms', { method: 'GET', path: assetPath(id) }, { ...options, preserveKeys: ASSET_KEYS });
112
+ return body.data;
113
+ },
114
+ async byStorageKey(storageKey, options) {
115
+ const body = await transport.rest('cms', {
116
+ method: 'GET',
117
+ path: '/assets',
118
+ // Keyset mode on purpose: it pages the ids first and then loads each file whole. An
119
+ // offset read limits the joined translation rows instead, so `limit: 1` there would
120
+ // answer the file with one of its translations.
121
+ query: { storage_key: storageKey, limit: 1, after: FIRST_PAGE },
122
+ }, { ...options, preserveKeys: ASSET_KEYS });
123
+ return body.data?.[0] ?? null;
124
+ },
125
+ async upload(input, options) {
126
+ const fileName = input.fileName ?? nameOf(input.file);
127
+ if (fileName === undefined || fileName.trim() === '') {
128
+ throw new ValidationError({
129
+ code: 'ASSET_FILE_NAME_REQUIRED',
130
+ message: 'A file needs a name: pass fileName, or a File that carries one. A bare Blob would be stored as "blob". Nothing was sent.',
131
+ status: 400,
132
+ service: 'cms',
133
+ });
134
+ }
135
+ const fields = {};
136
+ if (input.folderId !== undefined)
137
+ fields.folder_id = input.folderId;
138
+ if (input.altText !== undefined)
139
+ fields.alt_text = input.altText;
140
+ const answer = await transport.rest('cms', {
141
+ method: 'POST',
142
+ path: '/assets/upload',
143
+ multipart: { fields, files: [{ field: 'file', file: input.file, fileName }] },
144
+ }, { ...options, preserveKeys: ASSET_KEYS });
145
+ return answer.data;
146
+ },
147
+ uploadFromUrl(input, options) {
148
+ return writeAsset('POST', '/assets/upload-from-url', {
149
+ url: input.url,
150
+ ...(input.folderId === undefined ? {} : { folderId: input.folderId }),
151
+ ...(input.altText === undefined ? {} : { altText: input.altText }),
152
+ }, options);
153
+ },
154
+ update(id, input, options) {
155
+ return writeAsset('PATCH', assetPath(id), {
156
+ ...input,
157
+ ...(input.translations === undefined
158
+ ? {}
159
+ : {
160
+ translations: input.translations.map(({ languageCode, altText }) => ({
161
+ languageCode,
162
+ altText,
163
+ })),
164
+ }),
165
+ }, options);
166
+ },
167
+ async delete(id, options) {
168
+ await transport.rest('cms', { method: 'DELETE', path: assetPath(id), expect: 'void' }, options);
169
+ },
170
+ async downloadUrl(id, options) {
171
+ const body = await transport.rest('cms', { method: 'GET', path: `${assetPath(id)}/download-url` }, options);
172
+ return body.data.url;
173
+ },
174
+ async proxyFetch(url, options) {
175
+ const answer = await transport.rest('cms', { method: 'POST', path: '/assets/proxy-fetch', body: { url }, expect: 'blob' }, options);
176
+ const type = answer.blob.type || (answer.contentType ?? '');
177
+ const name = fileNameOfDisposition(answer.contentDisposition) ?? fileNameOfUrl(url, type);
178
+ return new File([answer.blob], name, { type });
179
+ },
180
+ };
181
+ }