@fluid-app/portal-sdk 0.1.133 → 0.1.134

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 (23) hide show
  1. package/dist/{ProductsScreen-DMldTDPg.mjs → ProductsScreen-BsuY5uQt.mjs} +2 -2
  2. package/dist/{ProductsScreen-BPNdpsYm.cjs → ProductsScreen-CC4bgiMI.cjs} +2 -2
  3. package/dist/{ProductsScreen-jdy2j9_J.mjs → ProductsScreen-Ccxux_2H.mjs} +2 -2
  4. package/dist/{ProductsScreen-jdy2j9_J.mjs.map → ProductsScreen-Ccxux_2H.mjs.map} +1 -1
  5. package/dist/{ProductsScreen-BY285fh-.cjs → ProductsScreen-yBjW5cWx.cjs} +2 -2
  6. package/dist/{ProductsScreen-BY285fh-.cjs.map → ProductsScreen-yBjW5cWx.cjs.map} +1 -1
  7. package/dist/{ShareablesScreen-DMBXIMMj.cjs → ShareablesScreen-BGHIqliw.cjs} +2 -2
  8. package/dist/{ShareablesScreen-znVUD_OK.cjs → ShareablesScreen-C3Xue2W7.cjs} +156 -110
  9. package/dist/ShareablesScreen-C3Xue2W7.cjs.map +1 -0
  10. package/dist/{ShareablesScreen-B6tMQzuX.mjs → ShareablesScreen-D8o7wTGM.mjs} +157 -111
  11. package/dist/ShareablesScreen-D8o7wTGM.mjs.map +1 -0
  12. package/dist/{ShareablesScreen-d_ThfnNQ.mjs → ShareablesScreen-DdaBX6Su.mjs} +2 -2
  13. package/dist/index.cjs +7 -7
  14. package/dist/index.d.cts.map +1 -1
  15. package/dist/index.d.mts.map +1 -1
  16. package/dist/index.mjs +7 -7
  17. package/dist/{src-C5GUPE_i.cjs → src-BAcQ6URl.cjs} +9 -8
  18. package/dist/{src-C5GUPE_i.cjs.map → src-BAcQ6URl.cjs.map} +1 -1
  19. package/dist/{src-DcIV4Mpe.mjs → src-j_uOaPI8.mjs} +10 -3
  20. package/dist/{src-DcIV4Mpe.mjs.map → src-j_uOaPI8.mjs.map} +1 -1
  21. package/package.json +19 -19
  22. package/dist/ShareablesScreen-B6tMQzuX.mjs.map +0 -1
  23. package/dist/ShareablesScreen-znVUD_OK.cjs.map +0 -1
@@ -4,7 +4,7 @@ const require_use_fluid_auth = require("./use-fluid-auth-B7lF_1HS.cjs");
4
4
  const require_use_current_user = require("./use-current-user-DISrPz9d.cjs");
5
5
  const require_AppNavigationContext = require("./AppNavigationContext-Agp0UkCQ.cjs");
6
6
  const require_use_account_clients = require("./use-account-clients-CH92XyHh.cjs");
7
- const require_src = require("./src-C5GUPE_i.cjs");
7
+ const require_src = require("./src-BAcQ6URl.cjs");
8
8
  const require_use_portal_products_client = require("./use-portal-products-client-CIrRGIxC.cjs");
9
9
  let react = require("react");
10
10
  let react_jsx_runtime = require("react/jsx-runtime");
@@ -78,7 +78,7 @@ zod.z.object({ asset: zod.z.object({
78
78
  description: zod.z.string().optional(),
79
79
  tags: zod.z.string().optional()
80
80
  }) });
81
- const damAssetCreateResponseSchema = zod.z.object({
81
+ zod.z.object({
82
82
  asset: damAssetSchema,
83
83
  meta: zod.z.object({
84
84
  request_id: zod.z.string(),
@@ -115,7 +115,7 @@ zod.z.object({
115
115
  }).optional(),
116
116
  skip_autotagging: zod.z.boolean().optional()
117
117
  });
118
- const damAssetPathCreateResponseSchema = zod.z.object({
118
+ zod.z.object({
119
119
  asset: zod.z.object({
120
120
  id: zod.z.number(),
121
121
  canonical_path: zod.z.string(),
@@ -294,8 +294,14 @@ async function dam_assets_list(client, params) {
294
294
  return client.get(`/api/content/dam/assets`, params);
295
295
  }
296
296
  /**
297
- * Create a DAM asset placeholder
298
- * Creates a new DAM asset placeholder record with a canonical path.
297
+ * Create a DAM asset
298
+ * Creates a new DAM asset. Supports two modes:
299
+ 1. **JSON placeholder** — send `application/json` with `asset[name]` to
300
+ create a placeholder record for later file upload.
301
+
302
+ 2. **File upload** — send `multipart/form-data` with `asset[file]`,
303
+ `asset[name]`, and optionally `asset[description]` and `asset[tags]`
304
+ to upload a file and create the full asset with variants.
299
305
  *
300
306
  * @param client - Fetch client instance
301
307
  * @param body - body
@@ -325,6 +331,36 @@ async function dam_asset_paths_list(client, asset_code, params) {
325
331
  async function dam_asset_paths_create(client, asset_code, body) {
326
332
  return client.post(`/api/content/dam/assets/${asset_code}/paths`, body);
327
333
  }
334
+ /**
335
+ * Query DAM assets using tree paths and tags
336
+ * Searches and retrieves DAM assets using tree path pattern matching, tag-based variant filtering, wildcard name matching, and partial search. Supports cursor pagination for large result sets.
337
+ *
338
+ * @param client - Fetch client instance
339
+ * @param body - body
340
+ */
341
+ async function dam_query(client, body) {
342
+ return client.post(`/api/content/dam/query`, body);
343
+ }
344
+ /**
345
+ * Delete a DAM asset
346
+ * Permanently destroys a DAM asset, including its ImageKit storage, variants, and database records.
347
+ *
348
+ * @param client - Fetch client instance
349
+ * @param code - code
350
+ */
351
+ async function dam_assets_destroy(client, code) {
352
+ return client.delete(`/api/content/dam/assets/${code}`);
353
+ }
354
+ /**
355
+ * Discard (soft-delete) a DAM asset
356
+ * Soft-deletes a DAM asset. Used to discard an in-progress upload without permanently removing the record.
357
+ *
358
+ * @param client - Fetch client instance
359
+ * @param code - code
360
+ */
361
+ async function dam_assets_discard(client, code) {
362
+ return client.patch(`/api/content/dam/assets/${code}/discard`);
363
+ }
328
364
  //#endregion
329
365
  //#region ../../shareables/api-client/src/portal-tenant-media-adapter.ts
330
366
  /**
@@ -800,9 +836,12 @@ function mapDamAsset(raw) {
800
836
  id: raw.id ?? 0,
801
837
  code: raw.code ?? "",
802
838
  name: raw.name ?? "",
803
- content_type: raw.content_type ?? null,
804
- byte_size: raw.byte_size ?? null,
805
- url: raw.url ?? null,
839
+ description: raw.description ?? null,
840
+ category: raw.category ?? null,
841
+ company: raw.company ?? null,
842
+ default_variant_id: raw.default_variant_id ?? null,
843
+ default_variant_url: raw.default_variant_url ?? null,
844
+ canonical_path: raw.canonical_path ?? null,
806
845
  created_at: raw.created_at ?? "",
807
846
  updated_at: raw.updated_at ?? ""
808
847
  };
@@ -899,15 +938,15 @@ function createPortalTenantFilesShareablesAdapter(client) {
899
938
  const fileResources = response.assets.map((asset) => ({
900
939
  id: asset.id,
901
940
  alt_text: asset.name,
902
- url: asset.url ?? "",
941
+ url: asset.default_variant_url ?? "",
903
942
  filename: asset.name,
904
- content_type: asset.content_type ?? "application/octet-stream",
905
- content_size: asset.byte_size ?? 0,
943
+ content_type: "application/octet-stream",
944
+ content_size: 0,
906
945
  handle: asset.code,
907
946
  dam_asset_code: asset.code,
908
- preview_image_url: asset.url ?? "",
947
+ preview_image_url: asset.default_variant_url ?? "",
909
948
  created_at: asset.created_at,
910
- updated_at: asset.updated_at,
949
+ updated_at: asset.updated_at ?? "",
911
950
  relateable_type: null,
912
951
  relateable_id: null,
913
952
  content: null
@@ -1017,87 +1056,6 @@ function createPortalTenantSharesShareablesAdapter(client) {
1017
1056
  } };
1018
1057
  }
1019
1058
  //#endregion
1020
- //#region ../../file-picker/api-client/src/client.ts
1021
- function createFilePickerClient(config) {
1022
- return {
1023
- fetchClient: config.fetchClient,
1024
- uploadStrategy: config.uploadStrategy,
1025
- unsplashAccessKey: config.unsplashAccessKey,
1026
- proxyEndpoint: config.proxyEndpoint ?? "/api/proxy-url"
1027
- };
1028
- }
1029
- //#endregion
1030
- //#region ../../file-picker/api-client/src/api/dam-assets.ts
1031
- /**
1032
- * Create a DAM asset. Text files use FormData upload; non-text files
1033
- * delegate to the provided uploadStrategy (e.g. ImageKit).
1034
- * If no uploadStrategy is provided, all files use FormData upload.
1035
- */
1036
- async function createDamAsset(fetchClient, params, uploadStrategy) {
1037
- const mimeType = require_src.getFileMimeType(params.file);
1038
- if (mimeType.startsWith("text/") || mimeType === "application/json" || mimeType === "application/xml" || params.file.name.endsWith(".txt") || params.file.name.endsWith(".json") || params.file.name.endsWith(".xml") || params.file.name.endsWith(".csv")) return createDamAssetViaFormData(fetchClient, params);
1039
- if (uploadStrategy) return uploadStrategy.uploadFile(params);
1040
- return createDamAssetViaFormData(fetchClient, params);
1041
- }
1042
- async function createDamAssetViaFormData(fetchClient, params) {
1043
- const formData = new FormData();
1044
- formData.append("asset[file]", params.file);
1045
- formData.append("asset[name]", params.name);
1046
- if (params.description) formData.append("asset[description]", params.description);
1047
- if (params.tags && params.tags.length > 0) formData.append("asset[tags]", params.tags.join(","));
1048
- const response = await fetchClient.requestWithFormData("/dam/assets", formData, { method: "POST" });
1049
- return damAssetCreateResponseSchema.parse(response);
1050
- }
1051
- async function createDamAssetPathForAssets(fetchClient, { asset_paths, code }) {
1052
- const response = await fetchClient.post(`/dam/assets/${code}/asset_paths`, { asset_paths });
1053
- return damAssetPathCreateResponseSchema.parse(response);
1054
- }
1055
- //#endregion
1056
- //#region ../../file-picker/api-client/src/api/dam-query.ts
1057
- async function queryDamAssets(fetchClient, params) {
1058
- const response = await fetchClient.post("/dam/query", params);
1059
- return damQueryResponseSchema.parse(response);
1060
- }
1061
- async function deleteDamAsset(fetchClient, code) {
1062
- return fetchClient.delete(`/dam/assets/${code}`);
1063
- }
1064
- async function discardDamAsset(fetchClient, code) {
1065
- return fetchClient.patch(`/dam/assets/${code}/discard`);
1066
- }
1067
- //#endregion
1068
- //#region ../../file-picker/api-client/src/api/unsplash.ts
1069
- const unsplashImageSchema = zod.z.object({
1070
- id: zod.z.string(),
1071
- urls: zod.z.object({
1072
- raw: zod.z.string(),
1073
- full: zod.z.string(),
1074
- regular: zod.z.string(),
1075
- small: zod.z.string(),
1076
- thumb: zod.z.string()
1077
- }),
1078
- alt_description: zod.z.string().nullable(),
1079
- description: zod.z.string().nullable(),
1080
- user: zod.z.object({
1081
- name: zod.z.string(),
1082
- username: zod.z.string()
1083
- }),
1084
- width: zod.z.number(),
1085
- height: zod.z.number()
1086
- });
1087
- const unsplashSearchResponseSchema = zod.z.object({
1088
- results: zod.z.array(unsplashImageSchema),
1089
- total: zod.z.number(),
1090
- total_pages: zod.z.number()
1091
- });
1092
- /**
1093
- * Search Unsplash for photos matching a query.
1094
- */
1095
- async function searchUnsplash(query, accessKey, page = 1, perPage = 20) {
1096
- const response = await fetch(`https://api.unsplash.com/search/photos?query=${encodeURIComponent(query)}&page=${page}&per_page=${perPage}&client_id=${accessKey}`);
1097
- if (!response.ok) throw new Error("Failed to search Unsplash");
1098
- return unsplashSearchResponseSchema.parse(await response.json());
1099
- }
1100
- //#endregion
1101
1059
  //#region ../../file-picker/api-client/src/api/url-proxy.ts
1102
1060
  const urlProxyResponseSchema = zod.z.object({
1103
1061
  data: zod.z.string(),
@@ -1125,24 +1083,93 @@ async function proxyUrlFetch(url, proxyEndpoint = "/api/proxy-url") {
1125
1083
  return urlProxyResponseSchema.parse(data);
1126
1084
  }
1127
1085
  //#endregion
1128
- //#region ../../file-picker/api-client/src/create-file-picker-api.ts
1086
+ //#region ../../file-picker/api-client/src/portal-tenant-adapter.ts
1087
+ /**
1088
+ * Maps a BFF DAM asset to the file-picker port's DamAssetCreateResponse shape.
1089
+ *
1090
+ * The BFF response includes nullable meta.request_id (from Api::Response),
1091
+ * while the port schema requires a string. We coalesce to empty string.
1092
+ */
1093
+ function mapCreateResponse(response) {
1094
+ const raw = response.asset ?? {};
1095
+ return {
1096
+ asset: damAssetSchema.parse({
1097
+ ...raw,
1098
+ canonical_path: raw.canonical_path ?? "",
1099
+ category: raw.category ?? "",
1100
+ company: raw.company ?? "",
1101
+ description: raw.description ?? "",
1102
+ default_variant_id: raw.default_variant_id ?? ""
1103
+ }),
1104
+ meta: {
1105
+ request_id: response.meta?.request_id ?? "",
1106
+ timestamp: response.meta?.timestamp ?? (/* @__PURE__ */ new Date()).toISOString()
1107
+ }
1108
+ };
1109
+ }
1110
+ /**
1111
+ * Maps a BFF asset path response to the file-picker port's shape.
1112
+ */
1113
+ function mapAssetPathCreateResponse(response) {
1114
+ const assetPath = response.asset_path ?? {};
1115
+ return {
1116
+ asset: {
1117
+ id: assetPath.id ?? 0,
1118
+ canonical_path: assetPath.path ?? "",
1119
+ name: assetPath.asset_code ?? ""
1120
+ },
1121
+ meta: {
1122
+ request_id: response.meta?.request_id ?? "",
1123
+ timestamp: response.meta?.timestamp ?? (/* @__PURE__ */ new Date()).toISOString()
1124
+ }
1125
+ };
1126
+ }
1129
1127
  /**
1130
- * Creates a FilePickerApi-compatible adapter backed by the real API client
1131
- * functions. The returned object satisfies the FilePickerApi interface
1132
- * from @fluid-app/file-picker-core via structural typing.
1128
+ * Creates a FilePickerApi adapter backed by the portal-tenant BFF's
1129
+ * `/api/content/dam/*` endpoints, using cookie-based auth via the
1130
+ * provided FetchClient.
1131
+ *
1132
+ * Unsplash search is not available through the BFF — callers that need
1133
+ * Unsplash should use the legacy adapter or provide their own
1134
+ * implementation.
1135
+ *
1136
+ * The `onProgress` callback in `createDamAsset` is not supported — the
1137
+ * underlying `fetch` API does not expose upload progress events.
1138
+ *
1139
+ * URL proxy delegates to the same-origin `/api/proxy-url` endpoint
1140
+ * (served by the hosting app, not the BFF) for CORS bypass, identical
1141
+ * to the legacy adapter behaviour.
1133
1142
  */
1134
- function createFilePickerApi(client) {
1143
+ function createPortalTenantFilePickerApiAdapter(client) {
1135
1144
  return {
1136
- createDamAsset: (params) => createDamAsset(client.fetchClient, params, client.uploadStrategy),
1137
- queryDamAssets: (params) => queryDamAssets(client.fetchClient, params),
1138
- searchUnsplash: (query, page, perPage) => {
1139
- if (!client.unsplashAccessKey) throw new Error("Unsplash access key not configured");
1140
- return searchUnsplash(query, client.unsplashAccessKey, page, perPage);
1145
+ createDamAsset: async (params) => {
1146
+ const formData = new FormData();
1147
+ formData.append("asset[file]", params.file);
1148
+ formData.append("asset[name]", params.name);
1149
+ if (params.description) formData.append("asset[description]", params.description);
1150
+ if (params.tags && params.tags.length > 0) formData.append("asset[tags]", params.tags.join(","));
1151
+ return mapCreateResponse(await client.requestWithFormData("/api/content/dam/assets", formData, { method: "POST" }));
1152
+ },
1153
+ queryDamAssets: async (params) => {
1154
+ const response = await dam_query(client, params);
1155
+ return damQueryResponseSchema.parse({
1156
+ ...response,
1157
+ meta: response.meta ? { next_cursor: response.meta.pagination?.next_cursor ?? void 0 } : void 0
1158
+ });
1159
+ },
1160
+ deleteDamAsset: async (code) => {
1161
+ return dam_assets_destroy(client, code);
1162
+ },
1163
+ discardDamAsset: async (code) => {
1164
+ return dam_assets_discard(client, code);
1165
+ },
1166
+ createDamAssetPathForAssets: async (params) => {
1167
+ return mapAssetPathCreateResponse(await dam_asset_paths_create(client, params.code, { asset_path: { path: params.asset_paths.join(",") } }));
1168
+ },
1169
+ searchUnsplash: async (_query, _page, _perPage) => {
1170
+ throw new Error("Unsplash search is not available through the portal-tenant BFF. Configure an Unsplash access key and use the standard FilePickerApi adapter instead.");
1141
1171
  },
1142
- deleteDamAsset: (code) => deleteDamAsset(client.fetchClient, code),
1143
- discardDamAsset: (code) => discardDamAsset(client.fetchClient, code),
1144
- createDamAssetPathForAssets: (params) => createDamAssetPathForAssets(client.fetchClient, params),
1145
- proxyUrlFetch: (url) => proxyUrlFetch(url, client.proxyEndpoint)
1172
+ proxyUrlFetch: (url) => proxyUrlFetch(url)
1146
1173
  };
1147
1174
  }
1148
1175
  //#endregion
@@ -1179,6 +1206,7 @@ function ShareablesScreen({ background, textColor, accentColor, padding, borderR
1179
1206
  const domainClient = require_use_account_clients.useSdkClient();
1180
1207
  const portalProductsApi = require_use_portal_products_client.usePortalProductsClient();
1181
1208
  const portalTenantClient = require_FluidProvider.usePortalTenantClient();
1209
+ const { config } = require_FluidProvider.useFluidContext();
1182
1210
  const { data: userData } = require_use_current_user.useCurrentUser();
1183
1211
  const { currentSlug, navigate } = require_AppNavigationContext.useAppNavigation();
1184
1212
  const { isCustomer } = useUserType();
@@ -1239,7 +1267,25 @@ function ShareablesScreen({ background, textColor, accentColor, padding, borderR
1239
1267
  }) };
1240
1268
  } }
1241
1269
  }), [portalTenantClient, portalProductsApi]);
1242
- const filePickerApi = (0, react.useMemo)(() => createFilePickerApi(createFilePickerClient({ fetchClient: domainClient })), [domainClient]);
1270
+ const filePickerApi = (0, react.useMemo)(() => {
1271
+ const baseUrl = config.baseUrl.replace(/\/+$/, "").replace(/\/api$/, "");
1272
+ const csrfToken = typeof document !== "undefined" ? document.querySelector("meta[name=\"csrf-token\"]")?.getAttribute("content") : null;
1273
+ return createPortalTenantFilePickerApiAdapter(require_FluidProvider.createFetchClient({
1274
+ baseUrl,
1275
+ getAuthToken: config.getAuthToken,
1276
+ onAuthError: config.onAuthError,
1277
+ credentials: "include",
1278
+ defaultHeaders: {
1279
+ ...config.defaultHeaders,
1280
+ ...csrfToken ? { "X-CSRF-Token": csrfToken } : {}
1281
+ }
1282
+ }));
1283
+ }, [
1284
+ config.baseUrl,
1285
+ config.getAuthToken,
1286
+ config.onAuthError,
1287
+ config.defaultHeaders
1288
+ ]);
1243
1289
  const uiConfig = (0, react.useMemo)(() => ({
1244
1290
  user: userData ? {
1245
1291
  id: userData.id,
@@ -1329,4 +1375,4 @@ Object.defineProperty(exports, "useUserType", {
1329
1375
  }
1330
1376
  });
1331
1377
 
1332
- //# sourceMappingURL=ShareablesScreen-znVUD_OK.cjs.map
1378
+ //# sourceMappingURL=ShareablesScreen-C3Xue2W7.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ShareablesScreen-C3Xue2W7.cjs","names":["useFluidAuth","USER_TYPES","z","mapMeta","portalTenantContent.media_list","portalTenantContent.media_create","portalTenantContent.media_show","portalTenantContent.media_update","portalTenantContent.media_destroy","mediaKindFromType","mapMeta","portalTenantContent.playlists_list","portalTenantContent.playlists_create","portalTenantContent.playlists_show","portalTenantContent.playlists_update","portalTenantContent.playlists_destroy","portalTenantContent.playlists_items_list","portalTenantContent.playlists_items_add","portalTenantContent.playlists_items_remove","mapMeta","portalTenantContent.dam_assets_list","portalTenantContent.dam_assets_create","portalTenantContent.dam_asset_paths_list","portalTenantContent.dam_asset_paths_create","portalTenantContent.shares_list","portalTenantContent.shares_create","z","portalTenantContent.dam_query","portalTenantContent.dam_assets_destroy","portalTenantContent.dam_assets_discard","portalTenantContent.dam_asset_paths_create","useSdkClient","usePortalProductsClient","usePortalTenantClient","useFluidContext","useCurrentUser","useAppNavigation","createFetchClient","ShareablesCoreProvider","ShareablesApiProvider","ShareablesUIProvider","ShareablesApp"],"sources":["../src/hooks/use-user-type.ts","../../../file-picker/core/src/schemas/dam.ts","../../../api-clients/portal-tenant-content/src/namespaces/portal_tenant_content.ts","../../../shareables/api-client/src/portal-tenant-media-adapter.ts","../../../shareables/api-client/src/portal-tenant-playlists-adapter.ts","../../../shareables/api-client/src/portal-tenant-dam-assets-adapter.ts","../../../shareables/api-client/src/portal-tenant-shares-adapter.ts","../../../file-picker/api-client/src/api/url-proxy.ts","../../../file-picker/api-client/src/portal-tenant-adapter.ts","../src/screens/ShareablesScreen.tsx"],"sourcesContent":["import { useMemo } from \"react\";\nimport { useFluidAuth } from \"./use-fluid-auth\";\nimport { USER_TYPES, type UserType } from \"../auth/types\";\n\nexport interface UseUserTypeResult {\n userType: UserType | null;\n isCustomer: boolean;\n isRep: boolean;\n isAdmin: boolean;\n}\n\n/**\n * Convenience hook for user-type checks in the portal SDK.\n */\nexport function useUserType(): UseUserTypeResult {\n const { user } = useFluidAuth();\n\n return useMemo(() => {\n const userType = user?.user_type ?? null;\n return {\n userType,\n isCustomer: userType === USER_TYPES.customer,\n isRep: userType === USER_TYPES.rep,\n isAdmin:\n userType === USER_TYPES.admin || userType === USER_TYPES.root_admin,\n };\n }, [user?.user_type]);\n}\n","import { z } from \"zod\";\n\ntype DamVariantApi = {\n id: string;\n url: string | null;\n file_name: string;\n mime_type: string;\n content: any;\n created_at: string;\n updated_at: string;\n default: boolean;\n is_original: boolean;\n is_text: boolean;\n media_type: string;\n processing_status: string;\n tags: string[];\n};\n\nexport const damVariantSchema: z.ZodType<DamVariantApi> = z.object({\n id: z.string(),\n url: z.string().nullable(),\n file_name: z.string(),\n mime_type: z.string(),\n content: z.any().nullable(),\n created_at: z.string(),\n updated_at: z.string(),\n default: z.boolean(),\n is_original: z.boolean(),\n is_text: z.boolean(),\n media_type: z.string(),\n processing_status: z.string(),\n tags: z.array(z.string()),\n});\n\ntype DamAssetApi = {\n id: number;\n canonical_path: string;\n category: string;\n code: string;\n company: string;\n created_at: string;\n default_variant_id: string;\n default_variant_url?: string;\n description: string;\n name: string;\n updated_at: string;\n variants?: DamVariantApi[];\n};\n\nexport const damAssetSchema: z.ZodType<DamAssetApi> = z.object({\n id: z.number(),\n canonical_path: z.string(),\n category: z.string(),\n code: z.string(),\n company: z.string(),\n created_at: z.string(),\n default_variant_id: z.string(),\n default_variant_url: z.string().optional(),\n description: z.string(),\n name: z.string(),\n updated_at: z.string(),\n variants: z.array(damVariantSchema).optional(),\n});\n\ntype DamTreeFolderNode = {\n asset_code?: string | Record<string, unknown>;\n name?: string | Record<string, unknown>;\n category?: string | Record<string, unknown>;\n variants?: unknown[] | Record<string, unknown>;\n [key: string]: unknown;\n};\n\nexport const damTreeFolderNodeSchema: z.ZodType<DamTreeFolderNode> = z\n .object({\n asset_code: z\n .union([z.string(), z.record(z.string(), z.unknown())])\n .optional(),\n name: z.union([z.string(), z.record(z.string(), z.unknown())]).optional(),\n category: z\n .union([z.string(), z.record(z.string(), z.unknown())])\n .optional(),\n variants: z\n .union([z.array(z.unknown()), z.record(z.string(), z.unknown())])\n .optional(),\n })\n .passthrough();\n\nexport const damTreeSchema: z.ZodType<Record<string, any>> = z.record(\n z.string(),\n z.union([\n z.lazy(() => damTreeSchema),\n damAssetSchema,\n damTreeFolderNodeSchema,\n ]),\n);\n\ntype DamQueryResponse = {\n path: string;\n tree: Record<string, any>;\n meta?: { next_cursor?: string };\n};\n\nexport const damQueryResponseSchema: z.ZodType<DamQueryResponse> = z.object({\n path: z.string(),\n tree: damTreeSchema,\n meta: z\n .object({\n next_cursor: z.string().optional(),\n })\n .optional(),\n});\n\ntype DamAssetCreateRequest = {\n asset: {\n file: any;\n name: string;\n description?: string;\n tags?: string;\n };\n};\n\nexport const damAssetCreateRequestSchema: z.ZodType<DamAssetCreateRequest> =\n z.object({\n asset: z.object({\n file: z.any(),\n name: z.string(),\n description: z.string().optional(),\n tags: z.string().optional(),\n }),\n });\n\ntype DamAssetCreateResponse = {\n asset: DamAssetApi;\n meta: { request_id: string; timestamp: string };\n};\n\nexport const damAssetCreateResponseSchema: z.ZodType<DamAssetCreateResponse> =\n z.object({\n asset: damAssetSchema,\n meta: z.object({\n request_id: z.string(),\n timestamp: z.string(),\n }),\n });\n\n// Schema for creating asset with placeholder (for ImageKit direct upload)\ntype DamAssetCreateWithPlaceholderRequest = {\n placeholder_asset: {\n mime_type: string;\n name?: string;\n description?: string;\n };\n skip_autotagging?: boolean;\n};\n\nexport const damAssetCreateWithPlaceholderRequestSchema: z.ZodType<DamAssetCreateWithPlaceholderRequest> =\n z.object({\n placeholder_asset: z.object({\n mime_type: z.string(),\n name: z.string().optional(),\n description: z.string().optional(),\n }),\n skip_autotagging: z.boolean().optional(),\n });\n\n// Schema for creating asset path without file upload (legacy)\ntype DamAssetPathCreateRequest = {\n asset?: {\n file: any;\n name: string;\n description?: string;\n tags?: string;\n };\n text_asset?: {\n file_name: string;\n mime_type: string;\n text: string;\n name?: string;\n description?: string;\n tags?: string;\n };\n placeholder_asset?: {\n mime_type: string;\n name?: string;\n description?: string;\n };\n skip_autotagging?: boolean;\n};\n\nexport const damAssetPathCreateRequestSchema: z.ZodType<DamAssetPathCreateRequest> =\n z.object({\n asset: z\n .object({\n file: z.any(),\n name: z.string(),\n description: z.string().optional(),\n tags: z.string().optional(),\n })\n .optional(),\n text_asset: z\n .object({\n file_name: z.string(),\n mime_type: z.string(),\n text: z.string(),\n name: z.string().optional(),\n description: z.string().optional(),\n tags: z.string().optional(),\n })\n .optional(),\n placeholder_asset: z\n .object({\n mime_type: z.string(),\n name: z.string().optional(),\n description: z.string().optional(),\n })\n .optional(),\n skip_autotagging: z.boolean().optional(),\n });\n\ntype DamAssetPathCreateResponse = {\n asset: { id: number; canonical_path: string; name: string };\n meta: { request_id: string; timestamp: string };\n};\n\nexport const damAssetPathCreateResponseSchema: z.ZodType<DamAssetPathCreateResponse> =\n z.object({\n asset: z.object({\n id: z.number(),\n canonical_path: z.string(),\n name: z.string(),\n }),\n meta: z.object({\n request_id: z.string(),\n timestamp: z.string(),\n }),\n });\n\nexport type { DamVariantApi, DamAssetApi };\nexport type DamTreeApi = z.infer<typeof damTreeSchema>;\nexport type { DamQueryResponse };\nexport type { DamAssetCreateRequest };\nexport type { DamAssetCreateResponse };\nexport type { DamAssetCreateWithPlaceholderRequest };\nexport type { DamAssetPathCreateRequest };\nexport type { DamAssetPathCreateResponse };\n","/**\n * Generated API client functions for portal_tenant_content\n *\n * DO NOT EDIT THIS FILE DIRECTLY\n * This file is auto-generated. To update:\n * 1. Update the OpenAPI spec file\n * 2. Run: pnpm generate\n */\n\nimport type { FetchClient } from \"../lib/fetch-client\";\nimport type { operations } from \"../generated/portal-tenant-content\";\n\n// ============================================================================\n// content\n// ============================================================================\n\n/**\n * List media (own uploads and company media)\n * Returns a paginated list of the member's own uploads and company-owned media.\n *\n * @param client - Fetch client instance\n * @param [params] - params\n */\nexport async function media_list(\n client: FetchClient,\n params?: operations[\"media_list\"][\"parameters\"][\"query\"],\n): Promise<\n operations[\"media_list\"][\"responses\"][200][\"content\"][\"application/json\"]\n> {\n return client.get(`/api/content/media`, params);\n}\n\n/**\n * Create a new media item\n * Creates a new media item record.\n *\n * @param client - Fetch client instance\n * @param body - body\n */\nexport async function media_create(\n client: FetchClient,\n body: operations[\"media_create\"][\"requestBody\"][\"content\"][\"application/json\"],\n): Promise<\n operations[\"media_create\"][\"responses\"][201][\"content\"][\"application/json\"]\n> {\n return client.post(`/api/content/media`, body);\n}\n\n/**\n * Get a specific media item\n * Returns a single media item by ID.\n *\n * @param client - Fetch client instance\n * @param id - id\n */\nexport async function media_show(\n client: FetchClient,\n id: string | number,\n): Promise<\n operations[\"media_show\"][\"responses\"][200][\"content\"][\"application/json\"]\n> {\n return client.get(`/api/content/media/${id}`);\n}\n\n/**\n * Update a media item (own uploads only)\n * Updates a media item's title or description.\n *\n * @param client - Fetch client instance\n * @param id - id\n * @param body - body\n */\nexport async function media_update(\n client: FetchClient,\n id: string | number,\n body: operations[\"media_update\"][\"requestBody\"][\"content\"][\"application/json\"],\n): Promise<\n operations[\"media_update\"][\"responses\"][200][\"content\"][\"application/json\"]\n> {\n return client.patch(`/api/content/media/${id}`, body);\n}\n\n/**\n * Delete a media item (own uploads only)\n * Removes a media item.\n *\n * @param client - Fetch client instance\n * @param id - id\n */\nexport async function media_destroy(\n client: FetchClient,\n id: string | number,\n): Promise<\n operations[\"media_destroy\"][\"responses\"][200][\"content\"][\"application/json\"]\n> {\n return client.delete(`/api/content/media/${id}`);\n}\n\n/**\n * List playlists with cursor pagination\n * Returns a paginated list of playlists.\n *\n * @param client - Fetch client instance\n * @param [params] - params\n */\nexport async function playlists_list(\n client: FetchClient,\n params?: operations[\"playlists_list\"][\"parameters\"][\"query\"],\n): Promise<\n operations[\"playlists_list\"][\"responses\"][200][\"content\"][\"application/json\"]\n> {\n return client.get(`/api/content/playlists`, params);\n}\n\n/**\n * Create a new playlist\n * Creates a new playlist.\n *\n * @param client - Fetch client instance\n * @param body - body\n */\nexport async function playlists_create(\n client: FetchClient,\n body: operations[\"playlists_create\"][\"requestBody\"][\"content\"][\"application/json\"],\n): Promise<\n operations[\"playlists_create\"][\"responses\"][201][\"content\"][\"application/json\"]\n> {\n return client.post(`/api/content/playlists`, body);\n}\n\n/**\n * Get a specific playlist\n * Returns a single playlist by ID.\n *\n * @param client - Fetch client instance\n * @param id - id\n */\nexport async function playlists_show(\n client: FetchClient,\n id: string | number,\n): Promise<\n operations[\"playlists_show\"][\"responses\"][200][\"content\"][\"application/json\"]\n> {\n return client.get(`/api/content/playlists/${id}`);\n}\n\n/**\n * Update a playlist\n * Updates a playlist's title or metadata.\n *\n * @param client - Fetch client instance\n * @param id - id\n * @param body - body\n */\nexport async function playlists_update(\n client: FetchClient,\n id: string | number,\n body: operations[\"playlists_update\"][\"requestBody\"][\"content\"][\"application/json\"],\n): Promise<\n operations[\"playlists_update\"][\"responses\"][200][\"content\"][\"application/json\"]\n> {\n return client.patch(`/api/content/playlists/${id}`, body);\n}\n\n/**\n * Delete a playlist\n * Removes a playlist.\n *\n * @param client - Fetch client instance\n * @param id - id\n */\nexport async function playlists_destroy(\n client: FetchClient,\n id: string | number,\n): Promise<\n operations[\"playlists_destroy\"][\"responses\"][200][\"content\"][\"application/json\"]\n> {\n return client.delete(`/api/content/playlists/${id}`);\n}\n\n/**\n * List items in a playlist\n * Returns a paginated list of items in a playlist.\n *\n * @param client - Fetch client instance\n * @param playlist_id - playlist_id\n * @param [params] - params\n */\nexport async function playlists_items_list(\n client: FetchClient,\n playlist_id: string | number,\n params?: operations[\"playlists_items_list\"][\"parameters\"][\"query\"],\n): Promise<\n operations[\"playlists_items_list\"][\"responses\"][200][\"content\"][\"application/json\"]\n> {\n return client.get(`/api/content/playlists/${playlist_id}/items`, params);\n}\n\n/**\n * Add an item to a playlist\n * Adds a media item to a playlist.\n *\n * @param client - Fetch client instance\n * @param playlist_id - playlist_id\n * @param body - body\n */\nexport async function playlists_items_add(\n client: FetchClient,\n playlist_id: string | number,\n body: operations[\"playlists_items_add\"][\"requestBody\"][\"content\"][\"application/json\"],\n): Promise<\n operations[\"playlists_items_add\"][\"responses\"][201][\"content\"][\"application/json\"]\n> {\n return client.post(`/api/content/playlists/${playlist_id}/items`, body);\n}\n\n/**\n * Remove an item from a playlist\n * Removes a single item from a playlist.\n *\n * @param client - Fetch client instance\n * @param playlist_id - playlist_id\n * @param id - id\n */\nexport async function playlists_items_remove(\n client: FetchClient,\n playlist_id: string | number,\n id: string | number,\n): Promise<\n operations[\"playlists_items_remove\"][\"responses\"][200][\"content\"][\"application/json\"]\n> {\n return client.delete(`/api/content/playlists/${playlist_id}/items/${id}`);\n}\n\n/**\n * List share links for the current user\n * Returns a paginated list of share links.\n *\n * @param client - Fetch client instance\n * @param [params] - params\n */\nexport async function shares_list(\n client: FetchClient,\n params?: operations[\"shares_list\"][\"parameters\"][\"query\"],\n): Promise<\n operations[\"shares_list\"][\"responses\"][200][\"content\"][\"application/json\"]\n> {\n return client.get(`/api/shares`, params);\n}\n\n/**\n * Create a share link\n * Creates a new share link for content.\n *\n * @param client - Fetch client instance\n * @param body - body\n */\nexport async function shares_create(\n client: FetchClient,\n body: operations[\"shares_create\"][\"requestBody\"][\"content\"][\"application/json\"],\n): Promise<\n operations[\"shares_create\"][\"responses\"][201][\"content\"][\"application/json\"]\n> {\n return client.post(`/api/shares`, body);\n}\n\n/**\n * List DAM assets\n * Returns a paginated list of DAM assets for the company.\n *\n * @param client - Fetch client instance\n * @param [params] - params\n */\nexport async function dam_assets_list(\n client: FetchClient,\n params?: operations[\"dam_assets_list\"][\"parameters\"][\"query\"],\n): Promise<\n operations[\"dam_assets_list\"][\"responses\"][200][\"content\"][\"application/json\"]\n> {\n return client.get(`/api/content/dam/assets`, params);\n}\n\n/**\n * Create a DAM asset\n * Creates a new DAM asset. Supports two modes:\n1. **JSON placeholder** — send `application/json` with `asset[name]` to\n create a placeholder record for later file upload.\n\n2. **File upload** — send `multipart/form-data` with `asset[file]`,\n `asset[name]`, and optionally `asset[description]` and `asset[tags]`\n to upload a file and create the full asset with variants.\n *\n * @param client - Fetch client instance\n * @param body - body\n */\nexport async function dam_assets_create(\n client: FetchClient,\n body: operations[\"dam_assets_create\"][\"requestBody\"][\"content\"][\"application/json\"],\n): Promise<\n operations[\"dam_assets_create\"][\"responses\"][201][\"content\"][\"application/json\"]\n> {\n return client.post(`/api/content/dam/assets`, body);\n}\n\n/**\n * List paths for a DAM asset\n * Returns a paginated list of path aliases for a DAM asset.\n *\n * @param client - Fetch client instance\n * @param asset_code - asset_code\n * @param [params] - params\n */\nexport async function dam_asset_paths_list(\n client: FetchClient,\n asset_code: string | number,\n params?: operations[\"dam_asset_paths_list\"][\"parameters\"][\"query\"],\n): Promise<\n operations[\"dam_asset_paths_list\"][\"responses\"][200][\"content\"][\"application/json\"]\n> {\n return client.get(`/api/content/dam/assets/${asset_code}/paths`, params);\n}\n\n/**\n * Create a path alias for a DAM asset\n * Creates a new path alias for an existing DAM asset.\n *\n * @param client - Fetch client instance\n * @param asset_code - asset_code\n * @param body - body\n */\nexport async function dam_asset_paths_create(\n client: FetchClient,\n asset_code: string | number,\n body: operations[\"dam_asset_paths_create\"][\"requestBody\"][\"content\"][\"application/json\"],\n): Promise<\n operations[\"dam_asset_paths_create\"][\"responses\"][201][\"content\"][\"application/json\"]\n> {\n return client.post(`/api/content/dam/assets/${asset_code}/paths`, body);\n}\n\n/**\n * Query DAM assets using tree paths and tags\n * Searches and retrieves DAM assets using tree path pattern matching, tag-based variant filtering, wildcard name matching, and partial search. Supports cursor pagination for large result sets.\n *\n * @param client - Fetch client instance\n * @param body - body\n */\nexport async function dam_query(\n client: FetchClient,\n body: operations[\"dam_query\"][\"requestBody\"][\"content\"][\"application/json\"],\n): Promise<\n operations[\"dam_query\"][\"responses\"][200][\"content\"][\"application/json\"]\n> {\n return client.post(`/api/content/dam/query`, body);\n}\n\n/**\n * Delete a DAM asset\n * Permanently destroys a DAM asset, including its ImageKit storage, variants, and database records.\n *\n * @param client - Fetch client instance\n * @param code - code\n */\nexport async function dam_assets_destroy(\n client: FetchClient,\n code: string | number,\n): Promise<\n operations[\"dam_assets_destroy\"][\"responses\"][200][\"content\"][\"application/json\"]\n> {\n return client.delete(`/api/content/dam/assets/${code}`);\n}\n\n/**\n * Discard (soft-delete) a DAM asset\n * Soft-deletes a DAM asset. Used to discard an in-progress upload without permanently removing the record.\n *\n * @param client - Fetch client instance\n * @param code - code\n */\nexport async function dam_assets_discard(\n client: FetchClient,\n code: string | number,\n): Promise<\n operations[\"dam_assets_discard\"][\"responses\"][200][\"content\"][\"application/json\"]\n> {\n return client.patch(`/api/content/dam/assets/${code}/discard`);\n}\n","import type {\n ContentMediaApi,\n media,\n} from \"@fluid-app/shareables-core/media-api\";\nimport type { MediaApi } from \"@fluid-app/shareables-core/shareables-api\";\nimport type { shareables } from \"@fluid-app/shareables-core/types\";\nimport type { FetchClient } from \"./lib/fetch-client\";\nimport { portalTenantContent } from \"@fluid-app/portal-tenant-content-api-client\";\n\n/**\n * Maps a BFF media object to the port's Media shape, providing defaults\n * for optional fields returned by the generated client.\n */\nfunction mapMedia(\n raw: NonNullable<\n Awaited<ReturnType<typeof portalTenantContent.media_show>>[\"media\"]\n >,\n): media.Media {\n return {\n id: raw.id ?? 0,\n title: raw.title ?? \"\",\n description: raw.description ?? null,\n media_type: raw.media_type ?? \"\",\n url: raw.url ?? null,\n thumbnail_url: raw.thumbnail_url ?? null,\n owner_type: raw.owner_type ?? \"\",\n created_at: raw.created_at ?? \"\",\n updated_at: raw.updated_at ?? \"\",\n };\n}\n\n/**\n * Maps the BFF meta envelope to the port's ApiMeta shape.\n */\nfunction mapMeta(\n raw: Awaited<ReturnType<typeof portalTenantContent.media_list>>[\"meta\"],\n): media.MediaListResponse[\"meta\"] {\n return {\n request_id: raw?.request_id ?? null,\n timestamp: raw?.timestamp ?? \"\",\n pagination: raw?.pagination\n ? {\n cursor: raw.pagination.cursor ?? null,\n limit: raw.pagination.limit,\n next_cursor: raw.pagination.next_cursor ?? null,\n prev_cursor: raw.pagination.prev_cursor ?? null,\n }\n : undefined,\n };\n}\n\n/**\n * Creates a ContentMediaApi adapter backed by the portal-tenant content BFF.\n *\n * Maps the generated portal-tenant-content namespace functions to the abstract\n * ContentMediaApi port, closing over the FetchClient so consumers don't need\n * to pass it per-call.\n */\nexport function createPortalTenantMediaAdapter(\n client: FetchClient,\n): ContentMediaApi {\n return {\n listMedia: async (params) => {\n const response = await portalTenantContent.media_list(client, {\n \"page[cursor]\": params?.cursor,\n \"page[limit]\": params?.limit,\n media_type: params?.media_type,\n \"filter[title]\": params?.[\"filter[title]\"],\n sort: params?.sort,\n });\n return {\n media: (response.media ?? []).map(mapMedia),\n meta: mapMeta(response.meta),\n };\n },\n\n createMedia: async (body) => {\n const response = await portalTenantContent.media_create(client, {\n media: body,\n });\n return {\n media: mapMedia(response.media ?? {}),\n meta: mapMeta(response.meta),\n };\n },\n\n getMedia: async (id) => {\n const response = await portalTenantContent.media_show(client, id);\n return {\n media: mapMedia(response.media ?? {}),\n meta: mapMeta(response.meta),\n };\n },\n\n updateMedia: async (id, body) => {\n const response = await portalTenantContent.media_update(client, id, {\n media: body,\n });\n return {\n media: mapMedia(response.media ?? {}),\n meta: mapMeta(response.meta),\n };\n },\n\n deleteMedia: async (id) => {\n const response = await portalTenantContent.media_destroy(client, id);\n return {\n media: { id: response.media?.id ?? 0 },\n meta: mapMeta(response.meta),\n };\n },\n };\n}\n\n// ---------------------------------------------------------------------------\n// ShareablesApi-compatible adapter\n// ---------------------------------------------------------------------------\n\nfunction mediaKindFromType(mediaType: string): string | null {\n if (mediaType === \"video\") return \"video\";\n if (mediaType === \"image\") return \"image\";\n if (mediaType === \"document\" || mediaType === \"pdf\") return \"pdf\";\n return null;\n}\n\nfunction toBffMediumResponse(bff: media.Media): shareables.MediumResponse {\n const kind = mediaKindFromType(bff.media_type);\n const isVideo = bff.media_type === \"video\";\n const isPdf = bff.media_type === \"pdf\" || bff.media_type === \"document\";\n\n return {\n id: bff.id,\n user_id: null,\n media_type: bff.media_type,\n media_format: bff.media_type,\n image_url:\n !isVideo && !isPdf\n ? (bff.url ?? bff.thumbnail_url ?? null)\n : (bff.thumbnail_url ?? null),\n video_url: isVideo ? (bff.url ?? null) : null,\n pdf_url: isPdf ? (bff.url ?? null) : null,\n title: bff.title,\n description: {\n id: null,\n name: null,\n body: bff.description ?? null,\n record_type: null,\n record_id: null,\n created_at: bff.created_at ?? null,\n updated_at: bff.updated_at ?? null,\n locale: null,\n },\n stripped: bff.description ?? null,\n kind,\n active: true,\n visibility: null,\n share_link: null,\n views: 0,\n leads: 0,\n watch: null,\n video_status: null,\n duration: null,\n cta_url: null,\n cta_button_text: null,\n cta_enabled: false,\n cta_action_type: null,\n video_shopping_enabled: false,\n prompts_enabled: false,\n ranks: [],\n preview_link: null,\n attached_shareables: [],\n created_at: bff.created_at,\n };\n}\n\n/**\n * Creates a ShareablesApi[\"media\"]-compatible adapter backed by the\n * portal-tenant content BFF. Includes cursor-to-page-number caching\n * for bridging the legacy UI's page-number pagination.\n */\nexport function createPortalTenantMediaShareablesAdapter(\n client: FetchClient,\n): MediaApi {\n const portAdapter = createPortalTenantMediaAdapter(client);\n const cursorByPage = new Map<number, string>();\n let lastFilterKey = \"\";\n\n return {\n getMedia: async (options) => {\n const pageNumber = options?.page ?? 1;\n\n // Clear cursor cache when search/sort/type filters change\n const filterKey = `${options?.search_query ?? \"\"}|${options?.sorted_by ?? \"\"}|${options?.media_type ?? options?.with_type ?? \"\"}`;\n if (filterKey !== lastFilterKey) {\n cursorByPage.clear();\n lastFilterKey = filterKey;\n }\n\n const cursor = pageNumber > 1 ? cursorByPage.get(pageNumber) : undefined;\n\n // Map UI sort values (\"title_asc\", \"title_desc\") to BFF sort.\n const rawSort = options?.sorted_by;\n let bffSort: \"title_asc\" | \"title_desc\" | undefined;\n if (rawSort === \"title_asc\") {\n bffSort = \"title_asc\";\n } else if (rawSort === \"title_desc\") {\n bffSort = \"title_desc\";\n }\n\n const response = await portAdapter.listMedia({\n cursor,\n limit: options?.per_page,\n media_type: options?.media_type ?? options?.with_type,\n \"filter[title]\": options?.search_query,\n sort: bffSort,\n });\n\n const nextCursor = response.meta.pagination?.next_cursor;\n if (nextCursor) {\n cursorByPage.set(pageNumber + 1, nextCursor);\n }\n\n const transformedItems = response.media.map(toBffMediumResponse);\n\n return {\n data: transformedItems,\n status: \"success\",\n media: transformedItems,\n meta: {\n total_count: transformedItems.length,\n current: pageNumber,\n per_page: options?.per_page ?? 24,\n pages: nextCursor ? pageNumber + 1 : pageNumber,\n next: nextCursor ? pageNumber + 1 : null,\n previous: pageNumber > 1 ? pageNumber - 1 : null,\n },\n };\n },\n\n getMediaById: async (id) => {\n const response = await portAdapter.getMedia(id);\n const medium = toBffMediumResponse(response.media);\n return {\n data: medium,\n status: \"success\",\n media: medium,\n };\n },\n\n createMedia: async (mediaData) => {\n const response = await portAdapter.createMedia({\n title: mediaData.title ?? \"\",\n description: mediaData.description,\n media_type: mediaData.media_type ?? \"image\",\n url:\n mediaData.image_url ??\n mediaData.video_url ??\n mediaData.pdf_url ??\n undefined,\n });\n return toBffMediumResponse(response.media);\n },\n\n updateMedia: async (id, mediaData) => {\n const response = await portAdapter.updateMedia(id, {\n title: mediaData.title,\n description: mediaData.description,\n });\n return toBffMediumResponse(response.media);\n },\n\n deleteMedia: async (id) => {\n await portAdapter.deleteMedia(id);\n return { success: true };\n },\n };\n}\n","import type {\n ContentPlaylistsApi,\n playlists,\n} from \"@fluid-app/shareables-core/playlists-api\";\nimport type { PlaylistsApi } from \"@fluid-app/shareables-core/shareables-api\";\nimport type {\n shareables,\n PlaylistsQuery,\n} from \"@fluid-app/shareables-core/types\";\nimport type { FetchClient } from \"./lib/fetch-client\";\nimport { portalTenantContent } from \"@fluid-app/portal-tenant-content-api-client\";\n\n/**\n * Maps a BFF playlist object to the port's Playlist shape.\n */\nfunction mapPlaylist(\n raw: NonNullable<\n Awaited<ReturnType<typeof portalTenantContent.playlists_show>>[\"playlist\"]\n >,\n): playlists.Playlist {\n return {\n id: raw.id ?? 0,\n title: raw.title ?? \"\",\n description: raw.description ?? null,\n items_count: raw.items_count ?? 0,\n user_id: raw.user_id,\n is_favorited: raw.is_favorited,\n image_url: raw.image_url ?? null,\n created_at: raw.created_at ?? \"\",\n updated_at: raw.updated_at ?? \"\",\n };\n}\n\n/**\n * Maps a BFF playlist item to the port's PlaylistItem shape.\n */\nfunction mapPlaylistItem(\n raw: NonNullable<\n Awaited<\n ReturnType<typeof portalTenantContent.playlists_items_list>\n >[\"playlist_items\"]\n >[number],\n): playlists.PlaylistItem {\n return {\n id: raw.id ?? 0,\n media_id: raw.media_id ?? 0,\n position: raw.position ?? null,\n title: raw.title,\n image_url: raw.image_url ?? null,\n media_type: raw.media_type,\n video_url: raw.video_url ?? null,\n duration: raw.duration ?? null,\n created_at: raw.created_at ?? \"\",\n };\n}\n\n/**\n * Maps the BFF meta envelope to the port's ApiMeta shape.\n */\nfunction mapMeta(\n raw: Awaited<ReturnType<typeof portalTenantContent.playlists_list>>[\"meta\"],\n): playlists.PlaylistsListResponse[\"meta\"] {\n return {\n request_id: raw?.request_id ?? null,\n timestamp: raw?.timestamp ?? \"\",\n pagination: raw?.pagination\n ? {\n cursor: raw.pagination.cursor ?? null,\n limit: raw.pagination.limit,\n next_cursor: raw.pagination.next_cursor ?? null,\n prev_cursor: raw.pagination.prev_cursor ?? null,\n }\n : undefined,\n };\n}\n\n/**\n * Creates a ContentPlaylistsApi adapter backed by the portal-tenant content BFF.\n *\n * Maps the generated portal-tenant-content namespace functions to the abstract\n * ContentPlaylistsApi port, closing over the FetchClient so consumers don't\n * need to pass it per-call.\n */\nexport function createPortalTenantPlaylistsAdapter(\n client: FetchClient,\n): ContentPlaylistsApi {\n return {\n listPlaylists: async (params) => {\n const response = await portalTenantContent.playlists_list(client, {\n \"page[cursor]\": params?.cursor,\n \"page[limit]\": params?.limit,\n \"filter[title]\": params?.[\"filter[title]\"],\n sort: params?.sort,\n });\n return {\n playlists: (response.playlists ?? []).map(mapPlaylist),\n meta: mapMeta(response.meta),\n };\n },\n\n createPlaylist: async (body) => {\n const response = await portalTenantContent.playlists_create(client, {\n playlist: body,\n });\n return {\n playlist: mapPlaylist(response.playlist ?? {}),\n meta: mapMeta(response.meta),\n };\n },\n\n getPlaylist: async (id) => {\n const response = await portalTenantContent.playlists_show(client, id);\n return {\n playlist: mapPlaylist(response.playlist ?? {}),\n meta: mapMeta(response.meta),\n };\n },\n\n updatePlaylist: async (id, body) => {\n const response = await portalTenantContent.playlists_update(client, id, {\n playlist: body,\n });\n return {\n playlist: mapPlaylist(response.playlist ?? {}),\n meta: mapMeta(response.meta),\n };\n },\n\n deletePlaylist: async (id) => {\n const response = await portalTenantContent.playlists_destroy(client, id);\n return {\n playlist: { id: response.playlist?.id ?? 0 },\n meta: mapMeta(response.meta),\n };\n },\n\n listPlaylistItems: async (playlistId, params) => {\n const response = await portalTenantContent.playlists_items_list(\n client,\n playlistId,\n {\n \"page[cursor]\": params?.cursor,\n \"page[limit]\": params?.limit,\n },\n );\n return {\n playlist_items: (response.playlist_items ?? []).map(mapPlaylistItem),\n meta: mapMeta(response.meta),\n };\n },\n\n addPlaylistItem: async (playlistId, body) => {\n const response = await portalTenantContent.playlists_items_add(\n client,\n playlistId,\n { item: body },\n );\n return {\n playlist_item: mapPlaylistItem(response.playlist_item ?? {}),\n meta: mapMeta(response.meta),\n };\n },\n\n removePlaylistItem: async (playlistId, itemId) => {\n const response = await portalTenantContent.playlists_items_remove(\n client,\n playlistId,\n itemId,\n );\n return {\n playlist_item: { id: response.playlist_item?.id ?? 0 },\n meta: mapMeta(response.meta),\n };\n },\n };\n}\n\n// ---------------------------------------------------------------------------\n// ShareablesApi-compatible adapter\n// ---------------------------------------------------------------------------\n\nfunction mediaKindFromType(mediaType: string): string | null {\n if (mediaType === \"video\") return \"video\";\n if (mediaType === \"image\") return \"image\";\n if (mediaType === \"document\" || mediaType === \"pdf\") return \"pdf\";\n return null;\n}\n\nfunction toBffPlaylist(\n bff: playlists.Playlist,\n items?: shareables.PlaylistItem[],\n): shareables.Playlist {\n return {\n id: bff.id,\n title: bff.title,\n description: bff.description ?? null,\n image_url: bff.image_url ?? null,\n slug: null,\n active: true,\n user_id: bff.user_id ?? null,\n is_favorited: bff.is_favorited ?? false,\n items: items ?? [],\n items_count: bff.items_count,\n };\n}\n\n/**\n * Creates a ShareablesApi[\"playlists\"]-compatible adapter backed by the\n * portal-tenant content BFF. Fetches playlist + items in parallel for\n * detail views and maps enriched flat fields onto playlist items.\n */\nexport function createPortalTenantPlaylistsShareablesAdapter(\n client: FetchClient,\n): PlaylistsApi {\n const portAdapter = createPortalTenantPlaylistsAdapter(client);\n\n return {\n getPlaylists: async (options?: PlaylistsQuery) => {\n // The UI sends Rails-style sort (\"-title\", \"title\", \"-created_at\")\n // but the BFF expects \"title_asc\", \"title_desc\", etc.\n const rawSort = options?.sort;\n let bffSort:\n | \"title_asc\"\n | \"title_desc\"\n | \"created_at_asc\"\n | \"created_at_desc\"\n | undefined;\n if (rawSort === \"title_asc\" || rawSort === \"title\") {\n bffSort = \"title_asc\";\n } else if (rawSort === \"title_desc\" || rawSort === \"-title\") {\n bffSort = \"title_desc\";\n } else if (rawSort === \"created_at_asc\" || rawSort === \"created_at\") {\n bffSort = \"created_at_asc\";\n } else if (rawSort === \"created_at_desc\" || rawSort === \"-created_at\") {\n bffSort = \"created_at_desc\";\n }\n\n const response = await portAdapter.listPlaylists({\n cursor: options?.[\"page[cursor]\"],\n limit: options?.[\"page[limit]\"],\n \"filter[title]\": options?.[\"filter[title]\"],\n sort: bffSort,\n });\n\n return {\n playlists: response.playlists.map((p) => toBffPlaylist(p)),\n meta: {\n request_id: response.meta.request_id ?? \"\",\n timestamp: response.meta.timestamp ?? \"\",\n pagination: {\n cursor: response.meta.pagination?.cursor ?? null,\n limit: response.meta.pagination?.limit ?? 12,\n prev_cursor: response.meta.pagination?.prev_cursor ?? null,\n next_cursor: response.meta.pagination?.next_cursor ?? null,\n total_count: 0,\n total_pages: 0,\n },\n },\n };\n },\n\n getPlaylistById: async (id) => {\n // Fetch playlist metadata and all items in parallel.\n // Items are cursor-paginated so we follow next_cursor until exhausted.\n const response = await portAdapter.getPlaylist(id);\n\n const allItems: playlists.PlaylistItem[] = [];\n let cursor: string | undefined;\n const MAX_PAGES = 50; // Safety limit: 50 pages × 100 items = 5,000 items max\n for (let i = 0; i < MAX_PAGES; i++) {\n const page = await portAdapter.listPlaylistItems(id, {\n cursor,\n limit: 100,\n });\n allItems.push(...page.playlist_items);\n cursor = page.meta.pagination?.next_cursor ?? undefined;\n if (!cursor) break;\n }\n\n const items: shareables.PlaylistItem[] = allItems.map((item) => ({\n id: item.id,\n order: item.position ?? undefined,\n relateable_type: \"Medium\",\n relateable: { id: item.media_id },\n // Flat fields from BFF — the UI reads these first\n title: item.title ?? \"Untitled\",\n image_url: item.image_url ?? null,\n kind: mediaKindFromType(item.media_type ?? \"\"),\n video_url: item.video_url ?? null,\n duration: item.duration ?? null,\n media_format: item.media_type ?? null,\n }));\n\n return {\n playlist: toBffPlaylist(response.playlist, items),\n meta: {\n request_id: response.meta.request_id ?? \"\",\n timestamp: response.meta.timestamp ?? \"\",\n },\n };\n },\n\n createPlaylist: async (data) => {\n const response = await portAdapter.createPlaylist({\n title: data.playlist.title,\n description: data.playlist.description,\n });\n return {\n playlist: toBffPlaylist(response.playlist),\n meta: {\n request_id: response.meta.request_id ?? \"\",\n timestamp: response.meta.timestamp ?? \"\",\n },\n };\n },\n\n updatePlaylist: async (id, data) => {\n const response = await portAdapter.updatePlaylist(id, {\n title: data.playlist.title,\n description: data.playlist.description,\n });\n return {\n playlist: toBffPlaylist(response.playlist),\n meta: {\n request_id: response.meta.request_id ?? \"\",\n timestamp: response.meta.timestamp ?? \"\",\n },\n };\n },\n\n addItemToPlaylist: async (id, data) => {\n await Promise.all(\n data.items.map((item) =>\n portAdapter.addPlaylistItem(id, {\n media_id: item.relateable_id,\n position:\n typeof item.order === \"number\" ? item.order : Number(item.order),\n }),\n ),\n );\n const updated = await portAdapter.getPlaylist(id);\n return toBffPlaylist(updated.playlist);\n },\n\n removeItemsFromPlaylist: async (playlistId, data) => {\n await Promise.all(\n data.item_ids.map((itemId) =>\n portAdapter.removePlaylistItem(playlistId, itemId),\n ),\n );\n const updated = await portAdapter.getPlaylist(playlistId);\n return toBffPlaylist(updated.playlist);\n },\n };\n}\n","import type {\n ContentDamAssetsApi,\n damAssets,\n} from \"@fluid-app/shareables-core/dam-assets-api\";\nimport type { FileResourcesApi } from \"@fluid-app/shareables-core/shareables-api\";\nimport type { shareables } from \"@fluid-app/shareables-core/types\";\nimport type { FetchClient } from \"./lib/fetch-client\";\nimport { portalTenantContent } from \"@fluid-app/portal-tenant-content-api-client\";\n\n/**\n * Maps a BFF DAM asset to the port's DamAsset shape.\n */\nfunction mapDamAsset(\n raw: NonNullable<\n Awaited<ReturnType<typeof portalTenantContent.dam_assets_list>>[\"assets\"]\n >[number],\n): damAssets.DamAsset {\n return {\n id: raw.id ?? 0,\n code: raw.code ?? \"\",\n name: raw.name ?? \"\",\n description: raw.description ?? null,\n category: raw.category ?? null,\n company: raw.company ?? null,\n default_variant_id: raw.default_variant_id ?? null,\n default_variant_url: raw.default_variant_url ?? null,\n canonical_path: raw.canonical_path ?? null,\n created_at: raw.created_at ?? \"\",\n updated_at: raw.updated_at ?? \"\",\n };\n}\n\n/**\n * Maps a BFF DAM asset path to the port's DamAssetPath shape.\n */\nfunction mapDamAssetPath(\n raw: NonNullable<\n Awaited<\n ReturnType<typeof portalTenantContent.dam_asset_paths_list>\n >[\"asset_paths\"]\n >[number],\n): damAssets.DamAssetPath {\n return {\n id: raw.id ?? 0,\n asset_code: raw.asset_code ?? \"\",\n path: raw.path ?? \"\",\n created_at: raw.created_at ?? \"\",\n };\n}\n\n/**\n * Maps the BFF meta envelope to the port's ApiMeta shape.\n */\nfunction mapMeta(\n raw: Awaited<ReturnType<typeof portalTenantContent.dam_assets_list>>[\"meta\"],\n): damAssets.DamAssetsListResponse[\"meta\"] {\n return {\n request_id: raw?.request_id ?? null,\n timestamp: raw?.timestamp ?? \"\",\n pagination: raw?.pagination\n ? {\n cursor: raw.pagination.cursor ?? null,\n limit: raw.pagination.limit,\n next_cursor: raw.pagination.next_cursor ?? null,\n prev_cursor: raw.pagination.prev_cursor ?? null,\n }\n : undefined,\n };\n}\n\n/**\n * Creates a ContentDamAssetsApi adapter backed by the portal-tenant content BFF.\n *\n * Maps the generated portal-tenant-content namespace functions to the abstract\n * ContentDamAssetsApi port, closing over the FetchClient so consumers don't\n * need to pass it per-call.\n */\nexport function createPortalTenantDamAssetsAdapter(\n client: FetchClient,\n): ContentDamAssetsApi {\n return {\n listAssets: async (params) => {\n const response = await portalTenantContent.dam_assets_list(client, {\n \"page[cursor]\": params?.cursor,\n \"page[limit]\": params?.limit,\n });\n return {\n assets: (response.assets ?? []).map(mapDamAsset),\n meta: mapMeta(response.meta),\n };\n },\n\n createAsset: async (body) => {\n const response = await portalTenantContent.dam_assets_create(client, {\n asset: body,\n });\n return {\n asset: mapDamAsset(response.asset ?? {}),\n meta: mapMeta(response.meta),\n };\n },\n\n listAssetPaths: async (assetCode, params) => {\n const response = await portalTenantContent.dam_asset_paths_list(\n client,\n assetCode,\n {\n \"page[cursor]\": params?.cursor,\n \"page[limit]\": params?.limit,\n },\n );\n return {\n asset_paths: (response.asset_paths ?? []).map(mapDamAssetPath),\n meta: mapMeta(response.meta),\n };\n },\n\n createAssetPath: async (assetCode, body) => {\n const response = await portalTenantContent.dam_asset_paths_create(\n client,\n assetCode,\n { asset_path: body },\n );\n return {\n asset_path: mapDamAssetPath(response.asset_path ?? {}),\n meta: mapMeta(response.meta),\n };\n },\n };\n}\n\n// ---------------------------------------------------------------------------\n// ShareablesApi-compatible adapter\n// ---------------------------------------------------------------------------\n\n/**\n * Creates a ShareablesApi[\"fileResources\"]-compatible adapter backed by the\n * portal-tenant content BFF. Maps DamAsset to FileResource shape and\n * includes cursor-to-page-number caching for legacy UI pagination.\n */\nexport function createPortalTenantFilesShareablesAdapter(\n client: FetchClient,\n): FileResourcesApi {\n const portAdapter = createPortalTenantDamAssetsAdapter(client);\n const cursorByPage = new Map<number, string>();\n\n return {\n getFileResources: async (params) => {\n const pageNumber = params?.pageParam ? Number(params.pageParam) : 1;\n const pageSize = params?.pageSize ? Number(params.pageSize) : 25;\n const cursor = pageNumber > 1 ? cursorByPage.get(pageNumber) : undefined;\n\n const response = await portAdapter.listAssets({\n cursor,\n limit: pageSize,\n });\n\n const nextCursor = response.meta.pagination?.next_cursor;\n if (nextCursor) {\n cursorByPage.set(pageNumber + 1, nextCursor);\n }\n\n const fileResources: shareables.FileResource[] = response.assets.map(\n (asset) => ({\n id: asset.id,\n alt_text: asset.name,\n url: asset.default_variant_url ?? \"\",\n filename: asset.name,\n content_type: \"application/octet-stream\",\n content_size: 0,\n handle: asset.code,\n dam_asset_code: asset.code,\n preview_image_url: asset.default_variant_url ?? \"\",\n created_at: asset.created_at,\n updated_at: asset.updated_at ?? \"\",\n relateable_type: null,\n relateable_id: null,\n content: null,\n }),\n );\n\n const hasNextPage = !!nextCursor;\n\n return {\n file_resources: fileResources,\n meta: {\n total_count: fileResources.length,\n per_page: pageSize,\n current_page: pageNumber,\n total_pages: hasNextPage ? pageNumber + 1 : pageNumber,\n },\n };\n },\n };\n}\n","import type {\n ContentSharesApi,\n shares,\n} from \"@fluid-app/shareables-core/shares-api\";\nimport type { ShareApi } from \"@fluid-app/shareables-core/shareables-api\";\nimport type { FetchClient } from \"./lib/fetch-client\";\nimport { portalTenantContent } from \"@fluid-app/portal-tenant-content-api-client\";\n\n/**\n * Narrows an unknown string to a valid ShareableType at runtime.\n */\nfunction isShareableType(\n value: string | undefined,\n): value is shares.ShareableType {\n return (\n value === \"media\" ||\n value === \"product\" ||\n value === \"library\" ||\n value === \"page\"\n );\n}\n\n/**\n * Maps a BFF share to the port's Share shape.\n */\nfunction mapShare(\n raw: NonNullable<\n Awaited<ReturnType<typeof portalTenantContent.shares_list>>[\"shares\"]\n >[number],\n): shares.Share {\n return {\n id: raw.id ?? 0,\n url: raw.url ?? \"\",\n shareable_type: isShareableType(raw.shareable_type)\n ? raw.shareable_type\n : \"media\",\n shareable_id: raw.shareable_id ?? 0,\n created_at: raw.created_at ?? \"\",\n };\n}\n\n/**\n * Maps the BFF meta envelope to the port's ApiMeta shape.\n */\nfunction mapMeta(\n raw: Awaited<ReturnType<typeof portalTenantContent.shares_list>>[\"meta\"],\n): shares.SharesListResponse[\"meta\"] {\n return {\n request_id: raw?.request_id ?? null,\n timestamp: raw?.timestamp ?? \"\",\n pagination: raw?.pagination\n ? {\n cursor: raw.pagination.cursor ?? null,\n limit: raw.pagination.limit,\n next_cursor: raw.pagination.next_cursor ?? null,\n prev_cursor: raw.pagination.prev_cursor ?? null,\n }\n : undefined,\n };\n}\n\n/**\n * Creates a ContentSharesApi adapter backed by the portal-tenant content BFF.\n *\n * Maps the generated portal-tenant-content namespace functions to the abstract\n * ContentSharesApi port, closing over the FetchClient so consumers don't need\n * to pass it per-call.\n */\nexport function createPortalTenantSharesAdapter(\n client: FetchClient,\n): ContentSharesApi {\n return {\n listShares: async (params) => {\n const response = await portalTenantContent.shares_list(client, {\n \"page[cursor]\": params?.cursor,\n \"page[limit]\": params?.limit,\n });\n return {\n shares: (response.shares ?? []).map(mapShare),\n meta: mapMeta(response.meta),\n };\n },\n\n createShare: async (body) => {\n const response = await portalTenantContent.shares_create(client, {\n share: body,\n });\n return {\n share: mapShare(response.share ?? {}),\n meta: mapMeta(response.meta),\n };\n },\n };\n}\n\n// ---------------------------------------------------------------------------\n// ShareablesApi-compatible adapter\n// ---------------------------------------------------------------------------\n\n/**\n * Maps legacy Rails model names (used by the UI) to BFF shareable_type values.\n */\nconst SHAREABLE_TYPE_MAP: Record<string, shares.ShareableType> = {\n Medium: \"media\",\n media: \"media\",\n Product: \"product\",\n product: \"product\",\n Library: \"library\",\n library: \"library\",\n Page: \"page\",\n page: \"page\",\n};\n\n/**\n * Creates a ShareablesApi[\"share\"]-compatible adapter backed by the\n * portal-tenant content BFF. Maps legacy model names to BFF shareable types.\n */\nexport function createPortalTenantSharesShareablesAdapter(\n client: FetchClient,\n): ShareApi {\n const portAdapter = createPortalTenantSharesAdapter(client);\n\n return {\n createShareLink: async (input) => {\n if (!input.relateableId) {\n throw new Error(\"Cannot create share link without a relateableId\");\n }\n const shareableType = SHAREABLE_TYPE_MAP[input.relateableType];\n if (!shareableType) {\n throw new Error(`Unknown shareable type: \"${input.relateableType}\"`);\n }\n const response = await portAdapter.createShare({\n shareable_type: shareableType,\n shareable_id: input.relateableId,\n });\n return response.share.url;\n },\n };\n}\n","import { z } from \"zod\";\nimport type { UrlProxyResponse } from \"@fluid-app/file-picker-core\";\n\nexport type { UrlProxyResponse };\n\nconst urlProxyResponseSchema: z.ZodType<UrlProxyResponse> = z.object({\n data: z.string(),\n contentType: z.string(),\n size: z.number(),\n});\n\n/**\n * Proxy a URL fetch through the backend to bypass CORS restrictions.\n * The backend fetches the file and returns it as base64-encoded data.\n *\n * @param url - The URL to fetch\n * @param proxyEndpoint - The proxy endpoint (defaults to \"/api/proxy-url\")\n */\nexport async function proxyUrlFetch(\n url: string,\n proxyEndpoint: string = \"/api/proxy-url\",\n): Promise<UrlProxyResponse> {\n const response = await fetch(proxyEndpoint, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n },\n body: JSON.stringify({ url }),\n });\n\n if (!response.ok) {\n const errorData = await response.json().catch(() => ({\n error: \"Failed to proxy URL fetch\",\n }));\n throw new Error(\n (errorData as { error?: string }).error || `HTTP ${response.status}`,\n );\n }\n\n const data: unknown = await response.json();\n return urlProxyResponseSchema.parse(data);\n}\n","import type { FetchClientInstance } from \"@fluid-app/api-client-core\";\nimport {\n damAssetSchema,\n damQueryResponseSchema,\n type CreateDamAssetParams,\n type CreateDamAssetPathForAssetsParams,\n type DamAssetCreateResponse,\n type DamAssetPathCreateResponse,\n type DamQueryParams,\n type DamQueryResponse,\n type FilePickerApi,\n type UnsplashSearchResponse,\n} from \"@fluid-app/file-picker-core\";\nimport {\n portalTenantContent,\n type operations,\n} from \"@fluid-app/portal-tenant-content-api-client\";\nimport { proxyUrlFetch } from \"./api/url-proxy\";\n\n/** BFF create response — derived from the generated OpenAPI types. */\ntype DamAssetCreateBffResponse = Awaited<\n ReturnType<typeof portalTenantContent.dam_assets_create>\n>;\n\n/** BFF asset path create response — derived from the generated OpenAPI types. */\ntype DamAssetPathCreateBffResponse = Awaited<\n ReturnType<typeof portalTenantContent.dam_asset_paths_create>\n>;\n\n/**\n * Maps a BFF DAM asset to the file-picker port's DamAssetCreateResponse shape.\n *\n * The BFF response includes nullable meta.request_id (from Api::Response),\n * while the port schema requires a string. We coalesce to empty string.\n */\nfunction mapCreateResponse(\n response: DamAssetCreateBffResponse,\n): DamAssetCreateResponse {\n const raw = response.asset ?? {};\n return {\n asset: damAssetSchema.parse({\n ...raw,\n canonical_path: raw.canonical_path ?? \"\",\n category: raw.category ?? \"\",\n company: raw.company ?? \"\",\n description: raw.description ?? \"\",\n default_variant_id: raw.default_variant_id ?? \"\",\n }),\n meta: {\n request_id: response.meta?.request_id ?? \"\",\n timestamp: response.meta?.timestamp ?? new Date().toISOString(),\n },\n };\n}\n\n/**\n * Maps a BFF asset path response to the file-picker port's shape.\n */\nfunction mapAssetPathCreateResponse(\n response: DamAssetPathCreateBffResponse,\n): DamAssetPathCreateResponse {\n const assetPath = response.asset_path ?? {};\n return {\n asset: {\n id: assetPath.id ?? 0,\n canonical_path: assetPath.path ?? \"\",\n name: assetPath.asset_code ?? \"\",\n },\n meta: {\n request_id: response.meta?.request_id ?? \"\",\n timestamp: response.meta?.timestamp ?? new Date().toISOString(),\n },\n };\n}\n\n/**\n * Creates a FilePickerApi adapter backed by the portal-tenant BFF's\n * `/api/content/dam/*` endpoints, using cookie-based auth via the\n * provided FetchClient.\n *\n * Unsplash search is not available through the BFF — callers that need\n * Unsplash should use the legacy adapter or provide their own\n * implementation.\n *\n * The `onProgress` callback in `createDamAsset` is not supported — the\n * underlying `fetch` API does not expose upload progress events.\n *\n * URL proxy delegates to the same-origin `/api/proxy-url` endpoint\n * (served by the hosting app, not the BFF) for CORS bypass, identical\n * to the legacy adapter behaviour.\n */\nexport function createPortalTenantFilePickerApiAdapter(\n client: FetchClientInstance,\n): FilePickerApi {\n return {\n createDamAsset: async (\n params: CreateDamAssetParams,\n ): Promise<DamAssetCreateResponse> => {\n // The generated dam_assets_create uses client.post (JSON body), but\n // file uploads require multipart/form-data. Use requestWithFormData\n // directly, typing the response from the generated operation type.\n const formData = new FormData();\n formData.append(\"asset[file]\", params.file);\n formData.append(\"asset[name]\", params.name);\n\n if (params.description) {\n formData.append(\"asset[description]\", params.description);\n }\n\n if (params.tags && params.tags.length > 0) {\n formData.append(\"asset[tags]\", params.tags.join(\",\"));\n }\n\n const response =\n await client.requestWithFormData<DamAssetCreateBffResponse>(\n \"/api/content/dam/assets\",\n formData,\n { method: \"POST\" },\n );\n\n return mapCreateResponse(response);\n },\n\n queryDamAssets: async (\n params: DamQueryParams,\n ): Promise<DamQueryResponse> => {\n const response = await portalTenantContent.dam_query(\n client,\n params satisfies operations[\"dam_query\"][\"requestBody\"][\"content\"][\"application/json\"],\n );\n return damQueryResponseSchema.parse({\n ...response,\n meta: response.meta\n ? { next_cursor: response.meta.pagination?.next_cursor ?? undefined }\n : undefined,\n });\n },\n\n deleteDamAsset: async (code: string): Promise<unknown> => {\n return portalTenantContent.dam_assets_destroy(client, code);\n },\n\n discardDamAsset: async (code: string): Promise<unknown> => {\n return portalTenantContent.dam_assets_discard(client, code);\n },\n\n createDamAssetPathForAssets: async (\n params: CreateDamAssetPathForAssetsParams,\n ): Promise<DamAssetPathCreateResponse> => {\n const response = await portalTenantContent.dam_asset_paths_create(\n client,\n params.code,\n { asset_path: { path: params.asset_paths.join(\",\") } },\n );\n return mapAssetPathCreateResponse(response);\n },\n\n searchUnsplash: async (\n _query: string,\n _page?: number,\n _perPage?: number,\n ): Promise<UnsplashSearchResponse> => {\n throw new Error(\n \"Unsplash search is not available through the portal-tenant BFF. \" +\n \"Configure an Unsplash access key and use the standard FilePickerApi adapter instead.\",\n );\n },\n\n proxyUrlFetch: (url: string) => proxyUrlFetch(url),\n };\n}\n","import { useCallback, useMemo, type ComponentProps } from \"react\";\nimport type {\n BackgroundValue,\n BorderRadiusOptions,\n ColorOptions,\n PaddingOptions,\n} from \"../types\";\nimport type { WidgetPropertySchema } from \"../registries/property-schema-types\";\nimport {\n ShareablesCoreProvider,\n ShareablesApiProvider,\n type ShareablesApi,\n} from \"@fluid-app/shareables-core\";\nimport { ShareablesUIProvider, ShareablesApp } from \"@fluid-app/shareables-ui\";\nimport {\n createPortalTenantPlaylistsAdapter,\n createPortalTenantMediaShareablesAdapter,\n createPortalTenantPlaylistsShareablesAdapter,\n createPortalTenantFilesShareablesAdapter,\n createPortalTenantSharesShareablesAdapter,\n} from \"@fluid-app/shareables-api-client\";\nimport { createPortalTenantFilePickerApiAdapter } from \"@fluid-app/file-picker-api-client\";\nimport { createFetchClient } from \"@fluid-app/api-client-core\";\nimport { useCurrentUser } from \"../hooks/use-current-user\";\nimport { useAppNavigation } from \"../shell/AppNavigationContext\";\nimport { useSdkClient } from \"../account/use-account-clients\";\nimport { usePortalTenantClient } from \"../providers/PortalTenantClientProvider\";\nimport { useFluidContext } from \"../providers/FluidProvider\";\nimport { useUserType } from \"../hooks/use-user-type\";\nimport { usePortalProductsClient } from \"../products/use-portal-products-client\";\n\ntype ShareablesScreenProps = ComponentProps<\"div\"> & {\n background?: BackgroundValue;\n textColor?: ColorOptions;\n accentColor?: ColorOptions;\n padding?: PaddingOptions;\n borderRadius?: BorderRadiusOptions;\n};\n\n/**\n * Parse the current shareables sub-route from the full slug.\n *\n * System nav slugs are \"share/products\", \"share/media\", \"share/playlists\".\n * Detail pages append an ID: \"share/products/123\", \"share/media/456\".\n *\n * \"share/products\" → screen=\"products\", detailId=null\n * \"share/products/123\" → screen=\"products\", detailId=\"123\"\n * \"share/media/456\" → screen=\"media\", detailId=\"456\"\n * \"share/playlists\" → screen=\"playlists\", detailId=null\n * \"share/playlists/789\" → screen=\"playlists\", detailId=\"789\"\n * \"share/files\" → screen=\"files\", detailId=null\n * \"share\" → screen=null (default to products)\n */\nfunction parseShareablesRoute(currentSlug: string): {\n screen: string | null;\n detailId: string | null;\n action: string | null;\n} {\n // Strip the \"share\" prefix\n const slugWithoutPrefix = currentSlug.replace(/^share\\/?/, \"\");\n if (!slugWithoutPrefix) {\n return { screen: null, detailId: null, action: null };\n }\n\n const parts = slugWithoutPrefix.split(\"/\");\n const screen = parts[0] || null;\n const detailId = parts[1] || null;\n const action = parts[2] || null;\n return { screen, detailId, action };\n}\n\nexport function ShareablesScreen({\n /* eslint-disable @typescript-eslint/no-unused-vars -- destructured to exclude from divProps spread */\n background,\n textColor,\n accentColor,\n padding,\n borderRadius,\n /* eslint-enable @typescript-eslint/no-unused-vars */\n ...divProps\n}: ShareablesScreenProps): React.JSX.Element {\n const domainClient = useSdkClient();\n const portalProductsApi = usePortalProductsClient();\n const portalTenantClient = usePortalTenantClient();\n const { config } = useFluidContext();\n const { data: userData } = useCurrentUser();\n const { currentSlug, navigate } = useAppNavigation();\n const { isCustomer } = useUserType();\n\n const fetchProducts = useCallback(\n async (search: string, cursor?: string, limit?: number) => {\n if (search) {\n return portalProductsApi.searchProducts(search, { cursor, limit });\n }\n return portalProductsApi.listProducts({ cursor, limit });\n },\n [portalProductsApi],\n );\n\n const fetchProduct = useCallback(\n async (id: string | number) => portalProductsApi.getProduct(id),\n [portalProductsApi],\n );\n\n const { screen, detailId, action } = parseShareablesRoute(currentSlug);\n\n const handleNavigate = useCallback(\n (subScreen: string, id?: string) => {\n const path = id ? `share/${subScreen}/${id}` : `share/${subScreen}`;\n navigate(path);\n },\n [navigate],\n );\n\n const handleBack = useCallback(() => {\n if (detailId && screen) {\n // Navigate back to the listing for the current screen\n navigate(`share/${screen}`);\n } else {\n // Navigate to the default shareables screen\n navigate(\"share/products\");\n }\n }, [navigate, detailId, screen]);\n\n const coreConfig = useMemo(\n () => ({\n client: domainClient,\n user: userData ? { id: userData.id } : null,\n repContext: true,\n }),\n [domainClient, userData],\n );\n\n // Create the port-level playlists adapter for the standalone deletePlaylist\n // callback (which uses the port interface directly, not ShareablesApi).\n const playlistsAdapter = useMemo(\n () => createPortalTenantPlaylistsAdapter(portalTenantClient),\n [portalTenantClient],\n );\n\n // Build ShareablesApi by composing portal-tenant BFF adapters directly.\n const shareablesApi = useMemo<ShareablesApi>(\n () => ({\n media: createPortalTenantMediaShareablesAdapter(portalTenantClient),\n playlists:\n createPortalTenantPlaylistsShareablesAdapter(portalTenantClient),\n fileResources:\n createPortalTenantFilesShareablesAdapter(portalTenantClient),\n share: createPortalTenantSharesShareablesAdapter(portalTenantClient),\n productMedia: {\n getProductMedia: async (productId: number) => {\n const response = await portalProductsApi.getProductMedia(productId);\n return {\n media: (response.media ?? []).map((item) => {\n const isVideo = item.media_type === \"video\";\n const isPdf =\n item.media_type === \"pdf\" || item.media_type === \"document\";\n return {\n id: item.id ?? 0,\n slug: null,\n title: item.title ?? \"\",\n kind: isVideo ? \"video\" : isPdf ? \"pdf\" : \"image\",\n media_type: item.media_type ?? \"image\",\n media_format: item.media_type ?? \"image\",\n image_url: !isVideo && !isPdf ? (item.url ?? null) : null,\n video_url: isVideo ? (item.url ?? null) : null,\n pdf_url: isPdf ? (item.url ?? null) : null,\n powerpoint_url: null,\n duration: 0,\n description: null,\n subtitles: {},\n comments_count: 0,\n };\n }),\n };\n },\n },\n }),\n [portalTenantClient, portalProductsApi],\n );\n\n const filePickerApi = useMemo(() => {\n const baseUrl = config.baseUrl.replace(/\\/+$/, \"\").replace(/\\/api$/, \"\");\n const csrfToken =\n typeof document !== \"undefined\"\n ? document\n .querySelector('meta[name=\"csrf-token\"]')\n ?.getAttribute(\"content\")\n : null;\n\n const bffClient = createFetchClient({\n baseUrl,\n getAuthToken: config.getAuthToken,\n onAuthError: config.onAuthError,\n credentials: \"include\",\n defaultHeaders: {\n ...config.defaultHeaders,\n ...(csrfToken ? { \"X-CSRF-Token\": csrfToken } : {}),\n },\n });\n\n return createPortalTenantFilePickerApiAdapter(bffClient);\n }, [\n config.baseUrl,\n config.getAuthToken,\n config.onAuthError,\n config.defaultHeaders,\n ]);\n\n const uiConfig = useMemo(\n () => ({\n user: userData\n ? {\n id: userData.id,\n company: userData.company\n ? { logo_url: userData.company.logo_url }\n : null,\n }\n : undefined,\n affiliateId:\n (userData as { affiliate_id?: number } | undefined)?.affiliate_id ??\n null,\n basePath: \"\",\n navigate: (path: string) => {\n // Strip leading slash — cards generate paths like \"/share/product/123\"\n const cleanPath = path.replace(/^\\//, \"\");\n // Ensure share/ prefix — screen components pass relative paths like \"media/new\"\n const prefixed = cleanPath.startsWith(\"share/\")\n ? cleanPath\n : `share/${cleanPath}`;\n navigate(prefixed);\n },\n showToast: (opts: {\n title: string;\n type: \"success\" | \"error\" | \"warning\";\n }) => {\n console.warn(`[Shareables] ${opts.type}: ${opts.title}`);\n },\n filePickerApi,\n onToggleFavorite: async (params: {\n favoriteableId: number;\n favoriteableType: string;\n }) => {\n const affiliateId = (userData as { affiliate_id?: number } | undefined)\n ?.affiliate_id;\n if (!affiliateId) throw new Error(\"No affiliate ID\");\n return domainClient.post<{ is_favorited: boolean }>(\n `/user_companies/${affiliateId}/favorites/toggle.json`,\n {\n favoriteable_id: params.favoriteableId,\n favoriteable_type: params.favoriteableType,\n },\n );\n },\n onDeletePlaylist: isCustomer\n ? undefined\n : async (playlistId: number) => {\n await playlistsAdapter.deletePlaylist(playlistId);\n },\n readOnly: isCustomer,\n }),\n [\n userData,\n navigate,\n filePickerApi,\n domainClient,\n isCustomer,\n playlistsAdapter,\n ],\n );\n\n return (\n <div {...divProps} className={`h-full ${divProps.className ?? \"\"}`}>\n <ShareablesCoreProvider config={coreConfig}>\n <ShareablesApiProvider api={shareablesApi}>\n <ShareablesUIProvider config={uiConfig}>\n <ShareablesApp\n screen={screen}\n detailId={detailId}\n action={action}\n companyLogoUrl={userData?.company?.logo_url}\n countryCode={userData?.country?.iso}\n fetchProducts={fetchProducts}\n fetchProduct={fetchProduct}\n onNavigate={handleNavigate}\n onBack={handleBack}\n />\n </ShareablesUIProvider>\n </ShareablesApiProvider>\n </ShareablesCoreProvider>\n </div>\n );\n}\n\nexport const shareablesScreenPropertySchema: WidgetPropertySchema = {\n widgetType: \"ShareablesScreen\",\n displayName: \"Shareables Screen\",\n tabsConfig: [{ id: \"styling\", label: \"Styling\" }],\n fields: [],\n} as const satisfies WidgetPropertySchema;\n"],"mappings":"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"}