@axpecter/lync 2.3.2 → 3.0.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 (135) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +302 -299
  3. package/Types.luau +215 -0
  4. package/api/Define.luau +154 -0
  5. package/api/Lifecycle.luau +119 -0
  6. package/api/Packet.luau +108 -0
  7. package/api/Query.luau +149 -0
  8. package/api/Set.luau +189 -0
  9. package/channel/Blocks.luau +124 -0
  10. package/channel/Group.luau +118 -0
  11. package/channel/Link.luau +135 -0
  12. package/channel/Outbox.luau +261 -0
  13. package/channel/Recipients.luau +129 -0
  14. package/channel/Transport.luau +138 -0
  15. package/codec/Base.luau +236 -0
  16. package/codec/Composites.luau +128 -0
  17. package/codec/Datatypes.luau +162 -0
  18. package/codec/Legality.luau +61 -0
  19. package/codec/Scalars.luau +72 -0
  20. package/codec/Text.luau +89 -0
  21. package/codec/init.luau +39 -0
  22. package/index.d.ts +285 -0
  23. package/init.luau +91 -0
  24. package/kernel/Bits.luau +16 -0
  25. package/kernel/Catalog.luau +121 -0
  26. package/kernel/Heap.luau +80 -0
  27. package/kernel/Listeners.luau +95 -0
  28. package/kernel/Log.luau +197 -0
  29. package/kernel/Tally.luau +128 -0
  30. package/kernel/Threads.luau +44 -0
  31. package/kernel/Wide.luau +267 -0
  32. package/package.json +15 -32
  33. package/runtime/Boundary.luau +180 -0
  34. package/runtime/Dispatch.luau +91 -0
  35. package/runtime/Handles.luau +114 -0
  36. package/runtime/Handshake.luau +90 -0
  37. package/runtime/Pending.luau +196 -0
  38. package/runtime/Router.luau +379 -0
  39. package/runtime/Session.luau +731 -0
  40. package/shape/Compile.luau +340 -0
  41. package/shape/Folds.luau +332 -0
  42. package/shape/Frames.luau +170 -0
  43. package/shape/Grid.luau +169 -0
  44. package/shape/Ir.luau +540 -0
  45. package/shape/Joins.luau +125 -0
  46. package/shape/Legality.luau +273 -0
  47. package/shape/Lower.luau +419 -0
  48. package/shape/Order.luau +64 -0
  49. package/shape/Regions.luau +113 -0
  50. package/shape/Rotation.luau +472 -0
  51. package/shape/Serial.luau +258 -0
  52. package/shape/Words.luau +89 -0
  53. package/shape/emit/Block.luau +453 -0
  54. package/shape/emit/Column.luau +338 -0
  55. package/shape/emit/Leaf.luau +462 -0
  56. package/shape/emit/Word.luau +234 -0
  57. package/shape/emit/init.luau +222 -0
  58. package/shape/init.luau +78 -0
  59. package/state/Apply.luau +330 -0
  60. package/state/Audience.luau +126 -0
  61. package/state/Catchup.luau +218 -0
  62. package/state/Coalesce.luau +43 -0
  63. package/state/Fieldset.luau +177 -0
  64. package/state/Newest.luau +339 -0
  65. package/state/Plan.luau +441 -0
  66. package/state/Sections.luau +468 -0
  67. package/state/Store.luau +188 -0
  68. package/state/Sync.luau +762 -0
  69. package/wire/Bytes.luau +118 -0
  70. package/wire/Clock.luau +37 -0
  71. package/wire/Protocol.luau +110 -0
  72. package/wire/Sequence.luau +40 -0
  73. package/wire/Varint.luau +124 -0
  74. package/src/Types.luau +0 -180
  75. package/src/api/Group.luau +0 -156
  76. package/src/api/Packet.luau +0 -319
  77. package/src/api/Query.luau +0 -338
  78. package/src/api/Scope.luau +0 -90
  79. package/src/api/Signal.luau +0 -188
  80. package/src/codec/Base.luau +0 -136
  81. package/src/codec/composite/Array.luau +0 -456
  82. package/src/codec/composite/Map.luau +0 -469
  83. package/src/codec/composite/Optional.luau +0 -54
  84. package/src/codec/composite/Shared.luau +0 -291
  85. package/src/codec/composite/Struct.luau +0 -494
  86. package/src/codec/composite/Tagged.luau +0 -103
  87. package/src/codec/composite/Tuple.luau +0 -143
  88. package/src/codec/datatype/Buffer.luau +0 -66
  89. package/src/codec/datatype/CFrame.luau +0 -109
  90. package/src/codec/datatype/Color.luau +0 -32
  91. package/src/codec/datatype/Instance.luau +0 -52
  92. package/src/codec/datatype/IntVector.luau +0 -39
  93. package/src/codec/datatype/NumberRange.luau +0 -39
  94. package/src/codec/datatype/Ray.luau +0 -36
  95. package/src/codec/datatype/Rect.luau +0 -31
  96. package/src/codec/datatype/Region.luau +0 -62
  97. package/src/codec/datatype/Sequence.luau +0 -184
  98. package/src/codec/datatype/String.luau +0 -94
  99. package/src/codec/datatype/UDim.luau +0 -46
  100. package/src/codec/datatype/Vector.luau +0 -130
  101. package/src/codec/meta/Auto.luau +0 -318
  102. package/src/codec/meta/Bitfield.luau +0 -147
  103. package/src/codec/meta/Custom.luau +0 -33
  104. package/src/codec/meta/DeltaScalar.luau +0 -390
  105. package/src/codec/meta/Enum.luau +0 -50
  106. package/src/codec/meta/Float.luau +0 -38
  107. package/src/codec/meta/Nothing.luau +0 -13
  108. package/src/codec/meta/Unknown.luau +0 -41
  109. package/src/codec/primitive/Bool.luau +0 -26
  110. package/src/codec/primitive/Float16.luau +0 -99
  111. package/src/codec/primitive/Int.luau +0 -59
  112. package/src/codec/primitive/Number.luau +0 -24
  113. package/src/codec/primitive/Signed.luau +0 -32
  114. package/src/codec/primitive/Varint.luau +0 -165
  115. package/src/codec/primitive/Zint.luau +0 -99
  116. package/src/index.d.ts +0 -423
  117. package/src/init.luau +0 -362
  118. package/src/internal/Baseline.luau +0 -57
  119. package/src/internal/Channel.luau +0 -390
  120. package/src/internal/Middleware.luau +0 -100
  121. package/src/internal/Pool.luau +0 -65
  122. package/src/internal/Registry.luau +0 -119
  123. package/src/internal/Transport.luau +0 -28
  124. package/src/transport/Bridge.luau +0 -110
  125. package/src/transport/Client.luau +0 -97
  126. package/src/transport/Gate.luau +0 -488
  127. package/src/transport/Reader.luau +0 -331
  128. package/src/transport/Server.luau +0 -211
  129. package/src/util/Array.luau +0 -18
  130. package/src/util/Buffer.luau +0 -90
  131. package/src/util/Constants.luau +0 -30
  132. package/src/util/Log.luau +0 -68
  133. package/src/util/Player.luau +0 -14
  134. package/src/util/Quantize.luau +0 -84
  135. package/src/util/Quat.luau +0 -124
@@ -0,0 +1,89 @@
1
+ --!strict
2
+ --!optimize 2
3
+ -- Strings and bytes: the two counted kinds, and the five symbol sets the library names.
4
+
5
+ local Base = require (script.Parent.Base)
6
+ local Legality = require (script.Parent.Legality)
7
+ local Log = require (script.Parent.Parent.kernel.Log)
8
+ local Types = require (script.Parent.Parent.Types)
9
+
10
+ local Text = {}
11
+
12
+ -- Constants -----------------------------------------------------------
13
+
14
+ --[[
15
+ The five sets a caller never writes, which is the point of them. A set is a layout, since a
16
+ symbol's place in it is the index it encodes as, so the order inside each is arbitrary and it is
17
+ pinned: a preset a caller could spell differently would be two layouts under one name.
18
+ ]]
19
+ const ALPHANUM = "abcdefghijklmnopqrstuvwxyz0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"
20
+ const BASE32 = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567"
21
+ const BASE64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
22
+ const DIGITS = "0123456789"
23
+ const HEX = "0123456789abcdef"
24
+
25
+ -- Private -------------------------------------------------------------
26
+
27
+ --[[
28
+ A symbol set indexes its own characters, so a character appearing twice is two indices decoding
29
+ to one value and an encode with no answer to give. Walked a byte at a time because the set is
30
+ bytes: a character past the ASCII range is several of them, and each is a symbol.
31
+ ]]
32
+ local function symbolic (symbols: string)
33
+ if type (symbols) ~= "string" then
34
+ Log.refuse (`an alphabet takes a symbol set, not a {typeof (symbols)}`)
35
+ end
36
+ if symbols == "" then Log.refuse ("an alphabet takes at least one symbol") end
37
+
38
+ local seen = {}
39
+ for at = 1, #symbols do
40
+ local byte = string.byte (symbols, at)
41
+ if seen[byte] ~= nil then
42
+ Log.refuse (`an alphabet takes each symbol once, and {string.char (byte)} is twice`)
43
+ end
44
+ seen[byte] = true
45
+ end
46
+ end
47
+
48
+ local function alphabet (symbols: string, min: number, max: number): Types.Codec<string>
49
+ symbolic (symbols)
50
+ Legality.counts ("str", min, max)
51
+
52
+ return Base.make ("str", { symbols = symbols, min = min, max = max })
53
+ end
54
+
55
+ -- A preset is one of the sets above with the general form behind it, so every set is one path.
56
+ local function preset (symbols: string): (number, number) -> Types.Codec<string>
57
+ return function (min: number, max: number): Types.Codec<string>
58
+ return alphabet (symbols, min, max)
59
+ end
60
+ end
61
+
62
+ -- The bare name, reached through the metamethod, which is handed the table before the bounds.
63
+ local function called (_: any, min: number, max: number): Types.Codec<string>
64
+ Legality.counts ("str", min, max)
65
+ return Base.make ("str", { min = min, max = max })
66
+ end
67
+
68
+ -- Public --------------------------------------------------------------
69
+
70
+ --[[
71
+ One name carrying seven forms: the bare one, the general alphabet, and the five presets. What
72
+ tells a plain string from an alphabet downstream is whether a symbol set was recorded.
73
+ ]]
74
+ Text.str = table.freeze (setmetatable ({
75
+ alphabet = alphabet,
76
+ alphanum = preset (ALPHANUM),
77
+ base32 = preset (BASE32),
78
+ base64 = preset (BASE64),
79
+ digits = preset (DIGITS),
80
+ hex = preset (HEX),
81
+ }, { __call = called }))
82
+
83
+ -- Bytes nobody opens, counted the same way and carrying no set to be outside of.
84
+ function Text.buffer (min: number, max: number): Types.Codec<buffer>
85
+ Legality.counts ("buffer", min, max)
86
+ return Base.make ("buffer", { min = min, max = max })
87
+ end
88
+
89
+ return table.freeze (Text)
@@ -0,0 +1,39 @@
1
+ --!strict
2
+ --!optimize 2
3
+ -- Every constructor a schema is written from, in one frozen table.
4
+
5
+ local Composites = require (script.Composites)
6
+ local Datatypes = require (script.Datatypes)
7
+ local Scalars = require (script.Scalars)
8
+ local Text = require (script.Text)
9
+
10
+ local Codecs = {
11
+ bool = Scalars.bool,
12
+ int = Scalars.int,
13
+ quant = Scalars.quant,
14
+ angle = Scalars.angle,
15
+ f32 = Scalars.f32,
16
+ f64 = Scalars.f64,
17
+ vlq = Scalars.vlq,
18
+ vli = Scalars.vli,
19
+
20
+ str = Text.str,
21
+ buffer = Text.buffer,
22
+
23
+ struct = Composites.struct,
24
+ array = Composites.array,
25
+ map = Composites.map,
26
+ optional = Composites.optional,
27
+ tagged = Composites.tagged,
28
+ enum = Composites.enum,
29
+ bitfield = Composites.bitfield,
30
+
31
+ vec2 = Datatypes.vec2,
32
+ vec3 = Datatypes.vec3,
33
+ cframe = Datatypes.cframe,
34
+ rotation = Datatypes.rotation,
35
+ color3 = Datatypes.color3,
36
+ inst = Datatypes.inst,
37
+ }
38
+
39
+ return table.freeze (Codecs)
package/index.d.ts ADDED
@@ -0,0 +1,285 @@
1
+ /// <reference types="@rbxts/types" />
2
+
3
+ declare namespace Lync {
4
+ /** Opaque codec brand. `_nominal_codec` never exists at runtime. */
5
+ interface Codec<T> {
6
+ /** @hidden */ readonly _nominal_codec: T;
7
+ /** Runs on decode. Return a reason to reject or nil to accept. The first reason wins. */
8
+ validate(this: Codec<T>, check: (value: T, context: ValidateContext) => string | undefined): Codec<T>;
9
+ /** Set fields only. The field never decreases, and an update that would lower it throws. */
10
+ monotonic(this: Codec<number>): Codec<number>;
11
+ /** Set fields only: lossy latest-wins replication, rate-capped when hz is given. */
12
+ newest(this: Codec<T>, hz?: number): Codec<T>;
13
+ /** Domain transform: `to` lifts after decode, `from` lowers before encode. */
14
+ as<U>(this: Codec<T>, to: (value: T) => U, from: (value: U) => T): Codec<U>;
15
+ }
16
+
17
+ /** Reused between decodes, so copy anything kept. */
18
+ interface ValidateContext {
19
+ readonly player?: Player;
20
+ readonly now: number;
21
+ readonly last?: number;
22
+ }
23
+
24
+ interface Connection {
25
+ readonly connected: boolean;
26
+ disconnect(this: Connection): void;
27
+ }
28
+
29
+ /** @hidden */ interface None {
30
+ readonly _nominal_none: never;
31
+ }
32
+ /** @hidden */ interface All {
33
+ readonly _nominal_all: never;
34
+ }
35
+ /** @hidden */ interface Except {
36
+ readonly _nominal_except: never;
37
+ }
38
+
39
+ type Recipient = All | Player | ReadonlyArray<Player> | Group | Except;
40
+
41
+ type LogKind = "warn" | "error" | "debug";
42
+ type Cause = "removed" | "cleared";
43
+ type OutcomeCode = "timeout" | "unanswered" | "leave" | "shutdown";
44
+
45
+ interface OutcomeData {
46
+ readonly definition: string;
47
+ readonly elapsed?: number;
48
+ }
49
+
50
+ interface LogData {
51
+ readonly [key: string]: unknown;
52
+ readonly file: string;
53
+ readonly line: number;
54
+ readonly player?: Player;
55
+ readonly definition?: string;
56
+ }
57
+
58
+ interface DefinitionStats {
59
+ readonly sent: number;
60
+ readonly sentBytes: number;
61
+ readonly received: number;
62
+ readonly receivedBytes: number;
63
+ readonly drops: number;
64
+ }
65
+
66
+ interface Stats {
67
+ readonly flushes: number;
68
+ readonly sentBytes: number;
69
+ readonly receivedBytes: number;
70
+ readonly drops: number;
71
+ readonly definitions: ReadonlyMap<string, DefinitionStats>;
72
+ }
73
+
74
+ interface Packet<T> {
75
+ /** Declaration: lossy, unordered, every fire still sent. */
76
+ unreliable(this: Packet<T>): Packet<T>;
77
+ /** Declaration: lossy latest-wins, rate-capped when hz is given. */
78
+ newest(this: Packet<T>, hz?: number): Packet<T>;
79
+ /** Declaration: stamps every fire with its flush's shared send instant. */
80
+ timestamped(this: Packet<T>): Packet<T>;
81
+ fireServer(this: Packet<T>, value: T): void;
82
+ fireClient(this: Packet<T>, recipient: Recipient, value: T): void;
83
+ /** `sent` is the fire's instant, and arrives only where the packet declared a stamp. */
84
+ onServer(this: Packet<T>, fn: (value: T, player: Player, sent?: number) => void): Connection;
85
+ /** `sent` is the fire's instant, and arrives only where the packet declared a stamp. */
86
+ onClient(this: Packet<T>, fn: (value: T, sent?: number) => void): Connection;
87
+ describe(this: Packet<T>): string;
88
+ }
89
+
90
+ /** The completion triple: `(true, reply)` or `(false, code, data)`, never a throw. */
91
+ type Outcome<R> = (...result: [true, R] | [false, OutcomeCode, OutcomeData]) => void;
92
+
93
+ interface Query<Q, R> {
94
+ /** The one responder for client requests. What it returns is the reply. Before start only. */
95
+ onServer(this: Query<Q, R>, fn: (value: Q, player: Player) => R): void;
96
+ /** The one responder for server requests. Before start only. */
97
+ onClient(this: Query<Q, R>, fn: (value: Q) => R): void;
98
+ /** Server form. Never yields, and `complete` receives the outcome exactly once. */
99
+ request(
100
+ this: Query<Q, R>,
101
+ client: Player,
102
+ value: Q,
103
+ complete: Outcome<R>,
104
+ timeout?: number,
105
+ ): void;
106
+ /** Client form: yields until the reply or an outcome code. No ending throws, and it never hangs. */
107
+ request(
108
+ this: Query<Q, R>,
109
+ value: Q,
110
+ timeout?: number,
111
+ ): LuaTuple<[true, R] | [false, OutcomeCode, OutcomeData]>;
112
+ describe(this: Query<Q, R>): string;
113
+ }
114
+
115
+ /** `Lync.none` clears an optional field. A required field rejects it at the type level. */
116
+ type Update<T> = {
117
+ [K in keyof T]?: undefined extends T[K] ? T[K] | None : T[K];
118
+ };
119
+
120
+ type KeyableField<T> = {
121
+ [K in keyof T]: T[K] extends boolean | number | string ? K & string : never;
122
+ }[keyof T];
123
+
124
+ interface Set<T, K = never> extends Iterable<LuaTuple<[number, T]>> {
125
+ /** Declaration: partitions records by the field's value. Unkeyed sets have no audiences. */
126
+ keyBy<F extends KeyableField<T>>(this: Set<T, never>, field: F): Set<T, T[F & keyof T]>;
127
+ /** On first sight, which is an add, a late join, or a record coming into view. */
128
+ onAdded(this: Set<T, K>, fn: (id: number, record: T) => void): Connection;
129
+ onChanged(this: Set<T, K>, fn: (id: number, record: T, old?: T) => void): Connection;
130
+ onRemoved(this: Set<T, K>, fn: (id: number, cause: Cause) => void): Connection;
131
+ /** Server only. Throws on a live id. */
132
+ add(this: Set<T, K>, id: number, record: T): void;
133
+ /** Server only. Throws on an absent id. */
134
+ update(this: Set<T, K>, id: number, fields: Update<T>): void;
135
+ /** Server only. Throws on an absent id. */
136
+ remove(this: Set<T, K>, id: number): void;
137
+ /** Server only. Empties the whole set. */
138
+ clear(this: Set<T, K>): void;
139
+ /** Server only, keyed sets only. A later assignment replaces the earlier one. */
140
+ audience(this: Set<T, K>, key: K, recipient: Recipient): void;
141
+ /** The live record or nil. This is the library's own storage, so read it and never write. */
142
+ get(this: Set<T, K>, id: number): T | undefined;
143
+ size(this: Set<T, K>): number;
144
+ describe(this: Set<T, K>): string;
145
+ }
146
+
147
+ interface Group extends Iterable<Player> {
148
+ add(this: Group, player: Player): void;
149
+ remove(this: Group, player: Player): void;
150
+ has(this: Group, player: Player): boolean;
151
+ size(this: Group): number;
152
+ /** Empties the group. Any later use throws. */
153
+ destroy(this: Group): void;
154
+ }
155
+
156
+ interface Str {
157
+ (this: void, min: number, max: number): Codec<string>;
158
+ /** Every character drawn from the symbol set. The smaller the set, the fewer the bits. */
159
+ alphabet(this: void, symbols: string, min: number, max: number): Codec<string>;
160
+ alphanum(this: void, min: number, max: number): Codec<string>;
161
+ base32(this: void, min: number, max: number): Codec<string>;
162
+ base64(this: void, min: number, max: number): Codec<string>;
163
+ digits(this: void, min: number, max: number): Codec<string>;
164
+ hex(this: void, min: number, max: number): Codec<string>;
165
+ }
166
+
167
+ interface Vec3 {
168
+ (this: void, component?: Codec<number>): Codec<Vector3>;
169
+ /** A direction at the angular precision. Any nonzero vector normalizes at encode. */
170
+ unit(this: void, precision: number): Codec<Vector3>;
171
+ /** A direction at the implied precision and a quantized magnitude. */
172
+ bounded(this: void, max: number, step: number): Codec<Vector3>;
173
+ }
174
+
175
+ /** Zero, one, two, or three degrees of freedom, in that order. */
176
+ interface Rotation {
177
+ none(this: void): Codec<CFrame>;
178
+ axis(this: void, axis: Vector3, precision: number): Codec<CFrame>;
179
+ direction(this: void, precision: number): Codec<CFrame>;
180
+ quat(this: void, precision: number): Codec<CFrame>;
181
+ }
182
+
183
+ interface Color3Codec {
184
+ (this: void): Codec<Color3>;
185
+ rgb565(this: void): Codec<Color3>;
186
+ palette(this: void, colors: ReadonlyArray<Color3>): Codec<Color3>;
187
+ }
188
+
189
+ type Infer<C> = C extends Codec<infer T> ? T : never;
190
+
191
+ /** @hidden */ type Flat<T> = { [K in keyof T]: T[K] };
192
+
193
+ /** A field whose codec is `optional` is omittable, as a Luau `{ tag: string? }` is. */
194
+ type Schema<S extends Record<string, Codec<unknown>>> = Flat<
195
+ { [K in keyof S as undefined extends Infer<S[K]> ? never : K]: Infer<S[K]> } & {
196
+ [K in keyof S as undefined extends Infer<S[K]> ? K : never]?: Infer<S[K]>;
197
+ }
198
+ >;
199
+ }
200
+
201
+ interface LyncModule {
202
+ /** Opens a namespace. The returned table is pure data, required by both machines. */
203
+ define<T extends Record<string, Lync.Packet<any> | Lync.Query<any, any> | Lync.Set<any, any>>>(
204
+ this: void,
205
+ name: string,
206
+ defs: T,
207
+ ): Readonly<T>;
208
+ /** Seals, compiles, opens transports, runs the handshake. Exactly once per machine. */
209
+ start(this: void): void;
210
+ /** Sends everything buffered. Nothing goes out without it, and an empty flush is free. */
211
+ flush(this: void): void;
212
+ /** The byte budget caps replicated state only. Packets and requests always send in full. */
213
+ flush(this: void, maxBytes: number): void;
214
+ /** One namespace, on a cadence of its own. */
215
+ flush(this: void, name: string, maxBytes?: number): void;
216
+ /** One final flush. Outstanding requests resolve as shutdown and the transports go. */
217
+ close(this: void): void;
218
+
219
+ packet<T>(this: void, codec: Lync.Codec<T>): Lync.Packet<T>;
220
+ query<Q, R>(this: void, request: Lync.Codec<Q>, response: Lync.Codec<R>): Lync.Query<Q, R>;
221
+ replicate<T extends object>(this: void, struct: Lync.Codec<T>): Lync.Set<T>;
222
+ group(this: void): Lync.Group;
223
+
224
+ readonly all: Lync.All;
225
+ except(this: void, target: Player | ReadonlyArray<Player> | Lync.Group): Lync.Except;
226
+ /** The clear sentinel: an update writes it to empty an optional field. */
227
+ readonly none: Lync.None;
228
+
229
+ onLog(this: void, fn: (kind: Lync.LogKind, message: string, data: Lync.LogData) => void): Lync.Connection;
230
+ /** The default printer. Disconnect it to format records yourself. */
231
+ readonly console: Lync.Connection;
232
+ /** A frozen snapshot of monotonic counters, so any two of them diff into exact rates. */
233
+ stats(this: void, name: string): Lync.Stats;
234
+
235
+ bool(this: void): Lync.Codec<boolean>;
236
+ int(this: void, min: number, max: number): Lync.Codec<number>;
237
+ quant(this: void, min: number, max: number, step: number): Lync.Codec<number>;
238
+ /** Degrees, cyclic, wrapping at a whole turn. */
239
+ angle(this: void, precision: number): Lync.Codec<number>;
240
+ f32(this: void): Lync.Codec<number>;
241
+ f64(this: void): Lync.Codec<number>;
242
+ /** Unsigned integer exact to 2^53. */
243
+ vlq(this: void): Lync.Codec<number>;
244
+ /** Signed integer exact to 2^53. */
245
+ vli(this: void): Lync.Codec<number>;
246
+ readonly str: Lync.Str;
247
+ buffer(this: void, min: number, max: number): Lync.Codec<buffer>;
248
+
249
+ struct<S extends Record<string, Lync.Codec<unknown>>>(
250
+ this: void,
251
+ fields: S,
252
+ ): Lync.Codec<Lync.Schema<S>>;
253
+ array<T>(this: void, element: Lync.Codec<T>, min: number, max: number): Lync.Codec<T[]>;
254
+ map<K, V>(
255
+ this: void,
256
+ key: Lync.Codec<K>,
257
+ value: Lync.Codec<V>,
258
+ min: number,
259
+ max: number,
260
+ ): Lync.Codec<Map<K, V>>;
261
+ optional<T>(this: void, inner: Lync.Codec<T>): Lync.Codec<T | undefined>;
262
+ tagged<F extends string, V extends Record<string, Lync.Codec<object>>>(
263
+ this: void,
264
+ field: F,
265
+ variants: V,
266
+ ): Lync.Codec<
267
+ { [K in keyof V & string]: { [P in F]: K } & Lync.Infer<V[K]> }[keyof V & string]
268
+ >;
269
+ enum<const T extends readonly string[]>(this: void, names: T): Lync.Codec<T[number]>;
270
+ bitfield<const T extends readonly string[]>(
271
+ this: void,
272
+ names: T,
273
+ ): Lync.Codec<{ [K in T[number]]: boolean }>;
274
+
275
+ vec2(this: void, component?: Lync.Codec<number>): Lync.Codec<Vector2>;
276
+ readonly vec3: Lync.Vec3;
277
+ cframe(this: void, position: Lync.Codec<Vector3>, rotation: Lync.Codec<CFrame>): Lync.Codec<CFrame>;
278
+ readonly rotation: Lync.Rotation;
279
+ readonly color3: Lync.Color3Codec;
280
+ /** Decodes to the instance or nil when the receiver cannot see it. */
281
+ inst<T extends Instance = Instance>(this: void, className?: string): Lync.Codec<T | undefined>;
282
+ }
283
+
284
+ declare const Lync: LyncModule;
285
+ export = Lync;
package/init.luau ADDED
@@ -0,0 +1,91 @@
1
+ --!strict
2
+ --!optimize 2
3
+ -- The public binding: every name the surface offers, bound once and frozen.
4
+
5
+ local Codecs = require (script.codec)
6
+ local Define = require (script.api.Define)
7
+ local Group = require (script.channel.Group)
8
+ local Lifecycle = require (script.api.Lifecycle)
9
+ local Log = require (script.kernel.Log)
10
+ local Packet = require (script.api.Packet)
11
+ local Query = require (script.api.Query)
12
+ local Recipients = require (script.channel.Recipients)
13
+ local Set = require (script.api.Set)
14
+ local Types = require (script.Types)
15
+ -- Types ---------------------------------------------------------------
16
+
17
+ --[[
18
+ Infer, Schema, and Update are absent because Luau cannot alias a type function: `export type
19
+ Infer<C> = Types.Infer<C>` reduces to an error type. A consumer that needs to name one requires
20
+ the Types module beside this one.
21
+ ]]
22
+
23
+ export type All = Types.All
24
+ export type Cause = Types.Cause
25
+ export type Codec<T> = Types.Codec<T>
26
+ export type Connection = Types.Connection
27
+ export type DefinitionStats = Types.DefinitionStats
28
+ export type Except = Types.Except
29
+ export type Group = Types.Group
30
+ export type LogData = Types.LogData
31
+ export type LogKind = Types.LogKind
32
+ export type None = Types.None
33
+ export type Outcome<R> = Types.Outcome<R>
34
+ export type OutcomeCode = Types.OutcomeCode
35
+ export type OutcomeData = Types.OutcomeData
36
+ export type Packet<T> = Types.Packet<T>
37
+ export type Query<Q, R> = Types.Query<Q, R>
38
+ export type Recipient = Types.Recipient
39
+ export type Set<T> = Types.Set<T>
40
+ export type Stats = Types.Stats
41
+ export type ValidateContext = Types.ValidateContext
42
+
43
+ -- Public --------------------------------------------------------------
44
+
45
+ local Lync = {
46
+ define = Define.define,
47
+ start = Lifecycle.start,
48
+ flush = Lifecycle.flush,
49
+ close = Lifecycle.close,
50
+ stats = Lifecycle.stats,
51
+
52
+ packet = Packet.new,
53
+ query = Query.new,
54
+ replicate = Set.new,
55
+ group = Group.new,
56
+
57
+ all = Recipients.ALL,
58
+ except = Recipients.except,
59
+ none = Set.NONE,
60
+
61
+ onLog = Log.onLog,
62
+ console = Log.console,
63
+
64
+ bool = Codecs.bool,
65
+ int = Codecs.int,
66
+ quant = Codecs.quant,
67
+ angle = Codecs.angle,
68
+ f32 = Codecs.f32,
69
+ f64 = Codecs.f64,
70
+ vlq = Codecs.vlq,
71
+ vli = Codecs.vli,
72
+ str = Codecs.str,
73
+ buffer = Codecs.buffer,
74
+
75
+ struct = Codecs.struct,
76
+ array = Codecs.array,
77
+ map = Codecs.map,
78
+ optional = Codecs.optional,
79
+ tagged = Codecs.tagged,
80
+ enum = Codecs.enum,
81
+ bitfield = Codecs.bitfield,
82
+
83
+ vec2 = Codecs.vec2,
84
+ vec3 = Codecs.vec3,
85
+ cframe = Codecs.cframe,
86
+ rotation = Codecs.rotation,
87
+ color3 = Codecs.color3,
88
+ inst = Codecs.inst,
89
+ }
90
+
91
+ return table.freeze (Lync)
@@ -0,0 +1,16 @@
1
+ --!strict
2
+ --!optimize 2
3
+ -- The ranged-int bit width: the bits needed to address a span of values.
4
+
5
+ local Bits = {}
6
+
7
+ --[[
8
+ A logarithm rounds short from two to the forty-ninth up, and a width a bit short encodes cleanly
9
+ and decodes as a different value.
10
+ ]]
11
+ function Bits.width (span: number): number
12
+ if span <= 0 then return 0 end
13
+ return (select (2, math.frexp (span)))
14
+ end
15
+
16
+ return table.freeze (Bits)
@@ -0,0 +1,121 @@
1
+ --!strict
2
+ --!optimize 2
3
+ --[[
4
+ Every diagnosis the library can reach, as the code a caller branches on and the line it reads.
5
+
6
+ One home, a code being part of the surface: two spellings of one is a caller's branch that
7
+ quietly stops matching.
8
+ ]]
9
+
10
+ local Catalog = {}
11
+
12
+ -- Types ---------------------------------------------------------------
13
+
14
+ --[[
15
+ A site raising a code with no row here does not compile, which is the half of a closed
16
+ catalogue a type checker can hold on its own.
17
+ ]]
18
+ export type Code =
19
+ "drop.tag"
20
+ | "drop.defid"
21
+ | "drop.count"
22
+ | "drop.varint"
23
+ | "drop.pad"
24
+ | "drop.map-order"
25
+ | "drop.sidecar"
26
+ | "drop.bounds"
27
+ | "drop.section"
28
+ | "drop.validate"
29
+ | "drop.hello"
30
+ | "drop.unready"
31
+ | "drop.mismatch"
32
+
33
+ --[[
34
+ The template is prose and may be reworded in any release. The keys are what a caller may hold
35
+ to, each reaching `data` under its own name.
36
+ ]]
37
+ export type Entry = {
38
+ read template: string,
39
+ read keys: { string },
40
+ }
41
+
42
+ -- Public --------------------------------------------------------------
43
+
44
+ -- Every code, under the name a raising site reaches it by.
45
+ Catalog.TAG = "drop.tag" :: Code
46
+ Catalog.DEFID = "drop.defid" :: Code
47
+ Catalog.COUNT = "drop.count" :: Code
48
+ Catalog.VARINT = "drop.varint" :: Code
49
+ Catalog.PAD = "drop.pad" :: Code
50
+ Catalog.MAP_ORDER = "drop.map-order" :: Code
51
+ Catalog.SIDECAR = "drop.sidecar" :: Code
52
+ Catalog.BOUNDS = "drop.bounds" :: Code
53
+ Catalog.SECTION = "drop.section" :: Code
54
+ Catalog.VALIDATE = "drop.validate" :: Code
55
+ Catalog.HELLO = "drop.hello" :: Code
56
+ Catalog.UNREADY = "drop.unready" :: Code
57
+ Catalog.MISMATCH = "drop.mismatch" :: Code
58
+
59
+ --[[
60
+ Every row, and the whole of what a drop can be. The first seven are shape violations, six of
61
+ them saying the field, what the schema wanted and what the bytes had, since there are only
62
+ three things to say. The last six are about a frame rather than a field and name no path. A
63
+ section is one row, its ways of failing being settled phrases nobody should branch on.
64
+ ]]
65
+ Catalog.entries = table.freeze ({
66
+ [Catalog.TAG] = table.freeze ({
67
+ template = "{path} expected {expected}, got {got}",
68
+ keys = table.freeze ({ "path", "expected", "got" }),
69
+ }),
70
+ [Catalog.COUNT] = table.freeze ({
71
+ template = "{path} expected a count within {expected}, got {got}",
72
+ keys = table.freeze ({ "path", "expected", "got" }),
73
+ }),
74
+ [Catalog.VARINT] = table.freeze ({
75
+ template = "{path} expected {expected}, got a varint that {got}",
76
+ keys = table.freeze ({ "path", "expected", "got" }),
77
+ }),
78
+ [Catalog.PAD] = table.freeze ({
79
+ template = "{path} expected nothing but zeroes after {expected}, got {got}",
80
+ keys = table.freeze ({ "path", "expected", "got" }),
81
+ }),
82
+ [Catalog.MAP_ORDER] = table.freeze ({
83
+ template = "{path} expected a key above {expected}, got {got}",
84
+ keys = table.freeze ({ "path", "expected", "got" }),
85
+ }),
86
+ [Catalog.SIDECAR] = table.freeze ({
87
+ template = "{path} expected {expected}, got an instance of {got}",
88
+ keys = table.freeze ({ "path", "expected", "got" }),
89
+ }),
90
+ [Catalog.VALIDATE] = table.freeze ({
91
+ template = "{path} was turned away: {reason}",
92
+ keys = table.freeze ({ "path", "reason" }),
93
+ }),
94
+
95
+ [Catalog.BOUNDS] = table.freeze ({
96
+ template = "a frame ran past the bytes it arrived with, and said {said}",
97
+ keys = table.freeze ({ "said" }),
98
+ }),
99
+ [Catalog.SECTION] = table.freeze ({
100
+ template = "a state section is not one this definition could have written, and said {said}",
101
+ keys = table.freeze ({ "said" }),
102
+ }),
103
+ [Catalog.DEFID] = table.freeze ({
104
+ template = "a frame named definition {id}, and this namespace has {count}",
105
+ keys = table.freeze ({ "id", "count" }),
106
+ }),
107
+ [Catalog.HELLO] = table.freeze ({
108
+ template = "the handshake lane carried something that is no hello",
109
+ keys = table.freeze ({}),
110
+ }),
111
+ [Catalog.UNREADY] = table.freeze ({
112
+ template = "a frame arrived before this client was told it could send one",
113
+ keys = table.freeze ({}),
114
+ }),
115
+ [Catalog.MISMATCH] = table.freeze ({
116
+ template = "this namespace hashes {ours} and the other machine says {theirs}",
117
+ keys = table.freeze ({ "ours", "theirs" }),
118
+ }),
119
+ }) :: { [Code]: Entry }
120
+
121
+ return table.freeze (Catalog)