@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.cjs +489 -20
- package/dist/index.cli.js +112 -45
- package/dist/index.d.cts +42 -1
- package/dist/index.d.ts +42 -1
- package/dist/index.js +458 -20
- package/package.json +3 -3
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
|
|
138
|
-
import {
|
|
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
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
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,
|
|
262
|
+
function validateShape(shape, path5, errors, warnings) {
|
|
199
263
|
if (!shape || typeof shape !== "object") {
|
|
200
|
-
errors.push(`Invalid shape at ${
|
|
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 ${
|
|
269
|
+
warnings.push(`Shape at ${path5 || "root"} is empty`);
|
|
206
270
|
}
|
|
207
271
|
for (const key of keys) {
|
|
208
|
-
const fieldPath =
|
|
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,
|
|
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 ${
|
|
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 ${
|
|
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
|
-
|
|
303
|
+
path5,
|
|
240
304
|
errors,
|
|
241
305
|
warnings
|
|
242
306
|
);
|
|
243
307
|
} else {
|
|
244
|
-
validateShape(type,
|
|
308
|
+
validateShape(type, path5, errors, warnings);
|
|
245
309
|
}
|
|
246
310
|
} else {
|
|
247
|
-
errors.push(`Invalid type at ${
|
|
311
|
+
errors.push(`Invalid type at ${path5}: must be a string, object, or DerivedField`);
|
|
248
312
|
}
|
|
249
313
|
}
|
|
250
|
-
function validateDerivedField(field,
|
|
314
|
+
function validateDerivedField(field, path5, errors, _warnings) {
|
|
251
315
|
if (typeof field.derive !== "function") {
|
|
252
|
-
errors.push(`Derived field at ${
|
|
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 ${
|
|
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 ${
|
|
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 ${
|
|
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
|
|
393
|
-
result.add(
|
|
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,
|
|
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
|
|
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 =
|
|
918
|
+
const fileName = path3.basename(file, ".contract.ts");
|
|
851
919
|
start(`Processing ${fileName}...`);
|
|
852
920
|
try {
|
|
853
|
-
const
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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
|
|
945
|
-
import {
|
|
946
|
-
|
|
947
|
-
|
|
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 =
|
|
1039
|
+
const fileName = path4.basename(file, ".contract.ts");
|
|
971
1040
|
start(`Validating ${fileName}...`);
|
|
972
1041
|
try {
|
|
973
|
-
const
|
|
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
|
-
|
|
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
|
-
|
|
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 };
|