@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,400 @@
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 _admission = TS.import(script, script.Parent, "admission")
5
+ local admitWrite = _admission.admitWrite
6
+ local loadRecord = _admission.loadRecord
7
+ local sealSchema = _admission.sealSchema
8
+ local _envelope = TS.import(script, script.Parent, "envelope")
9
+ local emptyEnvelope = _envelope.emptyEnvelope
10
+ local envelopeBytes = _envelope.envelopeBytes
11
+ local jsonBytes = _envelope.jsonBytes
12
+ local parseEnvelope = _envelope.parseEnvelope
13
+ local _limits = TS.import(script, script.Parent, "limits")
14
+ local CALL_DEADLINE_SECONDS = _limits.CALL_DEADLINE_SECONDS
15
+ local KEY_MAX_BYTES = _limits.KEY_MAX_BYTES
16
+ local MAX_ENVELOPE_BYTES = _limits.MAX_ENVELOPE_BYTES
17
+ local OPERATION_ID_MAX_AGE_SECONDS = _limits.OPERATION_ID_MAX_AGE_SECONDS
18
+ local OPERATION_ID_MAX_BYTES = _limits.OPERATION_ID_MAX_BYTES
19
+ local OPERATION_MARKERS_PER_KEY = _limits.OPERATION_MARKERS_PER_KEY
20
+ local RECORD_PAYLOAD_MAX_BYTES = _limits.RECORD_PAYLOAD_MAX_BYTES
21
+ local _operation_id = TS.import(script, script.Parent, "operation-id")
22
+ local ageOf = _operation_id.ageOf
23
+ local createdAt = _operation_id.createdAt
24
+ local evictable = _operation_id.evictable
25
+ local newOperationId = _operation_id.newOperationId
26
+ local _session = TS.import(script, script.Parent, "session")
27
+ local assertOutsideCallback = _session.assertOutsideCallback
28
+ local dataCallback = _session.dataCallback
29
+ --* @internal The failure kinds of rule 90.
30
+ --* @internal A generic update's result: the stored record and whether it is a replay (rule 88).
31
+ local function stored(current, envelope)
32
+ local _object = {
33
+ value = envelope,
34
+ }
35
+ local _left = "userIds"
36
+ local _result = current
37
+ if _result ~= nil then
38
+ _result = _result.userIds
39
+ end
40
+ local _condition = _result
41
+ if _condition == nil then
42
+ _condition = {}
43
+ end
44
+ _object[_left] = _condition
45
+ local _left_1 = "metadata"
46
+ local _result_1 = current
47
+ if _result_1 ~= nil then
48
+ _result_1 = _result_1.metadata
49
+ end
50
+ local _condition_1 = _result_1
51
+ if _condition_1 == nil then
52
+ _condition_1 = {}
53
+ end
54
+ _object[_left_1] = _condition_1
55
+ return _object
56
+ end
57
+ --[[
58
+ *
59
+ * Rules 87 and 88 as one transform: it recognizes its own id, refuses an expired id or a young full ring, migrates and
60
+ * admits the record, runs `change`, and stores the result with the id in one write.
61
+
62
+ ]]
63
+ local function updateRecord(current, input, record)
64
+ local _binding = input
65
+ local schema = _binding.schema
66
+ local id = _binding.id
67
+ local _result = current
68
+ if _result ~= nil then
69
+ _result = _result.value
70
+ end
71
+ local parsed = parseEnvelope(_result, "records")
72
+ if parsed.kind == "unreadable" then
73
+ record(parsed)
74
+ return nil
75
+ end
76
+ local envelope = if parsed.kind == "ok" then parsed.envelope else emptyEnvelope("records")
77
+ if envelope.payload ~= nil and envelope.schemaVersion > schema.version then
78
+ record({
79
+ kind = "newer",
80
+ version = envelope.schemaVersion,
81
+ })
82
+ return nil
83
+ end
84
+ if table.find(envelope.operations, id) ~= nil then
85
+ record({
86
+ kind = "replay",
87
+ envelope = envelope,
88
+ })
89
+ return nil
90
+ end
91
+ local created = createdAt(id)
92
+ if created <= envelope.operationFloor then
93
+ record({
94
+ kind = "expired",
95
+ })
96
+ return nil
97
+ end
98
+ local operations = envelope.operations
99
+ local floor = envelope.operationFloor
100
+ if #operations >= OPERATION_MARKERS_PER_KEY then
101
+ local oldest = operations[1]
102
+ if not evictable(oldest, input.wall()) then
103
+ record({
104
+ kind = "overloaded",
105
+ })
106
+ return nil
107
+ end
108
+ -- ▼ ReadonlyArray.filter ▼
109
+ local _newValue = {}
110
+ local _callback = function(_, index)
111
+ return index > 0
112
+ end
113
+ local _length = 0
114
+ for _k, _v in operations do
115
+ if _callback(_v, _k - 1, operations) == true then
116
+ _length += 1
117
+ _newValue[_length] = _v
118
+ end
119
+ end
120
+ -- ▲ ReadonlyArray.filter ▲
121
+ operations = _newValue
122
+ local _exp = floor
123
+ local _condition = createdAt(oldest)
124
+ if _condition == nil then
125
+ _condition = floor
126
+ end
127
+ floor = math.max(_exp, _condition)
128
+ end
129
+ local loaded = loadRecord(schema, envelope.payload, envelope.schemaVersion)
130
+ if loaded.kind ~= "ok" then
131
+ record({
132
+ kind = "unreadable",
133
+ reason = if loaded.kind == "unreadable" then loaded.reason else "newer",
134
+ })
135
+ return nil
136
+ end
137
+ local ok, result = pcall(function()
138
+ local changed = dataCallback(input.change, loaded.value, nil)
139
+ local unchanged = rawequal(changed, loaded.value)
140
+ if unchanged and not loaded.migrated then
141
+ return loaded.value
142
+ end
143
+ local admitted = if unchanged then loaded.value else admitWrite(schema, changed)
144
+ local bytes = jsonBytes(admitted)
145
+ if bytes > RECORD_PAYLOAD_MAX_BYTES then
146
+ error(`{schema.name}: {input.key}'s record is {bytes} bytes, over {RECORD_PAYLOAD_MAX_BYTES}`, 0)
147
+ end
148
+ return admitted
149
+ end)
150
+ if not ok then
151
+ record({
152
+ kind = "refused",
153
+ error = result,
154
+ })
155
+ return nil
156
+ end
157
+ local value = result
158
+ local unchanged = rawequal(value, loaded.value)
159
+ local keep = unchanged and not loaded.migrated
160
+ local _object = table.clone(envelope)
161
+ setmetatable(_object, nil)
162
+ _object.payload = if keep then envelope.payload else value
163
+ _object.schemaVersion = if keep then envelope.schemaVersion else schema.version
164
+ local _left = "operations"
165
+ local _array = {}
166
+ local _length = #_array
167
+ local _operationsLength = #operations
168
+ table.move(operations, 1, _operationsLength, _length + 1, _array)
169
+ _length += _operationsLength
170
+ _array[_length + 1] = id
171
+ _object[_left] = _array
172
+ _object.operationFloor = floor
173
+ local written = _object
174
+ local bytes = envelopeBytes(written)
175
+ if bytes > MAX_ENVELOPE_BYTES then
176
+ record({
177
+ kind = "too-large",
178
+ bytes = bytes,
179
+ })
180
+ return nil
181
+ end
182
+ record({
183
+ kind = "written",
184
+ value = value,
185
+ bytes = bytes,
186
+ })
187
+ return stored(current, written)
188
+ end
189
+ --* @internal Keyed records with no session and no lease (rules 85 to 91): `read` and `update`, each one scheduled request.
190
+ local RecordStore
191
+ do
192
+ RecordStore = setmetatable({}, {
193
+ __tostring = function()
194
+ return "RecordStore"
195
+ end,
196
+ })
197
+ RecordStore.__index = RecordStore
198
+ function RecordStore.new(...)
199
+ local self = setmetatable({}, RecordStore)
200
+ return self:constructor(...) or self
201
+ end
202
+ function RecordStore:constructor(engine, schema)
203
+ self.engine = engine
204
+ self.sizes = {}
205
+ self.schema = sealSchema(schema)
206
+ end
207
+ function RecordStore:check(key)
208
+ local _key = key
209
+ local _condition = not (type(_key) == "string")
210
+ if not _condition then
211
+ _condition = #key < 1 or #key > KEY_MAX_BYTES
212
+ end
213
+ if _condition then
214
+ error(`{self.schema.name}: a key is 1 to {KEY_MAX_BYTES} bytes`, 3)
215
+ end
216
+ return key
217
+ end
218
+ function RecordStore:weigh(key)
219
+ local _sizes = self.sizes
220
+ local _key = key
221
+ local _condition = _sizes[_key]
222
+ if _condition == nil then
223
+ _condition = 0
224
+ end
225
+ return math.max(_condition, 1024)
226
+ end
227
+ function RecordStore:remember(key, bytes)
228
+ if bytes > 4096 then
229
+ local _sizes = self.sizes
230
+ local _key = key
231
+ local _bytes = bytes
232
+ _sizes[_key] = _bytes
233
+ else
234
+ local _sizes = self.sizes
235
+ local _key = key
236
+ _sizes[_key] = nil
237
+ end
238
+ end
239
+ function RecordStore:unavailable(key, code, reason)
240
+ self.engine:diagnose(`record:{self.schema.name}:{code}`, `{self.schema.name}: {key} {reason}`)
241
+ return {
242
+ kind = "unavailable",
243
+ }
244
+ end
245
+ function RecordStore:read(key)
246
+ self:check(key)
247
+ local deadline = bootClock() + CALL_DEADLINE_SECONDS
248
+ local ticket = self.engine.scheduler:submit({
249
+ store = self.schema.name,
250
+ key = key,
251
+ priority = 5,
252
+ kind = "read",
253
+ dispatchDeadline = deadline,
254
+ bytes = self:weigh(key),
255
+ })
256
+ local settled = ticket:wait(deadline) or {
257
+ kind = if ticket.dispatched then "uncertain" else "timeout",
258
+ }
259
+ if settled.kind ~= "done" then
260
+ return {
261
+ kind = settled.kind,
262
+ }
263
+ end
264
+ if settled.outcome.kind == "error" then
265
+ return {
266
+ kind = "unavailable",
267
+ }
268
+ end
269
+ local _result = settled.outcome.value
270
+ if _result ~= nil then
271
+ _result = _result.value
272
+ end
273
+ local parsed = parseEnvelope(_result, "records")
274
+ if parsed.kind == "absent" then
275
+ return {
276
+ kind = "ok",
277
+ value = nil,
278
+ }
279
+ end
280
+ if parsed.kind == "unreadable" then
281
+ return self:unavailable(key, "unreadable", "is unreadable")
282
+ end
283
+ if parsed.envelope.payload == nil then
284
+ return {
285
+ kind = "ok",
286
+ value = nil,
287
+ }
288
+ end
289
+ self:remember(key, envelopeBytes(parsed.envelope))
290
+ local loaded = loadRecord(self.schema, parsed.envelope.payload, parsed.envelope.schemaVersion)
291
+ if loaded.kind == "newer" then
292
+ return self:unavailable(key, "newer", "was written by a newer build")
293
+ end
294
+ if loaded.kind == "unreadable" then
295
+ return self:unavailable(key, "unreadable", `did not load: {loaded.reason}`)
296
+ end
297
+ return {
298
+ kind = "ok",
299
+ value = loaded.value,
300
+ }
301
+ end
302
+ function RecordStore:update(key, change, id)
303
+ assertOutsideCallback()
304
+ self:check(key)
305
+ local port = self.engine.port
306
+ local _condition = id
307
+ if _condition == nil then
308
+ _condition = newOperationId(port)
309
+ end
310
+ local sent = _condition
311
+ if not (type(sent) == "string") or #sent > OPERATION_ID_MAX_BYTES or createdAt(sent) == nil then
312
+ error(`{self.schema.name}: an operation id comes from operationId() and is at most {OPERATION_ID_MAX_BYTES} bytes`, 2)
313
+ end
314
+ if ageOf(sent, port.wall(), OPERATION_ID_MAX_AGE_SECONDS) ~= "fresh" then
315
+ return {
316
+ kind = "expired",
317
+ }
318
+ end
319
+ local decision
320
+ local input = {
321
+ schema = self.schema,
322
+ key = key,
323
+ id = sent,
324
+ change = change,
325
+ wall = port.wall,
326
+ }
327
+ local deadline = bootClock() + CALL_DEADLINE_SECONDS
328
+ local ticket = self.engine.scheduler:submit({
329
+ store = self.schema.name,
330
+ key = key,
331
+ priority = 5,
332
+ kind = "update",
333
+ dispatchDeadline = deadline,
334
+ bytes = self:weigh(key),
335
+ transform = function()
336
+ return function(current)
337
+ return updateRecord(current, input, function(d)
338
+ decision = d
339
+ return decision
340
+ end)
341
+ end
342
+ end,
343
+ })
344
+ local settled = ticket:wait(deadline) or {
345
+ kind = if ticket.dispatched then "uncertain" else "timeout",
346
+ }
347
+ if settled.kind ~= "done" then
348
+ return {
349
+ kind = settled.kind,
350
+ }
351
+ end
352
+ if settled.outcome.kind == "error" or decision == nil then
353
+ return {
354
+ kind = "unavailable",
355
+ }
356
+ end
357
+ local _exp = decision.kind
358
+ repeat
359
+ if _exp == "refused" then
360
+ return error(decision.error, 0)
361
+ end
362
+ if _exp == "written" then
363
+ self:remember(key, decision.bytes)
364
+ return {
365
+ kind = "ok",
366
+ value = decision.value,
367
+ replayed = false,
368
+ }
369
+ end
370
+ if _exp == "replay" then
371
+ local loaded = loadRecord(self.schema, decision.envelope.payload, decision.envelope.schemaVersion)
372
+ if loaded.kind ~= "ok" then
373
+ return {
374
+ kind = "unavailable",
375
+ }
376
+ end
377
+ return {
378
+ kind = "ok",
379
+ value = loaded.value,
380
+ replayed = true,
381
+ }
382
+ end
383
+ if _exp == "newer" then
384
+ return self:unavailable(key, "newer", "was written by a newer build")
385
+ end
386
+ if _exp == "unreadable" then
387
+ return self:unavailable(key, "unreadable", `did not load: {decision.reason}`)
388
+ end
389
+ if _exp == "too-large" then
390
+ return self:unavailable(key, "large", `would make the envelope {decision.bytes} bytes`)
391
+ end
392
+ return {
393
+ kind = decision.kind,
394
+ }
395
+ until true
396
+ end
397
+ end
398
+ return {
399
+ RecordStore = RecordStore,
400
+ }
@@ -0,0 +1 @@
1
+ export {};