@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.
Files changed (134) hide show
  1. package/out/client/index.d.ts +7 -0
  2. package/out/client/init.luau +36 -1
  3. package/out/configs.d.ts +37 -0
  4. package/out/configs.luau +46 -0
  5. package/out/data.d.ts +224 -0
  6. package/out/data.luau +97 -0
  7. package/out/emit/configs.d.ts +4 -0
  8. package/out/emit/configs.luau +22 -0
  9. package/out/emit/data.d.ts +6 -0
  10. package/out/emit/data.luau +19 -0
  11. package/out/emit/index.d.ts +5 -1
  12. package/out/emit/init.luau +23 -7
  13. package/out/emit/network.d.ts +6 -0
  14. package/out/emit/network.luau +36 -0
  15. package/out/emit/replicated.d.ts +4 -0
  16. package/out/emit/replicated.luau +13 -0
  17. package/out/index.d.ts +8 -0
  18. package/out/init.luau +23 -0
  19. package/out/internal/binding.luau +5 -0
  20. package/out/internal/boot.luau +107 -8
  21. package/out/internal/configs/limits.d.ts +10 -0
  22. package/out/internal/configs/limits.luau +25 -0
  23. package/out/internal/configs/port.d.ts +29 -0
  24. package/out/internal/configs/port.luau +99 -0
  25. package/out/internal/configs/runtime.d.ts +21 -0
  26. package/out/internal/configs/runtime.luau +981 -0
  27. package/out/internal/data/admission.d.ts +1 -0
  28. package/out/internal/data/admission.luau +673 -0
  29. package/out/internal/data/declarations.d.ts +1 -0
  30. package/out/internal/data/declarations.luau +384 -0
  31. package/out/internal/data/envelope.d.ts +1 -0
  32. package/out/internal/data/envelope.luau +318 -0
  33. package/out/internal/data/handles.d.ts +1 -0
  34. package/out/internal/data/handles.luau +255 -0
  35. package/out/internal/data/inbox-transforms.d.ts +1 -0
  36. package/out/internal/data/inbox-transforms.luau +458 -0
  37. package/out/internal/data/inbox.d.ts +1 -0
  38. package/out/internal/data/inbox.luau +437 -0
  39. package/out/internal/data/lease.d.ts +1 -0
  40. package/out/internal/data/lease.luau +304 -0
  41. package/out/internal/data/limits.d.ts +1 -0
  42. package/out/internal/data/limits.luau +234 -0
  43. package/out/internal/data/messages.d.ts +1 -0
  44. package/out/internal/data/messages.luau +69 -0
  45. package/out/internal/data/operation-id.d.ts +1 -0
  46. package/out/internal/data/operation-id.luau +61 -0
  47. package/out/internal/data/paths.d.ts +2 -0
  48. package/out/internal/data/paths.luau +323 -0
  49. package/out/internal/data/port.d.ts +1 -0
  50. package/out/internal/data/port.luau +290 -0
  51. package/out/internal/data/profiles.d.ts +1 -0
  52. package/out/internal/data/profiles.luau +862 -0
  53. package/out/internal/data/receipts.d.ts +1 -0
  54. package/out/internal/data/receipts.luau +190 -0
  55. package/out/internal/data/record-bindings.d.ts +1 -0
  56. package/out/internal/data/record-bindings.luau +92 -0
  57. package/out/internal/data/record-declarations.d.ts +1 -0
  58. package/out/internal/data/record-declarations.luau +140 -0
  59. package/out/internal/data/records.d.ts +1 -0
  60. package/out/internal/data/records.luau +400 -0
  61. package/out/internal/data/runtime.d.ts +1 -0
  62. package/out/internal/data/runtime.luau +918 -0
  63. package/out/internal/data/scheduler.d.ts +1 -0
  64. package/out/internal/data/scheduler.luau +507 -0
  65. package/out/internal/data/session.d.ts +1 -0
  66. package/out/internal/data/session.luau +234 -0
  67. package/out/internal/data/shutdown.d.ts +1 -0
  68. package/out/internal/data/shutdown.luau +91 -0
  69. package/out/internal/data/waiters.d.ts +1 -0
  70. package/out/internal/data/waiters.luau +65 -0
  71. package/out/internal/lifecycle.luau +45 -0
  72. package/out/internal/network/catalog.d.ts +26 -0
  73. package/out/internal/network/catalog.luau +182 -0
  74. package/out/internal/network/claims.d.ts +1 -0
  75. package/out/internal/network/claims.luau +154 -0
  76. package/out/internal/network/client-session.d.ts +1 -0
  77. package/out/internal/network/client-session.luau +403 -0
  78. package/out/internal/network/codec.d.ts +63 -0
  79. package/out/internal/network/codec.luau +1183 -0
  80. package/out/internal/network/envelope.d.ts +1 -0
  81. package/out/internal/network/envelope.luau +247 -0
  82. package/out/internal/network/limits.d.ts +26 -0
  83. package/out/internal/network/limits.luau +55 -0
  84. package/out/internal/network/port.d.ts +1 -0
  85. package/out/internal/network/port.luau +19 -0
  86. package/out/internal/network/roblox-network-port.d.ts +1 -0
  87. package/out/internal/network/roblox-network-port.luau +105 -0
  88. package/out/internal/network/schema.d.ts +72 -0
  89. package/out/internal/network/schema.luau +386 -0
  90. package/out/internal/network/server-session.d.ts +1 -0
  91. package/out/internal/network/server-session.luau +393 -0
  92. package/out/internal/network/session.d.ts +1 -0
  93. package/out/internal/network/session.luau +266 -0
  94. package/out/internal/reactive/context.d.ts +1 -0
  95. package/out/internal/reactive/context.luau +149 -0
  96. package/out/internal/reactive/flush.d.ts +1 -0
  97. package/out/internal/reactive/flush.luau +355 -0
  98. package/out/internal/reactive/graph.d.ts +1 -0
  99. package/out/internal/reactive/graph.luau +626 -0
  100. package/out/internal/reactive/limits.d.ts +1 -0
  101. package/out/internal/reactive/limits.luau +54 -0
  102. package/out/internal/reactive/ownership.d.ts +1 -0
  103. package/out/internal/reactive/ownership.luau +626 -0
  104. package/out/internal/registry.luau +10 -1
  105. package/out/internal/sync/client-sync.d.ts +1 -0
  106. package/out/internal/sync/client-sync.luau +157 -0
  107. package/out/internal/sync/declarations.d.ts +1 -0
  108. package/out/internal/sync/declarations.luau +276 -0
  109. package/out/internal/sync/frame.d.ts +1 -0
  110. package/out/internal/sync/frame.luau +91 -0
  111. package/out/internal/sync/server-sync.d.ts +1 -0
  112. package/out/internal/sync/server-sync.luau +502 -0
  113. package/out/internal/sync/values.d.ts +1 -0
  114. package/out/internal/sync/values.luau +930 -0
  115. package/out/network.d.ts +66 -0
  116. package/out/network.luau +24 -0
  117. package/out/reactive.d.ts +76 -0
  118. package/out/reactive.luau +136 -0
  119. package/out/replicated.d.ts +38 -0
  120. package/out/replicated.luau +45 -0
  121. package/out/server/data.d.ts +54 -0
  122. package/out/server/data.luau +44 -0
  123. package/out/server/index.d.ts +16 -0
  124. package/out/server/init.luau +49 -3
  125. package/out/server/publish.d.ts +20 -0
  126. package/out/server/publish.luau +24 -0
  127. package/out/vide/index.d.ts +7 -0
  128. package/out/vide/init.luau +50 -0
  129. package/package.json +13 -2
  130. package/transformer/data.cjs +72 -0
  131. package/transformer/declarations.cjs +153 -0
  132. package/transformer/index.js +37 -11
  133. package/transformer/network.cjs +961 -0
  134. package/vide/package.json +4 -0
@@ -0,0 +1,437 @@
1
+ -- Compiled with roblox-ts v3.0.0
2
+ local TS = _G[script]
3
+ local bootClock = TS.import(script, script.Parent.Parent, "boot").bootClock
4
+ local _inbox_transforms = TS.import(script, script.Parent, "inbox-transforms")
5
+ local append = _inbox_transforms.append
6
+ local clearQuarantine = _inbox_transforms.clearQuarantine
7
+ local _envelope = TS.import(script, script.Parent, "envelope")
8
+ local jsonBytes = _envelope.jsonBytes
9
+ local parseEnvelope = _envelope.parseEnvelope
10
+ local _limits = TS.import(script, script.Parent, "limits")
11
+ local CALL_DEADLINE_SECONDS = _limits.CALL_DEADLINE_SECONDS
12
+ local INBOX_MAX_ATTEMPTS = _limits.INBOX_MAX_ATTEMPTS
13
+ local INBOX_RETRY_SECONDS = _limits.INBOX_RETRY_SECONDS
14
+ local MESSAGE_ID_MAX_AGE_SECONDS = _limits.MESSAGE_ID_MAX_AGE_SECONDS
15
+ local MESSAGE_PAYLOAD_MAX_BYTES = _limits.MESSAGE_PAYLOAD_MAX_BYTES
16
+ local OPERATION_ID_MAX_BYTES = _limits.OPERATION_ID_MAX_BYTES
17
+ local _operation_id = TS.import(script, script.Parent, "operation-id")
18
+ local ageOf = _operation_id.ageOf
19
+ local createdAt = _operation_id.createdAt
20
+ local newOperationId = _operation_id.newOperationId
21
+ --* @internal A message payload admitted against a descriptor (rule 76), or refused.
22
+ --* @internal A declared message: its name and the admission of its payload (rule 65).
23
+ --* @internal A quarantined item as `quarantined` lists it (rule 73).
24
+ local function retryAfter(attempts)
25
+ return INBOX_RETRY_SECONDS[math.min(attempts, #INBOX_RETRY_SECONDS)]
26
+ end
27
+ --* @internal One profile store's offline inbox: its receivers, its sends and the session-side delivery (rules 65 to 73).
28
+ local Inbox
29
+ do
30
+ Inbox = setmetatable({}, {
31
+ __tostring = function()
32
+ return "Inbox"
33
+ end,
34
+ })
35
+ Inbox.__index = Inbox
36
+ function Inbox.new(...)
37
+ local self = setmetatable({}, Inbox)
38
+ return self:constructor(...) or self
39
+ end
40
+ function Inbox:constructor(runtime)
41
+ self.runtime = runtime
42
+ self.receivers = {}
43
+ self.engine = runtime.engine
44
+ self.store = runtime.schema.name
45
+ end
46
+ function Inbox:receive(message, receive)
47
+ local _receivers = self.receivers
48
+ local _name = message.name
49
+ local _arg1 = {
50
+ message = message,
51
+ receive = receive,
52
+ }
53
+ _receivers[_name] = _arg1
54
+ end
55
+ function Inbox:unreceive(name)
56
+ local _receivers = self.receivers
57
+ local _name = name
58
+ _receivers[_name] = nil
59
+ end
60
+ function Inbox:send(userId, message, payload, id)
61
+ local admitted = message.admit(payload)
62
+ if not admitted.ok then
63
+ error(`{self.store}: the payload of message {message.name} is invalid`, 2)
64
+ end
65
+ local bytes = jsonBytes(admitted.value)
66
+ if bytes > MESSAGE_PAYLOAD_MAX_BYTES then
67
+ error(`{self.store}: the payload of message {message.name} is {bytes} bytes, over {MESSAGE_PAYLOAD_MAX_BYTES}`, 2)
68
+ end
69
+ local port = self.engine.port
70
+ local _condition = id
71
+ if _condition == nil then
72
+ _condition = newOperationId(port)
73
+ end
74
+ local sent = _condition
75
+ if not (type(sent) == "string") or #sent > OPERATION_ID_MAX_BYTES or createdAt(sent) == nil then
76
+ error(`{self.store}: a message id comes from operationId() and is at most {OPERATION_ID_MAX_BYTES} bytes`, 2)
77
+ end
78
+ if ageOf(sent, port.wall(), MESSAGE_ID_MAX_AGE_SECONDS) ~= "fresh" then
79
+ return {
80
+ kind = "expired",
81
+ }
82
+ end
83
+ local item = {
84
+ id = sent,
85
+ name = message.name,
86
+ version = self.runtime.schema.version,
87
+ payload = admitted.value,
88
+ generation = 0,
89
+ attempts = 0,
90
+ }
91
+ local decision
92
+ local deadline = bootClock() + CALL_DEADLINE_SECONDS
93
+ local ticket = self.engine.scheduler:submit({
94
+ store = self.store,
95
+ key = tostring(userId),
96
+ priority = 5,
97
+ kind = "update",
98
+ dispatchDeadline = deadline,
99
+ bytes = bytes + 1024,
100
+ transform = function()
101
+ return function(current)
102
+ return append(current, userId, item, port.wall, function(d)
103
+ decision = d
104
+ return decision
105
+ end)
106
+ end
107
+ end,
108
+ })
109
+ local settled = ticket:wait(deadline) or {
110
+ kind = if ticket.dispatched then "uncertain" else "timeout",
111
+ }
112
+ if settled.kind ~= "done" then
113
+ return {
114
+ kind = settled.kind,
115
+ }
116
+ end
117
+ if settled.outcome.kind == "error" or decision == nil then
118
+ return {
119
+ kind = "unavailable",
120
+ }
121
+ end
122
+ if decision.kind == "unreadable" then
123
+ return {
124
+ kind = "unavailable",
125
+ }
126
+ end
127
+ if decision.kind ~= "appended" and decision.kind ~= "duplicate" then
128
+ return {
129
+ kind = decision.kind,
130
+ }
131
+ end
132
+ self:prompt(userId)
133
+ return {
134
+ kind = "ok",
135
+ value = nil,
136
+ }
137
+ end
138
+ function Inbox:prompt(userId)
139
+ for _, session in self.runtime.sessions do
140
+ if session.userId == userId and session.phase == 1 then
141
+ self.runtime:requestWrite(session, 3)
142
+ end
143
+ end
144
+ end
145
+ function Inbox:quarantined(userId)
146
+ local deadline = bootClock() + CALL_DEADLINE_SECONDS
147
+ local ticket = self.engine.scheduler:submit({
148
+ store = self.store,
149
+ key = tostring(userId),
150
+ priority = 5,
151
+ kind = "read",
152
+ dispatchDeadline = deadline,
153
+ bytes = 1024,
154
+ })
155
+ local settled = ticket:wait(deadline) or {
156
+ kind = if ticket.dispatched then "uncertain" else "timeout",
157
+ }
158
+ if settled.kind ~= "done" then
159
+ return {
160
+ kind = settled.kind,
161
+ }
162
+ end
163
+ if settled.outcome.kind == "error" then
164
+ return {
165
+ kind = "unavailable",
166
+ }
167
+ end
168
+ local _result = settled.outcome.value
169
+ if _result ~= nil then
170
+ _result = _result.value
171
+ end
172
+ local parsed = parseEnvelope(_result, "profiles")
173
+ if parsed.kind == "absent" then
174
+ return {
175
+ kind = "ok",
176
+ value = {},
177
+ }
178
+ end
179
+ if parsed.kind == "unreadable" then
180
+ return {
181
+ kind = "unavailable",
182
+ }
183
+ end
184
+ local _object = {
185
+ kind = "ok",
186
+ }
187
+ local _left = "value"
188
+ local _exp = parsed.envelope.quarantine
189
+ -- ▼ ReadonlyArray.map ▼
190
+ local _newValue = table.create(#_exp)
191
+ local _callback = function(item)
192
+ local _object_1 = {
193
+ id = item.id,
194
+ name = item.name,
195
+ reason = item.reason,
196
+ attempts = item.attempts,
197
+ }
198
+ local _left_1 = "sentAt"
199
+ local _condition = createdAt(item.id)
200
+ if _condition == nil then
201
+ _condition = 0
202
+ end
203
+ _object_1[_left_1] = _condition
204
+ _object_1.payload = item.payload
205
+ return _object_1
206
+ end
207
+ for _k, _v in _exp do
208
+ _newValue[_k] = _callback(_v, _k - 1, _exp)
209
+ end
210
+ -- ▲ ReadonlyArray.map ▲
211
+ _object[_left] = _newValue
212
+ return _object
213
+ end
214
+ function Inbox:clearQuarantined(userId, ids, action)
215
+ for _, id in ids do
216
+ if not (type(id) == "string") or #id > OPERATION_ID_MAX_BYTES then
217
+ error(`{self.store}: a message id is at most {OPERATION_ID_MAX_BYTES} bytes`, 2)
218
+ end
219
+ end
220
+ local decision
221
+ local _set = {}
222
+ for _, _v in ids do
223
+ _set[_v] = true
224
+ end
225
+ local listed = _set
226
+ local deadline = bootClock() + CALL_DEADLINE_SECONDS
227
+ local ticket = self.engine.scheduler:submit({
228
+ store = self.store,
229
+ key = tostring(userId),
230
+ priority = 5,
231
+ kind = "update",
232
+ dispatchDeadline = deadline,
233
+ bytes = 1024,
234
+ transform = function()
235
+ return function(current)
236
+ return clearQuarantine(current, userId, listed, action, function(d)
237
+ decision = d
238
+ return decision
239
+ end)
240
+ end
241
+ end,
242
+ })
243
+ local settled = ticket:wait(deadline) or {
244
+ kind = if ticket.dispatched then "uncertain" else "timeout",
245
+ }
246
+ if settled.kind ~= "done" then
247
+ return {
248
+ kind = settled.kind,
249
+ }
250
+ end
251
+ if settled.outcome.kind == "error" or decision == nil or decision.kind == "unreadable" then
252
+ return {
253
+ kind = "unavailable",
254
+ }
255
+ end
256
+ if decision.kind == "full" then
257
+ return {
258
+ kind = "full",
259
+ }
260
+ end
261
+ if decision.changed > 0 then
262
+ self:prompt(userId)
263
+ end
264
+ return {
265
+ kind = "ok",
266
+ value = decision.changed,
267
+ }
268
+ end
269
+ function Inbox:deliver(session, envelope)
270
+ for id, revision in session.acks do
271
+ if revision <= session.confirmed then
272
+ session.acks[id] = nil
273
+ end
274
+ end
275
+ for id, mark in session.marks do
276
+ local _exp = envelope.inbox
277
+ -- ▼ ReadonlyArray.find ▼
278
+ local _callback = function(other)
279
+ return other.id == id
280
+ end
281
+ local _result
282
+ for _i, _v in _exp do
283
+ if _callback(_v, _i - 1, _exp) == true then
284
+ _result = _v
285
+ break
286
+ end
287
+ end
288
+ -- ▲ ReadonlyArray.find ▲
289
+ local item = _result
290
+ local _result_1 = item
291
+ if _result_1 ~= nil then
292
+ _result_1 = _result_1.generation
293
+ end
294
+ if _result_1 == mark.generation then
295
+ continue
296
+ end
297
+ local _condition = mark.quarantine ~= nil
298
+ if _condition then
299
+ local _result_2 = item
300
+ if _result_2 ~= nil then
301
+ _result_2 = _result_2.generation
302
+ end
303
+ _condition = _result_2 == mark.generation + 1
304
+ if _condition then
305
+ _condition = item.attempts == mark.attempts
306
+ end
307
+ end
308
+ if _condition then
309
+ self.engine:diagnose(`quarantine-full:{self.store}:{session.userId}`, `quarantine full for {self.store} {session.userId}; clear it with clearQuarantined`)
310
+ end
311
+ session.marks[id] = nil
312
+ end
313
+ session.inbox = envelope.inbox
314
+ self:apply(session)
315
+ end
316
+ function Inbox:apply(session)
317
+ if not self.runtime:accept(session) then
318
+ return nil
319
+ end
320
+ local present = {}
321
+ for _, item in session.inbox do
322
+ local _id = item.id
323
+ present[_id] = true
324
+ end
325
+ for id in session.retryAt do
326
+ if not (present[id] ~= nil) then
327
+ session.retryAt[id] = nil
328
+ end
329
+ end
330
+ local soonest
331
+ local staged = false
332
+ for _, item in session.inbox do
333
+ local now = bootClock()
334
+ local _receivers = self.receivers
335
+ local _name = item.name
336
+ local receiver = _receivers[_name]
337
+ local _condition = receiver == nil
338
+ if not _condition then
339
+ local _acks = session.acks
340
+ local _id = item.id
341
+ _condition = _acks[_id] ~= nil
342
+ if not _condition then
343
+ local _marks = session.marks
344
+ local _id_1 = item.id
345
+ _condition = _marks[_id_1] ~= nil
346
+ end
347
+ end
348
+ if _condition then
349
+ continue
350
+ end
351
+ if item.version > self.runtime.schema.version then
352
+ self.engine:diagnose(`inbox-version:{session.token}:{item.id}`, `{self.store}: {session.userId}'s inbox item {item.id} was sent by a newer build (version {item.version}) and stays queued`)
353
+ continue
354
+ end
355
+ local _retryAt = session.retryAt
356
+ local _id = item.id
357
+ local at = _retryAt[_id]
358
+ if at == nil and item.attempts > 0 then
359
+ at = now + retryAfter(item.attempts)
360
+ local _retryAt_1 = session.retryAt
361
+ local _id_1 = item.id
362
+ local _at = at
363
+ _retryAt_1[_id_1] = _at
364
+ end
365
+ if at ~= nil and now < at then
366
+ local _condition_1 = soonest
367
+ if _condition_1 == nil then
368
+ _condition_1 = at
369
+ end
370
+ soonest = math.min(_condition_1, at)
371
+ continue
372
+ end
373
+ local admitted = receiver.message.admit(item.payload)
374
+ if not admitted.ok then
375
+ local _marks = session.marks
376
+ local _id_1 = item.id
377
+ local _arg1 = {
378
+ id = item.id,
379
+ generation = item.generation,
380
+ attempts = item.attempts,
381
+ quarantine = "payload",
382
+ }
383
+ _marks[_id_1] = _arg1
384
+ local _retryAt_1 = session.retryAt
385
+ local _id_2 = item.id
386
+ local _arg1_1 = now + retryAfter(#INBOX_RETRY_SECONDS)
387
+ _retryAt_1[_id_2] = _arg1_1
388
+ self.engine:diagnose(`inbox-payload:{session.token}:{item.id}`, `{self.store}: {session.userId}'s inbox item {item.id} ({item.name}) has a payload this build refuses; quarantined`)
389
+ staged = true
390
+ continue
391
+ end
392
+ local ok = pcall(function()
393
+ return session:write(function(data)
394
+ return receiver.receive(session.player, data, admitted.value)
395
+ end, function()
396
+ local _acks = session.acks
397
+ local _id_1 = item.id
398
+ local _arg1 = session.revision + 1
399
+ _acks[_id_1] = _arg1
400
+ return _acks
401
+ end)
402
+ end)
403
+ if not ok then
404
+ local attempts = item.attempts + 1
405
+ local _marks = session.marks
406
+ local _id_1 = item.id
407
+ local _arg1 = {
408
+ id = item.id,
409
+ generation = item.generation,
410
+ attempts = attempts,
411
+ quarantine = if attempts >= INBOX_MAX_ATTEMPTS then "attempts" else nil,
412
+ }
413
+ _marks[_id_1] = _arg1
414
+ local _retryAt_1 = session.retryAt
415
+ local _id_2 = item.id
416
+ local _arg1_1 = now + retryAfter(attempts)
417
+ _retryAt_1[_id_2] = _arg1_1
418
+ self.engine:diagnose(`inbox-attempt:{session.token}:{item.id}`, `{self.store}: {session.userId}'s inbox item {item.id} ({item.name}) failed attempt {attempts}`)
419
+ staged = true
420
+ else
421
+ staged = true
422
+ end
423
+ end
424
+ if staged then
425
+ self.runtime:requestWrite(session, 3)
426
+ end
427
+ if soonest ~= nil then
428
+ local at = soonest
429
+ self.engine.port.delay(math.max(at - bootClock(), 0), function()
430
+ return self:apply(session)
431
+ end)
432
+ end
433
+ end
434
+ end
435
+ return {
436
+ Inbox = Inbox,
437
+ }
@@ -0,0 +1 @@
1
+ export {};