@supacloud/compiler 0.40.1 → 0.42.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/README.md +8 -0
- package/dist/analyze.d.ts +2 -2
- package/dist/cli.js +534 -15
- package/dist/config.d.ts +4 -1
- package/dist/effect-governance.d.ts +3 -0
- package/dist/index.d.ts +2 -1
- package/dist/index.js +535 -15
- package/dist/migration-app-version.d.ts +1 -1
- package/dist/migration-elysia-version.d.ts +1 -1
- package/dist/types.d.ts +38 -0
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -1237,6 +1237,24 @@ var init_validate = __esm(() => {
|
|
|
1237
1237
|
"route-contract-required": { code: "SC3021", docsUrl: "https://supacloud.dev/errors/SC3021" },
|
|
1238
1238
|
"route-contract-unverified": { code: "SC3022", docsUrl: "https://supacloud.dev/errors/SC3022" },
|
|
1239
1239
|
"route-contract-evidence-required": { code: "SC3023", docsUrl: "https://supacloud.dev/errors/SC3023" },
|
|
1240
|
+
"invalid-effect-contract": { code: "SC3030", docsUrl: "https://supacloud.dev/errors/SC3030" },
|
|
1241
|
+
"invalid-effect-retry": { code: "SC3031", docsUrl: "https://supacloud.dev/errors/SC3031" },
|
|
1242
|
+
"invalid-effect-timeout": { code: "SC3032", docsUrl: "https://supacloud.dev/errors/SC3032" },
|
|
1243
|
+
"invalid-effect-dependencies": { code: "SC3033", docsUrl: "https://supacloud.dev/errors/SC3033" },
|
|
1244
|
+
"invalid-effect-errors": { code: "SC3034", docsUrl: "https://supacloud.dev/errors/SC3034" },
|
|
1245
|
+
"effect-return-type": { code: "SC3035", docsUrl: "https://supacloud.dev/errors/SC3035" },
|
|
1246
|
+
"effect-contract-required": { code: "SC3036", docsUrl: "https://supacloud.dev/errors/SC3036" },
|
|
1247
|
+
"effect-error-mapping-required": { code: "SC3037", docsUrl: "https://supacloud.dev/errors/SC3037" },
|
|
1248
|
+
"effect-dependencies-required": { code: "SC3038", docsUrl: "https://supacloud.dev/errors/SC3038" },
|
|
1249
|
+
"effect-command-retry-forbidden": { code: "SC3039", docsUrl: "https://supacloud.dev/errors/SC3039" },
|
|
1250
|
+
"effect-dependency-type": { code: "SC3040", docsUrl: "https://supacloud.dev/errors/SC3040" },
|
|
1251
|
+
"effect-error-type": { code: "SC3041", docsUrl: "https://supacloud.dev/errors/SC3041" },
|
|
1252
|
+
"effect-error-mapping-type": { code: "SC3042", docsUrl: "https://supacloud.dev/errors/SC3042" },
|
|
1253
|
+
"effect-dependency-mismatch": { code: "SC3043", docsUrl: "https://supacloud.dev/errors/SC3043" },
|
|
1254
|
+
"effect-timeout-required": { code: "SC3044", docsUrl: "https://supacloud.dev/errors/SC3044" },
|
|
1255
|
+
"effect-direct-runtime-execution": { code: "SC3045", docsUrl: "https://supacloud.dev/errors/SC3045" },
|
|
1256
|
+
"effect-direct-throw": { code: "SC3046", docsUrl: "https://supacloud.dev/errors/SC3046" },
|
|
1257
|
+
"effect-promise-return": { code: "SC3047", docsUrl: "https://supacloud.dev/errors/SC3047" },
|
|
1240
1258
|
"invalid-route-response-map": { code: "SC3024", docsUrl: "https://supacloud.dev/errors/SC3024" },
|
|
1241
1259
|
"invalid-route-response-selector": { code: "SC3025", docsUrl: "https://supacloud.dev/errors/SC3025" },
|
|
1242
1260
|
"conflicting-route-response-schema": { code: "SC3026", docsUrl: "https://supacloud.dev/errors/SC3026" },
|
|
@@ -1775,6 +1793,9 @@ class ModuleGenerator {
|
|
|
1775
1793
|
});
|
|
1776
1794
|
fields.push(`responses: { ${responseFields.join(", ")} }`);
|
|
1777
1795
|
}
|
|
1796
|
+
if (route.effect !== undefined) {
|
|
1797
|
+
fields.push(`effect: ${JSON.stringify(route.effect)}`);
|
|
1798
|
+
}
|
|
1778
1799
|
if (route.command)
|
|
1779
1800
|
fields.push(`command: ${JSON.stringify(route.command)}`);
|
|
1780
1801
|
if (route.guards && route.guards.length > 0) {
|
|
@@ -3586,6 +3607,14 @@ var HEADER = "// GENERATED BY @supacloud/compiler — do not edit", INTERFACES =
|
|
|
3586
3607
|
cookie?: unknown;
|
|
3587
3608
|
response?: unknown;
|
|
3588
3609
|
responses?: Record<string | number, unknown>;
|
|
3610
|
+
effect?: {
|
|
3611
|
+
required: true;
|
|
3612
|
+
errors?: Array<{ tag: string; status: number; code: string; message?: string }>;
|
|
3613
|
+
dependencies?: string[];
|
|
3614
|
+
retry?: "none" | "explicit";
|
|
3615
|
+
maxAttempts?: number;
|
|
3616
|
+
timeoutMs?: number;
|
|
3617
|
+
};
|
|
3589
3618
|
/** Compile-time ownership and transport classification for the route boundary. */
|
|
3590
3619
|
contract?: {
|
|
3591
3620
|
body?: "framework" | "domain";
|
|
@@ -27793,6 +27822,13 @@ function withDiagnosticMetadata(diagnostic) {
|
|
|
27793
27822
|
const metadata = COMPILER_DIAGNOSTIC_CODES[diagnostic.code];
|
|
27794
27823
|
return metadata ? { ...diagnostic, errorCode: metadata.code, docsUrl: metadata.docsUrl } : diagnostic;
|
|
27795
27824
|
}
|
|
27825
|
+
var DEFAULT_EFFECT_ANALYSIS_OPTIONS = {
|
|
27826
|
+
requireTaggedErrorTypes: true,
|
|
27827
|
+
requireExactDependencyTypes: true,
|
|
27828
|
+
requireTimeoutForDependencies: true,
|
|
27829
|
+
forbidDirectRuntimeExecution: true,
|
|
27830
|
+
forbidDirectThrows: true
|
|
27831
|
+
};
|
|
27796
27832
|
function nodeText(node) {
|
|
27797
27833
|
return node.getText(node.getSourceFile());
|
|
27798
27834
|
}
|
|
@@ -27838,7 +27874,7 @@ function descendantsOfKind(root, predicate) {
|
|
|
27838
27874
|
visit(root);
|
|
27839
27875
|
return result;
|
|
27840
27876
|
}
|
|
27841
|
-
async function analyzeProject(rootDir, include, cache, changedPaths) {
|
|
27877
|
+
async function analyzeProject(rootDir, include, cache, changedPaths, effectOptions = {}) {
|
|
27842
27878
|
const session = cache?.programSession ?? createIncrementalProgramSession(rootDir);
|
|
27843
27879
|
if (cache)
|
|
27844
27880
|
cache.programSession = session;
|
|
@@ -27857,8 +27893,21 @@ async function analyzeProject(rootDir, include, cache, changedPaths) {
|
|
|
27857
27893
|
moduleHandlerFiles: new Map,
|
|
27858
27894
|
infraResources: new Map,
|
|
27859
27895
|
infraResourceClasses: new Map,
|
|
27860
|
-
diagnostics: []
|
|
27896
|
+
diagnostics: [],
|
|
27897
|
+
effectOptions: {
|
|
27898
|
+
...DEFAULT_EFFECT_ANALYSIS_OPTIONS,
|
|
27899
|
+
requireTaggedErrorTypes: effectOptions.requireTaggedErrorTypes ?? DEFAULT_EFFECT_ANALYSIS_OPTIONS.requireTaggedErrorTypes,
|
|
27900
|
+
requireExactDependencyTypes: effectOptions.requireExactDependencyTypes ?? DEFAULT_EFFECT_ANALYSIS_OPTIONS.requireExactDependencyTypes,
|
|
27901
|
+
requireTimeoutForDependencies: effectOptions.requireTimeoutForDependencies ?? DEFAULT_EFFECT_ANALYSIS_OPTIONS.requireTimeoutForDependencies,
|
|
27902
|
+
forbidDirectRuntimeExecution: effectOptions.forbidDirectRuntimeExecution ?? DEFAULT_EFFECT_ANALYSIS_OPTIONS.forbidDirectRuntimeExecution,
|
|
27903
|
+
forbidDirectThrows: effectOptions.forbidDirectThrows ?? DEFAULT_EFFECT_ANALYSIS_OPTIONS.forbidDirectThrows
|
|
27904
|
+
}
|
|
27861
27905
|
};
|
|
27906
|
+
if (ctx.effectOptions.forbidDirectRuntimeExecution) {
|
|
27907
|
+
for (const sourceFile of sourceFiles) {
|
|
27908
|
+
scanEffectRuntimeEscapes(sourceFile, ctx);
|
|
27909
|
+
}
|
|
27910
|
+
}
|
|
27862
27911
|
const nativeTraitFiles = new Map;
|
|
27863
27912
|
for (const diagnostic of session.getDiagnostics()) {
|
|
27864
27913
|
ctx.diagnostics.push(toCompilerDiagnostic(diagnostic, rootDir));
|
|
@@ -29034,11 +29083,150 @@ function declaredClassType(declaration, ctx) {
|
|
|
29034
29083
|
return symbol ? ctx.checker.getDeclaredTypeOfSymbol(symbol) : undefined;
|
|
29035
29084
|
}
|
|
29036
29085
|
function typeArgumentsOf(type, ctx) {
|
|
29037
|
-
|
|
29086
|
+
if (!isTypeReference(type))
|
|
29087
|
+
return [];
|
|
29088
|
+
const argumentsFromChecker = ctx.checker.getTypeArguments(type);
|
|
29089
|
+
if (argumentsFromChecker.length > 0)
|
|
29090
|
+
return argumentsFromChecker;
|
|
29091
|
+
const reference = type;
|
|
29092
|
+
if (reference.resolvedTypeArguments && reference.resolvedTypeArguments.length > 0) {
|
|
29093
|
+
return reference.resolvedTypeArguments;
|
|
29094
|
+
}
|
|
29095
|
+
return reference.aliasTypeArguments ?? [];
|
|
29038
29096
|
}
|
|
29039
29097
|
function isTypeReference(type) {
|
|
29040
29098
|
return "target" in type;
|
|
29041
29099
|
}
|
|
29100
|
+
function effectTypeArgumentsOf(type, ctx) {
|
|
29101
|
+
if (!type)
|
|
29102
|
+
return;
|
|
29103
|
+
const reference = isTypeReference(type) ? type : undefined;
|
|
29104
|
+
const target = reference?.target ?? type;
|
|
29105
|
+
const symbol = target.aliasSymbol ?? target.getSymbol();
|
|
29106
|
+
const text = ctx.checker.typeToString(type, undefined, ts7.TypeFormatFlags.NoTruncation);
|
|
29107
|
+
if (symbol?.getName() !== "Effect" && !/^(?:Effect\.)?Effect</.test(text))
|
|
29108
|
+
return;
|
|
29109
|
+
const args = typeArgumentsOf(type, ctx);
|
|
29110
|
+
return args.length >= 3 ? [args[0], args[1], args[2]] : undefined;
|
|
29111
|
+
}
|
|
29112
|
+
function effectTypeName(type, location, ctx) {
|
|
29113
|
+
return ctx.checker.typeToString(type, location, ts7.TypeFormatFlags.NoTruncation);
|
|
29114
|
+
}
|
|
29115
|
+
function stringLiteralTypeValue(type) {
|
|
29116
|
+
return (type.flags & ts7.TypeFlags.StringLiteral) !== 0 ? type.value : undefined;
|
|
29117
|
+
}
|
|
29118
|
+
function effectFailureTags(type, location, ctx) {
|
|
29119
|
+
if ((type.flags & ts7.TypeFlags.Never) !== 0)
|
|
29120
|
+
return { tags: [], invalid: false };
|
|
29121
|
+
if ((type.flags & (ts7.TypeFlags.Any | ts7.TypeFlags.Unknown)) !== 0) {
|
|
29122
|
+
return { tags: [], invalid: true };
|
|
29123
|
+
}
|
|
29124
|
+
if (type.isUnion()) {
|
|
29125
|
+
return type.types.reduce((result, part) => {
|
|
29126
|
+
const next = effectFailureTags(part, location, ctx);
|
|
29127
|
+
result.tags.push(...next.tags);
|
|
29128
|
+
result.invalid ||= next.invalid;
|
|
29129
|
+
return result;
|
|
29130
|
+
}, { tags: [], invalid: false });
|
|
29131
|
+
}
|
|
29132
|
+
const apparent = ctx.checker.getApparentType(type);
|
|
29133
|
+
for (const propertyName of ["_tag", "tag"]) {
|
|
29134
|
+
const property = ctx.checker.getPropertyOfType(apparent, propertyName);
|
|
29135
|
+
if (!property)
|
|
29136
|
+
continue;
|
|
29137
|
+
const propertyType = ctx.checker.getTypeOfSymbolAtLocation(property, location);
|
|
29138
|
+
const tag = stringLiteralTypeValue(propertyType);
|
|
29139
|
+
if (tag)
|
|
29140
|
+
return { tags: [tag], invalid: false };
|
|
29141
|
+
}
|
|
29142
|
+
return { tags: [], invalid: true };
|
|
29143
|
+
}
|
|
29144
|
+
function effectEnvironmentNames(type, location, ctx) {
|
|
29145
|
+
if ((type.flags & ts7.TypeFlags.Never) !== 0)
|
|
29146
|
+
return { names: [], invalid: false };
|
|
29147
|
+
if ((type.flags & (ts7.TypeFlags.Any | ts7.TypeFlags.Unknown)) !== 0) {
|
|
29148
|
+
return { names: [], invalid: true };
|
|
29149
|
+
}
|
|
29150
|
+
if (type.isUnion() || type.isIntersection()) {
|
|
29151
|
+
return type.types.reduce((result, part) => {
|
|
29152
|
+
const next = effectEnvironmentNames(part, location, ctx);
|
|
29153
|
+
result.names.push(...next.names);
|
|
29154
|
+
result.invalid ||= next.invalid;
|
|
29155
|
+
return result;
|
|
29156
|
+
}, { names: [], invalid: false });
|
|
29157
|
+
}
|
|
29158
|
+
const literal = stringLiteralTypeValue(type);
|
|
29159
|
+
if (literal)
|
|
29160
|
+
return { names: [literal], invalid: false };
|
|
29161
|
+
const symbolName = type.getSymbol()?.getName();
|
|
29162
|
+
if (symbolName && symbolName !== "__type" && symbolName !== "__object") {
|
|
29163
|
+
return { names: [symbolName], invalid: false };
|
|
29164
|
+
}
|
|
29165
|
+
const text = effectTypeName(type, location, ctx).trim();
|
|
29166
|
+
if (text && text !== "object")
|
|
29167
|
+
return { names: [text], invalid: false };
|
|
29168
|
+
return { names: [], invalid: true };
|
|
29169
|
+
}
|
|
29170
|
+
function sortedUnique(values) {
|
|
29171
|
+
return [...new Set(values)].sort((left, right) => left.localeCompare(right, "en"));
|
|
29172
|
+
}
|
|
29173
|
+
function pushEffectDiagnostic(ctx, code, file, line, message, suggestion) {
|
|
29174
|
+
const metadata = COMPILER_DIAGNOSTIC_CODES[code];
|
|
29175
|
+
ctx.diagnostics.push({
|
|
29176
|
+
severity: "error",
|
|
29177
|
+
code,
|
|
29178
|
+
...metadata ? { errorCode: metadata.code, docsUrl: metadata.docsUrl } : {},
|
|
29179
|
+
file,
|
|
29180
|
+
line,
|
|
29181
|
+
message,
|
|
29182
|
+
suggestion
|
|
29183
|
+
});
|
|
29184
|
+
}
|
|
29185
|
+
function scanEffectRuntimeEscapes(sourceFile, ctx) {
|
|
29186
|
+
const relativeFile = sourcePath(ctx.rootDir, sourceFile.fileName);
|
|
29187
|
+
if (/\.(?:test|spec)\.[cm]?[jt]sx?$/.test(relativeFile))
|
|
29188
|
+
return;
|
|
29189
|
+
const namespaceBindings = new Set;
|
|
29190
|
+
const runtimeBindings = new Set;
|
|
29191
|
+
for (const statement of sourceFile.statements) {
|
|
29192
|
+
if (!ts7.isImportDeclaration(statement) || !ts7.isStringLiteral(statement.moduleSpecifier))
|
|
29193
|
+
continue;
|
|
29194
|
+
const moduleName = statement.moduleSpecifier.text;
|
|
29195
|
+
if (moduleName !== "effect" && moduleName !== "effect/Effect" && moduleName !== "effect/Runtime")
|
|
29196
|
+
continue;
|
|
29197
|
+
const clause = statement.importClause;
|
|
29198
|
+
if (!clause)
|
|
29199
|
+
continue;
|
|
29200
|
+
if (clause.name)
|
|
29201
|
+
namespaceBindings.add(clause.name.text);
|
|
29202
|
+
if (clause.namedBindings && ts7.isNamespaceImport(clause.namedBindings)) {
|
|
29203
|
+
namespaceBindings.add(clause.namedBindings.name.text);
|
|
29204
|
+
} else if (clause.namedBindings && ts7.isNamedImports(clause.namedBindings)) {
|
|
29205
|
+
for (const element of clause.namedBindings.elements) {
|
|
29206
|
+
const imported = element.propertyName?.text ?? element.name.text;
|
|
29207
|
+
if (moduleName === "effect" && (imported === "Effect" || imported === "Runtime")) {
|
|
29208
|
+
namespaceBindings.add(element.name.text);
|
|
29209
|
+
}
|
|
29210
|
+
if (["runPromise", "runPromiseExit", "runSync", "runFork"].includes(imported)) {
|
|
29211
|
+
runtimeBindings.add(element.name.text);
|
|
29212
|
+
}
|
|
29213
|
+
}
|
|
29214
|
+
}
|
|
29215
|
+
}
|
|
29216
|
+
if (namespaceBindings.size === 0 && runtimeBindings.size === 0)
|
|
29217
|
+
return;
|
|
29218
|
+
for (const call of descendantsOfKind(sourceFile, ts7.isCallExpression)) {
|
|
29219
|
+
let runtimeName;
|
|
29220
|
+
if (ts7.isPropertyAccessExpression(call.expression) && namespaceBindings.has(call.expression.expression.getText(sourceFile)) && ["runPromise", "runPromiseExit", "runSync", "runFork"].includes(call.expression.name.text)) {
|
|
29221
|
+
runtimeName = call.expression.name.text;
|
|
29222
|
+
} else if (ts7.isIdentifier(call.expression) && runtimeBindings.has(call.expression.text)) {
|
|
29223
|
+
runtimeName = call.expression.text;
|
|
29224
|
+
}
|
|
29225
|
+
if (!runtimeName)
|
|
29226
|
+
continue;
|
|
29227
|
+
pushEffectDiagnostic(ctx, "effect-direct-runtime-execution", sourcePath(ctx.rootDir, sourceFile.fileName), lineOf(call), `Direct ${runtimeName} execution escapes the Effect boundary.`, "Return the Effect program to the Elysia boundary; only the framework adapter may interpret it.");
|
|
29228
|
+
}
|
|
29229
|
+
}
|
|
29042
29230
|
function isUnknownOrAny(type) {
|
|
29043
29231
|
return (type.flags & (ts7.TypeFlags.Any | ts7.TypeFlags.Unknown)) !== 0;
|
|
29044
29232
|
}
|
|
@@ -29102,7 +29290,8 @@ function parseController(input, ctx) {
|
|
|
29102
29290
|
handler: propertyName(method.name)
|
|
29103
29291
|
};
|
|
29104
29292
|
const signature = ctx.checker.getSignatureFromDeclaration(method);
|
|
29105
|
-
const
|
|
29293
|
+
const returnType = signature?.getReturnType();
|
|
29294
|
+
const resultType = returnType && ctx.checker.typeToString(returnType);
|
|
29106
29295
|
if (resultType && /\bResponse\b/.test(resultType))
|
|
29107
29296
|
route.nativeResponse = true;
|
|
29108
29297
|
const pathParams = [];
|
|
@@ -29330,6 +29519,235 @@ function parseController(input, ctx) {
|
|
|
29330
29519
|
Object.assign(route.contract, { [field]: value });
|
|
29331
29520
|
}
|
|
29332
29521
|
}
|
|
29522
|
+
const effectExpr = getProp(optionsObject, "effect");
|
|
29523
|
+
if (effectExpr) {
|
|
29524
|
+
const effectObject = resolveStaticObjectLiteral(effectExpr, ctx);
|
|
29525
|
+
if (!effectObject) {
|
|
29526
|
+
ctx.diagnostics.push({
|
|
29527
|
+
severity: "error",
|
|
29528
|
+
code: "invalid-effect-contract",
|
|
29529
|
+
errorCode: "SC3030",
|
|
29530
|
+
docsUrl: "https://supacloud.dev/errors/SC3030",
|
|
29531
|
+
file,
|
|
29532
|
+
line: lineOf(effectExpr),
|
|
29533
|
+
message: `Route ${route.handler} effect must be a statically resolvable object literal.`,
|
|
29534
|
+
suggestion: "Use effect: { required: true, errors: [...], dependencies: [...] }."
|
|
29535
|
+
});
|
|
29536
|
+
} else {
|
|
29537
|
+
const effect = { required: true };
|
|
29538
|
+
const requiredExpr = getProp(effectObject, "required");
|
|
29539
|
+
if (requiredExpr !== undefined && booleanProp(effectObject, "required") !== true) {
|
|
29540
|
+
ctx.diagnostics.push({
|
|
29541
|
+
severity: "error",
|
|
29542
|
+
code: "invalid-effect-contract",
|
|
29543
|
+
errorCode: "SC3030",
|
|
29544
|
+
docsUrl: "https://supacloud.dev/errors/SC3030",
|
|
29545
|
+
file,
|
|
29546
|
+
line: lineOf(requiredExpr),
|
|
29547
|
+
message: `Route ${route.handler} effect.required must be the literal true.`
|
|
29548
|
+
});
|
|
29549
|
+
}
|
|
29550
|
+
const retry = stringLiteralProp(effectObject, "retry");
|
|
29551
|
+
if (retry !== undefined) {
|
|
29552
|
+
if (retry !== "none" && retry !== "explicit") {
|
|
29553
|
+
ctx.diagnostics.push({
|
|
29554
|
+
severity: "error",
|
|
29555
|
+
code: "invalid-effect-retry",
|
|
29556
|
+
errorCode: "SC3031",
|
|
29557
|
+
docsUrl: "https://supacloud.dev/errors/SC3031",
|
|
29558
|
+
file,
|
|
29559
|
+
line: lineOf(getProp(effectObject, "retry")),
|
|
29560
|
+
message: `Route ${route.handler} effect.retry must be "none" or "explicit".`
|
|
29561
|
+
});
|
|
29562
|
+
} else {
|
|
29563
|
+
effect.retry = retry;
|
|
29564
|
+
}
|
|
29565
|
+
}
|
|
29566
|
+
const maxAttemptsExpr = getProp(effectObject, "maxAttempts");
|
|
29567
|
+
if (maxAttemptsExpr !== undefined) {
|
|
29568
|
+
const maxAttempts = numberLiteralProp(effectObject, "maxAttempts");
|
|
29569
|
+
if (maxAttempts === undefined || maxAttempts < 1 || !Number.isInteger(maxAttempts)) {
|
|
29570
|
+
ctx.diagnostics.push({
|
|
29571
|
+
severity: "error",
|
|
29572
|
+
code: "invalid-effect-retry",
|
|
29573
|
+
errorCode: "SC3031",
|
|
29574
|
+
docsUrl: "https://supacloud.dev/errors/SC3031",
|
|
29575
|
+
file,
|
|
29576
|
+
line: lineOf(maxAttemptsExpr),
|
|
29577
|
+
message: `Route ${route.handler} effect.maxAttempts must be a positive integer.`
|
|
29578
|
+
});
|
|
29579
|
+
} else {
|
|
29580
|
+
effect.maxAttempts = maxAttempts;
|
|
29581
|
+
}
|
|
29582
|
+
}
|
|
29583
|
+
if (effect.retry === "explicit" && effect.maxAttempts === undefined) {
|
|
29584
|
+
ctx.diagnostics.push({
|
|
29585
|
+
severity: "error",
|
|
29586
|
+
code: "invalid-effect-retry",
|
|
29587
|
+
errorCode: "SC3031",
|
|
29588
|
+
docsUrl: "https://supacloud.dev/errors/SC3031",
|
|
29589
|
+
file,
|
|
29590
|
+
line: lineOf(effectExpr),
|
|
29591
|
+
message: `Route ${route.handler} effect.maxAttempts is required when retry is "explicit".`
|
|
29592
|
+
});
|
|
29593
|
+
}
|
|
29594
|
+
const timeoutExpr = getProp(effectObject, "timeoutMs");
|
|
29595
|
+
if (timeoutExpr !== undefined) {
|
|
29596
|
+
const timeoutMs = numberLiteralProp(effectObject, "timeoutMs");
|
|
29597
|
+
if (timeoutMs === undefined || timeoutMs <= 0 || !Number.isInteger(timeoutMs)) {
|
|
29598
|
+
ctx.diagnostics.push({
|
|
29599
|
+
severity: "error",
|
|
29600
|
+
code: "invalid-effect-timeout",
|
|
29601
|
+
errorCode: "SC3032",
|
|
29602
|
+
docsUrl: "https://supacloud.dev/errors/SC3032",
|
|
29603
|
+
file,
|
|
29604
|
+
line: lineOf(timeoutExpr),
|
|
29605
|
+
message: `Route ${route.handler} effect.timeoutMs must be a positive integer.`
|
|
29606
|
+
});
|
|
29607
|
+
} else {
|
|
29608
|
+
effect.timeoutMs = timeoutMs;
|
|
29609
|
+
}
|
|
29610
|
+
}
|
|
29611
|
+
const dependenciesExpr = getProp(effectObject, "dependencies");
|
|
29612
|
+
if (dependenciesExpr !== undefined) {
|
|
29613
|
+
if (!ts7.isArrayLiteralExpression(dependenciesExpr) || dependenciesExpr.elements.some((element) => !ts7.isStringLiteral(element) || element.text.trim().length === 0)) {
|
|
29614
|
+
ctx.diagnostics.push({
|
|
29615
|
+
severity: "error",
|
|
29616
|
+
code: "invalid-effect-dependencies",
|
|
29617
|
+
errorCode: "SC3033",
|
|
29618
|
+
docsUrl: "https://supacloud.dev/errors/SC3033",
|
|
29619
|
+
file,
|
|
29620
|
+
line: lineOf(dependenciesExpr),
|
|
29621
|
+
message: `Route ${route.handler} effect.dependencies must be an explicit string array.`
|
|
29622
|
+
});
|
|
29623
|
+
} else {
|
|
29624
|
+
effect.dependencies = dependenciesExpr.elements.map((element) => element.text);
|
|
29625
|
+
if (new Set(effect.dependencies).size !== effect.dependencies.length) {
|
|
29626
|
+
ctx.diagnostics.push({
|
|
29627
|
+
severity: "error",
|
|
29628
|
+
code: "invalid-effect-dependencies",
|
|
29629
|
+
errorCode: "SC3033",
|
|
29630
|
+
docsUrl: "https://supacloud.dev/errors/SC3033",
|
|
29631
|
+
file,
|
|
29632
|
+
line: lineOf(dependenciesExpr),
|
|
29633
|
+
message: `Route ${route.handler} effect.dependencies must not contain duplicate service names.`,
|
|
29634
|
+
suggestion: "List each logical Effect dependency exactly once."
|
|
29635
|
+
});
|
|
29636
|
+
}
|
|
29637
|
+
}
|
|
29638
|
+
}
|
|
29639
|
+
const errorsExpr = getProp(effectObject, "errors");
|
|
29640
|
+
if (errorsExpr !== undefined) {
|
|
29641
|
+
if (!ts7.isArrayLiteralExpression(errorsExpr)) {
|
|
29642
|
+
ctx.diagnostics.push({
|
|
29643
|
+
severity: "error",
|
|
29644
|
+
code: "invalid-effect-errors",
|
|
29645
|
+
errorCode: "SC3034",
|
|
29646
|
+
docsUrl: "https://supacloud.dev/errors/SC3034",
|
|
29647
|
+
file,
|
|
29648
|
+
line: lineOf(errorsExpr),
|
|
29649
|
+
message: `Route ${route.handler} effect.errors must be an explicit object array.`
|
|
29650
|
+
});
|
|
29651
|
+
} else {
|
|
29652
|
+
const errors = [];
|
|
29653
|
+
for (const errorExpr of errorsExpr.elements) {
|
|
29654
|
+
if (!ts7.isObjectLiteralExpression(errorExpr)) {
|
|
29655
|
+
ctx.diagnostics.push({
|
|
29656
|
+
severity: "error",
|
|
29657
|
+
code: "invalid-effect-errors",
|
|
29658
|
+
errorCode: "SC3034",
|
|
29659
|
+
docsUrl: "https://supacloud.dev/errors/SC3034",
|
|
29660
|
+
file,
|
|
29661
|
+
line: lineOf(errorExpr),
|
|
29662
|
+
message: `Route ${route.handler} effect.errors entries must be object literals.`
|
|
29663
|
+
});
|
|
29664
|
+
continue;
|
|
29665
|
+
}
|
|
29666
|
+
const tag = stringLiteralProp(errorExpr, "tag");
|
|
29667
|
+
const code = stringLiteralProp(errorExpr, "code");
|
|
29668
|
+
const status = numberLiteralProp(errorExpr, "status");
|
|
29669
|
+
const message = stringLiteralProp(errorExpr, "message");
|
|
29670
|
+
if (!tag || !code || status === undefined || !Number.isInteger(status) || status < 400 || status > 599) {
|
|
29671
|
+
ctx.diagnostics.push({
|
|
29672
|
+
severity: "error",
|
|
29673
|
+
code: "invalid-effect-errors",
|
|
29674
|
+
errorCode: "SC3034",
|
|
29675
|
+
docsUrl: "https://supacloud.dev/errors/SC3034",
|
|
29676
|
+
file,
|
|
29677
|
+
line: lineOf(errorExpr),
|
|
29678
|
+
message: `Route ${route.handler} effect.errors entries require tag, code and an HTTP status from 400 to 599.`
|
|
29679
|
+
});
|
|
29680
|
+
continue;
|
|
29681
|
+
}
|
|
29682
|
+
errors.push({ tag, code, status, ...message === undefined ? {} : { message } });
|
|
29683
|
+
}
|
|
29684
|
+
effect.errors = errors;
|
|
29685
|
+
if (new Set(errors.map((entry) => entry.tag)).size !== errors.length) {
|
|
29686
|
+
ctx.diagnostics.push({
|
|
29687
|
+
severity: "error",
|
|
29688
|
+
code: "invalid-effect-errors",
|
|
29689
|
+
errorCode: "SC3034",
|
|
29690
|
+
docsUrl: "https://supacloud.dev/errors/SC3034",
|
|
29691
|
+
file,
|
|
29692
|
+
line: lineOf(errorsExpr),
|
|
29693
|
+
message: `Route ${route.handler} effect.errors must not contain duplicate tags.`,
|
|
29694
|
+
suggestion: "Map each tagged failure exactly once."
|
|
29695
|
+
});
|
|
29696
|
+
}
|
|
29697
|
+
}
|
|
29698
|
+
}
|
|
29699
|
+
route.effect = effect;
|
|
29700
|
+
const effectArguments = effectTypeArgumentsOf(returnType, ctx);
|
|
29701
|
+
const promiseReturn = Boolean(resultType && /\bPromise(?:<|\s)/.test(resultType));
|
|
29702
|
+
if (promiseReturn) {
|
|
29703
|
+
pushEffectDiagnostic(ctx, "effect-promise-return", file, lineOf(method), `Route ${route.handler} returns Promise instead of an Effect program.`, "Return Effect.Effect<Success, Failure, Requirements>; the Elysia boundary owns Promise interpretation.");
|
|
29704
|
+
} else if (!effectArguments) {
|
|
29705
|
+
ctx.diagnostics.push({
|
|
29706
|
+
severity: "error",
|
|
29707
|
+
code: "effect-return-type",
|
|
29708
|
+
errorCode: "SC3035",
|
|
29709
|
+
docsUrl: "https://supacloud.dev/errors/SC3035",
|
|
29710
|
+
file,
|
|
29711
|
+
line: lineOf(method),
|
|
29712
|
+
message: `Route ${route.handler} declares an Effect contract but returns ${resultType ?? "an unresolved type"}.`,
|
|
29713
|
+
suggestion: "Return Effect.Effect<Success, Failure, Requirements> from the handler."
|
|
29714
|
+
});
|
|
29715
|
+
}
|
|
29716
|
+
if (effectArguments) {
|
|
29717
|
+
const [, failureType, environmentType] = effectArguments;
|
|
29718
|
+
const failure = effectFailureTags(failureType, method, ctx);
|
|
29719
|
+
if (ctx.effectOptions.requireTaggedErrorTypes && failure.invalid) {
|
|
29720
|
+
pushEffectDiagnostic(ctx, "effect-error-type", file, lineOf(method), `Route ${route.handler} must use never or a tagged failure union in Effect's error channel, not ${effectTypeName(failureType, method, ctx)}.`, "Replace unknown/any with never or a union of objects carrying a literal _tag.");
|
|
29721
|
+
} else if (ctx.effectOptions.requireTaggedErrorTypes && effect.errors !== undefined && !failure.invalid) {
|
|
29722
|
+
const declaredTags = sortedUnique(effect.errors.map((entry) => entry.tag));
|
|
29723
|
+
const typedTags = sortedUnique(failure.tags);
|
|
29724
|
+
if (declaredTags.join("\x00") !== typedTags.join("\x00")) {
|
|
29725
|
+
pushEffectDiagnostic(ctx, "effect-error-mapping-type", file, lineOf(method), `Route ${route.handler} maps [${declaredTags.join(", ")}], but its Effect failure type declares [${typedTags.join(", ")}].`, "Make effect.errors and the Effect error type the same tagged union; do not hide unmapped failures.");
|
|
29726
|
+
}
|
|
29727
|
+
}
|
|
29728
|
+
const environment = effectEnvironmentNames(environmentType, method, ctx);
|
|
29729
|
+
if (ctx.effectOptions.requireExactDependencyTypes && effect.dependencies !== undefined) {
|
|
29730
|
+
if (environment.invalid) {
|
|
29731
|
+
pushEffectDiagnostic(ctx, "effect-dependency-mismatch", file, lineOf(method), `Route ${route.handler} uses an Effect environment that cannot be represented by its explicit dependency list.`, "Use named service types or never in the third Effect type parameter.");
|
|
29732
|
+
} else {
|
|
29733
|
+
const declaredDependencies = sortedUnique(effect.dependencies);
|
|
29734
|
+
const typedDependencies = sortedUnique(environment.names);
|
|
29735
|
+
if (declaredDependencies.join("\x00") !== typedDependencies.join("\x00")) {
|
|
29736
|
+
pushEffectDiagnostic(ctx, "effect-dependency-mismatch", file, lineOf(method), `Route ${route.handler} declares dependencies [${declaredDependencies.join(", ")}], but its Effect environment is [${typedDependencies.join(", ")}].`, "Make effect.dependencies exactly match the third Effect type parameter.");
|
|
29737
|
+
}
|
|
29738
|
+
}
|
|
29739
|
+
}
|
|
29740
|
+
if (ctx.effectOptions.requireTimeoutForDependencies && (effect.dependencies?.length ?? 0) > 0 && effect.timeoutMs === undefined) {
|
|
29741
|
+
pushEffectDiagnostic(ctx, "effect-timeout-required", file, lineOf(method), `Route ${route.handler} declares external Effect dependencies without a timeout.`, "Add a positive effect.timeoutMs so external I/O cannot occupy a request indefinitely.");
|
|
29742
|
+
}
|
|
29743
|
+
}
|
|
29744
|
+
if (ctx.effectOptions.forbidDirectThrows && method.body) {
|
|
29745
|
+
for (const throwStatement of descendantsOfKind(method.body, ts7.isThrowStatement)) {
|
|
29746
|
+
pushEffectDiagnostic(ctx, "effect-direct-throw", file, lineOf(throwStatement), `Route ${route.handler} throws directly instead of representing failure in Effect.`, "Use Effect.fail with a tagged error value and map it in effect.errors.");
|
|
29747
|
+
}
|
|
29748
|
+
}
|
|
29749
|
+
}
|
|
29750
|
+
}
|
|
29333
29751
|
for (const field of ["body", "params", "query", "headers", "cookie", "response"]) {
|
|
29334
29752
|
const schemaExpr = getProp(optionsObject, field);
|
|
29335
29753
|
if (schemaExpr && ts7.isIdentifier(schemaExpr)) {
|
|
@@ -29913,6 +30331,13 @@ function stringLiteralProp(obj, name) {
|
|
|
29913
30331
|
const expr = getProp(obj, name);
|
|
29914
30332
|
return expr && ts7.isStringLiteral(expr) ? expr.text : undefined;
|
|
29915
30333
|
}
|
|
30334
|
+
function numberLiteralProp(obj, name) {
|
|
30335
|
+
const expr = getProp(obj, name);
|
|
30336
|
+
if (!expr || !ts7.isNumericLiteral(expr))
|
|
30337
|
+
return;
|
|
30338
|
+
const value = Number(expr.text);
|
|
30339
|
+
return Number.isFinite(value) ? value : undefined;
|
|
30340
|
+
}
|
|
29916
30341
|
function arrayProp(obj, name) {
|
|
29917
30342
|
const expr = getProp(obj, name);
|
|
29918
30343
|
return expr && ts7.isArrayLiteralExpression(expr) ? [...expr.elements] : [];
|
|
@@ -30574,6 +30999,57 @@ function validateClientSchemaBoundaries(graph, options) {
|
|
|
30574
30999
|
return diagnostics;
|
|
30575
31000
|
}
|
|
30576
31001
|
|
|
31002
|
+
// src/effect-governance.ts
|
|
31003
|
+
init_validate();
|
|
31004
|
+
function diagnostic(code, message, file, suggestion) {
|
|
31005
|
+
const metadata = COMPILER_DIAGNOSTIC_CODES[code];
|
|
31006
|
+
return {
|
|
31007
|
+
severity: "error",
|
|
31008
|
+
code,
|
|
31009
|
+
message,
|
|
31010
|
+
...file === undefined ? {} : { file },
|
|
31011
|
+
...suggestion === undefined ? {} : { suggestion },
|
|
31012
|
+
...metadata ? { errorCode: metadata.code, docsUrl: metadata.docsUrl } : {}
|
|
31013
|
+
};
|
|
31014
|
+
}
|
|
31015
|
+
function routeLabel(route, controllerPath) {
|
|
31016
|
+
return `${route.method} ${controllerPath}${route.path}`;
|
|
31017
|
+
}
|
|
31018
|
+
function validateEffectPolicies(graph, options = {}) {
|
|
31019
|
+
const diagnostics = [];
|
|
31020
|
+
for (const module of graph.modules) {
|
|
31021
|
+
const commands = new Map(module.commands.map((command) => [command.className, command]));
|
|
31022
|
+
for (const controller of module.controllers) {
|
|
31023
|
+
for (const route of controller.routes) {
|
|
31024
|
+
const label = routeLabel(route, controller.path);
|
|
31025
|
+
if (options.requireRouteEffects && route.effect === undefined) {
|
|
31026
|
+
diagnostics.push(diagnostic("effect-contract-required", `Route ${label} must declare an Effect contract.`, controller.file, 'Add effect: { dependencies: [...], errors: [...], retry: "none" } to the route options.'));
|
|
31027
|
+
continue;
|
|
31028
|
+
}
|
|
31029
|
+
const effect = route.effect;
|
|
31030
|
+
if (effect === undefined)
|
|
31031
|
+
continue;
|
|
31032
|
+
if (options.requireErrorMappings && effect.errors === undefined) {
|
|
31033
|
+
diagnostics.push(diagnostic("effect-error-mapping-required", `Route ${label} declares an Effect contract without public error mappings.`, controller.file, "Map every expected domain failure to a stable HTTP status and public code."));
|
|
31034
|
+
}
|
|
31035
|
+
if (options.requireDependencies && effect.dependencies === undefined) {
|
|
31036
|
+
diagnostics.push(diagnostic("effect-dependencies-required", `Route ${label} declares an Effect contract without dependencies.`, controller.file, "Declare the logical services required by the Effect environment."));
|
|
31037
|
+
}
|
|
31038
|
+
if (effect.retry === "explicit" && effect.maxAttempts === undefined) {
|
|
31039
|
+
diagnostics.push(diagnostic("invalid-effect-retry", `Route ${label} enables explicit Effect retry without maxAttempts.`, controller.file, "Declare a positive maxAttempts value including the initial attempt."));
|
|
31040
|
+
}
|
|
31041
|
+
if (route.command && effect.retry === "explicit") {
|
|
31042
|
+
const command = commands.get(route.command);
|
|
31043
|
+
if (command && command.idempotency !== "required") {
|
|
31044
|
+
diagnostics.push(diagnostic("effect-command-retry-forbidden", `Route ${label} enables explicit Effect retry for non-idempotent command "${command.name}".`, controller.file, 'Set command idempotency to "required" or use effect.retry: "none".'));
|
|
31045
|
+
}
|
|
31046
|
+
}
|
|
31047
|
+
}
|
|
31048
|
+
}
|
|
31049
|
+
}
|
|
31050
|
+
return diagnostics;
|
|
31051
|
+
}
|
|
31052
|
+
|
|
30577
31053
|
// src/compile.ts
|
|
30578
31054
|
function withDefaultGovernance(options) {
|
|
30579
31055
|
return options.commandCapabilities === undefined ? { ...options, commandCapabilities: {
|
|
@@ -30589,11 +31065,12 @@ async function renderOptionalGraphql(options) {
|
|
|
30589
31065
|
}
|
|
30590
31066
|
async function compileProject(options) {
|
|
30591
31067
|
options = withDefaultGovernance(options);
|
|
30592
|
-
const graph = await analyzeProject(options.rootDir, options.include, options.cache, options.changedPaths);
|
|
31068
|
+
const graph = await analyzeProject(options.rootDir, options.include, options.cache, options.changedPaths, options.effect);
|
|
30593
31069
|
const diagnostics = [
|
|
30594
31070
|
...graph.diagnostics ?? [],
|
|
30595
31071
|
...validateGraph(graph, options),
|
|
30596
|
-
...validateClientSchemaBoundaries(graph, options)
|
|
31072
|
+
...validateClientSchemaBoundaries(graph, options),
|
|
31073
|
+
...validateEffectPolicies(graph, options.effect)
|
|
30597
31074
|
];
|
|
30598
31075
|
if (diagnostics.some((item) => item.code === "runtime-injection-disallowed")) {
|
|
30599
31076
|
return { diagnostics, graph, written: [] };
|
|
@@ -30653,11 +31130,12 @@ async function compileProject(options) {
|
|
|
30653
31130
|
}
|
|
30654
31131
|
async function checkProject(options) {
|
|
30655
31132
|
options = withDefaultGovernance(options);
|
|
30656
|
-
const graph = await analyzeProject(options.rootDir, options.include, options.cache, options.changedPaths);
|
|
31133
|
+
const graph = await analyzeProject(options.rootDir, options.include, options.cache, options.changedPaths, options.effect);
|
|
30657
31134
|
const diagnostics = [
|
|
30658
31135
|
...graph.diagnostics ?? [],
|
|
30659
31136
|
...validateGraph(graph, options),
|
|
30660
|
-
...validateClientSchemaBoundaries(graph, options)
|
|
31137
|
+
...validateClientSchemaBoundaries(graph, options),
|
|
31138
|
+
...validateEffectPolicies(graph, options.effect)
|
|
30661
31139
|
];
|
|
30662
31140
|
if (diagnostics.some((item) => item.code === "runtime-injection-disallowed")) {
|
|
30663
31141
|
return { diagnostics, graph, upToDate: false, mismatches: ["Runtime DI must be migrated to constructor injection."] };
|
|
@@ -31140,6 +31618,7 @@ function optionsKeyOf(options) {
|
|
|
31140
31618
|
openApi: options.openApi,
|
|
31141
31619
|
generatePermissions: options.generatePermissions,
|
|
31142
31620
|
typeSafety: options.typeSafety,
|
|
31621
|
+
effect: options.effect,
|
|
31143
31622
|
treeShakeUnusedProviders: options.treeShakeUnusedProviders,
|
|
31144
31623
|
graphql: options.graphql
|
|
31145
31624
|
});
|
|
@@ -31467,7 +31946,17 @@ var DEFAULT_SUPACLOUD_CONFIG = {
|
|
|
31467
31946
|
idempotency: true,
|
|
31468
31947
|
transaction: true
|
|
31469
31948
|
},
|
|
31470
|
-
moduleBoundaryPreset: "modular-monolith"
|
|
31949
|
+
moduleBoundaryPreset: "modular-monolith",
|
|
31950
|
+
effect: {
|
|
31951
|
+
requireRouteEffects: true,
|
|
31952
|
+
requireErrorMappings: true,
|
|
31953
|
+
requireDependencies: true,
|
|
31954
|
+
requireTaggedErrorTypes: true,
|
|
31955
|
+
requireExactDependencyTypes: true,
|
|
31956
|
+
requireTimeoutForDependencies: true,
|
|
31957
|
+
forbidDirectRuntimeExecution: true,
|
|
31958
|
+
forbidDirectThrows: true
|
|
31959
|
+
}
|
|
31471
31960
|
};
|
|
31472
31961
|
function defineSupacloudConfig(config = {}) {
|
|
31473
31962
|
if (config.delivery !== undefined)
|
|
@@ -31479,7 +31968,8 @@ function defineSupacloudConfig(config = {}) {
|
|
|
31479
31968
|
...DEFAULT_SUPACLOUD_CONFIG,
|
|
31480
31969
|
...config,
|
|
31481
31970
|
include: config.include ?? [...DEFAULT_SUPACLOUD_CONFIG.include],
|
|
31482
|
-
graphql: config.graphql ?? DEFAULT_SUPACLOUD_CONFIG.graphql
|
|
31971
|
+
graphql: config.graphql ?? DEFAULT_SUPACLOUD_CONFIG.graphql,
|
|
31972
|
+
effect: config.effect === undefined ? { ...DEFAULT_SUPACLOUD_CONFIG.effect } : { ...DEFAULT_SUPACLOUD_CONFIG.effect, ...config.effect }
|
|
31483
31973
|
};
|
|
31484
31974
|
}
|
|
31485
31975
|
function validateGovernanceConfig(config) {
|
|
@@ -31536,6 +32026,34 @@ function validateGovernanceConfig(config) {
|
|
|
31536
32026
|
if (config[key] !== undefined && typeof config[key] !== "boolean")
|
|
31537
32027
|
throw new Error(`${key} must be a boolean.`);
|
|
31538
32028
|
}
|
|
32029
|
+
if (config.effect !== undefined) {
|
|
32030
|
+
if (!isRecord(config.effect) || Object.keys(config.effect).some((key) => ![
|
|
32031
|
+
"requireRouteEffects",
|
|
32032
|
+
"requireErrorMappings",
|
|
32033
|
+
"requireDependencies",
|
|
32034
|
+
"requireTaggedErrorTypes",
|
|
32035
|
+
"requireExactDependencyTypes",
|
|
32036
|
+
"requireTimeoutForDependencies",
|
|
32037
|
+
"forbidDirectRuntimeExecution",
|
|
32038
|
+
"forbidDirectThrows"
|
|
32039
|
+
].includes(key))) {
|
|
32040
|
+
throw new Error("effect contains an unsupported governance option.");
|
|
32041
|
+
}
|
|
32042
|
+
for (const key of [
|
|
32043
|
+
"requireRouteEffects",
|
|
32044
|
+
"requireErrorMappings",
|
|
32045
|
+
"requireDependencies",
|
|
32046
|
+
"requireTaggedErrorTypes",
|
|
32047
|
+
"requireExactDependencyTypes",
|
|
32048
|
+
"requireTimeoutForDependencies",
|
|
32049
|
+
"forbidDirectRuntimeExecution",
|
|
32050
|
+
"forbidDirectThrows"
|
|
32051
|
+
]) {
|
|
32052
|
+
if (config.effect[key] !== undefined && typeof config.effect[key] !== "boolean") {
|
|
32053
|
+
throw new Error(`effect.${key} must be boolean.`);
|
|
32054
|
+
}
|
|
32055
|
+
}
|
|
32056
|
+
}
|
|
31539
32057
|
}
|
|
31540
32058
|
function resolveSupacloudConfig(config = {}, cwd = process.cwd()) {
|
|
31541
32059
|
const resolved = defineSupacloudConfig(config);
|
|
@@ -31556,6 +32074,7 @@ function resolveSupacloudConfig(config = {}, cwd = process.cwd()) {
|
|
|
31556
32074
|
...resolved.allowRouteCommandBindings === undefined ? {} : { allowRouteCommandBindings: resolved.allowRouteCommandBindings },
|
|
31557
32075
|
...resolved.disallowControllerDirectDb === undefined ? {} : { disallowControllerDirectDb: resolved.disallowControllerDirectDb },
|
|
31558
32076
|
...resolved.detectOrphanModules === undefined ? {} : { detectOrphanModules: resolved.detectOrphanModules },
|
|
32077
|
+
effect: resolved.effect === undefined ? { ...DEFAULT_SUPACLOUD_CONFIG.effect } : { ...DEFAULT_SUPACLOUD_CONFIG.effect, ...resolved.effect },
|
|
31559
32078
|
treeShakeUnusedProviders: resolved.treeShakeUnusedProviders ?? DEFAULT_SUPACLOUD_CONFIG.treeShakeUnusedProviders,
|
|
31560
32079
|
graphql: resolved.graphql ? {
|
|
31561
32080
|
...resolved.graphql,
|
|
@@ -31644,7 +32163,7 @@ import { createHash as createHash10 } from "node:crypto";
|
|
|
31644
32163
|
init_validate();
|
|
31645
32164
|
var compare3 = (a, b) => a < b ? -1 : a > b ? 1 : 0;
|
|
31646
32165
|
var sorted = (values) => [...new Set(values)].sort(compare3);
|
|
31647
|
-
function
|
|
32166
|
+
function diagnostic2(error) {
|
|
31648
32167
|
return {
|
|
31649
32168
|
severity: error.severity,
|
|
31650
32169
|
code: error.code,
|
|
@@ -31669,8 +32188,8 @@ function createDeliveryPlan(graph, input) {
|
|
|
31669
32188
|
}] };
|
|
31670
32189
|
}
|
|
31671
32190
|
const diagnostics = [
|
|
31672
|
-
...(graph.diagnostics ?? []).map(
|
|
31673
|
-
...validateGraph(graph, { strict: true }).map(
|
|
32191
|
+
...(graph.diagnostics ?? []).map(diagnostic2),
|
|
32192
|
+
...validateGraph(graph, { strict: true }).map(diagnostic2)
|
|
31674
32193
|
];
|
|
31675
32194
|
const fail = (code, message, suggestion) => {
|
|
31676
32195
|
diagnostics.push({ severity: "error", code, message, suggestion });
|
|
@@ -32840,10 +33359,10 @@ import { readFileSync as readFileSync4 } from "node:fs";
|
|
|
32840
33359
|
import { resolve as resolve18 } from "node:path";
|
|
32841
33360
|
|
|
32842
33361
|
// src/migration-app-version.ts
|
|
32843
|
-
var MIGRATION_APP_VERSION = "0.
|
|
33362
|
+
var MIGRATION_APP_VERSION = "0.24.0";
|
|
32844
33363
|
|
|
32845
33364
|
// src/migration-elysia-version.ts
|
|
32846
|
-
var MIGRATION_ELYSIA_VERSION = "0.
|
|
33365
|
+
var MIGRATION_ELYSIA_VERSION = "0.29.0";
|
|
32847
33366
|
|
|
32848
33367
|
// src/migration-policy.ts
|
|
32849
33368
|
var cachedCompilerVersion;
|