@typetorch/framework 0.5.2 → 0.5.3

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.
@@ -65,6 +65,7 @@ First-ever-session detail, one row per packed chunk.
65
65
  | tech | `load` (src client) | `secs`: from the client starting to the engine starting (first client generation only) |
66
66
  | tech | `error` | Server or client (src). `msg` (first 300 chars, player names replaced by `<player>`), `n` (times in the window). At most 10 per tech tick (server), 5 per 30 s and 100 per session (client) |
67
67
  | tech | `start` | First generation of a server that runs the engine: `gen`, `kernel`, `load?` |
68
+ | tech | `assets` | Each generation whose artifact has hot assets (or an unusable manifest), at its start: `gen`, `boot` (the server's first generation), `manifest` (`ok`, `invalid`, `unread`), `place?` (`same`: the manifest names this place version; `vouched`: the place's backup vouches for it; `differs`: keys without a matching baked copy load from their uploads; `unknown`), `mpv?` (the manifest's place version), `pv?` (game.PlaceVersion), `keys`, `kept`, `baked`, `loaded`, `failed`, `loading` (keys per AssetSync source), `missing` (keys with nothing live), `late` (loaded after the start-up wait), `timedOut`, `ms?`, `first?` (the first failure or manifest error) |
68
69
  | tech | `swap` / `rollback` | A generation started by a hot swap (`rollback` for rollback reasons), 5 s after it started: `reason`, `from?` (previous artifact id), `gen`, `load?`, `stop?`, `swap?` (seconds), `players` |
69
70
  | tech | `swap_out` | Just before a generation stops: `reason`, `next?` (next artifact id), `players` |
70
71
  | zone | `enter` | `zone`, `from` (previous zone or `""`) |
@@ -19,6 +19,8 @@ local _typetorch = TS.import(script, script.Parent.Parent, "typetorch")
19
19
  local closeHooksSupported = _typetorch.closeHooksSupported
20
20
  local onGenerationClose = _typetorch.onGenerationClose
21
21
  local TypeTorch = _typetorch.TypeTorch
22
+ local assetCounts = TS.import(script, script.Parent.Parent, "assets", "manifest").assetCounts
23
+ local assetReport = TS.import(script, script.Parent.Parent, "assets", "sync").assetReport
22
24
  local toBase64 = TS.import(script, script.Parent, "codec").toBase64
23
25
  local _props = TS.import(script, script.Parent, "props")
24
26
  local encodeProps = _props.encodeProps
@@ -495,7 +497,7 @@ do
495
497
  end
496
498
  function ServerAnalytics:track(player, kind, name, props)
497
499
  if not isEventName(name) then
498
- warn("[src/analytics/server.ts:471]", `[analytics] {kind} event name must be 1-64 characters: {string.sub(tostring(name), 1, 80)}`)
500
+ warn("[src/analytics/server.ts:473]", `[analytics] {kind} event name must be 1-64 characters: {string.sub(tostring(name), 1, 80)}`)
499
501
  return nil
500
502
  end
501
503
  local session = if player then self:sessionOf(player) else nil
@@ -603,7 +605,7 @@ do
603
605
  end
604
606
  local control = _condition
605
607
  if not isExperimentName(name) or not isVariantList(variants) then
606
- warn("[src/analytics/server.ts:523]", `[analytics] experiment("{string.sub(tostring(name), 1, 64)}"): bad name or variants (1-16 distinct names)`)
608
+ warn("[src/analytics/server.ts:525]", `[analytics] experiment("{string.sub(tostring(name), 1, 64)}"): bad name or variants (1-16 distinct names)`)
607
609
  return control
608
610
  end
609
611
  local session = self:sessionOf(player)
@@ -668,7 +670,7 @@ do
668
670
  function ServerAnalytics:applySettings(parsed)
669
671
  local errorsText = table.concat(parsed.errors, "; ")
670
672
  if errorsText ~= table.concat(self.settingsErrors, "; ") and errorsText ~= "" then
671
- warn("[src/analytics/server.ts:558]", `[analytics] the sink settings (settings.backend or settings.analytics): {errorsText}`)
673
+ warn("[src/analytics/server.ts:560]", `[analytics] the sink settings (settings.backend or settings.analytics): {errorsText}`)
672
674
  end
673
675
  self.settingsErrors = parsed.errors
674
676
  local hadSettings = self.settings ~= nil
@@ -698,7 +700,7 @@ do
698
700
  -- `new AnalyticsEngine({ settings })`.
699
701
  if not settingsSupported() then
700
702
  if self.kernel ~= nil and self.kernel.test ~= true then
701
- warn("[src/analytics/server.ts:585]", `[analytics] no sink settings: they come from the signed settings record (kernel 0.3.8+, "typetorch backend setup"); this server runs kernel {self.kernel.kernelVersion}. Or pass new AnalyticsEngine(\{ settings \})`)
703
+ warn("[src/analytics/server.ts:587]", `[analytics] no sink settings: they come from the signed settings record (kernel 0.3.8+, "typetorch backend setup"); this server runs kernel {self.kernel.kernelVersion}. Or pass new AnalyticsEngine(\{ settings \})`)
702
704
  end
703
705
  self.settingsTried = true
704
706
  return nil
@@ -1227,6 +1229,20 @@ do
1227
1229
  load = start.loadSeconds,
1228
1230
  }))
1229
1231
  end
1232
+ -- Hot assets: how this generation's AssetSync went (it ran before the modules loaded), so "every new server
1233
+ -- loads its hot assets from uploads" shows in the events table (2026-10-13 incident).
1234
+ local assets = assetReport()
1235
+ if assets.manifest ~= "none" or #assets.errors > 0 then
1236
+ local _self = self
1237
+ local _object = {
1238
+ gen = generation,
1239
+ boot = start.kind ~= "swap",
1240
+ }
1241
+ for _k, _v in assetCounts(assets) do
1242
+ _object[_k] = _v
1243
+ end
1244
+ _self:emit(nil, "tech", "assets", encodeProps(_object))
1245
+ end
1230
1246
  end
1231
1247
  self.trove:add(TypeTorch:onSwapOut(function(info)
1232
1248
  local _self = self
@@ -1500,7 +1516,7 @@ do
1500
1516
  end
1501
1517
  end)
1502
1518
  if not ok then
1503
- warn("[src/analytics/server.ts:1078]", `[analytics] client message failed: {string.sub(tostring(err), 1, 200)}`)
1519
+ warn("[src/analytics/server.ts:1086]", `[analytics] client message failed: {string.sub(tostring(err), 1, 200)}`)
1504
1520
  end
1505
1521
  end)
1506
1522
  self.trove:add(function()
@@ -1783,7 +1799,7 @@ do
1783
1799
  self.lastWarnAt = now
1784
1800
  local more = if self.suppressed > 0 then ` (+{self.suppressed} more in the last minute)` else ""
1785
1801
  self.suppressed = 0
1786
- warn("[src/analytics/server.ts:1273]", `[analytics] {message}{more}`)
1802
+ warn("[src/analytics/server.ts:1281]", `[analytics] {message}{more}`)
1787
1803
  return true
1788
1804
  end
1789
1805
  function ServerAnalytics:noteSuccess()
@@ -23,6 +23,14 @@ export declare const ATTR_VERSION = "TypeTorchAssetVersion";
23
23
  * a template are tagged too: only an instance whose Parent is still this path counts as the live one.
24
24
  */
25
25
  export declare const ATTR_PATH = "TypeTorchAssetPath";
26
+ /**
27
+ * The place's backup build (ServerStorage.TypeTorchBackup) vouches for the place's hot-asset copies after the CLI's
28
+ * backup refresh published the place (cli commands/backup.ts, kernelpatch-task step 4b): `HotAssetsPlaceVersion` is the
29
+ * lockfile placeVersion the copies match, `HotAssetsSavedAs` the place version that save became.
30
+ */
31
+ export declare const BACKUP_NAME = "TypeTorchBackup";
32
+ export declare const ATTR_BACKUP_PLACE_VERSION = "HotAssetsPlaceVersion";
33
+ export declare const ATTR_BACKUP_SAVED_AS = "HotAssetsSavedAs";
26
34
  export declare const MAX_KEY_LENGTH = 64;
27
35
  export type AssetRealm = "replicated" | "server";
28
36
  /** One manifest entry (one key), validated. */
@@ -95,6 +103,20 @@ export interface AssetDecision {
95
103
  */
96
104
  fallbackBaked?: number;
97
105
  }
106
+ /**
107
+ * Whether this server's place holds exactly the copies the manifest was exported from:
108
+ * - same: the manifest names this place version;
109
+ * - vouched: it names another one, but the place's backup says this version is a save of that one that changed nothing
110
+ * outside the kernel slots (`backup` = its HotAssetsPlaceVersion / HotAssetsSavedAs);
111
+ * - differs: neither (new servers load every key whose baked copy has no matching hash);
112
+ * - unknown: the manifest names no place version.
113
+ */
114
+ export type PlaceMatch = "same" | "vouched" | "differs" | "unknown";
115
+ export interface BackupVouch {
116
+ placeVersion?: number;
117
+ savedAs?: number;
118
+ }
119
+ export declare function placeMatchesManifest(manifestVersion: number | undefined, placeVersion: number, backup?: BackupVouch): PlaceMatch;
98
120
  /**
99
121
  * What AssetSync does for one key:
100
122
  * 1. keep the tagged copy whose TypeTorchAssetVersion is the manifest's `ver` (the newest one if several);
@@ -128,6 +150,11 @@ export interface AssetSyncReport {
128
150
  manifest: "none" | "ok" | "invalid" | "unread";
129
151
  /** Where the manifest was found: "mount", "Server" (the payload's Server folder) or "payload" (re-read). */
130
152
  from?: string;
153
+ /** placeMatchesManifest: whether the place's copies are the manifest's ("differs": the upload path). */
154
+ place?: PlaceMatch;
155
+ /** The manifest's placeVersion and this server's game.PlaceVersion. */
156
+ manifestPlaceVersion?: number;
157
+ placeVersion?: number;
131
158
  errors: string[];
132
159
  /** AssetSync is still holding the generation's start. */
133
160
  running: boolean;
@@ -138,6 +165,8 @@ export interface AssetSyncReport {
138
165
  /** Tagged keys this manifest doesn't name: left as they are. */
139
166
  unmanaged: string[];
140
167
  }
168
+ /** Keys per source, and the rest of what the `tech` / `assets` analytics row carries (SCHEMA.md). */
169
+ export declare function assetCounts(report: AssetSyncReport): Record<string, unknown>;
141
170
  /** What Server > Status needs (devtools ServerFacts.assets). */
142
171
  export interface AssetFacts {
143
172
  failed: number;
@@ -28,6 +28,16 @@ local ATTR_VERSION = "TypeTorchAssetVersion"
28
28
 
29
29
  ]]
30
30
  local ATTR_PATH = "TypeTorchAssetPath"
31
+ --[[
32
+ *
33
+ * The place's backup build (ServerStorage.TypeTorchBackup) vouches for the place's hot-asset copies after the CLI's
34
+ * backup refresh published the place (cli commands/backup.ts, kernelpatch-task step 4b): `HotAssetsPlaceVersion` is the
35
+ * lockfile placeVersion the copies match, `HotAssetsSavedAs` the place version that save became.
36
+
37
+ ]]
38
+ local BACKUP_NAME = "TypeTorchBackup"
39
+ local ATTR_BACKUP_PLACE_VERSION = "HotAssetsPlaceVersion"
40
+ local ATTR_BACKUP_SAVED_AS = "HotAssetsSavedAs"
31
41
  local MAX_KEY_LENGTH = 64
32
42
  local MAX_ASSETS = 1000
33
43
  local MAX_ERRORS = 20
@@ -255,6 +265,28 @@ end
255
265
  -- The decision --------------------------------------------------------------------------------------------------------
256
266
  --* A live (tagged) copy of the key: its TypeTorchAssetVersion. Only copies whose Parent is their own path count.
257
267
  --* An untagged copy of the key under the entry's path (the place's baked copy): its TypeTorchAssetHash.
268
+ --[[
269
+ *
270
+ * Whether this server's place holds exactly the copies the manifest was exported from:
271
+ * - same: the manifest names this place version;
272
+ * - vouched: it names another one, but the place's backup says this version is a save of that one that changed nothing
273
+ * outside the kernel slots (`backup` = its HotAssetsPlaceVersion / HotAssetsSavedAs);
274
+ * - differs: neither (new servers load every key whose baked copy has no matching hash);
275
+ * - unknown: the manifest names no place version.
276
+
277
+ ]]
278
+ local function placeMatchesManifest(manifestVersion, placeVersion, backup)
279
+ if manifestVersion == nil then
280
+ return "unknown"
281
+ end
282
+ if manifestVersion == placeVersion then
283
+ return "same"
284
+ end
285
+ if backup ~= nil and backup.placeVersion == manifestVersion and backup.savedAs == placeVersion then
286
+ return "vouched"
287
+ end
288
+ return "differs"
289
+ end
258
290
  local function indexes(count, except)
259
291
  local list = {}
260
292
  do
@@ -328,6 +360,61 @@ local function decideAsset(entry, tagged, baked, placeMatches)
328
360
  end
329
361
  -- Status (devtools op "assets", Server > Status) ------------------------------------------------------------------------
330
362
  --* kept: already live with the manifest version; baked: the place's copy adopted; loaded; failed (see `error`).
363
+ --* Keys per source, and the rest of what the `tech` / `assets` analytics row carries (SCHEMA.md).
364
+ local function assetCounts(report)
365
+ local counts = {
366
+ kept = 0,
367
+ baked = 0,
368
+ loaded = 0,
369
+ failed = 0,
370
+ loading = 0,
371
+ }
372
+ local missing = 0
373
+ local late = 0
374
+ local first
375
+ for _, status in report.entries do
376
+ local _condition = counts[status.source]
377
+ if _condition == nil then
378
+ _condition = 0
379
+ end
380
+ counts[status.source] = _condition + 1
381
+ if not status.live then
382
+ missing += 1
383
+ end
384
+ if status.late == true then
385
+ late += 1
386
+ end
387
+ if status.source == "failed" and first == nil then
388
+ local _exp = status.key
389
+ local _condition_1 = status.error
390
+ if _condition_1 == nil then
391
+ _condition_1 = "failed"
392
+ end
393
+ first = `{_exp}: {_condition_1}`
394
+ end
395
+ end
396
+ local _object = {
397
+ manifest = report.manifest,
398
+ place = report.place,
399
+ mpv = report.manifestPlaceVersion,
400
+ pv = report.placeVersion,
401
+ keys = #report.entries,
402
+ }
403
+ for _k, _v in counts do
404
+ _object[_k] = _v
405
+ end
406
+ _object.missing = missing
407
+ _object.late = late
408
+ _object.timedOut = report.timedOut == true
409
+ _object.ms = report.ms
410
+ local _left = "first"
411
+ local _condition = first
412
+ if _condition == nil then
413
+ _condition = report.errors[1]
414
+ end
415
+ _object[_left] = _condition
416
+ return _object
417
+ end
331
418
  --* What Server > Status needs (devtools ServerFacts.assets).
332
419
  local function summarizeAssets(report)
333
420
  if report.manifest == "none" and #report.errors == 0 then
@@ -368,7 +455,9 @@ return {
368
455
  pathSegments = pathSegments,
369
456
  realmOfPath = realmOfPath,
370
457
  parseManifest = parseManifest,
458
+ placeMatchesManifest = placeMatchesManifest,
371
459
  decideAsset = decideAsset,
460
+ assetCounts = assetCounts,
372
461
  summarizeAssets = summarizeAssets,
373
462
  ASSET_TAG_PREFIX = ASSET_TAG_PREFIX,
374
463
  ATTR_KEY = ATTR_KEY,
@@ -376,5 +465,8 @@ return {
376
465
  ATTR_HASH = ATTR_HASH,
377
466
  ATTR_VERSION = ATTR_VERSION,
378
467
  ATTR_PATH = ATTR_PATH,
468
+ BACKUP_NAME = BACKUP_NAME,
469
+ ATTR_BACKUP_PLACE_VERSION = ATTR_BACKUP_PLACE_VERSION,
470
+ ATTR_BACKUP_SAVED_AS = ATTR_BACKUP_SAVED_AS,
379
471
  MAX_KEY_LENGTH = MAX_KEY_LENGTH,
380
472
  }
@@ -4,6 +4,7 @@ local _services = TS.import(script, TS.getModule(script, "@rbxts", "services"))
4
4
  local CollectionService = _services.CollectionService
5
5
  local HttpService = _services.HttpService
6
6
  local InsertService = _services.InsertService
7
+ local ServerStorage = _services.ServerStorage
7
8
  local _dom = TS.import(script, script.Parent, "dom")
8
9
  local liveCopies = _dom.liveCopies
9
10
  local numberAttribute = _dom.numberAttribute
@@ -15,10 +16,14 @@ local ATTR_HASH = _manifest.ATTR_HASH
15
16
  local ATTR_ID = _manifest.ATTR_ID
16
17
  local ATTR_KEY = _manifest.ATTR_KEY
17
18
  local ATTR_PATH = _manifest.ATTR_PATH
19
+ local ATTR_BACKUP_PLACE_VERSION = _manifest.ATTR_BACKUP_PLACE_VERSION
20
+ local ATTR_BACKUP_SAVED_AS = _manifest.ATTR_BACKUP_SAVED_AS
18
21
  local ATTR_VERSION = _manifest.ATTR_VERSION
22
+ local BACKUP_NAME = _manifest.BACKUP_NAME
19
23
  local decideAsset = _manifest.decideAsset
20
24
  local keyOfTag = _manifest.keyOfTag
21
25
  local parseManifest = _manifest.parseManifest
26
+ local placeMatchesManifest = _manifest.placeMatchesManifest
22
27
  local summarizeAssets = _manifest.summarizeAssets
23
28
  --[[
24
29
  *
@@ -217,6 +222,17 @@ local function readManifest(kernel, deadline)
217
222
  from = "payload",
218
223
  }
219
224
  end
225
+ --* What the place's backup build says about its hot-asset copies (BackupVouch), if anything.
226
+ local function backupVouch()
227
+ local backup = ServerStorage:FindFirstChild(BACKUP_NAME)
228
+ if backup == nil then
229
+ return nil
230
+ end
231
+ return {
232
+ placeVersion = numberAttribute(backup, ATTR_BACKUP_PLACE_VERSION),
233
+ savedAs = numberAttribute(backup, ATTR_BACKUP_SAVED_AS),
234
+ }
235
+ end
220
236
  --* Marks `instance` as the live copy of `entry` (attributes first, so they replicate with the tag).
221
237
  local function stamp(instance, entry, version)
222
238
  instance:SetAttribute(ATTR_KEY, entry.key)
@@ -283,7 +299,7 @@ local function loadCopy(entry)
283
299
  container:Destroy()
284
300
  local removed = stripScripts(root)
285
301
  if removed > 0 then
286
- warn("[src/assets/sync.ts:204]", `hot asset {entry.key}: removed {removed} scripts`)
302
+ warn("[src/assets/sync.ts:215]", `hot asset {entry.key}: removed {removed} scripts`)
287
303
  end
288
304
  return root
289
305
  end
@@ -409,7 +425,7 @@ local function syncEntry(entry, status, placeMatches, ownEpoch, threads)
409
425
  if _condition == nil then
410
426
  _condition = entry.ver
411
427
  end
412
- warn("[src/assets/sync.ts:287]", `hot asset {_exp_1} (version {_condition}) failed: {reason}`)
428
+ warn("[src/assets/sync.ts:298]", `hot asset {_exp_1} (version {_condition}) failed: {reason}`)
413
429
  return nil
414
430
  end
415
431
  -- The new copy is live: the copies it replaces go.
@@ -422,7 +438,7 @@ local function syncEntry(entry, status, placeMatches, ownEpoch, threads)
422
438
  status.ms = math.floor((os.clock() - started) * 1000)
423
439
  if late then
424
440
  status.late = true
425
- print("[src/assets/sync.ts:300]", `hot asset {entry.key} loaded late ({status.ms} ms)`)
441
+ print("[src/assets/sync.ts:311]", `hot asset {entry.key} loaded late ({status.ms} ms)`)
426
442
  end
427
443
  end)
428
444
  table.insert(_threads, _arg0)
@@ -471,7 +487,16 @@ local function run(kernel, deadline, ownEpoch, threads)
471
487
  end
472
488
  setManifest(manifest)
473
489
  report.manifest = "ok"
474
- local placeMatches = manifest.placeVersion ~= nil and manifest.placeVersion == game.PlaceVersion
490
+ -- A publish that changed nothing outside the kernel slots (the CLI's backup refresh) doesn't move servers to the
491
+ -- upload path: the backup vouches for the version it saved (2026-10-13 incident).
492
+ local place = placeMatchesManifest(manifest.placeVersion, game.PlaceVersion, backupVouch())
493
+ report.place = place
494
+ report.manifestPlaceVersion = manifest.placeVersion
495
+ report.placeVersion = game.PlaceVersion
496
+ local placeMatches = place == "same" or place == "vouched"
497
+ if place == "differs" then
498
+ warn("[src/assets/sync.ts:356]", `hot assets: the manifest is for place v{manifest.placeVersion}, this server runs v{game.PlaceVersion}: keys without a matching baked copy load from their uploaded versions (typetorch assets sync --deploy <branch> fixes it)`)
499
+ end
475
500
  local loads = {}
476
501
  for _, entry in manifest.entries do
477
502
  local status = {
@@ -490,7 +515,7 @@ local function run(kernel, deadline, ownEpoch, threads)
490
515
  if not ok then
491
516
  status.source = "failed"
492
517
  status.error = shortError(result)
493
- warn("[src/assets/sync.ts:347]", `hot asset {entry.key} failed: {status.error}`)
518
+ warn("[src/assets/sync.ts:369]", `hot asset {entry.key} failed: {status.error}`)
494
519
  elseif result ~= nil then
495
520
  table.insert(loads, result)
496
521
  end
@@ -524,7 +549,7 @@ local function run(kernel, deadline, ownEpoch, threads)
524
549
  end
525
550
  report.timedOut = true
526
551
  load.fail("timed out")
527
- warn("[src/assets/sync.ts:356]", `hot asset {load.status.key}: still loading; the old copy stays until it finishes`)
552
+ warn("[src/assets/sync.ts:378]", `hot asset {load.status.key}: still loading; the old copy stays until it finishes`)
528
553
  end
529
554
  report.unmanaged = unmanagedKeys()
530
555
  end
@@ -571,7 +596,7 @@ local function syncHotAssets(kernel, trove)
571
596
  local _errors = report.errors
572
597
  local _arg0 = `AssetSync failed: {shortError(err)}`
573
598
  table.insert(_errors, _arg0)
574
- warn("[src/assets/sync.ts:385]", `AssetSync failed: {err}`)
599
+ warn("[src/assets/sync.ts:407]", `AssetSync failed: {err}`)
575
600
  end
576
601
  report.running = false
577
602
  report.ms = math.floor((os.clock() - started) * 1000)
@@ -592,7 +617,7 @@ local function syncHotAssets(kernel, trove)
592
617
  table.insert(parts, _arg0)
593
618
  end
594
619
  table.sort(parts)
595
- print("[src/assets/sync.ts:395]", `hot assets: {table.concat(parts, ", ")} in {report.ms} ms`)
620
+ print("[src/assets/sync.ts:417]", `hot assets: {table.concat(parts, ", ")} in {report.ms} ms (place {report.place or "?"})`)
596
621
  end
597
622
  end
598
623
  return {
@@ -63,6 +63,19 @@ local function drawServer(target, report)
63
63
  target:field("Manifest", if ok then `{#report.entries} keys{if report.from ~= nil then ` ({report.from})` else ""}` else report.manifest, if ok then COLORS.text else COLORS.warn)
64
64
  local sync = if report.running then "Running" elseif report.ms ~= nil then `{report.ms} ms{if report.timedOut == true then " timed out" else ""}` else "-"
65
65
  target:field("Sync", sync, if report.timedOut == true then COLORS.warn else COLORS.text)
66
+ if report.place ~= nil then
67
+ local _condition = report.manifestPlaceVersion
68
+ if _condition == nil then
69
+ _condition = "?"
70
+ end
71
+ local _condition_1 = report.placeVersion
72
+ if _condition_1 == nil then
73
+ _condition_1 = "?"
74
+ end
75
+ local versions = `manifest v{_condition}, place v{_condition_1}`
76
+ local place = if report.place == "differs" then `differs ({versions}): keys load from uploads` elseif report.place == "vouched" then `vouched by the backup ({versions})` else report.place
77
+ target:field("Place", place, if report.place == "differs" then COLORS.warn else COLORS.text)
78
+ end
66
79
  for _, why in report.errors do
67
80
  target:text(why, COLORS.warn)
68
81
  end
@@ -188,7 +201,7 @@ local function renderAssetsTab(tab, options)
188
201
  end
189
202
  end)
190
203
  if not ok then
191
- warn("[src/devtools/assets-ui.ts:138]", `[devtools] assets refresh failed: {err}`)
204
+ warn("[src/devtools/assets-ui.ts:143]", `[devtools] assets refresh failed: {err}`)
192
205
  end
193
206
  task.wait(REFRESH)
194
207
  end
@@ -2,7 +2,7 @@ import type { AnalyticsStats } from "../analytics/schema";
2
2
  import type { AssetFacts } from "../assets/manifest";
3
3
  import type { DetachedStatus, ErrorReportsStatus, FleetSenderInfo, HealthInfo, KernelStatus, PerfSummary, SettingsStatus } from "../kernel";
4
4
  /** The newest kernel this framework release knows about. Bump it with every kernel release. */
5
- export declare const LATEST_KERNEL = "0.5.1";
5
+ export declare const LATEST_KERNEL = "0.5.3";
6
6
  /**
7
7
  * The live health window while it is open (kernel 0.3.7 thresholds; older kernels: 3): "1/3 errors, 24 s left", plus
8
8
  * ", no rollback" when a failure would only mark the server degraded. Undefined once the window closed.
@@ -2,7 +2,7 @@
2
2
  local TS = _G[script]
3
3
  local describeFleetError = TS.import(script, script.Parent.Parent, "analytics", "hints").describeFleetError
4
4
  --* The newest kernel this framework release knows about. Bump it with every kernel release.
5
- local LATEST_KERNEL = "0.5.1"
5
+ local LATEST_KERNEL = "0.5.3"
6
6
  --* Kernels before 0.3.7 use fixed thresholds: 3 errors within 30 s of ready roll back.
7
7
  local OLD_HEALTH_ERRORS = 3
8
8
  local OLD_HEALTH_WINDOW = 30
package/out/version.d.ts CHANGED
@@ -2,4 +2,4 @@
2
2
  * This framework's version, as package.json says (scripts/test-state-inspect.luau checks they match). Shown in
3
3
  * uploaded log headers (Logs > Upload); bump it with package.json.
4
4
  */
5
- export declare const FRAMEWORK_VERSION = "0.5.2";
5
+ export declare const FRAMEWORK_VERSION = "0.5.3";
package/out/version.luau CHANGED
@@ -5,7 +5,7 @@
5
5
  * uploaded log headers (Logs > Upload); bump it with package.json.
6
6
 
7
7
  ]]
8
- local FRAMEWORK_VERSION = "0.5.2"
8
+ local FRAMEWORK_VERSION = "0.5.3"
9
9
  return {
10
10
  FRAMEWORK_VERSION = FRAMEWORK_VERSION,
11
11
  }
package/package.json CHANGED
@@ -1,53 +1,53 @@
1
- {
2
- "name": "@typetorch/framework",
3
- "version": "0.5.2",
4
- "description": "TypeTorch framework for roblox-ts: hot-swappable modules (dependency injection, lifecycle, troves), guarded networking, hot assets and the in-game dev menu. Ships inside every TypeTorch artifact.",
5
- "license": "MIT",
6
- "author": "TypeTorch",
7
- "homepage": "https://github.com/typetorch/framework#readme",
8
- "repository": {
9
- "type": "git",
10
- "url": "git+https://github.com/typetorch/framework.git"
11
- },
12
- "bugs": {
13
- "url": "https://github.com/typetorch/framework/issues"
14
- },
15
- "keywords": ["roblox", "roblox-ts", "rbxts", "hot-reload", "hot-swap", "dependency-injection", "typetorch"],
16
- "main": "out/init.luau",
17
- "types": "out/index.d.ts",
18
- "files": ["out", "!**/*.tsbuildinfo"],
19
- "publishConfig": {
20
- "access": "public"
21
- },
22
- "scripts": {
23
- "build": "rbxtsc --type package",
24
- "watch": "rbxtsc --type package -w",
25
- "clean": "node -e \"require('fs').rmSync('out',{recursive:true,force:true})\"",
26
- "prepublishOnly": "node -e \"require('fs').rmSync('out',{recursive:true,force:true})\" && rbxtsc --type package"
27
- },
28
- "peerDependencies": {
29
- "@rbxts/compiler-types": "^3.0.0-types.0",
30
- "@typetorch/transformer": "^0.2.0",
31
- "roblox-ts": "^3.0.0"
32
- },
33
- "peerDependenciesMeta": {
34
- "@typetorch/transformer": {
35
- "optional": true
36
- }
37
- },
38
- "dependencies": {
39
- "@rbxts/services": "^1.6.0",
40
- "@rbxts/t": "^3.2.1",
41
- "@rbxts/trove": "^1.3.0"
42
- },
43
- "devDependencies": {
44
- "@rbxts/compiler-types": "^3.0.0-types.0",
45
- "@rbxts/types": "^1.0.956",
46
- "rbxts-transform-debug": "2.2.0",
47
- "roblox-ts": "^3.0.0",
48
- "typescript": "5.5.3"
49
- },
50
- "overrides": {
51
- "typescript": "5.5.3"
52
- }
53
- }
1
+ {
2
+ "name": "@typetorch/framework",
3
+ "version": "0.5.3",
4
+ "description": "TypeTorch framework for roblox-ts: hot-swappable modules (dependency injection, lifecycle, troves), guarded networking, hot assets and the in-game dev menu. Ships inside every TypeTorch artifact.",
5
+ "license": "MIT",
6
+ "author": "TypeTorch",
7
+ "homepage": "https://github.com/typetorch/framework#readme",
8
+ "repository": {
9
+ "type": "git",
10
+ "url": "git+https://github.com/typetorch/framework.git"
11
+ },
12
+ "bugs": {
13
+ "url": "https://github.com/typetorch/framework/issues"
14
+ },
15
+ "keywords": ["roblox", "roblox-ts", "rbxts", "hot-reload", "hot-swap", "dependency-injection", "typetorch"],
16
+ "main": "out/init.luau",
17
+ "types": "out/index.d.ts",
18
+ "files": ["out", "!**/*.tsbuildinfo"],
19
+ "publishConfig": {
20
+ "access": "public"
21
+ },
22
+ "scripts": {
23
+ "build": "rbxtsc --type package",
24
+ "watch": "rbxtsc --type package -w",
25
+ "clean": "node -e \"require('fs').rmSync('out',{recursive:true,force:true})\"",
26
+ "prepublishOnly": "node -e \"require('fs').rmSync('out',{recursive:true,force:true})\" && rbxtsc --type package"
27
+ },
28
+ "peerDependencies": {
29
+ "@rbxts/compiler-types": "^3.0.0-types.0",
30
+ "@typetorch/transformer": "^0.2.0",
31
+ "roblox-ts": "^3.0.0"
32
+ },
33
+ "peerDependenciesMeta": {
34
+ "@typetorch/transformer": {
35
+ "optional": true
36
+ }
37
+ },
38
+ "dependencies": {
39
+ "@rbxts/services": "^1.6.0",
40
+ "@rbxts/t": "^3.2.1",
41
+ "@rbxts/trove": "^1.3.0"
42
+ },
43
+ "devDependencies": {
44
+ "@rbxts/compiler-types": "^3.0.0-types.0",
45
+ "@rbxts/types": "^1.0.956",
46
+ "rbxts-transform-debug": "2.2.0",
47
+ "roblox-ts": "^3.0.0",
48
+ "typescript": "5.5.3"
49
+ },
50
+ "overrides": {
51
+ "typescript": "5.5.3"
52
+ }
53
+ }