@devorash/core 0.1.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/LICENSE +21 -0
- package/README.md +187 -0
- package/dist/constants/api-url.gen.d.ts +4 -0
- package/dist/constants/api-url.gen.d.ts.map +1 -0
- package/dist/constants/api-url.gen.example.d.ts +7 -0
- package/dist/constants/api-url.gen.example.d.ts.map +1 -0
- package/dist/constants/api-url.gen.example.js +7 -0
- package/dist/constants/api-url.gen.example.js.map +1 -0
- package/dist/constants/api-url.gen.js +4 -0
- package/dist/constants/api-url.gen.js.map +1 -0
- package/dist/constants/index.d.ts +203 -0
- package/dist/constants/index.d.ts.map +1 -0
- package/dist/constants/index.js +236 -0
- package/dist/constants/index.js.map +1 -0
- package/dist/constants/sessionLifecycle.d.ts +22 -0
- package/dist/constants/sessionLifecycle.d.ts.map +1 -0
- package/dist/constants/sessionLifecycle.js +21 -0
- package/dist/constants/sessionLifecycle.js.map +1 -0
- package/dist/index.d.ts +15 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +72 -0
- package/dist/index.js.map +1 -0
- package/dist/security/index.d.ts +118 -0
- package/dist/security/index.d.ts.map +1 -0
- package/dist/security/index.js +345 -0
- package/dist/security/index.js.map +1 -0
- package/dist/security/request-body.d.ts +16 -0
- package/dist/security/request-body.d.ts.map +1 -0
- package/dist/security/request-body.js +84 -0
- package/dist/security/request-body.js.map +1 -0
- package/dist/security/signing.d.ts +127 -0
- package/dist/security/signing.d.ts.map +1 -0
- package/dist/security/signing.js +297 -0
- package/dist/security/signing.js.map +1 -0
- package/dist/types/index.d.ts +487 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/index.js +19 -0
- package/dist/types/index.js.map +1 -0
- package/dist/utils/index.d.ts +211 -0
- package/dist/utils/index.d.ts.map +1 -0
- package/dist/utils/index.js +600 -0
- package/dist/utils/index.js.map +1 -0
- package/package.json +53 -0
|
@@ -0,0 +1,487 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Core types for Devora SDKs
|
|
3
|
+
* @module @devorash/core/types
|
|
4
|
+
*/
|
|
5
|
+
/**
|
|
6
|
+
* Normalized request object passed to customer handlers
|
|
7
|
+
*/
|
|
8
|
+
export interface DevoraRequest<TParams extends Record<string, string> = Record<string, string>, TQuery extends Record<string, string | string[] | undefined> = Record<string, string | string[] | undefined>, TBody = unknown> {
|
|
9
|
+
/** HTTP method (GET, POST, etc.) */
|
|
10
|
+
method: string;
|
|
11
|
+
/** Request path */
|
|
12
|
+
path: string;
|
|
13
|
+
/** URL parameters (e.g., :id in /impersonate/:id) */
|
|
14
|
+
params: TParams;
|
|
15
|
+
/** Query string parameters */
|
|
16
|
+
query: TQuery;
|
|
17
|
+
/** Request body (for POST, PUT, etc.) */
|
|
18
|
+
body: TBody;
|
|
19
|
+
/** Request headers */
|
|
20
|
+
headers: Record<string, string | string[] | undefined>;
|
|
21
|
+
/** Organization ID from Devora */
|
|
22
|
+
orgId: string;
|
|
23
|
+
/** API Key ID from Devora */
|
|
24
|
+
keyId: string;
|
|
25
|
+
/** Session ID for impersonation requests */
|
|
26
|
+
sessionId?: string;
|
|
27
|
+
/**
|
|
28
|
+
* Trusted impersonation context derived only after Devora HMAC validation.
|
|
29
|
+
*
|
|
30
|
+
* Customer handlers should prefer this over raw body fields for impersonation
|
|
31
|
+
* metadata because the SDK only populates it after request authentication.
|
|
32
|
+
*/
|
|
33
|
+
devoraContext?: DevoraImpersonationContext;
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Standard response format for SDK endpoints
|
|
37
|
+
*/
|
|
38
|
+
export interface DevoraResponse<T = unknown> {
|
|
39
|
+
/** Whether the request was successful */
|
|
40
|
+
success: boolean;
|
|
41
|
+
/** Response data (on success) */
|
|
42
|
+
data?: T;
|
|
43
|
+
/** Error message (on failure) */
|
|
44
|
+
error?: string;
|
|
45
|
+
/** Error code (on failure) */
|
|
46
|
+
errorCode?: string;
|
|
47
|
+
/** Unix timestamp of the response */
|
|
48
|
+
timestamp: number;
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* User object returned from search
|
|
52
|
+
*/
|
|
53
|
+
export interface DevoraUser {
|
|
54
|
+
/** Unique user identifier (required) */
|
|
55
|
+
id: string;
|
|
56
|
+
/** User's email address (recommended for display) */
|
|
57
|
+
email?: string;
|
|
58
|
+
/** User's display name (recommended for display) */
|
|
59
|
+
name?: string;
|
|
60
|
+
/** Optional avatar URL */
|
|
61
|
+
avatarUrl?: string;
|
|
62
|
+
/** Optional additional metadata */
|
|
63
|
+
metadata?: Record<string, unknown>;
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Request for user search endpoint
|
|
67
|
+
*/
|
|
68
|
+
export interface UserSearchRequest {
|
|
69
|
+
term: string;
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Response from user search endpoint
|
|
73
|
+
*/
|
|
74
|
+
export interface UserSearchResponse {
|
|
75
|
+
users: DevoraUser[];
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Impersonation scope levels
|
|
79
|
+
*/
|
|
80
|
+
export type ImpersonationScope = "read" | "write";
|
|
81
|
+
/**
|
|
82
|
+
* Duration window for impersonation
|
|
83
|
+
*/
|
|
84
|
+
export interface ImpersonationDuration {
|
|
85
|
+
/** Start time (ISO timestamp) */
|
|
86
|
+
from: string;
|
|
87
|
+
/** End time (ISO timestamp) */
|
|
88
|
+
to: string;
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* Request body for impersonation start
|
|
92
|
+
*/
|
|
93
|
+
export interface ImpersonationStartRequest {
|
|
94
|
+
/** Unique session ID from Devora */
|
|
95
|
+
sessionId: string;
|
|
96
|
+
/** Access scope (read-only or read-write) */
|
|
97
|
+
scope: ImpersonationScope;
|
|
98
|
+
/** Session expiration timestamp (Unix milliseconds) */
|
|
99
|
+
expiresAt?: number;
|
|
100
|
+
/** Time window for impersonation */
|
|
101
|
+
duration?: ImpersonationDuration;
|
|
102
|
+
/**
|
|
103
|
+
* Email of the agent requesting impersonation.
|
|
104
|
+
*
|
|
105
|
+
* PRIVACY NOTE: This field contains PII (Personally Identifiable Information).
|
|
106
|
+
* Ensure this value is not logged in plain text in application logs to comply
|
|
107
|
+
* with privacy requirements. Consider masking or hashing when logging.
|
|
108
|
+
*/
|
|
109
|
+
requestedBy?: string;
|
|
110
|
+
/** Agent performing the impersonation */
|
|
111
|
+
impersonator?: ImpersonationUserInfo;
|
|
112
|
+
/** Target user being impersonated */
|
|
113
|
+
targetUser?: ImpersonationUserInfo;
|
|
114
|
+
}
|
|
115
|
+
/**
|
|
116
|
+
* Response from impersonation start endpoint
|
|
117
|
+
*/
|
|
118
|
+
export interface ImpersonationStartResponse {
|
|
119
|
+
/** Authentication token for the target user */
|
|
120
|
+
token: string;
|
|
121
|
+
/** Optional initialization data for the frontend */
|
|
122
|
+
data?: Record<string, unknown>;
|
|
123
|
+
}
|
|
124
|
+
/**
|
|
125
|
+
* Request body for session termination
|
|
126
|
+
*/
|
|
127
|
+
export interface ImpersonationTerminateRequest {
|
|
128
|
+
/** Session ID to terminate */
|
|
129
|
+
sessionId: string;
|
|
130
|
+
/** Reason for termination */
|
|
131
|
+
reason: "manual" | "expired" | "terminated";
|
|
132
|
+
}
|
|
133
|
+
/**
|
|
134
|
+
* Response from termination endpoint
|
|
135
|
+
*/
|
|
136
|
+
export interface ImpersonationTerminateResponse {
|
|
137
|
+
/** Whether termination was successful */
|
|
138
|
+
success: boolean;
|
|
139
|
+
/** Optional message */
|
|
140
|
+
message?: string;
|
|
141
|
+
}
|
|
142
|
+
/**
|
|
143
|
+
* User info for impersonation (target or impersonator)
|
|
144
|
+
*/
|
|
145
|
+
export interface ImpersonationUserInfo {
|
|
146
|
+
/** User ID */
|
|
147
|
+
id: string;
|
|
148
|
+
/** User's email address */
|
|
149
|
+
email?: string;
|
|
150
|
+
/** User's display name */
|
|
151
|
+
name?: string;
|
|
152
|
+
}
|
|
153
|
+
/**
|
|
154
|
+
* Trusted impersonation context exposed to backend customer handlers.
|
|
155
|
+
*/
|
|
156
|
+
export interface DevoraImpersonationContext {
|
|
157
|
+
/**
|
|
158
|
+
* Always true. Present so the object can be stored on the customer session
|
|
159
|
+
* and handed back to `createImpersonationGuard` unchanged.
|
|
160
|
+
*/
|
|
161
|
+
isImpersonation: true;
|
|
162
|
+
/** Devora session ID */
|
|
163
|
+
sessionId: string;
|
|
164
|
+
/** Access scope */
|
|
165
|
+
scope: ImpersonationScope;
|
|
166
|
+
/** Session expiration timestamp (Unix milliseconds) */
|
|
167
|
+
expiresAt: number;
|
|
168
|
+
/** Agent performing the impersonation */
|
|
169
|
+
impersonator: ImpersonationUserInfo;
|
|
170
|
+
/** Target user being impersonated */
|
|
171
|
+
targetUser: ImpersonationUserInfo;
|
|
172
|
+
/** Same identity as `impersonator`, in the guard's canonical field name. */
|
|
173
|
+
actor: {
|
|
174
|
+
id: string;
|
|
175
|
+
};
|
|
176
|
+
/** Same identity as `targetUser`, in the guard's canonical field name. */
|
|
177
|
+
subject: {
|
|
178
|
+
id: string;
|
|
179
|
+
};
|
|
180
|
+
/** This context is a dedicated impersonation session, not customer MFA. */
|
|
181
|
+
authMethod: "devora_impersonation";
|
|
182
|
+
/** How Devora authorized the request. */
|
|
183
|
+
authorizationSource: "standard" | "self_approved" | "self_approved_read" | "break_glass";
|
|
184
|
+
/**
|
|
185
|
+
* In the impersonate request: whether Devora's project policy will record
|
|
186
|
+
* this session. In the customer's response and the stored customer session:
|
|
187
|
+
* the customer backend's own decision for this exact session.
|
|
188
|
+
*/
|
|
189
|
+
recordingAllowed: boolean;
|
|
190
|
+
}
|
|
191
|
+
/**
|
|
192
|
+
* Payload passed to onImpersonate callback
|
|
193
|
+
*/
|
|
194
|
+
export interface ImpersonatePayload {
|
|
195
|
+
/** Authentication token from customer backend */
|
|
196
|
+
token: string;
|
|
197
|
+
/** Optional initialization data */
|
|
198
|
+
data?: Record<string, unknown>;
|
|
199
|
+
/** Access scope */
|
|
200
|
+
scope: ImpersonationScope;
|
|
201
|
+
/** Session expiration time (ISO timestamp) */
|
|
202
|
+
expiresAt: string;
|
|
203
|
+
/** Unique session ID */
|
|
204
|
+
sessionId: string;
|
|
205
|
+
/** Target user being impersonated */
|
|
206
|
+
targetUser?: ImpersonationUserInfo;
|
|
207
|
+
/** Agent performing the impersonation */
|
|
208
|
+
impersonator?: ImpersonationUserInfo;
|
|
209
|
+
}
|
|
210
|
+
/**
|
|
211
|
+
* Callback type for impersonation events
|
|
212
|
+
*/
|
|
213
|
+
export type ImpersonateCallback = (payload: ImpersonatePayload) => void | Promise<void>;
|
|
214
|
+
/**
|
|
215
|
+
* Callback type for session end events
|
|
216
|
+
*/
|
|
217
|
+
export type SessionEndCallback = (reason: string) => void | Promise<void>;
|
|
218
|
+
/**
|
|
219
|
+
* Frontend SDK configuration
|
|
220
|
+
*/
|
|
221
|
+
export interface FrontendSDKConfig {
|
|
222
|
+
/** Public client API key (pk_client_live_xxx) */
|
|
223
|
+
apiKey: string;
|
|
224
|
+
/** Enable debug logging */
|
|
225
|
+
debug?: boolean;
|
|
226
|
+
/** Callback for impersonation start */
|
|
227
|
+
onImpersonate?: ImpersonateCallback;
|
|
228
|
+
/** Callback for session end */
|
|
229
|
+
onSessionEnd?: SessionEndCallback;
|
|
230
|
+
/** Custom error handlers */
|
|
231
|
+
onError?: ErrorHandlers;
|
|
232
|
+
}
|
|
233
|
+
/**
|
|
234
|
+
* Payload error information
|
|
235
|
+
*/
|
|
236
|
+
export interface PayloadError {
|
|
237
|
+
/** Error code */
|
|
238
|
+
code: "invalid_format" | "missing_fields" | "expired" | "unknown";
|
|
239
|
+
/** Human-readable error message */
|
|
240
|
+
message: string;
|
|
241
|
+
}
|
|
242
|
+
/**
|
|
243
|
+
* Error handler configuration
|
|
244
|
+
*/
|
|
245
|
+
export interface ErrorHandlers {
|
|
246
|
+
tokenValidationFailed?: (error: Error) => void;
|
|
247
|
+
scopeViolation?: (violation: ScopeViolation) => void;
|
|
248
|
+
sessionExpired?: () => void;
|
|
249
|
+
networkError?: (error: Error) => void;
|
|
250
|
+
/** Called when the impersonation payload in the URL is invalid or expired */
|
|
251
|
+
payloadError?: (error: PayloadError) => void;
|
|
252
|
+
}
|
|
253
|
+
/**
|
|
254
|
+
* Masking profile chosen by the Devora administrator for a project.
|
|
255
|
+
*
|
|
256
|
+
* - `full`: every text node and input value is masked and media is blocked.
|
|
257
|
+
* The replay stays structurally intact (layout, navigation, clicks) but no
|
|
258
|
+
* content is captured unless the administrator unmasks a region or selector.
|
|
259
|
+
* - `partial`: input values are masked; page text and media are visible.
|
|
260
|
+
* - `minimal`: only sensitive fields are masked (password, one-time code,
|
|
261
|
+
* payment card and SSN-like inputs detected from type, autocomplete and
|
|
262
|
+
* name/id/aria-label heuristics).
|
|
263
|
+
*
|
|
264
|
+
* Sensitive fields stay masked in every profile and cannot be unmasked. The
|
|
265
|
+
* profile is never configured in the SDK; it arrives with the session.
|
|
266
|
+
*/
|
|
267
|
+
export type DevoraMaskingProfile = "full" | "partial" | "minimal";
|
|
268
|
+
/** Whether a capture channel is on for a session scope. */
|
|
269
|
+
export type DevoraCapturePolicy = "disabled" | "write_only" | "all_sessions";
|
|
270
|
+
/**
|
|
271
|
+
* The capture policy a session was created under, decided in the Devora
|
|
272
|
+
* dashboard and delivered to the browser SDK with the session. A later policy
|
|
273
|
+
* change applies to new sessions only.
|
|
274
|
+
*/
|
|
275
|
+
export interface DevoraCaptureSnapshot {
|
|
276
|
+
/** Server-owned migration marker. New sessions use Settings exclusively. */
|
|
277
|
+
settingsOnly?: boolean;
|
|
278
|
+
policyVersion: number;
|
|
279
|
+
recordingPolicy: DevoraCapturePolicy;
|
|
280
|
+
activityPolicy: DevoraCapturePolicy;
|
|
281
|
+
recordingMaskingProfile: DevoraMaskingProfile;
|
|
282
|
+
/** Block media elements (img, svg, video, audio, object, picture, embed). Always true for `full`. */
|
|
283
|
+
recordingBlockMedia: boolean;
|
|
284
|
+
/** Administrator selectors whose text and input values are masked in any profile. */
|
|
285
|
+
recordingMaskSelectors: string[];
|
|
286
|
+
/** Administrator selectors removed from the replay entirely. */
|
|
287
|
+
recordingBlockSelectors: string[];
|
|
288
|
+
/** `data-devora-region` names the administrator chose to reveal. */
|
|
289
|
+
recordingUnmaskRegions: string[];
|
|
290
|
+
/** Administrator selectors revealed despite the profile (never sensitive fields). */
|
|
291
|
+
recordingUnmaskSelectors: string[];
|
|
292
|
+
/**
|
|
293
|
+
* Capture console warnings/errors in the replay and uncaught errors in the
|
|
294
|
+
* activity timeline. Under the full profile only error categories are kept
|
|
295
|
+
* and console arguments are masked; otherwise text is redacted and bounded.
|
|
296
|
+
* Stack traces are never recorded.
|
|
297
|
+
*/
|
|
298
|
+
consoleErrorCaptureEnabled: boolean;
|
|
299
|
+
/** Accept `logAction` custom events in the activity timeline. */
|
|
300
|
+
activityCustomEventsEnabled: boolean;
|
|
301
|
+
}
|
|
302
|
+
/**
|
|
303
|
+
* Labels developers may place in their UI for administrator selectors/regions.
|
|
304
|
+
* New sessions apply them only when selected in Devora Settings; legacy sessions
|
|
305
|
+
* preserve their original hardening behavior. Labels cannot reveal content alone.
|
|
306
|
+
*/
|
|
307
|
+
export declare const DEVORA_PRIVACY_MARKERS: {
|
|
308
|
+
readonly MASK_CLASS: "devora-mask";
|
|
309
|
+
readonly BLOCK_CLASS: "devora-block";
|
|
310
|
+
readonly IGNORE_CLASS: "devora-ignore";
|
|
311
|
+
readonly MASK_ATTR: "data-devora-mask";
|
|
312
|
+
readonly BLOCK_ATTR: "data-devora-block";
|
|
313
|
+
readonly IGNORE_ATTR: "data-devora-ignore";
|
|
314
|
+
readonly REGION_ATTR: "data-devora-region";
|
|
315
|
+
};
|
|
316
|
+
/**
|
|
317
|
+
* Scope violation event
|
|
318
|
+
*/
|
|
319
|
+
export interface ScopeViolation {
|
|
320
|
+
/** Type of violation */
|
|
321
|
+
type: "write_attempt";
|
|
322
|
+
/** HTTP method that was blocked */
|
|
323
|
+
method: string;
|
|
324
|
+
/** URL that was blocked */
|
|
325
|
+
url: string;
|
|
326
|
+
/** Timestamp of the violation */
|
|
327
|
+
timestamp: number;
|
|
328
|
+
}
|
|
329
|
+
/**
|
|
330
|
+
* Backend SDK configuration
|
|
331
|
+
*/
|
|
332
|
+
export interface BackendSDKConfig {
|
|
333
|
+
/** Public server API key identifier (pk_server_*) */
|
|
334
|
+
apiKey: string;
|
|
335
|
+
/** Secret API key beginning with sk_server_ (backend only; keep out of source control) */
|
|
336
|
+
secretKey: string;
|
|
337
|
+
/** Devora organization ID bound into every signed request */
|
|
338
|
+
orgId: string;
|
|
339
|
+
/** Enable debug logging */
|
|
340
|
+
debug?: boolean;
|
|
341
|
+
/** Timestamp tolerance in seconds (default: 300) */
|
|
342
|
+
timestampTolerance?: number;
|
|
343
|
+
}
|
|
344
|
+
/**
|
|
345
|
+
* Route handler function type
|
|
346
|
+
*/
|
|
347
|
+
export type RouteHandler<TRequest = DevoraRequest, TResponse = unknown> = (request: TRequest) => Promise<TResponse> | TResponse;
|
|
348
|
+
/**
|
|
349
|
+
* Route definition for SDK endpoints
|
|
350
|
+
*/
|
|
351
|
+
export interface RouteDefinition<TRequest = DevoraRequest, TResponse = unknown> {
|
|
352
|
+
/** Endpoint path (e.g., /user/search) */
|
|
353
|
+
path: string;
|
|
354
|
+
/** HTTP method */
|
|
355
|
+
method: "GET" | "POST" | "DELETE" | "PUT" | "PATCH";
|
|
356
|
+
/** Route handler function */
|
|
357
|
+
handler: RouteHandler<TRequest, TResponse>;
|
|
358
|
+
}
|
|
359
|
+
/**
|
|
360
|
+
* HTTP methods for whitelist endpoints (write methods only)
|
|
361
|
+
*/
|
|
362
|
+
export type WhitelistMethod = "POST" | "PUT" | "PATCH" | "DELETE";
|
|
363
|
+
/**
|
|
364
|
+
* HTTP methods for blacklist endpoints (all methods including wildcard)
|
|
365
|
+
*/
|
|
366
|
+
export type BlacklistMethod = "GET" | "POST" | "PUT" | "PATCH" | "DELETE" | "*";
|
|
367
|
+
/**
|
|
368
|
+
* Whitelist entry - allows write methods in read-only mode
|
|
369
|
+
* These POST/PUT/PATCH/DELETE endpoints are allowed during read-only impersonation
|
|
370
|
+
*/
|
|
371
|
+
export interface SafeReadEndpoint {
|
|
372
|
+
/** HTTP method */
|
|
373
|
+
method: WhitelistMethod;
|
|
374
|
+
/** URL pattern (supports glob wildcards like /api/filters/*) */
|
|
375
|
+
pattern: string;
|
|
376
|
+
/** Optional description */
|
|
377
|
+
description?: string;
|
|
378
|
+
}
|
|
379
|
+
/**
|
|
380
|
+
* Blacklist entry - blocks endpoints in ANY impersonation mode
|
|
381
|
+
* These endpoints are ALWAYS blocked during impersonation (even in write mode)
|
|
382
|
+
*/
|
|
383
|
+
export interface BlockedEndpoint {
|
|
384
|
+
/** HTTP method (* blocks all methods) */
|
|
385
|
+
method: BlacklistMethod;
|
|
386
|
+
/** URL pattern (supports glob wildcards like /api/payments/*) */
|
|
387
|
+
pattern: string;
|
|
388
|
+
/** Optional description */
|
|
389
|
+
description?: string;
|
|
390
|
+
}
|
|
391
|
+
/**
|
|
392
|
+
* Response from /api/sdk/scope-config endpoint
|
|
393
|
+
*/
|
|
394
|
+
export interface ScopeConfigResponse {
|
|
395
|
+
/** Configuration version (for cache invalidation) */
|
|
396
|
+
version: number;
|
|
397
|
+
/** Whitelist: Safe endpoints allowed in read-only mode */
|
|
398
|
+
safeReadEndpoints: SafeReadEndpoint[];
|
|
399
|
+
/** Blacklist: Endpoints blocked during ANY impersonation */
|
|
400
|
+
blockedEndpoints: BlockedEndpoint[];
|
|
401
|
+
/** Cache expiration timestamp (Unix ms) */
|
|
402
|
+
cachedUntil: number;
|
|
403
|
+
}
|
|
404
|
+
/**
|
|
405
|
+
* Log event types
|
|
406
|
+
*/
|
|
407
|
+
export type LogEventType = "session_start" | "session_end" | "navigation" | "click" | "form_submit" | "api_call" | "error" | "scope_violation";
|
|
408
|
+
/**
|
|
409
|
+
* Log event structure
|
|
410
|
+
*/
|
|
411
|
+
export interface LogEvent {
|
|
412
|
+
/** Event type */
|
|
413
|
+
type: LogEventType;
|
|
414
|
+
/** Unix timestamp in milliseconds */
|
|
415
|
+
timestamp: number;
|
|
416
|
+
/** Session ID */
|
|
417
|
+
sessionId: string;
|
|
418
|
+
/** Page/route path */
|
|
419
|
+
path?: string;
|
|
420
|
+
/** Action description */
|
|
421
|
+
action?: string;
|
|
422
|
+
/** Additional metadata */
|
|
423
|
+
metadata?: Record<string, unknown>;
|
|
424
|
+
}
|
|
425
|
+
/**
|
|
426
|
+
* SDK metadata
|
|
427
|
+
*/
|
|
428
|
+
export interface SDKInfo {
|
|
429
|
+
/** SDK name */
|
|
430
|
+
name: string;
|
|
431
|
+
/** SDK version */
|
|
432
|
+
version: string;
|
|
433
|
+
/** SDK type */
|
|
434
|
+
type: "backend" | "frontend" | "adapter";
|
|
435
|
+
/** Runtime environment */
|
|
436
|
+
runtime?: "node" | "browser" | "edge";
|
|
437
|
+
}
|
|
438
|
+
/**
|
|
439
|
+
* Health check response
|
|
440
|
+
*/
|
|
441
|
+
export interface HealthCheckResponse {
|
|
442
|
+
/** SDK status */
|
|
443
|
+
status: "healthy" | "degraded" | "unhealthy";
|
|
444
|
+
/** SDK info */
|
|
445
|
+
sdk: SDKInfo;
|
|
446
|
+
/** Request statistics */
|
|
447
|
+
stats?: {
|
|
448
|
+
totalRequests: number;
|
|
449
|
+
successfulRequests: number;
|
|
450
|
+
failedRequests: number;
|
|
451
|
+
securityErrors: number;
|
|
452
|
+
};
|
|
453
|
+
/** Registered endpoints */
|
|
454
|
+
endpoints?: Record<string, string>;
|
|
455
|
+
}
|
|
456
|
+
/**
|
|
457
|
+
* Connection test response
|
|
458
|
+
*/
|
|
459
|
+
export interface ConnectionTestResponse {
|
|
460
|
+
status: "success";
|
|
461
|
+
message: string;
|
|
462
|
+
timestamp: number;
|
|
463
|
+
/** Organization ID from request headers (informational) */
|
|
464
|
+
orgId: string;
|
|
465
|
+
keyId: string;
|
|
466
|
+
sdk: SDKInfo;
|
|
467
|
+
security: {
|
|
468
|
+
hmacValidated: boolean;
|
|
469
|
+
timestampValid: boolean;
|
|
470
|
+
};
|
|
471
|
+
}
|
|
472
|
+
/** What the customer route returns to the browser SDK for a tab restore. */
|
|
473
|
+
export type SessionBridgeResult = {
|
|
474
|
+
status: "none";
|
|
475
|
+
} | {
|
|
476
|
+
status: "resume";
|
|
477
|
+
code: string;
|
|
478
|
+
} | {
|
|
479
|
+
status: "blocked";
|
|
480
|
+
reason: "control_plane_unavailable" | "session_invalid";
|
|
481
|
+
};
|
|
482
|
+
/** One-time resume code minted by Devora for a specific tab and origin. */
|
|
483
|
+
export interface BrowserResumeCode {
|
|
484
|
+
code: string;
|
|
485
|
+
expiresAt: number;
|
|
486
|
+
}
|
|
487
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/types/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Core types for Devora SDKs
|
|
3
|
+
* @module @devorash/core/types
|
|
4
|
+
*/
|
|
5
|
+
/**
|
|
6
|
+
* Labels developers may place in their UI for administrator selectors/regions.
|
|
7
|
+
* New sessions apply them only when selected in Devora Settings; legacy sessions
|
|
8
|
+
* preserve their original hardening behavior. Labels cannot reveal content alone.
|
|
9
|
+
*/
|
|
10
|
+
export const DEVORA_PRIVACY_MARKERS = {
|
|
11
|
+
MASK_CLASS: "devora-mask",
|
|
12
|
+
BLOCK_CLASS: "devora-block",
|
|
13
|
+
IGNORE_CLASS: "devora-ignore",
|
|
14
|
+
MASK_ATTR: "data-devora-mask",
|
|
15
|
+
BLOCK_ATTR: "data-devora-block",
|
|
16
|
+
IGNORE_ATTR: "data-devora-ignore",
|
|
17
|
+
REGION_ATTR: "data-devora-region",
|
|
18
|
+
};
|
|
19
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/types/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAwVH;;;;GAIG;AACH,MAAM,CAAC,MAAM,sBAAsB,GAAG;IACrC,UAAU,EAAE,aAAa;IACzB,WAAW,EAAE,cAAc;IAC3B,YAAY,EAAE,eAAe;IAC7B,SAAS,EAAE,kBAAkB;IAC7B,UAAU,EAAE,mBAAmB;IAC/B,WAAW,EAAE,oBAAoB;IACjC,WAAW,EAAE,oBAAoB;CACxB,CAAA"}
|