@scalemule/nextjs 0.1.39 → 0.1.41
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/README.md +30 -0
- package/dist/audio.css +206 -0
- package/dist/audio.d.mts +33 -0
- package/dist/audio.d.ts +33 -0
- package/dist/audio.js +384 -0
- package/dist/audio.mjs +364 -0
- package/dist/client.d.mts +1 -1
- package/dist/client.d.ts +1 -1
- package/dist/client.js +33 -3
- package/dist/client.mjs +33 -3
- package/dist/{index-Tq5WdDfS.d.mts → index-Bv1m_qQ8.d.mts} +20 -0
- package/dist/{index-Tq5WdDfS.d.ts → index-Bv1m_qQ8.d.ts} +20 -0
- package/dist/index.d.mts +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.js +33 -3
- package/dist/index.mjs +33 -3
- package/dist/server/auth.js +29 -2
- package/dist/server/auth.mjs +29 -2
- package/dist/server/index.d.mts +24 -4
- package/dist/server/index.d.ts +24 -4
- package/dist/server/index.js +38 -5
- package/dist/server/index.mjs +38 -5
- package/dist/server/webhook-handler.d.mts +2 -2
- package/dist/server/webhook-handler.d.ts +2 -2
- package/dist/testing.d.mts +1 -1
- package/dist/testing.d.ts +1 -1
- package/dist/testing.js +3 -0
- package/dist/testing.mjs +3 -0
- package/dist/{webhook-handler-Cfa7-yKN.d.ts → webhook-handler-5ymVecTD.d.ts} +1 -1
- package/dist/{webhook-handler-DN4WBjbm.d.mts → webhook-handler-CVmBvPFN.d.mts} +1 -1
- package/package.json +12 -5
package/dist/audio.mjs
ADDED
|
@@ -0,0 +1,364 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
"use client";
|
|
3
|
+
|
|
4
|
+
// src/components/audio-player.tsx
|
|
5
|
+
import {
|
|
6
|
+
useEffect,
|
|
7
|
+
useId,
|
|
8
|
+
useMemo,
|
|
9
|
+
useRef,
|
|
10
|
+
useState
|
|
11
|
+
} from "react";
|
|
12
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
13
|
+
var SPEEDS = [1, 1.25, 1.5, 2, 3];
|
|
14
|
+
var PLAY_EVENT = "scalemule:audio:play";
|
|
15
|
+
var positive = (n) => n != null && Number.isFinite(n) && n > 0 ? n : 0;
|
|
16
|
+
var durationOf = (audio) => positive(audio.duration_ms) / 1e3;
|
|
17
|
+
function time(seconds) {
|
|
18
|
+
const s = Math.floor(positive(seconds));
|
|
19
|
+
return `${Math.floor(s / 60)}:${String(s % 60).padStart(2, "0")}`;
|
|
20
|
+
}
|
|
21
|
+
function AudioPlayer(props) {
|
|
22
|
+
if (!props.audio.url) return null;
|
|
23
|
+
return /* @__PURE__ */ jsx(PlayerSession, { ...props }, props.audioKey ?? props.audio.url);
|
|
24
|
+
}
|
|
25
|
+
function PlayerSession({
|
|
26
|
+
audio,
|
|
27
|
+
variant = "waveform",
|
|
28
|
+
label,
|
|
29
|
+
className,
|
|
30
|
+
style,
|
|
31
|
+
preload = "none",
|
|
32
|
+
onRefresh,
|
|
33
|
+
showRefreshButton = false,
|
|
34
|
+
onPlaybackError,
|
|
35
|
+
playbackRateStorageKey = "scalemule:audio:playback-rate",
|
|
36
|
+
exclusivePlayback = true
|
|
37
|
+
}) {
|
|
38
|
+
const media = useRef(null);
|
|
39
|
+
const alive = useRef(true);
|
|
40
|
+
const request = useRef(null);
|
|
41
|
+
const triedRefresh = useRef(false);
|
|
42
|
+
const intent = useRef(false);
|
|
43
|
+
const resume = useRef(null);
|
|
44
|
+
const [source, setSource] = useState(audio);
|
|
45
|
+
const [duration, setDuration] = useState(durationOf(audio));
|
|
46
|
+
const [position, setPosition] = useState(0);
|
|
47
|
+
const [speed, setSpeed] = useState(1);
|
|
48
|
+
const [playing, setPlaying] = useState(false);
|
|
49
|
+
const [pendingPlay, setPendingPlay] = useState(false);
|
|
50
|
+
const [busy, setBusy] = useState(false);
|
|
51
|
+
const [error, setError] = useState(null);
|
|
52
|
+
const sliderId = useId();
|
|
53
|
+
const labelId = useId();
|
|
54
|
+
const rate = useRef(1);
|
|
55
|
+
useEffect(() => {
|
|
56
|
+
alive.current = true;
|
|
57
|
+
const element = media.current;
|
|
58
|
+
return () => {
|
|
59
|
+
alive.current = false;
|
|
60
|
+
intent.current = false;
|
|
61
|
+
request.current?.abort();
|
|
62
|
+
element?.pause();
|
|
63
|
+
};
|
|
64
|
+
}, []);
|
|
65
|
+
useEffect(() => {
|
|
66
|
+
request.current?.abort();
|
|
67
|
+
request.current = null;
|
|
68
|
+
setBusy(false);
|
|
69
|
+
resume.current = media.current?.currentTime ?? 0;
|
|
70
|
+
setSource(audio);
|
|
71
|
+
setDuration(durationOf(audio));
|
|
72
|
+
}, [audio.url, audio.expires_at, audio.duration_ms]);
|
|
73
|
+
useEffect(() => {
|
|
74
|
+
if (!playbackRateStorageKey) return;
|
|
75
|
+
try {
|
|
76
|
+
const saved = Number(window.localStorage.getItem(playbackRateStorageKey));
|
|
77
|
+
if (SPEEDS.includes(saved)) {
|
|
78
|
+
setSpeed(saved);
|
|
79
|
+
rate.current = saved;
|
|
80
|
+
}
|
|
81
|
+
} catch {
|
|
82
|
+
}
|
|
83
|
+
}, [playbackRateStorageKey]);
|
|
84
|
+
useEffect(() => {
|
|
85
|
+
if (media.current) {
|
|
86
|
+
media.current.playbackRate = speed;
|
|
87
|
+
media.current.preservesPitch = true;
|
|
88
|
+
}
|
|
89
|
+
}, [speed, source.url]);
|
|
90
|
+
useEffect(() => {
|
|
91
|
+
if (!exclusivePlayback) return;
|
|
92
|
+
const stop = (event) => {
|
|
93
|
+
if (event.detail !== media.current) {
|
|
94
|
+
intent.current = false;
|
|
95
|
+
setPendingPlay(false);
|
|
96
|
+
media.current?.pause();
|
|
97
|
+
}
|
|
98
|
+
};
|
|
99
|
+
document.addEventListener(PLAY_EVENT, stop);
|
|
100
|
+
return () => document.removeEventListener(PLAY_EVENT, stop);
|
|
101
|
+
}, [exclusivePlayback]);
|
|
102
|
+
function fail() {
|
|
103
|
+
if (!alive.current) return;
|
|
104
|
+
intent.current = false;
|
|
105
|
+
setPendingPlay(false);
|
|
106
|
+
setPlaying(false);
|
|
107
|
+
setBusy(false);
|
|
108
|
+
setError("Audio could not be played. Try again.");
|
|
109
|
+
onPlaybackError?.();
|
|
110
|
+
}
|
|
111
|
+
async function play() {
|
|
112
|
+
const element = media.current;
|
|
113
|
+
if (!element || !intent.current) return;
|
|
114
|
+
try {
|
|
115
|
+
await element.play();
|
|
116
|
+
} catch (e) {
|
|
117
|
+
if (!alive.current || !intent.current) return;
|
|
118
|
+
if (e.name === "NotAllowedError") {
|
|
119
|
+
fail();
|
|
120
|
+
return;
|
|
121
|
+
}
|
|
122
|
+
await recover();
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
async function recover(manual = false) {
|
|
126
|
+
if (!alive.current || request.current) return;
|
|
127
|
+
if (!onRefresh || !manual && triedRefresh.current) {
|
|
128
|
+
fail();
|
|
129
|
+
return;
|
|
130
|
+
}
|
|
131
|
+
triedRefresh.current = true;
|
|
132
|
+
const controller = new AbortController();
|
|
133
|
+
request.current = controller;
|
|
134
|
+
resume.current = media.current?.currentTime ?? position;
|
|
135
|
+
setBusy(true);
|
|
136
|
+
setError(null);
|
|
137
|
+
try {
|
|
138
|
+
const fresh = await onRefresh(controller.signal);
|
|
139
|
+
if (!alive.current || controller.signal.aborted) return;
|
|
140
|
+
if (!fresh.url) throw new Error("No audio URL");
|
|
141
|
+
setDuration(durationOf(fresh) || duration);
|
|
142
|
+
if (fresh.url === source.url) {
|
|
143
|
+
media.current?.load();
|
|
144
|
+
} else {
|
|
145
|
+
setSource(fresh);
|
|
146
|
+
}
|
|
147
|
+
} catch {
|
|
148
|
+
if (!controller.signal.aborted) fail();
|
|
149
|
+
} finally {
|
|
150
|
+
if (request.current === controller) {
|
|
151
|
+
request.current = null;
|
|
152
|
+
if (alive.current) setBusy(false);
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
function toggle() {
|
|
157
|
+
if (intent.current || playing) {
|
|
158
|
+
intent.current = false;
|
|
159
|
+
setPendingPlay(false);
|
|
160
|
+
media.current?.pause();
|
|
161
|
+
return;
|
|
162
|
+
}
|
|
163
|
+
intent.current = true;
|
|
164
|
+
setPendingPlay(true);
|
|
165
|
+
triedRefresh.current = false;
|
|
166
|
+
setError(null);
|
|
167
|
+
if (exclusivePlayback)
|
|
168
|
+
document.dispatchEvent(
|
|
169
|
+
new CustomEvent(PLAY_EVENT, { detail: media.current })
|
|
170
|
+
);
|
|
171
|
+
if (request.current) return;
|
|
172
|
+
const expiry = Date.parse(source.expires_at ?? "");
|
|
173
|
+
if (onRefresh && Number.isFinite(expiry) && expiry - Date.now() <= 6e4) {
|
|
174
|
+
void recover();
|
|
175
|
+
} else {
|
|
176
|
+
void play();
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
function seek(value) {
|
|
180
|
+
if (!media.current || !duration || !Number.isFinite(value)) return;
|
|
181
|
+
const target = Math.max(0, Math.min(duration, value));
|
|
182
|
+
try {
|
|
183
|
+
media.current.currentTime = target;
|
|
184
|
+
resume.current = target;
|
|
185
|
+
setPosition(target);
|
|
186
|
+
} catch {
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
function changeSpeed() {
|
|
190
|
+
const next = SPEEDS[(SPEEDS.indexOf(rate.current) + 1) % SPEEDS.length];
|
|
191
|
+
rate.current = next;
|
|
192
|
+
setSpeed(next);
|
|
193
|
+
if (playbackRateStorageKey) {
|
|
194
|
+
try {
|
|
195
|
+
window.localStorage.setItem(playbackRateStorageKey, String(next));
|
|
196
|
+
} catch {
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
const peaks = useMemo(() => {
|
|
201
|
+
const input = source.waveform_peaks;
|
|
202
|
+
if (!input?.length) return [];
|
|
203
|
+
const count = Math.min(64, input.length);
|
|
204
|
+
return Array.from({ length: count }, (_, i) => {
|
|
205
|
+
const value = input[Math.floor(i * input.length / count)];
|
|
206
|
+
return Number.isFinite(value) ? Math.min(1, Math.max(0.08, Math.abs(value))) : 0.08;
|
|
207
|
+
});
|
|
208
|
+
}, [source.waveform_peaks]);
|
|
209
|
+
const current = Math.min(positive(position), duration || Infinity);
|
|
210
|
+
const progress = duration ? Math.max(0, Math.min(100, current / duration * 100)) : 0;
|
|
211
|
+
const remaining = duration ? `${time((duration - current) / speed)} remaining` : "Duration available when played";
|
|
212
|
+
const title = label ?? (source.ai_generated === false ? "Listen to narration" : "Listen to AI narration");
|
|
213
|
+
return /* @__PURE__ */ jsxs(
|
|
214
|
+
"div",
|
|
215
|
+
{
|
|
216
|
+
className: `sm-audio sm-audio--${variant}${className ? ` ${className}` : ""}`,
|
|
217
|
+
style,
|
|
218
|
+
role: "group",
|
|
219
|
+
"aria-labelledby": labelId,
|
|
220
|
+
"data-audio-variant": variant,
|
|
221
|
+
children: [
|
|
222
|
+
/* @__PURE__ */ jsx(
|
|
223
|
+
"audio",
|
|
224
|
+
{
|
|
225
|
+
ref: media,
|
|
226
|
+
src: source.url ?? void 0,
|
|
227
|
+
preload,
|
|
228
|
+
onLoadedMetadata: (e) => {
|
|
229
|
+
const element = e.currentTarget;
|
|
230
|
+
const total = positive(element.duration) || durationOf(source);
|
|
231
|
+
setDuration(total);
|
|
232
|
+
element.playbackRate = rate.current;
|
|
233
|
+
element.preservesPitch = true;
|
|
234
|
+
if (resume.current != null) {
|
|
235
|
+
try {
|
|
236
|
+
element.currentTime = Math.min(
|
|
237
|
+
resume.current,
|
|
238
|
+
total || resume.current
|
|
239
|
+
);
|
|
240
|
+
} catch {
|
|
241
|
+
}
|
|
242
|
+
resume.current = null;
|
|
243
|
+
}
|
|
244
|
+
setPosition(positive(element.currentTime));
|
|
245
|
+
if (intent.current) void play();
|
|
246
|
+
},
|
|
247
|
+
onDurationChange: (e) => {
|
|
248
|
+
const d = positive(e.currentTarget.duration);
|
|
249
|
+
if (d) setDuration(d);
|
|
250
|
+
},
|
|
251
|
+
onTimeUpdate: (e) => setPosition(positive(e.currentTarget.currentTime)),
|
|
252
|
+
onPlay: () => {
|
|
253
|
+
intent.current = true;
|
|
254
|
+
setPlaying(true);
|
|
255
|
+
setError(null);
|
|
256
|
+
if (exclusivePlayback)
|
|
257
|
+
document.dispatchEvent(
|
|
258
|
+
new CustomEvent(PLAY_EVENT, { detail: media.current })
|
|
259
|
+
);
|
|
260
|
+
},
|
|
261
|
+
onPause: () => setPlaying(false),
|
|
262
|
+
onEnded: () => {
|
|
263
|
+
intent.current = false;
|
|
264
|
+
setPendingPlay(false);
|
|
265
|
+
setPlaying(false);
|
|
266
|
+
setPosition(duration);
|
|
267
|
+
},
|
|
268
|
+
onError: () => {
|
|
269
|
+
if (intent.current) void recover();
|
|
270
|
+
else if (!request.current) {
|
|
271
|
+
setError("Audio is unavailable. Press play to retry.");
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
),
|
|
276
|
+
/* @__PURE__ */ jsx(
|
|
277
|
+
"button",
|
|
278
|
+
{
|
|
279
|
+
type: "button",
|
|
280
|
+
className: "sm-audio__play",
|
|
281
|
+
onClick: toggle,
|
|
282
|
+
"aria-label": playing || busy && pendingPlay ? "Pause narration" : "Play narration",
|
|
283
|
+
children: playing || busy && pendingPlay ? /* @__PURE__ */ jsx("svg", { viewBox: "0 0 24 24", "aria-hidden": "true", children: /* @__PURE__ */ jsx("path", { d: "M6 4h4v16H6zm8 0h4v16h-4z" }) }) : /* @__PURE__ */ jsx("svg", { viewBox: "0 0 24 24", "aria-hidden": "true", children: /* @__PURE__ */ jsx("path", { d: "M8 4v16l12-8z" }) })
|
|
284
|
+
}
|
|
285
|
+
),
|
|
286
|
+
/* @__PURE__ */ jsxs("div", { className: "sm-audio__content", children: [
|
|
287
|
+
/* @__PURE__ */ jsx("span", { className: "sm-audio__label", id: labelId, children: title }),
|
|
288
|
+
/* @__PURE__ */ jsxs("div", { className: "sm-audio__times", children: [
|
|
289
|
+
/* @__PURE__ */ jsxs("span", { children: [
|
|
290
|
+
time(current),
|
|
291
|
+
" ",
|
|
292
|
+
/* @__PURE__ */ jsx("span", { className: "sm-audio__elapsed", children: "elapsed" })
|
|
293
|
+
] }),
|
|
294
|
+
/* @__PURE__ */ jsx("span", { children: remaining })
|
|
295
|
+
] }),
|
|
296
|
+
variant !== "inline" && /* @__PURE__ */ jsxs(
|
|
297
|
+
"div",
|
|
298
|
+
{
|
|
299
|
+
className: "sm-audio__seek",
|
|
300
|
+
style: { "--sm-audio-progress": `${progress}%` },
|
|
301
|
+
children: [
|
|
302
|
+
variant === "waveform" && peaks.length > 0 && /* @__PURE__ */ jsx("div", { className: "sm-audio__waveform", "aria-hidden": "true", children: peaks.map((peak, i) => /* @__PURE__ */ jsx(
|
|
303
|
+
"span",
|
|
304
|
+
{
|
|
305
|
+
style: {
|
|
306
|
+
height: `${peak * 100}%`,
|
|
307
|
+
background: (i + 0.5) / peaks.length * 100 <= progress ? "var(--sm-audio-accent)" : "var(--sm-audio-wave)"
|
|
308
|
+
}
|
|
309
|
+
},
|
|
310
|
+
i
|
|
311
|
+
)) }),
|
|
312
|
+
/* @__PURE__ */ jsx("label", { className: "sm-audio__sr", htmlFor: sliderId, children: "Seek narration" }),
|
|
313
|
+
/* @__PURE__ */ jsx(
|
|
314
|
+
"input",
|
|
315
|
+
{
|
|
316
|
+
id: sliderId,
|
|
317
|
+
className: "sm-audio__range",
|
|
318
|
+
type: "range",
|
|
319
|
+
min: "0",
|
|
320
|
+
max: duration || 1,
|
|
321
|
+
step: "0.1",
|
|
322
|
+
disabled: !duration,
|
|
323
|
+
value: Math.min(current, duration || 1),
|
|
324
|
+
"aria-valuetext": `${time(current)} elapsed; ${remaining}`,
|
|
325
|
+
onChange: (e) => seek(Number(e.currentTarget.value))
|
|
326
|
+
}
|
|
327
|
+
)
|
|
328
|
+
]
|
|
329
|
+
}
|
|
330
|
+
)
|
|
331
|
+
] }),
|
|
332
|
+
/* @__PURE__ */ jsxs("div", { className: "sm-audio__actions", children: [
|
|
333
|
+
/* @__PURE__ */ jsxs(
|
|
334
|
+
"button",
|
|
335
|
+
{
|
|
336
|
+
type: "button",
|
|
337
|
+
className: "sm-audio__speed",
|
|
338
|
+
onClick: changeSpeed,
|
|
339
|
+
"aria-label": `Change playback speed (current ${speed}\xD7)`,
|
|
340
|
+
children: [
|
|
341
|
+
speed,
|
|
342
|
+
"\xD7"
|
|
343
|
+
]
|
|
344
|
+
}
|
|
345
|
+
),
|
|
346
|
+
onRefresh && showRefreshButton && /* @__PURE__ */ jsx(
|
|
347
|
+
"button",
|
|
348
|
+
{
|
|
349
|
+
type: "button",
|
|
350
|
+
className: "sm-audio__refresh",
|
|
351
|
+
disabled: busy,
|
|
352
|
+
onClick: () => void recover(true),
|
|
353
|
+
children: "Refresh"
|
|
354
|
+
}
|
|
355
|
+
)
|
|
356
|
+
] }),
|
|
357
|
+
(busy || error) && /* @__PURE__ */ jsx("p", { className: "sm-audio__status", role: "status", children: busy ? "Refreshing audio\u2026" : error })
|
|
358
|
+
]
|
|
359
|
+
}
|
|
360
|
+
);
|
|
361
|
+
}
|
|
362
|
+
export {
|
|
363
|
+
AudioPlayer
|
|
364
|
+
};
|
package/dist/client.d.mts
CHANGED
package/dist/client.d.ts
CHANGED
package/dist/client.js
CHANGED
|
@@ -12,9 +12,31 @@ var ScaleMuleApiError = class extends Error {
|
|
|
12
12
|
this.code = error.code;
|
|
13
13
|
this.field = error.field;
|
|
14
14
|
this.status = status;
|
|
15
|
+
this.requestId = error.requestId;
|
|
16
|
+
this.traceId = error.traceId;
|
|
17
|
+
this.problem = error.problem;
|
|
15
18
|
}
|
|
16
19
|
};
|
|
17
20
|
|
|
21
|
+
// src/error-context.ts
|
|
22
|
+
function withErrorContext(error, responseData, headers) {
|
|
23
|
+
const enriched = { ...error };
|
|
24
|
+
const meta = responseData?.meta;
|
|
25
|
+
const requestId = meta?.request_id ?? headers?.get("x-request-id") ?? void 0;
|
|
26
|
+
if (requestId !== void 0 && enriched.requestId === void 0) {
|
|
27
|
+
enriched.requestId = requestId;
|
|
28
|
+
}
|
|
29
|
+
const traceId = meta?.trace_id;
|
|
30
|
+
if (traceId !== void 0 && enriched.traceId === void 0) {
|
|
31
|
+
enriched.traceId = traceId;
|
|
32
|
+
}
|
|
33
|
+
const rawError = responseData?.error;
|
|
34
|
+
if (rawError !== null && typeof rawError === "object" && enriched.problem === void 0) {
|
|
35
|
+
enriched.problem = rawError;
|
|
36
|
+
}
|
|
37
|
+
return enriched;
|
|
38
|
+
}
|
|
39
|
+
|
|
18
40
|
// src/client.ts
|
|
19
41
|
var GATEWAY_URLS = {
|
|
20
42
|
dev: "https://api-dev.scalemule.com",
|
|
@@ -591,7 +613,8 @@ var ScaleMuleClient = class {
|
|
|
591
613
|
}
|
|
592
614
|
if (!response.ok) {
|
|
593
615
|
const rawError = responseData?.error;
|
|
594
|
-
const
|
|
616
|
+
const baseError = rawError && typeof rawError === "object" ? rawError : { code: `HTTP_${response.status}`, message: typeof rawError === "string" ? rawError : responseData?.message || text || response.statusText };
|
|
617
|
+
const error = withErrorContext(baseError, responseData, response.headers);
|
|
595
618
|
if (response.status === 401 && this.sessionToken && !options.isAutoRefresh && refreshAttempts < MAX_REFRESH_ATTEMPTS) {
|
|
596
619
|
refreshAttempts++;
|
|
597
620
|
if (this.debug) console.log("[ScaleMule] 401 received, attempting auto-refresh...");
|
|
@@ -853,10 +876,17 @@ var ScaleMuleClient = class {
|
|
|
853
876
|
const unwrapped = data?.data !== void 0 ? data.data : data;
|
|
854
877
|
resolve(unwrapped);
|
|
855
878
|
} else {
|
|
856
|
-
|
|
879
|
+
const uploadError = data?.error || {
|
|
857
880
|
code: `HTTP_${xhr.status}`,
|
|
858
881
|
message: data?.message || xhr.responseText || "Upload failed"
|
|
859
|
-
}
|
|
882
|
+
};
|
|
883
|
+
reject(
|
|
884
|
+
new ScaleMuleApiError(
|
|
885
|
+
withErrorContext(uploadError, data, {
|
|
886
|
+
get: (name) => xhr.getResponseHeader(name)
|
|
887
|
+
})
|
|
888
|
+
)
|
|
889
|
+
);
|
|
860
890
|
}
|
|
861
891
|
} catch {
|
|
862
892
|
reject(new ScaleMuleApiError({ code: "PARSE_ERROR", message: "Failed to parse response" }));
|
package/dist/client.mjs
CHANGED
|
@@ -10,9 +10,31 @@ var ScaleMuleApiError = class extends Error {
|
|
|
10
10
|
this.code = error.code;
|
|
11
11
|
this.field = error.field;
|
|
12
12
|
this.status = status;
|
|
13
|
+
this.requestId = error.requestId;
|
|
14
|
+
this.traceId = error.traceId;
|
|
15
|
+
this.problem = error.problem;
|
|
13
16
|
}
|
|
14
17
|
};
|
|
15
18
|
|
|
19
|
+
// src/error-context.ts
|
|
20
|
+
function withErrorContext(error, responseData, headers) {
|
|
21
|
+
const enriched = { ...error };
|
|
22
|
+
const meta = responseData?.meta;
|
|
23
|
+
const requestId = meta?.request_id ?? headers?.get("x-request-id") ?? void 0;
|
|
24
|
+
if (requestId !== void 0 && enriched.requestId === void 0) {
|
|
25
|
+
enriched.requestId = requestId;
|
|
26
|
+
}
|
|
27
|
+
const traceId = meta?.trace_id;
|
|
28
|
+
if (traceId !== void 0 && enriched.traceId === void 0) {
|
|
29
|
+
enriched.traceId = traceId;
|
|
30
|
+
}
|
|
31
|
+
const rawError = responseData?.error;
|
|
32
|
+
if (rawError !== null && typeof rawError === "object" && enriched.problem === void 0) {
|
|
33
|
+
enriched.problem = rawError;
|
|
34
|
+
}
|
|
35
|
+
return enriched;
|
|
36
|
+
}
|
|
37
|
+
|
|
16
38
|
// src/client.ts
|
|
17
39
|
var GATEWAY_URLS = {
|
|
18
40
|
dev: "https://api-dev.scalemule.com",
|
|
@@ -589,7 +611,8 @@ var ScaleMuleClient = class {
|
|
|
589
611
|
}
|
|
590
612
|
if (!response.ok) {
|
|
591
613
|
const rawError = responseData?.error;
|
|
592
|
-
const
|
|
614
|
+
const baseError = rawError && typeof rawError === "object" ? rawError : { code: `HTTP_${response.status}`, message: typeof rawError === "string" ? rawError : responseData?.message || text || response.statusText };
|
|
615
|
+
const error = withErrorContext(baseError, responseData, response.headers);
|
|
593
616
|
if (response.status === 401 && this.sessionToken && !options.isAutoRefresh && refreshAttempts < MAX_REFRESH_ATTEMPTS) {
|
|
594
617
|
refreshAttempts++;
|
|
595
618
|
if (this.debug) console.log("[ScaleMule] 401 received, attempting auto-refresh...");
|
|
@@ -851,10 +874,17 @@ var ScaleMuleClient = class {
|
|
|
851
874
|
const unwrapped = data?.data !== void 0 ? data.data : data;
|
|
852
875
|
resolve(unwrapped);
|
|
853
876
|
} else {
|
|
854
|
-
|
|
877
|
+
const uploadError = data?.error || {
|
|
855
878
|
code: `HTTP_${xhr.status}`,
|
|
856
879
|
message: data?.message || xhr.responseText || "Upload failed"
|
|
857
|
-
}
|
|
880
|
+
};
|
|
881
|
+
reject(
|
|
882
|
+
new ScaleMuleApiError(
|
|
883
|
+
withErrorContext(uploadError, data, {
|
|
884
|
+
get: (name) => xhr.getResponseHeader(name)
|
|
885
|
+
})
|
|
886
|
+
)
|
|
887
|
+
);
|
|
858
888
|
}
|
|
859
889
|
} catch {
|
|
860
890
|
reject(new ScaleMuleApiError({ code: "PARSE_ERROR", message: "Failed to parse response" }));
|
|
@@ -121,6 +121,20 @@ declare class ScaleMuleApiError extends Error {
|
|
|
121
121
|
status?: number;
|
|
122
122
|
field?: string;
|
|
123
123
|
details?: unknown;
|
|
124
|
+
/**
|
|
125
|
+
* Correlation id for the failed request. Comes from the response envelope
|
|
126
|
+
* (`meta.request_id`), falling back to the `x-request-id` response header
|
|
127
|
+
* echoed by the gateway. Surface this in support flows.
|
|
128
|
+
*/
|
|
129
|
+
requestId?: string;
|
|
130
|
+
/** Distributed-trace id, when the backend supplies `meta.trace_id`. */
|
|
131
|
+
traceId?: string;
|
|
132
|
+
/**
|
|
133
|
+
* The raw `error` object from the response body, unmodified. Escape hatch
|
|
134
|
+
* for consumers (such as `@scalemule/signals` `fromError`) that need fields
|
|
135
|
+
* this class does not model yet.
|
|
136
|
+
*/
|
|
137
|
+
problem?: unknown;
|
|
124
138
|
constructor(error: ApiError, status?: number);
|
|
125
139
|
}
|
|
126
140
|
interface ApiResponse<T> {
|
|
@@ -132,6 +146,12 @@ interface ApiError {
|
|
|
132
146
|
code: string;
|
|
133
147
|
message: string;
|
|
134
148
|
field?: string;
|
|
149
|
+
/** Correlation id: envelope `meta.request_id`, else the `x-request-id` header. */
|
|
150
|
+
requestId?: string;
|
|
151
|
+
/** Distributed-trace id from envelope `meta.trace_id`. */
|
|
152
|
+
traceId?: string;
|
|
153
|
+
/** The raw body `error` object, passed through untouched. */
|
|
154
|
+
problem?: unknown;
|
|
135
155
|
}
|
|
136
156
|
interface User {
|
|
137
157
|
id: string;
|
|
@@ -121,6 +121,20 @@ declare class ScaleMuleApiError extends Error {
|
|
|
121
121
|
status?: number;
|
|
122
122
|
field?: string;
|
|
123
123
|
details?: unknown;
|
|
124
|
+
/**
|
|
125
|
+
* Correlation id for the failed request. Comes from the response envelope
|
|
126
|
+
* (`meta.request_id`), falling back to the `x-request-id` response header
|
|
127
|
+
* echoed by the gateway. Surface this in support flows.
|
|
128
|
+
*/
|
|
129
|
+
requestId?: string;
|
|
130
|
+
/** Distributed-trace id, when the backend supplies `meta.trace_id`. */
|
|
131
|
+
traceId?: string;
|
|
132
|
+
/**
|
|
133
|
+
* The raw `error` object from the response body, unmodified. Escape hatch
|
|
134
|
+
* for consumers (such as `@scalemule/signals` `fromError`) that need fields
|
|
135
|
+
* this class does not model yet.
|
|
136
|
+
*/
|
|
137
|
+
problem?: unknown;
|
|
124
138
|
constructor(error: ApiError, status?: number);
|
|
125
139
|
}
|
|
126
140
|
interface ApiResponse<T> {
|
|
@@ -132,6 +146,12 @@ interface ApiError {
|
|
|
132
146
|
code: string;
|
|
133
147
|
message: string;
|
|
134
148
|
field?: string;
|
|
149
|
+
/** Correlation id: envelope `meta.request_id`, else the `x-request-id` header. */
|
|
150
|
+
requestId?: string;
|
|
151
|
+
/** Distributed-trace id from envelope `meta.trace_id`. */
|
|
152
|
+
traceId?: string;
|
|
153
|
+
/** The raw body `error` object, passed through untouched. */
|
|
154
|
+
problem?: unknown;
|
|
135
155
|
}
|
|
136
156
|
interface User {
|
|
137
157
|
id: string;
|
package/dist/index.d.mts
CHANGED
|
@@ -6,8 +6,8 @@ import { MoneyClient } from '@scalemule/money';
|
|
|
6
6
|
export { MoneyClient, MoneyClientConfig, createMoneyClient } from '@scalemule/money';
|
|
7
7
|
import { ScaleMuleClient } from './client.mjs';
|
|
8
8
|
export { ClientConfig, RequestOptions, createClient } from './client.mjs';
|
|
9
|
-
import { S as ScaleMuleConfig, U as User, L as LoginResponse, A as ApiError$1, a as UseAuthReturn, b as UseBillingReturn, c as ListFilesParams, d as UseContentReturn, e as UseUserReturn, f as UseAnalyticsOptions, g as UseAnalyticsReturn } from './index-
|
|
10
|
-
export { h as AccountBalance, i as AnalyticsEvent, j as ApiResponse, B as BatchTrackRequest, k as BillingPayment, l as BillingPayout, m as BillingRefund, n as BillingTransaction, C as ChangeEmailRequest, o as ChangePasswordRequest, p as ClientContext, q as ConnectedAccount, D as DeviceFingerprint, r as DeviceInfo, E as EnhancedAnalyticsEvent, F as ForgotPasswordRequest, K as KnownAccountInfo, s as LinkedAccount, t as ListFilesResponse, u as LoginDeviceInfo, v as LoginRequest, w as LoginResponseWithMFA, x as LoginRiskInfo, M as MFAChallengeResponse, y as MFAMethod, z as MFASMSSetupResponse, G as MFASetupRequest, H as MFAStatus, I as MFATOTPSetupResponse, J as MFAVerifyRequest, O as OAuthCallbackRequest, N as OAuthCallbackResponse, P as OAuthConfig, Q as OAuthProvider, R as OAuthStartResponse, T as PageViewData, V as PayoutSchedule, W as PhoneLoginRequest, X as PhoneSendCodeRequest, Y as PhoneVerifyRequest, Z as Profile, _ as RefreshResponse, $ as RegisterRequest, a0 as ResetPasswordRequest, a1 as ScaleMuleApiError, a2 as ScaleMuleEnvironment, a3 as Session, a4 as SignedUploadCompleteRequest, a5 as SignedUploadRequest, a6 as SignedUploadResponse, a7 as SignedUploadUrl, a8 as StorageAdapter, a9 as StorageFile, aa as TrackEventResponse, ab as TransactionSummary, ac as UTMParams, ad as UpdateProfileRequest, ae as UploadOptions, af as UploadResponse, ag as VerifyEmailRequest } from './index-
|
|
9
|
+
import { S as ScaleMuleConfig, U as User, L as LoginResponse, A as ApiError$1, a as UseAuthReturn, b as UseBillingReturn, c as ListFilesParams, d as UseContentReturn, e as UseUserReturn, f as UseAnalyticsOptions, g as UseAnalyticsReturn } from './index-Bv1m_qQ8.mjs';
|
|
10
|
+
export { h as AccountBalance, i as AnalyticsEvent, j as ApiResponse, B as BatchTrackRequest, k as BillingPayment, l as BillingPayout, m as BillingRefund, n as BillingTransaction, C as ChangeEmailRequest, o as ChangePasswordRequest, p as ClientContext, q as ConnectedAccount, D as DeviceFingerprint, r as DeviceInfo, E as EnhancedAnalyticsEvent, F as ForgotPasswordRequest, K as KnownAccountInfo, s as LinkedAccount, t as ListFilesResponse, u as LoginDeviceInfo, v as LoginRequest, w as LoginResponseWithMFA, x as LoginRiskInfo, M as MFAChallengeResponse, y as MFAMethod, z as MFASMSSetupResponse, G as MFASetupRequest, H as MFAStatus, I as MFATOTPSetupResponse, J as MFAVerifyRequest, O as OAuthCallbackRequest, N as OAuthCallbackResponse, P as OAuthConfig, Q as OAuthProvider, R as OAuthStartResponse, T as PageViewData, V as PayoutSchedule, W as PhoneLoginRequest, X as PhoneSendCodeRequest, Y as PhoneVerifyRequest, Z as Profile, _ as RefreshResponse, $ as RegisterRequest, a0 as ResetPasswordRequest, a1 as ScaleMuleApiError, a2 as ScaleMuleEnvironment, a3 as Session, a4 as SignedUploadCompleteRequest, a5 as SignedUploadRequest, a6 as SignedUploadResponse, a7 as SignedUploadUrl, a8 as StorageAdapter, a9 as StorageFile, aa as TrackEventResponse, ab as TransactionSummary, ac as UTMParams, ad as UpdateProfileRequest, ae as UploadOptions, af as UploadResponse, ag as VerifyEmailRequest } from './index-Bv1m_qQ8.mjs';
|
|
11
11
|
|
|
12
12
|
/**
|
|
13
13
|
* Tri-state file visibility — `'private' | 'app_public' | 'anonymous_visible'`.
|
package/dist/index.d.ts
CHANGED
|
@@ -6,8 +6,8 @@ import { MoneyClient } from '@scalemule/money';
|
|
|
6
6
|
export { MoneyClient, MoneyClientConfig, createMoneyClient } from '@scalemule/money';
|
|
7
7
|
import { ScaleMuleClient } from './client.js';
|
|
8
8
|
export { ClientConfig, RequestOptions, createClient } from './client.js';
|
|
9
|
-
import { S as ScaleMuleConfig, U as User, L as LoginResponse, A as ApiError$1, a as UseAuthReturn, b as UseBillingReturn, c as ListFilesParams, d as UseContentReturn, e as UseUserReturn, f as UseAnalyticsOptions, g as UseAnalyticsReturn } from './index-
|
|
10
|
-
export { h as AccountBalance, i as AnalyticsEvent, j as ApiResponse, B as BatchTrackRequest, k as BillingPayment, l as BillingPayout, m as BillingRefund, n as BillingTransaction, C as ChangeEmailRequest, o as ChangePasswordRequest, p as ClientContext, q as ConnectedAccount, D as DeviceFingerprint, r as DeviceInfo, E as EnhancedAnalyticsEvent, F as ForgotPasswordRequest, K as KnownAccountInfo, s as LinkedAccount, t as ListFilesResponse, u as LoginDeviceInfo, v as LoginRequest, w as LoginResponseWithMFA, x as LoginRiskInfo, M as MFAChallengeResponse, y as MFAMethod, z as MFASMSSetupResponse, G as MFASetupRequest, H as MFAStatus, I as MFATOTPSetupResponse, J as MFAVerifyRequest, O as OAuthCallbackRequest, N as OAuthCallbackResponse, P as OAuthConfig, Q as OAuthProvider, R as OAuthStartResponse, T as PageViewData, V as PayoutSchedule, W as PhoneLoginRequest, X as PhoneSendCodeRequest, Y as PhoneVerifyRequest, Z as Profile, _ as RefreshResponse, $ as RegisterRequest, a0 as ResetPasswordRequest, a1 as ScaleMuleApiError, a2 as ScaleMuleEnvironment, a3 as Session, a4 as SignedUploadCompleteRequest, a5 as SignedUploadRequest, a6 as SignedUploadResponse, a7 as SignedUploadUrl, a8 as StorageAdapter, a9 as StorageFile, aa as TrackEventResponse, ab as TransactionSummary, ac as UTMParams, ad as UpdateProfileRequest, ae as UploadOptions, af as UploadResponse, ag as VerifyEmailRequest } from './index-
|
|
9
|
+
import { S as ScaleMuleConfig, U as User, L as LoginResponse, A as ApiError$1, a as UseAuthReturn, b as UseBillingReturn, c as ListFilesParams, d as UseContentReturn, e as UseUserReturn, f as UseAnalyticsOptions, g as UseAnalyticsReturn } from './index-Bv1m_qQ8.js';
|
|
10
|
+
export { h as AccountBalance, i as AnalyticsEvent, j as ApiResponse, B as BatchTrackRequest, k as BillingPayment, l as BillingPayout, m as BillingRefund, n as BillingTransaction, C as ChangeEmailRequest, o as ChangePasswordRequest, p as ClientContext, q as ConnectedAccount, D as DeviceFingerprint, r as DeviceInfo, E as EnhancedAnalyticsEvent, F as ForgotPasswordRequest, K as KnownAccountInfo, s as LinkedAccount, t as ListFilesResponse, u as LoginDeviceInfo, v as LoginRequest, w as LoginResponseWithMFA, x as LoginRiskInfo, M as MFAChallengeResponse, y as MFAMethod, z as MFASMSSetupResponse, G as MFASetupRequest, H as MFAStatus, I as MFATOTPSetupResponse, J as MFAVerifyRequest, O as OAuthCallbackRequest, N as OAuthCallbackResponse, P as OAuthConfig, Q as OAuthProvider, R as OAuthStartResponse, T as PageViewData, V as PayoutSchedule, W as PhoneLoginRequest, X as PhoneSendCodeRequest, Y as PhoneVerifyRequest, Z as Profile, _ as RefreshResponse, $ as RegisterRequest, a0 as ResetPasswordRequest, a1 as ScaleMuleApiError, a2 as ScaleMuleEnvironment, a3 as Session, a4 as SignedUploadCompleteRequest, a5 as SignedUploadRequest, a6 as SignedUploadResponse, a7 as SignedUploadUrl, a8 as StorageAdapter, a9 as StorageFile, aa as TrackEventResponse, ab as TransactionSummary, ac as UTMParams, ad as UpdateProfileRequest, ae as UploadOptions, af as UploadResponse, ag as VerifyEmailRequest } from './index-Bv1m_qQ8.js';
|
|
11
11
|
|
|
12
12
|
/**
|
|
13
13
|
* Tri-state file visibility — `'private' | 'app_public' | 'anonymous_visible'`.
|
package/dist/index.js
CHANGED
|
@@ -35,9 +35,31 @@ var ScaleMuleApiError = class extends Error {
|
|
|
35
35
|
this.code = error.code;
|
|
36
36
|
this.field = error.field;
|
|
37
37
|
this.status = status;
|
|
38
|
+
this.requestId = error.requestId;
|
|
39
|
+
this.traceId = error.traceId;
|
|
40
|
+
this.problem = error.problem;
|
|
38
41
|
}
|
|
39
42
|
};
|
|
40
43
|
|
|
44
|
+
// src/error-context.ts
|
|
45
|
+
function withErrorContext(error, responseData, headers) {
|
|
46
|
+
const enriched = { ...error };
|
|
47
|
+
const meta = responseData?.meta;
|
|
48
|
+
const requestId = meta?.request_id ?? headers?.get("x-request-id") ?? void 0;
|
|
49
|
+
if (requestId !== void 0 && enriched.requestId === void 0) {
|
|
50
|
+
enriched.requestId = requestId;
|
|
51
|
+
}
|
|
52
|
+
const traceId = meta?.trace_id;
|
|
53
|
+
if (traceId !== void 0 && enriched.traceId === void 0) {
|
|
54
|
+
enriched.traceId = traceId;
|
|
55
|
+
}
|
|
56
|
+
const rawError = responseData?.error;
|
|
57
|
+
if (rawError !== null && typeof rawError === "object" && enriched.problem === void 0) {
|
|
58
|
+
enriched.problem = rawError;
|
|
59
|
+
}
|
|
60
|
+
return enriched;
|
|
61
|
+
}
|
|
62
|
+
|
|
41
63
|
// src/client.ts
|
|
42
64
|
var GATEWAY_URLS = {
|
|
43
65
|
dev: "https://api-dev.scalemule.com",
|
|
@@ -614,7 +636,8 @@ var ScaleMuleClient = class {
|
|
|
614
636
|
}
|
|
615
637
|
if (!response.ok) {
|
|
616
638
|
const rawError = responseData?.error;
|
|
617
|
-
const
|
|
639
|
+
const baseError = rawError && typeof rawError === "object" ? rawError : { code: `HTTP_${response.status}`, message: typeof rawError === "string" ? rawError : responseData?.message || text || response.statusText };
|
|
640
|
+
const error = withErrorContext(baseError, responseData, response.headers);
|
|
618
641
|
if (response.status === 401 && this.sessionToken && !options.isAutoRefresh && refreshAttempts < MAX_REFRESH_ATTEMPTS) {
|
|
619
642
|
refreshAttempts++;
|
|
620
643
|
if (this.debug) console.log("[ScaleMule] 401 received, attempting auto-refresh...");
|
|
@@ -876,10 +899,17 @@ var ScaleMuleClient = class {
|
|
|
876
899
|
const unwrapped = data?.data !== void 0 ? data.data : data;
|
|
877
900
|
resolve(unwrapped);
|
|
878
901
|
} else {
|
|
879
|
-
|
|
902
|
+
const uploadError = data?.error || {
|
|
880
903
|
code: `HTTP_${xhr.status}`,
|
|
881
904
|
message: data?.message || xhr.responseText || "Upload failed"
|
|
882
|
-
}
|
|
905
|
+
};
|
|
906
|
+
reject(
|
|
907
|
+
new ScaleMuleApiError(
|
|
908
|
+
withErrorContext(uploadError, data, {
|
|
909
|
+
get: (name) => xhr.getResponseHeader(name)
|
|
910
|
+
})
|
|
911
|
+
)
|
|
912
|
+
);
|
|
883
913
|
}
|
|
884
914
|
} catch {
|
|
885
915
|
reject(new ScaleMuleApiError({ code: "PARSE_ERROR", message: "Failed to parse response" }));
|