@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.
- package/LICENSE +21 -0
- package/client/package.json +4 -0
- package/emit/package.json +4 -0
- package/out/all.d.ts +7 -0
- package/out/all.luau +2 -0
- package/out/attribute.d.ts +7 -0
- package/out/attribute.luau +7 -0
- package/out/client/index.d.ts +10 -0
- package/out/client/init.luau +55 -0
- package/out/configs.d.ts +37 -0
- package/out/configs.luau +46 -0
- package/out/crucible-behaviour.d.ts +16 -0
- package/out/crucible-behaviour.luau +78 -0
- package/out/crucible-object.d.ts +10 -0
- package/out/crucible-object.luau +69 -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 +45 -0
- package/out/emit/init.luau +101 -0
- 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 +20 -0
- package/out/init.luau +34 -0
- package/out/inject.d.ts +1 -0
- package/out/inject.luau +7 -0
- package/out/internal/binding.d.ts +1 -0
- package/out/internal/binding.luau +615 -0
- package/out/internal/boot.d.ts +1 -0
- package/out/internal/boot.luau +562 -0
- 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/construction-claim.d.ts +1 -0
- package/out/internal/construction-claim.luau +26 -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/dense-list.d.ts +1 -0
- package/out/internal/dense-list.luau +155 -0
- package/out/internal/diagnostics.d.ts +1 -0
- package/out/internal/diagnostics.luau +29 -0
- package/out/internal/hooks.d.ts +1 -0
- package/out/internal/hooks.luau +53 -0
- package/out/internal/implementers.d.ts +1 -0
- package/out/internal/implementers.luau +119 -0
- package/out/internal/lifecycle.d.ts +1 -0
- package/out/internal/lifecycle.luau +912 -0
- package/out/internal/log-output.d.ts +1 -0
- package/out/internal/log-output.luau +106 -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/port.d.ts +1 -0
- package/out/internal/port.luau +12 -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.d.ts +1 -0
- package/out/internal/registry.luau +455 -0
- package/out/internal/roblox-port.d.ts +1 -0
- package/out/internal/roblox-port.luau +117 -0
- package/out/internal/runner.d.ts +1 -0
- package/out/internal/runner.luau +86 -0
- package/out/internal/scheduler.d.ts +1 -0
- package/out/internal/scheduler.luau +207 -0
- package/out/internal/state.d.ts +1 -0
- package/out/internal/state.luau +41 -0
- 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/log-level.d.ts +7 -0
- package/out/log-level.luau +21 -0
- package/out/logging.d.ts +34 -0
- package/out/logging.luau +233 -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 +18 -0
- package/out/server/init.luau +62 -0
- package/out/server/publish.d.ts +20 -0
- package/out/server/publish.luau +24 -0
- package/out/subsystem.d.ts +4 -0
- package/out/subsystem.luau +30 -0
- package/out/vide/index.d.ts +7 -0
- package/out/vide/init.luau +50 -0
- package/package.json +52 -5
- package/server/package.json +4 -0
- package/transformer/data.cjs +72 -0
- package/transformer/declarations.cjs +153 -0
- package/transformer/diagnostics.cjs +19 -0
- package/transformer/index.js +741 -0
- package/transformer/network.cjs +961 -0
- package/transformer/type-id.cjs +80 -0
- package/vide/package.json +4 -0
- package/README.md +0 -3
|
@@ -0,0 +1,255 @@
|
|
|
1
|
+
-- Compiled with roblox-ts v3.0.0
|
|
2
|
+
local TS = _G[script]
|
|
3
|
+
local _graph = TS.import(script, script.Parent.Parent, "reactive", "graph")
|
|
4
|
+
local createComputedNode = _graph.createComputedNode
|
|
5
|
+
local readComputed = _graph.readComputed
|
|
6
|
+
local _paths = TS.import(script, script.Parent, "paths")
|
|
7
|
+
local childNode = _paths.childNode
|
|
8
|
+
local describePath = _paths.describePath
|
|
9
|
+
local getPath = _paths.getPath
|
|
10
|
+
local luaKey = _paths.luaKey
|
|
11
|
+
local setPath = _paths.setPath
|
|
12
|
+
local tableBranch = _paths.tableBranch
|
|
13
|
+
--[[
|
|
14
|
+
*
|
|
15
|
+
* @internal What a tree of handles reads from and writes to: a player's slot on the server, an inert source for a
|
|
16
|
+
* player who is not present, or the owner's views on the client (rules 115, 117, 124, 126).
|
|
17
|
+
|
|
18
|
+
]]
|
|
19
|
+
--* The node lives in each handle's own metatable: Luau's weak keys are not ephemerons, so no shared table maps them.
|
|
20
|
+
local NODE = {}
|
|
21
|
+
local RESERVED = {
|
|
22
|
+
["set"] = true,
|
|
23
|
+
["update"] = true,
|
|
24
|
+
["insert"] = true,
|
|
25
|
+
["remove"] = true,
|
|
26
|
+
}
|
|
27
|
+
local function nodeOf(target)
|
|
28
|
+
local _target = target
|
|
29
|
+
if not (type(_target) == "table") then
|
|
30
|
+
return nil
|
|
31
|
+
end
|
|
32
|
+
local meta = getmetatable(target)
|
|
33
|
+
return if type(meta) == "table" then (meta[NODE]) else nil
|
|
34
|
+
end
|
|
35
|
+
local function mapKey(name)
|
|
36
|
+
error(`a map key named "{name}" has no handle; read the map and write it with the map's update`, 3)
|
|
37
|
+
end
|
|
38
|
+
local function writer(handle, method)
|
|
39
|
+
local node = nodeOf(handle)
|
|
40
|
+
if node == nil then
|
|
41
|
+
mapKey(method)
|
|
42
|
+
end
|
|
43
|
+
return node
|
|
44
|
+
end
|
|
45
|
+
--* Rule 118: the parent of the written position exists and is a table, and an index is an integer in range.
|
|
46
|
+
local function check(node, root, path, extra)
|
|
47
|
+
local name = node.source.name
|
|
48
|
+
local parent = root
|
|
49
|
+
for i = 0, #path - 2 do
|
|
50
|
+
parent = parent[luaKey(path[i + 1])]
|
|
51
|
+
local _parent = parent
|
|
52
|
+
if not (type(_parent) == "table") then
|
|
53
|
+
error(`{describePath(name, path)}: its parent does not exist; set the parent whole`, 0)
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
local last = path[#path]
|
|
57
|
+
if type(last) == "number" then
|
|
58
|
+
local highest = #parent - 1 + extra
|
|
59
|
+
if last % 1 ~= 0 or last < 0 or last > highest then
|
|
60
|
+
error(`{describePath(name, path)}: {if highest < 0 then "the array is empty" else `the index is not an integer from 0 to {highest}`}`, 0)
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
return parent
|
|
64
|
+
end
|
|
65
|
+
local function method(name, run)
|
|
66
|
+
return setmetatable({}, {
|
|
67
|
+
__call = function(_, handle, ...)
|
|
68
|
+
local args = { ... }
|
|
69
|
+
return run(writer(handle, name), unpack(args))
|
|
70
|
+
end,
|
|
71
|
+
__index = function()
|
|
72
|
+
return mapKey(name)
|
|
73
|
+
end,
|
|
74
|
+
__newindex = function()
|
|
75
|
+
return mapKey(name)
|
|
76
|
+
end,
|
|
77
|
+
})
|
|
78
|
+
end
|
|
79
|
+
--* The four write members, shared by every server handle (rules 118 to 120, 122).
|
|
80
|
+
local arrayAt
|
|
81
|
+
local METHODS = {
|
|
82
|
+
set = method("set", function(node, value)
|
|
83
|
+
return node.source:write(function(root)
|
|
84
|
+
if #node.path > 0 then
|
|
85
|
+
check(node, root, node.path, 0)
|
|
86
|
+
end
|
|
87
|
+
return setPath(root, node.path, value)
|
|
88
|
+
end)
|
|
89
|
+
end),
|
|
90
|
+
update = method("update", function(node, change)
|
|
91
|
+
return node.source:write(function(root)
|
|
92
|
+
if #node.path > 0 then
|
|
93
|
+
check(node, root, node.path, 0)
|
|
94
|
+
end
|
|
95
|
+
return setPath(root, node.path, change(getPath(root, node.path)))
|
|
96
|
+
end)
|
|
97
|
+
end),
|
|
98
|
+
insert = method("insert", function(node, value, index)
|
|
99
|
+
return node.source:write(function(root)
|
|
100
|
+
local array = arrayAt(node, root)
|
|
101
|
+
local position = if index == nil then #array else index
|
|
102
|
+
local _exp = node
|
|
103
|
+
local _exp_1 = root
|
|
104
|
+
local _array = {}
|
|
105
|
+
local _length = #_array
|
|
106
|
+
local _array_1 = node.path
|
|
107
|
+
local _Length = #_array_1
|
|
108
|
+
table.move(_array_1, 1, _Length, _length + 1, _array)
|
|
109
|
+
_length += _Length
|
|
110
|
+
_array[_length + 1] = position
|
|
111
|
+
check(_exp, _exp_1, _array, 1)
|
|
112
|
+
local copy = table.clone(array)
|
|
113
|
+
local _value = value
|
|
114
|
+
table.insert(copy, position + 1, _value)
|
|
115
|
+
return setPath(root, node.path, copy)
|
|
116
|
+
end)
|
|
117
|
+
end),
|
|
118
|
+
remove = method("remove", function(node, index)
|
|
119
|
+
return node.source:write(function(root)
|
|
120
|
+
local array = arrayAt(node, root)
|
|
121
|
+
local _exp = node
|
|
122
|
+
local _exp_1 = root
|
|
123
|
+
local _array = {}
|
|
124
|
+
local _length = #_array
|
|
125
|
+
local _array_1 = node.path
|
|
126
|
+
local _Length = #_array_1
|
|
127
|
+
table.move(_array_1, 1, _Length, _length + 1, _array)
|
|
128
|
+
_length += _Length
|
|
129
|
+
_array[_length + 1] = index
|
|
130
|
+
check(_exp, _exp_1, _array, 0)
|
|
131
|
+
local copy = table.clone(array)
|
|
132
|
+
local _index = index
|
|
133
|
+
table.remove(copy, _index + 1)
|
|
134
|
+
return setPath(root, node.path, copy)
|
|
135
|
+
end)
|
|
136
|
+
end),
|
|
137
|
+
}
|
|
138
|
+
function arrayAt(node, root)
|
|
139
|
+
local value = getPath(root, node.path)
|
|
140
|
+
if not (type(value) == "table") then
|
|
141
|
+
error(`{describePath(node.source.name, node.path)}: it does not exist; set it whole`, 0)
|
|
142
|
+
end
|
|
143
|
+
return value
|
|
144
|
+
end
|
|
145
|
+
local makeHandle
|
|
146
|
+
local function child(parent, node, key)
|
|
147
|
+
local schema = childNode(node.schema, key)
|
|
148
|
+
local _condition = schema ~= nil
|
|
149
|
+
if _condition then
|
|
150
|
+
local _result = tableBranch(node.schema)
|
|
151
|
+
if _result ~= nil then
|
|
152
|
+
_result = _result.kind
|
|
153
|
+
end
|
|
154
|
+
_condition = _result == "object"
|
|
155
|
+
end
|
|
156
|
+
local strong = _condition
|
|
157
|
+
local cache = if strong then node.fields else node.entries
|
|
158
|
+
local _key = key
|
|
159
|
+
local found = cache[_key]
|
|
160
|
+
if found ~= nil then
|
|
161
|
+
return found
|
|
162
|
+
end
|
|
163
|
+
local _object = {
|
|
164
|
+
source = node.source,
|
|
165
|
+
}
|
|
166
|
+
local _left = "path"
|
|
167
|
+
local _array = {}
|
|
168
|
+
local _length = #_array
|
|
169
|
+
local _array_1 = node.path
|
|
170
|
+
local _Length = #_array_1
|
|
171
|
+
table.move(_array_1, 1, _Length, _length + 1, _array)
|
|
172
|
+
_length += _Length
|
|
173
|
+
_array[_length + 1] = key
|
|
174
|
+
_object[_left] = _array
|
|
175
|
+
_object.schema = schema
|
|
176
|
+
_object.parent = parent
|
|
177
|
+
_object.fields = {}
|
|
178
|
+
_object.entries = setmetatable({}, {
|
|
179
|
+
__mode = "v",
|
|
180
|
+
})
|
|
181
|
+
local made = makeHandle(_object)
|
|
182
|
+
local _key_1 = key
|
|
183
|
+
cache[_key_1] = made
|
|
184
|
+
return made
|
|
185
|
+
end
|
|
186
|
+
local handleCall = function(handle, ...)
|
|
187
|
+
local args = { ... }
|
|
188
|
+
local node = nodeOf(handle)
|
|
189
|
+
local source = node.source
|
|
190
|
+
if #args > 0 then
|
|
191
|
+
error(if source.client then `{source.name}() is read-only on the client` else `{source.name}: read a handle with h(); write it with set or update`, 2)
|
|
192
|
+
end
|
|
193
|
+
source:beforeRead()
|
|
194
|
+
if node.read == nil then
|
|
195
|
+
local path = node.path
|
|
196
|
+
node.read = createComputedNode(function()
|
|
197
|
+
return source:read(path)
|
|
198
|
+
end)
|
|
199
|
+
end
|
|
200
|
+
return readComputed(node.read)
|
|
201
|
+
end
|
|
202
|
+
local handleIndex = function(handle, key)
|
|
203
|
+
local node = nodeOf(handle)
|
|
204
|
+
local _key = key
|
|
205
|
+
local _condition = type(_key) == "string"
|
|
206
|
+
if _condition then
|
|
207
|
+
local _key_1 = key
|
|
208
|
+
_condition = RESERVED[_key_1] ~= nil
|
|
209
|
+
if _condition then
|
|
210
|
+
_condition = not node.source.client
|
|
211
|
+
end
|
|
212
|
+
end
|
|
213
|
+
if _condition then
|
|
214
|
+
local _key_1 = key
|
|
215
|
+
return METHODS[_key_1]
|
|
216
|
+
end
|
|
217
|
+
local _key_1 = key
|
|
218
|
+
local _condition_1 = not (type(_key_1) == "string")
|
|
219
|
+
if _condition_1 then
|
|
220
|
+
local _key_2 = key
|
|
221
|
+
_condition_1 = not (type(_key_2) == "number")
|
|
222
|
+
end
|
|
223
|
+
if _condition_1 then
|
|
224
|
+
error("a handle's children are reached by key or index", 2)
|
|
225
|
+
end
|
|
226
|
+
return child(handle, node, key)
|
|
227
|
+
end
|
|
228
|
+
local handleNewindex = function()
|
|
229
|
+
return error("a handle has no fields to assign; write with set or update", 2)
|
|
230
|
+
end
|
|
231
|
+
--* @internal Rule 122: an empty table with a metatable of its own, holding its node under a private key.
|
|
232
|
+
function makeHandle(node)
|
|
233
|
+
local meta = {
|
|
234
|
+
__call = handleCall,
|
|
235
|
+
__index = handleIndex,
|
|
236
|
+
__newindex = handleNewindex,
|
|
237
|
+
[NODE] = node,
|
|
238
|
+
}
|
|
239
|
+
return setmetatable({}, meta)
|
|
240
|
+
end
|
|
241
|
+
--* @internal A root handle over `source`, typed by the descriptor `schema` (rules 115, 124).
|
|
242
|
+
local function rootHandle(source, schema)
|
|
243
|
+
return makeHandle({
|
|
244
|
+
source = source,
|
|
245
|
+
path = {},
|
|
246
|
+
schema = schema,
|
|
247
|
+
fields = {},
|
|
248
|
+
entries = setmetatable({}, {
|
|
249
|
+
__mode = "v",
|
|
250
|
+
}),
|
|
251
|
+
})
|
|
252
|
+
end
|
|
253
|
+
return {
|
|
254
|
+
rootHandle = rootHandle,
|
|
255
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,458 @@
|
|
|
1
|
+
-- Compiled with roblox-ts v3.0.0
|
|
2
|
+
local TS = _G[script]
|
|
3
|
+
local _envelope = TS.import(script, script.Parent, "envelope")
|
|
4
|
+
local emptyEnvelope = _envelope.emptyEnvelope
|
|
5
|
+
local envelopeBytes = _envelope.envelopeBytes
|
|
6
|
+
local jsonBytes = _envelope.jsonBytes
|
|
7
|
+
local parseEnvelope = _envelope.parseEnvelope
|
|
8
|
+
local _limits = TS.import(script, script.Parent, "limits")
|
|
9
|
+
local INBOX_MAX_BYTES = _limits.INBOX_MAX_BYTES
|
|
10
|
+
local INBOX_MAX_MESSAGES = _limits.INBOX_MAX_MESSAGES
|
|
11
|
+
local MAX_ENVELOPE_BYTES = _limits.MAX_ENVELOPE_BYTES
|
|
12
|
+
local MESSAGE_ID_MAX_AGE_SECONDS = _limits.MESSAGE_ID_MAX_AGE_SECONDS
|
|
13
|
+
local QUARANTINE_MAX_BYTES = _limits.QUARANTINE_MAX_BYTES
|
|
14
|
+
local QUARANTINE_MAX_MESSAGES = _limits.QUARANTINE_MAX_MESSAGES
|
|
15
|
+
local SEEN_MAX_MESSAGES = _limits.SEEN_MAX_MESSAGES
|
|
16
|
+
local _operation_id = TS.import(script, script.Parent, "operation-id")
|
|
17
|
+
local ageOf = _operation_id.ageOf
|
|
18
|
+
local createdAt = _operation_id.createdAt
|
|
19
|
+
local evictable = _operation_id.evictable
|
|
20
|
+
--[[
|
|
21
|
+
*
|
|
22
|
+
* @internal An attempt count or a quarantine move for one item, compare-and-set on the item's generation: it applies
|
|
23
|
+
* only while the stored item is live with exactly `generation` (rule 72).
|
|
24
|
+
|
|
25
|
+
]]
|
|
26
|
+
local function addSeen(envelope, ids)
|
|
27
|
+
local _array = {}
|
|
28
|
+
local _length = #_array
|
|
29
|
+
local _array_1 = envelope.seen
|
|
30
|
+
table.move(_array_1, 1, #_array_1, _length + 1, _array)
|
|
31
|
+
local seen = _array
|
|
32
|
+
local floor = envelope.seenFloor
|
|
33
|
+
for _, id in ids do
|
|
34
|
+
if not (table.find(seen, id) ~= nil) then
|
|
35
|
+
table.insert(seen, id)
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
while #seen > SEEN_MAX_MESSAGES do
|
|
39
|
+
local evicted = table.remove(seen, 1)
|
|
40
|
+
local _exp = floor
|
|
41
|
+
local _condition = createdAt(evicted)
|
|
42
|
+
if _condition == nil then
|
|
43
|
+
_condition = 0
|
|
44
|
+
end
|
|
45
|
+
floor = math.max(_exp, _condition)
|
|
46
|
+
end
|
|
47
|
+
return {
|
|
48
|
+
seen = seen,
|
|
49
|
+
seenFloor = floor,
|
|
50
|
+
}
|
|
51
|
+
end
|
|
52
|
+
--* @internal Each id leaves the inbox or the quarantine, wherever it is, and enters seen as its exact string (rule 72).
|
|
53
|
+
local function applyAcks(envelope, acks)
|
|
54
|
+
local _set = {}
|
|
55
|
+
for _, _v in acks do
|
|
56
|
+
_set[_v] = true
|
|
57
|
+
end
|
|
58
|
+
local gone = _set
|
|
59
|
+
local _object = table.clone(envelope)
|
|
60
|
+
setmetatable(_object, nil)
|
|
61
|
+
local _left = "inbox"
|
|
62
|
+
local _exp = envelope.inbox
|
|
63
|
+
-- ▼ ReadonlyArray.filter ▼
|
|
64
|
+
local _newValue = {}
|
|
65
|
+
local _callback = function(item)
|
|
66
|
+
local _id = item.id
|
|
67
|
+
return not (gone[_id] ~= nil)
|
|
68
|
+
end
|
|
69
|
+
local _length = 0
|
|
70
|
+
for _k, _v in _exp do
|
|
71
|
+
if _callback(_v, _k - 1, _exp) == true then
|
|
72
|
+
_length += 1
|
|
73
|
+
_newValue[_length] = _v
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
-- ▲ ReadonlyArray.filter ▲
|
|
77
|
+
_object[_left] = _newValue
|
|
78
|
+
local _left_1 = "quarantine"
|
|
79
|
+
local _exp_1 = envelope.quarantine
|
|
80
|
+
-- ▼ ReadonlyArray.filter ▼
|
|
81
|
+
local _newValue_1 = {}
|
|
82
|
+
local _callback_1 = function(item)
|
|
83
|
+
local _id = item.id
|
|
84
|
+
return not (gone[_id] ~= nil)
|
|
85
|
+
end
|
|
86
|
+
local _length_1 = 0
|
|
87
|
+
for _k, _v in _exp_1 do
|
|
88
|
+
if _callback_1(_v, _k - 1, _exp_1) == true then
|
|
89
|
+
_length_1 += 1
|
|
90
|
+
_newValue_1[_length_1] = _v
|
|
91
|
+
end
|
|
92
|
+
end
|
|
93
|
+
-- ▲ ReadonlyArray.filter ▲
|
|
94
|
+
_object[_left_1] = _newValue_1
|
|
95
|
+
for _k, _v in addSeen(envelope, acks) do
|
|
96
|
+
_object[_k] = _v
|
|
97
|
+
end
|
|
98
|
+
return _object
|
|
99
|
+
end
|
|
100
|
+
--[[
|
|
101
|
+
*
|
|
102
|
+
* @internal Rule 72's compare-and-set. A move into a full quarantine leaves the item live with its attempts updated.
|
|
103
|
+
* A mark whose generation no longer matches skips, and the item is untouched.
|
|
104
|
+
|
|
105
|
+
]]
|
|
106
|
+
local function applyMarks(envelope, marks)
|
|
107
|
+
local inbox = envelope.inbox
|
|
108
|
+
local quarantine = envelope.quarantine
|
|
109
|
+
for _, mark in marks do
|
|
110
|
+
-- ▼ ReadonlyArray.findIndex ▼
|
|
111
|
+
local _callback = function(item)
|
|
112
|
+
return item.id == mark.id and item.generation == mark.generation
|
|
113
|
+
end
|
|
114
|
+
local _result = -1
|
|
115
|
+
for _i, _v in inbox do
|
|
116
|
+
if _callback(_v, _i - 1, inbox) == true then
|
|
117
|
+
_result = _i - 1
|
|
118
|
+
break
|
|
119
|
+
end
|
|
120
|
+
end
|
|
121
|
+
-- ▲ ReadonlyArray.findIndex ▲
|
|
122
|
+
local index = _result
|
|
123
|
+
if index < 0 then
|
|
124
|
+
continue
|
|
125
|
+
end
|
|
126
|
+
local item = inbox[index + 1]
|
|
127
|
+
local _object = table.clone(item)
|
|
128
|
+
setmetatable(_object, nil)
|
|
129
|
+
_object.attempts = mark.attempts
|
|
130
|
+
_object.generation = item.generation + 1
|
|
131
|
+
local marked = _object
|
|
132
|
+
local _array = {}
|
|
133
|
+
local _length = #_array
|
|
134
|
+
local _quarantineLength = #quarantine
|
|
135
|
+
table.move(quarantine, 1, _quarantineLength, _length + 1, _array)
|
|
136
|
+
_length += _quarantineLength
|
|
137
|
+
local _object_1 = table.clone(marked)
|
|
138
|
+
setmetatable(_object_1, nil)
|
|
139
|
+
_object_1.reason = mark.quarantine
|
|
140
|
+
_array[_length + 1] = _object_1
|
|
141
|
+
local moved = _array
|
|
142
|
+
local room = mark.quarantine ~= nil and #moved <= QUARANTINE_MAX_MESSAGES and jsonBytes(moved) <= QUARANTINE_MAX_BYTES
|
|
143
|
+
local _array_1 = {}
|
|
144
|
+
local _length_1 = #_array_1
|
|
145
|
+
table.move(inbox, 1, #inbox, _length_1 + 1, _array_1)
|
|
146
|
+
local rest = _array_1
|
|
147
|
+
if room then
|
|
148
|
+
table.remove(rest, index + 1)
|
|
149
|
+
quarantine = moved
|
|
150
|
+
else
|
|
151
|
+
rest[index + 1] = marked
|
|
152
|
+
end
|
|
153
|
+
inbox = rest
|
|
154
|
+
end
|
|
155
|
+
local _result
|
|
156
|
+
if inbox == envelope.inbox then
|
|
157
|
+
_result = envelope
|
|
158
|
+
else
|
|
159
|
+
local _object = table.clone(envelope)
|
|
160
|
+
setmetatable(_object, nil)
|
|
161
|
+
_object.inbox = inbox
|
|
162
|
+
_object.quarantine = quarantine
|
|
163
|
+
_result = _object
|
|
164
|
+
end
|
|
165
|
+
return _result
|
|
166
|
+
end
|
|
167
|
+
--* @internal Exact structural equality of two stored JSON values (rule 68).
|
|
168
|
+
local function sameValue(a, b)
|
|
169
|
+
local _a = a
|
|
170
|
+
local _condition = not (type(_a) == "table")
|
|
171
|
+
if not _condition then
|
|
172
|
+
local _b = b
|
|
173
|
+
_condition = not (type(_b) == "table")
|
|
174
|
+
end
|
|
175
|
+
if _condition then
|
|
176
|
+
return a == b
|
|
177
|
+
end
|
|
178
|
+
local count = 0
|
|
179
|
+
for key, value in pairs(a) do
|
|
180
|
+
count += 1
|
|
181
|
+
if not sameValue(value, b[key]) then
|
|
182
|
+
return false
|
|
183
|
+
end
|
|
184
|
+
end
|
|
185
|
+
for _element, _element_1 in pairs(b) do
|
|
186
|
+
local _ = { _element, _element_1 }
|
|
187
|
+
count -= 1
|
|
188
|
+
end
|
|
189
|
+
return count == 0
|
|
190
|
+
end
|
|
191
|
+
local function stored(current, userId, envelope)
|
|
192
|
+
local _array = {}
|
|
193
|
+
local _length = #_array
|
|
194
|
+
local _result = current
|
|
195
|
+
if _result ~= nil then
|
|
196
|
+
_result = _result.userIds
|
|
197
|
+
end
|
|
198
|
+
local _condition = _result
|
|
199
|
+
if _condition == nil then
|
|
200
|
+
_condition = {}
|
|
201
|
+
end
|
|
202
|
+
table.move(_condition, 1, #_condition, _length + 1, _array)
|
|
203
|
+
local userIds = _array
|
|
204
|
+
local _userId = userId
|
|
205
|
+
if not (table.find(userIds, _userId) ~= nil) then
|
|
206
|
+
local _userId_1 = userId
|
|
207
|
+
table.insert(userIds, _userId_1)
|
|
208
|
+
end
|
|
209
|
+
local _object = {
|
|
210
|
+
value = envelope,
|
|
211
|
+
userIds = userIds,
|
|
212
|
+
}
|
|
213
|
+
local _left = "metadata"
|
|
214
|
+
local _result_1 = current
|
|
215
|
+
if _result_1 ~= nil then
|
|
216
|
+
_result_1 = _result_1.metadata
|
|
217
|
+
end
|
|
218
|
+
local _condition_1 = _result_1
|
|
219
|
+
if _condition_1 == nil then
|
|
220
|
+
_condition_1 = {}
|
|
221
|
+
end
|
|
222
|
+
_object[_left] = _condition_1
|
|
223
|
+
return _object
|
|
224
|
+
end
|
|
225
|
+
--* @internal
|
|
226
|
+
--[[
|
|
227
|
+
*
|
|
228
|
+
* @internal An inbox append (rules 66 to 70). Touches only `inbox`, needs no lease and works on any stored version. An
|
|
229
|
+
* absent key becomes an unopened envelope.
|
|
230
|
+
|
|
231
|
+
]]
|
|
232
|
+
local function append(current, userId, item, wall, record)
|
|
233
|
+
local _result = current
|
|
234
|
+
if _result ~= nil then
|
|
235
|
+
_result = _result.value
|
|
236
|
+
end
|
|
237
|
+
local parsed = parseEnvelope(_result, "profiles")
|
|
238
|
+
if parsed.kind == "unreadable" then
|
|
239
|
+
record(parsed)
|
|
240
|
+
return nil
|
|
241
|
+
end
|
|
242
|
+
local envelope = if parsed.kind == "ok" then parsed.envelope else emptyEnvelope("profiles")
|
|
243
|
+
local _exp = envelope.inbox
|
|
244
|
+
-- ▼ ReadonlyArray.find ▼
|
|
245
|
+
local _callback = function(other)
|
|
246
|
+
return other.id == item.id
|
|
247
|
+
end
|
|
248
|
+
local _result_1
|
|
249
|
+
for _i, _v in _exp do
|
|
250
|
+
if _callback(_v, _i - 1, _exp) == true then
|
|
251
|
+
_result_1 = _v
|
|
252
|
+
break
|
|
253
|
+
end
|
|
254
|
+
end
|
|
255
|
+
-- ▲ ReadonlyArray.find ▲
|
|
256
|
+
local _condition = _result_1
|
|
257
|
+
if _condition == nil then
|
|
258
|
+
local _exp_1 = envelope.quarantine
|
|
259
|
+
-- ▼ ReadonlyArray.find ▼
|
|
260
|
+
local _callback_1 = function(other)
|
|
261
|
+
return other.id == item.id
|
|
262
|
+
end
|
|
263
|
+
local _result_2
|
|
264
|
+
for _i, _v in _exp_1 do
|
|
265
|
+
if _callback_1(_v, _i - 1, _exp_1) == true then
|
|
266
|
+
_result_2 = _v
|
|
267
|
+
break
|
|
268
|
+
end
|
|
269
|
+
end
|
|
270
|
+
-- ▲ ReadonlyArray.find ▲
|
|
271
|
+
_condition = _result_2
|
|
272
|
+
end
|
|
273
|
+
local held = _condition
|
|
274
|
+
if held ~= nil then
|
|
275
|
+
record({
|
|
276
|
+
kind = if held.name == item.name and sameValue(held.payload, item.payload) then "duplicate" else "reused",
|
|
277
|
+
})
|
|
278
|
+
return nil
|
|
279
|
+
end
|
|
280
|
+
local _seen = envelope.seen
|
|
281
|
+
local _id = item.id
|
|
282
|
+
if table.find(_seen, _id) ~= nil then
|
|
283
|
+
record({
|
|
284
|
+
kind = "duplicate",
|
|
285
|
+
})
|
|
286
|
+
return nil
|
|
287
|
+
end
|
|
288
|
+
local now = wall()
|
|
289
|
+
if ageOf(item.id, now, MESSAGE_ID_MAX_AGE_SECONDS) ~= "fresh" or createdAt(item.id) <= envelope.seenFloor then
|
|
290
|
+
record({
|
|
291
|
+
kind = "expired",
|
|
292
|
+
})
|
|
293
|
+
return nil
|
|
294
|
+
end
|
|
295
|
+
local _array = {}
|
|
296
|
+
local _length = #_array
|
|
297
|
+
local _array_1 = envelope.inbox
|
|
298
|
+
local _Length = #_array_1
|
|
299
|
+
table.move(_array_1, 1, _Length, _length + 1, _array)
|
|
300
|
+
_length += _Length
|
|
301
|
+
_array[_length + 1] = item
|
|
302
|
+
local inbox = _array
|
|
303
|
+
if #inbox > INBOX_MAX_MESSAGES or jsonBytes(inbox) > INBOX_MAX_BYTES then
|
|
304
|
+
record({
|
|
305
|
+
kind = "full",
|
|
306
|
+
})
|
|
307
|
+
return nil
|
|
308
|
+
end
|
|
309
|
+
if #envelope.seen >= SEEN_MAX_MESSAGES and not evictable(envelope.seen[1], now) then
|
|
310
|
+
record({
|
|
311
|
+
kind = "overloaded",
|
|
312
|
+
})
|
|
313
|
+
return nil
|
|
314
|
+
end
|
|
315
|
+
local _object = table.clone(envelope)
|
|
316
|
+
setmetatable(_object, nil)
|
|
317
|
+
_object.inbox = inbox
|
|
318
|
+
local written = _object
|
|
319
|
+
if envelopeBytes(written) > MAX_ENVELOPE_BYTES then
|
|
320
|
+
record({
|
|
321
|
+
kind = "full",
|
|
322
|
+
})
|
|
323
|
+
return nil
|
|
324
|
+
end
|
|
325
|
+
record({
|
|
326
|
+
kind = "appended",
|
|
327
|
+
})
|
|
328
|
+
return stored(current, userId, written)
|
|
329
|
+
end
|
|
330
|
+
--* @internal
|
|
331
|
+
--* @internal clearQuarantined (rule 73): touches only `inbox`, `quarantine` and `seen`, with no lease.
|
|
332
|
+
local function clearQuarantine(current, userId, ids, action, record)
|
|
333
|
+
local _result = current
|
|
334
|
+
if _result ~= nil then
|
|
335
|
+
_result = _result.value
|
|
336
|
+
end
|
|
337
|
+
local parsed = parseEnvelope(_result, "profiles")
|
|
338
|
+
if parsed.kind == "unreadable" then
|
|
339
|
+
record(parsed)
|
|
340
|
+
return nil
|
|
341
|
+
end
|
|
342
|
+
if parsed.kind == "absent" then
|
|
343
|
+
record({
|
|
344
|
+
kind = "cleared",
|
|
345
|
+
changed = 0,
|
|
346
|
+
})
|
|
347
|
+
return nil
|
|
348
|
+
end
|
|
349
|
+
local envelope = parsed.envelope
|
|
350
|
+
local _exp = envelope.quarantine
|
|
351
|
+
-- ▼ ReadonlyArray.filter ▼
|
|
352
|
+
local _newValue = {}
|
|
353
|
+
local _callback = function(item)
|
|
354
|
+
local _ids = ids
|
|
355
|
+
local _id = item.id
|
|
356
|
+
return _ids[_id] ~= nil
|
|
357
|
+
end
|
|
358
|
+
local _length = 0
|
|
359
|
+
for _k, _v in _exp do
|
|
360
|
+
if _callback(_v, _k - 1, _exp) == true then
|
|
361
|
+
_length += 1
|
|
362
|
+
_newValue[_length] = _v
|
|
363
|
+
end
|
|
364
|
+
end
|
|
365
|
+
-- ▲ ReadonlyArray.filter ▲
|
|
366
|
+
local listed = _newValue
|
|
367
|
+
if #listed == 0 then
|
|
368
|
+
record({
|
|
369
|
+
kind = "cleared",
|
|
370
|
+
changed = 0,
|
|
371
|
+
})
|
|
372
|
+
return nil
|
|
373
|
+
end
|
|
374
|
+
local _exp_1 = envelope.quarantine
|
|
375
|
+
-- ▼ ReadonlyArray.filter ▼
|
|
376
|
+
local _newValue_1 = {}
|
|
377
|
+
local _callback_1 = function(item)
|
|
378
|
+
local _ids = ids
|
|
379
|
+
local _id = item.id
|
|
380
|
+
return not (_ids[_id] ~= nil)
|
|
381
|
+
end
|
|
382
|
+
local _length_1 = 0
|
|
383
|
+
for _k, _v in _exp_1 do
|
|
384
|
+
if _callback_1(_v, _k - 1, _exp_1) == true then
|
|
385
|
+
_length_1 += 1
|
|
386
|
+
_newValue_1[_length_1] = _v
|
|
387
|
+
end
|
|
388
|
+
end
|
|
389
|
+
-- ▲ ReadonlyArray.filter ▲
|
|
390
|
+
local quarantine = _newValue_1
|
|
391
|
+
local written
|
|
392
|
+
if action == "retry" then
|
|
393
|
+
local _array = {}
|
|
394
|
+
local _length_2 = #_array
|
|
395
|
+
local _array_1 = envelope.inbox
|
|
396
|
+
local _Length = #_array_1
|
|
397
|
+
table.move(_array_1, 1, _Length, _length_2 + 1, _array)
|
|
398
|
+
_length_2 += _Length
|
|
399
|
+
-- ▼ ReadonlyArray.map ▼
|
|
400
|
+
local _newValue_2 = table.create(#listed)
|
|
401
|
+
local _callback_2 = function(item)
|
|
402
|
+
return {
|
|
403
|
+
id = item.id,
|
|
404
|
+
name = item.name,
|
|
405
|
+
version = item.version,
|
|
406
|
+
payload = item.payload,
|
|
407
|
+
generation = item.generation + 1,
|
|
408
|
+
attempts = 0,
|
|
409
|
+
}
|
|
410
|
+
end
|
|
411
|
+
for _k, _v in listed do
|
|
412
|
+
_newValue_2[_k] = _callback_2(_v, _k - 1, listed)
|
|
413
|
+
end
|
|
414
|
+
-- ▲ ReadonlyArray.map ▲
|
|
415
|
+
table.move(_newValue_2, 1, #_newValue_2, _length_2 + 1, _array)
|
|
416
|
+
local inbox = _array
|
|
417
|
+
if #inbox > INBOX_MAX_MESSAGES or jsonBytes(inbox) > INBOX_MAX_BYTES then
|
|
418
|
+
record({
|
|
419
|
+
kind = "full",
|
|
420
|
+
})
|
|
421
|
+
return nil
|
|
422
|
+
end
|
|
423
|
+
local _object = table.clone(envelope)
|
|
424
|
+
setmetatable(_object, nil)
|
|
425
|
+
_object.inbox = inbox
|
|
426
|
+
_object.quarantine = quarantine
|
|
427
|
+
written = _object
|
|
428
|
+
else
|
|
429
|
+
local _object = table.clone(envelope)
|
|
430
|
+
setmetatable(_object, nil)
|
|
431
|
+
_object.quarantine = quarantine
|
|
432
|
+
-- ▼ ReadonlyArray.map ▼
|
|
433
|
+
local _newValue_2 = table.create(#listed)
|
|
434
|
+
local _callback_2 = function(item)
|
|
435
|
+
return item.id
|
|
436
|
+
end
|
|
437
|
+
for _k, _v in listed do
|
|
438
|
+
_newValue_2[_k] = _callback_2(_v, _k - 1, listed)
|
|
439
|
+
end
|
|
440
|
+
-- ▲ ReadonlyArray.map ▲
|
|
441
|
+
for _k, _v in addSeen(envelope, _newValue_2) do
|
|
442
|
+
_object[_k] = _v
|
|
443
|
+
end
|
|
444
|
+
written = _object
|
|
445
|
+
end
|
|
446
|
+
record({
|
|
447
|
+
kind = "cleared",
|
|
448
|
+
changed = #listed,
|
|
449
|
+
})
|
|
450
|
+
return stored(current, userId, written)
|
|
451
|
+
end
|
|
452
|
+
return {
|
|
453
|
+
applyAcks = applyAcks,
|
|
454
|
+
applyMarks = applyMarks,
|
|
455
|
+
sameValue = sameValue,
|
|
456
|
+
append = append,
|
|
457
|
+
clearQuarantine = clearQuarantine,
|
|
458
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|