@trioforge/crucible 0.1.0 → 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 (134) hide show
  1. package/out/client/index.d.ts +7 -0
  2. package/out/client/init.luau +36 -1
  3. package/out/configs.d.ts +37 -0
  4. package/out/configs.luau +46 -0
  5. package/out/data.d.ts +224 -0
  6. package/out/data.luau +97 -0
  7. package/out/emit/configs.d.ts +4 -0
  8. package/out/emit/configs.luau +22 -0
  9. package/out/emit/data.d.ts +6 -0
  10. package/out/emit/data.luau +19 -0
  11. package/out/emit/index.d.ts +5 -1
  12. package/out/emit/init.luau +23 -7
  13. package/out/emit/network.d.ts +6 -0
  14. package/out/emit/network.luau +36 -0
  15. package/out/emit/replicated.d.ts +4 -0
  16. package/out/emit/replicated.luau +13 -0
  17. package/out/index.d.ts +8 -0
  18. package/out/init.luau +23 -0
  19. package/out/internal/binding.luau +5 -0
  20. package/out/internal/boot.luau +107 -8
  21. package/out/internal/configs/limits.d.ts +10 -0
  22. package/out/internal/configs/limits.luau +25 -0
  23. package/out/internal/configs/port.d.ts +29 -0
  24. package/out/internal/configs/port.luau +99 -0
  25. package/out/internal/configs/runtime.d.ts +21 -0
  26. package/out/internal/configs/runtime.luau +981 -0
  27. package/out/internal/data/admission.d.ts +1 -0
  28. package/out/internal/data/admission.luau +673 -0
  29. package/out/internal/data/declarations.d.ts +1 -0
  30. package/out/internal/data/declarations.luau +384 -0
  31. package/out/internal/data/envelope.d.ts +1 -0
  32. package/out/internal/data/envelope.luau +318 -0
  33. package/out/internal/data/handles.d.ts +1 -0
  34. package/out/internal/data/handles.luau +255 -0
  35. package/out/internal/data/inbox-transforms.d.ts +1 -0
  36. package/out/internal/data/inbox-transforms.luau +458 -0
  37. package/out/internal/data/inbox.d.ts +1 -0
  38. package/out/internal/data/inbox.luau +437 -0
  39. package/out/internal/data/lease.d.ts +1 -0
  40. package/out/internal/data/lease.luau +304 -0
  41. package/out/internal/data/limits.d.ts +1 -0
  42. package/out/internal/data/limits.luau +234 -0
  43. package/out/internal/data/messages.d.ts +1 -0
  44. package/out/internal/data/messages.luau +69 -0
  45. package/out/internal/data/operation-id.d.ts +1 -0
  46. package/out/internal/data/operation-id.luau +61 -0
  47. package/out/internal/data/paths.d.ts +2 -0
  48. package/out/internal/data/paths.luau +323 -0
  49. package/out/internal/data/port.d.ts +1 -0
  50. package/out/internal/data/port.luau +290 -0
  51. package/out/internal/data/profiles.d.ts +1 -0
  52. package/out/internal/data/profiles.luau +862 -0
  53. package/out/internal/data/receipts.d.ts +1 -0
  54. package/out/internal/data/receipts.luau +190 -0
  55. package/out/internal/data/record-bindings.d.ts +1 -0
  56. package/out/internal/data/record-bindings.luau +92 -0
  57. package/out/internal/data/record-declarations.d.ts +1 -0
  58. package/out/internal/data/record-declarations.luau +140 -0
  59. package/out/internal/data/records.d.ts +1 -0
  60. package/out/internal/data/records.luau +400 -0
  61. package/out/internal/data/runtime.d.ts +1 -0
  62. package/out/internal/data/runtime.luau +918 -0
  63. package/out/internal/data/scheduler.d.ts +1 -0
  64. package/out/internal/data/scheduler.luau +507 -0
  65. package/out/internal/data/session.d.ts +1 -0
  66. package/out/internal/data/session.luau +234 -0
  67. package/out/internal/data/shutdown.d.ts +1 -0
  68. package/out/internal/data/shutdown.luau +91 -0
  69. package/out/internal/data/waiters.d.ts +1 -0
  70. package/out/internal/data/waiters.luau +65 -0
  71. package/out/internal/lifecycle.luau +45 -0
  72. package/out/internal/network/catalog.d.ts +26 -0
  73. package/out/internal/network/catalog.luau +182 -0
  74. package/out/internal/network/claims.d.ts +1 -0
  75. package/out/internal/network/claims.luau +154 -0
  76. package/out/internal/network/client-session.d.ts +1 -0
  77. package/out/internal/network/client-session.luau +403 -0
  78. package/out/internal/network/codec.d.ts +63 -0
  79. package/out/internal/network/codec.luau +1183 -0
  80. package/out/internal/network/envelope.d.ts +1 -0
  81. package/out/internal/network/envelope.luau +247 -0
  82. package/out/internal/network/limits.d.ts +26 -0
  83. package/out/internal/network/limits.luau +55 -0
  84. package/out/internal/network/port.d.ts +1 -0
  85. package/out/internal/network/port.luau +19 -0
  86. package/out/internal/network/roblox-network-port.d.ts +1 -0
  87. package/out/internal/network/roblox-network-port.luau +105 -0
  88. package/out/internal/network/schema.d.ts +72 -0
  89. package/out/internal/network/schema.luau +386 -0
  90. package/out/internal/network/server-session.d.ts +1 -0
  91. package/out/internal/network/server-session.luau +393 -0
  92. package/out/internal/network/session.d.ts +1 -0
  93. package/out/internal/network/session.luau +266 -0
  94. package/out/internal/reactive/context.d.ts +1 -0
  95. package/out/internal/reactive/context.luau +149 -0
  96. package/out/internal/reactive/flush.d.ts +1 -0
  97. package/out/internal/reactive/flush.luau +355 -0
  98. package/out/internal/reactive/graph.d.ts +1 -0
  99. package/out/internal/reactive/graph.luau +626 -0
  100. package/out/internal/reactive/limits.d.ts +1 -0
  101. package/out/internal/reactive/limits.luau +54 -0
  102. package/out/internal/reactive/ownership.d.ts +1 -0
  103. package/out/internal/reactive/ownership.luau +626 -0
  104. package/out/internal/registry.luau +10 -1
  105. package/out/internal/sync/client-sync.d.ts +1 -0
  106. package/out/internal/sync/client-sync.luau +157 -0
  107. package/out/internal/sync/declarations.d.ts +1 -0
  108. package/out/internal/sync/declarations.luau +276 -0
  109. package/out/internal/sync/frame.d.ts +1 -0
  110. package/out/internal/sync/frame.luau +91 -0
  111. package/out/internal/sync/server-sync.d.ts +1 -0
  112. package/out/internal/sync/server-sync.luau +502 -0
  113. package/out/internal/sync/values.d.ts +1 -0
  114. package/out/internal/sync/values.luau +930 -0
  115. package/out/network.d.ts +66 -0
  116. package/out/network.luau +24 -0
  117. package/out/reactive.d.ts +76 -0
  118. package/out/reactive.luau +136 -0
  119. package/out/replicated.d.ts +38 -0
  120. package/out/replicated.luau +45 -0
  121. package/out/server/data.d.ts +54 -0
  122. package/out/server/data.luau +44 -0
  123. package/out/server/index.d.ts +16 -0
  124. package/out/server/init.luau +49 -3
  125. package/out/server/publish.d.ts +20 -0
  126. package/out/server/publish.luau +24 -0
  127. package/out/vide/index.d.ts +7 -0
  128. package/out/vide/init.luau +50 -0
  129. package/package.json +13 -2
  130. package/transformer/data.cjs +72 -0
  131. package/transformer/declarations.cjs +153 -0
  132. package/transformer/index.js +37 -11
  133. package/transformer/network.cjs +961 -0
  134. package/vide/package.json +4 -0
@@ -0,0 +1,66 @@
1
+ declare const message: unique symbol;
2
+ declare const width: unique symbol;
3
+ export type f64 = number & {
4
+ readonly [width]?: "f64";
5
+ };
6
+ export type f32 = number & {
7
+ readonly [width]?: "f32";
8
+ };
9
+ export type u8 = number & {
10
+ readonly [width]?: "u8";
11
+ };
12
+ export type u16 = number & {
13
+ readonly [width]?: "u16";
14
+ };
15
+ export type u32 = number & {
16
+ readonly [width]?: "u32";
17
+ };
18
+ export interface ToServer<P> {
19
+ readonly [message]: {
20
+ readonly kind: "to-server";
21
+ readonly payload: (value: P) => P;
22
+ };
23
+ }
24
+ export interface ToClient<P> {
25
+ readonly [message]: {
26
+ readonly kind: "to-client";
27
+ readonly payload: (value: P) => P;
28
+ };
29
+ }
30
+ export interface AskServer<Request, Response> {
31
+ readonly [message]: {
32
+ readonly kind: "ask-server";
33
+ readonly request: (value: Request) => Request;
34
+ readonly response: (value: Response) => Response;
35
+ };
36
+ }
37
+ export interface ClientReady {
38
+ readonly [message]: {
39
+ readonly kind: "client-ready";
40
+ };
41
+ }
42
+ export declare const ClientReady: ClientReady;
43
+ export interface Receiver {
44
+ readonly [message]: {
45
+ readonly kind: "receiver";
46
+ };
47
+ }
48
+ export type PayloadArgs<P> = [P] extends [undefined] ? [] : [payload: P];
49
+ export type AskFailure = "timeout" | "disconnected" | "unavailable" | "handler-error" | "overloaded" | "invalid";
50
+ export type AskResult<T> = {
51
+ readonly ok: true;
52
+ readonly value: T;
53
+ } | {
54
+ readonly ok: false;
55
+ readonly reason: AskFailure;
56
+ };
57
+ export interface AskOptions {
58
+ readonly timeout?: number;
59
+ }
60
+ export type ReceiveTarget = ToServer<any> | ToClient<any> | AskServer<any, any> | ClientReady;
61
+ export type ReceiveCallback<T extends ReceiveTarget> = T extends ToServer<infer P> ? (player: Player, ...payload: PayloadArgs<P>) => void : T extends AskServer<infer Request, infer Response> ? (player: Player, ...request: PayloadArgs<Request>) => Response : T extends ToClient<infer P> ? (...payload: PayloadArgs<P>) => void : (player: Player) => (() => void) | void;
62
+ export declare function toServer<P>(_name: string): ToServer<P>;
63
+ export declare function toClient<P>(_name: string): ToClient<P>;
64
+ export declare function askServer<Request, Response>(_name: string, _options?: AskOptions): AskServer<Request, Response>;
65
+ export declare function receive<T extends ReceiveTarget>(_target: T, _callback: ReceiveCallback<T>): Receiver;
66
+ export {};
@@ -0,0 +1,24 @@
1
+ -- Compiled with roblox-ts v3.0.0
2
+ -- Function-valued phantom positions keep every payload type invariant under strictFunctionTypes.
3
+ local ClientReady = table.freeze({})
4
+ -- `any` is the only argument every invariant token is assignable to.
5
+ local UNTRANSFORMED = "Crucible networking needs the @trioforge/crucible/transformer plugin"
6
+ local function toServer(_name)
7
+ error(UNTRANSFORMED)
8
+ end
9
+ local function toClient(_name)
10
+ error(UNTRANSFORMED)
11
+ end
12
+ local function askServer(_name, _options)
13
+ error(UNTRANSFORMED)
14
+ end
15
+ local function receive(_target, _callback)
16
+ error(UNTRANSFORMED)
17
+ end
18
+ return {
19
+ toServer = toServer,
20
+ toClient = toClient,
21
+ askServer = askServer,
22
+ receive = receive,
23
+ ClientReady = ClientReady,
24
+ }
@@ -0,0 +1,76 @@
1
+ declare const stateKind: unique symbol;
2
+ /**
3
+ * A value read by calling it. Any function of no arguments is one, so a getter that reads state, such as
4
+ * `() => this.open() && !this.locked()`, tracks like a state or a computed.
5
+ */
6
+ export interface Readable<T> {
7
+ (): T;
8
+ }
9
+ /**
10
+ * A writable local value. `set` stores its argument as is, and `update` stores what its callback returns for the
11
+ * current value. A write equal (`rawequal`) to the current value does nothing, so replace tables instead of
12
+ * mutating them. At runtime a State is a callable table, not a function: pass `() => s()` to code that tests
13
+ * `type(x) == "function"`.
14
+ */
15
+ export interface State<T> extends Readable<T> {
16
+ readonly [stateKind]: {
17
+ readonly value: (value: T) => T;
18
+ };
19
+ set(value: T): void;
20
+ update(change: (current: T) => T): void;
21
+ }
22
+ /** A local source of truth. It needs no owner: a field initializer, a local or a module constant. */
23
+ export declare function state<T>(initial: T): State<T>;
24
+ /**
25
+ * A cached value derived from other readables. It evaluates on its first read and again only when read after a
26
+ * dependency changed. A result equal to the cached one stops propagation. A throw or a yield is cached and rethrown
27
+ * to every reader until a dependency changes. Writing state or creating an effect, watch, scope or cleanup inside
28
+ * `read` throws. It needs no owner.
29
+ */
30
+ export declare function computed<T>(read: () => T): Readable<T>;
31
+ /**
32
+ * Keeps something outside the graph in step with state. The body runs on the next reactive flush, never inside
33
+ * the call that created it, and again after anything it read changed. It may return a cleanup, which runs before
34
+ * the next run and when the effect ends. It belongs to the code that created it: inside `onEnable` it ends at
35
+ * disable; otherwise it ends with its object, its parent effect run or its scope. A throw, a yield or another
36
+ * return value stops it for good, after what its run created is released. With no owner it throws.
37
+ */
38
+ export declare function effect(body: () => void | (() => void)): void;
39
+ /**
40
+ * Calls `onChange(value, previous)` on the flushes where `source()` differs from the last value seen. The source is
41
+ * read now as the baseline, so a change before the first flush is seen. It observes the value at each flush, not
42
+ * each write. Owned and stopped like `effect`.
43
+ */
44
+ export declare function watch<T>(source: Readable<T>, onChange: (value: T, previous: T) => void): void;
45
+ /** Calls `read` without tracking what it reads. Ownership is unchanged. */
46
+ export declare function untracked<T>(read: () => T): T;
47
+ /**
48
+ * Runs `setup` now as a new lifetime that owns every effect, watch, scope and cleanup created inside it. Returns
49
+ * an idempotent dispose. Inside another owner it also ends when that owner ends; outside every owner it is a root
50
+ * that only its dispose ends. If `setup` throws or yields, what it created ends and the error is rethrown.
51
+ */
52
+ export declare function scope(setup: () => void): () => void;
53
+ /**
54
+ * Registers `release` to run once when the current owner ends: after the owner's effects and scopes ended, newest
55
+ * release first. Under an owner that already ended it runs at once. With no owner it throws.
56
+ */
57
+ export declare function onCleanup(release: () => void): void;
58
+ /**
59
+ * What `own` releases: a function, called with no arguments; a connection, disconnected; an Instance, destroyed; a
60
+ * thread, cancelled with `task.cancel`; or an object, through its `destroy()` or else its `Destroy()`. A `State` is a
61
+ * callable table, not a function, so it is not one.
62
+ */
63
+ export type Ownable = ((() => void) & {
64
+ readonly [stateKind]?: never;
65
+ }) | RBXScriptConnection | Instance | thread | {
66
+ destroy(): void;
67
+ } | {
68
+ Destroy(): void;
69
+ };
70
+ /**
71
+ * Releases `value` once when the current owner ends, exactly as `onCleanup` would release a function that does it,
72
+ * and returns `value`. How it is released is decided now, from its type. Under an owner that already ended it is
73
+ * released at once. With no owner it throws, and so does a value that is not `Ownable`.
74
+ */
75
+ export declare function own<T extends Ownable>(value: T): T;
76
+ export {};
@@ -0,0 +1,136 @@
1
+ -- Compiled with roblox-ts v3.0.0
2
+ local TS = _G[script]
3
+ local _flush = TS.import(script, script.Parent, "internal", "reactive", "flush")
4
+ local createEffect = _flush.createEffect
5
+ local createWatch = _flush.createWatch
6
+ local _graph = TS.import(script, script.Parent, "internal", "reactive", "graph")
7
+ local createComputedNode = _graph.createComputedNode
8
+ local createStateNode = _graph.createStateNode
9
+ local readComputed = _graph.readComputed
10
+ local readState = _graph.readState
11
+ local readUntracked = _graph.readUntracked
12
+ local update = _graph.update
13
+ local write = _graph.write
14
+ local _ownership = TS.import(script, script.Parent, "internal", "reactive", "ownership")
15
+ local createScope = _ownership.createScope
16
+ local registerCleanup = _ownership.registerCleanup
17
+ local registerOwned = _ownership.registerOwned
18
+ --[[
19
+ *
20
+ * A value read by calling it. Any function of no arguments is one, so a getter that reads state, such as
21
+ * `() => this.open() && !this.locked()`, tracks like a state or a computed.
22
+
23
+ ]]
24
+ --[[
25
+ *
26
+ * A writable local value. `set` stores its argument as is, and `update` stores what its callback returns for the
27
+ * current value. A write equal (`rawequal`) to the current value does nothing, so replace tables instead of
28
+ * mutating them. At runtime a State is a callable table, not a function: pass `() => s()` to code that tests
29
+ * `type(x) == "function"`.
30
+
31
+ ]]
32
+ local stateMethods = {
33
+ set = function(self, value)
34
+ write(self, value)
35
+ end,
36
+ update = function(self, change)
37
+ update(self, change)
38
+ end,
39
+ }
40
+ local stateMeta = {
41
+ __index = stateMethods,
42
+ __call = function(node)
43
+ return readState(node)
44
+ end,
45
+ }
46
+ --* A local source of truth. It needs no owner: a field initializer, a local or a module constant.
47
+ local function state(initial)
48
+ return setmetatable(createStateNode(initial), stateMeta)
49
+ end
50
+ --[[
51
+ *
52
+ * A cached value derived from other readables. It evaluates on its first read and again only when read after a
53
+ * dependency changed. A result equal to the cached one stops propagation. A throw or a yield is cached and rethrown
54
+ * to every reader until a dependency changes. Writing state or creating an effect, watch, scope or cleanup inside
55
+ * `read` throws. It needs no owner.
56
+
57
+ ]]
58
+ local function computed(read)
59
+ local node = createComputedNode(read)
60
+ return function()
61
+ return readComputed(node)
62
+ end
63
+ end
64
+ --[[
65
+ *
66
+ * Keeps something outside the graph in step with state. The body runs on the next reactive flush, never inside
67
+ * the call that created it, and again after anything it read changed. It may return a cleanup, which runs before
68
+ * the next run and when the effect ends. It belongs to the code that created it: inside `onEnable` it ends at
69
+ * disable; otherwise it ends with its object, its parent effect run or its scope. A throw, a yield or another
70
+ * return value stops it for good, after what its run created is released. With no owner it throws.
71
+
72
+ ]]
73
+ local function effect(body)
74
+ createEffect(body)
75
+ end
76
+ --[[
77
+ *
78
+ * Calls `onChange(value, previous)` on the flushes where `source()` differs from the last value seen. The source is
79
+ * read now as the baseline, so a change before the first flush is seen. It observes the value at each flush, not
80
+ * each write. Owned and stopped like `effect`.
81
+
82
+ ]]
83
+ local function watch(source, onChange)
84
+ createWatch(source, onChange)
85
+ end
86
+ --* Calls `read` without tracking what it reads. Ownership is unchanged.
87
+ local function untracked(read)
88
+ return readUntracked(read)
89
+ end
90
+ --[[
91
+ *
92
+ * Runs `setup` now as a new lifetime that owns every effect, watch, scope and cleanup created inside it. Returns
93
+ * an idempotent dispose. Inside another owner it also ends when that owner ends; outside every owner it is a root
94
+ * that only its dispose ends. If `setup` throws or yields, what it created ends and the error is rethrown.
95
+
96
+ ]]
97
+ local function scope(setup)
98
+ return createScope(setup)
99
+ end
100
+ --[[
101
+ *
102
+ * Registers `release` to run once when the current owner ends: after the owner's effects and scopes ended, newest
103
+ * release first. Under an owner that already ended it runs at once. With no owner it throws.
104
+
105
+ ]]
106
+ local function onCleanup(release)
107
+ registerCleanup(release)
108
+ end
109
+ --[[
110
+ *
111
+ * What `own` releases: a function, called with no arguments; a connection, disconnected; an Instance, destroyed; a
112
+ * thread, cancelled with `task.cancel`; or an object, through its `destroy()` or else its `Destroy()`. A `State` is a
113
+ * callable table, not a function, so it is not one.
114
+
115
+ ]]
116
+ --[[
117
+ *
118
+ * Releases `value` once when the current owner ends, exactly as `onCleanup` would release a function that does it,
119
+ * and returns `value`. How it is released is decided now, from its type. Under an owner that already ended it is
120
+ * released at once. With no owner it throws, and so does a value that is not `Ownable`.
121
+
122
+ ]]
123
+ local function own(value)
124
+ registerOwned(value)
125
+ return value
126
+ end
127
+ return {
128
+ state = state,
129
+ computed = computed,
130
+ effect = effect,
131
+ watch = watch,
132
+ untracked = untracked,
133
+ scope = scope,
134
+ onCleanup = onCleanup,
135
+ own = own,
136
+ }
@@ -0,0 +1,38 @@
1
+ import type { Readable } from "./reactive";
2
+ declare const replicatedKind: unique symbol;
3
+ /** Whether a replicated value holds what the server published: before the first frame, after it, or failed. */
4
+ export type SyncStatus = "pending" | "ready" | "failed";
5
+ /**
6
+ * A value every client receives. On the server it reads the authoritative value; on a client, the last value the
7
+ * server sent, or `initial` before the first frame. At runtime it is a plain function, so it composes like any getter.
8
+ */
9
+ export interface Replicated<T> extends Readable<T> {
10
+ readonly [replicatedKind]: {
11
+ readonly audience: "all";
12
+ readonly value: (value: T) => T;
13
+ };
14
+ }
15
+ /**
16
+ * A value each player receives privately. On a client it reads the local player's value, or `initial` before the
17
+ * first frame. On the server there is no single value, so calling it throws.
18
+ */
19
+ export interface ReplicatedPerPlayer<T> extends Readable<T> {
20
+ readonly [replicatedKind]: {
21
+ readonly audience: "player";
22
+ readonly value: (value: T) => T;
23
+ };
24
+ }
25
+ /**
26
+ * Declares a value every client receives. Write it as the initializer of an exported top-level const in a shared
27
+ * module, with an explicit type argument and a literal name. The server writes it through `publish`; fields the type
28
+ * does not declare are never sent.
29
+ */
30
+ export declare function replicated<T = never>(_name: string, _initial: NoInfer<T>): Replicated<T>;
31
+ /** Declares a value each player receives privately. The server supplies it with `publish(declaration, project)`. */
32
+ export declare function replicatedPerPlayer<T = never>(_name: string, _initial: NoInfer<T>): ReplicatedPerPlayer<T>;
33
+ /**
34
+ * The declaration's sync status. Inside an effect or a computed it tracks like a value. On the server, a per-player
35
+ * declaration throws.
36
+ */
37
+ export declare function syncStatus<T>(declaration: Replicated<T> | ReplicatedPerPlayer<T>): SyncStatus;
38
+ export {};
@@ -0,0 +1,45 @@
1
+ -- Compiled with roblox-ts v3.0.0
2
+ local TS = _G[script]
3
+ local statusOf = TS.import(script, script.Parent, "internal", "sync", "declarations").statusOf
4
+ --* Whether a replicated value holds what the server published: before the first frame, after it, or failed.
5
+ --[[
6
+ *
7
+ * A value every client receives. On the server it reads the authoritative value; on a client, the last value the
8
+ * server sent, or `initial` before the first frame. At runtime it is a plain function, so it composes like any getter.
9
+
10
+ ]]
11
+ --[[
12
+ *
13
+ * A value each player receives privately. On a client it reads the local player's value, or `initial` before the
14
+ * first frame. On the server there is no single value, so calling it throws.
15
+
16
+ ]]
17
+ local UNTRANSFORMED = "Crucible replicated state needs the @trioforge/crucible/transformer plugin"
18
+ --[[
19
+ *
20
+ * Declares a value every client receives. Write it as the initializer of an exported top-level const in a shared
21
+ * module, with an explicit type argument and a literal name. The server writes it through `publish`; fields the type
22
+ * does not declare are never sent.
23
+
24
+ ]]
25
+ local function replicated(_name, _initial)
26
+ error(UNTRANSFORMED)
27
+ end
28
+ --* Declares a value each player receives privately. The server supplies it with `publish(declaration, project)`.
29
+ local function replicatedPerPlayer(_name, _initial)
30
+ error(UNTRANSFORMED)
31
+ end
32
+ --[[
33
+ *
34
+ * The declaration's sync status. Inside an effect or a computed it tracks like a value. On the server, a per-player
35
+ * declaration throws.
36
+
37
+ ]]
38
+ local function syncStatus(declaration)
39
+ return statusOf(declaration)
40
+ end
41
+ return {
42
+ replicated = replicated,
43
+ replicatedPerPlayer = replicatedPerPlayer,
44
+ syncStatus = syncStatus,
45
+ }
@@ -0,0 +1,54 @@
1
+ import type { Migration, OperationId, ProfileMessage, StoreFailure, StoreResult } from "../data";
2
+ declare const recordKind: unique symbol;
3
+ export interface StoreOptions<T> {
4
+ /**
5
+ * A new record's value, and the value of a missing required key on an object path from the root. It never fills an
6
+ * array element, a map value or an optional key of an existing record. Admitted and frozen once.
7
+ */
8
+ readonly defaults: T;
9
+ /** Append-only. Removing or reordering a step changes what every older record becomes. */
10
+ readonly migrations?: readonly Migration[];
11
+ }
12
+ /**
13
+ * A generic update's result. `replayed` is true when the operation id was already recorded for the key: `change` did
14
+ * not run, and `value` is the current stored record (the defaults when the key holds no payload), which may include
15
+ * later writers' changes. A marker stores no result, so the original one cannot be returned.
16
+ */
17
+ export type UpdateResult<T> = {
18
+ readonly ok: true;
19
+ readonly value: T;
20
+ readonly replayed: boolean;
21
+ } | {
22
+ readonly ok: false;
23
+ readonly reason: StoreFailure;
24
+ };
25
+ /**
26
+ * Keyed records with no session. Each call is one DataStore request that yields and may fail, so the surface is two
27
+ * calls with result unions, not live handles.
28
+ */
29
+ export interface DataStore<T> {
30
+ readonly [recordKind]: {
31
+ readonly kind: "records";
32
+ readonly value: (value: T) => T;
33
+ };
34
+ /** Yields. The stored record, migrated and admitted, or undefined when the key has none. */
35
+ read(key: string): StoreResult<T | undefined>;
36
+ /**
37
+ * Yields. One atomic change; `change` may run more than once, so it must be pure. A replayed id returns the current
38
+ * record with `replayed: true`. A throw in `change` cancels the write and is rethrown here.
39
+ */
40
+ update(key: string, change: (current: T) => T, id?: OperationId): UpdateResult<T>;
41
+ }
42
+ /**
43
+ * Declares keyed records by their DataStore name. It must initialize an exported top-level const in a server module,
44
+ * with `T` explicit. `T` is an object or a string-keyed record at its root.
45
+ */
46
+ export declare function dataStore<T extends object>(_name: string, _options: StoreOptions<NoInfer<T>>): DataStore<T>;
47
+ /** A fresh operation identity. Create it once per logical operation and reuse it for that operation's retries. */
48
+ export declare function operationId(): OperationId;
49
+ /**
50
+ * Declares a durable message to a player's profile. It must initialize an exported top-level const in a server module,
51
+ * with the payload type explicit. A payload type changes only by adding optional fields.
52
+ */
53
+ export declare function profileMessage<P>(_name: string): ProfileMessage<P>;
54
+ export {};
@@ -0,0 +1,44 @@
1
+ -- Compiled with roblox-ts v3.0.0
2
+ local TS = _G[script]
3
+ local newOperationId = TS.import(script, script.Parent.Parent, "internal", "data", "operation-id").newOperationId
4
+ local dataPort = TS.import(script, script.Parent.Parent, "internal", "data", "port").dataPort
5
+ --[[
6
+ *
7
+ * A generic update's result. `replayed` is true when the operation id was already recorded for the key: `change` did
8
+ * not run, and `value` is the current stored record (the defaults when the key holds no payload), which may include
9
+ * later writers' changes. A marker stores no result, so the original one cannot be returned.
10
+
11
+ ]]
12
+ --[[
13
+ *
14
+ * Keyed records with no session. Each call is one DataStore request that yields and may fail, so the surface is two
15
+ * calls with result unions, not live handles.
16
+
17
+ ]]
18
+ --[[
19
+ *
20
+ * Declares keyed records by their DataStore name. It must initialize an exported top-level const in a server module,
21
+ * with `T` explicit. `T` is an object or a string-keyed record at its root.
22
+
23
+ ]]
24
+ local function dataStore(_name, _options)
25
+ error("Crucible data needs the @trioforge/crucible/transformer plugin")
26
+ end
27
+ --* A fresh operation identity. Create it once per logical operation and reuse it for that operation's retries.
28
+ local function operationId()
29
+ return newOperationId(dataPort())
30
+ end
31
+ --[[
32
+ *
33
+ * Declares a durable message to a player's profile. It must initialize an exported top-level const in a server module,
34
+ * with the payload type explicit. A payload type changes only by adding optional fields.
35
+
36
+ ]]
37
+ local function profileMessage(_name)
38
+ error("Crucible data needs the @trioforge/crucible/transformer plugin")
39
+ end
40
+ return {
41
+ dataStore = dataStore,
42
+ operationId = operationId,
43
+ profileMessage = profileMessage,
44
+ }
@@ -1,2 +1,18 @@
1
1
  import type { BootOptions } from "../index";
2
+ import type { PayloadArgs, ToClient } from "../network";
3
+ import "../internal/data/record-bindings";
4
+ export { config, playerConfig, configReady, refreshConfigs } from "../configs";
5
+ export type { Config, PlayerConfig } from "../configs";
6
+ export { publish } from "./publish";
7
+ export type { Publication } from "./publish";
8
+ export { dataStore, operationId, profileMessage } from "./data";
9
+ export type { DataStore, StoreOptions, UpdateResult } from "./data";
10
+ export type { OperationId, StoreFailure, StoreResult } from "../data";
11
+ export type { ProfileMessage, ProfileClaim, LoadReason, ReleaseReason, Purchase, AfterGrant, QuarantinedMessage, SaveResult, SaveFailure, } from "../data";
2
12
  export declare function boot(options: BootOptions): void;
13
+ export declare namespace Network {
14
+ /** True when accepted locally: fired now, or queued before the player's client is ready. Never delivery. */
15
+ function send<P>(player: Player, message: ToClient<P>, ...payload: PayloadArgs<NoInfer<P>>): boolean;
16
+ /** Encodes once and delivers the same packet to every player in the game now. */
17
+ function broadcast<P>(message: ToClient<P>, ...payload: PayloadArgs<NoInfer<P>>): void;
18
+ }
@@ -1,16 +1,62 @@
1
1
  -- Compiled with roblox-ts v3.0.0
2
2
  local TS = _G[script]
3
+ local exports = {}
3
4
  local bootSide = TS.import(script, script.Parent, "internal", "boot").boot
5
+ local installConfigRuntime = TS.import(script, script.Parent, "internal", "configs", "runtime").installConfigRuntime
6
+ local networkPort = TS.import(script, script.Parent, "internal", "network", "port").networkPort
7
+ local ServerSession = TS.import(script, script.Parent, "internal", "network", "server-session").ServerSession
8
+ local SyncFrameMessage = TS.import(script, script.Parent, "internal", "sync", "frame").SyncFrameMessage
9
+ local installServerSync = TS.import(script, script.Parent, "internal", "sync", "server-sync").installServerSync
10
+ TS.import(script, script.Parent, "internal", "data", "record-bindings")
11
+ local installDataRuntime = TS.import(script, script.Parent, "internal", "data", "runtime").installDataRuntime
4
12
  if not game:GetService("RunService"):IsServer() then
5
13
  error("@trioforge/crucible/server loaded on the client")
6
14
  end
15
+ local session
16
+ local _configs = TS.import(script, script.Parent, "configs")
17
+ exports.config = _configs.config
18
+ exports.playerConfig = _configs.playerConfig
19
+ exports.configReady = _configs.configReady
20
+ exports.refreshConfigs = _configs.refreshConfigs
21
+ exports.publish = TS.import(script, script, "publish").publish
22
+ local _data = TS.import(script, script, "data")
23
+ exports.dataStore = _data.dataStore
24
+ exports.operationId = _data.operationId
25
+ exports.profileMessage = _data.profileMessage
26
+ local function active()
27
+ return session or error("Crucible networking is not ready; send from a constructor, a hook, or later", 0)
28
+ end
29
+ installConfigRuntime()
30
+ installDataRuntime()
7
31
  local function boot(options)
32
+ local network = ServerSession.new(networkPort())
33
+ session = network
34
+ installServerSync(function(player, frame)
35
+ return active():send(player, SyncFrameMessage, frame)
36
+ end)
8
37
  bootSide({
9
38
  name = "server",
10
39
  renders = false,
11
40
  booted = function() end,
41
+ network = network,
12
42
  }, options)
13
43
  end
14
- return {
15
- boot = boot,
16
- }
44
+ local Network = {}
45
+ do
46
+ local _container = Network
47
+ --* True when accepted locally: fired now, or queued before the player's client is ready. Never delivery.
48
+ local function send(player, message, ...)
49
+ local payload = { ... }
50
+ return active():send(player, message, payload[1])
51
+ end
52
+ _container.send = send
53
+ --* Encodes once and delivers the same packet to every player in the game now.
54
+ local function broadcast(message, ...)
55
+ local payload = { ... }
56
+ active():broadcast(message, payload[1])
57
+ end
58
+ _container.broadcast = broadcast
59
+ end
60
+ exports.boot = boot
61
+ exports.Network = Network
62
+ return exports
@@ -0,0 +1,20 @@
1
+ import type { State } from "../reactive";
2
+ import type { Replicated, ReplicatedPerPlayer } from "../replicated";
3
+ declare const publication: unique symbol;
4
+ /** What a per-player `publish` returns: an opaque claim that holds nothing callable. */
5
+ export interface Publication {
6
+ readonly [publication]: true;
7
+ }
8
+ /**
9
+ * Takes the write side of a value every client sees. Call it while a server Subsystem is constructed, as a field
10
+ * initializer: `private readonly phase = publish(JobPhase)`. `set` and `update` drop fields the type does not
11
+ * declare, check and freeze the rest, and throw at a write that cannot replicate. One Subsystem publishes each value.
12
+ */
13
+ export declare function publish<T>(target: Replicated<T>): State<T>;
14
+ /**
15
+ * Supplies a per-player value as a projection. For each player whose client is ready, it is evaluated like a
16
+ * `computed`, admitted on every pull and sent to that player alone. It must be pure and must not yield. A throw or a
17
+ * rejected value marks that player's value failed until it next changes.
18
+ */
19
+ export declare function publish<T>(target: ReplicatedPerPlayer<T>, project: (player: Player) => T): Publication;
20
+ export {};
@@ -0,0 +1,24 @@
1
+ -- Compiled with roblox-ts v3.0.0
2
+ local TS = _G[script]
3
+ local claimPublication = TS.import(script, script.Parent.Parent, "internal", "sync", "declarations").claimPublication
4
+ --* What a per-player `publish` returns: an opaque claim that holds nothing callable.
5
+ --[[
6
+ *
7
+ * Takes the write side of a value every client sees. Call it while a server Subsystem is constructed, as a field
8
+ * initializer: `private readonly phase = publish(JobPhase)`. `set` and `update` drop fields the type does not
9
+ * declare, check and freeze the rest, and throw at a write that cannot replicate. One Subsystem publishes each value.
10
+
11
+ ]]
12
+ --[[
13
+ *
14
+ * Supplies a per-player value as a projection. For each player whose client is ready, it is evaluated like a
15
+ * `computed`, admitted on every pull and sent to that player alone. It must be pure and must not yield. A throw or a
16
+ * rejected value marks that player's value failed until it next changes.
17
+
18
+ ]]
19
+ local function publish(target, project)
20
+ return claimPublication(target, project)
21
+ end
22
+ return {
23
+ publish = publish,
24
+ }
@@ -0,0 +1,7 @@
1
+ import { type Readable } from "../reactive";
2
+ /**
3
+ * A Vide source that follows `readable`. It holds the value read now, untracked, and takes each change on the
4
+ * reactive flush, through a `watch` that ends with the current Vide owner. Outside a Vide component or `root` it
5
+ * throws.
6
+ */
7
+ export declare function useReadable<T>(readable: Readable<T>): () => T;