@boshyxd/rosentry 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/out/index.d.ts +32 -0
- package/out/init.luau +229 -0
- package/out/internal/AutoCapture.d.ts +13 -0
- package/out/internal/AutoCapture.luau +74 -0
- package/out/internal/Capture.d.ts +17 -0
- package/out/internal/Capture.luau +142 -0
- package/out/internal/Config.d.ts +12 -0
- package/out/internal/Config.luau +116 -0
- package/out/internal/Queue.d.ts +19 -0
- package/out/internal/Queue.luau +120 -0
- package/out/internal/Scope.d.ts +25 -0
- package/out/internal/Scope.luau +155 -0
- package/out/internal/Trace.d.ts +21 -0
- package/out/internal/Trace.luau +299 -0
- package/out/internal/Transport.d.ts +13 -0
- package/out/internal/Transport.luau +140 -0
- package/out/internal/util.d.ts +9 -0
- package/out/internal/util.luau +68 -0
- package/out/types.d.ts +94 -0
- package/out/types.luau +2 -0
- package/package.json +34 -0
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
-- Compiled with roblox-ts v3.0.0
|
|
2
|
+
local TS = _G[script]
|
|
3
|
+
local _services = TS.import(script, TS.getModule(script, "@rbxts", "services"))
|
|
4
|
+
local HttpService = _services.HttpService
|
|
5
|
+
local ReplicatedStorage = _services.ReplicatedStorage
|
|
6
|
+
local RunService = _services.RunService
|
|
7
|
+
local IS_SERVER = RunService:IsServer()
|
|
8
|
+
local IS_CLIENT = RunService:IsClient()
|
|
9
|
+
local REMOTE_EVENT_NAME = "RoSentryRelay"
|
|
10
|
+
local Transport
|
|
11
|
+
do
|
|
12
|
+
Transport = setmetatable({}, {
|
|
13
|
+
__tostring = function()
|
|
14
|
+
return "Transport"
|
|
15
|
+
end,
|
|
16
|
+
})
|
|
17
|
+
Transport.__index = Transport
|
|
18
|
+
function Transport.new(...)
|
|
19
|
+
local self = setmetatable({}, Transport)
|
|
20
|
+
return self:constructor(...) or self
|
|
21
|
+
end
|
|
22
|
+
function Transport:constructor(config)
|
|
23
|
+
self._config = config
|
|
24
|
+
end
|
|
25
|
+
function Transport:_debug(...)
|
|
26
|
+
local args = { ... }
|
|
27
|
+
if self._config:get("debug") then
|
|
28
|
+
print("[RoSentry:Transport]", unpack(args))
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
function Transport:_getRemoteEvent()
|
|
32
|
+
if self._remoteEvent then
|
|
33
|
+
return self._remoteEvent
|
|
34
|
+
end
|
|
35
|
+
if IS_SERVER then
|
|
36
|
+
local existing = ReplicatedStorage:FindFirstChild(REMOTE_EVENT_NAME)
|
|
37
|
+
if existing and existing:IsA("RemoteEvent") then
|
|
38
|
+
self._remoteEvent = existing
|
|
39
|
+
else
|
|
40
|
+
local remote = Instance.new("RemoteEvent")
|
|
41
|
+
remote.Name = REMOTE_EVENT_NAME
|
|
42
|
+
remote.Parent = ReplicatedStorage
|
|
43
|
+
self._remoteEvent = remote
|
|
44
|
+
end
|
|
45
|
+
else
|
|
46
|
+
local found = ReplicatedStorage:WaitForChild(REMOTE_EVENT_NAME, 10)
|
|
47
|
+
if found and found:IsA("RemoteEvent") then
|
|
48
|
+
self._remoteEvent = found
|
|
49
|
+
else
|
|
50
|
+
self:_debug("RemoteEvent not found - server may not have RoSentry initialized")
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
return self._remoteEvent
|
|
54
|
+
end
|
|
55
|
+
function Transport:setupServerRelay()
|
|
56
|
+
if not IS_SERVER then
|
|
57
|
+
return nil
|
|
58
|
+
end
|
|
59
|
+
local remote = self:_getRemoteEvent()
|
|
60
|
+
if not remote then
|
|
61
|
+
return nil
|
|
62
|
+
end
|
|
63
|
+
remote.OnServerEvent:Connect(function(player, payload)
|
|
64
|
+
self:_debug("Received client errors from", player.Name)
|
|
65
|
+
local p = payload
|
|
66
|
+
if p.errors then
|
|
67
|
+
for _, errorData in p.errors do
|
|
68
|
+
errorData.context = errorData.context or {}
|
|
69
|
+
errorData.context.relayedFrom = "client"
|
|
70
|
+
errorData.context.playerName = player.Name
|
|
71
|
+
errorData.context.playerId = player.UserId
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
self:_sendHttp(payload)
|
|
75
|
+
end)
|
|
76
|
+
self:_debug("Server relay setup complete")
|
|
77
|
+
end
|
|
78
|
+
function Transport:_sendHttp(payload)
|
|
79
|
+
local apiKey = self._config:get("apiKey")
|
|
80
|
+
local endpoint = self._config:get("endpoint")
|
|
81
|
+
if apiKey == "" then
|
|
82
|
+
self:_debug("No API key configured")
|
|
83
|
+
return false
|
|
84
|
+
end
|
|
85
|
+
local success, result = pcall(function()
|
|
86
|
+
local response = HttpService:RequestAsync({
|
|
87
|
+
Url = endpoint,
|
|
88
|
+
Method = "POST",
|
|
89
|
+
Headers = {
|
|
90
|
+
["Content-Type"] = "application/json",
|
|
91
|
+
["x-api-key"] = apiKey,
|
|
92
|
+
},
|
|
93
|
+
Body = HttpService:JSONEncode(payload),
|
|
94
|
+
})
|
|
95
|
+
return response.Success
|
|
96
|
+
end)
|
|
97
|
+
if not success then
|
|
98
|
+
self:_debug("Request failed:", result)
|
|
99
|
+
return false
|
|
100
|
+
end
|
|
101
|
+
self:_debug("Request sent successfully")
|
|
102
|
+
return result
|
|
103
|
+
end
|
|
104
|
+
function Transport:_sendRemote(payload)
|
|
105
|
+
local remote = self:_getRemoteEvent()
|
|
106
|
+
if not remote then
|
|
107
|
+
self:_debug("Cannot send - RemoteEvent not available")
|
|
108
|
+
return false
|
|
109
|
+
end
|
|
110
|
+
local success, err = pcall(function()
|
|
111
|
+
remote:FireServer(payload)
|
|
112
|
+
end)
|
|
113
|
+
if not success then
|
|
114
|
+
self:_debug("Remote send failed:", err)
|
|
115
|
+
return false
|
|
116
|
+
end
|
|
117
|
+
self:_debug("Sent to server via RemoteEvent")
|
|
118
|
+
return true
|
|
119
|
+
end
|
|
120
|
+
function Transport:send(payload)
|
|
121
|
+
if not self._config:isEnabled() then
|
|
122
|
+
self:_debug("SDK disabled, skipping send")
|
|
123
|
+
return false
|
|
124
|
+
end
|
|
125
|
+
if IS_SERVER then
|
|
126
|
+
return self:_sendHttp(payload)
|
|
127
|
+
elseif IS_CLIENT then
|
|
128
|
+
return self:_sendRemote(payload)
|
|
129
|
+
end
|
|
130
|
+
return false
|
|
131
|
+
end
|
|
132
|
+
function Transport:sendAsync(payload)
|
|
133
|
+
task.spawn(function()
|
|
134
|
+
self:send(payload)
|
|
135
|
+
end)
|
|
136
|
+
end
|
|
137
|
+
end
|
|
138
|
+
return {
|
|
139
|
+
Transport = Transport,
|
|
140
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export declare function generateUUID(): string;
|
|
2
|
+
export declare function getJobId(): string;
|
|
3
|
+
export declare function getPlaceId(): number;
|
|
4
|
+
export declare function getGameVersion(): string;
|
|
5
|
+
export declare function isServer(): boolean;
|
|
6
|
+
export declare function sanitizeStackTrace(trace?: string): string | undefined;
|
|
7
|
+
export declare function deepCopy<T>(original: T): T;
|
|
8
|
+
export declare function merge<T extends object>(base: T, override?: Partial<T>): T;
|
|
9
|
+
export declare function getTimestamp(): string;
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
-- Compiled with roblox-ts v3.0.0
|
|
2
|
+
local TS = _G[script]
|
|
3
|
+
local RunService = TS.import(script, TS.getModule(script, "@rbxts", "services")).RunService
|
|
4
|
+
local function generateUUID()
|
|
5
|
+
local template = "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx"
|
|
6
|
+
return (string.gsub(template, "[xy]", function(c)
|
|
7
|
+
local v = if c == "x" then math.random(0, 15) else math.random(8, 11)
|
|
8
|
+
return string.format("%x", v)
|
|
9
|
+
end))
|
|
10
|
+
end
|
|
11
|
+
local function getJobId()
|
|
12
|
+
return if game.JobId ~= "" then game.JobId else "local"
|
|
13
|
+
end
|
|
14
|
+
local function getPlaceId()
|
|
15
|
+
return game.PlaceId
|
|
16
|
+
end
|
|
17
|
+
local function getGameVersion()
|
|
18
|
+
return tostring(game.PlaceVersion)
|
|
19
|
+
end
|
|
20
|
+
local function isServer()
|
|
21
|
+
return RunService:IsServer()
|
|
22
|
+
end
|
|
23
|
+
local function sanitizeStackTrace(trace)
|
|
24
|
+
if trace == nil then
|
|
25
|
+
return nil
|
|
26
|
+
end
|
|
27
|
+
local lines = {}
|
|
28
|
+
for line in string.gmatch(trace, "[^\n]+") do
|
|
29
|
+
local cleaned = string.gsub(line, "^%s+", "")
|
|
30
|
+
table.insert(lines, cleaned)
|
|
31
|
+
end
|
|
32
|
+
return table.concat(lines, "\n")
|
|
33
|
+
end
|
|
34
|
+
local function deepCopy(original)
|
|
35
|
+
local _original = original
|
|
36
|
+
if not (type(_original) == "table") then
|
|
37
|
+
return original
|
|
38
|
+
end
|
|
39
|
+
local copy = {}
|
|
40
|
+
for key, value in pairs(original) do
|
|
41
|
+
copy[key] = deepCopy(value)
|
|
42
|
+
end
|
|
43
|
+
return copy
|
|
44
|
+
end
|
|
45
|
+
local function merge(base, override)
|
|
46
|
+
if override == nil then
|
|
47
|
+
return base
|
|
48
|
+
end
|
|
49
|
+
local result = deepCopy(base)
|
|
50
|
+
for key, value in pairs(override) do
|
|
51
|
+
result[key] = value
|
|
52
|
+
end
|
|
53
|
+
return result
|
|
54
|
+
end
|
|
55
|
+
local function getTimestamp()
|
|
56
|
+
return DateTime.now():ToIsoDate()
|
|
57
|
+
end
|
|
58
|
+
return {
|
|
59
|
+
generateUUID = generateUUID,
|
|
60
|
+
getJobId = getJobId,
|
|
61
|
+
getPlaceId = getPlaceId,
|
|
62
|
+
getGameVersion = getGameVersion,
|
|
63
|
+
isServer = isServer,
|
|
64
|
+
sanitizeStackTrace = sanitizeStackTrace,
|
|
65
|
+
deepCopy = deepCopy,
|
|
66
|
+
merge = merge,
|
|
67
|
+
getTimestamp = getTimestamp,
|
|
68
|
+
}
|
package/out/types.d.ts
ADDED
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
export type LogLevel = "debug" | "info" | "warn" | "error";
|
|
2
|
+
export interface Config {
|
|
3
|
+
apiKey: string;
|
|
4
|
+
environment: string;
|
|
5
|
+
endpoint: string;
|
|
6
|
+
enabled: boolean;
|
|
7
|
+
sampleRate: number;
|
|
8
|
+
maxBreadcrumbs: number;
|
|
9
|
+
maxQueueSize: number;
|
|
10
|
+
flushInterval: number;
|
|
11
|
+
debug: boolean;
|
|
12
|
+
autoCapture: boolean;
|
|
13
|
+
captureWarnings: boolean;
|
|
14
|
+
beforeSend?: (errorData: ErrorData) => ErrorData | undefined;
|
|
15
|
+
}
|
|
16
|
+
export interface ConfigOptions {
|
|
17
|
+
apiKey?: string;
|
|
18
|
+
environment?: string;
|
|
19
|
+
endpoint?: string;
|
|
20
|
+
enabled?: boolean;
|
|
21
|
+
sampleRate?: number;
|
|
22
|
+
maxBreadcrumbs?: number;
|
|
23
|
+
maxQueueSize?: number;
|
|
24
|
+
flushInterval?: number;
|
|
25
|
+
debug?: boolean;
|
|
26
|
+
autoCapture?: boolean;
|
|
27
|
+
captureWarnings?: boolean;
|
|
28
|
+
beforeSend?: (errorData: ErrorData) => ErrorData | undefined;
|
|
29
|
+
}
|
|
30
|
+
export interface Breadcrumb {
|
|
31
|
+
category: string;
|
|
32
|
+
message: string;
|
|
33
|
+
data?: Map<string, unknown>;
|
|
34
|
+
timestamp: number;
|
|
35
|
+
}
|
|
36
|
+
export interface UserContext {
|
|
37
|
+
id: number;
|
|
38
|
+
name?: string;
|
|
39
|
+
data: Map<string, unknown>;
|
|
40
|
+
}
|
|
41
|
+
export interface ErrorContext {
|
|
42
|
+
userData?: Map<string, unknown>;
|
|
43
|
+
custom?: Map<string, unknown>;
|
|
44
|
+
breadcrumbs?: Breadcrumb[];
|
|
45
|
+
isServer: boolean;
|
|
46
|
+
script?: string;
|
|
47
|
+
trace?: Map<string, unknown>;
|
|
48
|
+
}
|
|
49
|
+
export interface ErrorData {
|
|
50
|
+
message: string;
|
|
51
|
+
level: LogLevel;
|
|
52
|
+
stack_trace?: string;
|
|
53
|
+
user_id?: number;
|
|
54
|
+
player_name?: string;
|
|
55
|
+
trace_id?: string;
|
|
56
|
+
job_id: string;
|
|
57
|
+
place_id: number;
|
|
58
|
+
game_version: string;
|
|
59
|
+
environment: string;
|
|
60
|
+
context: ErrorContext;
|
|
61
|
+
tags: string[];
|
|
62
|
+
timestamp: string;
|
|
63
|
+
}
|
|
64
|
+
export interface CaptureOptions {
|
|
65
|
+
level?: LogLevel;
|
|
66
|
+
stackTrace?: string;
|
|
67
|
+
context?: Map<string, unknown>;
|
|
68
|
+
tags?: string[];
|
|
69
|
+
}
|
|
70
|
+
export interface ScopeInterface {
|
|
71
|
+
setUser: (userId: number, playerName?: string, data?: Map<string, unknown>) => void;
|
|
72
|
+
clearUser: () => void;
|
|
73
|
+
addBreadcrumb: (category: string, message: string, data?: Map<string, unknown>) => void;
|
|
74
|
+
setTag: (key: string, value: string) => void;
|
|
75
|
+
setExtra: (key: string, value: unknown) => void;
|
|
76
|
+
}
|
|
77
|
+
export type TraceStatus = "active" | "completed" | "error" | "timeout";
|
|
78
|
+
export interface TraceOptions {
|
|
79
|
+
maxEvents?: number;
|
|
80
|
+
expiresIn?: number;
|
|
81
|
+
tags?: string[];
|
|
82
|
+
metadata?: Map<string, unknown>;
|
|
83
|
+
}
|
|
84
|
+
export interface Trace {
|
|
85
|
+
id: string;
|
|
86
|
+
name: string;
|
|
87
|
+
log: (message: string, data?: Map<string, unknown>) => void;
|
|
88
|
+
debug: (message: string, data?: Map<string, unknown>) => void;
|
|
89
|
+
info: (message: string, data?: Map<string, unknown>) => void;
|
|
90
|
+
warn: (message: string, data?: Map<string, unknown>) => void;
|
|
91
|
+
error: (message: string, data?: Map<string, unknown>) => void;
|
|
92
|
+
isActive: () => boolean;
|
|
93
|
+
finish: (status?: TraceStatus) => void;
|
|
94
|
+
}
|
package/out/types.luau
ADDED
package/package.json
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@boshyxd/rosentry",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Error monitoring SDK for Roblox",
|
|
5
|
+
"main": "out/init.lua",
|
|
6
|
+
"types": "out/index.d.ts",
|
|
7
|
+
"typings": "out/index.d.ts",
|
|
8
|
+
"repository": {
|
|
9
|
+
"type": "git",
|
|
10
|
+
"url": "https://github.com/boshyxd/rosentry"
|
|
11
|
+
},
|
|
12
|
+
"publishConfig": {
|
|
13
|
+
"access": "public"
|
|
14
|
+
},
|
|
15
|
+
"scripts": {
|
|
16
|
+
"build": "rbxtsc",
|
|
17
|
+
"build:dev": "rbxtsc --type game -i include",
|
|
18
|
+
"watch": "rbxtsc -w",
|
|
19
|
+
"watch:dev": "rbxtsc -w --type game -i include"
|
|
20
|
+
},
|
|
21
|
+
"keywords": ["roblox", "roblox-ts", "error-monitoring", "sentry"],
|
|
22
|
+
"author": "boshyxd",
|
|
23
|
+
"license": "MIT",
|
|
24
|
+
"dependencies": {
|
|
25
|
+
"@rbxts/services": "^1.5.1"
|
|
26
|
+
},
|
|
27
|
+
"devDependencies": {
|
|
28
|
+
"@rbxts/compiler-types": "^3.0.0-types.0",
|
|
29
|
+
"@rbxts/types": "^1.0.838",
|
|
30
|
+
"roblox-ts": "^3.0.0",
|
|
31
|
+
"typescript": "^5.6.3"
|
|
32
|
+
},
|
|
33
|
+
"files": ["out"]
|
|
34
|
+
}
|