@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,50 @@
1
+ -- Compiled with roblox-ts v3.0.0
2
+ local TS = _G[script]
3
+ local _vide = TS.import(script, TS.getModule(script, "@rbxts", "vide").src)
4
+ local cleanup = _vide.cleanup
5
+ local source = _vide.source
6
+ local untracked = TS.import(script, script.Parent, "reactive").untracked
7
+ local createWatchFrom = TS.import(script, script.Parent, "internal", "reactive", "flush").createWatchFrom
8
+ local createRootScope = TS.import(script, script.Parent, "internal", "reactive", "ownership").createRootScope
9
+ local NO_OWNER = "useReadable() needs a Vide owner: call it inside a component or root()"
10
+ --[[
11
+ *
12
+ * A Vide source that follows `readable`. It holds the value read now, untracked, and takes each change on the
13
+ * reactive flush, through a `watch` that ends with the current Vide owner. Outside a Vide component or `root` it
14
+ * throws.
15
+
16
+ ]]
17
+ local function useReadable(readable)
18
+ local dispose
19
+ -- Vide's cleanup is the only public test for an owner, and registering first leaves nothing to undo.
20
+ local owned = pcall(cleanup, function()
21
+ local _result = dispose
22
+ if _result ~= nil then
23
+ _result = _result()
24
+ end
25
+ return _result
26
+ end)
27
+ if not owned then
28
+ error(NO_OWNER, 2)
29
+ end
30
+ local initial = untracked(readable)
31
+ local value = source(initial)
32
+ local latest = initial
33
+ local write = function()
34
+ value(latest)
35
+ return nil
36
+ end
37
+ -- A root, so a component mounted from inside a Crucible run or callback does not end with it.
38
+ dispose = createRootScope(function()
39
+ return createWatchFrom(readable, initial, function(current)
40
+ latest = current
41
+ -- Vide runs its effects inside the write. Across gsub's C call a yield in one throws where it yields, so
42
+ -- Vide's own error path pops its scope stack. Without it the runner cancels the yielded worker mid-effect.
43
+ string.gsub("x", "x", write)
44
+ end)
45
+ end)
46
+ return value
47
+ end
48
+ return {
49
+ useReadable = useReadable,
50
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@trioforge/crucible",
3
- "version": "0.1.0",
3
+ "version": "0.2.0",
4
4
  "license": "MIT",
5
5
  "main": "out/init.luau",
6
6
  "types": "out/index.d.ts",
@@ -21,10 +21,20 @@
21
21
  "types": "./out/client/index.d.ts",
22
22
  "default": "./out/client/init.luau"
23
23
  },
24
+ "./vide": {
25
+ "types": "./out/vide/index.d.ts",
26
+ "default": "./out/vide/init.luau"
27
+ },
24
28
  "./transformer": "./transformer/index.js"
25
29
  },
26
30
  "peerDependencies": {
27
- "typescript": "5.5.3"
31
+ "typescript": "5.5.3",
32
+ "@rbxts/vide": "^0.6.1"
33
+ },
34
+ "peerDependenciesMeta": {
35
+ "@rbxts/vide": {
36
+ "optional": true
37
+ }
28
38
  },
29
39
  "files": [
30
40
  "out",
@@ -32,6 +42,7 @@
32
42
  "server",
33
43
  "client",
34
44
  "emit",
45
+ "vide",
35
46
  "LICENSE"
36
47
  ],
37
48
  "repository": {
@@ -0,0 +1,72 @@
1
+ function createDataChecks({ ts, program, checker, api }) {
2
+ const unwrap = (expression) => {
3
+ while (ts.isParenthesizedExpression(expression) || ts.isNonNullExpression(expression))
4
+ expression = expression.expression;
5
+ return expression;
6
+ };
7
+ const member = (expression) => {
8
+ if (ts.isPropertyAccessExpression(expression)) return expression.name.text;
9
+ if (ts.isElementAccessExpression(expression) && ts.isStringLiteralLike(expression.argumentExpression))
10
+ return expression.argumentExpression.text;
11
+ };
12
+ const store = (expression) => api(checker.getTypeAtLocation(expression).getSymbol()) === "ProfileStore";
13
+ const marketplace = (expression) => {
14
+ const symbol = checker.getTypeAtLocation(expression).getSymbol();
15
+ return (
16
+ symbol?.name === "MarketplaceService" &&
17
+ symbol.declarations?.some((d) =>
18
+ d.getSourceFile().fileName.replaceAll("\\", "/").includes("/@rbxts/types/"),
19
+ )
20
+ );
21
+ };
22
+ const storeCall = (node) => {
23
+ const callee = unwrap(node.expression);
24
+ if (store(callee)) return { store: callee };
25
+ const name = member(callee);
26
+ if (name !== undefined && store(callee.expression)) return { store: callee.expression, member: name };
27
+ };
28
+ let granted;
29
+ const grants = () => {
30
+ if (granted === undefined) {
31
+ granted = false;
32
+ const walk = (node) => {
33
+ if (granted) return;
34
+ if (ts.isCallExpression(node) && storeCall(node)?.member === "grant") granted = true;
35
+ else ts.forEachChild(node, walk);
36
+ };
37
+ for (const source of program.getSourceFiles()) if (!source.isDeclarationFile) walk(source);
38
+ }
39
+ return granted;
40
+ };
41
+ return ({ report, side }) =>
42
+ (node) => {
43
+ if (ts.isCallExpression(node)) {
44
+ const call = storeCall(node);
45
+ if (call) {
46
+ const text = call.store.getText();
47
+ const client =
48
+ call.member === undefined || call.member === "loaded" ? !node.arguments.length : false;
49
+ const required = client ? "client" : "server";
50
+ if (side(node) !== required)
51
+ report(
52
+ node,
53
+ "CRU027",
54
+ client
55
+ ? `${text}${call.member ? ".loaded" : ""}() reads the local player's profile; on the server call ${text}${call.member ? ".loaded" : ""}(player).`
56
+ : `${text}${call.member === undefined ? "(player)" : `.${call.member}`} is server-only; the client reads ${text}().`,
57
+ );
58
+ }
59
+ }
60
+ if (ts.isBinaryExpression(node) && node.operatorToken.kind === ts.SyntaxKind.EqualsToken) {
61
+ const target = unwrap(node.left);
62
+ if (member(target) === "ProcessReceipt" && marketplace(target.expression) && grants())
63
+ report(
64
+ node,
65
+ "CRU025",
66
+ "Crucible's data module owns ProcessReceipt; register grants with a profile store's grant(productId, ...).",
67
+ );
68
+ }
69
+ };
70
+ }
71
+
72
+ module.exports = { createDataChecks };
@@ -0,0 +1,153 @@
1
+ const limits = Object.freeze({
2
+ MESSAGE_NAME_MAX_BYTES: 64,
3
+ MAX_ASK_TIMEOUT_SECONDS: 120,
4
+ MAX_UNION_BRANCHES: 256,
5
+ MAX_SCHEMA_NODES: 4096,
6
+ MAX_DEPTH: 32,
7
+ });
8
+ const reactiveLimits = Object.freeze({
9
+ MESSAGE_NAME_MAX_BYTES: 64,
10
+ SYNC_VALUE_DEPTH: 28,
11
+ });
12
+ const dataLimits = Object.freeze({
13
+ STORE_NAME_MAX_BYTES: 50,
14
+ MESSAGE_NAME_MAX_BYTES: 64,
15
+ CONFIG_NAME_MAX_BYTES: 64,
16
+ PERSIST_DEPTH: 28,
17
+ VIEW_NAME_PREFIX: "Crucible.Data.",
18
+ LIVE_NAME_PREFIX: "Crucible.Live.",
19
+ VIEW_NAME_MAX_BYTES: 64,
20
+ RESERVED_HANDLE_NAMES: Object.freeze(["set", "update", "insert", "remove"]),
21
+ });
22
+
23
+ const message = {
24
+ label: "messages",
25
+ side: "shared",
26
+ names: "Message",
27
+ maxBytes: limits.MESSAGE_NAME_MAX_BYTES,
28
+ typeArguments: 1,
29
+ arguments: 1,
30
+ passed: [],
31
+ position: "payload",
32
+ };
33
+ const replicated = {
34
+ label: "replicated state",
35
+ side: "shared",
36
+ names: "Replicated",
37
+ maxBytes: reactiveLimits.MESSAGE_NAME_MAX_BYTES,
38
+ typeArguments: 1,
39
+ arguments: 2,
40
+ passed: [1],
41
+ position: "state",
42
+ };
43
+ const config = {
44
+ label: "configs",
45
+ side: "server",
46
+ names: "Config",
47
+ maxBytes: dataLimits.CONFIG_NAME_MAX_BYTES,
48
+ typeArguments: 1,
49
+ arguments: 2,
50
+ passed: [1],
51
+ position: "persist",
52
+ };
53
+ const kinds = Object.freeze({
54
+ toServer: message,
55
+ toClient: message,
56
+ askServer: { ...message, typeArguments: 2, arguments: 2 },
57
+ replicated,
58
+ replicatedPerPlayer: replicated,
59
+ profileStore: {
60
+ label: "profile stores",
61
+ side: "shared",
62
+ names: "Store",
63
+ maxBytes: dataLimits.STORE_NAME_MAX_BYTES,
64
+ typeArguments: "optional",
65
+ arguments: 2,
66
+ passed: [1],
67
+ position: "persist",
68
+ store: true,
69
+ handles: true,
70
+ },
71
+ dataStore: {
72
+ label: "data stores",
73
+ side: "server",
74
+ names: "Store",
75
+ maxBytes: dataLimits.STORE_NAME_MAX_BYTES,
76
+ typeArguments: 1,
77
+ arguments: 2,
78
+ passed: [1],
79
+ position: "persist",
80
+ store: true,
81
+ },
82
+ profileMessage: {
83
+ label: "profile messages",
84
+ side: "server",
85
+ names: "Profile message",
86
+ maxBytes: dataLimits.MESSAGE_NAME_MAX_BYTES,
87
+ typeArguments: 1,
88
+ arguments: 1,
89
+ passed: [],
90
+ position: "persist",
91
+ },
92
+ config,
93
+ playerConfig: config,
94
+ });
95
+ const depthLimits = Object.freeze({
96
+ payload: limits.MAX_DEPTH,
97
+ state: reactiveLimits.SYNC_VALUE_DEPTH,
98
+ persist: dataLimits.PERSIST_DEPTH,
99
+ });
100
+
101
+ // Marks sit only on object fields from the root, so only object fields can lose every field.
102
+ function replica(node) {
103
+ if (node.kind !== "object") return node;
104
+ const fields = [];
105
+ for (const [key, child] of node.fields) {
106
+ if (child.serverOnly) continue;
107
+ const kept = replica(child);
108
+ if (kept.kind === "object" && !kept.fields.length) continue;
109
+ fields.push([key, kept]);
110
+ }
111
+ return { kind: "object", fields };
112
+ }
113
+ // Object fields are sorted by bytes, so a depth-first walk lists paths in rule 131 order, a prefix first.
114
+ function serverOnlyPaths(node, prefix = []) {
115
+ if (node.kind !== "object") return [];
116
+ return node.fields.flatMap(([key, child]) =>
117
+ child.serverOnly ? [[...prefix, key]] : serverOnlyPaths(child, [...prefix, key]),
118
+ );
119
+ }
120
+ const SEGMENT = /^[A-Za-z][A-Za-z0-9_]*$/;
121
+ function viewNameProblems(store, node) {
122
+ if (node.kind !== "object") return [];
123
+ const prefix = `${dataLimits.VIEW_NAME_PREFIX}${store}.`;
124
+ return node.fields.flatMap(([field]) => {
125
+ const name = prefix + field;
126
+ if (!SEGMENT.test(field))
127
+ return [
128
+ [
129
+ `value[${JSON.stringify(field)}]`,
130
+ "a field the owner's client receives is named by one declaration segment, [A-Za-z][A-Za-z0-9_]*; rename it or mark it serverOnly",
131
+ ],
132
+ ];
133
+ if (Buffer.byteLength(name) > dataLimits.VIEW_NAME_MAX_BYTES)
134
+ return [
135
+ [
136
+ `value.${field}`,
137
+ `its view name ${name} is ${Buffer.byteLength(name)} bytes, over ${dataLimits.VIEW_NAME_MAX_BYTES}; this store allows field names of at most ${dataLimits.VIEW_NAME_MAX_BYTES - Buffer.byteLength(prefix)} bytes; rename the field, shorten the store name, or mark it serverOnly`,
138
+ ],
139
+ ];
140
+ return [];
141
+ });
142
+ }
143
+
144
+ module.exports = {
145
+ limits,
146
+ reactiveLimits,
147
+ dataLimits,
148
+ kinds,
149
+ depthLimits,
150
+ replica,
151
+ serverOnlyPaths,
152
+ viewNameProblems,
153
+ };
@@ -2,6 +2,8 @@ const path = require("node:path");
2
2
  const { createRequire } = require("node:module");
3
3
  const { packageOf, typeId, sourceRoot } = require("./type-id.cjs");
4
4
  const { createReporter } = require("./diagnostics.cjs");
5
+ const { createNetwork } = require("./network.cjs");
6
+ const { createDataChecks } = require("./data.cjs");
5
7
 
6
8
  module.exports = function transformer(program, config = {}, extras = {}) {
7
9
  const ts = extras.ts ?? require("typescript");
@@ -9,10 +11,20 @@ module.exports = function transformer(program, config = {}, extras = {}) {
9
11
  const f = ts.factory;
10
12
  const symbols = new Map();
11
13
  const unalias = (s) => s && (s.flags & ts.SymbolFlags.Alias ? checker.getAliasedSymbol(s) : s);
12
- let invalidMetadata = false;
14
+ const describe = (error) =>
15
+ error instanceof RangeError && error.message === "Maximum call stack size exceeded"
16
+ ? "Cannot transform input: the type checker ran out of stack. A type in this file nests or inherits too deeply to analyse."
17
+ : `Cannot transform input: ${String(error instanceof Error ? error.message : error).split(/\r?\n/)[0]}`;
18
+ let startupFault;
13
19
  try {
14
20
  for (const source of program.getSourceFiles()) {
15
- const owner = packageOf(source.fileName);
21
+ let owner;
22
+ try {
23
+ owner = packageOf(source.fileName);
24
+ } catch {
25
+ startupFault = "Cannot read package metadata. Check the project's package.json files.";
26
+ break;
27
+ }
16
28
  if (owner?.json.name !== "@trioforge/crucible") continue;
17
29
  const module = checker.getSymbolAtLocation(source);
18
30
  if (!module) continue;
@@ -21,8 +33,8 @@ module.exports = function transformer(program, config = {}, extras = {}) {
21
33
  if (!symbols.has(target)) symbols.set(target, symbol.name);
22
34
  }
23
35
  }
24
- } catch {
25
- invalidMetadata = true;
36
+ } catch (error) {
37
+ startupFault = describe(error);
26
38
  }
27
39
  const api = (symbol) => symbols.get(unalias(symbol));
28
40
  const modifiers = (node, flags) => (ts.getCombinedModifierFlags(node) & flags) !== 0;
@@ -46,12 +58,14 @@ module.exports = function transformer(program, config = {}, extras = {}) {
46
58
  };
47
59
  const diagnosticSink = extras.addDiagnostic;
48
60
  let report;
61
+ let networkProgram;
62
+ let dataProgram;
49
63
  return (context) => (source) => {
50
64
  if (source.isDeclarationFile) return source;
51
65
  let used = false;
52
66
  report ??= createReporter(ts, diagnosticSink ?? ((d) => context.addDiagnostic(d)));
53
- if (invalidMetadata) {
54
- report(source, "Crucible", "Cannot read package metadata. Check the project's package.json files.");
67
+ if (startupFault) {
68
+ report(source, "Crucible", startupFault);
55
69
  return source;
56
70
  }
57
71
  const isGlobal = (decl) => {
@@ -499,7 +513,11 @@ module.exports = function transformer(program, config = {}, extras = {}) {
499
513
  .slice(0, -1)
500
514
  .reverse()
501
515
  .find((part) => ["server", "client", "shared"].includes(part));
516
+ let network;
517
+ let data;
502
518
  const inspect = (node) => {
519
+ network.inspect(node);
520
+ data(node);
503
521
  if (
504
522
  ts.isImportDeclaration(node) ||
505
523
  ts.isExportDeclaration(node) ||
@@ -622,6 +640,8 @@ module.exports = function transformer(program, config = {}, extras = {}) {
622
640
  };
623
641
 
624
642
  const visit = (node) => {
643
+ const networkNode = network.visit(node);
644
+ if (networkNode) return networkNode;
625
645
  if (isMacro(node, "attribute")) {
626
646
  const field = node.parent;
627
647
  if (
@@ -694,6 +714,16 @@ module.exports = function transformer(program, config = {}, extras = {}) {
694
714
  return next;
695
715
  };
696
716
  try {
717
+ networkProgram ??= createNetwork({ ts, program, checker, api, kindOf, instanceType, modifiers, isMacro });
718
+ network = networkProgram({
719
+ source,
720
+ report,
721
+ runtime,
722
+ side,
723
+ visitInput: (node) => ts.visitNode(node, visit),
724
+ });
725
+ dataProgram ??= createDataChecks({ ts, program, checker, api });
726
+ data = dataProgram({ report, side });
697
727
  inspect(source);
698
728
  const next = ts.visitEachChild(source, visit, context);
699
729
  if (!used) return next;
@@ -704,11 +734,7 @@ module.exports = function transformer(program, config = {}, extras = {}) {
704
734
  );
705
735
  return f.updateSourceFile(next, [imports, ...next.statements]);
706
736
  } catch (error) {
707
- report(
708
- source,
709
- "Crucible",
710
- `Cannot transform input: ${String(error instanceof Error ? error.message : error).split(/\r?\n/)[0]}`,
711
- );
737
+ report(source, "Crucible", describe(error));
712
738
  return source;
713
739
  }
714
740
  };