glitchgrab 1.48.1 → 1.50.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +57 -1
- package/dist/server.d.mts +128 -3
- package/dist/server.d.ts +128 -3
- package/dist/server.js +133 -15
- package/dist/server.js.map +1 -1
- package/dist/server.mjs +130 -15
- package/dist/server.mjs.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -886,7 +886,63 @@ interface GuideReadOptions {
|
|
|
886
886
|
}
|
|
887
887
|
```
|
|
888
888
|
|
|
889
|
-
Never pass the `
|
|
889
|
+
Never pass the `ggc_` server key here for a public page — it reads drafts too, and client calls.
|
|
890
|
+
|
|
891
|
+
## How do I show recorded client calls in my admin panel?
|
|
892
|
+
|
|
893
|
+
On a call page in Glitchgrab, press **share with client**, then issue a **calls
|
|
894
|
+
key** (`ggc_…`) on API Tokens → Server keys. Put it in your server env as `GLITCHGRAB_CALLS_KEY` — never
|
|
895
|
+
a `NEXT_PUBLIC_` variable, because it plays and deletes calls. Your admin panel
|
|
896
|
+
then reads only the calls you shared:
|
|
897
|
+
|
|
898
|
+
```tsx
|
|
899
|
+
// app/admin/calls/[id]/page.tsx — server component, behind your admin auth
|
|
900
|
+
import { notFound } from "next/navigation";
|
|
901
|
+
import { getCall } from "glitchgrab/server";
|
|
902
|
+
|
|
903
|
+
export default async function CallPage({ params }: { params: Promise<{ id: string }> }) {
|
|
904
|
+
const { id } = await params;
|
|
905
|
+
const call = await getCall(id);
|
|
906
|
+
if (!call) notFound();
|
|
907
|
+
|
|
908
|
+
return (
|
|
909
|
+
<article>
|
|
910
|
+
<h1>{call.title}</h1>
|
|
911
|
+
{call.audio.map((a) => (
|
|
912
|
+
<audio key={a.track} controls src={a.url} />
|
|
913
|
+
))}
|
|
914
|
+
<p>{call.summary?.overview}</p>
|
|
915
|
+
<pre>{call.transcript}</pre>
|
|
916
|
+
</article>
|
|
917
|
+
);
|
|
918
|
+
}
|
|
919
|
+
```
|
|
920
|
+
|
|
921
|
+
- **Shared calls only.** Everything else on the project — internal calls,
|
|
922
|
+
prospects — is a 404 to this key.
|
|
923
|
+
- **Links expire after 15 minutes** (`linksExpireInSeconds`). Read the call when
|
|
924
|
+
the page renders; never store `url`s. Nothing is cached for the same reason.
|
|
925
|
+
- **Audio tracks:** a bot recording has one (`call`). An extension recording has
|
|
926
|
+
two — `client` (everyone else on the call) and `recorder` (your side).
|
|
927
|
+
- **Match a call to a customer** by `participants[].email`.
|
|
928
|
+
- **Screenshots come a page at a time** — `getCall(id, { framesOffset: 50, framesLimit: 50 })`;
|
|
929
|
+
`frames.total` says how many exist.
|
|
930
|
+
- **`deleteCall(id)` is permanent** — recording, screenshots and transcript. It
|
|
931
|
+
resolves `{ deleted: false, error }` rather than throwing, e.g. while the bot
|
|
932
|
+
is still in that call.
|
|
933
|
+
- **Never throws.** `listCalls()` → `[]`, `getCall()` → `null` on any failure.
|
|
934
|
+
|
|
935
|
+
```ts
|
|
936
|
+
listCalls(options?: { limit?: number; before?: string } & CallsOptions): Promise<CallListItem[]>
|
|
937
|
+
getCall(id: string, options?: { framesOffset?: number; framesLimit?: number } & CallsOptions): Promise<Call | null>
|
|
938
|
+
deleteCall(id: string, options?: CallsOptions): Promise<{ deleted: true } | { deleted: false; error: string }>
|
|
939
|
+
|
|
940
|
+
interface CallsOptions {
|
|
941
|
+
key?: string; // default: GLITCHGRAB_CALLS_KEY
|
|
942
|
+
baseUrl?: string; // default: GLITCHGRAB_BASE_URL, then https://glitchgrab.dev
|
|
943
|
+
timeoutMs?: number; // default 5000
|
|
944
|
+
}
|
|
945
|
+
```
|
|
890
946
|
|
|
891
947
|
## What configuration options are available?
|
|
892
948
|
|
package/dist/server.d.mts
CHANGED
|
@@ -136,7 +136,7 @@ interface GuideSummary {
|
|
|
136
136
|
category: string | null;
|
|
137
137
|
/** The order set in Glitchgrab. The list already arrives sorted by it. */
|
|
138
138
|
orderIndex: number;
|
|
139
|
-
/** Always `"PUBLISHED"` with a `gg_` token; only
|
|
139
|
+
/** Always `"PUBLISHED"` with a `gg_` token; only the project's `ggc_` server key sees drafts. */
|
|
140
140
|
status: "DRAFT" | "PUBLISHED";
|
|
141
141
|
/** When someone last re-checked the steps against the product. */
|
|
142
142
|
lastVerifiedAt: string | null;
|
|
@@ -170,7 +170,7 @@ interface GuideReadOptions {
|
|
|
170
170
|
* then `GLITCHGRAB_TOKEN`, then `NEXT_PUBLIC_GLITCHGRAB_TOKEN` — the token is
|
|
171
171
|
* public, so the one your provider already uses is fine.
|
|
172
172
|
*
|
|
173
|
-
* Never pass the `
|
|
173
|
+
* Never pass the `ggc_` server key for a public page: it reads drafts too, and client calls.
|
|
174
174
|
*/
|
|
175
175
|
token?: string;
|
|
176
176
|
/** Override the API host. Falls back to `GLITCHGRAB_BASE_URL`, then `https://glitchgrab.dev`. */
|
|
@@ -209,6 +209,131 @@ declare function listGuides(options?: GuideReadOptions): Promise<GuideSummary[]>
|
|
|
209
209
|
* `notFound()` on it. Never throws.
|
|
210
210
|
*/
|
|
211
211
|
declare function getGuide(slug: string, options?: GuideReadOptions): Promise<Guide | null>;
|
|
212
|
+
/** Someone on the call, as the recorder saw them. */
|
|
213
|
+
interface CallParticipant {
|
|
214
|
+
name: string | null;
|
|
215
|
+
email: string | null;
|
|
216
|
+
}
|
|
217
|
+
/** One shared call as the list returns it — no transcript, no links. */
|
|
218
|
+
interface CallListItem {
|
|
219
|
+
id: string;
|
|
220
|
+
title: string | null;
|
|
221
|
+
startsAt: string | null;
|
|
222
|
+
endsAt: string | null;
|
|
223
|
+
durationSec: number | null;
|
|
224
|
+
/** `"bot"` (joined the Meet) or `"extension"` (recorded from someone's Chrome). */
|
|
225
|
+
recorder: string | null;
|
|
226
|
+
/** Match a call to your customer by these emails. */
|
|
227
|
+
participants: CallParticipant[];
|
|
228
|
+
hasRecording: boolean;
|
|
229
|
+
/** `IDLE`, `RUNNING`, `DONE` or `FAILED`. */
|
|
230
|
+
transcriptStatus: string;
|
|
231
|
+
/** One line on what the call was about, once Glitchgrab has read it. */
|
|
232
|
+
overview: string | null;
|
|
233
|
+
createdAt: string;
|
|
234
|
+
}
|
|
235
|
+
interface CallAudio {
|
|
236
|
+
/**
|
|
237
|
+
* `call` — a bot recording's single mixed track. An extension recording has
|
|
238
|
+
* two instead: `client` (everyone else on the call) and `recorder` (the
|
|
239
|
+
* person who recorded it).
|
|
240
|
+
*/
|
|
241
|
+
track: "call" | "client" | "recorder";
|
|
242
|
+
/** Plays for {@link Call.linksExpireInSeconds}. */
|
|
243
|
+
url: string;
|
|
244
|
+
}
|
|
245
|
+
interface CallMoment {
|
|
246
|
+
/** Milliseconds into the call — seek the audio here. */
|
|
247
|
+
tMs: number | null;
|
|
248
|
+
speaker: string | null;
|
|
249
|
+
text: string;
|
|
250
|
+
}
|
|
251
|
+
/** One shared call in full. */
|
|
252
|
+
interface Call extends CallListItem {
|
|
253
|
+
meetUrl: string | null;
|
|
254
|
+
audio: CallAudio[];
|
|
255
|
+
/** Audio and screenshot links stop working after this many seconds. Read the call per render; never store them. */
|
|
256
|
+
linksExpireInSeconds: number;
|
|
257
|
+
transcript: string | null;
|
|
258
|
+
summary: {
|
|
259
|
+
overview: string;
|
|
260
|
+
moments: CallMoment[];
|
|
261
|
+
} | null;
|
|
262
|
+
/** Screenshots grabbed during the call, a page at a time. `tMs` is on the transcript's clock. */
|
|
263
|
+
frames: {
|
|
264
|
+
total: number;
|
|
265
|
+
offset: number;
|
|
266
|
+
limit: number;
|
|
267
|
+
items: {
|
|
268
|
+
tMs: number;
|
|
269
|
+
url: string;
|
|
270
|
+
}[];
|
|
271
|
+
};
|
|
272
|
+
}
|
|
273
|
+
/** Options for {@link listCalls}, {@link getCall} and {@link deleteCall}. */
|
|
274
|
+
interface CallsOptions {
|
|
275
|
+
/**
|
|
276
|
+
* The project's server key (`ggc_…`, API Tokens → Server keys). Falls back to `GLITCHGRAB_CALLS_KEY`.
|
|
277
|
+
* Server only — never a `NEXT_PUBLIC_` variable: it plays and deletes calls.
|
|
278
|
+
*/
|
|
279
|
+
key?: string;
|
|
280
|
+
/** Override the API host. Falls back to `GLITCHGRAB_BASE_URL`, then `https://glitchgrab.dev`. */
|
|
281
|
+
baseUrl?: string;
|
|
282
|
+
/** Give up after this many ms. Default 5000. */
|
|
283
|
+
timeoutMs?: number;
|
|
284
|
+
}
|
|
285
|
+
interface ListCallsOptions extends CallsOptions {
|
|
286
|
+
/** Default 20, max 100. */
|
|
287
|
+
limit?: number;
|
|
288
|
+
/** ISO date: only calls recorded before it. Pass the last call's `createdAt` for the next page. */
|
|
289
|
+
before?: string;
|
|
290
|
+
}
|
|
291
|
+
interface GetCallOptions extends CallsOptions {
|
|
292
|
+
/** First screenshot to return. Default 0. */
|
|
293
|
+
framesOffset?: number;
|
|
294
|
+
/** Screenshots per page. Default 50, max 200, `0` for none. */
|
|
295
|
+
framesLimit?: number;
|
|
296
|
+
}
|
|
297
|
+
type DeleteCallResult = {
|
|
298
|
+
deleted: true;
|
|
299
|
+
} | {
|
|
300
|
+
deleted: false;
|
|
301
|
+
error: string;
|
|
302
|
+
};
|
|
303
|
+
/**
|
|
304
|
+
* Calls shared with the client on the key's project, newest first. No links —
|
|
305
|
+
* open one with {@link getCall}.
|
|
306
|
+
*
|
|
307
|
+
* ```ts
|
|
308
|
+
* // app/admin/calls/page.tsx — server component
|
|
309
|
+
* import { listCalls } from "glitchgrab/server";
|
|
310
|
+
*
|
|
311
|
+
* export default async function CallsPage() {
|
|
312
|
+
* const calls = await listCalls(); // GLITCHGRAB_CALLS_KEY=ggc_…
|
|
313
|
+
* return calls.map((c) => <a key={c.id} href={`/admin/calls/${c.id}`}>{c.title}</a>);
|
|
314
|
+
* }
|
|
315
|
+
* ```
|
|
316
|
+
*
|
|
317
|
+
* Returns `[]` on any failure. Never throws.
|
|
318
|
+
*/
|
|
319
|
+
declare function listCalls(options?: ListCallsOptions): Promise<CallListItem[]>;
|
|
320
|
+
/**
|
|
321
|
+
* One shared call: audio links, transcript, summary and a page of screenshots.
|
|
322
|
+
* Links expire after `linksExpireInSeconds` — read the call when the page
|
|
323
|
+
* renders and never store them.
|
|
324
|
+
*
|
|
325
|
+
* Returns `null` when the call does not exist, is not shared, and on any
|
|
326
|
+
* failure — call `notFound()` on it. Never throws.
|
|
327
|
+
*/
|
|
328
|
+
declare function getCall(id: string, options?: GetCallOptions): Promise<Call | null>;
|
|
329
|
+
/**
|
|
330
|
+
* Permanently delete one shared call — the recording, the screenshots, the
|
|
331
|
+
* transcript. Cannot be undone.
|
|
332
|
+
*
|
|
333
|
+
* Resolves `{ deleted: false, error }` instead of throwing: the reason is worth
|
|
334
|
+
* showing ("The bot is still in this call — delete it after the call ends").
|
|
335
|
+
*/
|
|
336
|
+
declare function deleteCall(id: string, options?: CallsOptions): Promise<DeleteCallResult>;
|
|
212
337
|
/**
|
|
213
338
|
* `JSON.stringify` that is safe inside `<script type="application/ld+json">`.
|
|
214
339
|
*
|
|
@@ -229,4 +354,4 @@ declare function getGuide(slug: string, options?: GuideReadOptions): Promise<Gui
|
|
|
229
354
|
*/
|
|
230
355
|
declare function escapeJsonForScript(value: unknown): string;
|
|
231
356
|
|
|
232
|
-
export { type CaptureServerErrorsOptions, type Guide, type GuideReadOptions, type GuideSummary, ReportResult, ReportSeverity, ReportType, type ServerReportOptions, type ServerReporterConfig, type ServerReporterIdentity, type SignSessionOptions, captureServerErrors, configureServerReporter, escapeJsonForScript, getGuide, listGuides, reportServerError, resetServerReporter, signSession };
|
|
357
|
+
export { type Call, type CallAudio, type CallListItem, type CallMoment, type CallParticipant, type CallsOptions, type CaptureServerErrorsOptions, type DeleteCallResult, type GetCallOptions, type Guide, type GuideReadOptions, type GuideSummary, type ListCallsOptions, ReportResult, ReportSeverity, ReportType, type ServerReportOptions, type ServerReporterConfig, type ServerReporterIdentity, type SignSessionOptions, captureServerErrors, configureServerReporter, deleteCall, escapeJsonForScript, getCall, getGuide, listCalls, listGuides, reportServerError, resetServerReporter, signSession };
|
package/dist/server.d.ts
CHANGED
|
@@ -136,7 +136,7 @@ interface GuideSummary {
|
|
|
136
136
|
category: string | null;
|
|
137
137
|
/** The order set in Glitchgrab. The list already arrives sorted by it. */
|
|
138
138
|
orderIndex: number;
|
|
139
|
-
/** Always `"PUBLISHED"` with a `gg_` token; only
|
|
139
|
+
/** Always `"PUBLISHED"` with a `gg_` token; only the project's `ggc_` server key sees drafts. */
|
|
140
140
|
status: "DRAFT" | "PUBLISHED";
|
|
141
141
|
/** When someone last re-checked the steps against the product. */
|
|
142
142
|
lastVerifiedAt: string | null;
|
|
@@ -170,7 +170,7 @@ interface GuideReadOptions {
|
|
|
170
170
|
* then `GLITCHGRAB_TOKEN`, then `NEXT_PUBLIC_GLITCHGRAB_TOKEN` — the token is
|
|
171
171
|
* public, so the one your provider already uses is fine.
|
|
172
172
|
*
|
|
173
|
-
* Never pass the `
|
|
173
|
+
* Never pass the `ggc_` server key for a public page: it reads drafts too, and client calls.
|
|
174
174
|
*/
|
|
175
175
|
token?: string;
|
|
176
176
|
/** Override the API host. Falls back to `GLITCHGRAB_BASE_URL`, then `https://glitchgrab.dev`. */
|
|
@@ -209,6 +209,131 @@ declare function listGuides(options?: GuideReadOptions): Promise<GuideSummary[]>
|
|
|
209
209
|
* `notFound()` on it. Never throws.
|
|
210
210
|
*/
|
|
211
211
|
declare function getGuide(slug: string, options?: GuideReadOptions): Promise<Guide | null>;
|
|
212
|
+
/** Someone on the call, as the recorder saw them. */
|
|
213
|
+
interface CallParticipant {
|
|
214
|
+
name: string | null;
|
|
215
|
+
email: string | null;
|
|
216
|
+
}
|
|
217
|
+
/** One shared call as the list returns it — no transcript, no links. */
|
|
218
|
+
interface CallListItem {
|
|
219
|
+
id: string;
|
|
220
|
+
title: string | null;
|
|
221
|
+
startsAt: string | null;
|
|
222
|
+
endsAt: string | null;
|
|
223
|
+
durationSec: number | null;
|
|
224
|
+
/** `"bot"` (joined the Meet) or `"extension"` (recorded from someone's Chrome). */
|
|
225
|
+
recorder: string | null;
|
|
226
|
+
/** Match a call to your customer by these emails. */
|
|
227
|
+
participants: CallParticipant[];
|
|
228
|
+
hasRecording: boolean;
|
|
229
|
+
/** `IDLE`, `RUNNING`, `DONE` or `FAILED`. */
|
|
230
|
+
transcriptStatus: string;
|
|
231
|
+
/** One line on what the call was about, once Glitchgrab has read it. */
|
|
232
|
+
overview: string | null;
|
|
233
|
+
createdAt: string;
|
|
234
|
+
}
|
|
235
|
+
interface CallAudio {
|
|
236
|
+
/**
|
|
237
|
+
* `call` — a bot recording's single mixed track. An extension recording has
|
|
238
|
+
* two instead: `client` (everyone else on the call) and `recorder` (the
|
|
239
|
+
* person who recorded it).
|
|
240
|
+
*/
|
|
241
|
+
track: "call" | "client" | "recorder";
|
|
242
|
+
/** Plays for {@link Call.linksExpireInSeconds}. */
|
|
243
|
+
url: string;
|
|
244
|
+
}
|
|
245
|
+
interface CallMoment {
|
|
246
|
+
/** Milliseconds into the call — seek the audio here. */
|
|
247
|
+
tMs: number | null;
|
|
248
|
+
speaker: string | null;
|
|
249
|
+
text: string;
|
|
250
|
+
}
|
|
251
|
+
/** One shared call in full. */
|
|
252
|
+
interface Call extends CallListItem {
|
|
253
|
+
meetUrl: string | null;
|
|
254
|
+
audio: CallAudio[];
|
|
255
|
+
/** Audio and screenshot links stop working after this many seconds. Read the call per render; never store them. */
|
|
256
|
+
linksExpireInSeconds: number;
|
|
257
|
+
transcript: string | null;
|
|
258
|
+
summary: {
|
|
259
|
+
overview: string;
|
|
260
|
+
moments: CallMoment[];
|
|
261
|
+
} | null;
|
|
262
|
+
/** Screenshots grabbed during the call, a page at a time. `tMs` is on the transcript's clock. */
|
|
263
|
+
frames: {
|
|
264
|
+
total: number;
|
|
265
|
+
offset: number;
|
|
266
|
+
limit: number;
|
|
267
|
+
items: {
|
|
268
|
+
tMs: number;
|
|
269
|
+
url: string;
|
|
270
|
+
}[];
|
|
271
|
+
};
|
|
272
|
+
}
|
|
273
|
+
/** Options for {@link listCalls}, {@link getCall} and {@link deleteCall}. */
|
|
274
|
+
interface CallsOptions {
|
|
275
|
+
/**
|
|
276
|
+
* The project's server key (`ggc_…`, API Tokens → Server keys). Falls back to `GLITCHGRAB_CALLS_KEY`.
|
|
277
|
+
* Server only — never a `NEXT_PUBLIC_` variable: it plays and deletes calls.
|
|
278
|
+
*/
|
|
279
|
+
key?: string;
|
|
280
|
+
/** Override the API host. Falls back to `GLITCHGRAB_BASE_URL`, then `https://glitchgrab.dev`. */
|
|
281
|
+
baseUrl?: string;
|
|
282
|
+
/** Give up after this many ms. Default 5000. */
|
|
283
|
+
timeoutMs?: number;
|
|
284
|
+
}
|
|
285
|
+
interface ListCallsOptions extends CallsOptions {
|
|
286
|
+
/** Default 20, max 100. */
|
|
287
|
+
limit?: number;
|
|
288
|
+
/** ISO date: only calls recorded before it. Pass the last call's `createdAt` for the next page. */
|
|
289
|
+
before?: string;
|
|
290
|
+
}
|
|
291
|
+
interface GetCallOptions extends CallsOptions {
|
|
292
|
+
/** First screenshot to return. Default 0. */
|
|
293
|
+
framesOffset?: number;
|
|
294
|
+
/** Screenshots per page. Default 50, max 200, `0` for none. */
|
|
295
|
+
framesLimit?: number;
|
|
296
|
+
}
|
|
297
|
+
type DeleteCallResult = {
|
|
298
|
+
deleted: true;
|
|
299
|
+
} | {
|
|
300
|
+
deleted: false;
|
|
301
|
+
error: string;
|
|
302
|
+
};
|
|
303
|
+
/**
|
|
304
|
+
* Calls shared with the client on the key's project, newest first. No links —
|
|
305
|
+
* open one with {@link getCall}.
|
|
306
|
+
*
|
|
307
|
+
* ```ts
|
|
308
|
+
* // app/admin/calls/page.tsx — server component
|
|
309
|
+
* import { listCalls } from "glitchgrab/server";
|
|
310
|
+
*
|
|
311
|
+
* export default async function CallsPage() {
|
|
312
|
+
* const calls = await listCalls(); // GLITCHGRAB_CALLS_KEY=ggc_…
|
|
313
|
+
* return calls.map((c) => <a key={c.id} href={`/admin/calls/${c.id}`}>{c.title}</a>);
|
|
314
|
+
* }
|
|
315
|
+
* ```
|
|
316
|
+
*
|
|
317
|
+
* Returns `[]` on any failure. Never throws.
|
|
318
|
+
*/
|
|
319
|
+
declare function listCalls(options?: ListCallsOptions): Promise<CallListItem[]>;
|
|
320
|
+
/**
|
|
321
|
+
* One shared call: audio links, transcript, summary and a page of screenshots.
|
|
322
|
+
* Links expire after `linksExpireInSeconds` — read the call when the page
|
|
323
|
+
* renders and never store them.
|
|
324
|
+
*
|
|
325
|
+
* Returns `null` when the call does not exist, is not shared, and on any
|
|
326
|
+
* failure — call `notFound()` on it. Never throws.
|
|
327
|
+
*/
|
|
328
|
+
declare function getCall(id: string, options?: GetCallOptions): Promise<Call | null>;
|
|
329
|
+
/**
|
|
330
|
+
* Permanently delete one shared call — the recording, the screenshots, the
|
|
331
|
+
* transcript. Cannot be undone.
|
|
332
|
+
*
|
|
333
|
+
* Resolves `{ deleted: false, error }` instead of throwing: the reason is worth
|
|
334
|
+
* showing ("The bot is still in this call — delete it after the call ends").
|
|
335
|
+
*/
|
|
336
|
+
declare function deleteCall(id: string, options?: CallsOptions): Promise<DeleteCallResult>;
|
|
212
337
|
/**
|
|
213
338
|
* `JSON.stringify` that is safe inside `<script type="application/ld+json">`.
|
|
214
339
|
*
|
|
@@ -229,4 +354,4 @@ declare function getGuide(slug: string, options?: GuideReadOptions): Promise<Gui
|
|
|
229
354
|
*/
|
|
230
355
|
declare function escapeJsonForScript(value: unknown): string;
|
|
231
356
|
|
|
232
|
-
export { type CaptureServerErrorsOptions, type Guide, type GuideReadOptions, type GuideSummary, ReportResult, ReportSeverity, ReportType, type ServerReportOptions, type ServerReporterConfig, type ServerReporterIdentity, type SignSessionOptions, captureServerErrors, configureServerReporter, escapeJsonForScript, getGuide, listGuides, reportServerError, resetServerReporter, signSession };
|
|
357
|
+
export { type Call, type CallAudio, type CallListItem, type CallMoment, type CallParticipant, type CallsOptions, type CaptureServerErrorsOptions, type DeleteCallResult, type GetCallOptions, type Guide, type GuideReadOptions, type GuideSummary, type ListCallsOptions, ReportResult, ReportSeverity, ReportType, type ServerReportOptions, type ServerReporterConfig, type ServerReporterIdentity, type SignSessionOptions, captureServerErrors, configureServerReporter, deleteCall, escapeJsonForScript, getCall, getGuide, listCalls, listGuides, reportServerError, resetServerReporter, signSession };
|
package/dist/server.js
CHANGED
|
@@ -22,8 +22,11 @@ var server_exports = {};
|
|
|
22
22
|
__export(server_exports, {
|
|
23
23
|
captureServerErrors: () => captureServerErrors,
|
|
24
24
|
configureServerReporter: () => configureServerReporter,
|
|
25
|
+
deleteCall: () => deleteCall,
|
|
25
26
|
escapeJsonForScript: () => escapeJsonForScript,
|
|
27
|
+
getCall: () => getCall,
|
|
26
28
|
getGuide: () => getGuide,
|
|
29
|
+
listCalls: () => listCalls,
|
|
27
30
|
listGuides: () => listGuides,
|
|
28
31
|
reportServerError: () => reportServerError,
|
|
29
32
|
resetServerReporter: () => resetServerReporter,
|
|
@@ -251,31 +254,39 @@ function signSession(options) {
|
|
|
251
254
|
return `v1.${exp}.${sig}`;
|
|
252
255
|
}
|
|
253
256
|
var GUIDES_REVALIDATE_SECONDS = 300;
|
|
254
|
-
var
|
|
255
|
-
async function
|
|
256
|
-
var _a, _b, _c, _d, _e, _f
|
|
257
|
-
const
|
|
258
|
-
if (!token) return null;
|
|
259
|
-
const baseUrl = ((_f = (_e = (_d = options.baseUrl) != null ? _d : defaults.baseUrl) != null ? _e : readEnv("GLITCHGRAB_BASE_URL")) != null ? _f : DEFAULT_BASE_URL).replace(/\/+$/, "");
|
|
260
|
-
const revalidate = typeof options.revalidate === "number" && Number.isFinite(options.revalidate) && options.revalidate >= 0 ? options.revalidate : GUIDES_REVALIDATE_SECONDS;
|
|
257
|
+
var SDK_API_TIMEOUT_MS = 5e3;
|
|
258
|
+
async function callSdkApi(path, token, options, init) {
|
|
259
|
+
var _a, _b, _c, _d, _e, _f;
|
|
260
|
+
const baseUrl = ((_c = (_b = (_a = options.baseUrl) != null ? _a : defaults.baseUrl) != null ? _b : readEnv("GLITCHGRAB_BASE_URL")) != null ? _c : DEFAULT_BASE_URL).replace(/\/+$/, "");
|
|
261
261
|
const controller = typeof AbortController === "function" ? new AbortController() : void 0;
|
|
262
|
-
const timer = controller ? setTimeout(() => controller.abort(), (
|
|
262
|
+
const timer = controller ? setTimeout(() => controller.abort(), (_d = options.timeoutMs) != null ? _d : SDK_API_TIMEOUT_MS) : void 0;
|
|
263
263
|
try {
|
|
264
|
-
const
|
|
264
|
+
const request = {
|
|
265
|
+
method: (_e = init.method) != null ? _e : "GET",
|
|
265
266
|
headers: { Authorization: `Bearer ${token}` },
|
|
266
|
-
next: { revalidate },
|
|
267
|
+
...init.cache === "no-store" ? { cache: "no-store" } : { next: { revalidate: init.cache.revalidate } },
|
|
267
268
|
...controller ? { signal: controller.signal } : {}
|
|
268
269
|
};
|
|
269
|
-
const response = await fetch(`${baseUrl}
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
270
|
+
const response = await fetch(`${baseUrl}${path}`, request);
|
|
271
|
+
const body = await response.json().catch(() => null);
|
|
272
|
+
if (!response.ok || (body == null ? void 0 : body.success) !== true) {
|
|
273
|
+
return { data: null, error: typeof (body == null ? void 0 : body.error) === "string" ? body.error : `HTTP ${response.status}` };
|
|
274
|
+
}
|
|
275
|
+
return { data: (_f = body.data) != null ? _f : null, error: null };
|
|
273
276
|
} catch (e) {
|
|
274
|
-
return null;
|
|
277
|
+
return { data: null, error: "Could not reach Glitchgrab" };
|
|
275
278
|
} finally {
|
|
276
279
|
if (timer) clearTimeout(timer);
|
|
277
280
|
}
|
|
278
281
|
}
|
|
282
|
+
async function readGuidesApi(query, options) {
|
|
283
|
+
var _a, _b, _c;
|
|
284
|
+
const token = (_c = (_b = (_a = options.token) != null ? _a : defaults.token) != null ? _b : readEnv("GLITCHGRAB_TOKEN")) != null ? _c : readEnv("NEXT_PUBLIC_GLITCHGRAB_TOKEN");
|
|
285
|
+
if (!token) return null;
|
|
286
|
+
const revalidate = typeof options.revalidate === "number" && Number.isFinite(options.revalidate) && options.revalidate >= 0 ? options.revalidate : GUIDES_REVALIDATE_SECONDS;
|
|
287
|
+
const result = await callSdkApi(`/api/v1/sdk/guides${query}`, token, options, { cache: { revalidate } });
|
|
288
|
+
return result.data;
|
|
289
|
+
}
|
|
279
290
|
function readGuideSummary(value) {
|
|
280
291
|
if (!value || typeof value !== "object" || Array.isArray(value)) return null;
|
|
281
292
|
const guide = value;
|
|
@@ -320,6 +331,110 @@ async function getGuide(slug, options = {}) {
|
|
|
320
331
|
return null;
|
|
321
332
|
}
|
|
322
333
|
}
|
|
334
|
+
var CALLS_KEY_PREFIX = "ggc_";
|
|
335
|
+
function callsKey(options) {
|
|
336
|
+
var _a;
|
|
337
|
+
const key = (_a = options.key) != null ? _a : readEnv("GLITCHGRAB_CALLS_KEY");
|
|
338
|
+
return typeof key === "string" && key.startsWith(CALLS_KEY_PREFIX) ? key : null;
|
|
339
|
+
}
|
|
340
|
+
function isRecord(value) {
|
|
341
|
+
return !!value && typeof value === "object" && !Array.isArray(value);
|
|
342
|
+
}
|
|
343
|
+
function stringOrNull(value) {
|
|
344
|
+
return typeof value === "string" ? value : null;
|
|
345
|
+
}
|
|
346
|
+
function numberOrNull(value) {
|
|
347
|
+
return typeof value === "number" && Number.isFinite(value) ? value : null;
|
|
348
|
+
}
|
|
349
|
+
function readCallListItem(value) {
|
|
350
|
+
if (!isRecord(value) || typeof value.id !== "string") return null;
|
|
351
|
+
return {
|
|
352
|
+
id: value.id,
|
|
353
|
+
title: stringOrNull(value.title),
|
|
354
|
+
startsAt: stringOrNull(value.startsAt),
|
|
355
|
+
endsAt: stringOrNull(value.endsAt),
|
|
356
|
+
durationSec: numberOrNull(value.durationSec),
|
|
357
|
+
recorder: stringOrNull(value.recorder),
|
|
358
|
+
participants: Array.isArray(value.participants) ? value.participants.filter(isRecord).map((p) => ({ name: stringOrNull(p.name), email: stringOrNull(p.email) })) : [],
|
|
359
|
+
hasRecording: value.hasRecording === true,
|
|
360
|
+
transcriptStatus: typeof value.transcriptStatus === "string" ? value.transcriptStatus : "IDLE",
|
|
361
|
+
overview: stringOrNull(value.overview),
|
|
362
|
+
createdAt: typeof value.createdAt === "string" ? value.createdAt : ""
|
|
363
|
+
};
|
|
364
|
+
}
|
|
365
|
+
var AUDIO_TRACKS = /* @__PURE__ */ new Set(["call", "client", "recorder"]);
|
|
366
|
+
function readCall(value) {
|
|
367
|
+
var _a, _b, _c, _d;
|
|
368
|
+
const base = readCallListItem(value);
|
|
369
|
+
if (!base || !isRecord(value)) return null;
|
|
370
|
+
const rawSummary = value.summary;
|
|
371
|
+
const summary = isRecord(rawSummary) && typeof rawSummary.overview === "string" ? {
|
|
372
|
+
overview: rawSummary.overview,
|
|
373
|
+
moments: Array.isArray(rawSummary.moments) ? rawSummary.moments.filter(isRecord).filter((m) => typeof m.text === "string").map((m) => ({ tMs: numberOrNull(m.tMs), speaker: stringOrNull(m.speaker), text: m.text })) : []
|
|
374
|
+
} : null;
|
|
375
|
+
const frames = isRecord(value.frames) ? value.frames : {};
|
|
376
|
+
return {
|
|
377
|
+
...base,
|
|
378
|
+
meetUrl: stringOrNull(value.meetUrl),
|
|
379
|
+
audio: Array.isArray(value.audio) ? value.audio.filter(isRecord).filter((a) => typeof a.url === "string" && typeof a.track === "string" && AUDIO_TRACKS.has(a.track)).map((a) => ({ track: a.track, url: a.url })) : [],
|
|
380
|
+
linksExpireInSeconds: (_a = numberOrNull(value.linksExpireInSeconds)) != null ? _a : 0,
|
|
381
|
+
transcript: stringOrNull(value.transcript),
|
|
382
|
+
summary,
|
|
383
|
+
frames: {
|
|
384
|
+
total: (_b = numberOrNull(frames.total)) != null ? _b : 0,
|
|
385
|
+
offset: (_c = numberOrNull(frames.offset)) != null ? _c : 0,
|
|
386
|
+
limit: (_d = numberOrNull(frames.limit)) != null ? _d : 0,
|
|
387
|
+
items: Array.isArray(frames.items) ? frames.items.filter(isRecord).filter((f) => typeof f.url === "string" && typeof f.tMs === "number").map((f) => ({ tMs: f.tMs, url: f.url })) : []
|
|
388
|
+
}
|
|
389
|
+
};
|
|
390
|
+
}
|
|
391
|
+
function setNumber(query, name, value) {
|
|
392
|
+
if (typeof value === "number" && Number.isFinite(value)) query.set(name, String(Math.floor(value)));
|
|
393
|
+
}
|
|
394
|
+
async function listCalls(options = {}) {
|
|
395
|
+
try {
|
|
396
|
+
const key = callsKey(options);
|
|
397
|
+
if (!key) return [];
|
|
398
|
+
const query = new URLSearchParams();
|
|
399
|
+
setNumber(query, "limit", options.limit);
|
|
400
|
+
if (typeof options.before === "string" && options.before) query.set("before", options.before);
|
|
401
|
+
const qs = query.toString();
|
|
402
|
+
const { data } = await callSdkApi(`/api/v1/sdk/calls${qs ? `?${qs}` : ""}`, key, options, { cache: "no-store" });
|
|
403
|
+
if (!Array.isArray(data)) return [];
|
|
404
|
+
return data.map(readCallListItem).filter((call) => call !== null);
|
|
405
|
+
} catch (e) {
|
|
406
|
+
return [];
|
|
407
|
+
}
|
|
408
|
+
}
|
|
409
|
+
async function getCall(id, options = {}) {
|
|
410
|
+
try {
|
|
411
|
+
const wanted = typeof id === "string" ? id.trim() : "";
|
|
412
|
+
const key = callsKey(options);
|
|
413
|
+
if (!wanted || !key) return null;
|
|
414
|
+
const query = new URLSearchParams({ id: wanted });
|
|
415
|
+
setNumber(query, "framesOffset", options.framesOffset);
|
|
416
|
+
setNumber(query, "framesLimit", options.framesLimit);
|
|
417
|
+
const { data } = await callSdkApi(`/api/v1/sdk/calls?${query.toString()}`, key, options, { cache: "no-store" });
|
|
418
|
+
return readCall(data);
|
|
419
|
+
} catch (e) {
|
|
420
|
+
return null;
|
|
421
|
+
}
|
|
422
|
+
}
|
|
423
|
+
async function deleteCall(id, options = {}) {
|
|
424
|
+
try {
|
|
425
|
+
const wanted = typeof id === "string" ? id.trim() : "";
|
|
426
|
+
if (!wanted) return { deleted: false, error: "No call id" };
|
|
427
|
+
const key = callsKey(options);
|
|
428
|
+
if (!key) return { deleted: false, error: "No server key \u2014 set GLITCHGRAB_CALLS_KEY (ggc_\u2026)" };
|
|
429
|
+
const { error } = await callSdkApi(`/api/v1/sdk/calls?id=${encodeURIComponent(wanted)}`, key, options, {
|
|
430
|
+
method: "DELETE",
|
|
431
|
+
cache: "no-store"
|
|
432
|
+
});
|
|
433
|
+
return error === null ? { deleted: true } : { deleted: false, error };
|
|
434
|
+
} catch (e) {
|
|
435
|
+
return { deleted: false, error: "Could not delete the call" };
|
|
436
|
+
}
|
|
437
|
+
}
|
|
323
438
|
function escapeJsonForScript(value) {
|
|
324
439
|
try {
|
|
325
440
|
const json = JSON.stringify(value);
|
|
@@ -333,8 +448,11 @@ function escapeJsonForScript(value) {
|
|
|
333
448
|
0 && (module.exports = {
|
|
334
449
|
captureServerErrors,
|
|
335
450
|
configureServerReporter,
|
|
451
|
+
deleteCall,
|
|
336
452
|
escapeJsonForScript,
|
|
453
|
+
getCall,
|
|
337
454
|
getGuide,
|
|
455
|
+
listCalls,
|
|
338
456
|
listGuides,
|
|
339
457
|
reportServerError,
|
|
340
458
|
resetServerReporter,
|