@trioforge/crucible 0.1.0 → 0.2.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/client/index.d.ts +7 -0
- package/out/client/init.luau +36 -1
- package/out/configs.d.ts +37 -0
- package/out/configs.luau +46 -0
- package/out/data.d.ts +224 -0
- package/out/data.luau +97 -0
- package/out/emit/configs.d.ts +4 -0
- package/out/emit/configs.luau +22 -0
- package/out/emit/data.d.ts +6 -0
- package/out/emit/data.luau +19 -0
- package/out/emit/index.d.ts +5 -1
- package/out/emit/init.luau +23 -7
- package/out/emit/network.d.ts +6 -0
- package/out/emit/network.luau +36 -0
- package/out/emit/replicated.d.ts +4 -0
- package/out/emit/replicated.luau +13 -0
- package/out/index.d.ts +8 -0
- package/out/init.luau +23 -0
- package/out/internal/binding.luau +5 -0
- package/out/internal/boot.luau +107 -8
- package/out/internal/configs/limits.d.ts +10 -0
- package/out/internal/configs/limits.luau +25 -0
- package/out/internal/configs/port.d.ts +29 -0
- package/out/internal/configs/port.luau +99 -0
- package/out/internal/configs/runtime.d.ts +21 -0
- package/out/internal/configs/runtime.luau +981 -0
- package/out/internal/data/admission.d.ts +1 -0
- package/out/internal/data/admission.luau +673 -0
- package/out/internal/data/declarations.d.ts +1 -0
- package/out/internal/data/declarations.luau +384 -0
- package/out/internal/data/envelope.d.ts +1 -0
- package/out/internal/data/envelope.luau +318 -0
- package/out/internal/data/handles.d.ts +1 -0
- package/out/internal/data/handles.luau +255 -0
- package/out/internal/data/inbox-transforms.d.ts +1 -0
- package/out/internal/data/inbox-transforms.luau +458 -0
- package/out/internal/data/inbox.d.ts +1 -0
- package/out/internal/data/inbox.luau +437 -0
- package/out/internal/data/lease.d.ts +1 -0
- package/out/internal/data/lease.luau +304 -0
- package/out/internal/data/limits.d.ts +1 -0
- package/out/internal/data/limits.luau +234 -0
- package/out/internal/data/messages.d.ts +1 -0
- package/out/internal/data/messages.luau +69 -0
- package/out/internal/data/operation-id.d.ts +1 -0
- package/out/internal/data/operation-id.luau +61 -0
- package/out/internal/data/paths.d.ts +2 -0
- package/out/internal/data/paths.luau +323 -0
- package/out/internal/data/port.d.ts +1 -0
- package/out/internal/data/port.luau +290 -0
- package/out/internal/data/profiles.d.ts +1 -0
- package/out/internal/data/profiles.luau +862 -0
- package/out/internal/data/receipts.d.ts +1 -0
- package/out/internal/data/receipts.luau +190 -0
- package/out/internal/data/record-bindings.d.ts +1 -0
- package/out/internal/data/record-bindings.luau +92 -0
- package/out/internal/data/record-declarations.d.ts +1 -0
- package/out/internal/data/record-declarations.luau +140 -0
- package/out/internal/data/records.d.ts +1 -0
- package/out/internal/data/records.luau +400 -0
- package/out/internal/data/runtime.d.ts +1 -0
- package/out/internal/data/runtime.luau +918 -0
- package/out/internal/data/scheduler.d.ts +1 -0
- package/out/internal/data/scheduler.luau +507 -0
- package/out/internal/data/session.d.ts +1 -0
- package/out/internal/data/session.luau +234 -0
- package/out/internal/data/shutdown.d.ts +1 -0
- package/out/internal/data/shutdown.luau +91 -0
- package/out/internal/data/waiters.d.ts +1 -0
- package/out/internal/data/waiters.luau +65 -0
- package/out/internal/lifecycle.luau +45 -0
- package/out/internal/network/catalog.d.ts +26 -0
- package/out/internal/network/catalog.luau +182 -0
- package/out/internal/network/claims.d.ts +1 -0
- package/out/internal/network/claims.luau +154 -0
- package/out/internal/network/client-session.d.ts +1 -0
- package/out/internal/network/client-session.luau +403 -0
- package/out/internal/network/codec.d.ts +63 -0
- package/out/internal/network/codec.luau +1183 -0
- package/out/internal/network/envelope.d.ts +1 -0
- package/out/internal/network/envelope.luau +247 -0
- package/out/internal/network/limits.d.ts +26 -0
- package/out/internal/network/limits.luau +55 -0
- package/out/internal/network/port.d.ts +1 -0
- package/out/internal/network/port.luau +19 -0
- package/out/internal/network/roblox-network-port.d.ts +1 -0
- package/out/internal/network/roblox-network-port.luau +105 -0
- package/out/internal/network/schema.d.ts +72 -0
- package/out/internal/network/schema.luau +386 -0
- package/out/internal/network/server-session.d.ts +1 -0
- package/out/internal/network/server-session.luau +393 -0
- package/out/internal/network/session.d.ts +1 -0
- package/out/internal/network/session.luau +266 -0
- package/out/internal/reactive/context.d.ts +1 -0
- package/out/internal/reactive/context.luau +149 -0
- package/out/internal/reactive/flush.d.ts +1 -0
- package/out/internal/reactive/flush.luau +355 -0
- package/out/internal/reactive/graph.d.ts +1 -0
- package/out/internal/reactive/graph.luau +626 -0
- package/out/internal/reactive/limits.d.ts +1 -0
- package/out/internal/reactive/limits.luau +54 -0
- package/out/internal/reactive/ownership.d.ts +1 -0
- package/out/internal/reactive/ownership.luau +626 -0
- package/out/internal/registry.luau +10 -1
- package/out/internal/sync/client-sync.d.ts +1 -0
- package/out/internal/sync/client-sync.luau +157 -0
- package/out/internal/sync/declarations.d.ts +1 -0
- package/out/internal/sync/declarations.luau +276 -0
- package/out/internal/sync/frame.d.ts +1 -0
- package/out/internal/sync/frame.luau +91 -0
- package/out/internal/sync/server-sync.d.ts +1 -0
- package/out/internal/sync/server-sync.luau +502 -0
- package/out/internal/sync/values.d.ts +1 -0
- package/out/internal/sync/values.luau +930 -0
- package/out/network.d.ts +66 -0
- package/out/network.luau +24 -0
- package/out/reactive.d.ts +76 -0
- package/out/reactive.luau +136 -0
- package/out/replicated.d.ts +38 -0
- package/out/replicated.luau +45 -0
- package/out/server/data.d.ts +54 -0
- package/out/server/data.luau +44 -0
- package/out/server/index.d.ts +16 -0
- package/out/server/init.luau +49 -3
- package/out/server/publish.d.ts +20 -0
- package/out/server/publish.luau +24 -0
- package/out/vide/index.d.ts +7 -0
- package/out/vide/init.luau +50 -0
- package/package.json +13 -2
- package/transformer/data.cjs +72 -0
- package/transformer/declarations.cjs +153 -0
- package/transformer/index.js +37 -11
- package/transformer/network.cjs +961 -0
- package/vide/package.json +4 -0
|
@@ -0,0 +1,626 @@
|
|
|
1
|
+
-- Compiled with roblox-ts v3.0.0
|
|
2
|
+
local TS = _G[script]
|
|
3
|
+
local runtimeDiagnostic = TS.import(script, script.Parent.Parent, "diagnostics").runtimeDiagnostic
|
|
4
|
+
local _lifecycle = TS.import(script, script.Parent.Parent, "lifecycle")
|
|
5
|
+
local closeScope = _lifecycle.closeScope
|
|
6
|
+
local current = _lifecycle.current
|
|
7
|
+
local drain = _lifecycle.drain
|
|
8
|
+
local enabling = _lifecycle.enabling
|
|
9
|
+
local hasEnded = _lifecycle.hasEnded
|
|
10
|
+
local openScope = _lifecycle.openScope
|
|
11
|
+
local call = TS.import(script, script.Parent.Parent, "runner").call
|
|
12
|
+
local _context = TS.import(script, script.Parent, "context")
|
|
13
|
+
local boundary = _context.boundary
|
|
14
|
+
local discard = _context.discard
|
|
15
|
+
local pop = _context.pop
|
|
16
|
+
local push = _context.push
|
|
17
|
+
local top = _context.top
|
|
18
|
+
local _graph = TS.import(script, script.Parent, "graph")
|
|
19
|
+
local finishOpen = _graph.finishOpen
|
|
20
|
+
local guardPure = _graph.guardPure
|
|
21
|
+
local originOf = _graph.originOf
|
|
22
|
+
local releaseEdges = _graph.releaseEdges
|
|
23
|
+
local OWNER_CHILDREN_WARNING = TS.import(script, script.Parent, "limits").OWNER_CHILDREN_WARNING
|
|
24
|
+
--* @internal
|
|
25
|
+
--[[
|
|
26
|
+
*
|
|
27
|
+
* @internal
|
|
28
|
+
* How a lifetime releases one owned value. `own` decides it once, when the value is owned. The engine's kinds come
|
|
29
|
+
* last, so `kind >= Release.Disconnect` is a release that runs no Luau.
|
|
30
|
+
|
|
31
|
+
]]
|
|
32
|
+
--* @internal Everything that must end, ends exactly once, through one of these.
|
|
33
|
+
--* @internal
|
|
34
|
+
--* @internal
|
|
35
|
+
local NO_OWNER = "effect() needs an owner: create it in a Subsystem or behaviour, or inside scope()"
|
|
36
|
+
local generations = setmetatable({}, {
|
|
37
|
+
__mode = "k",
|
|
38
|
+
})
|
|
39
|
+
local intervals = setmetatable({}, {
|
|
40
|
+
__mode = "k",
|
|
41
|
+
})
|
|
42
|
+
--* Bodies running now, outermost first. Boundaries nest synchronously, so one stack serves every thread.
|
|
43
|
+
local bodies = {}
|
|
44
|
+
local describe = tostring
|
|
45
|
+
--* @internal
|
|
46
|
+
local function useDescribe(port)
|
|
47
|
+
describe = port
|
|
48
|
+
end
|
|
49
|
+
--* @internal "JobService" or "DoorPrompt on Workspace.Door", for diagnostics.
|
|
50
|
+
local function ownerLabel(owner)
|
|
51
|
+
if owner == nil then
|
|
52
|
+
return "a root scope"
|
|
53
|
+
end
|
|
54
|
+
local id = (owner.record).id
|
|
55
|
+
return if owner.instance == nil then id else `{id} on {describe(owner.instance)}`
|
|
56
|
+
end
|
|
57
|
+
--* @internal
|
|
58
|
+
local function newLifetime(role, parent, owner, effect)
|
|
59
|
+
local lifetime = {
|
|
60
|
+
kind = 3,
|
|
61
|
+
role = role,
|
|
62
|
+
parent = parent,
|
|
63
|
+
owner = owner,
|
|
64
|
+
effect = effect,
|
|
65
|
+
count = 0,
|
|
66
|
+
releases = {},
|
|
67
|
+
ended = false,
|
|
68
|
+
finished = false,
|
|
69
|
+
linked = false,
|
|
70
|
+
warned = false,
|
|
71
|
+
}
|
|
72
|
+
return lifetime
|
|
73
|
+
end
|
|
74
|
+
--* @internal Appends `child` to `parent`'s children, newest last.
|
|
75
|
+
local function adopt(parent, child)
|
|
76
|
+
child.prev = parent.tail
|
|
77
|
+
child.next = nil
|
|
78
|
+
if parent.tail == nil then
|
|
79
|
+
parent.head = child
|
|
80
|
+
else
|
|
81
|
+
parent.tail.next = child
|
|
82
|
+
end
|
|
83
|
+
parent.tail = child
|
|
84
|
+
child.linked = true
|
|
85
|
+
parent.count += 1
|
|
86
|
+
if parent.role == 0 and parent.count >= OWNER_CHILDREN_WARNING and not parent.warned then
|
|
87
|
+
parent.warned = true
|
|
88
|
+
runtimeDiagnostic("warning", `Crucible: {ownerLabel(parent.owner)} owns {OWNER_CHILDREN_WARNING} live effects and scopes; one may be created every frame`)
|
|
89
|
+
end
|
|
90
|
+
end
|
|
91
|
+
local function leave(child)
|
|
92
|
+
local parent = child.parent
|
|
93
|
+
if not child.linked or parent == nil then
|
|
94
|
+
return nil
|
|
95
|
+
end
|
|
96
|
+
child.linked = false
|
|
97
|
+
if child.prev == nil then
|
|
98
|
+
parent.head = child.next
|
|
99
|
+
else
|
|
100
|
+
child.prev.next = child.next
|
|
101
|
+
end
|
|
102
|
+
if child.next == nil then
|
|
103
|
+
parent.tail = child.prev
|
|
104
|
+
else
|
|
105
|
+
child.next.prev = child.prev
|
|
106
|
+
end
|
|
107
|
+
child.prev = nil
|
|
108
|
+
child.next = nil
|
|
109
|
+
parent.count -= 1
|
|
110
|
+
end
|
|
111
|
+
local endLifetime
|
|
112
|
+
local function generation(s)
|
|
113
|
+
local _s = s
|
|
114
|
+
local lifetime = generations[_s]
|
|
115
|
+
if lifetime == nil then
|
|
116
|
+
local created = newLifetime(0, nil, s)
|
|
117
|
+
lifetime = created
|
|
118
|
+
local _s_1 = s
|
|
119
|
+
generations[_s_1] = created
|
|
120
|
+
if s.subscriptions == nil then
|
|
121
|
+
s.subscriptions = {}
|
|
122
|
+
end
|
|
123
|
+
local _exp = s.subscriptions
|
|
124
|
+
table.insert(_exp, function()
|
|
125
|
+
local _s_2 = s
|
|
126
|
+
generations[_s_2] = nil
|
|
127
|
+
endLifetime(created)
|
|
128
|
+
end)
|
|
129
|
+
end
|
|
130
|
+
return lifetime
|
|
131
|
+
end
|
|
132
|
+
local function interval(s)
|
|
133
|
+
local _s = s
|
|
134
|
+
local lifetime = intervals[_s]
|
|
135
|
+
if lifetime == nil then
|
|
136
|
+
local parent = generation(s)
|
|
137
|
+
lifetime = newLifetime(1, parent, s)
|
|
138
|
+
adopt(parent, lifetime)
|
|
139
|
+
local _s_1 = s
|
|
140
|
+
local _lifetime = lifetime
|
|
141
|
+
intervals[_s_1] = _lifetime
|
|
142
|
+
end
|
|
143
|
+
return lifetime
|
|
144
|
+
end
|
|
145
|
+
--[[
|
|
146
|
+
*
|
|
147
|
+
* @internal
|
|
148
|
+
* The owner of something created now: the innermost frame's lifetime; else the running Crucible object's enable
|
|
149
|
+
* interval while its Enable step runs, or its generation; else none. A returned lifetime may have ended.
|
|
150
|
+
|
|
151
|
+
]]
|
|
152
|
+
local ownerAt
|
|
153
|
+
local function resolveOwner()
|
|
154
|
+
return ownerAt(top())
|
|
155
|
+
end
|
|
156
|
+
function ownerAt(frame)
|
|
157
|
+
local _result = frame
|
|
158
|
+
if _result ~= nil then
|
|
159
|
+
_result = _result.lifetime
|
|
160
|
+
end
|
|
161
|
+
if _result ~= nil then
|
|
162
|
+
return frame.lifetime
|
|
163
|
+
end
|
|
164
|
+
local s = current()
|
|
165
|
+
if s == nil then
|
|
166
|
+
return nil
|
|
167
|
+
end
|
|
168
|
+
if hasEnded(s) then
|
|
169
|
+
local ended = newLifetime(0, nil, s)
|
|
170
|
+
ended.ended = true
|
|
171
|
+
ended.finished = true
|
|
172
|
+
return ended
|
|
173
|
+
end
|
|
174
|
+
return if enabling() == s then interval(s) else generation(s)
|
|
175
|
+
end
|
|
176
|
+
--* @internal Core edit E1: lifecycle calls it after `onDisable` and when it releases a generation. Idempotent.
|
|
177
|
+
local function endInterval(s)
|
|
178
|
+
local _s = s
|
|
179
|
+
local lifetime = intervals[_s]
|
|
180
|
+
if lifetime == nil then
|
|
181
|
+
return nil
|
|
182
|
+
end
|
|
183
|
+
local _s_1 = s
|
|
184
|
+
intervals[_s_1] = nil
|
|
185
|
+
endLifetime(lifetime)
|
|
186
|
+
end
|
|
187
|
+
--* @internal Detaches an effect from the graph for good. No diagnostic: an ending is not a failure.
|
|
188
|
+
local function halt(effect)
|
|
189
|
+
if effect.stopped then
|
|
190
|
+
return nil
|
|
191
|
+
end
|
|
192
|
+
effect.stopped = true
|
|
193
|
+
releaseEdges(effect)
|
|
194
|
+
end
|
|
195
|
+
local function latch(root)
|
|
196
|
+
if root.head == nil then
|
|
197
|
+
root.ended = true
|
|
198
|
+
return nil
|
|
199
|
+
end
|
|
200
|
+
local pending = { root }
|
|
201
|
+
while #pending > 0 do
|
|
202
|
+
-- ▼ Array.pop ▼
|
|
203
|
+
local _length = #pending
|
|
204
|
+
local _result = pending[_length]
|
|
205
|
+
pending[_length] = nil
|
|
206
|
+
-- ▲ Array.pop ▲
|
|
207
|
+
local lifetime = _result
|
|
208
|
+
lifetime.ended = true
|
|
209
|
+
local child = lifetime.head
|
|
210
|
+
while child ~= nil do
|
|
211
|
+
if child.kind == 2 then
|
|
212
|
+
halt(child)
|
|
213
|
+
if child.run ~= nil and not child.run.ended then
|
|
214
|
+
local _run = child.run
|
|
215
|
+
table.insert(pending, _run)
|
|
216
|
+
end
|
|
217
|
+
elseif not child.ended then
|
|
218
|
+
local _child = child
|
|
219
|
+
table.insert(pending, _child)
|
|
220
|
+
end
|
|
221
|
+
child = child.next
|
|
222
|
+
end
|
|
223
|
+
end
|
|
224
|
+
end
|
|
225
|
+
local function under(body, lifetime)
|
|
226
|
+
local at = body
|
|
227
|
+
while at ~= nil do
|
|
228
|
+
if at == lifetime then
|
|
229
|
+
return true
|
|
230
|
+
end
|
|
231
|
+
at = at.parent
|
|
232
|
+
end
|
|
233
|
+
return false
|
|
234
|
+
end
|
|
235
|
+
--[[
|
|
236
|
+
*
|
|
237
|
+
* @internal
|
|
238
|
+
* Ends a lifetime once: latch it and everything under it, then, unless a body under it is still running, end its
|
|
239
|
+
* children newest first, run its releases newest first, and unlink it. A running body defers those steps to its
|
|
240
|
+
* completion.
|
|
241
|
+
|
|
242
|
+
]]
|
|
243
|
+
local finish
|
|
244
|
+
function endLifetime(lifetime)
|
|
245
|
+
if lifetime.ended then
|
|
246
|
+
return nil
|
|
247
|
+
end
|
|
248
|
+
latch(lifetime)
|
|
249
|
+
for _, body in bodies do
|
|
250
|
+
if not under(body, lifetime) then
|
|
251
|
+
continue
|
|
252
|
+
end
|
|
253
|
+
if body.deferred == nil then
|
|
254
|
+
body.deferred = {}
|
|
255
|
+
end
|
|
256
|
+
local _deferred = body.deferred
|
|
257
|
+
local _lifetime = lifetime
|
|
258
|
+
table.insert(_deferred, _lifetime)
|
|
259
|
+
return nil
|
|
260
|
+
end
|
|
261
|
+
finish(lifetime)
|
|
262
|
+
end
|
|
263
|
+
local retire, runReleases
|
|
264
|
+
function finish(lifetime)
|
|
265
|
+
if lifetime.finished then
|
|
266
|
+
return nil
|
|
267
|
+
end
|
|
268
|
+
lifetime.finished = true
|
|
269
|
+
local child = lifetime.tail
|
|
270
|
+
while child ~= nil do
|
|
271
|
+
local previous = child.prev
|
|
272
|
+
if child.kind == 2 then
|
|
273
|
+
retire(child)
|
|
274
|
+
else
|
|
275
|
+
finish(child)
|
|
276
|
+
end
|
|
277
|
+
child = previous
|
|
278
|
+
end
|
|
279
|
+
local releases = lifetime.releases
|
|
280
|
+
runReleases(lifetime, releases, #releases - 2)
|
|
281
|
+
table.clear(releases)
|
|
282
|
+
leave(lifetime)
|
|
283
|
+
end
|
|
284
|
+
--* @internal Stops an effect for good: detached, its current run ended, unlinked from its owner.
|
|
285
|
+
function retire(effect)
|
|
286
|
+
halt(effect)
|
|
287
|
+
local run = effect.run
|
|
288
|
+
effect.run = nil
|
|
289
|
+
if run ~= nil then
|
|
290
|
+
latch(run)
|
|
291
|
+
finish(run)
|
|
292
|
+
end
|
|
293
|
+
leave(effect)
|
|
294
|
+
end
|
|
295
|
+
local function releaseLabel(kind, value)
|
|
296
|
+
if kind == 3 then
|
|
297
|
+
return "of a connection"
|
|
298
|
+
end
|
|
299
|
+
if kind == 4 then
|
|
300
|
+
return `of {describe(value)}`
|
|
301
|
+
end
|
|
302
|
+
if kind == 5 then
|
|
303
|
+
return "of a thread"
|
|
304
|
+
end
|
|
305
|
+
local code = if kind == 0 then value elseif kind == 1 then value.destroy else value.Destroy
|
|
306
|
+
return if type(code) == "function" then `at {originOf(code)}` else `of {tostring(value)}`
|
|
307
|
+
end
|
|
308
|
+
local function reportRelease(lifetime, kind, value, err)
|
|
309
|
+
runtimeDiagnostic("error", `Crucible: cleanup {releaseLabel(kind, value)} ({ownerLabel(lifetime.owner)}) failed: {tostring(err)}`)
|
|
310
|
+
end
|
|
311
|
+
local function disconnect(connection)
|
|
312
|
+
connection:Disconnect()
|
|
313
|
+
end
|
|
314
|
+
local function destroyInstance(instance)
|
|
315
|
+
instance:Destroy()
|
|
316
|
+
end
|
|
317
|
+
--[[
|
|
318
|
+
*
|
|
319
|
+
* `Disconnect`, `Destroy` and `task.cancel` run no Luau on this thread and cannot yield, so a `pcall` is their
|
|
320
|
+
* boundary. A throw is reported and nobody is faulted.
|
|
321
|
+
|
|
322
|
+
]]
|
|
323
|
+
local function releaseEngine(lifetime, kind, value)
|
|
324
|
+
local engine
|
|
325
|
+
if kind == 3 then
|
|
326
|
+
engine = disconnect
|
|
327
|
+
elseif kind == 4 then
|
|
328
|
+
engine = destroyInstance
|
|
329
|
+
else
|
|
330
|
+
engine = task.cancel
|
|
331
|
+
end
|
|
332
|
+
local ok, err = pcall(engine, value)
|
|
333
|
+
if not ok then
|
|
334
|
+
pcall(reportRelease, lifetime, kind, value, err)
|
|
335
|
+
end
|
|
336
|
+
end
|
|
337
|
+
--* A lifetime's releases that run code, newest first, on one worker until one fails or yields. Its own frame.
|
|
338
|
+
--[[
|
|
339
|
+
*
|
|
340
|
+
* Runs `batch` from `batch.at` down on the running worker, as its ended lifetime. A release that runs code opens its
|
|
341
|
+
* own lifecycle scope, as `within` would. One that leaves requests for a drain outside any other scope stops the
|
|
342
|
+
* batch with `true`, so the caller drains before the next release, as that release's own `within` would have.
|
|
343
|
+
|
|
344
|
+
]]
|
|
345
|
+
local function releaseBatch(batch)
|
|
346
|
+
batch.worker = coroutine.running()
|
|
347
|
+
local lifetime = batch.lifetime
|
|
348
|
+
local releases = batch.releases
|
|
349
|
+
push(batch)
|
|
350
|
+
do
|
|
351
|
+
local i = batch.at
|
|
352
|
+
local _shouldIncrement = false
|
|
353
|
+
while true do
|
|
354
|
+
if _shouldIncrement then
|
|
355
|
+
i -= 2
|
|
356
|
+
else
|
|
357
|
+
_shouldIncrement = true
|
|
358
|
+
end
|
|
359
|
+
if not (i >= 0) then
|
|
360
|
+
break
|
|
361
|
+
end
|
|
362
|
+
batch.at = i
|
|
363
|
+
local kind = releases[i + 1]
|
|
364
|
+
local value = releases[i + 2]
|
|
365
|
+
if kind >= 3 then
|
|
366
|
+
releaseEngine(lifetime, kind, value)
|
|
367
|
+
continue
|
|
368
|
+
end
|
|
369
|
+
batch.scoped = true
|
|
370
|
+
openScope()
|
|
371
|
+
if kind == 0 then
|
|
372
|
+
value()
|
|
373
|
+
elseif kind == 1 then
|
|
374
|
+
value:destroy()
|
|
375
|
+
else
|
|
376
|
+
value:Destroy()
|
|
377
|
+
end
|
|
378
|
+
batch.scoped = false
|
|
379
|
+
if closeScope() then
|
|
380
|
+
pop(batch)
|
|
381
|
+
return true
|
|
382
|
+
end
|
|
383
|
+
end
|
|
384
|
+
end
|
|
385
|
+
pop(batch)
|
|
386
|
+
return false
|
|
387
|
+
end
|
|
388
|
+
--* The release at `batch.at` threw or yielded, and its worker is gone: finish what it left open and report it.
|
|
389
|
+
local function failRelease(batch, result, value)
|
|
390
|
+
finishOpen({
|
|
391
|
+
frame = nil,
|
|
392
|
+
result = result,
|
|
393
|
+
value = value,
|
|
394
|
+
open = discard(batch.worker, false),
|
|
395
|
+
after = nil,
|
|
396
|
+
})
|
|
397
|
+
local at = batch.at
|
|
398
|
+
reportRelease(batch.lifetime, batch.releases[at + 1], batch.releases[at + 2], value)
|
|
399
|
+
if not batch.scoped then
|
|
400
|
+
return nil
|
|
401
|
+
end
|
|
402
|
+
batch.scoped = false
|
|
403
|
+
if closeScope() then
|
|
404
|
+
drain()
|
|
405
|
+
end
|
|
406
|
+
end
|
|
407
|
+
--[[
|
|
408
|
+
*
|
|
409
|
+
* Releases `releases` from index `from` down, as their ended lifetime. Engine values before the first function or
|
|
410
|
+
* `destroy()` release here. From there one worker runs the rest as a batch. A release that throws or yields loses
|
|
411
|
+
* that worker: it is cancelled and reported, and the batch goes on from the next release on a fresh one. Each
|
|
412
|
+
* release's requests still apply when it returns, before the next one runs.
|
|
413
|
+
|
|
414
|
+
]]
|
|
415
|
+
function runReleases(lifetime, releases, from)
|
|
416
|
+
local batch
|
|
417
|
+
local i = from
|
|
418
|
+
while i >= 0 do
|
|
419
|
+
local kind = releases[i + 1]
|
|
420
|
+
if kind >= 3 then
|
|
421
|
+
releaseEngine(lifetime, kind, releases[i + 2])
|
|
422
|
+
i -= 2
|
|
423
|
+
continue
|
|
424
|
+
end
|
|
425
|
+
if batch == nil then
|
|
426
|
+
batch = {
|
|
427
|
+
kind = 3,
|
|
428
|
+
lifetime = lifetime,
|
|
429
|
+
releases = releases,
|
|
430
|
+
at = i,
|
|
431
|
+
scoped = false,
|
|
432
|
+
}
|
|
433
|
+
else
|
|
434
|
+
batch.at = i
|
|
435
|
+
end
|
|
436
|
+
local result, value = call(releaseBatch, batch, nil, lifetime.owner)
|
|
437
|
+
if result ~= 0 then
|
|
438
|
+
failRelease(batch, result, value)
|
|
439
|
+
elseif value == true then
|
|
440
|
+
drain()
|
|
441
|
+
end
|
|
442
|
+
i = batch.at - 2
|
|
443
|
+
end
|
|
444
|
+
end
|
|
445
|
+
--* Releases one value now, as its ended lifetime: a throw or a yield is reported and nobody is faulted.
|
|
446
|
+
local function releaseValue(lifetime, kind, value)
|
|
447
|
+
if kind >= 3 then
|
|
448
|
+
releaseEngine(lifetime, kind, value)
|
|
449
|
+
else
|
|
450
|
+
runReleases(lifetime, { kind, value }, 0)
|
|
451
|
+
end
|
|
452
|
+
end
|
|
453
|
+
--* @internal
|
|
454
|
+
local function hold(lifetime, kind, value)
|
|
455
|
+
local releases = lifetime.releases
|
|
456
|
+
local size = #releases
|
|
457
|
+
releases[size + 1] = kind
|
|
458
|
+
releases[size + 2] = value
|
|
459
|
+
end
|
|
460
|
+
--[[
|
|
461
|
+
*
|
|
462
|
+
* @internal
|
|
463
|
+
* Runs a lifetime's own body in a boundary, under `frame`, whose lifetime it is. While it runs, endings that reach
|
|
464
|
+
* it wait; at completion `after` runs, then those endings.
|
|
465
|
+
|
|
466
|
+
]]
|
|
467
|
+
local completeBody
|
|
468
|
+
local function runBody(frame, body, arg, after)
|
|
469
|
+
local lifetime = frame.lifetime
|
|
470
|
+
table.insert(bodies, lifetime)
|
|
471
|
+
boundary(lifetime.owner, frame, body, arg, completeBody, after)
|
|
472
|
+
end
|
|
473
|
+
local function settleBody(completion)
|
|
474
|
+
finishOpen(completion)
|
|
475
|
+
completion.after(completion)
|
|
476
|
+
end
|
|
477
|
+
function completeBody(completion)
|
|
478
|
+
local lifetime = completion.frame.lifetime
|
|
479
|
+
local index = (table.find(bodies, lifetime) or 0) - 1
|
|
480
|
+
if index >= 0 then
|
|
481
|
+
table.remove(bodies, index + 1)
|
|
482
|
+
end
|
|
483
|
+
local ok, err = pcall(settleBody, completion)
|
|
484
|
+
local deferred = lifetime.deferred
|
|
485
|
+
lifetime.deferred = nil
|
|
486
|
+
if deferred ~= nil then
|
|
487
|
+
for _, ending in deferred do
|
|
488
|
+
finish(ending)
|
|
489
|
+
end
|
|
490
|
+
end
|
|
491
|
+
if not ok then
|
|
492
|
+
error(err, 0)
|
|
493
|
+
end
|
|
494
|
+
end
|
|
495
|
+
--* @internal
|
|
496
|
+
local startScope
|
|
497
|
+
local function createScope(setup)
|
|
498
|
+
guardPure("create scopes")
|
|
499
|
+
local parent = resolveOwner()
|
|
500
|
+
local _result = parent
|
|
501
|
+
if _result ~= nil then
|
|
502
|
+
_result = _result.ended
|
|
503
|
+
end
|
|
504
|
+
if _result then
|
|
505
|
+
return function() end
|
|
506
|
+
end
|
|
507
|
+
return startScope(parent, setup)
|
|
508
|
+
end
|
|
509
|
+
--* @internal A scope that only its dispose ends, whatever owner is current.
|
|
510
|
+
local function createRootScope(setup)
|
|
511
|
+
guardPure("create scopes")
|
|
512
|
+
return startScope(nil, setup)
|
|
513
|
+
end
|
|
514
|
+
function startScope(parent, setup)
|
|
515
|
+
local _exp = parent
|
|
516
|
+
local _result = parent
|
|
517
|
+
if _result ~= nil then
|
|
518
|
+
_result = _result.owner
|
|
519
|
+
end
|
|
520
|
+
local scope = newLifetime(3, _exp, _result)
|
|
521
|
+
if parent ~= nil then
|
|
522
|
+
adopt(parent, scope)
|
|
523
|
+
end
|
|
524
|
+
local failed = false
|
|
525
|
+
local failure
|
|
526
|
+
runBody({
|
|
527
|
+
kind = 3,
|
|
528
|
+
lifetime = scope,
|
|
529
|
+
}, setup, nil, function(completion)
|
|
530
|
+
if completion.result == 0 then
|
|
531
|
+
return nil
|
|
532
|
+
end
|
|
533
|
+
failed = true
|
|
534
|
+
failure = completion.value
|
|
535
|
+
endLifetime(scope)
|
|
536
|
+
end)
|
|
537
|
+
if failed then
|
|
538
|
+
error(failure, 0)
|
|
539
|
+
end
|
|
540
|
+
return function()
|
|
541
|
+
return endLifetime(scope)
|
|
542
|
+
end
|
|
543
|
+
end
|
|
544
|
+
--* `guardPure("register cleanups")` that returns the frame it read, so `register` does not read it again.
|
|
545
|
+
local function cleanupFrame()
|
|
546
|
+
local frame = top()
|
|
547
|
+
local _pure = frame
|
|
548
|
+
if _pure ~= nil then
|
|
549
|
+
_pure = _pure.pure
|
|
550
|
+
end
|
|
551
|
+
local pure = _pure
|
|
552
|
+
if pure ~= nil then
|
|
553
|
+
error(`{pure} must not register cleanups`, 0)
|
|
554
|
+
end
|
|
555
|
+
return frame
|
|
556
|
+
end
|
|
557
|
+
local function register(frame, kind, value)
|
|
558
|
+
local owner = ownerAt(frame)
|
|
559
|
+
if owner == nil then
|
|
560
|
+
error(NO_OWNER, 0)
|
|
561
|
+
end
|
|
562
|
+
if owner.ended then
|
|
563
|
+
releaseValue(owner, kind, value)
|
|
564
|
+
else
|
|
565
|
+
hold(owner, kind, value)
|
|
566
|
+
end
|
|
567
|
+
end
|
|
568
|
+
--* @internal
|
|
569
|
+
local function registerCleanup(release)
|
|
570
|
+
local frame = cleanupFrame()
|
|
571
|
+
-- A nil from untyped Luau would shift every pair after it, so it is dropped.
|
|
572
|
+
if release == nil then
|
|
573
|
+
return nil
|
|
574
|
+
end
|
|
575
|
+
register(frame, 0, release)
|
|
576
|
+
end
|
|
577
|
+
local NOT_OWNABLE = "own() takes a function, an RBXScriptConnection, an Instance, a thread, or an object with destroy() or Destroy()"
|
|
578
|
+
local function classify(value)
|
|
579
|
+
local _value = value
|
|
580
|
+
local typeName = typeof(_value)
|
|
581
|
+
if typeName == "RBXScriptConnection" then
|
|
582
|
+
return 3
|
|
583
|
+
end
|
|
584
|
+
if typeName == "Instance" then
|
|
585
|
+
return 4
|
|
586
|
+
end
|
|
587
|
+
if typeName == "function" then
|
|
588
|
+
return 0
|
|
589
|
+
end
|
|
590
|
+
if typeName == "thread" then
|
|
591
|
+
return 5
|
|
592
|
+
end
|
|
593
|
+
if typeName == "table" then
|
|
594
|
+
local _destroy = value.destroy
|
|
595
|
+
if type(_destroy) == "function" then
|
|
596
|
+
return 1
|
|
597
|
+
end
|
|
598
|
+
local _destroy_1 = value.Destroy
|
|
599
|
+
if type(_destroy_1) == "function" then
|
|
600
|
+
return 2
|
|
601
|
+
end
|
|
602
|
+
end
|
|
603
|
+
error(`{NOT_OWNABLE}; got a {typeName}`, 0)
|
|
604
|
+
end
|
|
605
|
+
--* @internal
|
|
606
|
+
local function registerOwned(value)
|
|
607
|
+
local frame = cleanupFrame()
|
|
608
|
+
register(frame, classify(value), value)
|
|
609
|
+
end
|
|
610
|
+
return {
|
|
611
|
+
useDescribe = useDescribe,
|
|
612
|
+
ownerLabel = ownerLabel,
|
|
613
|
+
newLifetime = newLifetime,
|
|
614
|
+
adopt = adopt,
|
|
615
|
+
resolveOwner = resolveOwner,
|
|
616
|
+
endInterval = endInterval,
|
|
617
|
+
endLifetime = endLifetime,
|
|
618
|
+
retire = retire,
|
|
619
|
+
hold = hold,
|
|
620
|
+
runBody = runBody,
|
|
621
|
+
createScope = createScope,
|
|
622
|
+
createRootScope = createRootScope,
|
|
623
|
+
registerCleanup = registerCleanup,
|
|
624
|
+
registerOwned = registerOwned,
|
|
625
|
+
NO_OWNER = NO_OWNER,
|
|
626
|
+
}
|
|
@@ -21,7 +21,10 @@ local duplicates = {}
|
|
|
21
21
|
local sets = {}
|
|
22
22
|
local sealed
|
|
23
23
|
--* @internal
|
|
24
|
-
local function define(klass, emitted)
|
|
24
|
+
local function define(klass, emitted, first)
|
|
25
|
+
if first == nil then
|
|
26
|
+
first = false
|
|
27
|
+
end
|
|
25
28
|
if sealed ~= nil then
|
|
26
29
|
error(`Crucible: {emitted.id} registered after boot; it would never be constructed or bound`)
|
|
27
30
|
end
|
|
@@ -90,6 +93,7 @@ local function define(klass, emitted)
|
|
|
90
93
|
local _object_1 = table.clone(base)
|
|
91
94
|
setmetatable(_object_1, nil)
|
|
92
95
|
_object_1.kind = 0
|
|
96
|
+
_object_1.first = first
|
|
93
97
|
record = _object_1
|
|
94
98
|
else
|
|
95
99
|
local tag = emitted.tag
|
|
@@ -264,6 +268,11 @@ local function plan(records, side)
|
|
|
264
268
|
local _record_5 = record
|
|
265
269
|
table.insert(order, _record_5)
|
|
266
270
|
end
|
|
271
|
+
for _, record in records do
|
|
272
|
+
if record.kind == 0 and record.first then
|
|
273
|
+
visit(record)
|
|
274
|
+
end
|
|
275
|
+
end
|
|
267
276
|
for _, record in records do
|
|
268
277
|
if record.kind == 0 then
|
|
269
278
|
visit(record)
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|