require("./chunk-9hOWP6kD.cjs");
let react = require("react");
let react_jsx_runtime = require("react/jsx-runtime");
//#region ../core/src/data-sources/fetchers/api.ts
/**
* Delegates a preset API source to the injected DataSourceApi adapter.
* Returns undefined for unknown presets (caller falls through to raw fetch).
*/
async function fetchViaAdapter(source, context) {
	const api = context.api;
	const vars = {
		...context.variables,
		...source.variables
	};
	switch (source.presetId) {
		case "rep-most-shared": return api.fetchMostShared(vars?.rep_id ?? "", {
			shareableType: vars?.shareable_type ?? "products",
			limit: vars?.limit ? Number(vars.limit) : void 0,
			period: vars?.period,
			languageIso: vars?.language_iso,
			signal: context.signal
		});
		case "rep-most-viewed": return api.fetchMostViewed(vars?.rep_id ?? "", {
			shareableType: vars?.shareable_type ?? "products",
			limit: vars?.limit ? Number(vars.limit) : void 0,
			period: vars?.period,
			languageIso: vars?.language_iso,
			signal: context.signal
		});
		case "customer-orders": return api.fetchOrders(vars?.customer_id ?? "", {
			limit: vars?.limit ? Number(vars.limit) : void 0,
			status: vars?.status,
			signal: context.signal
		});
		case "customer-subscriptions": return api.fetchSubscriptions(vars?.customer_id ?? "", {
			limit: vars?.limit ? Number(vars.limit) : void 0,
			status: vars?.status,
			signal: context.signal
		});
		default: return;
	}
}
/**
* Extracts a value from an object using dot notation path
* e.g., getByPath({ data: { items: [1,2,3] } }, 'data.items') => [1,2,3]
*/
function getByPath(obj, path) {
	return path.split(".").reduce((current, key) => {
		if (current && typeof current === "object" && key in current) return current[key];
	}, obj);
}
/**
* Replaces {variable} placeholders in an endpoint path with values from the
* variables map. E.g., "/reps/{rep_id}/most_shared" with { rep_id: "42" }
* becomes "/reps/42/most_shared".
*/
function interpolateVariables(endpoint, variables) {
	if (!variables) return endpoint;
	const resolved = endpoint.replace(/\{(\w+)\}/g, (match, key) => variables[key] ?? match);
	const unresolved = resolved.match(/\{(\w+)\}/g);
	if (unresolved) console.warn(`[DataSource] Unresolved variable placeholders in endpoint: ${unresolved.join(", ")}. Endpoint: "${endpoint}". Available variables: ${Object.keys(variables).join(", ") || "(none)"}`);
	return stripAllQueryParams(resolved);
}
/**
* Removes query parameters whose value is "all" from a URL.
*
* Convention: "all" is a reserved no-op value for data source preset
* config fields. Preset authors should use "all" as the default option
* for "show everything" filters. This function strips those params so
* APIs that don't recognise "all" fall back to their default (return
* everything) behaviour. Do not use "all" as a meaningful filter value
* in preset endpoints.
*/
function stripAllQueryParams(url) {
	const qIndex = url.indexOf("?");
	if (qIndex === -1) return url;
	const base = url.slice(0, qIndex);
	const kept = url.slice(qIndex + 1).split("&").filter((pair) => {
		const eqIndex = pair.indexOf("=");
		if (eqIndex === -1) return true;
		return pair.slice(eqIndex + 1) !== "all";
	}).join("&");
	return kept ? `${base}?${kept}` : base;
}
/**
* Resolves the full URL for an endpoint.
* - Substitutes {variable} placeholders from context variables
* - Absolute URLs (starting with http:// or https://) are used as-is
* - Relative paths are prefixed with the context's baseUrl
*/
function resolveEndpointUrl(endpoint, baseUrl, variables) {
	const resolved = interpolateVariables(endpoint, variables);
	if (resolved.startsWith("http://") || resolved.startsWith("https://")) return resolved;
	if (baseUrl) return `${baseUrl.endsWith("/") ? baseUrl.slice(0, -1) : baseUrl}${resolved.startsWith("/") ? resolved : `/${resolved}`}`;
	return resolved;
}
/**
* Default API fetcher implementation
*/
async function apiFetcher(source, context) {
	if (context.api && source.presetId) {
		const result = await fetchViaAdapter(source, context);
		if (result !== void 0) return result;
	}
	const { endpoint, method = "GET", headers = {}, body, resultPath } = source;
	const mergedVariables = {
		...context.variables,
		...source.variables
	};
	const url = resolveEndpointUrl(endpoint, context.baseUrl, mergedVariables);
	if (/\{\w+\}/.test(url)) return null;
	const fetchOptions = {
		method,
		headers: {
			"content-type": "application/json",
			...context.getApiHeaders?.(),
			...headers
		},
		signal: context.signal
	};
	if (body && (method === "POST" || method === "PUT")) fetchOptions.body = JSON.stringify(body);
	const response = await fetch(url, fetchOptions);
	if (!response.ok) throw new Error(`API request failed: ${response.status} ${response.statusText}`);
	const data = await response.json();
	if (resultPath) return getByPath(data, resultPath);
	return data;
}
//#endregion
//#region ../core/src/data-sources/fetchers/custom.ts
const SHAREABLE_TO_RESOURCE_TYPE = {
	Medium: "Medium",
	Page: "Page",
	Library: "Library",
	Product: "Product",
	EnrollmentPack: "EnrollmentPack"
};
async function fetchItemsViaAdapter(items, context) {
	const api = context.api;
	const settled = await Promise.allSettled(items.map(async (item) => {
		const resourceType = SHAREABLE_TO_RESOURCE_TYPE[item.shareableType];
		if (!resourceType) throw new Error(`Unknown shareable type: ${item.shareableType} for item #${item.id}`);
		return api.fetchResource(resourceType, item.id, context.signal);
	}));
	const successful = [];
	const failures = [];
	settled.forEach((result, index) => {
		const item = items[index];
		if (!item) return;
		if (result.status === "fulfilled") successful.push(result.value);
		else failures.push({
			item,
			reason: result.reason
		});
	});
	if (failures.length > 0) console.error(`[CustomFetcher] ${failures.length}/${items.length} items failed to fetch:`, failures.map((f) => ({
		shareableType: f.item.shareableType,
		id: f.item.id,
		error: f.reason instanceof Error ? f.reason.message : f.reason
	})));
	if (successful.length === 0 && items.length > 0) {
		const firstErr = failures[0]?.reason;
		throw new Error(`Failed to fetch all ${items.length} item(s)${firstErr instanceof Error ? `: ${firstErr.message}` : ""}`);
	}
	return successful;
}
/**
* Custom fetcher that fetches selected items via the injected DataSourceApi adapter.
*
* Fetches all items in parallel for performance.
* Returns an array of successfully fetched items, preserving order.
* Logs warnings for failed fetches but doesn't throw unless all items fail.
*/
async function customFetcher(source, context) {
	const { selectedItems } = source;
	if (!selectedItems || selectedItems.length === 0) return [];
	if (!context.api) throw new Error("customFetcher requires context.api — ensure a DataSourceApiProvider wraps this tree.");
	return fetchItemsViaAdapter(selectedItems, context);
}
//#endregion
//#region ../core/src/data-sources/fetchers/static.ts
/**
* Static data source fetcher.
*
* Delegates to the injected DataSourceApi adapter to fetch products filtered
* by collection, category, or tag.
*/
async function staticFetcher(source, context) {
	const { staticType, selectedId } = source;
	if (!context.api) throw new Error("staticFetcher requires context.api — ensure a DataSourceApiProvider wraps this tree.");
	if (staticType !== "tags" && Number.isNaN(Number(selectedId))) throw new Error(`staticFetcher: selectedId must be numeric, got "${selectedId}"`);
	return context.api.fetchProductsByFilter(staticType, selectedId, context.signal);
}
//#endregion
//#region ../core/src/data-sources/transformers.ts
/**
* Helper to extract image URL from various API response structures.
* Handles:
* - Flat fields (Medium, Page, Product): image_url, imageUrl, thumbnail_url, src
* - Nested images array (EnrollmentPack): images[0].image_url
* - Nested library_items (Library): library_items[0].relateable.image_url
*/
function extractImageUrl(d) {
	if (d.image_url) return d.image_url;
	if (d.imageUrl) return d.imageUrl;
	if (d.thumbnail_url) return d.thumbnail_url;
	if (d.src) return d.src;
	const images = d.images;
	if (images && images.length > 0) {
		const firstImage = images[0];
		if (firstImage?.image_url) return firstImage.image_url;
		if (firstImage?.url) return firstImage.url;
	}
	const libraryItems = d.library_items;
	if (libraryItems && libraryItems.length > 0) {
		const relateable = libraryItems[0]?.relateable;
		if (relateable?.image_url) return relateable.image_url;
		if (relateable?.imageUrl) return relateable.imageUrl;
		if (relateable?.thumbnail_url) return relateable.thumbnail_url;
	}
	return "";
}
/**
* Helper to extract description from various API response structures.
* Handles different field names across endpoints.
*/
function extractDescription(d) {
	const desc = d.description ?? d.stripped ?? d.stripped_description ?? d.body ?? "";
	if (typeof desc === "object" && desc !== null && "body" in desc) return desc.body ?? "";
	return desc;
}
/**
* Helper to extract custom widget config from a selected item.
*/
function extractCustomWidgetConfig(d, source) {
	if (source === void 0 || source.type !== "custom") return {};
	return source.selectedItems.find((s) => String(s.id) === String(d.id))?.widgetConfig ?? {};
}
/**
* ImageWidget transformer from shareable data
*/
const toImagePropsFromShareable = (data) => {
	const item = Array.isArray(data) ? data[0] : data;
	if (!item || typeof item !== "object") return {
		src: "",
		alt: "Image",
		objectFit: "cover"
	};
	const d = item;
	return {
		src: extractImageUrl(d),
		alt: d.title ?? d.name ?? d.alt ?? "Image",
		objectFit: "cover"
	};
};
/**
* VideoWidget transformer from shareable data
*/
const toVideoPropsFromShareable = (data) => {
	const item = Array.isArray(data) ? data[0] : data;
	if (!item || typeof item !== "object") return {
		src: "",
		poster: "",
		caption: ""
	};
	const d = item;
	return {
		src: (d.video_url ?? d.videoUrl ?? d.src) || "",
		poster: extractImageUrl(d),
		caption: d.title ?? d.name ?? d.caption ?? ""
	};
};
/**
* Shareable content transformer
* Normalizes shareable API responses to a consistent format
*/
const toShareableProps = (data, source) => {
	if (!Array.isArray(data)) return [];
	const sourceTypeHint = source?.type === "api" ? source.variables?.shareable_type : void 0;
	return data.map((item) => {
		const d = item;
		const widgetConfig = extractCustomWidgetConfig(d, source);
		const shareableType = d.type ?? d.relateable_type ?? d.shareable_type ?? d.shareableType ?? sourceTypeHint ?? "";
		return {
			...d,
			id: d.id,
			title: d.title ?? d.name ?? "",
			description: extractDescription(d),
			imageUrl: extractImageUrl(d),
			videoUrl: (d.video_url ?? d.videoUrl) || null,
			shareableType,
			kind: d.kind ?? "image",
			status: d.status ?? "active",
			wholesalePrice: d.display_wholesale_price ?? d.wholesale_price ?? null,
			subscriptionPrice: d.subscription_price ?? null,
			outOfStock: d.out_of_stock ?? false,
			lowInStock: d.low_in_stock ?? false,
			...widgetConfig
		};
	});
};
/**
* Carousel slides from shareables transformer
*/
const toCarouselSlidesFromShareables = (data, source) => {
	if (!Array.isArray(data)) return [];
	return data.map((item, index) => {
		const d = item;
		const widgetConfig = extractCustomWidgetConfig(d, source);
		const imageUrl = extractImageUrl(d);
		const isVideo = d.kind === "video" || d.content_format === "video" || d.videoUrl || d.video_url;
		const videoSrc = (d.videoUrl ?? d.video_url) || (isVideo ? d.url || "" : "");
		const title = d.title ?? d.name ?? "";
		const content = isVideo ? {
			type: "VideoWidget",
			props: {
				src: videoSrc,
				poster: imageUrl,
				caption: title,
				autoplay: false,
				loop: false,
				muted: true,
				controls: true
			}
		} : {
			type: "ImageWidget",
			props: {
				src: imageUrl,
				alt: title || "Slide image",
				objectFit: "cover"
			}
		};
		return {
			id: String(d.id ?? `slide-${index}`),
			content,
			title,
			description: extractDescription(d),
			...widgetConfig
		};
	});
};
/**
* Orders table transformer
* Normalizes order list data into ShareableItem rows + column definitions.
* Returns { data, columns } so multi-targetProp mapping sends each to the widget.
*/
const toOrderTableProps = (data) => {
	return {
		data: (Array.isArray(data) ? data : []).map((item) => {
			const d = item;
			const firstItem = d.first_item;
			const amount = parseFloat(String(d.amount ?? "0")) || 0;
			return {
				...d,
				id: d.id ?? d.token,
				shareableType: "Order",
				imageUrl: firstItem?.image_url || "",
				title: firstItem?.title || "",
				order_number: d.order_number ?? "",
				price: amount,
				display_price: d.total_display_amount ?? d.amount ?? "",
				status: d.status ?? "unknown",
				created_at: d.created_at
			};
		}),
		columns: [
			{
				key: "imageUrl",
				label: "Image",
				sortable: false
			},
			{
				key: "order_number",
				label: "Order #",
				sortable: true
			},
			{
				key: "price",
				label: "Total",
				sortable: true
			},
			{
				key: "status",
				label: "Status",
				sortable: true
			}
		]
	};
};
/**
* Subscriptions table transformer
* Normalizes subscription list data into ShareableItem rows + column definitions.
*/
const toSubscriptionTableProps = (data) => {
	return {
		data: (Array.isArray(data) ? data : []).map((item) => {
			const d = item;
			const product = d.variant?.product;
			return {
				...d,
				id: d.id ?? d.subscription_token,
				shareableType: "Subscription",
				imageUrl: product?.image_url || "",
				title: product?.title || "",
				price: parseFloat(String(d.price ?? "0")) || 0,
				display_price: product?.price_in_currency ?? String(d.price ?? ""),
				status: d.status ?? "unknown",
				next_bill_date: d.next_bill_date
			};
		}),
		columns: [
			{
				key: "imageUrl",
				label: "Image",
				sortable: false
			},
			{
				key: "title",
				label: "Product",
				sortable: true
			},
			{
				key: "price",
				label: "Price",
				sortable: true
			},
			{
				key: "status",
				label: "Status",
				sortable: true
			}
		]
	};
};
/**
* All widget transformers bundled for registration
*/
const WIDGET_TRANSFORMERS = {
	toShareableProps,
	toCarouselSlidesFromShareables,
	toImagePropsFromShareable,
	toVideoPropsFromShareable,
	toOrderTableProps,
	toSubscriptionTableProps
};
//#endregion
//#region ../core/src/data-sources/registry.ts
/**
* Creates a data source registry with default fetchers.
* Users can extend this with custom fetchers and transformers.
*/
function createDataSourceRegistry(options) {
	return {
		fetchers: {
			api: apiFetcher,
			custom: customFetcher,
			static: staticFetcher,
			...options?.fetchers
		},
		transformers: {
			...WIDGET_TRANSFORMERS,
			...options?.transformers
		}
	};
}
/** Default registry instance */
const DEFAULT_DATA_SOURCE_REGISTRY = createDataSourceRegistry();
//#endregion
//#region ../react/src/data-sources/context.tsx
const DataSourceContext = (0, react.createContext)({});
function DataSourceProvider({ baseUrl, getApiHeaders, children }) {
	const value = (0, react.useMemo)(() => ({
		baseUrl,
		getApiHeaders
	}), [baseUrl, getApiHeaders]);
	return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(DataSourceContext.Provider, {
		value,
		children
	});
}
//#endregion
//#region ../react/src/data-sources/registry-context.tsx
const DataSourceRegistryContext = (0, react.createContext)({ registry: DEFAULT_DATA_SOURCE_REGISTRY });
/**
* Provides data source registry and configuration to all descendants.
* If no registry is provided, uses the default.
* Also provides the shared DataSourceProvider from portal-core so that
* portal-widgets hooks can access baseUrl and getApiHeaders.
*
* The DataSourceApi adapter is provided separately via DataSourceApiProvider.
*/
function DataSourceRegistryProvider({ registry, baseUrl, getApiHeaders, variables, children }) {
	const value = (0, react.useMemo)(() => ({
		registry: registry ?? DEFAULT_DATA_SOURCE_REGISTRY,
		baseUrl,
		getApiHeaders,
		variables
	}), [
		registry,
		baseUrl,
		getApiHeaders,
		variables
	]);
	return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(DataSourceRegistryContext.Provider, {
		value,
		children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(DataSourceProvider, {
			baseUrl,
			getApiHeaders,
			children
		})
	});
}
/**
* Hook to access the full data source registry context (registry + config).
*/
function useDataSourceRegistryConfig() {
	return (0, react.useContext)(DataSourceRegistryContext);
}
//#endregion
Object.defineProperty(exports, "DataSourceRegistryProvider", {
	enumerable: true,
	get: function() {
		return DataSourceRegistryProvider;
	}
});
Object.defineProperty(exports, "useDataSourceRegistryConfig", {
	enumerable: true,
	get: function() {
		return useDataSourceRegistryConfig;
	}
});

//# sourceMappingURL=registry-context-DZOQLVi4.cjs.map