@trioforge/crucible 0.1.0 → 0.3.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 (145) hide show
  1. package/maths/spline/package.json +4 -0
  2. package/out/client/index.d.ts +7 -0
  3. package/out/client/init.luau +36 -1
  4. package/out/configs.d.ts +37 -0
  5. package/out/configs.luau +46 -0
  6. package/out/data.d.ts +224 -0
  7. package/out/data.luau +97 -0
  8. package/out/emit/configs.d.ts +4 -0
  9. package/out/emit/configs.luau +22 -0
  10. package/out/emit/data.d.ts +6 -0
  11. package/out/emit/data.luau +19 -0
  12. package/out/emit/index.d.ts +5 -1
  13. package/out/emit/init.luau +23 -7
  14. package/out/emit/network.d.ts +6 -0
  15. package/out/emit/network.luau +36 -0
  16. package/out/emit/replicated.d.ts +4 -0
  17. package/out/emit/replicated.luau +13 -0
  18. package/out/index.d.ts +8 -0
  19. package/out/init.luau +23 -0
  20. package/out/internal/binding.luau +5 -0
  21. package/out/internal/boot.luau +112 -8
  22. package/out/internal/configs/limits.d.ts +10 -0
  23. package/out/internal/configs/limits.luau +25 -0
  24. package/out/internal/configs/port.d.ts +29 -0
  25. package/out/internal/configs/port.luau +99 -0
  26. package/out/internal/configs/runtime.d.ts +21 -0
  27. package/out/internal/configs/runtime.luau +981 -0
  28. package/out/internal/data/admission.d.ts +1 -0
  29. package/out/internal/data/admission.luau +673 -0
  30. package/out/internal/data/declarations.d.ts +1 -0
  31. package/out/internal/data/declarations.luau +384 -0
  32. package/out/internal/data/envelope.d.ts +1 -0
  33. package/out/internal/data/envelope.luau +318 -0
  34. package/out/internal/data/handles.d.ts +1 -0
  35. package/out/internal/data/handles.luau +255 -0
  36. package/out/internal/data/inbox-transforms.d.ts +1 -0
  37. package/out/internal/data/inbox-transforms.luau +458 -0
  38. package/out/internal/data/inbox.d.ts +1 -0
  39. package/out/internal/data/inbox.luau +437 -0
  40. package/out/internal/data/lease.d.ts +1 -0
  41. package/out/internal/data/lease.luau +304 -0
  42. package/out/internal/data/limits.d.ts +1 -0
  43. package/out/internal/data/limits.luau +234 -0
  44. package/out/internal/data/messages.d.ts +1 -0
  45. package/out/internal/data/messages.luau +69 -0
  46. package/out/internal/data/operation-id.d.ts +1 -0
  47. package/out/internal/data/operation-id.luau +61 -0
  48. package/out/internal/data/paths.d.ts +2 -0
  49. package/out/internal/data/paths.luau +323 -0
  50. package/out/internal/data/port.d.ts +1 -0
  51. package/out/internal/data/port.luau +290 -0
  52. package/out/internal/data/profiles.d.ts +1 -0
  53. package/out/internal/data/profiles.luau +862 -0
  54. package/out/internal/data/receipts.d.ts +1 -0
  55. package/out/internal/data/receipts.luau +190 -0
  56. package/out/internal/data/record-bindings.d.ts +1 -0
  57. package/out/internal/data/record-bindings.luau +92 -0
  58. package/out/internal/data/record-declarations.d.ts +1 -0
  59. package/out/internal/data/record-declarations.luau +140 -0
  60. package/out/internal/data/records.d.ts +1 -0
  61. package/out/internal/data/records.luau +400 -0
  62. package/out/internal/data/runtime.d.ts +1 -0
  63. package/out/internal/data/runtime.luau +918 -0
  64. package/out/internal/data/scheduler.d.ts +1 -0
  65. package/out/internal/data/scheduler.luau +507 -0
  66. package/out/internal/data/session.d.ts +1 -0
  67. package/out/internal/data/session.luau +234 -0
  68. package/out/internal/data/shutdown.d.ts +1 -0
  69. package/out/internal/data/shutdown.luau +91 -0
  70. package/out/internal/data/waiters.d.ts +1 -0
  71. package/out/internal/data/waiters.luau +65 -0
  72. package/out/internal/lifecycle.luau +45 -0
  73. package/out/internal/network/catalog.d.ts +26 -0
  74. package/out/internal/network/catalog.luau +182 -0
  75. package/out/internal/network/claims.d.ts +1 -0
  76. package/out/internal/network/claims.luau +146 -0
  77. package/out/internal/network/client-session.d.ts +1 -0
  78. package/out/internal/network/client-session.luau +405 -0
  79. package/out/internal/network/codec.d.ts +63 -0
  80. package/out/internal/network/codec.luau +1183 -0
  81. package/out/internal/network/envelope.d.ts +1 -0
  82. package/out/internal/network/envelope.luau +247 -0
  83. package/out/internal/network/limits.d.ts +26 -0
  84. package/out/internal/network/limits.luau +55 -0
  85. package/out/internal/network/port.d.ts +1 -0
  86. package/out/internal/network/port.luau +19 -0
  87. package/out/internal/network/roblox-network-port.d.ts +1 -0
  88. package/out/internal/network/roblox-network-port.luau +105 -0
  89. package/out/internal/network/schema.d.ts +72 -0
  90. package/out/internal/network/schema.luau +386 -0
  91. package/out/internal/network/server-session.d.ts +1 -0
  92. package/out/internal/network/server-session.luau +395 -0
  93. package/out/internal/network/session.d.ts +1 -0
  94. package/out/internal/network/session.luau +266 -0
  95. package/out/internal/reactive/context.d.ts +1 -0
  96. package/out/internal/reactive/context.luau +149 -0
  97. package/out/internal/reactive/flush.d.ts +1 -0
  98. package/out/internal/reactive/flush.luau +355 -0
  99. package/out/internal/reactive/graph.d.ts +1 -0
  100. package/out/internal/reactive/graph.luau +626 -0
  101. package/out/internal/reactive/limits.d.ts +1 -0
  102. package/out/internal/reactive/limits.luau +54 -0
  103. package/out/internal/reactive/ownership.d.ts +1 -0
  104. package/out/internal/reactive/ownership.luau +626 -0
  105. package/out/internal/registry.luau +10 -1
  106. package/out/internal/sync/client-sync.d.ts +1 -0
  107. package/out/internal/sync/client-sync.luau +157 -0
  108. package/out/internal/sync/declarations.d.ts +1 -0
  109. package/out/internal/sync/declarations.luau +276 -0
  110. package/out/internal/sync/frame.d.ts +1 -0
  111. package/out/internal/sync/frame.luau +91 -0
  112. package/out/internal/sync/server-sync.d.ts +1 -0
  113. package/out/internal/sync/server-sync.luau +502 -0
  114. package/out/internal/sync/values.d.ts +1 -0
  115. package/out/internal/sync/values.luau +930 -0
  116. package/out/maths/spline/curve.d.ts +35 -0
  117. package/out/maths/spline/curve.luau +1122 -0
  118. package/out/maths/spline/index.d.ts +2 -0
  119. package/out/maths/spline/init.luau +8 -0
  120. package/out/network.d.ts +66 -0
  121. package/out/network.luau +24 -0
  122. package/out/reactive.d.ts +76 -0
  123. package/out/reactive.luau +136 -0
  124. package/out/replicated.d.ts +38 -0
  125. package/out/replicated.luau +45 -0
  126. package/out/server/data.d.ts +54 -0
  127. package/out/server/data.luau +44 -0
  128. package/out/server/index.d.ts +17 -0
  129. package/out/server/init.luau +58 -4
  130. package/out/server/publish.d.ts +20 -0
  131. package/out/server/publish.luau +24 -0
  132. package/out/testing/index.d.ts +38 -0
  133. package/out/testing/init.luau +189 -0
  134. package/out/util/random/index.d.ts +24 -0
  135. package/out/util/random/init.luau +301 -0
  136. package/out/vide/index.d.ts +7 -0
  137. package/out/vide/init.luau +50 -0
  138. package/package.json +28 -2
  139. package/testing/package.json +4 -0
  140. package/transformer/data.cjs +72 -0
  141. package/transformer/declarations.cjs +153 -0
  142. package/transformer/index.js +87 -23
  143. package/transformer/network.cjs +961 -0
  144. package/util/random/package.json +4 -0
  145. package/vide/package.json +4 -0
package/out/index.d.ts CHANGED
@@ -10,3 +10,11 @@ export interface BootOptions {
10
10
  }
11
11
  export { Logging, LogLevel } from "./logging";
12
12
  export type { Logger, LogRecord, LogSink, SinkSubscription, LogContext, LogProperties } from "./logging";
13
+ export { state, computed, effect, watch, untracked, scope, onCleanup, own } from "./reactive";
14
+ export type { Readable, State, Ownable } from "./reactive";
15
+ export { replicated, replicatedPerPlayer, syncStatus } from "./replicated";
16
+ export type { Replicated, ReplicatedPerPlayer, SyncStatus } from "./replicated";
17
+ export { toServer, toClient, askServer, receive, ClientReady } from "./network";
18
+ export type { ToServer, ToClient, AskServer, Receiver, ReceiveTarget, ReceiveCallback, AskResult, AskFailure, AskOptions, PayloadArgs, f32, f64, u8, u16, u32, } from "./network";
19
+ export { profileStore, serverOnly, replicatedPaths } from "./data";
20
+ export type { ProfileStore, ProfileOptions, ServerOnly, ProfileData, ClientData, Profile, ClientProfile, DataHandle, ReadHandle, WriteHandle, ArrayWrites, ReservedName, Json, JsonRecord, Migration, } from "./data";
package/out/init.luau CHANGED
@@ -8,4 +8,27 @@ exports.CrucibleBehaviour = TS.import(script, script, "crucible-behaviour").Cruc
8
8
  local _logging = TS.import(script, script, "logging")
9
9
  exports.Logging = _logging.Logging
10
10
  exports.LogLevel = _logging.LogLevel
11
+ local _reactive = TS.import(script, script, "reactive")
12
+ exports.state = _reactive.state
13
+ exports.computed = _reactive.computed
14
+ exports.effect = _reactive.effect
15
+ exports.watch = _reactive.watch
16
+ exports.untracked = _reactive.untracked
17
+ exports.scope = _reactive.scope
18
+ exports.onCleanup = _reactive.onCleanup
19
+ exports.own = _reactive.own
20
+ local _replicated = TS.import(script, script, "replicated")
21
+ exports.replicated = _replicated.replicated
22
+ exports.replicatedPerPlayer = _replicated.replicatedPerPlayer
23
+ exports.syncStatus = _replicated.syncStatus
24
+ local _network = TS.import(script, script, "network")
25
+ exports.toServer = _network.toServer
26
+ exports.toClient = _network.toClient
27
+ exports.askServer = _network.askServer
28
+ exports.receive = _network.receive
29
+ exports.ClientReady = _network.ClientReady
30
+ local _data = TS.import(script, script, "data")
31
+ exports.profileStore = _data.profileStore
32
+ exports.serverOnly = _data.serverOnly
33
+ exports.replicatedPaths = _data.replicatedPaths
11
34
  return exports
@@ -27,6 +27,10 @@ local scopeTarget
27
27
  local function port()
28
28
  return installed or error("Crucible binding used before boot")
29
29
  end
30
+ --* @internal The port boot installed, for framework Subsystems that connect engine events.
31
+ local function enginePort()
32
+ return port()
33
+ end
30
34
  --* @internal
31
35
  local function useEngine(engine)
32
36
  installed = engine
@@ -597,6 +601,7 @@ local function forget(state)
597
601
  end
598
602
  end
599
603
  return {
604
+ enginePort = enginePort,
600
605
  useEngine = useEngine,
601
606
  defaultAccepts = defaultAccepts,
602
607
  construct = construct,
@@ -26,6 +26,16 @@ local records = _registry.records
26
26
  local seal = _registry.seal
27
27
  local robloxPort = TS.import(script, script.Parent, "roblox-port").robloxPort
28
28
  local Scheduler = TS.import(script, script.Parent, "scheduler").Scheduler
29
+ local _flush = TS.import(script, script.Parent, "reactive", "flush")
30
+ local holdBarrier = _flush.holdBarrier
31
+ local releaseBarrier = _flush.releaseBarrier
32
+ local useReactivePort = _flush.useReactivePort
33
+ local endInterval = TS.import(script, script.Parent, "reactive", "ownership").endInterval
34
+ local _declarations = TS.import(script, script.Parent, "sync", "declarations")
35
+ local rollbackDeclarations = _declarations.rollbackDeclarations
36
+ local sealDeclarations = _declarations.sealDeclarations
37
+ local Logging = TS.import(script, script.Parent.Parent, "logging").Logging
38
+ local log = Logging:getLogger("Crucible.Boot")
29
39
  --* @internal
30
40
  local BOOT_CLASSES = { Subsystem, CrucibleBehaviour }
31
41
  --* @internal
@@ -35,6 +45,45 @@ local PATTERN_PREFIX = "crucible-path:"
35
45
  local SHUTDOWN_DEADLINE_SECONDS = 25
36
46
  --* @internal
37
47
  local SHUTDOWN_TIMEOUT_SECONDS = 28
48
+ local clock
49
+ local sweep
50
+ --* @internal
51
+ --* @internal The one monotonic clock, core's and data's (data rules 25, 105).
52
+ local function bootClock()
53
+ return (clock or os.clock)()
54
+ end
55
+ --* @internal Undefined until the sweep has begun.
56
+ local function shutdownProgress(subsystem)
57
+ if sweep == nil then
58
+ return nil
59
+ end
60
+ local pending = 0
61
+ local notStarted = 0
62
+ for _, record in sweep.order do
63
+ local _condition = sweep.finished[record] ~= nil
64
+ if not _condition then
65
+ local _result = record.state
66
+ if _result ~= nil then
67
+ _result = _result.object
68
+ end
69
+ _condition = _result == subsystem
70
+ end
71
+ if _condition then
72
+ continue
73
+ end
74
+ pending += 1
75
+ if not (sweep.started[record] ~= nil) then
76
+ notStarted += 1
77
+ end
78
+ end
79
+ return {
80
+ startedAt = sweep.startTime,
81
+ forceStartAt = sweep.startTime + SHUTDOWN_DEADLINE_SECONDS,
82
+ deadline = sweep.startTime + SHUTDOWN_TIMEOUT_SECONDS,
83
+ pending = pending,
84
+ notStarted = notStarted,
85
+ }
86
+ end
38
87
  --* @internal
39
88
  local function parsePattern(pattern)
40
89
  local _pattern = pattern
@@ -214,6 +263,12 @@ local function shutdown(port, order)
214
263
  end
215
264
  end
216
265
  local startTime = port.clock()
266
+ sweep = {
267
+ startTime = startTime,
268
+ order = order,
269
+ started = started,
270
+ finished = finished,
271
+ }
217
272
  while true do
218
273
  -- ▼ ReadonlySet.size ▼
219
274
  local _size = 0
@@ -287,6 +342,8 @@ local function shutdown(port, order)
287
342
  end
288
343
  --* @internal
289
344
  local function ignite(port, side, options)
345
+ clock = port.clock
346
+ sweep = nil
290
347
  local singletons = seal({
291
348
  renders = side.renders,
292
349
  })
@@ -330,6 +387,8 @@ local function ignite(port, side, options)
330
387
  end
331
388
  end
332
389
  useEngine(port)
390
+ holdBarrier()
391
+ useReactivePort(port)
333
392
  useImplementerPort(port)
334
393
  useEffects({
335
394
  construct = function(s)
@@ -391,6 +450,7 @@ local function ignite(port, side, options)
391
450
  diagnose = function(s, hook, err)
392
451
  return runtimeDiagnostic("error", `{where(port, s)} {HOOK_NAMES[hook + 1]} failed: {tostring(err)}`)
393
452
  end,
453
+ disabled = endInterval,
394
454
  })
395
455
  local ok, err = pcall(function()
396
456
  local _exp_1 = records()
@@ -427,6 +487,36 @@ local function ignite(port, side, options)
427
487
  if not ok then
428
488
  reject(tostring(err))
429
489
  end
490
+ if failure == nil then
491
+ local unbooted = nil
492
+ local finalized, finalizeErr = pcall(function()
493
+ -- Roblox cannot unbind BindToClose, so the callback checks that this boot committed.
494
+ if side.name == "server" then
495
+ port.bindToClose(function()
496
+ if stage ~= "committed" then
497
+ return nil
498
+ end
499
+ shutdown(port, singletons)
500
+ side.network:close()
501
+ end)
502
+ end
503
+ unbooted = side.booted()
504
+ if side.name == "client" then
505
+ sealDeclarations()
506
+ end
507
+ side.network:commit()
508
+ end)
509
+ if not finalized then
510
+ local undo = unbooted
511
+ if undo ~= nil then
512
+ local undone, undoErr = pcall(undo)
513
+ if not undone then
514
+ runtimeDiagnostic("error", `Crucible: undoing booted failed: {tostring(undoErr)}`)
515
+ end
516
+ end
517
+ reject(`boot finalization failed: {tostring(finalizeErr)}`)
518
+ end
519
+ end
430
520
  if failure ~= nil then
431
521
  for s in owned do
432
522
  if not hasEnded(s) then
@@ -435,23 +525,37 @@ local function ignite(port, side, options)
435
525
  end
436
526
  drain()
437
527
  table.clear(owned)
528
+ releaseBarrier()
438
529
  error(`Crucible boot failed: {failure}`, 0)
439
530
  end
440
531
  stage = "committed"
441
532
  table.clear(owned)
442
- if side.name == "server" then
443
- port.bindToClose(function()
444
- return shutdown(port, singletons)
445
- end)
446
- end
447
- side.booted()
533
+ releaseBarrier()
448
534
  end
449
535
  --* @internal
450
536
  local function boot(side, options)
451
- load(options.load)
452
- ignite(robloxPort(), side, options)
537
+ local ok, err = pcall(function()
538
+ side.network:open()
539
+ load(options.load)
540
+ side.network:loaded()
541
+ if side.name == "server" then
542
+ sealDeclarations()
543
+ end
544
+ ignite(robloxPort(), side, options)
545
+ end)
546
+ if ok then
547
+ log:debug("Crucible booted on the {side}", {
548
+ side = side.name,
549
+ })
550
+ return nil
551
+ end
552
+ side.network:close()
553
+ rollbackDeclarations()
554
+ error(err, 0)
453
555
  end
454
556
  return {
557
+ bootClock = bootClock,
558
+ shutdownProgress = shutdownProgress,
455
559
  parsePattern = parsePattern,
456
560
  load = load,
457
561
  ignite = ignite,
@@ -0,0 +1,10 @@
1
+ export declare const MAX_CONFIG_DECLARATIONS = 1000;
2
+ export declare const CONFIG_NAME_MAX_BYTES = 64;
3
+ export declare const CONFIG_VALUE_MAX_BYTES = 100000;
4
+ export declare const CONFIG_ADMISSION_VISITS = 100000;
5
+ export declare const CONFIG_REQUEST_CONCURRENCY = 4;
6
+ export declare const CONFIG_FETCH_RETRY_BASE_SECONDS = 1;
7
+ export declare const CONFIG_FETCH_RETRY_CAP_SECONDS = 30;
8
+ export declare const CONFIG_REFRESH_TIMEOUT_SECONDS = 10;
9
+ export declare const CONFIG_VALUE_DEPTH = 28;
10
+ export declare const CONFIG_STRING_MAX_BYTES = 65536;
@@ -0,0 +1,25 @@
1
+ -- Compiled with roblox-ts v3.0.0
2
+ local MAX_CONFIG_DECLARATIONS = 1_000
3
+ local CONFIG_NAME_MAX_BYTES = 64
4
+ local CONFIG_VALUE_MAX_BYTES = 100_000
5
+ local CONFIG_ADMISSION_VISITS = 100_000
6
+ local CONFIG_REQUEST_CONCURRENCY = 4
7
+ local CONFIG_FETCH_RETRY_BASE_SECONDS = 1
8
+ local CONFIG_FETCH_RETRY_CAP_SECONDS = 30
9
+ local CONFIG_REFRESH_TIMEOUT_SECONDS = 10
10
+ -- Rule 97 admits configs by rule 76's walk, whose depth and string bounds are data's PERSIST_DEPTH and
11
+ -- PERSIST_STRING_BYTES. Data's limits module is not under configs yet, so the values are restated here.
12
+ local CONFIG_VALUE_DEPTH = 28
13
+ local CONFIG_STRING_MAX_BYTES = 65_536
14
+ return {
15
+ MAX_CONFIG_DECLARATIONS = MAX_CONFIG_DECLARATIONS,
16
+ CONFIG_NAME_MAX_BYTES = CONFIG_NAME_MAX_BYTES,
17
+ CONFIG_VALUE_MAX_BYTES = CONFIG_VALUE_MAX_BYTES,
18
+ CONFIG_ADMISSION_VISITS = CONFIG_ADMISSION_VISITS,
19
+ CONFIG_REQUEST_CONCURRENCY = CONFIG_REQUEST_CONCURRENCY,
20
+ CONFIG_FETCH_RETRY_BASE_SECONDS = CONFIG_FETCH_RETRY_BASE_SECONDS,
21
+ CONFIG_FETCH_RETRY_CAP_SECONDS = CONFIG_FETCH_RETRY_CAP_SECONDS,
22
+ CONFIG_REFRESH_TIMEOUT_SECONDS = CONFIG_REFRESH_TIMEOUT_SECONDS,
23
+ CONFIG_VALUE_DEPTH = CONFIG_VALUE_DEPTH,
24
+ CONFIG_STRING_MAX_BYTES = CONFIG_STRING_MAX_BYTES,
25
+ }
@@ -0,0 +1,29 @@
1
+ import type { Disconnect } from "../port";
2
+ export interface ConfigSnapshotPort {
3
+ /** `GetValue`: nil for a missing key. A JSON config may arrive as a table or as its text. */
4
+ readonly get: (key: string) => unknown;
5
+ /** `Refresh`. Yields and may throw. */
6
+ readonly refresh: () => void;
7
+ /** Unmeasured in Studio: whether this turns true without `UpdateAvailable`. Runtime treats the two as independent. */
8
+ readonly outdated: () => boolean;
9
+ /** `Error` is `LoadFailed`. */
10
+ readonly failed: () => boolean;
11
+ readonly onUpdateAvailable: (listener: () => void) => Disconnect;
12
+ }
13
+ /** What configs ask of the engine. Requests yield and may throw. */
14
+ export interface ConfigPort {
15
+ readonly global: () => ConfigSnapshotPort;
16
+ readonly forPlayer: (player: Player) => ConfigSnapshotPort;
17
+ readonly players: () => readonly Player[];
18
+ readonly onPlayerAdded: (listener: (player: Player) => void) => Disconnect;
19
+ readonly onPlayerRemoving: (listener: (player: Player) => void) => Disconnect;
20
+ readonly wait: (seconds: number) => void;
21
+ /** Uniform in [0, 1). */
22
+ readonly random: () => number;
23
+ readonly decodeJson: (text: string) => unknown;
24
+ /** The JSON text length of a decoded value. Throws for a value JSON cannot hold. */
25
+ readonly jsonBytes: (value: object) => number;
26
+ }
27
+ export declare function installConfigPort(value: ConfigPort): void;
28
+ export declare function configPort(): ConfigPort;
29
+ export declare function createRobloxConfigPort(): ConfigPort;
@@ -0,0 +1,99 @@
1
+ -- Compiled with roblox-ts v3.0.0
2
+ --* What configs ask of the engine. Requests yield and may throw.
3
+ local port
4
+ local function installConfigPort(value)
5
+ local _arg0 = port == nil
6
+ assert(_arg0, "Config port already selected")
7
+ port = value
8
+ end
9
+ local createRobloxConfigPort
10
+ local function configPort()
11
+ if port == nil then
12
+ port = createRobloxConfigPort()
13
+ end
14
+ return port
15
+ end
16
+ local EMPTY = table.freeze({
17
+ get = function()
18
+ return nil
19
+ end,
20
+ refresh = function() end,
21
+ outdated = function()
22
+ return false
23
+ end,
24
+ failed = function()
25
+ return false
26
+ end,
27
+ onUpdateAvailable = function()
28
+ return function() end
29
+ end,
30
+ })
31
+ local function snapshotPort(snapshot)
32
+ return {
33
+ get = function(key)
34
+ return snapshot:GetValue(key)
35
+ end,
36
+ refresh = function()
37
+ return snapshot:Refresh()
38
+ end,
39
+ outdated = function()
40
+ return snapshot.Outdated
41
+ end,
42
+ failed = function()
43
+ return snapshot.Error == Enum.ConfigSnapshotErrorState.LoadFailed
44
+ end,
45
+ onUpdateAvailable = function(listener)
46
+ local connection = snapshot.UpdateAvailable:Connect(listener)
47
+ return function()
48
+ return connection:Disconnect()
49
+ end
50
+ end,
51
+ }
52
+ end
53
+ function createRobloxConfigPort()
54
+ local players = game:GetService("Players")
55
+ local http = game:GetService("HttpService")
56
+ local live = not game:GetService("RunService"):IsStudio() or game:GetService("ServerStorage"):GetAttribute("CrucibleLiveData") == true
57
+ local service = if live then game:GetService("ConfigService") else nil
58
+ local random = Random.new()
59
+ return {
60
+ global = function()
61
+ return if service == nil then EMPTY else snapshotPort(service:GetConfigAsync())
62
+ end,
63
+ forPlayer = function(player)
64
+ return if service == nil then EMPTY else snapshotPort(service:GetConfigForPlayerAsync(player))
65
+ end,
66
+ players = function()
67
+ return players:GetPlayers()
68
+ end,
69
+ onPlayerAdded = function(listener)
70
+ local connection = players.PlayerAdded:Connect(listener)
71
+ return function()
72
+ return connection:Disconnect()
73
+ end
74
+ end,
75
+ onPlayerRemoving = function(listener)
76
+ local connection = players.PlayerRemoving:Connect(listener)
77
+ return function()
78
+ return connection:Disconnect()
79
+ end
80
+ end,
81
+ wait = function(seconds)
82
+ task.wait(seconds)
83
+ end,
84
+ random = function()
85
+ return random:NextNumber()
86
+ end,
87
+ decodeJson = function(text)
88
+ return http:JSONDecode(text)
89
+ end,
90
+ jsonBytes = function(value)
91
+ return #http:JSONEncode(value)
92
+ end,
93
+ }
94
+ end
95
+ return {
96
+ installConfigPort = installConfigPort,
97
+ configPort = configPort,
98
+ createRobloxConfigPort = createRobloxConfigPort,
99
+ }
@@ -0,0 +1,21 @@
1
+ import { type Node } from "../network/schema";
2
+ import { type StateNode } from "../reactive/graph";
3
+ export type ConfigKind = "global" | "player";
4
+ /** One value a reader sees. Commit is its only writer. */
5
+ /** A config value as reactive state, so a read is tracked (rule 93) and a commit wakes its readers once. */
6
+ type Cell = StateNode;
7
+ export interface Declaration {
8
+ readonly name: string;
9
+ readonly kind: ConfigKind;
10
+ readonly node: Node;
11
+ readonly fallback: unknown;
12
+ /** The value admitted from the global snapshot. */
13
+ readonly global: Cell;
14
+ }
15
+ export declare function declare(name: string, kind: ConfigKind, fallback: unknown, node: Node): Declaration;
16
+ export declare function readGlobal(declaration: Declaration): unknown;
17
+ export declare function readPlayer(declaration: Declaration, player: Player): unknown;
18
+ export declare function ready(player?: Player): boolean;
19
+ export declare function refresh(): boolean;
20
+ export declare function installConfigRuntime(): void;
21
+ export {};