@trioforge/crucible 0.0.0-stage → 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.
Files changed (63) hide show
  1. package/LICENSE +21 -0
  2. package/client/package.json +4 -0
  3. package/emit/package.json +4 -0
  4. package/out/all.d.ts +7 -0
  5. package/out/all.luau +2 -0
  6. package/out/attribute.d.ts +7 -0
  7. package/out/attribute.luau +7 -0
  8. package/out/client/index.d.ts +3 -0
  9. package/out/client/init.luau +20 -0
  10. package/out/crucible-behaviour.d.ts +16 -0
  11. package/out/crucible-behaviour.luau +78 -0
  12. package/out/crucible-object.d.ts +10 -0
  13. package/out/crucible-object.luau +69 -0
  14. package/out/emit/index.d.ts +41 -0
  15. package/out/emit/init.luau +85 -0
  16. package/out/index.d.ts +12 -0
  17. package/out/init.luau +11 -0
  18. package/out/inject.d.ts +1 -0
  19. package/out/inject.luau +7 -0
  20. package/out/internal/binding.d.ts +1 -0
  21. package/out/internal/binding.luau +610 -0
  22. package/out/internal/boot.d.ts +1 -0
  23. package/out/internal/boot.luau +463 -0
  24. package/out/internal/construction-claim.d.ts +1 -0
  25. package/out/internal/construction-claim.luau +26 -0
  26. package/out/internal/dense-list.d.ts +1 -0
  27. package/out/internal/dense-list.luau +155 -0
  28. package/out/internal/diagnostics.d.ts +1 -0
  29. package/out/internal/diagnostics.luau +29 -0
  30. package/out/internal/hooks.d.ts +1 -0
  31. package/out/internal/hooks.luau +53 -0
  32. package/out/internal/implementers.d.ts +1 -0
  33. package/out/internal/implementers.luau +119 -0
  34. package/out/internal/lifecycle.d.ts +1 -0
  35. package/out/internal/lifecycle.luau +867 -0
  36. package/out/internal/log-output.d.ts +1 -0
  37. package/out/internal/log-output.luau +106 -0
  38. package/out/internal/port.d.ts +1 -0
  39. package/out/internal/port.luau +12 -0
  40. package/out/internal/registry.d.ts +1 -0
  41. package/out/internal/registry.luau +446 -0
  42. package/out/internal/roblox-port.d.ts +1 -0
  43. package/out/internal/roblox-port.luau +117 -0
  44. package/out/internal/runner.d.ts +1 -0
  45. package/out/internal/runner.luau +86 -0
  46. package/out/internal/scheduler.d.ts +1 -0
  47. package/out/internal/scheduler.luau +207 -0
  48. package/out/internal/state.d.ts +1 -0
  49. package/out/internal/state.luau +41 -0
  50. package/out/log-level.d.ts +7 -0
  51. package/out/log-level.luau +21 -0
  52. package/out/logging.d.ts +34 -0
  53. package/out/logging.luau +233 -0
  54. package/out/server/index.d.ts +2 -0
  55. package/out/server/init.luau +16 -0
  56. package/out/subsystem.d.ts +4 -0
  57. package/out/subsystem.luau +30 -0
  58. package/package.json +41 -5
  59. package/server/package.json +4 -0
  60. package/transformer/diagnostics.cjs +19 -0
  61. package/transformer/index.js +715 -0
  62. package/transformer/type-id.cjs +80 -0
  63. package/README.md +0 -3
@@ -0,0 +1,86 @@
1
+ -- Compiled with roblox-ts v3.0.0
2
+ --* @internal
3
+ --* @internal The error value of a call that yielded.
4
+ local YIELDED = "yielded; only start may yield"
5
+ local DONE = {}
6
+ local free = {}
7
+ local owners = {}
8
+ local function loop(work, a, b)
9
+ while true do
10
+ -- The only resumer is `call`, which always passes a function and its two arguments.
11
+ local result = work(a, b)
12
+ -- A parked worker must not keep its last arguments alive.
13
+ work = nil
14
+ a = nil
15
+ b = nil
16
+ local nextWork, nextA, nextB = coroutine.yield(DONE, result)
17
+ work = nextWork
18
+ a = nextA
19
+ b = nextB
20
+ end
21
+ end
22
+ --[[
23
+ *
24
+ * @internal
25
+ * Runs every callback that must not yield: hooks other than `start`, constructors, `accepts`, and `All<T>`
26
+ * callbacks. A pool of parked worker coroutines (GoodSignal's free-runner trick) detects a yield, which
27
+ * `pcall` cannot. A worker returns to the pool after a call that returned, so steady-state calls allocate
28
+ * nothing. A throw kills its worker, and a yield gets its worker cancelled, so only failures allocate a new
29
+ * one. A callback that calls `call` again takes another worker, and the pool grows to the deepest nesting.
30
+ *
31
+ * `owner` becomes the worker's owner for the call, which is what `current()` reads. Without one the call is
32
+ * ownerless. Returns the callback's result on Ok, or the error (with a traceback) on Threw and Yielded.
33
+
34
+ ]]
35
+ local function call(work, a, b, owner)
36
+ -- ▼ Array.pop ▼
37
+ local _length = #free
38
+ local _result = free[_length]
39
+ free[_length] = nil
40
+ -- ▲ Array.pop ▲
41
+ local _condition = _result
42
+ if _condition == nil then
43
+ _condition = coroutine.create(loop)
44
+ end
45
+ local worker = _condition
46
+ if owner ~= nil then
47
+ local _owner = owner
48
+ owners[worker] = _owner
49
+ end
50
+ local ok, marker, value = coroutine.resume(worker, work, a, b)
51
+ owners[worker] = nil
52
+ if not ok then
53
+ return 1, debug.traceback(worker, tostring(marker))
54
+ end
55
+ if marker ~= DONE then
56
+ local trace = debug.traceback(worker, YIELDED)
57
+ -- A failed cancel leaves the worker suspended, so it never returns to the pool.
58
+ local cancelled, err = pcall(task.cancel, worker)
59
+ return 2, if cancelled then trace else `{trace}\ncancellation failed: {err}`
60
+ end
61
+ table.insert(free, worker)
62
+ return 0, value
63
+ end
64
+ --* @internal The object that owns `thread`: a runner worker during an owned call, or a start thread for its life.
65
+ local function ownerOf(thread)
66
+ local _thread = thread
67
+ return owners[_thread]
68
+ end
69
+ --* @internal
70
+ local function own(thread, owner)
71
+ local _thread = thread
72
+ local _owner = owner
73
+ owners[_thread] = _owner
74
+ end
75
+ --* @internal
76
+ local function disown(thread)
77
+ local _thread = thread
78
+ owners[_thread] = nil
79
+ end
80
+ return {
81
+ call = call,
82
+ ownerOf = ownerOf,
83
+ own = own,
84
+ disown = disown,
85
+ YIELDED = YIELDED,
86
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,207 @@
1
+ -- Compiled with roblox-ts v3.0.0
2
+ local TS = _G[script]
3
+ local runtimeDiagnostic = TS.import(script, script.Parent, "diagnostics").runtimeDiagnostic
4
+ local DenseList = TS.import(script, script.Parent, "dense-list").DenseList
5
+ local within = TS.import(script, script.Parent, "lifecycle").within
6
+ --* @internal
7
+ --* @internal
8
+ --* @internal
9
+ --* @internal
10
+ local MAX_FIXED_STEPS = 4
11
+ --* @internal
12
+ local MAX_FRAME_SECONDS = 0.25
13
+ --* @internal
14
+ local function accumulate(carry, frameDt, step)
15
+ local total = carry + math.clamp(frameDt, 0, MAX_FRAME_SECONDS)
16
+ local whole = math.floor(total / step)
17
+ return math.min(whole, MAX_FIXED_STEPS), math.max(total - whole * step, 0)
18
+ end
19
+ local Lane
20
+ do
21
+ Lane = setmetatable({}, {
22
+ __tostring = function()
23
+ return "Lane"
24
+ end,
25
+ })
26
+ Lane.__index = Lane
27
+ function Lane.new(...)
28
+ local self = setmetatable({}, Lane)
29
+ return self:constructor(...) or self
30
+ end
31
+ function Lane:constructor(hook, frame)
32
+ self.members = DenseList.new()
33
+ self.dt = 0
34
+ local caller = function(s, dt)
35
+ return frame[hook](s.object, dt)
36
+ end
37
+ self.visit = function(s)
38
+ return within(s, s, 6, caller, s, self.dt)
39
+ end
40
+ end
41
+ function Lane:run(dt)
42
+ self.dt = dt
43
+ self.members:forEach(self.visit)
44
+ end
45
+ end
46
+ --* @internal
47
+ local Scheduler
48
+ do
49
+ Scheduler = setmetatable({}, {
50
+ __tostring = function()
51
+ return "Scheduler"
52
+ end,
53
+ })
54
+ Scheduler.__index = Scheduler
55
+ function Scheduler.new(...)
56
+ local self = setmetatable({}, Scheduler)
57
+ return self:constructor(...) or self
58
+ end
59
+ function Scheduler:constructor(port, config, frame)
60
+ self.port = port
61
+ self.config = config
62
+ self.frame = frame
63
+ self.renderLanes = {}
64
+ self.refusedClasses = {}
65
+ self.carry = 0
66
+ self.step = 1 / config.fixedRate
67
+ self.lanes = {
68
+ [0] = Lane.new(0, frame),
69
+ [1] = Lane.new(1, frame),
70
+ [2] = Lane.new(2, frame),
71
+ [3] = Lane.new(3, frame),
72
+ }
73
+ end
74
+ function Scheduler:add(s)
75
+ local record = s.record
76
+ local mask = record.hooks
77
+ if not self.config.renders and ((bit32.band(mask, (bit32.lshift(1, 0)))) ~= 0 or record.renderPriority ~= nil) then
78
+ local _refusedClasses = self.refusedClasses
79
+ local _id = record.id
80
+ if not (_refusedClasses[_id] ~= nil) then
81
+ local _refusedClasses_1 = self.refusedClasses
82
+ local _id_1 = record.id
83
+ _refusedClasses_1[_id_1] = true
84
+ runtimeDiagnostic("error", `Crucible server refuses preRender and renderPriority for {record.id}`)
85
+ end
86
+ return nil
87
+ end
88
+ if (bit32.band(mask, (bit32.lshift(1, 0)))) ~= 0 then
89
+ self:join(self:renderLaneOf(s), 0, s)
90
+ end
91
+ for _, hook in { 1, 2, 3 } do
92
+ if (bit32.band(mask, (bit32.lshift(1, hook)))) ~= 0 then
93
+ self:join(self.lanes[hook], hook, s)
94
+ end
95
+ end
96
+ end
97
+ function Scheduler:remove(s)
98
+ local priority = (s.record).renderPriority
99
+ local render = if priority == nil then self.lanes[0] else self.renderLanes[priority]
100
+ if render ~= nil and render.members:remove(s) and render.members:size() == 0 then
101
+ local _result = render.disconnect
102
+ if _result ~= nil then
103
+ _result()
104
+ end
105
+ render.disconnect = nil
106
+ end
107
+ local fixed = self.lanes[1]
108
+ if fixed.members:remove(s) and fixed.members:size() == 0 then
109
+ local _result = fixed.disconnect
110
+ if _result ~= nil then
111
+ _result()
112
+ end
113
+ fixed.disconnect = nil
114
+ self.carry = 0
115
+ end
116
+ local update = self.lanes[2]
117
+ local late = self.lanes[3]
118
+ local changed = update.members:remove(s)
119
+ if (late.members:remove(s) or changed) and update.members:size() == 0 and late.members:size() == 0 then
120
+ local _result = self.postSimulation
121
+ if _result ~= nil then
122
+ _result()
123
+ end
124
+ self.postSimulation = nil
125
+ end
126
+ end
127
+ function Scheduler:renderLaneOf(s)
128
+ local priority = (s.record).renderPriority
129
+ if priority == nil then
130
+ return self.lanes[0]
131
+ end
132
+ local lane = self.renderLanes[priority]
133
+ if lane == nil then
134
+ lane = Lane.new(0, self.frame)
135
+ local _renderLanes = self.renderLanes
136
+ local _lane = lane
137
+ _renderLanes[priority] = _lane
138
+ end
139
+ return lane
140
+ end
141
+ function Scheduler:join(lane, hook, s)
142
+ if not lane.members:add(s) then
143
+ return nil
144
+ end
145
+ local port = self.port
146
+ repeat
147
+ local _fallthrough = false
148
+ if hook == 0 then
149
+ if lane.disconnect ~= nil or not self.config.renders then
150
+ return nil
151
+ end
152
+ local priority = (s.record).renderPriority
153
+ local run = function(dt)
154
+ return lane:run(dt)
155
+ end
156
+ lane.disconnect = if priority == nil then port.onFrame(0, run) else port.bindRenderStep(`crucible:{priority}`, priority, run)
157
+ return nil
158
+ end
159
+ if hook == 1 then
160
+ if lane.disconnect ~= nil then
161
+ return nil
162
+ end
163
+ lane.disconnect = port.onFrame(1, function(dt)
164
+ local steps, carry = accumulate(self.carry, dt, self.step)
165
+ self.carry = carry
166
+ do
167
+ local i = 0
168
+ local _shouldIncrement = false
169
+ while true do
170
+ if _shouldIncrement then
171
+ i += 1
172
+ else
173
+ _shouldIncrement = true
174
+ end
175
+ if not (i < steps) then
176
+ break
177
+ end
178
+ lane:run(self.step)
179
+ end
180
+ end
181
+ end)
182
+ return nil
183
+ end
184
+ if hook == 2 then
185
+ _fallthrough = true
186
+ end
187
+ if _fallthrough or hook == 3 then
188
+ if self.postSimulation ~= nil then
189
+ return nil
190
+ end
191
+ local update = self.lanes[2]
192
+ local late = self.lanes[3]
193
+ self.postSimulation = port.onFrame(2, function(dt)
194
+ update:run(dt)
195
+ late:run(dt)
196
+ end)
197
+ return nil
198
+ end
199
+ until true
200
+ end
201
+ end
202
+ return {
203
+ accumulate = accumulate,
204
+ MAX_FIXED_STEPS = MAX_FIXED_STEPS,
205
+ MAX_FRAME_SECONDS = MAX_FRAME_SECONDS,
206
+ Scheduler = Scheduler,
207
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,41 @@
1
+ -- Compiled with roblox-ts v3.0.0
2
+ --* @internal Public API rule 1: a Subsystem has `awake` and `start` but no `onEnable`, `onDisable` or `onDestroy`.
3
+ --* @internal What lifecycle reads from a class record. The registry's records extend it.
4
+ --* @internal An object the `construct` effect built. Lifecycle never reads it.
5
+ --[[
6
+ *
7
+ * @internal
8
+ * Where one generation of an object is. A phase also records which inverse hooks are owed: from Awake on,
9
+ * one `onDestroy`; in Enabled, one `onDisable`. Each step enters its phase before running its hook, so a
10
+ * hook that throws part way still owes its inverse. Faulted and Destroyed are terminal.
11
+ *
12
+ * Unconstructed --constructor--> Constructed --awake--> Awake --onEnable--> Enabled <--onDisable / onEnable--> Disabled
13
+ * | | | \--(want Disabled, no hook)---------------------------^
14
+ * +--------Drop, no hooks-------+ +--------onDestroy (from Awake or Disabled)--> Destroyed | Faulted
15
+
16
+ ]]
17
+ --* @internal `start` runs once per generation, the first time the object is Enabled with the gate at Start.
18
+ --[[
19
+ *
20
+ * @internal How far the object's batch has opened. A batch raises the gate on all its objects together, so
21
+ * every object is awake before any is enabled and enabled before any starts. After its batch an object sits at Start.
22
+
23
+ ]]
24
+ --* @internal What the outside world asked for. The latest request wins; Destroyed is absorbing.
25
+ --* @internal Where a failure happened. The first one is the object's primary diagnostic.
26
+ --* @internal One atomic move. Each runs at most one hook or changes one index.
27
+ --* @internal Discriminates the two kinds of entry on the work stack.
28
+ --* @internal The first failure, with every later failure during its cleanup attached.
29
+ --* @internal The inputs `nextStep` reads. Lifecycle owns every field; nothing else writes them.
30
+ --* @internal Runtime state of one generation.
31
+ --* @internal
32
+ --* @internal New generations going through construct, awake, enable and start together.
33
+ --[[
34
+ *
35
+ * @internal
36
+ * Everything a step does outside lifecycle state. Boot installs the real ones; specs install doubles.
37
+ * Lifecycle runs `construct` and every hook caller through `runner.call` with the object as owner, so a throw
38
+ * or a yield comes back as a failure, and `start` on its own thread.
39
+
40
+ ]]
41
+ return nil
@@ -0,0 +1,7 @@
1
+ export declare enum LogLevel {
2
+ Debug = 0,
3
+ Info = 1,
4
+ Warning = 2,
5
+ Error = 3,
6
+ Off = 4
7
+ }
@@ -0,0 +1,21 @@
1
+ -- Compiled with roblox-ts v3.0.0
2
+ local LogLevel
3
+ do
4
+ local _inverse = {}
5
+ LogLevel = setmetatable({}, {
6
+ __index = _inverse,
7
+ })
8
+ LogLevel.Debug = 0
9
+ _inverse[0] = "Debug"
10
+ LogLevel.Info = 1
11
+ _inverse[1] = "Info"
12
+ LogLevel.Warning = 2
13
+ _inverse[2] = "Warning"
14
+ LogLevel.Error = 3
15
+ _inverse[3] = "Error"
16
+ LogLevel.Off = 4
17
+ _inverse[4] = "Off"
18
+ end
19
+ return {
20
+ LogLevel = LogLevel,
21
+ }
@@ -0,0 +1,34 @@
1
+ import { LogLevel } from "./log-level";
2
+ export { LogLevel } from "./log-level";
3
+ export type LogContext = Readonly<Record<string, unknown>>;
4
+ export type LogProperties = Readonly<Record<string, string | number | boolean>>;
5
+ export interface LogRecord {
6
+ readonly sequence: number;
7
+ readonly time: number;
8
+ readonly source: string;
9
+ readonly level: Exclude<LogLevel, LogLevel.Off>;
10
+ readonly template: string;
11
+ readonly context: LogProperties;
12
+ readonly message: string;
13
+ readonly traceback?: string;
14
+ }
15
+ export interface LogSink {
16
+ readonly write: (record: LogRecord) => void;
17
+ }
18
+ export interface SinkSubscription {
19
+ setMinimum(level: LogLevel): void;
20
+ disconnect(): void;
21
+ }
22
+ export interface Logger {
23
+ isEnabled(level: Exclude<LogLevel, LogLevel.Off>): boolean;
24
+ debug(template: string, context?: LogContext): void;
25
+ info(template: string, context?: LogContext): void;
26
+ warn(template: string, context?: LogContext): void;
27
+ error(template: string, context?: LogContext): void;
28
+ }
29
+ export declare class Logging {
30
+ private constructor();
31
+ static readonly defaultSink: SinkSubscription;
32
+ static getLogger(source: string): Logger;
33
+ static addSink(sink: LogSink, minimum?: LogLevel): SinkSubscription;
34
+ }
@@ -0,0 +1,233 @@
1
+ -- Compiled with roblox-ts v3.0.0
2
+ local TS = _G[script]
3
+ local exports = {}
4
+ local LogLevel = TS.import(script, script.Parent, "log-level").LogLevel
5
+ local runtimeDiagnostic = TS.import(script, script.Parent, "internal", "diagnostics").runtimeDiagnostic
6
+ local loggingPort = TS.import(script, script.Parent, "internal", "log-output").loggingPort
7
+ local call = TS.import(script, script.Parent, "internal", "runner").call
8
+ exports.LogLevel = TS.import(script, script.Parent, "log-level").LogLevel
9
+ local registrations = {}
10
+ local snapshot = {}
11
+ local minimum = LogLevel.Off
12
+ local sequence = 0
13
+ local emitting = false
14
+ local function derive(added)
15
+ local entries = {}
16
+ minimum = LogLevel.Off
17
+ -- roblox-ts Maps compile to unordered Luau tables. The array owns delivery order.
18
+ for _, registration in snapshot do
19
+ if not registration.connected then
20
+ continue
21
+ end
22
+ table.insert(entries, registration)
23
+ minimum = math.min(minimum, registration.minimum)
24
+ end
25
+ if added ~= nil then
26
+ local _added = added
27
+ table.insert(entries, _added)
28
+ minimum = math.min(minimum, added.minimum)
29
+ end
30
+ snapshot = table.freeze(entries)
31
+ end
32
+ local function validateMinimum(level)
33
+ local _arg0 = level >= LogLevel.Debug and level <= LogLevel.Off and level % 1 == 0
34
+ assert(_arg0, "Invalid log minimum")
35
+ end
36
+ local function register(sink, level)
37
+ validateMinimum(level)
38
+ local _sink = sink
39
+ local existing = registrations[_sink]
40
+ if existing ~= nil then
41
+ return existing.subscription
42
+ end
43
+ local _write = sink.write
44
+ local _arg0 = type(_write) == "function"
45
+ assert(_arg0, "Log sink requires write")
46
+ local registration
47
+ registration = {
48
+ sink = sink,
49
+ minimum = level,
50
+ connected = true,
51
+ subscription = {
52
+ setMinimum = function(self, value)
53
+ validateMinimum(value)
54
+ if not registration.connected then
55
+ return nil
56
+ end
57
+ registration.minimum = value
58
+ derive()
59
+ end,
60
+ disconnect = function(self)
61
+ if not registration.connected then
62
+ return nil
63
+ end
64
+ registration.connected = false
65
+ local _sink_1 = sink
66
+ registrations[_sink_1] = nil
67
+ derive()
68
+ end,
69
+ },
70
+ }
71
+ local _sink_1 = sink
72
+ registrations[_sink_1] = registration
73
+ derive(registration)
74
+ return registration.subscription
75
+ end
76
+ local function normalize(input)
77
+ local context = {}
78
+ if input ~= nil then
79
+ for key, value in pairs(input) do
80
+ context[key] = if typeof(value) == "Instance" then loggingPort().describeInstance(value) elseif type(value) == "string" or type(value) == "number" or type(value) == "boolean" then value else tostring(value)
81
+ end
82
+ end
83
+ return table.freeze(context)
84
+ end
85
+ local function render(template, context)
86
+ local parts = {}
87
+ local index = 1
88
+ while index <= #template do
89
+ local _template = template
90
+ local _index = index
91
+ local _index_1 = index
92
+ local char = string.sub(_template, _index, _index_1)
93
+ local _template_1 = template
94
+ local _index_2 = index
95
+ local _arg1 = index + 1
96
+ local pair = string.sub(_template_1, _index_2, _arg1)
97
+ if pair == "{{" or pair == "}}" then
98
+ table.insert(parts, char)
99
+ index += 2
100
+ elseif char == "{" then
101
+ local _template_2 = template
102
+ local _arg1_1 = index + 1
103
+ local close = string.find(_template_2, "}", _arg1_1, true)
104
+ if close ~= nil then
105
+ local _template_3 = template
106
+ local _arg0 = index + 1
107
+ local _arg1_2 = close - 1
108
+ local key = string.sub(_template_3, _arg0, _arg1_2)
109
+ local value = context[key]
110
+ local _result
111
+ if value == nil then
112
+ local _template_4 = template
113
+ local _index_3 = index
114
+ _result = string.sub(_template_4, _index_3, close)
115
+ else
116
+ _result = tostring(value)
117
+ end
118
+ table.insert(parts, _result)
119
+ index = close + 1
120
+ else
121
+ table.insert(parts, char)
122
+ index += 1
123
+ end
124
+ else
125
+ table.insert(parts, char)
126
+ index += 1
127
+ end
128
+ end
129
+ return table.concat(parts, "")
130
+ end
131
+ local function emit(source, level, template, input)
132
+ if emitting or level < minimum then
133
+ return nil
134
+ end
135
+ emitting = true
136
+ local trace = if level == LogLevel.Error then debug.traceback("", 3) else nil
137
+ local result, failure = call(function()
138
+ -- ▼ ReadonlyArray.filter ▼
139
+ local _newValue = {}
140
+ local _callback = function(r)
141
+ return level >= r.minimum
142
+ end
143
+ local _length = 0
144
+ for _k, _v in snapshot do
145
+ if _callback(_v, _k - 1, snapshot) == true then
146
+ _length += 1
147
+ _newValue[_length] = _v
148
+ end
149
+ end
150
+ -- ▲ ReadonlyArray.filter ▲
151
+ local accepted = _newValue
152
+ local context = normalize(input)
153
+ local _object = {}
154
+ local _left = "sequence"
155
+ sequence += 1
156
+ _object[_left] = sequence
157
+ _object.time = loggingPort().now()
158
+ _object.source = source
159
+ _object.level = level
160
+ _object.template = template
161
+ _object.context = context
162
+ _object.message = render(template, context)
163
+ _object.traceback = trace
164
+ local record = table.freeze(_object)
165
+ for _, registration in accepted do
166
+ if not registration.connected then
167
+ continue
168
+ end
169
+ local status, err = call(registration.sink.write, record, nil)
170
+ if status ~= 0 then
171
+ registration.subscription:disconnect()
172
+ runtimeDiagnostic("error", `Log sink failed: {tostring(err)}`)
173
+ end
174
+ end
175
+ end, nil, nil)
176
+ if result ~= 0 then
177
+ runtimeDiagnostic("error", `Logging failed: {tostring(failure)}`)
178
+ end
179
+ emitting = false
180
+ end
181
+ local Logging
182
+ do
183
+ Logging = setmetatable({}, {
184
+ __tostring = function()
185
+ return "Logging"
186
+ end,
187
+ })
188
+ Logging.__index = Logging
189
+ function Logging.new(...)
190
+ local self = setmetatable({}, Logging)
191
+ return self:constructor(...) or self
192
+ end
193
+ function Logging:constructor()
194
+ end
195
+ function Logging:getLogger(source)
196
+ local _arg0 = #source > 0 and #source <= 256 and (string.find(source, "%c")) == nil
197
+ assert(_arg0, "Invalid logger name")
198
+ for _, segment in string.split(source, ".") do
199
+ local _arg0_1 = #segment > 0
200
+ assert(_arg0_1, "Invalid logger name segment")
201
+ end
202
+ return {
203
+ isEnabled = function(self, level)
204
+ return level >= minimum
205
+ end,
206
+ debug = function(self, template, context)
207
+ emit(source, LogLevel.Debug, template, context)
208
+ end,
209
+ info = function(self, template, context)
210
+ emit(source, LogLevel.Info, template, context)
211
+ end,
212
+ warn = function(self, template, context)
213
+ emit(source, LogLevel.Warning, template, context)
214
+ end,
215
+ error = function(self, template, context)
216
+ emit(source, LogLevel.Error, template, context)
217
+ end,
218
+ }
219
+ end
220
+ function Logging:addSink(sink, minimum)
221
+ if minimum == nil then
222
+ minimum = LogLevel.Info
223
+ end
224
+ return register(sink, minimum)
225
+ end
226
+ Logging.defaultSink = register({
227
+ write = function(record)
228
+ return loggingPort().writeGame(record)
229
+ end,
230
+ }, if loggingPort().isStudio() then LogLevel.Debug else LogLevel.Info)
231
+ end
232
+ exports.Logging = Logging
233
+ return exports
@@ -0,0 +1,2 @@
1
+ import type { BootOptions } from "../index";
2
+ export declare function boot(options: BootOptions): void;
@@ -0,0 +1,16 @@
1
+ -- Compiled with roblox-ts v3.0.0
2
+ local TS = _G[script]
3
+ local bootSide = TS.import(script, script.Parent, "internal", "boot").boot
4
+ if not game:GetService("RunService"):IsServer() then
5
+ error("@trioforge/crucible/server loaded on the client")
6
+ end
7
+ local function boot(options)
8
+ bootSide({
9
+ name = "server",
10
+ renders = false,
11
+ booted = function() end,
12
+ }, options)
13
+ end
14
+ return {
15
+ boot = boot,
16
+ }
@@ -0,0 +1,4 @@
1
+ import { CrucibleObject } from "./crucible-object";
2
+ export declare abstract class Subsystem extends CrucibleObject {
3
+ protected onShutdown(): void;
4
+ }
@@ -0,0 +1,30 @@
1
+ -- Compiled with roblox-ts v3.0.0
2
+ local TS = _G[script]
3
+ local CrucibleObject = TS.import(script, script.Parent, "crucible-object").CrucibleObject
4
+ local installSubsystemHooks = TS.import(script, script.Parent, "internal", "hooks").installSubsystemHooks
5
+ local Subsystem
6
+ do
7
+ local super = CrucibleObject
8
+ Subsystem = setmetatable({}, {
9
+ __tostring = function()
10
+ return "Subsystem"
11
+ end,
12
+ __index = super,
13
+ })
14
+ Subsystem.__index = Subsystem
15
+ function Subsystem:constructor(...)
16
+ super.constructor(self, ...)
17
+ end
18
+ function Subsystem:onShutdown()
19
+ end
20
+ do
21
+ installSubsystemHooks({
22
+ onShutdown = function(o)
23
+ return o:onShutdown()
24
+ end,
25
+ })
26
+ end
27
+ end
28
+ return {
29
+ Subsystem = Subsystem,
30
+ }