@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,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 {};
|