@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 @@
1
+ export {};
@@ -0,0 +1,106 @@
1
+ -- Compiled with roblox-ts v3.0.0
2
+ local TS = _G[script]
3
+ local LogLevel = TS.import(script, script.Parent.Parent, "log-level").LogLevel
4
+ --* @internal
5
+ local port
6
+ --* @internal
7
+ local function installLoggingPort(value)
8
+ local _arg0 = port == nil
9
+ assert(_arg0, "Logging port already selected")
10
+ port = value
11
+ end
12
+ --* @internal
13
+ local createRobloxLoggingPort
14
+ local function loggingPort()
15
+ if port == nil then
16
+ port = createRobloxLoggingPort()
17
+ end
18
+ return port
19
+ end
20
+ local function escape(text)
21
+ return (string.gsub((string.gsub(text, "{", "{{")), "}", "}}"))
22
+ end
23
+ --* @internal
24
+ function createRobloxLoggingPort()
25
+ local gameLogger
26
+ local crucibleLogger
27
+ local fallback = false
28
+ local studio = game:GetService("RunService"):IsStudio()
29
+ local function native(write, plain)
30
+ if not fallback then
31
+ local ok = pcall(function()
32
+ local service = game:GetService("LogService")
33
+ if gameLogger == nil then
34
+ gameLogger = service:GetLogger("Game")
35
+ end
36
+ if crucibleLogger == nil then
37
+ crucibleLogger = service:GetLogger("Crucible")
38
+ end
39
+ write()
40
+ end)
41
+ if ok then
42
+ return nil
43
+ end
44
+ fallback = true
45
+ end
46
+ plain()
47
+ end
48
+ return {
49
+ isStudio = function()
50
+ return studio
51
+ end,
52
+ now = function()
53
+ return os.clock()
54
+ end,
55
+ describeInstance = function(instance)
56
+ return instance:GetFullName()
57
+ end,
58
+ writeGame = function(record)
59
+ local text = `[{record.source}] {record.message}`
60
+ if record.level == LogLevel.Error then
61
+ task.spawn(function()
62
+ local _condition = record.traceback
63
+ if _condition == nil then
64
+ _condition = ""
65
+ end
66
+ return error(`{text}\n{_condition}`, 0)
67
+ end)
68
+ return nil
69
+ end
70
+ native(function()
71
+ local logger = gameLogger
72
+ if record.level == LogLevel.Debug then
73
+ logger:Output(escape(text), record.context)
74
+ elseif record.level == LogLevel.Info then
75
+ logger:Info(escape(text), record.context)
76
+ else
77
+ logger:Warn(escape(text), record.context)
78
+ end
79
+ end, function()
80
+ if record.level == LogLevel.Warning then
81
+ warn(text)
82
+ else
83
+ print(text)
84
+ end
85
+ end)
86
+ end,
87
+ writeInternal = function(level, message)
88
+ if level == "error" then
89
+ task.spawn(function()
90
+ return error(`[Crucible] {message}`, 0)
91
+ end)
92
+ else
93
+ native(function()
94
+ return crucibleLogger:Warn(escape(`[Crucible] {message}`), {})
95
+ end, function()
96
+ return warn(`[Crucible] {message}`)
97
+ end)
98
+ end
99
+ end,
100
+ }
101
+ end
102
+ return {
103
+ installLoggingPort = installLoggingPort,
104
+ loggingPort = loggingPort,
105
+ createRobloxLoggingPort = createRobloxLoggingPort,
106
+ }
@@ -0,0 +1,26 @@
1
+ import { type Codec } from "./codec";
2
+ import type { Node } from "./schema";
3
+ export type MessageKind = "to-server" | "to-client" | "ask-server";
4
+ export interface MessageSpec {
5
+ readonly kind: MessageKind;
6
+ readonly name: string;
7
+ readonly input: Node;
8
+ readonly output?: Node;
9
+ readonly timeout?: number;
10
+ }
11
+ export interface Definition {
12
+ readonly kind: MessageKind;
13
+ readonly name: string;
14
+ readonly input: Codec;
15
+ readonly output?: Codec;
16
+ readonly timeout: number;
17
+ readonly index?: number;
18
+ }
19
+ export declare function define(spec: MessageSpec): Definition;
20
+ export declare function seal(): string;
21
+ export declare function adopt(catalog: string): void;
22
+ export declare function definitionAt(index: number): Definition | undefined;
23
+ export declare function definitionOf(token: unknown): Definition | undefined;
24
+ export declare function discardIndices(): void;
25
+ export declare function unpublished(): readonly Definition[];
26
+ export declare function catalogSize(): number;
@@ -0,0 +1,182 @@
1
+ -- Compiled with roblox-ts v3.0.0
2
+ local TS = _G[script]
3
+ local compile = TS.import(script, script.Parent, "codec").compile
4
+ local L = TS.import(script, script.Parent, "limits")
5
+ local definitions = {}
6
+ local tokens = {}
7
+ --* The framework's own messages: the only names under the reserved `Crucible.` segment (NET 3, rule 55).
8
+ local FRAMEWORK_MESSAGES = {
9
+ ["Crucible.State.Fault"] = true,
10
+ ["Crucible.State.Frame"] = true,
11
+ }
12
+ local names = {}
13
+ local indices = {}
14
+ local sealed = false
15
+ local function validName(name)
16
+ local _name = name
17
+ local _condition = type(_name) == "string"
18
+ if _condition then
19
+ _condition = #name > 0 and #name <= L.MESSAGE_NAME_MAX_BYTES
20
+ end
21
+ assert(_condition, "invalid message name")
22
+ local segments = string.split(name, ".")
23
+ local _condition_1 = segments[1] ~= "Crucible"
24
+ if not _condition_1 then
25
+ local _name_1 = name
26
+ _condition_1 = FRAMEWORK_MESSAGES[_name_1] ~= nil
27
+ end
28
+ assert(_condition_1, "reserved message name")
29
+ for _, segment in segments do
30
+ local _arg0 = (string.match(segment, "^[A-Za-z][A-Za-z0-9_]*$")) ~= nil
31
+ assert(_arg0, "invalid message name")
32
+ end
33
+ end
34
+ local register
35
+ local function define(spec)
36
+ validName(spec.name)
37
+ local _name = spec.name
38
+ local _arg0 = not (FRAMEWORK_MESSAGES[_name] ~= nil)
39
+ assert(_arg0, "reserved message name")
40
+ return register(spec)
41
+ end
42
+ --* @internal The trusted path: a framework message under the reserved `Crucible.` segment that game names refuse.
43
+ local function defineFramework(spec)
44
+ local _name = spec.name
45
+ local _arg0 = FRAMEWORK_MESSAGES[_name] ~= nil
46
+ assert(_arg0, "not a framework message")
47
+ return register(spec)
48
+ end
49
+ function register(spec)
50
+ local _arg0 = not sealed
51
+ local _arg1 = `{spec.name} was declared after the network catalog sealed; declare messages in modules that boot loads.`
52
+ assert(_arg0, _arg1)
53
+ local _name = spec.name
54
+ local _arg0_1 = not (definitions[_name] ~= nil)
55
+ local _arg1_1 = `duplicate message {spec.name}`
56
+ assert(_arg0_1, _arg1_1)
57
+ local _arg0_2 = spec.kind == "to-server" or spec.kind == "to-client" or spec.kind == "ask-server"
58
+ assert(_arg0_2, "invalid message kind")
59
+ local _arg0_3 = if spec.kind == "ask-server" then spec.output ~= nil else spec.output == nil and spec.timeout == nil
60
+ assert(_arg0_3, "invalid message options")
61
+ local _condition = spec.timeout
62
+ if _condition == nil then
63
+ _condition = L.DEFAULT_ASK_TIMEOUT_SECONDS
64
+ end
65
+ local timeout = _condition
66
+ local _arg0_4 = timeout > 0 and timeout <= L.MAX_ASK_TIMEOUT_SECONDS
67
+ assert(_arg0_4, "invalid ask timeout")
68
+ local tally = {
69
+ nodes = 0,
70
+ }
71
+ local definition = table.freeze(setmetatable({
72
+ kind = spec.kind,
73
+ name = spec.name,
74
+ input = compile(spec.input, tally),
75
+ output = if spec.output == nil then nil else compile(spec.output, tally),
76
+ timeout = timeout,
77
+ }, {
78
+ __index = function(_, key)
79
+ local _result
80
+ if key == "index" then
81
+ local _indices = indices
82
+ local _name_1 = spec.name
83
+ _result = _indices[_name_1]
84
+ else
85
+ _result = nil
86
+ end
87
+ return _result
88
+ end,
89
+ }))
90
+ local _name_1 = spec.name
91
+ definitions[_name_1] = definition
92
+ tokens[definition] = true
93
+ return definition
94
+ end
95
+ local function setNames(ordered)
96
+ names = ordered
97
+ indices = {}
98
+ for i = 0, #names - 1 do
99
+ local _indices = indices
100
+ local _arg0 = names[i + 1]
101
+ local _i = i
102
+ _indices[_arg0] = _i
103
+ end
104
+ end
105
+ local function seal()
106
+ local _arg0 = not sealed
107
+ assert(_arg0, "catalog already sealed")
108
+ -- ▼ ReadonlyMap.size ▼
109
+ local _size = 0
110
+ for _ in definitions do
111
+ _size += 1
112
+ end
113
+ -- ▲ ReadonlyMap.size ▲
114
+ local _arg0_1 = _size <= L.MAX_MESSAGES
115
+ assert(_arg0_1, "too many messages")
116
+ local ordered = {}
117
+ for name in definitions do
118
+ table.insert(ordered, name)
119
+ end
120
+ table.sort(ordered)
121
+ setNames(ordered)
122
+ sealed = true
123
+ return table.concat(names, "\n")
124
+ end
125
+ local function adopt(catalog)
126
+ local _condition = not sealed
127
+ if _condition then
128
+ local _catalog = catalog
129
+ _condition = type(_catalog) == "string"
130
+ if _condition then
131
+ _condition = #catalog < L.MAX_MESSAGES * (L.MESSAGE_NAME_MAX_BYTES + 1)
132
+ end
133
+ end
134
+ assert(_condition, "invalid catalog")
135
+ local ordered = if catalog == "" then {} else string.split(catalog, "\n")
136
+ local _arg0 = #ordered <= L.MAX_MESSAGES
137
+ assert(_arg0, "too many messages")
138
+ for i = 0, #ordered - 1 do
139
+ validName(ordered[i + 1])
140
+ local _arg0_1 = i == 0 or ordered[i] < ordered[i + 1]
141
+ assert(_arg0_1, "catalog not strictly ascending")
142
+ end
143
+ setNames(ordered)
144
+ end
145
+ local function definitionAt(index)
146
+ if index < 0 or index % 1 ~= 0 or index >= #names then
147
+ return nil
148
+ end
149
+ local _arg0 = names[index + 1]
150
+ return definitions[_arg0]
151
+ end
152
+ local function definitionOf(token)
153
+ local _token = token
154
+ return if tokens[_token] ~= nil then token else nil
155
+ end
156
+ local function discardIndices()
157
+ sealed = false
158
+ setNames({})
159
+ end
160
+ local function unpublished()
161
+ local result = {}
162
+ for _, definition in definitions do
163
+ if definition.index == nil then
164
+ table.insert(result, definition)
165
+ end
166
+ end
167
+ return result
168
+ end
169
+ local function catalogSize()
170
+ return #names
171
+ end
172
+ return {
173
+ define = define,
174
+ defineFramework = defineFramework,
175
+ seal = seal,
176
+ adopt = adopt,
177
+ definitionAt = definitionAt,
178
+ definitionOf = definitionOf,
179
+ discardIndices = discardIndices,
180
+ unpublished = unpublished,
181
+ catalogSize = catalogSize,
182
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,154 @@
1
+ -- Compiled with roblox-ts v3.0.0
2
+ local TS = _G[script]
3
+ local ClientReady = TS.import(script, script.Parent.Parent.Parent, "network").ClientReady
4
+ local readClaim = TS.import(script, script.Parent.Parent, "construction-claim").readClaim
5
+ local definitionOf = TS.import(script, script.Parent, "catalog").definitionOf
6
+ --* @internal
7
+ --* @internal
8
+ local session
9
+ local frozen = false
10
+ local receivers = {}
11
+ local ready = {}
12
+ local live = {}
13
+ local function retire(claim)
14
+ local _claim = claim
15
+ -- ▼ Set.delete ▼
16
+ local _valueExisted = live[_claim] ~= nil
17
+ live[_claim] = nil
18
+ -- ▲ Set.delete ▲
19
+ if not _valueExisted then
20
+ return nil
21
+ end
22
+ if claim.target == ClientReady then
23
+ local _claim_1 = claim
24
+ local _arg0 = (table.find(ready, _claim_1) or 0) - 1
25
+ table.remove(ready, _arg0 + 1)
26
+ else
27
+ local _target = claim.target
28
+ receivers[_target] = nil
29
+ end
30
+ local _result = session
31
+ if _result ~= nil then
32
+ _result.retired(claim)
33
+ end
34
+ end
35
+ --[[
36
+ *
37
+ * @internal
38
+ * Re-checks what the transformer checked, because untransformed or cast code skips it. A violation throws from the
39
+ * constructor and fails boot.
40
+
41
+ ]]
42
+ local function claim(owner, field, target, callback)
43
+ local state = readClaim()
44
+ if state == nil or state.object ~= owner then
45
+ error("receive() must initialize a readonly field of the Subsystem being constructed")
46
+ end
47
+ local record = state.record
48
+ local label = `{record.id}.{field}`
49
+ if record.kind ~= 0 then
50
+ error(`{label}: receive() must initialize a readonly field of a Subsystem; behaviours do not receive`)
51
+ end
52
+ if frozen then
53
+ error(`{label}: receive() after the network commit`)
54
+ end
55
+ local _result = session
56
+ if _result ~= nil then
57
+ _result = _result.side
58
+ end
59
+ local _condition = _result
60
+ if _condition == nil then
61
+ _condition = error(`{label}: receive() before the network session opened`)
62
+ end
63
+ local side = _condition
64
+ local _callback = callback
65
+ if not (type(_callback) == "function") then
66
+ error(`{label}: receive() needs a callback`)
67
+ end
68
+ if target == ClientReady then
69
+ if side ~= "server" then
70
+ error(`{label}: ClientReady is received in a server Subsystem.`)
71
+ end
72
+ for _, other in ready do
73
+ if other.owner == state then
74
+ error(`{label}: {other.label} already receives ClientReady; a Subsystem holds one`)
75
+ end
76
+ end
77
+ else
78
+ local definition = definitionOf(target) or error(`{label}: receive() needs a message declared with toServer, toClient or askServer`)
79
+ local serverSide = definition.kind ~= "to-client"
80
+ if serverSide ~= (side == "server") then
81
+ error(if serverSide then `{label}: {definition.name} is client-to-server; receive it in a server Subsystem.` else `{label}: {definition.name} is server-to-client; receive it in a client Subsystem.`)
82
+ end
83
+ local other = receivers[definition]
84
+ if other ~= nil then
85
+ error(`{definition.name} has two receivers: {other.label} and {label}`)
86
+ end
87
+ end
88
+ local entry = {
89
+ owner = state,
90
+ label = label,
91
+ target = target,
92
+ callback = callback,
93
+ }
94
+ if target == ClientReady then
95
+ table.insert(ready, entry)
96
+ else
97
+ local _target = target
98
+ receivers[_target] = entry
99
+ end
100
+ live[entry] = true
101
+ if state.subscriptions == nil then
102
+ state.subscriptions = {}
103
+ end
104
+ local _exp = state.subscriptions
105
+ table.insert(_exp, function()
106
+ return retire(entry)
107
+ end)
108
+ return table.freeze({})
109
+ end
110
+ --* @internal
111
+ local function isLive(claim)
112
+ local _claim = claim
113
+ return live[_claim] ~= nil
114
+ end
115
+ --* @internal
116
+ local function receiverOf(definition)
117
+ local _definition = definition
118
+ return receivers[_definition]
119
+ end
120
+ --* @internal
121
+ local function readyClaims()
122
+ local _array = {}
123
+ local _length = #_array
124
+ table.move(ready, 1, #ready, _length + 1, _array)
125
+ return _array
126
+ end
127
+ --* @internal
128
+ local function openClaims(value)
129
+ session = value
130
+ end
131
+ --* @internal
132
+ local function freezeClaims()
133
+ frozen = true
134
+ end
135
+ --* @internal
136
+ local function closeClaims(discard)
137
+ session = nil
138
+ if not discard then
139
+ return nil
140
+ end
141
+ frozen = false
142
+ table.clear(receivers)
143
+ table.clear(ready)
144
+ table.clear(live)
145
+ end
146
+ return {
147
+ claim = claim,
148
+ isLive = isLive,
149
+ receiverOf = receiverOf,
150
+ readyClaims = readyClaims,
151
+ openClaims = openClaims,
152
+ freezeClaims = freezeClaims,
153
+ closeClaims = closeClaims,
154
+ }
@@ -0,0 +1 @@
1
+ export {};