@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.
@@ -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
@@ -0,0 +1,2 @@
1
+ -- Compiled with roblox-ts v3.0.0
2
+ return nil
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
+ }