@scalemule/nextjs 0.1.4 → 0.1.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.mjs CHANGED
@@ -1,7 +1,8 @@
1
+ import * as React from 'react';
1
2
  import { createContext, useState, useMemo, useEffect, useCallback, useContext, useRef } from 'react';
2
3
  import { createMoneyClient } from '@scalemule/money';
3
4
  export { MoneyClient, createMoneyClient } from '@scalemule/money';
4
- import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
5
+ import { jsx, Fragment, jsxs } from 'react/jsx-runtime';
5
6
 
6
7
  var __defProp = Object.defineProperty;
7
8
  var __getOwnPropNames = Object.getOwnPropertyNames;
@@ -9029,6 +9030,176 @@ function useFileStatus(options) {
9029
9030
  const isReady = status?.scan.status === "clean";
9030
9031
  return { status, loading, error, isReady, refresh: fetchStatus };
9031
9032
  }
9033
+ function ScaleMuleMedia(props) {
9034
+ const {
9035
+ fileId,
9036
+ mimeType,
9037
+ blobPreview,
9038
+ width,
9039
+ height,
9040
+ className,
9041
+ style,
9042
+ alt,
9043
+ pollIntervalMs = 2e3,
9044
+ renderPlaceholder,
9045
+ renderBlocked,
9046
+ renderOverride
9047
+ } = props;
9048
+ const { status, isReady } = useFileStatus({ fileId, pollIntervalMs });
9049
+ const isImage = mimeType.startsWith("image/");
9050
+ const isVideo = mimeType.startsWith("video/");
9051
+ const isAudio = mimeType.startsWith("audio/");
9052
+ const src = useMemo(() => {
9053
+ const scan = status?.scan.status;
9054
+ if (scan === "threat" || scan === "quarantined") return null;
9055
+ if (status && isReady) {
9056
+ if (isImage && status.urls.optimized) return status.urls.optimized;
9057
+ if (isVideo && status.urls.hls) return status.urls.hls;
9058
+ return status.urls.original ?? null;
9059
+ }
9060
+ if (blobPreview) return blobPreview;
9061
+ return null;
9062
+ }, [status, isReady, isImage, isVideo, blobPreview]);
9063
+ const renderState = useMemo(() => {
9064
+ const scan = status?.scan.status;
9065
+ if (scan === "threat" || scan === "quarantined") return "blocked";
9066
+ if (isReady) return "ready";
9067
+ if (blobPreview) return "preview";
9068
+ return "pending";
9069
+ }, [status?.scan.status, isReady, blobPreview]);
9070
+ const videoRef = React.useRef(null);
9071
+ useEffect(() => {
9072
+ if (!isVideo) return;
9073
+ if (!src) return;
9074
+ if (!src.endsWith(".m3u8")) return;
9075
+ const el = videoRef.current;
9076
+ if (!el) return;
9077
+ if (el.canPlayType("application/vnd.apple.mpegurl")) {
9078
+ el.src = src;
9079
+ return;
9080
+ }
9081
+ let cancelled = false;
9082
+ let hls = null;
9083
+ void (async () => {
9084
+ try {
9085
+ const moduleName = "hls.js";
9086
+ const mod = await import(
9087
+ /* @vite-ignore */
9088
+ /* webpackIgnore: true */
9089
+ moduleName
9090
+ ).catch(
9091
+ () => null
9092
+ );
9093
+ if (cancelled || !mod) return;
9094
+ const Hls = mod.default ?? mod;
9095
+ if (Hls.isSupported()) {
9096
+ hls = new Hls();
9097
+ hls.loadSource(src);
9098
+ hls.attachMedia(el);
9099
+ } else {
9100
+ el.src = src;
9101
+ }
9102
+ } catch {
9103
+ }
9104
+ })();
9105
+ return () => {
9106
+ cancelled = true;
9107
+ if (hls) hls.destroy();
9108
+ };
9109
+ }, [isVideo, src]);
9110
+ if (renderOverride) {
9111
+ return /* @__PURE__ */ jsx(Fragment, { children: renderOverride({ src, state: renderState }) });
9112
+ }
9113
+ if (renderState === "blocked") {
9114
+ return /* @__PURE__ */ jsx(Fragment, { children: renderBlocked ? renderBlocked() : /* @__PURE__ */ jsx(
9115
+ "div",
9116
+ {
9117
+ role: "alert",
9118
+ className,
9119
+ style: { padding: 12, color: "#a00", ...style },
9120
+ children: "This file was blocked."
9121
+ }
9122
+ ) });
9123
+ }
9124
+ if (renderState === "pending" && !src) {
9125
+ return /* @__PURE__ */ jsx(Fragment, { children: renderPlaceholder ? renderPlaceholder() : /* @__PURE__ */ jsx(
9126
+ "div",
9127
+ {
9128
+ role: "status",
9129
+ "aria-busy": true,
9130
+ className,
9131
+ style: { padding: 12, color: "#666", ...style },
9132
+ children: "Loading\u2026"
9133
+ }
9134
+ ) });
9135
+ }
9136
+ if (isImage) {
9137
+ const finalSrc = src && status?.urls.optimized && (width || height) ? appendQuery(src, {
9138
+ width: width ? String(width) : void 0,
9139
+ height: height ? String(height) : void 0,
9140
+ fit: "cover"
9141
+ }) : src;
9142
+ return /* @__PURE__ */ jsx(
9143
+ "img",
9144
+ {
9145
+ src: finalSrc ?? void 0,
9146
+ alt: alt ?? "",
9147
+ width,
9148
+ height,
9149
+ className,
9150
+ style
9151
+ }
9152
+ );
9153
+ }
9154
+ if (isVideo) {
9155
+ return /* @__PURE__ */ jsx(
9156
+ "video",
9157
+ {
9158
+ ref: videoRef,
9159
+ src: src && !src.endsWith(".m3u8") ? src : void 0,
9160
+ controls: true,
9161
+ playsInline: true,
9162
+ preload: "metadata",
9163
+ className,
9164
+ style,
9165
+ "aria-label": alt,
9166
+ width,
9167
+ height
9168
+ }
9169
+ );
9170
+ }
9171
+ if (isAudio) {
9172
+ return /* @__PURE__ */ jsx(
9173
+ "audio",
9174
+ {
9175
+ src: src ?? void 0,
9176
+ controls: true,
9177
+ preload: "metadata",
9178
+ className,
9179
+ style,
9180
+ "aria-label": alt
9181
+ }
9182
+ );
9183
+ }
9184
+ return /* @__PURE__ */ jsx(
9185
+ "a",
9186
+ {
9187
+ href: src ?? "#",
9188
+ target: "_blank",
9189
+ rel: "noopener noreferrer",
9190
+ className,
9191
+ style,
9192
+ children: alt ?? "Download file"
9193
+ }
9194
+ );
9195
+ }
9196
+ function appendQuery(url, params) {
9197
+ const filtered = Object.entries(params).filter(([, v]) => v != null && v !== "");
9198
+ if (filtered.length === 0) return url;
9199
+ const sep = url.includes("?") ? "&" : "?";
9200
+ const qs = new URLSearchParams(filtered).toString();
9201
+ return url + sep + qs;
9202
+ }
9032
9203
 
9033
9204
  // src/hooks/useMoney.ts
9034
9205
  var useMoney = useMoneyClient;
@@ -10815,4 +10986,4 @@ function createSafeLogger(prefix) {
10815
10986
  };
10816
10987
  }
10817
10988
 
10818
- export { FeedbackWidget, ScaleMuleApiError, ScaleMuleClient2 as ScaleMuleClient, ScaleMuleProvider, composePhone, createClient, createSafeLogger, normalizePhone, phoneCountries, sanitizeForLog, useAnalytics, useAuth, useBilling, useContent, useFeatureFlags, useFeedback, useFileStatus, useMedia, useMoney, useMoneyClient, usePushNotifications, useRealtime, useScaleMule, useScaleMuleClient, useShare, useUser, validateForm, validators };
10989
+ export { FeedbackWidget, ScaleMuleApiError, ScaleMuleClient2 as ScaleMuleClient, ScaleMuleMedia, ScaleMuleProvider, composePhone, createClient, createSafeLogger, normalizePhone, phoneCountries, sanitizeForLog, useAnalytics, useAuth, useBilling, useContent, useFeatureFlags, useFeedback, useFileStatus, useMedia, useMoney, useMoneyClient, usePushNotifications, useRealtime, useScaleMule, useScaleMuleClient, useShare, useUser, validateForm, validators };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@scalemule/nextjs",
3
- "version": "0.1.4",
3
+ "version": "0.1.5",
4
4
  "description": "ScaleMule SDK for Next.js applications - authentication, storage, and user management",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.mjs",