uidex 0.4.0 → 0.5.1
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/cli/cli.cjs +1111 -87
- package/dist/cli/cli.cjs.map +1 -1
- package/dist/cloud/index.cjs +375 -72
- package/dist/cloud/index.cjs.map +1 -1
- package/dist/cloud/index.d.cts +82 -0
- package/dist/cloud/index.d.ts +82 -0
- package/dist/cloud/index.js +376 -71
- package/dist/cloud/index.js.map +1 -1
- package/dist/headless/index.cjs +623 -469
- package/dist/headless/index.cjs.map +1 -1
- package/dist/headless/index.d.cts +77 -75
- package/dist/headless/index.d.ts +77 -75
- package/dist/headless/index.js +627 -469
- package/dist/headless/index.js.map +1 -1
- package/dist/index.cjs +4258 -2884
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +275 -234
- package/dist/index.d.ts +275 -234
- package/dist/index.js +4280 -2890
- package/dist/index.js.map +1 -1
- package/dist/playwright/index.cjs +4 -4
- package/dist/playwright/index.cjs.map +1 -1
- package/dist/playwright/index.js +3 -3
- package/dist/playwright/index.js.map +1 -1
- package/dist/playwright/reporter.cjs +3 -3
- package/dist/playwright/reporter.cjs.map +1 -1
- package/dist/playwright/reporter.js +3 -3
- package/dist/playwright/reporter.js.map +1 -1
- package/dist/react/index.cjs +4299 -2906
- package/dist/react/index.cjs.map +1 -1
- package/dist/react/index.d.cts +206 -200
- package/dist/react/index.d.ts +206 -200
- package/dist/react/index.js +4339 -2926
- package/dist/react/index.js.map +1 -1
- package/dist/scan/index.cjs +201 -49
- package/dist/scan/index.cjs.map +1 -1
- package/dist/scan/index.d.cts +27 -1
- package/dist/scan/index.d.ts +27 -1
- package/dist/scan/index.js +200 -48
- package/dist/scan/index.js.map +1 -1
- package/package.json +8 -14
- package/templates/claude/api.md +110 -0
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import { ReportPayload, ReportResult, IngestConfig, ReportListResponse, PinRecord, ArchiveReason } from '@uidex/api-client';
|
|
2
|
+
export { ArchiveReason, IngestConfig, PinRecord, ReportListRecord, ReportListResponse, ReportPayload, ReportResult } from '@uidex/api-client';
|
|
3
|
+
|
|
4
|
+
interface UserIdentity {
|
|
5
|
+
id: string;
|
|
6
|
+
name?: string;
|
|
7
|
+
avatar?: string;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
declare const DEFAULT_CLOUD_ENDPOINT = "https://app.uidex.dev";
|
|
11
|
+
interface CloudOptions {
|
|
12
|
+
projectKey: string;
|
|
13
|
+
endpoint?: string;
|
|
14
|
+
fetch?: typeof fetch;
|
|
15
|
+
git?: {
|
|
16
|
+
branch?: string;
|
|
17
|
+
commit?: string;
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
declare class CloudError extends Error {
|
|
21
|
+
readonly status: number;
|
|
22
|
+
readonly retryAfter?: number;
|
|
23
|
+
readonly details?: unknown;
|
|
24
|
+
constructor(message: string, options: {
|
|
25
|
+
status: number;
|
|
26
|
+
retryAfter?: number;
|
|
27
|
+
details?: unknown;
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
type RealtimePresenceUser = {
|
|
31
|
+
userId: string;
|
|
32
|
+
name: string;
|
|
33
|
+
avatar: string | null;
|
|
34
|
+
};
|
|
35
|
+
type RealtimeChannelState = "connecting" | "connected" | "disconnected";
|
|
36
|
+
interface RealtimeConnectOpts {
|
|
37
|
+
user: UserIdentity;
|
|
38
|
+
route: string;
|
|
39
|
+
}
|
|
40
|
+
interface RealtimeChannel {
|
|
41
|
+
readonly state: RealtimeChannelState;
|
|
42
|
+
connect(): void;
|
|
43
|
+
disconnect(): void;
|
|
44
|
+
joinRoute(route: string): void;
|
|
45
|
+
onPresence(cb: (users: RealtimePresenceUser[]) => void): () => void;
|
|
46
|
+
onPin(cb: (pin: PinRecord) => void): () => void;
|
|
47
|
+
}
|
|
48
|
+
interface CloudAdapter<TPayload = ReportPayload, TResult = ReportResult, TIntegrations = {
|
|
49
|
+
getConfig(): Promise<IngestConfig>;
|
|
50
|
+
getCachedConfig(): IngestConfig | null;
|
|
51
|
+
}> {
|
|
52
|
+
readonly reports: {
|
|
53
|
+
submit(payload: TPayload): Promise<TResult>;
|
|
54
|
+
list?(opts?: {
|
|
55
|
+
page?: number;
|
|
56
|
+
limit?: number;
|
|
57
|
+
}): Promise<ReportListResponse>;
|
|
58
|
+
};
|
|
59
|
+
readonly integrations: TIntegrations;
|
|
60
|
+
readonly realtime: {
|
|
61
|
+
connect(opts: RealtimeConnectOpts): RealtimeChannel;
|
|
62
|
+
};
|
|
63
|
+
readonly pins: {
|
|
64
|
+
list(params: {
|
|
65
|
+
route?: string;
|
|
66
|
+
entities?: string;
|
|
67
|
+
}): Promise<PinRecord[]>;
|
|
68
|
+
archive(reportId: string, reason?: ArchiveReason): Promise<void>;
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
declare function cloud(options: CloudOptions): CloudAdapter;
|
|
73
|
+
|
|
74
|
+
type RealtimeChannelOptions = {
|
|
75
|
+
/** Builds the WebSocket URL on each (re)connect attempt. Allows the caller to refresh `route` query params. */
|
|
76
|
+
buildUrl: () => string;
|
|
77
|
+
/** Optional WebSocket constructor override (testing). Defaults to `globalThis.WebSocket`. */
|
|
78
|
+
WebSocketImpl?: typeof WebSocket;
|
|
79
|
+
};
|
|
80
|
+
declare function createRealtimeChannel(options: RealtimeChannelOptions): RealtimeChannel;
|
|
81
|
+
|
|
82
|
+
export { type CloudAdapter, CloudError, type CloudOptions, DEFAULT_CLOUD_ENDPOINT, type RealtimeChannel, type RealtimeChannelState, type RealtimeConnectOpts, type RealtimePresenceUser, cloud, createRealtimeChannel };
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import { ReportPayload, ReportResult, IngestConfig, ReportListResponse, PinRecord, ArchiveReason } from '@uidex/api-client';
|
|
2
|
+
export { ArchiveReason, IngestConfig, PinRecord, ReportListRecord, ReportListResponse, ReportPayload, ReportResult } from '@uidex/api-client';
|
|
3
|
+
|
|
4
|
+
interface UserIdentity {
|
|
5
|
+
id: string;
|
|
6
|
+
name?: string;
|
|
7
|
+
avatar?: string;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
declare const DEFAULT_CLOUD_ENDPOINT = "https://app.uidex.dev";
|
|
11
|
+
interface CloudOptions {
|
|
12
|
+
projectKey: string;
|
|
13
|
+
endpoint?: string;
|
|
14
|
+
fetch?: typeof fetch;
|
|
15
|
+
git?: {
|
|
16
|
+
branch?: string;
|
|
17
|
+
commit?: string;
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
declare class CloudError extends Error {
|
|
21
|
+
readonly status: number;
|
|
22
|
+
readonly retryAfter?: number;
|
|
23
|
+
readonly details?: unknown;
|
|
24
|
+
constructor(message: string, options: {
|
|
25
|
+
status: number;
|
|
26
|
+
retryAfter?: number;
|
|
27
|
+
details?: unknown;
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
type RealtimePresenceUser = {
|
|
31
|
+
userId: string;
|
|
32
|
+
name: string;
|
|
33
|
+
avatar: string | null;
|
|
34
|
+
};
|
|
35
|
+
type RealtimeChannelState = "connecting" | "connected" | "disconnected";
|
|
36
|
+
interface RealtimeConnectOpts {
|
|
37
|
+
user: UserIdentity;
|
|
38
|
+
route: string;
|
|
39
|
+
}
|
|
40
|
+
interface RealtimeChannel {
|
|
41
|
+
readonly state: RealtimeChannelState;
|
|
42
|
+
connect(): void;
|
|
43
|
+
disconnect(): void;
|
|
44
|
+
joinRoute(route: string): void;
|
|
45
|
+
onPresence(cb: (users: RealtimePresenceUser[]) => void): () => void;
|
|
46
|
+
onPin(cb: (pin: PinRecord) => void): () => void;
|
|
47
|
+
}
|
|
48
|
+
interface CloudAdapter<TPayload = ReportPayload, TResult = ReportResult, TIntegrations = {
|
|
49
|
+
getConfig(): Promise<IngestConfig>;
|
|
50
|
+
getCachedConfig(): IngestConfig | null;
|
|
51
|
+
}> {
|
|
52
|
+
readonly reports: {
|
|
53
|
+
submit(payload: TPayload): Promise<TResult>;
|
|
54
|
+
list?(opts?: {
|
|
55
|
+
page?: number;
|
|
56
|
+
limit?: number;
|
|
57
|
+
}): Promise<ReportListResponse>;
|
|
58
|
+
};
|
|
59
|
+
readonly integrations: TIntegrations;
|
|
60
|
+
readonly realtime: {
|
|
61
|
+
connect(opts: RealtimeConnectOpts): RealtimeChannel;
|
|
62
|
+
};
|
|
63
|
+
readonly pins: {
|
|
64
|
+
list(params: {
|
|
65
|
+
route?: string;
|
|
66
|
+
entities?: string;
|
|
67
|
+
}): Promise<PinRecord[]>;
|
|
68
|
+
archive(reportId: string, reason?: ArchiveReason): Promise<void>;
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
declare function cloud(options: CloudOptions): CloudAdapter;
|
|
73
|
+
|
|
74
|
+
type RealtimeChannelOptions = {
|
|
75
|
+
/** Builds the WebSocket URL on each (re)connect attempt. Allows the caller to refresh `route` query params. */
|
|
76
|
+
buildUrl: () => string;
|
|
77
|
+
/** Optional WebSocket constructor override (testing). Defaults to `globalThis.WebSocket`. */
|
|
78
|
+
WebSocketImpl?: typeof WebSocket;
|
|
79
|
+
};
|
|
80
|
+
declare function createRealtimeChannel(options: RealtimeChannelOptions): RealtimeChannel;
|
|
81
|
+
|
|
82
|
+
export { type CloudAdapter, CloudError, type CloudOptions, DEFAULT_CLOUD_ENDPOINT, type RealtimeChannel, type RealtimeChannelState, type RealtimeConnectOpts, type RealtimePresenceUser, cloud, createRealtimeChannel };
|
package/dist/cloud/index.js
CHANGED
|
@@ -1,3 +1,283 @@
|
|
|
1
|
+
// src/cloud/client.ts
|
|
2
|
+
import { createClient as createClient2, createConfig as createConfig2 } from "@hey-api/client-fetch";
|
|
3
|
+
|
|
4
|
+
// ../api-client/src/client.gen.ts
|
|
5
|
+
import {
|
|
6
|
+
createClient,
|
|
7
|
+
createConfig
|
|
8
|
+
} from "@hey-api/client-fetch";
|
|
9
|
+
var client = createClient(
|
|
10
|
+
createConfig({
|
|
11
|
+
baseUrl: "https://app.uidex.dev"
|
|
12
|
+
})
|
|
13
|
+
);
|
|
14
|
+
|
|
15
|
+
// ../api-client/src/sdk.gen.ts
|
|
16
|
+
var submitReport = (options) => {
|
|
17
|
+
return (options.client ?? client).post({
|
|
18
|
+
security: [
|
|
19
|
+
{
|
|
20
|
+
scheme: "bearer",
|
|
21
|
+
type: "http"
|
|
22
|
+
}
|
|
23
|
+
],
|
|
24
|
+
url: "/api/ingest",
|
|
25
|
+
...options,
|
|
26
|
+
headers: {
|
|
27
|
+
"Content-Type": "application/json",
|
|
28
|
+
...options?.headers
|
|
29
|
+
}
|
|
30
|
+
});
|
|
31
|
+
};
|
|
32
|
+
var getIngestConfig = (options) => {
|
|
33
|
+
return (options?.client ?? client).get({
|
|
34
|
+
security: [
|
|
35
|
+
{
|
|
36
|
+
scheme: "bearer",
|
|
37
|
+
type: "http"
|
|
38
|
+
}
|
|
39
|
+
],
|
|
40
|
+
url: "/api/ingest/config",
|
|
41
|
+
...options
|
|
42
|
+
});
|
|
43
|
+
};
|
|
44
|
+
var listIngestReports = (options) => {
|
|
45
|
+
return (options?.client ?? client).get({
|
|
46
|
+
security: [
|
|
47
|
+
{
|
|
48
|
+
scheme: "bearer",
|
|
49
|
+
type: "http"
|
|
50
|
+
}
|
|
51
|
+
],
|
|
52
|
+
url: "/api/ingest/reports",
|
|
53
|
+
...options
|
|
54
|
+
});
|
|
55
|
+
};
|
|
56
|
+
var listPins = (options) => {
|
|
57
|
+
return (options?.client ?? client).get({
|
|
58
|
+
security: [
|
|
59
|
+
{
|
|
60
|
+
scheme: "bearer",
|
|
61
|
+
type: "http"
|
|
62
|
+
}
|
|
63
|
+
],
|
|
64
|
+
url: "/api/ingest/pins",
|
|
65
|
+
...options
|
|
66
|
+
});
|
|
67
|
+
};
|
|
68
|
+
var archivePin = (options) => {
|
|
69
|
+
return (options.client ?? client).post({
|
|
70
|
+
security: [
|
|
71
|
+
{
|
|
72
|
+
scheme: "bearer",
|
|
73
|
+
type: "http"
|
|
74
|
+
}
|
|
75
|
+
],
|
|
76
|
+
url: "/api/ingest/pins/archive",
|
|
77
|
+
...options,
|
|
78
|
+
headers: {
|
|
79
|
+
"Content-Type": "application/json",
|
|
80
|
+
...options?.headers
|
|
81
|
+
}
|
|
82
|
+
});
|
|
83
|
+
};
|
|
84
|
+
|
|
85
|
+
// src/cloud/realtime.ts
|
|
86
|
+
var RECONNECT_INITIAL_MS = 1e3;
|
|
87
|
+
var RECONNECT_MAX_MS = 3e4;
|
|
88
|
+
var CLOSE_CODE_AUTH_FAILED = 4001;
|
|
89
|
+
function emit(listeners, value) {
|
|
90
|
+
for (const cb of listeners) {
|
|
91
|
+
try {
|
|
92
|
+
cb(value);
|
|
93
|
+
} catch {
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
function resolveWebSocket(override) {
|
|
98
|
+
if (override) return override;
|
|
99
|
+
if (typeof globalThis !== "undefined" && typeof globalThis.WebSocket === "function") {
|
|
100
|
+
return globalThis.WebSocket;
|
|
101
|
+
}
|
|
102
|
+
throw new Error(
|
|
103
|
+
"uidex/cloud: global WebSocket is not available; pass a `WebSocketImpl` override"
|
|
104
|
+
);
|
|
105
|
+
}
|
|
106
|
+
function createRealtimeChannel(options) {
|
|
107
|
+
const WS = resolveWebSocket(options.WebSocketImpl);
|
|
108
|
+
const presenceListeners = /* @__PURE__ */ new Set();
|
|
109
|
+
const pinListeners = /* @__PURE__ */ new Set();
|
|
110
|
+
let ws = null;
|
|
111
|
+
let state = "disconnected";
|
|
112
|
+
let disposed = false;
|
|
113
|
+
let reconnectAttempts = 0;
|
|
114
|
+
let reconnectTimer = null;
|
|
115
|
+
function clearReconnectTimer() {
|
|
116
|
+
if (reconnectTimer !== null) {
|
|
117
|
+
clearTimeout(reconnectTimer);
|
|
118
|
+
reconnectTimer = null;
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
function scheduleReconnect() {
|
|
122
|
+
if (disposed) return;
|
|
123
|
+
const delay = Math.min(
|
|
124
|
+
RECONNECT_INITIAL_MS * 2 ** reconnectAttempts,
|
|
125
|
+
RECONNECT_MAX_MS
|
|
126
|
+
);
|
|
127
|
+
reconnectAttempts += 1;
|
|
128
|
+
state = "connecting";
|
|
129
|
+
clearReconnectTimer();
|
|
130
|
+
reconnectTimer = setTimeout(() => {
|
|
131
|
+
reconnectTimer = null;
|
|
132
|
+
openSocket();
|
|
133
|
+
}, delay);
|
|
134
|
+
}
|
|
135
|
+
function handleMessage(event) {
|
|
136
|
+
if (typeof event.data !== "string") return;
|
|
137
|
+
let parsed;
|
|
138
|
+
try {
|
|
139
|
+
parsed = JSON.parse(event.data);
|
|
140
|
+
} catch {
|
|
141
|
+
return;
|
|
142
|
+
}
|
|
143
|
+
if (!parsed || typeof parsed !== "object") return;
|
|
144
|
+
const msg = parsed;
|
|
145
|
+
if (msg.type === "presence") {
|
|
146
|
+
const p = msg;
|
|
147
|
+
if (!Array.isArray(p.users)) return;
|
|
148
|
+
const users = [];
|
|
149
|
+
for (const raw of p.users) {
|
|
150
|
+
if (!raw || typeof raw !== "object") continue;
|
|
151
|
+
const u = raw;
|
|
152
|
+
if (typeof u.userId !== "string" || typeof u.name !== "string") continue;
|
|
153
|
+
users.push({
|
|
154
|
+
userId: u.userId,
|
|
155
|
+
name: u.name,
|
|
156
|
+
avatar: typeof u.avatar === "string" ? u.avatar : null
|
|
157
|
+
});
|
|
158
|
+
}
|
|
159
|
+
emit(presenceListeners, users);
|
|
160
|
+
return;
|
|
161
|
+
}
|
|
162
|
+
if (msg.type === "pin") {
|
|
163
|
+
const p = msg;
|
|
164
|
+
if (typeof p.feedbackId !== "string" || !p.elementRef || typeof p.elementRef !== "object" || typeof p.elementRef.kind !== "string" || typeof p.elementRef.id !== "string" || !p.author || typeof p.author !== "object" || typeof p.body !== "string" || typeof p.reportType !== "string" || typeof p.reportSeverity !== "string" || typeof p.createdAt !== "string") {
|
|
165
|
+
return;
|
|
166
|
+
}
|
|
167
|
+
const author = p.author;
|
|
168
|
+
const elRef = p.elementRef;
|
|
169
|
+
const pin = {
|
|
170
|
+
id: p.feedbackId,
|
|
171
|
+
entity: `${elRef.kind}:${elRef.id}`,
|
|
172
|
+
reporter: {
|
|
173
|
+
name: typeof author.name === "string" ? author.name : void 0,
|
|
174
|
+
email: typeof author.email === "string" ? author.email : void 0
|
|
175
|
+
},
|
|
176
|
+
body: p.body,
|
|
177
|
+
type: p.reportType,
|
|
178
|
+
severity: p.reportSeverity,
|
|
179
|
+
status: "open",
|
|
180
|
+
createdAt: p.createdAt,
|
|
181
|
+
url: ""
|
|
182
|
+
};
|
|
183
|
+
emit(pinListeners, pin);
|
|
184
|
+
return;
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
function openSocket() {
|
|
188
|
+
if (disposed) return;
|
|
189
|
+
state = "connecting";
|
|
190
|
+
let url;
|
|
191
|
+
try {
|
|
192
|
+
url = options.buildUrl();
|
|
193
|
+
} catch {
|
|
194
|
+
state = "disconnected";
|
|
195
|
+
return;
|
|
196
|
+
}
|
|
197
|
+
let socket;
|
|
198
|
+
try {
|
|
199
|
+
socket = new WS(url);
|
|
200
|
+
} catch {
|
|
201
|
+
scheduleReconnect();
|
|
202
|
+
return;
|
|
203
|
+
}
|
|
204
|
+
ws = socket;
|
|
205
|
+
socket.addEventListener("open", () => {
|
|
206
|
+
if (ws !== socket) return;
|
|
207
|
+
state = "connected";
|
|
208
|
+
reconnectAttempts = 0;
|
|
209
|
+
});
|
|
210
|
+
socket.addEventListener("message", (event) => {
|
|
211
|
+
if (ws !== socket) return;
|
|
212
|
+
handleMessage(event);
|
|
213
|
+
});
|
|
214
|
+
socket.addEventListener("close", (event) => {
|
|
215
|
+
if (ws !== socket) return;
|
|
216
|
+
ws = null;
|
|
217
|
+
const code = event.code;
|
|
218
|
+
if (disposed || code === CLOSE_CODE_AUTH_FAILED) {
|
|
219
|
+
state = "disconnected";
|
|
220
|
+
return;
|
|
221
|
+
}
|
|
222
|
+
scheduleReconnect();
|
|
223
|
+
});
|
|
224
|
+
socket.addEventListener("error", () => {
|
|
225
|
+
});
|
|
226
|
+
}
|
|
227
|
+
function connect() {
|
|
228
|
+
if (disposed) {
|
|
229
|
+
disposed = false;
|
|
230
|
+
}
|
|
231
|
+
if (ws) return;
|
|
232
|
+
if (state === "connecting") return;
|
|
233
|
+
reconnectAttempts = 0;
|
|
234
|
+
openSocket();
|
|
235
|
+
}
|
|
236
|
+
function disconnect() {
|
|
237
|
+
disposed = true;
|
|
238
|
+
clearReconnectTimer();
|
|
239
|
+
state = "disconnected";
|
|
240
|
+
if (ws) {
|
|
241
|
+
try {
|
|
242
|
+
ws.close(1e3);
|
|
243
|
+
} catch {
|
|
244
|
+
}
|
|
245
|
+
ws = null;
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
function joinRoute(route) {
|
|
249
|
+
emit(presenceListeners, []);
|
|
250
|
+
if (ws && ws.readyState === WS.OPEN) {
|
|
251
|
+
try {
|
|
252
|
+
ws.send(JSON.stringify({ type: "join", route }));
|
|
253
|
+
} catch {
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
function onPresence(cb) {
|
|
258
|
+
presenceListeners.add(cb);
|
|
259
|
+
return () => {
|
|
260
|
+
presenceListeners.delete(cb);
|
|
261
|
+
};
|
|
262
|
+
}
|
|
263
|
+
function onPin(cb) {
|
|
264
|
+
pinListeners.add(cb);
|
|
265
|
+
return () => {
|
|
266
|
+
pinListeners.delete(cb);
|
|
267
|
+
};
|
|
268
|
+
}
|
|
269
|
+
return {
|
|
270
|
+
get state() {
|
|
271
|
+
return state;
|
|
272
|
+
},
|
|
273
|
+
connect,
|
|
274
|
+
disconnect,
|
|
275
|
+
joinRoute,
|
|
276
|
+
onPresence,
|
|
277
|
+
onPin
|
|
278
|
+
};
|
|
279
|
+
}
|
|
280
|
+
|
|
1
281
|
// src/cloud/types.ts
|
|
2
282
|
var DEFAULT_CLOUD_ENDPOINT = "https://app.uidex.dev";
|
|
3
283
|
var CloudError = class extends Error {
|
|
@@ -14,18 +294,18 @@ var CloudError = class extends Error {
|
|
|
14
294
|
};
|
|
15
295
|
|
|
16
296
|
// src/cloud/client.ts
|
|
17
|
-
function resolveFetch(override) {
|
|
18
|
-
if (override) return override;
|
|
19
|
-
if (typeof globalThis !== "undefined" && typeof globalThis.fetch === "function") {
|
|
20
|
-
return globalThis.fetch.bind(globalThis);
|
|
21
|
-
}
|
|
22
|
-
throw new Error(
|
|
23
|
-
"uidex/cloud: global fetch is not available; pass a `fetch` override"
|
|
24
|
-
);
|
|
25
|
-
}
|
|
26
297
|
function trimEndpoint(endpoint) {
|
|
27
298
|
return endpoint.endsWith("/") ? endpoint.slice(0, -1) : endpoint;
|
|
28
299
|
}
|
|
300
|
+
function unwrap(result) {
|
|
301
|
+
if (result.error !== void 0 || !result.data) {
|
|
302
|
+
const status = result.response.status;
|
|
303
|
+
const retryAfter = status === 429 ? parseRetryAfter(result.response.headers.get("Retry-After")) : void 0;
|
|
304
|
+
const msg = result.error && typeof result.error === "object" && "error" in result.error && typeof result.error.error === "string" ? result.error.error : `Request failed (${status})`;
|
|
305
|
+
throw new CloudError(msg, { status, retryAfter, details: result.error });
|
|
306
|
+
}
|
|
307
|
+
return result.data;
|
|
308
|
+
}
|
|
29
309
|
function parseRetryAfter(header) {
|
|
30
310
|
if (!header) return void 0;
|
|
31
311
|
const seconds = Number(header);
|
|
@@ -37,21 +317,6 @@ function parseRetryAfter(header) {
|
|
|
37
317
|
}
|
|
38
318
|
return void 0;
|
|
39
319
|
}
|
|
40
|
-
async function readBody(response) {
|
|
41
|
-
const text = await response.text();
|
|
42
|
-
if (!text) return void 0;
|
|
43
|
-
try {
|
|
44
|
-
return JSON.parse(text);
|
|
45
|
-
} catch {
|
|
46
|
-
return text;
|
|
47
|
-
}
|
|
48
|
-
}
|
|
49
|
-
function errorMessage(body, fallback) {
|
|
50
|
-
if (body && typeof body === "object" && "error" in body && typeof body.error === "string") {
|
|
51
|
-
return body.error;
|
|
52
|
-
}
|
|
53
|
-
return fallback;
|
|
54
|
-
}
|
|
55
320
|
function cloud(options) {
|
|
56
321
|
let cachedConfig = null;
|
|
57
322
|
let resolvedConfig = null;
|
|
@@ -60,54 +325,34 @@ function cloud(options) {
|
|
|
60
325
|
throw new Error("uidex/cloud: `projectKey` is required");
|
|
61
326
|
}
|
|
62
327
|
const endpoint = trimEndpoint(options.endpoint ?? DEFAULT_CLOUD_ENDPOINT);
|
|
63
|
-
const
|
|
64
|
-
const
|
|
328
|
+
const git = options.git;
|
|
329
|
+
const apiClient = createClient2(
|
|
330
|
+
createConfig2({
|
|
331
|
+
baseUrl: endpoint,
|
|
332
|
+
...options.fetch ? { fetch: options.fetch } : {},
|
|
333
|
+
headers: { Authorization: `Bearer ${projectKey}` }
|
|
334
|
+
})
|
|
335
|
+
);
|
|
65
336
|
async function submit(payload) {
|
|
66
|
-
const
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
337
|
+
const enriched = git?.branch || git?.commit ? {
|
|
338
|
+
...payload,
|
|
339
|
+
context: {
|
|
340
|
+
...payload.context,
|
|
341
|
+
git: payload.context?.git ?? {
|
|
342
|
+
branch: git.branch,
|
|
343
|
+
commit: git.commit
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
} : payload;
|
|
347
|
+
const result = await submitReport({
|
|
348
|
+
client: apiClient,
|
|
349
|
+
body: enriched
|
|
73
350
|
});
|
|
74
|
-
|
|
75
|
-
if (!response.ok) {
|
|
76
|
-
const retryAfter = response.status === 429 ? parseRetryAfter(response.headers.get("Retry-After")) : void 0;
|
|
77
|
-
throw new CloudError(
|
|
78
|
-
errorMessage(body, `Feedback submission failed (${response.status})`),
|
|
79
|
-
{ status: response.status, retryAfter, details: body }
|
|
80
|
-
);
|
|
81
|
-
}
|
|
82
|
-
if (!body || typeof body !== "object") {
|
|
83
|
-
throw new CloudError("Feedback submission returned an empty response", {
|
|
84
|
-
status: response.status
|
|
85
|
-
});
|
|
86
|
-
}
|
|
87
|
-
return body;
|
|
351
|
+
return unwrap(result);
|
|
88
352
|
}
|
|
89
353
|
async function fetchConfig() {
|
|
90
|
-
const
|
|
91
|
-
|
|
92
|
-
headers: {
|
|
93
|
-
Accept: "application/json",
|
|
94
|
-
Authorization: authHeader
|
|
95
|
-
}
|
|
96
|
-
});
|
|
97
|
-
const body = await readBody(response);
|
|
98
|
-
if (!response.ok) {
|
|
99
|
-
const retryAfter = response.status === 429 ? parseRetryAfter(response.headers.get("Retry-After")) : void 0;
|
|
100
|
-
throw new CloudError(
|
|
101
|
-
errorMessage(body, `Ingest config request failed (${response.status})`),
|
|
102
|
-
{ status: response.status, retryAfter, details: body }
|
|
103
|
-
);
|
|
104
|
-
}
|
|
105
|
-
if (!body || typeof body !== "object") {
|
|
106
|
-
throw new CloudError("Ingest config returned an empty response", {
|
|
107
|
-
status: response.status
|
|
108
|
-
});
|
|
109
|
-
}
|
|
110
|
-
return body;
|
|
354
|
+
const result = await getIngestConfig({ client: apiClient });
|
|
355
|
+
return unwrap(result);
|
|
111
356
|
}
|
|
112
357
|
function startFetch() {
|
|
113
358
|
const promise = fetchConfig();
|
|
@@ -127,14 +372,74 @@ function cloud(options) {
|
|
|
127
372
|
function getCachedConfig() {
|
|
128
373
|
return resolvedConfig;
|
|
129
374
|
}
|
|
375
|
+
function realtimeUrl(route, user) {
|
|
376
|
+
const httpToWs = endpoint.replace(/^http:/, "ws:").replace(/^https:/, "wss:");
|
|
377
|
+
const params = new URLSearchParams();
|
|
378
|
+
params.set("key", projectKey);
|
|
379
|
+
params.set("route", route);
|
|
380
|
+
params.set("userId", user.id);
|
|
381
|
+
if (user.name) params.set("name", user.name);
|
|
382
|
+
if (user.avatar) params.set("avatar", user.avatar);
|
|
383
|
+
return `${httpToWs}/ws?${params.toString()}`;
|
|
384
|
+
}
|
|
385
|
+
function connectRealtime(opts) {
|
|
386
|
+
if (!opts || !opts.user || typeof opts.user.id !== "string") {
|
|
387
|
+
throw new TypeError("uidex/cloud: realtime.connect requires `user.id`");
|
|
388
|
+
}
|
|
389
|
+
let currentRoute = opts.route;
|
|
390
|
+
const channel = createRealtimeChannel({
|
|
391
|
+
buildUrl: () => realtimeUrl(currentRoute, opts.user)
|
|
392
|
+
});
|
|
393
|
+
const originalJoinRoute = channel.joinRoute;
|
|
394
|
+
const wrapped = {
|
|
395
|
+
get state() {
|
|
396
|
+
return channel.state;
|
|
397
|
+
},
|
|
398
|
+
connect: () => channel.connect(),
|
|
399
|
+
disconnect: () => channel.disconnect(),
|
|
400
|
+
joinRoute: (route) => {
|
|
401
|
+
currentRoute = route;
|
|
402
|
+
originalJoinRoute(route);
|
|
403
|
+
},
|
|
404
|
+
onPresence: (cb) => channel.onPresence(cb),
|
|
405
|
+
onPin: (cb) => channel.onPin(cb)
|
|
406
|
+
};
|
|
407
|
+
channel.connect();
|
|
408
|
+
return wrapped;
|
|
409
|
+
}
|
|
410
|
+
async function listPins2(params) {
|
|
411
|
+
const result = await listPins({
|
|
412
|
+
client: apiClient,
|
|
413
|
+
query: params
|
|
414
|
+
});
|
|
415
|
+
const data = unwrap(result);
|
|
416
|
+
return data.pins;
|
|
417
|
+
}
|
|
418
|
+
async function archivePin2(reportId, reason) {
|
|
419
|
+
const result = await archivePin({
|
|
420
|
+
client: apiClient,
|
|
421
|
+
body: { reportId, ...reason ? { reason } : {} }
|
|
422
|
+
});
|
|
423
|
+
unwrap(result);
|
|
424
|
+
}
|
|
425
|
+
async function listReports(opts) {
|
|
426
|
+
const result = await listIngestReports({
|
|
427
|
+
client: apiClient,
|
|
428
|
+
query: opts
|
|
429
|
+
});
|
|
430
|
+
return unwrap(result);
|
|
431
|
+
}
|
|
130
432
|
return {
|
|
131
|
-
|
|
132
|
-
integrations: { getConfig, getCachedConfig }
|
|
433
|
+
reports: { submit, list: listReports },
|
|
434
|
+
integrations: { getConfig, getCachedConfig },
|
|
435
|
+
realtime: { connect: connectRealtime },
|
|
436
|
+
pins: { list: listPins2, archive: archivePin2 }
|
|
133
437
|
};
|
|
134
438
|
}
|
|
135
439
|
export {
|
|
136
440
|
CloudError,
|
|
137
441
|
DEFAULT_CLOUD_ENDPOINT,
|
|
138
|
-
cloud
|
|
442
|
+
cloud,
|
|
443
|
+
createRealtimeChannel
|
|
139
444
|
};
|
|
140
445
|
//# sourceMappingURL=index.js.map
|