@reactive-contracts/compiler 0.1.2-beta → 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.
package/dist/index.cli.js CHANGED
@@ -134,20 +134,84 @@ ${gitignoreTemplate}` : gitignoreTemplate;
134
134
 
135
135
  // src/cli/commands/compile.ts
136
136
  import { glob } from "glob";
137
- import * as path2 from "path";
138
- import { pathToFileURL } from "url";
139
- import { register } from "tsx/esm/api";
137
+ import * as path3 from "path";
138
+ import { createJiti as createJiti2 } from "jiti";
140
139
 
141
140
  // src/config/index.ts
142
- async function loadConfig(_configPath = "./rcontracts.config.ts") {
143
- return {
144
- contracts: "./contracts/**/*.contract.ts",
145
- output: {
146
- frontend: "./generated/frontend",
147
- backend: "./generated/backend",
148
- runtime: "./generated/runtime"
141
+ import { createJiti } from "jiti";
142
+ import * as path2 from "path";
143
+ import { existsSync as existsSync2 } from "fs";
144
+ var defaultConfig = {
145
+ contracts: "./contracts/**/*.contract.ts",
146
+ output: {
147
+ frontend: "./generated/frontend",
148
+ backend: "./generated/backend",
149
+ runtime: "./generated/runtime"
150
+ },
151
+ validation: {
152
+ strictLatency: false,
153
+ requireIntent: true,
154
+ maxComplexity: 10
155
+ },
156
+ optimization: {
157
+ bundleSplitting: false,
158
+ treeShaking: false,
159
+ precompute: []
160
+ }
161
+ };
162
+ function resolveConfigPath(configPath, cwd = process.cwd()) {
163
+ if (configPath) {
164
+ const absolutePath = path2.isAbsolute(configPath) ? configPath : path2.join(cwd, configPath);
165
+ return existsSync2(absolutePath) ? absolutePath : null;
166
+ }
167
+ const configNames = [
168
+ "rcontracts.config.ts",
169
+ "rcontracts.config.js",
170
+ "rcontracts.config.mjs",
171
+ "reactive-contracts.config.ts",
172
+ "reactive-contracts.config.js"
173
+ ];
174
+ for (const name of configNames) {
175
+ const fullPath = path2.join(cwd, name);
176
+ if (existsSync2(fullPath)) {
177
+ return fullPath;
149
178
  }
150
- };
179
+ }
180
+ return null;
181
+ }
182
+ async function loadConfig(configPath, cwd = process.cwd()) {
183
+ const resolvedPath = resolveConfigPath(configPath, cwd);
184
+ if (!resolvedPath) {
185
+ return { ...defaultConfig };
186
+ }
187
+ const jiti3 = createJiti(import.meta.url, {
188
+ interopDefault: true,
189
+ moduleCache: false
190
+ // Disable cache to get fresh config on reload
191
+ });
192
+ try {
193
+ const module = await jiti3.import(resolvedPath);
194
+ const config = module.default || module;
195
+ return {
196
+ ...defaultConfig,
197
+ ...config,
198
+ output: {
199
+ ...defaultConfig.output,
200
+ ...config.output || {}
201
+ },
202
+ validation: {
203
+ ...defaultConfig.validation,
204
+ ...config.validation || {}
205
+ },
206
+ optimization: {
207
+ ...defaultConfig.optimization,
208
+ ...config.optimization || {}
209
+ }
210
+ };
211
+ } catch (err) {
212
+ const errorMessage = err instanceof Error ? err.message : String(err);
213
+ throw new Error(`Failed to load config from ${resolvedPath}: ${errorMessage}`);
214
+ }
151
215
  }
152
216
 
153
217
  // src/validator/index.ts
@@ -195,17 +259,17 @@ function validateContract(contract) {
195
259
  warnings
196
260
  };
197
261
  }
198
- function validateShape(shape, path4, errors, warnings) {
262
+ function validateShape(shape, path5, errors, warnings) {
199
263
  if (!shape || typeof shape !== "object") {
200
- errors.push(`Invalid shape at ${path4 || "root"}: must be an object`);
264
+ errors.push(`Invalid shape at ${path5 || "root"}: must be an object`);
201
265
  return;
202
266
  }
203
267
  const keys = Object.keys(shape);
204
268
  if (keys.length === 0) {
205
- warnings.push(`Shape at ${path4 || "root"} is empty`);
269
+ warnings.push(`Shape at ${path5 || "root"} is empty`);
206
270
  }
207
271
  for (const key of keys) {
208
- const fieldPath = path4 ? `${path4}.${key}` : key;
272
+ const fieldPath = path5 ? `${path5}.${key}` : key;
209
273
  const value = shape[key];
210
274
  if (!value) {
211
275
  errors.push(`Field "${fieldPath}" has undefined value`);
@@ -219,48 +283,48 @@ function validateShape(shape, path4, errors, warnings) {
219
283
  validateTypeDefinition(value, fieldPath, errors, warnings);
220
284
  }
221
285
  }
222
- function validateTypeDefinition(type, path4, errors, warnings) {
286
+ function validateTypeDefinition(type, path5, errors, warnings) {
223
287
  if (typeof type === "string") {
224
288
  const validPrimitives = ["string", "number", "boolean", "Date", "null", "undefined"];
225
289
  const isURL = type === "URL" || type.startsWith("URL<");
226
290
  if (!validPrimitives.includes(type) && !isURL) {
227
- errors.push(`Invalid type at ${path4}: "${type}" is not a valid primitive or URL type`);
291
+ errors.push(`Invalid type at ${path5}: "${type}" is not a valid primitive or URL type`);
228
292
  }
229
293
  if (isURL && type !== "URL") {
230
294
  const urlMatch = type.match(/^URL<(.+)>$/);
231
295
  if (!urlMatch) {
232
- errors.push(`Invalid URL type at ${path4}: must be "URL" or "URL<options>"`);
296
+ errors.push(`Invalid URL type at ${path5}: must be "URL" or "URL<options>"`);
233
297
  }
234
298
  }
235
299
  } else if (typeof type === "object" && type !== null) {
236
300
  if ("_brand" in type && type._brand === "DerivedField") {
237
301
  validateDerivedField(
238
302
  type,
239
- path4,
303
+ path5,
240
304
  errors,
241
305
  warnings
242
306
  );
243
307
  } else {
244
- validateShape(type, path4, errors, warnings);
308
+ validateShape(type, path5, errors, warnings);
245
309
  }
246
310
  } else {
247
- errors.push(`Invalid type at ${path4}: must be a string, object, or DerivedField`);
311
+ errors.push(`Invalid type at ${path5}: must be a string, object, or DerivedField`);
248
312
  }
249
313
  }
250
- function validateDerivedField(field, path4, errors, _warnings) {
314
+ function validateDerivedField(field, path5, errors, _warnings) {
251
315
  if (typeof field.derive !== "function") {
252
- errors.push(`Derived field at ${path4} must have a derive function`);
316
+ errors.push(`Derived field at ${path5} must have a derive function`);
253
317
  }
254
318
  if (field.dependencies && !Array.isArray(field.dependencies)) {
255
- errors.push(`Derived field dependencies at ${path4} must be an array`);
319
+ errors.push(`Derived field dependencies at ${path5} must be an array`);
256
320
  }
257
321
  if (field.preferredLayer && !["client", "edge", "origin"].includes(field.preferredLayer)) {
258
- errors.push(`Derived field preferredLayer at ${path4} must be 'client', 'edge', or 'origin'`);
322
+ errors.push(`Derived field preferredLayer at ${path5} must be 'client', 'edge', or 'origin'`);
259
323
  }
260
324
  if (field.dependencies && Array.isArray(field.dependencies)) {
261
325
  for (const dep of field.dependencies) {
262
326
  if (typeof dep !== "string") {
263
- errors.push(`Derived field dependency at ${path4} must be a string`);
327
+ errors.push(`Derived field dependency at ${path5} must be a string`);
264
328
  }
265
329
  }
266
330
  }
@@ -389,10 +453,10 @@ function validateVersioning(versioning, errors, warnings) {
389
453
  }
390
454
  function collectFieldPaths(shape, prefix, result) {
391
455
  for (const [key, value] of Object.entries(shape)) {
392
- const path4 = prefix ? `${prefix}.${key}` : key;
393
- result.add(path4);
456
+ const path5 = prefix ? `${prefix}.${key}` : key;
457
+ result.add(path5);
394
458
  if (typeof value === "object" && value !== null && !("_brand" in value) && typeof value !== "string") {
395
- collectFieldPaths(value, path4, result);
459
+ collectFieldPaths(value, path5, result);
396
460
  }
397
461
  }
398
462
  }
@@ -825,7 +889,11 @@ ${indentStr}}`;
825
889
  }
826
890
 
827
891
  // src/cli/commands/compile.ts
828
- var tsxUnregister = register();
892
+ var jiti = createJiti2(import.meta.url, {
893
+ interopDefault: true,
894
+ moduleCache: false
895
+ // Disable cache to avoid stale imports
896
+ });
829
897
  async function compile() {
830
898
  try {
831
899
  header("Compiling Reactive Contracts");
@@ -847,11 +915,10 @@ async function compile() {
847
915
  let successCount = 0;
848
916
  let errorCount = 0;
849
917
  for (const file of contractFiles) {
850
- const fileName = path2.basename(file, ".contract.ts");
918
+ const fileName = path3.basename(file, ".contract.ts");
851
919
  start(`Processing ${fileName}...`);
852
920
  try {
853
- const fileUrl = pathToFileURL(file).href;
854
- const module = await import(fileUrl);
921
+ const module = await jiti.import(file);
855
922
  const contractExports = Object.entries(module).filter(
856
923
  ([key, value]) => key.endsWith("Contract") && typeof value === "object" && value !== null && "_brand" in value && value._brand === "Contract"
857
924
  );
@@ -894,19 +961,19 @@ async function compile() {
894
961
  }
895
962
  }
896
963
  const contractName = contract.definition.name;
897
- const frontendPath = path2.join(
964
+ const frontendPath = path3.join(
898
965
  process.cwd(),
899
966
  config.output.frontend,
900
967
  `${contractName}.ts`
901
968
  );
902
969
  await generateFrontendTypes(contract, frontendPath);
903
- const backendPath = path2.join(
970
+ const backendPath = path3.join(
904
971
  process.cwd(),
905
972
  config.output.backend,
906
973
  `${contractName}.resolver.ts`
907
974
  );
908
975
  await generateBackendResolver(contract, backendPath);
909
- const runtimePath = path2.join(
976
+ const runtimePath = path3.join(
910
977
  process.cwd(),
911
978
  config.output.runtime,
912
979
  `${contractName}.negotiator.ts`
@@ -921,7 +988,6 @@ async function compile() {
921
988
  errorCount++;
922
989
  }
923
990
  }
924
- tsxUnregister();
925
991
  console.log("");
926
992
  if (successCount > 0) {
927
993
  success(`\u2713 Successfully compiled ${successCount} contract(s)`);
@@ -941,10 +1007,13 @@ async function compile() {
941
1007
 
942
1008
  // src/cli/commands/validate.ts
943
1009
  import { glob as glob2 } from "glob";
944
- import * as path3 from "path";
945
- import { pathToFileURL as pathToFileURL2 } from "url";
946
- import { register as register2 } from "tsx/esm/api";
947
- var tsxUnregister2 = register2();
1010
+ import * as path4 from "path";
1011
+ import { createJiti as createJiti3 } from "jiti";
1012
+ var jiti2 = createJiti3(import.meta.url, {
1013
+ interopDefault: true,
1014
+ moduleCache: false
1015
+ // Disable cache to avoid stale imports
1016
+ });
948
1017
  async function validate() {
949
1018
  try {
950
1019
  header("Validating Reactive Contracts");
@@ -967,11 +1036,10 @@ async function validate() {
967
1036
  let invalidCount = 0;
968
1037
  let warningCount = 0;
969
1038
  for (const file of contractFiles) {
970
- const fileName = path3.basename(file, ".contract.ts");
1039
+ const fileName = path4.basename(file, ".contract.ts");
971
1040
  start(`Validating ${fileName}...`);
972
1041
  try {
973
- const fileUrl = pathToFileURL2(file).href;
974
- const module = await import(fileUrl);
1042
+ const module = await jiti2.import(file);
975
1043
  const contractExports = Object.entries(module).filter(
976
1044
  ([key, value]) => key.endsWith("Contract") && typeof value === "object" && value !== null && "_brand" in value && value._brand === "Contract"
977
1045
  );
@@ -1031,7 +1099,6 @@ async function validate() {
1031
1099
  invalidCount++;
1032
1100
  }
1033
1101
  }
1034
- tsxUnregister2();
1035
1102
  console.log("");
1036
1103
  console.log("Validation Summary:");
1037
1104
  console.log(` \u2713 Valid: ${validCount}`);
package/dist/index.d.cts CHANGED
@@ -43,11 +43,52 @@ interface CompilationResult {
43
43
  type ConfigDefinition = CompilerConfig;
44
44
 
45
45
  declare function defineConfig(config: CompilerConfig): ConfigDefinition;
46
+ declare function loadConfig(configPath?: string, cwd?: string): Promise<CompilerConfig>;
46
47
 
47
48
  declare function validateContract(contract: Contract): ValidationResult;
48
49
 
49
50
  declare function analyzeLatency(contract: Contract): LatencyAnalysisResult;
50
51
 
51
52
  declare function generateFrontendTypes(contract: Contract, outputPath: string): Promise<void>;
53
+ declare function generateBackendResolver(contract: Contract, outputPath: string): Promise<void>;
54
+ declare function generateRuntimeNegotiator(contract: Contract, outputPath: string): Promise<void>;
52
55
 
53
- export { type CompilationResult, type CompilerConfig, type ConfigDefinition, type LatencyAnalysisResult, type ValidationResult, analyzeLatency, defineConfig, generateFrontendTypes, validateContract };
56
+ interface CompilerLogger {
57
+ info: (message: string) => void;
58
+ success: (message: string) => void;
59
+ warning: (message: string) => void;
60
+ error: (message: string) => void;
61
+ verbose?: (message: string) => void;
62
+ }
63
+ declare const silentLogger: CompilerLogger;
64
+ declare const consoleLogger: CompilerLogger;
65
+ interface CompileOptions {
66
+ config: CompilerConfig;
67
+ cwd?: string;
68
+ logger?: CompilerLogger;
69
+ file?: string;
70
+ }
71
+ interface CompileResult {
72
+ success: boolean;
73
+ results: CompilationResult[];
74
+ errors: string[];
75
+ warnings: string[];
76
+ }
77
+ declare function parseContractFile(filePath: string, logger?: CompilerLogger): Promise<{
78
+ contracts: Array<{
79
+ name: string;
80
+ contract: Contract;
81
+ }>;
82
+ errors: string[];
83
+ }>;
84
+ declare function compileContract(contract: Contract, config: CompilerConfig, cwd?: string, logger?: CompilerLogger): Promise<CompilationResult>;
85
+ declare function findContractFiles(config: CompilerConfig, cwd?: string): Promise<string[]>;
86
+ declare function compileAll(options: CompileOptions): Promise<CompileResult>;
87
+ declare function isContractFile(filePath: string): boolean;
88
+ declare function getGeneratedFilesForContract(contractName: string, config: CompilerConfig, cwd?: string): {
89
+ frontend: string;
90
+ backend: string;
91
+ runtime: string;
92
+ };
93
+
94
+ export { type CompilationResult, type CompileOptions, type CompileResult, type CompilerConfig, type CompilerLogger, type ConfigDefinition, type LatencyAnalysisResult, type ValidationResult, analyzeLatency, compileAll, compileContract, consoleLogger, defineConfig, findContractFiles, generateBackendResolver, generateFrontendTypes, generateRuntimeNegotiator, getGeneratedFilesForContract, isContractFile, loadConfig, parseContractFile, silentLogger, validateContract };
package/dist/index.d.ts CHANGED
@@ -43,11 +43,52 @@ interface CompilationResult {
43
43
  type ConfigDefinition = CompilerConfig;
44
44
 
45
45
  declare function defineConfig(config: CompilerConfig): ConfigDefinition;
46
+ declare function loadConfig(configPath?: string, cwd?: string): Promise<CompilerConfig>;
46
47
 
47
48
  declare function validateContract(contract: Contract): ValidationResult;
48
49
 
49
50
  declare function analyzeLatency(contract: Contract): LatencyAnalysisResult;
50
51
 
51
52
  declare function generateFrontendTypes(contract: Contract, outputPath: string): Promise<void>;
53
+ declare function generateBackendResolver(contract: Contract, outputPath: string): Promise<void>;
54
+ declare function generateRuntimeNegotiator(contract: Contract, outputPath: string): Promise<void>;
52
55
 
53
- export { type CompilationResult, type CompilerConfig, type ConfigDefinition, type LatencyAnalysisResult, type ValidationResult, analyzeLatency, defineConfig, generateFrontendTypes, validateContract };
56
+ interface CompilerLogger {
57
+ info: (message: string) => void;
58
+ success: (message: string) => void;
59
+ warning: (message: string) => void;
60
+ error: (message: string) => void;
61
+ verbose?: (message: string) => void;
62
+ }
63
+ declare const silentLogger: CompilerLogger;
64
+ declare const consoleLogger: CompilerLogger;
65
+ interface CompileOptions {
66
+ config: CompilerConfig;
67
+ cwd?: string;
68
+ logger?: CompilerLogger;
69
+ file?: string;
70
+ }
71
+ interface CompileResult {
72
+ success: boolean;
73
+ results: CompilationResult[];
74
+ errors: string[];
75
+ warnings: string[];
76
+ }
77
+ declare function parseContractFile(filePath: string, logger?: CompilerLogger): Promise<{
78
+ contracts: Array<{
79
+ name: string;
80
+ contract: Contract;
81
+ }>;
82
+ errors: string[];
83
+ }>;
84
+ declare function compileContract(contract: Contract, config: CompilerConfig, cwd?: string, logger?: CompilerLogger): Promise<CompilationResult>;
85
+ declare function findContractFiles(config: CompilerConfig, cwd?: string): Promise<string[]>;
86
+ declare function compileAll(options: CompileOptions): Promise<CompileResult>;
87
+ declare function isContractFile(filePath: string): boolean;
88
+ declare function getGeneratedFilesForContract(contractName: string, config: CompilerConfig, cwd?: string): {
89
+ frontend: string;
90
+ backend: string;
91
+ runtime: string;
92
+ };
93
+
94
+ export { type CompilationResult, type CompileOptions, type CompileResult, type CompilerConfig, type CompilerLogger, type ConfigDefinition, type LatencyAnalysisResult, type ValidationResult, analyzeLatency, compileAll, compileContract, consoleLogger, defineConfig, findContractFiles, generateBackendResolver, generateFrontendTypes, generateRuntimeNegotiator, getGeneratedFilesForContract, isContractFile, loadConfig, parseContractFile, silentLogger, validateContract };