@bazir/utils 0.1.0 → 0.1.2

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,54 @@
1
+ import { ReactiveCache } from "@bazir/state/out/State";
2
+ declare const SECRET: unique symbol;
3
+ declare namespace NetworkSystem {
4
+ export function generateHash(input: string): string;
5
+ export function createId(stackLevel?: number): string;
6
+ export function createNetworkId(stackLevel?: number): string;
7
+ type SignalListener<T extends unknown[]> = (...args: T) => void;
8
+ type RemoteEvent<T extends unknown[]> = Instance & {
9
+ FireClient: (this: RemoteEvent<T>, player: Player, ...args: T) => void;
10
+ FireServer: (this: RemoteEvent<T>, ...args: T) => void;
11
+ FireAllClients: (this: RemoteEvent<T>, ...args: T) => void;
12
+ OnServerEvent: RBXScriptSignal<(player: Player, ...args: T) => void>;
13
+ OnClientEvent: RBXScriptSignal<(...args: T) => void>;
14
+ };
15
+ type RemoteFunction<TReq extends unknown[], TRes> = Instance & {
16
+ InvokeClient: (this: RemoteFunction<TReq, TRes>, player: Player, ...args: TReq) => TRes;
17
+ InvokeServer: (this: RemoteFunction<TReq, TRes>, ...args: TReq) => TRes;
18
+ OnServerInvoke: (player: Player, ...args: TReq) => TRes;
19
+ OnClientInvoke: (...args: TReq) => TRes;
20
+ };
21
+ type RequestHandler<TReq extends unknown[], TRes> = (...args: TReq) => TRes;
22
+ export function createEvent<T extends unknown[]>(level?: number, parent?: LuaSourceContainer): RemoteEvent<T>;
23
+ export function onServer<T extends unknown[]>(instance: RemoteEvent<T>, listener: SignalListener<[player: Player, ...T]>): void;
24
+ export function onClient<T extends unknown[]>(instance: RemoteEvent<T>, listener: SignalListener<T>): void;
25
+ export function fireServer<T extends unknown[]>(instance: RemoteEvent<T>, ...args: T): void;
26
+ export function fireClient<T extends unknown[]>(instance: RemoteEvent<T>, player: Player, ...args: T): void;
27
+ export function broadcast<T extends unknown[]>(instance: RemoteEvent<T>, ...args: T): void;
28
+ export function createFunction<TReq extends unknown[], TRes>(level?: number, parent?: LuaSourceContainer): RemoteFunction<TReq, TRes>;
29
+ export function handleServer<TReq extends unknown[], TRes>(instance: RemoteFunction<TReq, TRes>, handler: RequestHandler<[player: Player, ...TReq], TRes>): void;
30
+ export function handleClient<TReq extends unknown[], TRes>(instance: RemoteFunction<TReq, TRes>, handler: RequestHandler<TReq, TRes>): void;
31
+ export function invokeServer<TReq extends unknown[], TRes>(instance: RemoteFunction<TReq, TRes>, ...args: TReq): TRes;
32
+ export function invokeClient<TReq extends unknown[], TRes>(instance: RemoteFunction<TReq, TRes>, player: Player, ...args: TReq): TRes;
33
+ export function createReplica<T>(initialValue: T, level?: number): ReactiveCache<T>;
34
+ interface SecretCache<T> extends ReactiveCache<T> {
35
+ [SECRET]: {
36
+ active: Set<Player>;
37
+ players: Map<Player, T>;
38
+ event: RemoteEvent<unknown[]>;
39
+ };
40
+ }
41
+ export function createSecret<T>(initialValue: T, level?: number): SecretCache<T>;
42
+ export function writePlayer<T>(secretCache: SecretCache<T>, player: Player, secret: T): void;
43
+ export function peekPlayer<T>(secretCache: SecretCache<T>, player: Player): T;
44
+ export function lazy<T>(importFn: () => T | Promise<T>): () => T;
45
+ type ActionFunction<Args extends unknown[], Return> = (...args: Args) => Promise<Return>;
46
+ export type ServerAction<Args extends unknown[], Return> = ActionFunction<Args, Return> & {
47
+ pending: ReactiveCache<boolean>;
48
+ error: ReactiveCache<string | undefined>;
49
+ };
50
+ export function createAction<Args extends unknown[], Return>(fn: (player: Player, ...args: Args) => Return | Promise<Return>): RemoteFunction<Args, Return>;
51
+ export function useAction<Args extends unknown[], Return>(actionHandler: RemoteFunction<Args, Return>): ServerAction<Args, Return>;
52
+ export {};
53
+ }
54
+ export = NetworkSystem;
@@ -0,0 +1,434 @@
1
+ -- Compiled with roblox-ts v3.0.0
2
+ local TS = _G[script]
3
+ local _Error = TS.import(script, TS.getModule(script, "@bazir", "core").out.Error)
4
+ local report = _Error.report
5
+ local createError = _Error.createError
6
+ local _Platform = TS.import(script, TS.getModule(script, "@bazir", "core").out.Platform)
7
+ local getDebugInfo = _Platform.getDebugInfo
8
+ local isRunningOnServer = _Platform.isRunningOnServer
9
+ local _State = TS.import(script, TS.getModule(script, "@bazir", "state").out.State)
10
+ local value = _State.value
11
+ local subscribe = _State.subscribe
12
+ local peek = _State.peek
13
+ local update = _State.update
14
+ local write = _State.write
15
+ local waitForOrCreate = TS.import(script, TS.getModule(script, "@bazir", "ui").out.Instance).waitForOrCreate
16
+ local Players = TS.import(script, TS.getModule(script, "@rbxts", "services")).Players
17
+ --[[
18
+ *
19
+ * Assigns a call signature to an object.
20
+ *
21
+ * @param callback The function to assign.
22
+ * @param object The object to assign the function to.
23
+ * @returns A callable object.
24
+
25
+ ]]
26
+ local function callable(callback, object)
27
+ return setmetatable(object, {
28
+ __call = function(_, ...)
29
+ local args = { ... }
30
+ return callback(unpack(args))
31
+ end,
32
+ })
33
+ end
34
+ local SECRET = "__secret"
35
+ local NetworkSystem = {}
36
+ do
37
+ local _container = NetworkSystem
38
+ local NONE = {
39
+ __none = "__none",
40
+ }
41
+ local function isEmpty(state)
42
+ return next(state) == nil
43
+ end
44
+ local function isNone(state)
45
+ local _state = state
46
+ local _condition = type(_state) == "table"
47
+ if _condition then
48
+ _condition = state[NONE.__none] ~= nil
49
+ end
50
+ if _condition then
51
+ return state.__none == NONE.__none
52
+ end
53
+ return false
54
+ end
55
+ local function diff(prevState, nextState)
56
+ if prevState == nextState then
57
+ return nil
58
+ elseif nextState == nil then
59
+ return NONE
60
+ else
61
+ local _prevState = prevState
62
+ local _condition = not (type(_prevState) == "table")
63
+ if not _condition then
64
+ local _nextState = nextState
65
+ _condition = not (type(_nextState) == "table")
66
+ end
67
+ if _condition then
68
+ return nextState
69
+ elseif isEmpty(prevState) and isEmpty(nextState) then
70
+ return nil
71
+ end
72
+ end
73
+ local _object = {}
74
+ if nextState then
75
+ for _k, _v in nextState do
76
+ _object[_k] = _v
77
+ end
78
+ end
79
+ local patches = _object
80
+ for key, previous in pairs(prevState) do
81
+ patches[key] = diff(previous, nextState[key])
82
+ end
83
+ if isEmpty(patches) then
84
+ return nil
85
+ end
86
+ return patches
87
+ end
88
+ local function apply(state, patches)
89
+ if isNone(patches) then
90
+ return nil
91
+ else
92
+ local _state = state
93
+ local _condition = not (type(_state) == "table")
94
+ if not _condition then
95
+ local _patches = patches
96
+ _condition = not (type(_patches) == "table")
97
+ end
98
+ if _condition then
99
+ return patches
100
+ end
101
+ end
102
+ local _object = {}
103
+ if state then
104
+ for _k, _v in state do
105
+ _object[_k] = _v
106
+ end
107
+ end
108
+ local nextState = _object
109
+ local stateIsArray = nextState[1] ~= nil
110
+ -- eslint-disable-next-line prefer-const
111
+ for key, patch in pairs(patches) do
112
+ local _condition = stateIsArray
113
+ if _condition then
114
+ local _key = key
115
+ _condition = type(_key) == "string"
116
+ end
117
+ if _condition then
118
+ local _condition_1 = tonumber(key)
119
+ if _condition_1 == nil then
120
+ _condition_1 = key
121
+ end
122
+ key = _condition_1
123
+ end
124
+ nextState[key] = apply(nextState[key], patch)
125
+ end
126
+ return nextState
127
+ end
128
+ local identifierCounts = {}
129
+ local function getSourceLocation(level)
130
+ local scriptPath, lineNumber = getDebugInfo(level)
131
+ return `{scriptPath}:{lineNumber}`
132
+ end
133
+ local function getAndIncrementCount(location)
134
+ local _location = location
135
+ local _condition = identifierCounts[_location]
136
+ if _condition == nil then
137
+ _condition = 0
138
+ end
139
+ local currentCount = _condition
140
+ local _location_1 = location
141
+ local _arg1 = currentCount + 1
142
+ identifierCounts[_location_1] = _arg1
143
+ return currentCount
144
+ end
145
+ local function generateHash(input)
146
+ local hash = 0
147
+ for i = 0, #input - 1 do
148
+ local byteValue = string.byte(input, i + 1)
149
+ hash = (bit32.lshift(hash, 5)) - hash + byteValue
150
+ hash = bit32.band(hash, hash)
151
+ end
152
+ return tostring(math.abs(hash))
153
+ end
154
+ _container.generateHash = generateHash
155
+ local function createId(stackLevel)
156
+ if stackLevel == nil then
157
+ stackLevel = 0
158
+ end
159
+ local sourceLocation = getSourceLocation(stackLevel + 3)
160
+ local currentCount = getAndIncrementCount(sourceLocation)
161
+ return `{sourceLocation}-{currentCount}`
162
+ end
163
+ _container.createId = createId
164
+ local function createNetworkId(stackLevel)
165
+ if stackLevel == nil then
166
+ stackLevel = 0
167
+ end
168
+ return generateHash(createId(stackLevel + 1))
169
+ end
170
+ _container.createNetworkId = createNetworkId
171
+ local function createEvent(level, parent)
172
+ if level == nil then
173
+ level = 0
174
+ end
175
+ if parent == nil then
176
+ parent = script
177
+ end
178
+ local id = `Event@{createNetworkId(level + 1)}`
179
+ return waitForOrCreate("RemoteEvent", parent, id)
180
+ end
181
+ _container.createEvent = createEvent
182
+ local function onServer(instance, listener)
183
+ instance.OnServerEvent:Connect(listener)
184
+ end
185
+ _container.onServer = onServer
186
+ local function onClient(instance, listener)
187
+ instance.OnClientEvent:Connect(listener)
188
+ end
189
+ _container.onClient = onClient
190
+ local function fireServer(instance, ...)
191
+ local args = { ... }
192
+ instance:FireServer(unpack(args))
193
+ end
194
+ _container.fireServer = fireServer
195
+ local function fireClient(instance, player, ...)
196
+ local args = { ... }
197
+ instance:FireClient(player, unpack(args))
198
+ end
199
+ _container.fireClient = fireClient
200
+ local function broadcast(instance, ...)
201
+ local args = { ... }
202
+ instance:FireAllClients(unpack(args))
203
+ end
204
+ _container.broadcast = broadcast
205
+ local function createFunction(level, parent)
206
+ if level == nil then
207
+ level = 0
208
+ end
209
+ if parent == nil then
210
+ parent = script
211
+ end
212
+ local id = `Function@{createNetworkId(level + 1)}`
213
+ return waitForOrCreate("RemoteFunction", parent, id)
214
+ end
215
+ _container.createFunction = createFunction
216
+ local function handleServer(instance, handler)
217
+ instance.OnServerInvoke = handler
218
+ end
219
+ _container.handleServer = handleServer
220
+ local function handleClient(instance, handler)
221
+ instance.OnClientInvoke = handler
222
+ end
223
+ _container.handleClient = handleClient
224
+ local function invokeServer(instance, ...)
225
+ local args = { ... }
226
+ return instance:InvokeServer(unpack(args))
227
+ end
228
+ _container.invokeServer = invokeServer
229
+ local function invokeClient(instance, player, ...)
230
+ local args = { ... }
231
+ return instance:InvokeClient(player, unpack(args))
232
+ end
233
+ _container.invokeClient = invokeClient
234
+ local function createReplica(initialValue, level)
235
+ if level == nil then
236
+ level = 0
237
+ end
238
+ local connectPatches = createEvent(level + 1)
239
+ local applyPatches = createEvent(level + 1)
240
+ local state = value(initialValue)
241
+ if isRunningOnServer() then
242
+ local activePlayers = {}
243
+ Players.PlayerRemoving:Connect(function(player)
244
+ local _player = player
245
+ activePlayers[_player] = nil
246
+ end)
247
+ subscribe(state, function(newValue, prevValue)
248
+ local patches = diff(prevValue, newValue)
249
+ if patches == nil then
250
+ return nil
251
+ end
252
+ for player in activePlayers do
253
+ fireClient(applyPatches, player, patches)
254
+ end
255
+ end)
256
+ onServer(connectPatches, function(player)
257
+ local patches = diff(initialValue, peek(state))
258
+ if patches ~= nil then
259
+ fireClient(applyPatches, player, patches)
260
+ end
261
+ if player:IsDescendantOf(game) then
262
+ local _player = player
263
+ activePlayers[_player] = true
264
+ end
265
+ end)
266
+ else
267
+ onClient(applyPatches, function(patches)
268
+ update(state, function(current)
269
+ return apply(current, patches)
270
+ end)
271
+ end)
272
+ fireServer(connectPatches)
273
+ end
274
+ return state
275
+ end
276
+ _container.createReplica = createReplica
277
+ local function createSecret(initialValue, level)
278
+ if level == nil then
279
+ level = 0
280
+ end
281
+ local connectPatches = createEvent(level + 1)
282
+ local applyPatches = createEvent(level + 1)
283
+ local state = value(initialValue)
284
+ if isRunningOnServer() then
285
+ state[SECRET] = {
286
+ active = {},
287
+ players = {},
288
+ event = applyPatches,
289
+ }
290
+ local _binding = (state[SECRET])
291
+ local active = _binding.active
292
+ local players = _binding.players
293
+ Players.PlayerRemoving:Connect(function(player)
294
+ local _player = player
295
+ active[_player] = nil
296
+ local _player_1 = player
297
+ players[_player_1] = nil
298
+ end)
299
+ onServer(connectPatches, function(player)
300
+ local _exp = initialValue
301
+ local _player = player
302
+ local _condition = players[_player]
303
+ if _condition == nil then
304
+ _condition = peek(state)
305
+ end
306
+ local patches = diff(_exp, _condition)
307
+ if patches ~= nil then
308
+ fireClient(applyPatches, player, patches)
309
+ end
310
+ if player:IsDescendantOf(game) then
311
+ local _player_1 = player
312
+ active[_player_1] = true
313
+ end
314
+ end)
315
+ else
316
+ onClient(applyPatches, function(patches)
317
+ update(state, function(current)
318
+ return apply(current, patches)
319
+ end)
320
+ end)
321
+ fireServer(connectPatches)
322
+ end
323
+ return state
324
+ end
325
+ _container.createSecret = createSecret
326
+ local function writePlayer(secretCache, player, secret)
327
+ local _binding = secretCache[SECRET]
328
+ local active = _binding.active
329
+ local players = _binding.players
330
+ local event = _binding.event
331
+ local _player = player
332
+ if not (active[_player] ~= nil) then
333
+ return nil
334
+ end
335
+ local _player_1 = player
336
+ local _condition = players[_player_1]
337
+ if _condition == nil then
338
+ _condition = peek(secretCache)
339
+ end
340
+ local lastSecret = _condition
341
+ local patches = diff(lastSecret, secret)
342
+ if patches == nil then
343
+ local _player_2 = player
344
+ local _secret = secret
345
+ players[_player_2] = _secret
346
+ fireClient(event, player, patches)
347
+ end
348
+ end
349
+ _container.writePlayer = writePlayer
350
+ local function peekPlayer(secretCache, player)
351
+ local _binding = secretCache[SECRET]
352
+ local players = _binding.players
353
+ local _player = player
354
+ local _condition = players[_player]
355
+ if _condition == nil then
356
+ _condition = peek(secretCache)
357
+ end
358
+ return _condition
359
+ end
360
+ _container.peekPlayer = peekPlayer
361
+ local function lazy(importFn)
362
+ local cached
363
+ return function()
364
+ if not isRunningOnServer() then
365
+ error("lazy can only be called on the server")
366
+ end
367
+ if cached == nil then
368
+ local result = importFn()
369
+ cached = if TS.Promise.is(result) then result:expect() else result
370
+ end
371
+ return cached
372
+ end
373
+ end
374
+ _container.lazy = lazy
375
+ -- Create server action (server-side)
376
+ local function createAction(fn)
377
+ local handler = createFunction(1)
378
+ if isRunningOnServer() then
379
+ handleServer(handler, function(...)
380
+ local args = { ... }
381
+ local _exitType, _returns = TS.try(function()
382
+ local result = fn(unpack(args))
383
+ return TS.TRY_RETURN, { if TS.Promise.is(result) then result:expect() else result }
384
+ end, function(err)
385
+ report(createError(tostring(err), {
386
+ code = "SERVER_ACTION_FAILED",
387
+ context = {
388
+ args = args,
389
+ },
390
+ }))
391
+ error(err)
392
+ end)
393
+ if _exitType then
394
+ return unpack(_returns)
395
+ end
396
+ end)
397
+ end
398
+ return handler
399
+ end
400
+ _container.createAction = createAction
401
+ -- Create action caller (client-side)
402
+ local function useAction(actionHandler)
403
+ if isRunningOnServer() then
404
+ error("useAction can only be called on the client")
405
+ end
406
+ local pending = value(false)
407
+ local errorMessage = value(nil)
408
+ local actionCaller = TS.async(function(...)
409
+ local args = { ... }
410
+ write(pending, true)
411
+ write(errorMessage, nil)
412
+ local lastError
413
+ local _exitType, _returns = TS.try(function()
414
+ local result = invokeServer(actionHandler, unpack(args))
415
+ write(pending, false)
416
+ return TS.TRY_RETURN, { result }
417
+ end, function(err)
418
+ lastError = err
419
+ end)
420
+ if _exitType then
421
+ return unpack(_returns)
422
+ end
423
+ write(pending, false)
424
+ write(errorMessage, tostring(lastError))
425
+ error(lastError)
426
+ end)
427
+ return callable(actionCaller, {
428
+ pending = pending,
429
+ error = errorMessage,
430
+ })
431
+ end
432
+ _container.useAction = useAction
433
+ end
434
+ return NetworkSystem
package/out/index.d.ts ADDED
@@ -0,0 +1,6 @@
1
+ import Network from './Network';
2
+ export { Network };
3
+ declare const _default: {
4
+ Network: typeof Network;
5
+ };
6
+ export default _default;
package/out/init.luau ADDED
@@ -0,0 +1,11 @@
1
+ -- Compiled with roblox-ts v3.0.0
2
+ local TS = _G[script]
3
+ -- Utility exports
4
+ local Network = TS.import(script, script, "Network")
5
+ local default = {
6
+ Network = Network,
7
+ }
8
+ return {
9
+ Network = Network,
10
+ default = default,
11
+ }
package/package.json CHANGED
@@ -1,19 +1,16 @@
1
1
  {
2
2
  "name": "@bazir/utils",
3
- "version": "0.1.0",
3
+ "version": "0.1.2",
4
4
  "description": "Utility functions for Bazir framework",
5
- "main": "dist/index.js",
6
- "types": "dist/index.d.ts",
7
- "scripts": {
8
- "build": "tsc",
9
- "prepublishOnly": "npm run build"
10
- },
5
+ "main": "out/init.lua",
6
+ "types": "out/index.d.ts",
11
7
  "files": [
12
- "dist",
8
+ "out",
13
9
  "!**/*.tsbuildinfo"
14
10
  ],
15
11
  "publishConfig": {
16
- "access": "public"
12
+ "access": "public",
13
+ "git-checks": false
17
14
  },
18
15
  "keywords": [
19
16
  "roblox",
@@ -24,12 +21,18 @@
24
21
  "author": "",
25
22
  "license": "ISC",
26
23
  "dependencies": {
27
- "@bazir/core": "^0.1.0",
24
+ "@bazir/core": "^0.1.2",
25
+ "@bazir/state": "^0.1.2",
26
+ "@bazir/ui": "^0.1.2",
28
27
  "@rbxts/services": "^1.5.5"
29
28
  },
30
29
  "devDependencies": {
31
30
  "@rbxts/compiler-types": "3.0.0-types.0",
32
- "@rbxts/types": "^1.0.813",
33
- "typescript": "^5.7.2"
31
+ "@rbxts/types": "^1.0.840",
32
+ "typescript": "^5.8.2"
33
+ },
34
+ "scripts": {
35
+ "build": "rbxtsc",
36
+ "watch": "rbxtsc -w"
34
37
  }
35
38
  }