@trioforge/crucible 0.0.0-stage → 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 (183) 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 +10 -0
  9. package/out/client/init.luau +55 -0
  10. package/out/configs.d.ts +37 -0
  11. package/out/configs.luau +46 -0
  12. package/out/crucible-behaviour.d.ts +16 -0
  13. package/out/crucible-behaviour.luau +78 -0
  14. package/out/crucible-object.d.ts +10 -0
  15. package/out/crucible-object.luau +69 -0
  16. package/out/data.d.ts +224 -0
  17. package/out/data.luau +97 -0
  18. package/out/emit/configs.d.ts +4 -0
  19. package/out/emit/configs.luau +22 -0
  20. package/out/emit/data.d.ts +6 -0
  21. package/out/emit/data.luau +19 -0
  22. package/out/emit/index.d.ts +45 -0
  23. package/out/emit/init.luau +101 -0
  24. package/out/emit/network.d.ts +6 -0
  25. package/out/emit/network.luau +36 -0
  26. package/out/emit/replicated.d.ts +4 -0
  27. package/out/emit/replicated.luau +13 -0
  28. package/out/index.d.ts +20 -0
  29. package/out/init.luau +34 -0
  30. package/out/inject.d.ts +1 -0
  31. package/out/inject.luau +7 -0
  32. package/out/internal/binding.d.ts +1 -0
  33. package/out/internal/binding.luau +615 -0
  34. package/out/internal/boot.d.ts +1 -0
  35. package/out/internal/boot.luau +562 -0
  36. package/out/internal/configs/limits.d.ts +10 -0
  37. package/out/internal/configs/limits.luau +25 -0
  38. package/out/internal/configs/port.d.ts +29 -0
  39. package/out/internal/configs/port.luau +99 -0
  40. package/out/internal/configs/runtime.d.ts +21 -0
  41. package/out/internal/configs/runtime.luau +981 -0
  42. package/out/internal/construction-claim.d.ts +1 -0
  43. package/out/internal/construction-claim.luau +26 -0
  44. package/out/internal/data/admission.d.ts +1 -0
  45. package/out/internal/data/admission.luau +673 -0
  46. package/out/internal/data/declarations.d.ts +1 -0
  47. package/out/internal/data/declarations.luau +384 -0
  48. package/out/internal/data/envelope.d.ts +1 -0
  49. package/out/internal/data/envelope.luau +318 -0
  50. package/out/internal/data/handles.d.ts +1 -0
  51. package/out/internal/data/handles.luau +255 -0
  52. package/out/internal/data/inbox-transforms.d.ts +1 -0
  53. package/out/internal/data/inbox-transforms.luau +458 -0
  54. package/out/internal/data/inbox.d.ts +1 -0
  55. package/out/internal/data/inbox.luau +437 -0
  56. package/out/internal/data/lease.d.ts +1 -0
  57. package/out/internal/data/lease.luau +304 -0
  58. package/out/internal/data/limits.d.ts +1 -0
  59. package/out/internal/data/limits.luau +234 -0
  60. package/out/internal/data/messages.d.ts +1 -0
  61. package/out/internal/data/messages.luau +69 -0
  62. package/out/internal/data/operation-id.d.ts +1 -0
  63. package/out/internal/data/operation-id.luau +61 -0
  64. package/out/internal/data/paths.d.ts +2 -0
  65. package/out/internal/data/paths.luau +323 -0
  66. package/out/internal/data/port.d.ts +1 -0
  67. package/out/internal/data/port.luau +290 -0
  68. package/out/internal/data/profiles.d.ts +1 -0
  69. package/out/internal/data/profiles.luau +862 -0
  70. package/out/internal/data/receipts.d.ts +1 -0
  71. package/out/internal/data/receipts.luau +190 -0
  72. package/out/internal/data/record-bindings.d.ts +1 -0
  73. package/out/internal/data/record-bindings.luau +92 -0
  74. package/out/internal/data/record-declarations.d.ts +1 -0
  75. package/out/internal/data/record-declarations.luau +140 -0
  76. package/out/internal/data/records.d.ts +1 -0
  77. package/out/internal/data/records.luau +400 -0
  78. package/out/internal/data/runtime.d.ts +1 -0
  79. package/out/internal/data/runtime.luau +918 -0
  80. package/out/internal/data/scheduler.d.ts +1 -0
  81. package/out/internal/data/scheduler.luau +507 -0
  82. package/out/internal/data/session.d.ts +1 -0
  83. package/out/internal/data/session.luau +234 -0
  84. package/out/internal/data/shutdown.d.ts +1 -0
  85. package/out/internal/data/shutdown.luau +91 -0
  86. package/out/internal/data/waiters.d.ts +1 -0
  87. package/out/internal/data/waiters.luau +65 -0
  88. package/out/internal/dense-list.d.ts +1 -0
  89. package/out/internal/dense-list.luau +155 -0
  90. package/out/internal/diagnostics.d.ts +1 -0
  91. package/out/internal/diagnostics.luau +29 -0
  92. package/out/internal/hooks.d.ts +1 -0
  93. package/out/internal/hooks.luau +53 -0
  94. package/out/internal/implementers.d.ts +1 -0
  95. package/out/internal/implementers.luau +119 -0
  96. package/out/internal/lifecycle.d.ts +1 -0
  97. package/out/internal/lifecycle.luau +912 -0
  98. package/out/internal/log-output.d.ts +1 -0
  99. package/out/internal/log-output.luau +106 -0
  100. package/out/internal/network/catalog.d.ts +26 -0
  101. package/out/internal/network/catalog.luau +182 -0
  102. package/out/internal/network/claims.d.ts +1 -0
  103. package/out/internal/network/claims.luau +154 -0
  104. package/out/internal/network/client-session.d.ts +1 -0
  105. package/out/internal/network/client-session.luau +403 -0
  106. package/out/internal/network/codec.d.ts +63 -0
  107. package/out/internal/network/codec.luau +1183 -0
  108. package/out/internal/network/envelope.d.ts +1 -0
  109. package/out/internal/network/envelope.luau +247 -0
  110. package/out/internal/network/limits.d.ts +26 -0
  111. package/out/internal/network/limits.luau +55 -0
  112. package/out/internal/network/port.d.ts +1 -0
  113. package/out/internal/network/port.luau +19 -0
  114. package/out/internal/network/roblox-network-port.d.ts +1 -0
  115. package/out/internal/network/roblox-network-port.luau +105 -0
  116. package/out/internal/network/schema.d.ts +72 -0
  117. package/out/internal/network/schema.luau +386 -0
  118. package/out/internal/network/server-session.d.ts +1 -0
  119. package/out/internal/network/server-session.luau +393 -0
  120. package/out/internal/network/session.d.ts +1 -0
  121. package/out/internal/network/session.luau +266 -0
  122. package/out/internal/port.d.ts +1 -0
  123. package/out/internal/port.luau +12 -0
  124. package/out/internal/reactive/context.d.ts +1 -0
  125. package/out/internal/reactive/context.luau +149 -0
  126. package/out/internal/reactive/flush.d.ts +1 -0
  127. package/out/internal/reactive/flush.luau +355 -0
  128. package/out/internal/reactive/graph.d.ts +1 -0
  129. package/out/internal/reactive/graph.luau +626 -0
  130. package/out/internal/reactive/limits.d.ts +1 -0
  131. package/out/internal/reactive/limits.luau +54 -0
  132. package/out/internal/reactive/ownership.d.ts +1 -0
  133. package/out/internal/reactive/ownership.luau +626 -0
  134. package/out/internal/registry.d.ts +1 -0
  135. package/out/internal/registry.luau +455 -0
  136. package/out/internal/roblox-port.d.ts +1 -0
  137. package/out/internal/roblox-port.luau +117 -0
  138. package/out/internal/runner.d.ts +1 -0
  139. package/out/internal/runner.luau +86 -0
  140. package/out/internal/scheduler.d.ts +1 -0
  141. package/out/internal/scheduler.luau +207 -0
  142. package/out/internal/state.d.ts +1 -0
  143. package/out/internal/state.luau +41 -0
  144. package/out/internal/sync/client-sync.d.ts +1 -0
  145. package/out/internal/sync/client-sync.luau +157 -0
  146. package/out/internal/sync/declarations.d.ts +1 -0
  147. package/out/internal/sync/declarations.luau +276 -0
  148. package/out/internal/sync/frame.d.ts +1 -0
  149. package/out/internal/sync/frame.luau +91 -0
  150. package/out/internal/sync/server-sync.d.ts +1 -0
  151. package/out/internal/sync/server-sync.luau +502 -0
  152. package/out/internal/sync/values.d.ts +1 -0
  153. package/out/internal/sync/values.luau +930 -0
  154. package/out/log-level.d.ts +7 -0
  155. package/out/log-level.luau +21 -0
  156. package/out/logging.d.ts +34 -0
  157. package/out/logging.luau +233 -0
  158. package/out/network.d.ts +66 -0
  159. package/out/network.luau +24 -0
  160. package/out/reactive.d.ts +76 -0
  161. package/out/reactive.luau +136 -0
  162. package/out/replicated.d.ts +38 -0
  163. package/out/replicated.luau +45 -0
  164. package/out/server/data.d.ts +54 -0
  165. package/out/server/data.luau +44 -0
  166. package/out/server/index.d.ts +18 -0
  167. package/out/server/init.luau +62 -0
  168. package/out/server/publish.d.ts +20 -0
  169. package/out/server/publish.luau +24 -0
  170. package/out/subsystem.d.ts +4 -0
  171. package/out/subsystem.luau +30 -0
  172. package/out/vide/index.d.ts +7 -0
  173. package/out/vide/init.luau +50 -0
  174. package/package.json +52 -5
  175. package/server/package.json +4 -0
  176. package/transformer/data.cjs +72 -0
  177. package/transformer/declarations.cjs +153 -0
  178. package/transformer/diagnostics.cjs +19 -0
  179. package/transformer/index.js +741 -0
  180. package/transformer/network.cjs +961 -0
  181. package/transformer/type-id.cjs +80 -0
  182. package/vide/package.json +4 -0
  183. package/README.md +0 -3
@@ -0,0 +1,1183 @@
1
+ -- Compiled with roblox-ts v3.0.0
2
+ local TS = _G[script]
3
+ local L = TS.import(script, script.Parent, "limits")
4
+ local _schema = TS.import(script, script.Parent, "schema")
5
+ local assertDescriptor = _schema.assertDescriptor
6
+ local checkUnion = _schema.checkUnion
7
+ local n = _schema.n
8
+ local runtimeKind = _schema.runtimeKind
9
+ local function fail(path, reason, missing)
10
+ error({
11
+ codecFailure = true,
12
+ path = path,
13
+ reason = reason,
14
+ missing = missing,
15
+ })
16
+ end
17
+ local function finite(value, path)
18
+ local _value = value
19
+ local _condition = not (type(_value) == "number")
20
+ if not _condition then
21
+ _condition = value ~= value or math.abs(value) == math.huge
22
+ end
23
+ if _condition then
24
+ fail(path, "expected finite number")
25
+ end
26
+ return value
27
+ end
28
+ local function uint(value, maximum)
29
+ local _arg0 = value >= 0 and value <= maximum and value % 1 == 0
30
+ assert(_arg0, "integer out of range")
31
+ end
32
+ --* @internal
33
+ local function validString(value)
34
+ local _arg0 = #value <= L.MAX_STRING_BYTES
35
+ assert(_arg0, "string too long")
36
+ local bytes = buffer.fromstring(value)
37
+ local offset = 0
38
+ while offset < buffer.len(bytes) do
39
+ local _original = offset
40
+ offset += 1
41
+ local lead = buffer.readu8(bytes, _original)
42
+ if lead < 0x80 then
43
+ continue
44
+ end
45
+ local count = if lead >= 0xc2 and lead <= 0xdf then 1 elseif lead >= 0xe0 and lead <= 0xef then 2 elseif lead >= 0xf0 and lead <= 0xf4 then 3 else 0
46
+ local _arg0_1 = count > 0 and count <= buffer.len(bytes) - offset
47
+ assert(_arg0_1, "invalid UTF-8")
48
+ local second = buffer.readu8(bytes, offset)
49
+ local _arg0_2 = lead ~= 0xe0 or second >= 0xa0
50
+ assert(_arg0_2, "invalid UTF-8")
51
+ local _arg0_3 = lead ~= 0xed or second < 0xa0
52
+ assert(_arg0_3, "invalid UTF-8")
53
+ local _arg0_4 = lead ~= 0xf0 or second >= 0x90
54
+ assert(_arg0_4, "invalid UTF-8")
55
+ local _arg0_5 = lead ~= 0xf4 or second < 0x90
56
+ assert(_arg0_5, "invalid UTF-8")
57
+ for i = 0, count - 1 do
58
+ local _original_1 = offset
59
+ offset += 1
60
+ local byte = buffer.readu8(bytes, _original_1)
61
+ local _arg0_6 = byte >= 0x80 and byte <= 0xbf
62
+ assert(_arg0_6, "invalid UTF-8")
63
+ end
64
+ end
65
+ end
66
+ local Budget
67
+ do
68
+ Budget = setmetatable({}, {
69
+ __tostring = function()
70
+ return "Budget"
71
+ end,
72
+ })
73
+ Budget.__index = Budget
74
+ function Budget.new(...)
75
+ local self = setmetatable({}, Budget)
76
+ return self:constructor(...) or self
77
+ end
78
+ function Budget:constructor()
79
+ self.visits = 0
80
+ self.entries = 0
81
+ self.depth = 0
82
+ end
83
+ function Budget:chargeEntries(count, path)
84
+ if count < 0 or count % 1 ~= 0 or count > L.MAX_COLLECTION_ENTRIES - self.entries then
85
+ fail(path, "entry budget exhausted")
86
+ end
87
+ self.entries += count
88
+ end
89
+ function Budget:visit(path)
90
+ self.visits += 1
91
+ if self.visits > L.MAX_VALUE_VISITS then
92
+ fail(path, "visit budget exhausted")
93
+ end
94
+ end
95
+ function Budget:enter(path)
96
+ self.depth += 1
97
+ if self.depth > L.MAX_DEPTH then
98
+ fail(path, "depth budget exhausted")
99
+ end
100
+ end
101
+ function Budget:leave()
102
+ self.depth -= 1
103
+ end
104
+ end
105
+ local Writer
106
+ do
107
+ Writer = setmetatable({}, {
108
+ __tostring = function()
109
+ return "Writer"
110
+ end,
111
+ })
112
+ Writer.__index = Writer
113
+ function Writer.new(...)
114
+ local self = setmetatable({}, Writer)
115
+ return self:constructor(...) or self
116
+ end
117
+ function Writer:constructor()
118
+ self.budget = Budget.new()
119
+ self.slots = {}
120
+ self.data = buffer.create(64)
121
+ self.cursor = 0
122
+ end
123
+ function Writer:reserve(size)
124
+ local _arg0 = size <= L.MAX_PACKET_BYTES - self.cursor
125
+ assert(_arg0, "packet too large")
126
+ if size > buffer.len(self.data) - self.cursor then
127
+ local capacity = math.min(L.MAX_PACKET_BYTES, math.max(buffer.len(self.data) * 2, self.cursor + size))
128
+ local grown = buffer.create(capacity)
129
+ buffer.copy(grown, 0, self.data, 0, self.cursor)
130
+ self.data = grown
131
+ end
132
+ local offset = self.cursor
133
+ self.cursor += size
134
+ return offset
135
+ end
136
+ function Writer:u8(value)
137
+ uint(value, 255)
138
+ local offset = self:reserve(1)
139
+ buffer.writeu8(self.data, offset, value)
140
+ end
141
+ function Writer:u16(value)
142
+ uint(value, 65535)
143
+ local offset = self:reserve(2)
144
+ buffer.writeu16(self.data, offset, value)
145
+ end
146
+ function Writer:u32(value)
147
+ uint(value, 4294967295)
148
+ local offset = self:reserve(4)
149
+ buffer.writeu32(self.data, offset, value)
150
+ end
151
+ function Writer:varuint(value)
152
+ uint(value, 4294967295)
153
+ repeat
154
+ do
155
+ local low = value % 128
156
+ value = math.floor(value / 128)
157
+ self:u8(low + (if value > 0 then 128 else 0))
158
+ end
159
+ until not (value > 0)
160
+ end
161
+ function Writer:f32(value)
162
+ finite(value, "payload")
163
+ local offset = self:reserve(4)
164
+ buffer.writef32(self.data, offset, value)
165
+ finite(buffer.readf32(self.data, offset), "payload")
166
+ end
167
+ function Writer:f64(value)
168
+ finite(value, "payload")
169
+ local offset = self:reserve(8)
170
+ buffer.writef64(self.data, offset, value)
171
+ end
172
+ function Writer:bytes(text)
173
+ validString(text)
174
+ self:varuint(#text)
175
+ local offset = self:reserve(#text)
176
+ buffer.writestring(self.data, offset, text)
177
+ end
178
+ function Writer:slot(instance)
179
+ local _arg0 = #self.slots < L.MAX_INSTANCE_SLOTS
180
+ assert(_arg0, "slot budget exhausted")
181
+ local _slots = self.slots
182
+ local _instance = instance
183
+ table.insert(_slots, _instance)
184
+ end
185
+ function Writer:finish(header)
186
+ local size = buffer.len(header) + self.cursor
187
+ local _arg0 = size <= L.MAX_PACKET_BYTES
188
+ assert(_arg0, "packet too large")
189
+ local packet = buffer.create(size)
190
+ buffer.copy(packet, 0, header)
191
+ buffer.copy(packet, buffer.len(header), self.data, 0, self.cursor)
192
+ local sidecar = {}
193
+ for i = 0, #self.slots - 1 do
194
+ sidecar[tostring(i + 1)] = self.slots[i + 1]
195
+ end
196
+ return packet, table.freeze(sidecar), #self.slots
197
+ end
198
+ end
199
+ local Reader
200
+ do
201
+ Reader = setmetatable({}, {
202
+ __tostring = function()
203
+ return "Reader"
204
+ end,
205
+ })
206
+ Reader.__index = Reader
207
+ function Reader.new(...)
208
+ local self = setmetatable({}, Reader)
209
+ return self:constructor(...) or self
210
+ end
211
+ function Reader:constructor(packet, cursor, sidecar, slotCount)
212
+ self.packet = packet
213
+ self.cursor = cursor
214
+ self.sidecar = sidecar
215
+ self.slotCount = slotCount
216
+ self.budget = Budget.new()
217
+ self.nextSlot = 1
218
+ local _packet = packet
219
+ local _condition = type(_packet) == "buffer"
220
+ if _condition then
221
+ _condition = buffer.len(packet) <= L.MAX_PACKET_BYTES
222
+ end
223
+ assert(_condition, "invalid packet")
224
+ uint(cursor, buffer.len(packet))
225
+ uint(slotCount, L.MAX_INSTANCE_SLOTS)
226
+ local _sidecar = sidecar
227
+ local _condition_1 = type(_sidecar) == "table"
228
+ if _condition_1 then
229
+ _condition_1 = getmetatable(sidecar) == nil
230
+ end
231
+ assert(_condition_1, "invalid sidecar")
232
+ -- At most R keys are valid, so the key check ends iteration by entry R + 1 (rule 71).
233
+ for key, value in pairs(sidecar) do
234
+ local slot = if type(key) == "string" then tonumber(key) else nil
235
+ local _arg0 = slot ~= nil and slot >= 1 and slot <= slotCount and slot % 1 == 0 and tostring(slot) == key and typeof(value) == "Instance"
236
+ assert(_arg0, "invalid sidecar slot")
237
+ end
238
+ end
239
+ function Reader:take(size)
240
+ local _arg0 = size <= buffer.len(self.packet) - self.cursor
241
+ assert(_arg0, "truncated packet")
242
+ local offset = self.cursor
243
+ self.cursor += size
244
+ return offset
245
+ end
246
+ function Reader:u8()
247
+ return buffer.readu8(self.packet, self:take(1))
248
+ end
249
+ function Reader:u16()
250
+ return buffer.readu16(self.packet, self:take(2))
251
+ end
252
+ function Reader:u32()
253
+ return buffer.readu32(self.packet, self:take(4))
254
+ end
255
+ function Reader:varuint()
256
+ local value = 0
257
+ for i = 0, L.VARUINT_MAX_BYTES - 1 do
258
+ local byte = self:u8()
259
+ local _arg0 = i < 4 or byte <= 15
260
+ assert(_arg0, "varuint overflow")
261
+ value += (byte % 128) * 2 ^ (7 * i)
262
+ if byte < 128 then
263
+ local _arg0_1 = i == 0 or byte ~= 0
264
+ assert(_arg0_1, "non-minimal varuint")
265
+ return value
266
+ end
267
+ end
268
+ error("varuint overflow")
269
+ end
270
+ function Reader:f32()
271
+ return finite(buffer.readf32(self.packet, self:take(4)), "payload")
272
+ end
273
+ function Reader:f64()
274
+ return finite(buffer.readf64(self.packet, self:take(8)), "payload")
275
+ end
276
+ function Reader:string()
277
+ local size = self:varuint()
278
+ local _arg0 = size <= L.MAX_STRING_BYTES
279
+ assert(_arg0, "string too long")
280
+ local text = buffer.readstring(self.packet, self:take(size), size)
281
+ validString(text)
282
+ return text
283
+ end
284
+ function Reader:count(minElementBytes, path)
285
+ local count = self:varuint()
286
+ self.budget:chargeEntries(count, path)
287
+ local _arg0 = minElementBytes == 0 or count <= math.floor((buffer.len(self.packet) - self.cursor) / minElementBytes)
288
+ assert(_arg0, "count exceeds remaining bytes")
289
+ return count
290
+ end
291
+ function Reader:slot(path)
292
+ if self.nextSlot > self.slotCount then
293
+ fail(path, "slot count exceeded")
294
+ end
295
+ local _exp = self.sidecar
296
+ local _original = self.nextSlot
297
+ self.nextSlot += 1
298
+ return _exp[tostring(_original)]
299
+ end
300
+ function Reader:finished()
301
+ return self.cursor == buffer.len(self.packet) and self.nextSlot == self.slotCount + 1
302
+ end
303
+ end
304
+ local function tableValue(value, path)
305
+ local _value = value
306
+ if not (type(_value) == "table") then
307
+ fail(path, "expected table")
308
+ end
309
+ return value
310
+ end
311
+ local function dense(value, path)
312
+ tableValue(value, path)
313
+ local array = value
314
+ local count = 0
315
+ for key in pairs(array) do
316
+ count += 1
317
+ if not (type(key) == "number") or key % 1 ~= 0 or key < 1 or key > #array then
318
+ fail(path, "expected dense array")
319
+ end
320
+ end
321
+ if count ~= #array then
322
+ fail(path, "expected dense array")
323
+ end
324
+ return array
325
+ end
326
+ --* @internal NET 60: finite, orthonormal, right-handed rows.
327
+ local function frame(values, path)
328
+ for _, v in values do
329
+ finite(v, path)
330
+ end
331
+ -- ▼ ReadonlyArray.filter ▼
332
+ local _newValue = {}
333
+ local _callback = function(_, i)
334
+ return i >= 3
335
+ end
336
+ local _length = 0
337
+ for _k, _v in values do
338
+ if _callback(_v, _k - 1, values) == true then
339
+ _length += 1
340
+ _newValue[_length] = _v
341
+ end
342
+ end
343
+ -- ▲ ReadonlyArray.filter ▲
344
+ local r = _newValue
345
+ local dot = function(a, b)
346
+ return r[a + 1] * r[b + 1] + r[a + 2] * r[b + 2] + r[a + 3] * r[b + 3]
347
+ end
348
+ for _, i in { 0, 3, 6 } do
349
+ if math.abs(math.sqrt(dot(i, i)) - 1) > L.CFRAME_TOLERANCE then
350
+ fail(path, "invalid CFrame rows")
351
+ end
352
+ end
353
+ if math.abs(dot(0, 3)) > L.CFRAME_TOLERANCE or math.abs(dot(0, 6)) > L.CFRAME_TOLERANCE or math.abs(dot(3, 6)) > L.CFRAME_TOLERANCE then
354
+ fail(path, "invalid CFrame orthogonality")
355
+ end
356
+ local det = r[1] * (r[5] * r[9] - r[6] * r[8]) - r[2] * (r[4] * r[9] - r[6] * r[7]) + r[3] * (r[4] * r[8] - r[5] * r[7])
357
+ if math.abs(det - 1) > L.CFRAME_TOLERANCE then
358
+ fail(path, "invalid CFrame determinant")
359
+ end
360
+ end
361
+ local encodeDynamic, decodeDynamic
362
+ local function compile(node, tally)
363
+ if tally == nil then
364
+ tally = {
365
+ nodes = 0,
366
+ }
367
+ end
368
+ assertDescriptor(node)
369
+ local active = {}
370
+ local function build(node, depth, empty, nilable)
371
+ if empty == nil then
372
+ empty = false
373
+ end
374
+ if nilable == nil then
375
+ nilable = false
376
+ end
377
+ tally.nodes += 1
378
+ local _condition = tally.nodes <= L.MAX_SCHEMA_NODES
379
+ if _condition then
380
+ _condition = depth <= L.MAX_DEPTH
381
+ if _condition then
382
+ local _node = node
383
+ _condition = not (active[_node] ~= nil)
384
+ end
385
+ end
386
+ assert(_condition, "schema budget exhausted")
387
+ local _node = node
388
+ active[_node] = true
389
+ local minBytes = 0
390
+ local encode
391
+ local decode
392
+ local _exp = node.kind
393
+ repeat
394
+ local _fallthrough = false
395
+ if _exp == "nil" then
396
+ encode = function(_, v, p)
397
+ if v ~= nil then
398
+ fail(p, "expected undefined")
399
+ end
400
+ end
401
+ decode = function()
402
+ return nil
403
+ end
404
+ break
405
+ end
406
+ if _exp == "literal" then
407
+ encode = function(_, v, p)
408
+ if v ~= node.value then
409
+ fail(p, "expected literal")
410
+ end
411
+ end
412
+ decode = function()
413
+ return node.value
414
+ end
415
+ break
416
+ end
417
+ if _exp == "bool" then
418
+ minBytes = 1
419
+ encode = function(w, v, p)
420
+ local _v = v
421
+ if not (type(_v) == "boolean") then
422
+ fail(p, "expected boolean")
423
+ end
424
+ w:u8(if v then 1 else 0)
425
+ end
426
+ decode = function(r, p)
427
+ local b = r:u8()
428
+ if b > 1 then
429
+ fail(p, "invalid boolean")
430
+ end
431
+ return b == 1
432
+ end
433
+ break
434
+ end
435
+ if _exp == "u8" then
436
+ _fallthrough = true
437
+ end
438
+ if _fallthrough or _exp == "u16" then
439
+ _fallthrough = true
440
+ end
441
+ if _fallthrough or _exp == "u32" then
442
+ _fallthrough = true
443
+ end
444
+ if _fallthrough or _exp == "f32" then
445
+ _fallthrough = true
446
+ end
447
+ if _fallthrough or _exp == "f64" then
448
+ local kind = node.kind
449
+ minBytes = if kind == "u8" then 1 elseif kind == "u16" then 2 elseif kind == "f64" then 8 else 4
450
+ encode = function(w, v, p)
451
+ local number = finite(v, p)
452
+ if kind == "u8" then
453
+ w:u8(number)
454
+ elseif kind == "u16" then
455
+ w:u16(number)
456
+ elseif kind == "u32" then
457
+ w:u32(number)
458
+ elseif kind == "f32" then
459
+ w:f32(number)
460
+ else
461
+ w:f64(number)
462
+ end
463
+ end
464
+ decode = function(r)
465
+ return if kind == "u8" then r:u8() elseif kind == "u16" then r:u16() elseif kind == "u32" then r:u32() elseif kind == "f32" then r:f32() else r:f64()
466
+ end
467
+ break
468
+ end
469
+ if _exp == "string" then
470
+ minBytes = 1
471
+ encode = function(w, v, p)
472
+ local _v = v
473
+ if not (type(_v) == "string") then
474
+ fail(p, "expected string")
475
+ end
476
+ w:bytes(v)
477
+ end
478
+ decode = function(r)
479
+ return r:string()
480
+ end
481
+ break
482
+ end
483
+ if _exp == "vector3" then
484
+ _fallthrough = true
485
+ end
486
+ if _fallthrough or _exp == "color3" then
487
+ _fallthrough = true
488
+ end
489
+ if _fallthrough or _exp == "cframe" then
490
+ local kind = node.kind
491
+ minBytes = if kind == "cframe" then 96 else 24
492
+ encode = function(w, v, p)
493
+ local values
494
+ local _condition_1 = kind == "vector3"
495
+ if _condition_1 then
496
+ local _v = v
497
+ _condition_1 = typeof(_v) == "Vector3"
498
+ end
499
+ if _condition_1 then
500
+ values = { v.X, v.Y, v.Z }
501
+ else
502
+ local _condition_2 = kind == "color3"
503
+ if _condition_2 then
504
+ local _v = v
505
+ _condition_2 = typeof(_v) == "Color3"
506
+ end
507
+ if _condition_2 then
508
+ values = { v.R, v.G, v.B }
509
+ else
510
+ local _condition_3 = kind == "cframe"
511
+ if _condition_3 then
512
+ local _v = v
513
+ _condition_3 = typeof(_v) == "CFrame"
514
+ end
515
+ if _condition_3 then
516
+ local _array = {}
517
+ local _length = #_array
518
+ local _array_1 = { v:GetComponents() }
519
+ table.move(_array_1, 1, #_array_1, _length + 1, _array)
520
+ values = _array
521
+ frame(values, p)
522
+ else
523
+ return fail(p, "expected datatype")
524
+ end
525
+ end
526
+ end
527
+ for _, component in values do
528
+ w:f64(component)
529
+ end
530
+ end
531
+ decode = function(r, p)
532
+ local values = {}
533
+ do
534
+ local i = 0
535
+ local _shouldIncrement = false
536
+ while true do
537
+ if _shouldIncrement then
538
+ i += 1
539
+ else
540
+ _shouldIncrement = true
541
+ end
542
+ if not (i < minBytes / 8) then
543
+ break
544
+ end
545
+ local _arg0 = r:f64()
546
+ table.insert(values, _arg0)
547
+ end
548
+ end
549
+ if kind == "vector3" then
550
+ local result = Vector3.new(values[1], values[2], values[3])
551
+ for _, value in { result.X, result.Y, result.Z } do
552
+ finite(value, p)
553
+ end
554
+ return result
555
+ end
556
+ if kind == "color3" then
557
+ local result = Color3.new(values[1], values[2], values[3])
558
+ for _, value in { result.R, result.G, result.B } do
559
+ finite(value, p)
560
+ end
561
+ return result
562
+ end
563
+ frame(values, p)
564
+ local result = CFrame.new(values[1], values[2], values[3], values[4], values[5], values[6], values[7], values[8], values[9], values[10], values[11], values[12])
565
+ for _, value in { result:GetComponents() } do
566
+ finite(value, p)
567
+ end
568
+ return result
569
+ end
570
+ break
571
+ end
572
+ if _exp == "instance" then
573
+ local check = function(v, p)
574
+ local _v = v
575
+ local _condition_1 = not (typeof(_v) == "Instance")
576
+ if not _condition_1 then
577
+ local _exp_1 = node.classes
578
+ -- ▼ ReadonlyArray.some ▼
579
+ local _result = false
580
+ local _callback = function(name)
581
+ return v:IsA(name)
582
+ end
583
+ for _k, _v_1 in _exp_1 do
584
+ if _callback(_v_1, _k - 1, _exp_1) then
585
+ _result = true
586
+ break
587
+ end
588
+ end
589
+ -- ▲ ReadonlyArray.some ▲
590
+ _condition_1 = not _result
591
+ end
592
+ if _condition_1 then
593
+ fail(p, "expected declared Instance class")
594
+ end
595
+ return v
596
+ end
597
+ encode = function(w, v, p)
598
+ return w:slot(check(v, p))
599
+ end
600
+ decode = function(r, p)
601
+ local value = r:slot(p)
602
+ if value == nil then
603
+ if nilable then
604
+ return nil
605
+ end
606
+ return fail(p, "missing Instance", true)
607
+ end
608
+ return check(value, p)
609
+ end
610
+ break
611
+ end
612
+ if _exp == "object" then
613
+ local _arg0 = empty or #node.fields > 0
614
+ assert(_arg0, "empty object type")
615
+ local _exp_1 = node.fields
616
+ -- ▼ ReadonlyArray.map ▼
617
+ local _newValue = table.create(#_exp_1)
618
+ local _callback = function(_param)
619
+ local name = _param[1]
620
+ local child = _param[2]
621
+ return { name, build(child, depth + 1) }
622
+ end
623
+ for _k, _v in _exp_1 do
624
+ _newValue[_k] = _callback(_v, _k - 1, _exp_1)
625
+ end
626
+ -- ▲ ReadonlyArray.map ▲
627
+ local fields = _newValue
628
+ for _, _binding in fields do
629
+ local codec = _binding[2]
630
+ minBytes += codec.minBytes
631
+ end
632
+ encode = function(w, v, p)
633
+ local object = tableValue(v, p)
634
+ for _, _binding in fields do
635
+ local name = _binding[1]
636
+ local c = _binding[2]
637
+ c.encode(w, object[name], `{p}.{name}`)
638
+ end
639
+ end
640
+ decode = function(r, p)
641
+ local object = {}
642
+ for _, _binding in fields do
643
+ local name = _binding[1]
644
+ local c = _binding[2]
645
+ object[name] = c.decode(r, `{p}.{name}`)
646
+ end
647
+ return object
648
+ end
649
+ break
650
+ end
651
+ if _exp == "variant" then
652
+ local _empty = empty
653
+ assert(_empty, "variant outside union")
654
+ local body = build(node.body, depth + 1, true)
655
+ minBytes = body.minBytes
656
+ encode = function(w, v, p)
657
+ if tableValue(v, p)[node.key] ~= node.value then
658
+ fail(p, "invalid discriminant")
659
+ end
660
+ body.encode(w, v, p)
661
+ end
662
+ decode = function(r, p)
663
+ local v = body.decode(r, p)
664
+ v[node.key] = node.value
665
+ return v
666
+ end
667
+ break
668
+ end
669
+ if _exp == "union" then
670
+ checkUnion(node.branches)
671
+ local _exp_1 = node.branches
672
+ -- ▼ ReadonlyArray.map ▼
673
+ local _newValue = table.create(#_exp_1)
674
+ local _callback = function(b)
675
+ local _exp_2 = b
676
+ local _exp_3 = depth + 1
677
+ local _exp_4 = b.kind == "variant"
678
+ local _exp_5 = node.branches
679
+ -- ▼ ReadonlyArray.some ▼
680
+ local _result = false
681
+ local _callback_1 = function(b)
682
+ return b.kind == "nil"
683
+ end
684
+ for _k, _v in _exp_5 do
685
+ if _callback_1(_v, _k - 1, _exp_5) then
686
+ _result = true
687
+ break
688
+ end
689
+ end
690
+ -- ▲ ReadonlyArray.some ▲
691
+ return build(_exp_2, _exp_3, _exp_4, _result)
692
+ end
693
+ for _k, _v in _exp_1 do
694
+ _newValue[_k] = _callback(_v, _k - 1, _exp_1)
695
+ end
696
+ -- ▲ ReadonlyArray.map ▲
697
+ local branches = _newValue
698
+ -- ▼ ReadonlyArray.map ▼
699
+ local _newValue_1 = table.create(#branches)
700
+ local _callback_1 = function(c)
701
+ return c.minBytes
702
+ end
703
+ for _k, _v in branches do
704
+ _newValue_1[_k] = _callback_1(_v, _k - 1, branches)
705
+ end
706
+ -- ▲ ReadonlyArray.map ▲
707
+ minBytes = 1 + math.min(unpack(_newValue_1))
708
+ local choose = function(v, p)
709
+ for i = 0, #node.branches - 1 do
710
+ local b = node.branches[i + 1]
711
+ if (b.kind == "nil" and v == nil) or (b.kind == "literal" and v == b.value) then
712
+ return i
713
+ end
714
+ end
715
+ if v ~= nil then
716
+ for i = 0, #node.branches - 1 do
717
+ local b = node.branches[i + 1]
718
+ if b.kind == "variant" then
719
+ local _v = v
720
+ local _condition_1 = type(_v) == "table"
721
+ if _condition_1 then
722
+ _condition_1 = v[b.key] == b.value
723
+ end
724
+ if _condition_1 then
725
+ return i
726
+ end
727
+ else
728
+ local _condition_1 = b.kind ~= "nil" and b.kind ~= "literal"
729
+ if _condition_1 then
730
+ local _exp_2 = runtimeKind(b)
731
+ local _v = v
732
+ _condition_1 = _exp_2 == typeof(_v)
733
+ end
734
+ if _condition_1 then
735
+ return i
736
+ end
737
+ end
738
+ end
739
+ end
740
+ return fail(p, "no union branch")
741
+ end
742
+ encode = function(w, v, p)
743
+ w.budget:visit(p)
744
+ local tag = choose(v, p)
745
+ w:varuint(tag)
746
+ branches[tag + 1].encode(w, v, p)
747
+ end
748
+ decode = function(r, p)
749
+ r.budget:visit(p)
750
+ local tag = r:varuint()
751
+ if tag >= #branches then
752
+ fail(p, "bad union tag")
753
+ end
754
+ return branches[tag + 1].decode(r, p)
755
+ end
756
+ break
757
+ end
758
+ if _exp == "array" then
759
+ _fallthrough = true
760
+ end
761
+ if _fallthrough or _exp == "tuple" then
762
+ local array = node.kind == "array"
763
+ local _result
764
+ if array then
765
+ _result = { build(node.element, depth + 1) }
766
+ else
767
+ local _exp_1 = node.elements
768
+ -- ▼ ReadonlyArray.map ▼
769
+ local _newValue = table.create(#_exp_1)
770
+ local _callback = function(b)
771
+ return build(b, depth + 1)
772
+ end
773
+ for _k, _v in _exp_1 do
774
+ _newValue[_k] = _callback(_v, _k - 1, _exp_1)
775
+ end
776
+ -- ▲ ReadonlyArray.map ▲
777
+ _result = _newValue
778
+ end
779
+ local children = _result
780
+ local _result_1
781
+ if array then
782
+ _result_1 = 1
783
+ else
784
+ -- ▼ ReadonlyArray.reduce ▼
785
+ local _result_2 = 0
786
+ local _callback = function(sum, c)
787
+ return sum + c.minBytes
788
+ end
789
+ for _i = 1, #children do
790
+ _result_2 = _callback(_result_2, children[_i], _i - 1, children)
791
+ end
792
+ -- ▲ ReadonlyArray.reduce ▲
793
+ _result_1 = _result_2
794
+ end
795
+ minBytes = _result_1
796
+ encode = function(w, v, p)
797
+ local values = dense(v, p)
798
+ if not array and #values ~= #children then
799
+ fail(p, "tuple length")
800
+ end
801
+ if array then
802
+ w.budget:chargeEntries(#values, p)
803
+ w:varuint(#values)
804
+ end
805
+ for i = 0, #values - 1 do
806
+ if values[i + 1] == nil then
807
+ fail(p, "nil element")
808
+ end
809
+ children[(if array then 0 else i) + 1].encode(w, values[i + 1], `{p}[]`)
810
+ end
811
+ end
812
+ decode = function(r, p)
813
+ local count = if array then r:count(children[1].minBytes, p) else #children
814
+ local values = {}
815
+ do
816
+ local i = 0
817
+ local _shouldIncrement = false
818
+ while true do
819
+ if _shouldIncrement then
820
+ i += 1
821
+ else
822
+ _shouldIncrement = true
823
+ end
824
+ if not (i < count) then
825
+ break
826
+ end
827
+ local value = children[(if array then 0 else i) + 1].decode(r, `{p}[]`)
828
+ if value == nil then
829
+ fail(p, "nil element")
830
+ end
831
+ table.insert(values, value)
832
+ end
833
+ end
834
+ return values
835
+ end
836
+ break
837
+ end
838
+ if _exp == "map" then
839
+ local key = build(node.key, depth + 1)
840
+ local value = build(node.value, depth + 1)
841
+ minBytes = 1
842
+ local order = function(v)
843
+ if node.key.kind == "union" then
844
+ local _exp_1 = node.key.branches
845
+ -- ▼ ReadonlyArray.findIndex ▼
846
+ local _callback = function(b)
847
+ return b.kind == "literal" and b.value == v
848
+ end
849
+ local _result = -1
850
+ for _i, _v in _exp_1 do
851
+ if _callback(_v, _i - 1, _exp_1) == true then
852
+ _result = _i - 1
853
+ break
854
+ end
855
+ end
856
+ -- ▲ ReadonlyArray.findIndex ▲
857
+ local index = _result
858
+ local _arg0 = index >= 0
859
+ assert(_arg0, "invalid map key")
860
+ return index
861
+ end
862
+ return v
863
+ end
864
+ local less = function(a, b)
865
+ local x = order(a)
866
+ local y = order(b)
867
+ return if type(x) == "number" and type(y) == "number" then x < y else x < y
868
+ end
869
+ encode = function(w, v, p)
870
+ local entries = {}
871
+ for k, val in pairs(tableValue(v, p)) do
872
+ local _arg0 = { k, val }
873
+ table.insert(entries, _arg0)
874
+ end
875
+ w.budget:chargeEntries(#entries, p)
876
+ table.sort(entries, function(a, b)
877
+ return less(a[1], b[1])
878
+ end)
879
+ w:varuint(#entries)
880
+ for _, _binding in entries do
881
+ local k = _binding[1]
882
+ local val = _binding[2]
883
+ key.encode(w, k, `{p}.key`)
884
+ if val == nil then
885
+ fail(p, "nil map value")
886
+ end
887
+ value.encode(w, val, `{p}[]`)
888
+ end
889
+ end
890
+ decode = function(r, p)
891
+ local count = r:count(key.minBytes + value.minBytes, p)
892
+ local values = {}
893
+ local last
894
+ do
895
+ local i = 0
896
+ local _shouldIncrement = false
897
+ while true do
898
+ if _shouldIncrement then
899
+ i += 1
900
+ else
901
+ _shouldIncrement = true
902
+ end
903
+ if not (i < count) then
904
+ break
905
+ end
906
+ local k = key.decode(r, `{p}.key`)
907
+ if i > 0 and not less(last, k) then
908
+ fail(p, "map keys not strictly ascending")
909
+ end
910
+ last = k
911
+ local v = value.decode(r, `{p}[]`)
912
+ if v == nil then
913
+ fail(p, "nil map value")
914
+ end
915
+ values[k] = v
916
+ end
917
+ end
918
+ return values
919
+ end
920
+ break
921
+ end
922
+ if _exp == "dynamic" then
923
+ minBytes = 1
924
+ encode = function(w, v, p)
925
+ return encodeDynamic(w, v, p, {})
926
+ end
927
+ decode = function(r, p)
928
+ return decodeDynamic(r, p, true)
929
+ end
930
+ break
931
+ end
932
+ error("unknown descriptor kind")
933
+ until true
934
+ local _node_1 = node
935
+ active[_node_1] = nil
936
+ local container = node.kind == "object" or node.kind == "array" or node.kind == "tuple" or node.kind == "map"
937
+ -- A union charges its selection, a variant is its body's value, and a dynamic value charges itself.
938
+ local visits = node.kind ~= "union" and node.kind ~= "variant" and node.kind ~= "dynamic"
939
+ return {
940
+ node = node,
941
+ minBytes = minBytes,
942
+ encode = function(w, v, p)
943
+ if visits then
944
+ w.budget:visit(p)
945
+ end
946
+ if container then
947
+ w.budget:enter(p)
948
+ end
949
+ local ok, reason = pcall(function()
950
+ return encode(w, v, p)
951
+ end)
952
+ if not ok then
953
+ if type(reason) == "table" and reason.codecFailure == true then
954
+ error(reason)
955
+ end
956
+ fail(p, tostring(reason))
957
+ end
958
+ if container then
959
+ w.budget:leave()
960
+ end
961
+ end,
962
+ decode = function(r, p)
963
+ if visits then
964
+ r.budget:visit(p)
965
+ end
966
+ if container then
967
+ r.budget:enter(p)
968
+ end
969
+ local value = decode(r, p)
970
+ if container then
971
+ r.budget:leave()
972
+ end
973
+ return value
974
+ end,
975
+ }
976
+ end
977
+ return build(node, 1)
978
+ end
979
+ local datatypes = {
980
+ vector3 = compile(n.vector3),
981
+ color3 = compile(n.color3),
982
+ cframe = compile(n.cframe),
983
+ }
984
+ function encodeDynamic(w, value, path, active, topLevel, arrayElement)
985
+ if topLevel == nil then
986
+ topLevel = true
987
+ end
988
+ if arrayElement == nil then
989
+ arrayElement = false
990
+ end
991
+ local _value = value
992
+ local _condition = typeof(_value) == "Vector3"
993
+ if not _condition then
994
+ local _value_1 = value
995
+ _condition = typeof(_value_1) == "Color3"
996
+ if not _condition then
997
+ local _value_2 = value
998
+ _condition = typeof(_value_2) == "CFrame"
999
+ end
1000
+ end
1001
+ local datatype = _condition
1002
+ if not datatype then
1003
+ w.budget:visit(path)
1004
+ end
1005
+ if value == nil then
1006
+ if not topLevel then
1007
+ fail(path, "nil nested dynamic value")
1008
+ end
1009
+ w:u8(0)
1010
+ else
1011
+ local _value_1 = value
1012
+ if type(_value_1) == "boolean" then
1013
+ w:u8(if value then 2 else 1)
1014
+ else
1015
+ local _value_2 = value
1016
+ if type(_value_2) == "number" then
1017
+ w:u8(3)
1018
+ w:f64(value)
1019
+ else
1020
+ local _value_3 = value
1021
+ if type(_value_3) == "string" then
1022
+ w:u8(4)
1023
+ w:bytes(value)
1024
+ else
1025
+ local _value_4 = value
1026
+ if typeof(_value_4) == "Instance" then
1027
+ if arrayElement then
1028
+ fail(path, "Instance array element")
1029
+ end
1030
+ w:u8(10)
1031
+ w:slot(value)
1032
+ else
1033
+ local _value_5 = value
1034
+ local _condition_1 = typeof(_value_5) == "Vector3"
1035
+ if not _condition_1 then
1036
+ local _value_6 = value
1037
+ _condition_1 = typeof(_value_6) == "Color3"
1038
+ if not _condition_1 then
1039
+ local _value_7 = value
1040
+ _condition_1 = typeof(_value_7) == "CFrame"
1041
+ end
1042
+ end
1043
+ if _condition_1 then
1044
+ local _value_6 = value
1045
+ local _result
1046
+ if typeof(_value_6) == "Vector3" then
1047
+ _result = "vector3"
1048
+ else
1049
+ local _value_7 = value
1050
+ _result = if typeof(_value_7) == "Color3" then "color3" else "cframe"
1051
+ end
1052
+ local kind = _result
1053
+ w:u8(if kind == "vector3" then 7 elseif kind == "color3" then 8 else 9)
1054
+ datatypes[kind].encode(w, value, path)
1055
+ else
1056
+ local object = tableValue(value, path)
1057
+ if getmetatable(object) ~= nil then
1058
+ fail(path, "dynamic metatable")
1059
+ end
1060
+ if active[object] ~= nil then
1061
+ fail(path, "cycle")
1062
+ end
1063
+ active[object] = true
1064
+ w.budget:enter(path)
1065
+ local keys = {}
1066
+ local numeric = 0
1067
+ for k in pairs(object) do
1068
+ if type(k) == "number" then
1069
+ numeric += 1
1070
+ elseif type(k) == "string" then
1071
+ table.insert(keys, k)
1072
+ else
1073
+ fail(path, "invalid dynamic key")
1074
+ end
1075
+ end
1076
+ if numeric > 0 then
1077
+ if not (#keys == 0) then
1078
+ fail(path, "mixed dynamic table")
1079
+ end
1080
+ local values = dense(value, path)
1081
+ w.budget:chargeEntries(#values, path)
1082
+ w:u8(5)
1083
+ w:varuint(#values)
1084
+ for _, v in values do
1085
+ encodeDynamic(w, v, `{path}[]`, active, false, true)
1086
+ end
1087
+ else
1088
+ table.sort(keys)
1089
+ w.budget:chargeEntries(#keys, path)
1090
+ w:u8(6)
1091
+ w:varuint(#keys)
1092
+ for _, key in keys do
1093
+ w.budget:visit(path)
1094
+ w:bytes(key)
1095
+ encodeDynamic(w, object[key], `{path}[]`, active, false)
1096
+ end
1097
+ end
1098
+ w.budget:leave()
1099
+ active[object] = nil
1100
+ end
1101
+ end
1102
+ end
1103
+ end
1104
+ end
1105
+ end
1106
+ end
1107
+ function decodeDynamic(r, path, topLevel, arrayElement)
1108
+ if arrayElement == nil then
1109
+ arrayElement = false
1110
+ end
1111
+ local tag = r:u8()
1112
+ if tag >= 7 and tag <= 9 then
1113
+ return datatypes[if tag == 7 then "vector3" elseif tag == 8 then "color3" else "cframe"].decode(r, path)
1114
+ end
1115
+ r.budget:visit(path)
1116
+ if tag == 0 then
1117
+ if not topLevel then
1118
+ fail(path, "nil nested dynamic value")
1119
+ end
1120
+ return nil
1121
+ end
1122
+ if tag == 1 or tag == 2 then
1123
+ return tag == 2
1124
+ end
1125
+ if tag == 3 then
1126
+ return r:f64()
1127
+ end
1128
+ if tag == 4 then
1129
+ return r:string()
1130
+ end
1131
+ if tag == 10 then
1132
+ if arrayElement then
1133
+ fail(path, "Instance array element")
1134
+ end
1135
+ return r:slot(path)
1136
+ end
1137
+ if tag ~= 5 and tag ~= 6 then
1138
+ return fail(path, "bad dynamic tag")
1139
+ end
1140
+ r.budget:enter(path)
1141
+ local count = r:count(if tag == 5 then 1 else 2, path)
1142
+ local values = {}
1143
+ local array = {}
1144
+ local last
1145
+ do
1146
+ local i = 0
1147
+ local _shouldIncrement = false
1148
+ while true do
1149
+ if _shouldIncrement then
1150
+ i += 1
1151
+ else
1152
+ _shouldIncrement = true
1153
+ end
1154
+ if not (i < count) then
1155
+ break
1156
+ end
1157
+ if tag == 5 then
1158
+ local _arg0 = decodeDynamic(r, `{path}[]`, false, true)
1159
+ table.insert(array, _arg0)
1160
+ else
1161
+ r.budget:visit(path)
1162
+ local key = r:string()
1163
+ if last ~= nil and key <= last then
1164
+ fail(path, "record keys not strictly ascending")
1165
+ end
1166
+ last = key
1167
+ values[key] = decodeDynamic(r, `{path}[]`, false)
1168
+ end
1169
+ end
1170
+ end
1171
+ r.budget:leave()
1172
+ return if tag == 5 then array else values
1173
+ end
1174
+ return {
1175
+ validString = validString,
1176
+ frame = frame,
1177
+ compile = compile,
1178
+ encodeDynamic = encodeDynamic,
1179
+ decodeDynamic = decodeDynamic,
1180
+ Budget = Budget,
1181
+ Writer = Writer,
1182
+ Reader = Reader,
1183
+ }