@supacloud/cli 0.73.0 → 0.74.1
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.js +312 -42
- package/package.json +3 -3
package/dist/index.js
CHANGED
|
@@ -221509,6 +221509,13 @@ var init_validate = __esm2(() => {
|
|
|
221509
221509
|
"effect-dependencies-required": { code: "SC3038", docsUrl: "https://supacloud.dev/errors/SC3038" },
|
|
221510
221510
|
"effect-command-retry-forbidden": { code: "SC3039", docsUrl: "https://supacloud.dev/errors/SC3039" },
|
|
221511
221511
|
"effect-dependency-type": { code: "SC3040", docsUrl: "https://supacloud.dev/errors/SC3040" },
|
|
221512
|
+
"effect-error-type": { code: "SC3041", docsUrl: "https://supacloud.dev/errors/SC3041" },
|
|
221513
|
+
"effect-error-mapping-type": { code: "SC3042", docsUrl: "https://supacloud.dev/errors/SC3042" },
|
|
221514
|
+
"effect-dependency-mismatch": { code: "SC3043", docsUrl: "https://supacloud.dev/errors/SC3043" },
|
|
221515
|
+
"effect-timeout-required": { code: "SC3044", docsUrl: "https://supacloud.dev/errors/SC3044" },
|
|
221516
|
+
"effect-direct-runtime-execution": { code: "SC3045", docsUrl: "https://supacloud.dev/errors/SC3045" },
|
|
221517
|
+
"effect-direct-throw": { code: "SC3046", docsUrl: "https://supacloud.dev/errors/SC3046" },
|
|
221518
|
+
"effect-promise-return": { code: "SC3047", docsUrl: "https://supacloud.dev/errors/SC3047" },
|
|
221512
221519
|
"invalid-route-response-map": { code: "SC3024", docsUrl: "https://supacloud.dev/errors/SC3024" },
|
|
221513
221520
|
"invalid-route-response-selector": { code: "SC3025", docsUrl: "https://supacloud.dev/errors/SC3025" },
|
|
221514
221521
|
"conflicting-route-response-schema": { code: "SC3026", docsUrl: "https://supacloud.dev/errors/SC3026" },
|
|
@@ -244750,6 +244757,13 @@ function withDiagnosticMetadata(diagnostic) {
|
|
|
244750
244757
|
const metadata = COMPILER_DIAGNOSTIC_CODES[diagnostic.code];
|
|
244751
244758
|
return metadata ? { ...diagnostic, errorCode: metadata.code, docsUrl: metadata.docsUrl } : diagnostic;
|
|
244752
244759
|
}
|
|
244760
|
+
var DEFAULT_EFFECT_ANALYSIS_OPTIONS = {
|
|
244761
|
+
requireTaggedErrorTypes: true,
|
|
244762
|
+
requireExactDependencyTypes: true,
|
|
244763
|
+
requireTimeoutForDependencies: true,
|
|
244764
|
+
forbidDirectRuntimeExecution: true,
|
|
244765
|
+
forbidDirectThrows: true
|
|
244766
|
+
};
|
|
244753
244767
|
function nodeText(node) {
|
|
244754
244768
|
return node.getText(node.getSourceFile());
|
|
244755
244769
|
}
|
|
@@ -244795,7 +244809,7 @@ function descendantsOfKind(root, predicate) {
|
|
|
244795
244809
|
visit(root);
|
|
244796
244810
|
return result;
|
|
244797
244811
|
}
|
|
244798
|
-
async function analyzeProject(rootDir, include, cache, changedPaths) {
|
|
244812
|
+
async function analyzeProject(rootDir, include, cache, changedPaths, effectOptions = {}) {
|
|
244799
244813
|
const session = cache?.programSession ?? createIncrementalProgramSession(rootDir);
|
|
244800
244814
|
if (cache)
|
|
244801
244815
|
cache.programSession = session;
|
|
@@ -244814,8 +244828,21 @@ async function analyzeProject(rootDir, include, cache, changedPaths) {
|
|
|
244814
244828
|
moduleHandlerFiles: new Map,
|
|
244815
244829
|
infraResources: new Map,
|
|
244816
244830
|
infraResourceClasses: new Map,
|
|
244817
|
-
diagnostics: []
|
|
244831
|
+
diagnostics: [],
|
|
244832
|
+
effectOptions: {
|
|
244833
|
+
...DEFAULT_EFFECT_ANALYSIS_OPTIONS,
|
|
244834
|
+
requireTaggedErrorTypes: effectOptions.requireTaggedErrorTypes ?? DEFAULT_EFFECT_ANALYSIS_OPTIONS.requireTaggedErrorTypes,
|
|
244835
|
+
requireExactDependencyTypes: effectOptions.requireExactDependencyTypes ?? DEFAULT_EFFECT_ANALYSIS_OPTIONS.requireExactDependencyTypes,
|
|
244836
|
+
requireTimeoutForDependencies: effectOptions.requireTimeoutForDependencies ?? DEFAULT_EFFECT_ANALYSIS_OPTIONS.requireTimeoutForDependencies,
|
|
244837
|
+
forbidDirectRuntimeExecution: effectOptions.forbidDirectRuntimeExecution ?? DEFAULT_EFFECT_ANALYSIS_OPTIONS.forbidDirectRuntimeExecution,
|
|
244838
|
+
forbidDirectThrows: effectOptions.forbidDirectThrows ?? DEFAULT_EFFECT_ANALYSIS_OPTIONS.forbidDirectThrows
|
|
244839
|
+
}
|
|
244818
244840
|
};
|
|
244841
|
+
if (ctx.effectOptions.forbidDirectRuntimeExecution) {
|
|
244842
|
+
for (const sourceFile of sourceFiles) {
|
|
244843
|
+
scanEffectRuntimeEscapes(sourceFile, ctx);
|
|
244844
|
+
}
|
|
244845
|
+
}
|
|
244819
244846
|
const nativeTraitFiles = new Map;
|
|
244820
244847
|
for (const diagnostic of session.getDiagnostics()) {
|
|
244821
244848
|
ctx.diagnostics.push(toCompilerDiagnostic(diagnostic, rootDir));
|
|
@@ -245991,11 +246018,150 @@ function declaredClassType(declaration, ctx) {
|
|
|
245991
246018
|
return symbol ? ctx.checker.getDeclaredTypeOfSymbol(symbol) : undefined;
|
|
245992
246019
|
}
|
|
245993
246020
|
function typeArgumentsOf(type, ctx) {
|
|
245994
|
-
|
|
246021
|
+
if (!isTypeReference(type))
|
|
246022
|
+
return [];
|
|
246023
|
+
const argumentsFromChecker = ctx.checker.getTypeArguments(type);
|
|
246024
|
+
if (argumentsFromChecker.length > 0)
|
|
246025
|
+
return argumentsFromChecker;
|
|
246026
|
+
const reference = type;
|
|
246027
|
+
if (reference.resolvedTypeArguments && reference.resolvedTypeArguments.length > 0) {
|
|
246028
|
+
return reference.resolvedTypeArguments;
|
|
246029
|
+
}
|
|
246030
|
+
return reference.aliasTypeArguments ?? [];
|
|
245995
246031
|
}
|
|
245996
246032
|
function isTypeReference(type) {
|
|
245997
246033
|
return "target" in type;
|
|
245998
246034
|
}
|
|
246035
|
+
function effectTypeArgumentsOf(type, ctx) {
|
|
246036
|
+
if (!type)
|
|
246037
|
+
return;
|
|
246038
|
+
const reference = isTypeReference(type) ? type : undefined;
|
|
246039
|
+
const target = reference?.target ?? type;
|
|
246040
|
+
const symbol = target.aliasSymbol ?? target.getSymbol();
|
|
246041
|
+
const text = ctx.checker.typeToString(type, undefined, ts7.TypeFormatFlags.NoTruncation);
|
|
246042
|
+
if (symbol?.getName() !== "Effect" && !/^(?:Effect\.)?Effect</.test(text))
|
|
246043
|
+
return;
|
|
246044
|
+
const args = typeArgumentsOf(type, ctx);
|
|
246045
|
+
return args.length >= 3 ? [args[0], args[1], args[2]] : undefined;
|
|
246046
|
+
}
|
|
246047
|
+
function effectTypeName(type, location, ctx) {
|
|
246048
|
+
return ctx.checker.typeToString(type, location, ts7.TypeFormatFlags.NoTruncation);
|
|
246049
|
+
}
|
|
246050
|
+
function stringLiteralTypeValue(type) {
|
|
246051
|
+
return (type.flags & ts7.TypeFlags.StringLiteral) !== 0 ? type.value : undefined;
|
|
246052
|
+
}
|
|
246053
|
+
function effectFailureTags(type, location, ctx) {
|
|
246054
|
+
if ((type.flags & ts7.TypeFlags.Never) !== 0)
|
|
246055
|
+
return { tags: [], invalid: false };
|
|
246056
|
+
if ((type.flags & (ts7.TypeFlags.Any | ts7.TypeFlags.Unknown)) !== 0) {
|
|
246057
|
+
return { tags: [], invalid: true };
|
|
246058
|
+
}
|
|
246059
|
+
if (type.isUnion()) {
|
|
246060
|
+
return type.types.reduce((result, part) => {
|
|
246061
|
+
const next = effectFailureTags(part, location, ctx);
|
|
246062
|
+
result.tags.push(...next.tags);
|
|
246063
|
+
result.invalid ||= next.invalid;
|
|
246064
|
+
return result;
|
|
246065
|
+
}, { tags: [], invalid: false });
|
|
246066
|
+
}
|
|
246067
|
+
const apparent = ctx.checker.getApparentType(type);
|
|
246068
|
+
for (const propertyName of ["_tag", "tag"]) {
|
|
246069
|
+
const property = ctx.checker.getPropertyOfType(apparent, propertyName);
|
|
246070
|
+
if (!property)
|
|
246071
|
+
continue;
|
|
246072
|
+
const propertyType = ctx.checker.getTypeOfSymbolAtLocation(property, location);
|
|
246073
|
+
const tag = stringLiteralTypeValue(propertyType);
|
|
246074
|
+
if (tag)
|
|
246075
|
+
return { tags: [tag], invalid: false };
|
|
246076
|
+
}
|
|
246077
|
+
return { tags: [], invalid: true };
|
|
246078
|
+
}
|
|
246079
|
+
function effectEnvironmentNames(type, location, ctx) {
|
|
246080
|
+
if ((type.flags & ts7.TypeFlags.Never) !== 0)
|
|
246081
|
+
return { names: [], invalid: false };
|
|
246082
|
+
if ((type.flags & (ts7.TypeFlags.Any | ts7.TypeFlags.Unknown)) !== 0) {
|
|
246083
|
+
return { names: [], invalid: true };
|
|
246084
|
+
}
|
|
246085
|
+
if (type.isUnion() || type.isIntersection()) {
|
|
246086
|
+
return type.types.reduce((result, part) => {
|
|
246087
|
+
const next = effectEnvironmentNames(part, location, ctx);
|
|
246088
|
+
result.names.push(...next.names);
|
|
246089
|
+
result.invalid ||= next.invalid;
|
|
246090
|
+
return result;
|
|
246091
|
+
}, { names: [], invalid: false });
|
|
246092
|
+
}
|
|
246093
|
+
const literal = stringLiteralTypeValue(type);
|
|
246094
|
+
if (literal)
|
|
246095
|
+
return { names: [literal], invalid: false };
|
|
246096
|
+
const symbolName = type.getSymbol()?.getName();
|
|
246097
|
+
if (symbolName && symbolName !== "__type" && symbolName !== "__object") {
|
|
246098
|
+
return { names: [symbolName], invalid: false };
|
|
246099
|
+
}
|
|
246100
|
+
const text = effectTypeName(type, location, ctx).trim();
|
|
246101
|
+
if (text && text !== "object")
|
|
246102
|
+
return { names: [text], invalid: false };
|
|
246103
|
+
return { names: [], invalid: true };
|
|
246104
|
+
}
|
|
246105
|
+
function sortedUnique(values) {
|
|
246106
|
+
return [...new Set(values)].sort((left, right) => left.localeCompare(right, "en"));
|
|
246107
|
+
}
|
|
246108
|
+
function pushEffectDiagnostic(ctx, code, file, line, message, suggestion) {
|
|
246109
|
+
const metadata = COMPILER_DIAGNOSTIC_CODES[code];
|
|
246110
|
+
ctx.diagnostics.push({
|
|
246111
|
+
severity: "error",
|
|
246112
|
+
code,
|
|
246113
|
+
...metadata ? { errorCode: metadata.code, docsUrl: metadata.docsUrl } : {},
|
|
246114
|
+
file,
|
|
246115
|
+
line,
|
|
246116
|
+
message,
|
|
246117
|
+
suggestion
|
|
246118
|
+
});
|
|
246119
|
+
}
|
|
246120
|
+
function scanEffectRuntimeEscapes(sourceFile, ctx) {
|
|
246121
|
+
const relativeFile = sourcePath(ctx.rootDir, sourceFile.fileName);
|
|
246122
|
+
if (/\.(?:test|spec)\.[cm]?[jt]sx?$/.test(relativeFile))
|
|
246123
|
+
return;
|
|
246124
|
+
const namespaceBindings = new Set;
|
|
246125
|
+
const runtimeBindings = new Set;
|
|
246126
|
+
for (const statement of sourceFile.statements) {
|
|
246127
|
+
if (!ts7.isImportDeclaration(statement) || !ts7.isStringLiteral(statement.moduleSpecifier))
|
|
246128
|
+
continue;
|
|
246129
|
+
const moduleName = statement.moduleSpecifier.text;
|
|
246130
|
+
if (moduleName !== "effect" && moduleName !== "effect/Effect" && moduleName !== "effect/Runtime")
|
|
246131
|
+
continue;
|
|
246132
|
+
const clause = statement.importClause;
|
|
246133
|
+
if (!clause)
|
|
246134
|
+
continue;
|
|
246135
|
+
if (clause.name)
|
|
246136
|
+
namespaceBindings.add(clause.name.text);
|
|
246137
|
+
if (clause.namedBindings && ts7.isNamespaceImport(clause.namedBindings)) {
|
|
246138
|
+
namespaceBindings.add(clause.namedBindings.name.text);
|
|
246139
|
+
} else if (clause.namedBindings && ts7.isNamedImports(clause.namedBindings)) {
|
|
246140
|
+
for (const element of clause.namedBindings.elements) {
|
|
246141
|
+
const imported = element.propertyName?.text ?? element.name.text;
|
|
246142
|
+
if (moduleName === "effect" && (imported === "Effect" || imported === "Runtime")) {
|
|
246143
|
+
namespaceBindings.add(element.name.text);
|
|
246144
|
+
}
|
|
246145
|
+
if (["runPromise", "runPromiseExit", "runSync", "runFork"].includes(imported)) {
|
|
246146
|
+
runtimeBindings.add(element.name.text);
|
|
246147
|
+
}
|
|
246148
|
+
}
|
|
246149
|
+
}
|
|
246150
|
+
}
|
|
246151
|
+
if (namespaceBindings.size === 0 && runtimeBindings.size === 0)
|
|
246152
|
+
return;
|
|
246153
|
+
for (const call of descendantsOfKind(sourceFile, ts7.isCallExpression)) {
|
|
246154
|
+
let runtimeName;
|
|
246155
|
+
if (ts7.isPropertyAccessExpression(call.expression) && namespaceBindings.has(call.expression.expression.getText(sourceFile)) && ["runPromise", "runPromiseExit", "runSync", "runFork"].includes(call.expression.name.text)) {
|
|
246156
|
+
runtimeName = call.expression.name.text;
|
|
246157
|
+
} else if (ts7.isIdentifier(call.expression) && runtimeBindings.has(call.expression.text)) {
|
|
246158
|
+
runtimeName = call.expression.text;
|
|
246159
|
+
}
|
|
246160
|
+
if (!runtimeName)
|
|
246161
|
+
continue;
|
|
246162
|
+
pushEffectDiagnostic(ctx, "effect-direct-runtime-execution", sourcePath(ctx.rootDir, sourceFile.fileName), lineOf2(call), `Direct ${runtimeName} execution escapes the Effect boundary.`, "Return the Effect program to the Elysia boundary; only the framework adapter may interpret it.");
|
|
246163
|
+
}
|
|
246164
|
+
}
|
|
245999
246165
|
function isUnknownOrAny(type) {
|
|
246000
246166
|
return (type.flags & (ts7.TypeFlags.Any | ts7.TypeFlags.Unknown)) !== 0;
|
|
246001
246167
|
}
|
|
@@ -246059,7 +246225,8 @@ function parseController(input, ctx) {
|
|
|
246059
246225
|
handler: propertyName(method.name)
|
|
246060
246226
|
};
|
|
246061
246227
|
const signature = ctx.checker.getSignatureFromDeclaration(method);
|
|
246062
|
-
const
|
|
246228
|
+
const returnType = signature?.getReturnType();
|
|
246229
|
+
const resultType = returnType && ctx.checker.typeToString(returnType);
|
|
246063
246230
|
if (resultType && /\bResponse\b/.test(resultType))
|
|
246064
246231
|
route.nativeResponse = true;
|
|
246065
246232
|
const pathParams = [];
|
|
@@ -246390,6 +246557,18 @@ function parseController(input, ctx) {
|
|
|
246390
246557
|
});
|
|
246391
246558
|
} else {
|
|
246392
246559
|
effect.dependencies = dependenciesExpr.elements.map((element) => element.text);
|
|
246560
|
+
if (new Set(effect.dependencies).size !== effect.dependencies.length) {
|
|
246561
|
+
ctx.diagnostics.push({
|
|
246562
|
+
severity: "error",
|
|
246563
|
+
code: "invalid-effect-dependencies",
|
|
246564
|
+
errorCode: "SC3033",
|
|
246565
|
+
docsUrl: "https://supacloud.dev/errors/SC3033",
|
|
246566
|
+
file,
|
|
246567
|
+
line: lineOf2(dependenciesExpr),
|
|
246568
|
+
message: `Route ${route.handler} effect.dependencies must not contain duplicate service names.`,
|
|
246569
|
+
suggestion: "List each logical Effect dependency exactly once."
|
|
246570
|
+
});
|
|
246571
|
+
}
|
|
246393
246572
|
}
|
|
246394
246573
|
}
|
|
246395
246574
|
const errorsExpr = getProp(effectObject, "errors");
|
|
@@ -246438,10 +246617,26 @@ function parseController(input, ctx) {
|
|
|
246438
246617
|
errors.push({ tag, code, status, ...message === undefined ? {} : { message } });
|
|
246439
246618
|
}
|
|
246440
246619
|
effect.errors = errors;
|
|
246620
|
+
if (new Set(errors.map((entry) => entry.tag)).size !== errors.length) {
|
|
246621
|
+
ctx.diagnostics.push({
|
|
246622
|
+
severity: "error",
|
|
246623
|
+
code: "invalid-effect-errors",
|
|
246624
|
+
errorCode: "SC3034",
|
|
246625
|
+
docsUrl: "https://supacloud.dev/errors/SC3034",
|
|
246626
|
+
file,
|
|
246627
|
+
line: lineOf2(errorsExpr),
|
|
246628
|
+
message: `Route ${route.handler} effect.errors must not contain duplicate tags.`,
|
|
246629
|
+
suggestion: "Map each tagged failure exactly once."
|
|
246630
|
+
});
|
|
246631
|
+
}
|
|
246441
246632
|
}
|
|
246442
246633
|
}
|
|
246443
246634
|
route.effect = effect;
|
|
246444
|
-
|
|
246635
|
+
const effectArguments = effectTypeArgumentsOf(returnType, ctx);
|
|
246636
|
+
const promiseReturn = Boolean(resultType && /\bPromise(?:<|\s)/.test(resultType));
|
|
246637
|
+
if (promiseReturn) {
|
|
246638
|
+
pushEffectDiagnostic(ctx, "effect-promise-return", file, lineOf2(method), `Route ${route.handler} returns Promise instead of an Effect program.`, "Return Effect.Effect<Success, Failure, Requirements>; the Elysia boundary owns Promise interpretation.");
|
|
246639
|
+
} else if (!effectArguments) {
|
|
246445
246640
|
ctx.diagnostics.push({
|
|
246446
246641
|
severity: "error",
|
|
246447
246642
|
code: "effect-return-type",
|
|
@@ -246453,18 +246648,37 @@ function parseController(input, ctx) {
|
|
|
246453
246648
|
suggestion: "Return Effect.Effect<Success, Failure, Requirements> from the handler."
|
|
246454
246649
|
});
|
|
246455
246650
|
}
|
|
246456
|
-
|
|
246457
|
-
|
|
246458
|
-
|
|
246459
|
-
|
|
246460
|
-
|
|
246461
|
-
|
|
246462
|
-
|
|
246463
|
-
|
|
246464
|
-
|
|
246465
|
-
|
|
246466
|
-
|
|
246467
|
-
|
|
246651
|
+
if (effectArguments) {
|
|
246652
|
+
const [, failureType, environmentType] = effectArguments;
|
|
246653
|
+
const failure = effectFailureTags(failureType, method, ctx);
|
|
246654
|
+
if (ctx.effectOptions.requireTaggedErrorTypes && failure.invalid) {
|
|
246655
|
+
pushEffectDiagnostic(ctx, "effect-error-type", file, lineOf2(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.");
|
|
246656
|
+
} else if (ctx.effectOptions.requireTaggedErrorTypes && effect.errors !== undefined && !failure.invalid) {
|
|
246657
|
+
const declaredTags = sortedUnique(effect.errors.map((entry) => entry.tag));
|
|
246658
|
+
const typedTags = sortedUnique(failure.tags);
|
|
246659
|
+
if (declaredTags.join("\x00") !== typedTags.join("\x00")) {
|
|
246660
|
+
pushEffectDiagnostic(ctx, "effect-error-mapping-type", file, lineOf2(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.");
|
|
246661
|
+
}
|
|
246662
|
+
}
|
|
246663
|
+
const environment = effectEnvironmentNames(environmentType, method, ctx);
|
|
246664
|
+
if (ctx.effectOptions.requireExactDependencyTypes && effect.dependencies !== undefined) {
|
|
246665
|
+
if (environment.invalid) {
|
|
246666
|
+
pushEffectDiagnostic(ctx, "effect-dependency-mismatch", file, lineOf2(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.");
|
|
246667
|
+
} else {
|
|
246668
|
+
const declaredDependencies = sortedUnique(effect.dependencies);
|
|
246669
|
+
const typedDependencies = sortedUnique(environment.names);
|
|
246670
|
+
if (declaredDependencies.join("\x00") !== typedDependencies.join("\x00")) {
|
|
246671
|
+
pushEffectDiagnostic(ctx, "effect-dependency-mismatch", file, lineOf2(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.");
|
|
246672
|
+
}
|
|
246673
|
+
}
|
|
246674
|
+
}
|
|
246675
|
+
if (ctx.effectOptions.requireTimeoutForDependencies && (effect.dependencies?.length ?? 0) > 0 && effect.timeoutMs === undefined) {
|
|
246676
|
+
pushEffectDiagnostic(ctx, "effect-timeout-required", file, lineOf2(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.");
|
|
246677
|
+
}
|
|
246678
|
+
}
|
|
246679
|
+
if (ctx.effectOptions.forbidDirectThrows && method.body) {
|
|
246680
|
+
for (const throwStatement of descendantsOfKind(method.body, ts7.isThrowStatement)) {
|
|
246681
|
+
pushEffectDiagnostic(ctx, "effect-direct-throw", file, lineOf2(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.");
|
|
246468
246682
|
}
|
|
246469
246683
|
}
|
|
246470
246684
|
}
|
|
@@ -247763,7 +247977,7 @@ async function renderOptionalGraphql(options) {
|
|
|
247763
247977
|
}
|
|
247764
247978
|
async function compileProject(options) {
|
|
247765
247979
|
options = withDefaultGovernance(options);
|
|
247766
|
-
const graph = await analyzeProject(options.rootDir, options.include, options.cache, options.changedPaths);
|
|
247980
|
+
const graph = await analyzeProject(options.rootDir, options.include, options.cache, options.changedPaths, options.effect);
|
|
247767
247981
|
const diagnostics = [
|
|
247768
247982
|
...graph.diagnostics ?? [],
|
|
247769
247983
|
...validateGraph(graph, options),
|
|
@@ -247828,7 +248042,7 @@ async function compileProject(options) {
|
|
|
247828
248042
|
}
|
|
247829
248043
|
async function checkProject(options) {
|
|
247830
248044
|
options = withDefaultGovernance(options);
|
|
247831
|
-
const graph = await analyzeProject(options.rootDir, options.include, options.cache, options.changedPaths);
|
|
248045
|
+
const graph = await analyzeProject(options.rootDir, options.include, options.cache, options.changedPaths, options.effect);
|
|
247832
248046
|
const diagnostics = [
|
|
247833
248047
|
...graph.diagnostics ?? [],
|
|
247834
248048
|
...validateGraph(graph, options),
|
|
@@ -249069,6 +249283,7 @@ function optionsKeyOf(options) {
|
|
|
249069
249283
|
openApi: options.openApi,
|
|
249070
249284
|
generatePermissions: options.generatePermissions,
|
|
249071
249285
|
typeSafety: options.typeSafety,
|
|
249286
|
+
effect: options.effect,
|
|
249072
249287
|
treeShakeUnusedProviders: options.treeShakeUnusedProviders,
|
|
249073
249288
|
graphql: options.graphql
|
|
249074
249289
|
});
|
|
@@ -249400,7 +249615,12 @@ var DEFAULT_SUPACLOUD_CONFIG = {
|
|
|
249400
249615
|
effect: {
|
|
249401
249616
|
requireRouteEffects: true,
|
|
249402
249617
|
requireErrorMappings: true,
|
|
249403
|
-
requireDependencies: true
|
|
249618
|
+
requireDependencies: true,
|
|
249619
|
+
requireTaggedErrorTypes: true,
|
|
249620
|
+
requireExactDependencyTypes: true,
|
|
249621
|
+
requireTimeoutForDependencies: true,
|
|
249622
|
+
forbidDirectRuntimeExecution: true,
|
|
249623
|
+
forbidDirectThrows: true
|
|
249404
249624
|
}
|
|
249405
249625
|
};
|
|
249406
249626
|
function defineSupacloudConfig(config = {}) {
|
|
@@ -249472,10 +249692,28 @@ function validateGovernanceConfig(config) {
|
|
|
249472
249692
|
throw new Error(`${key} must be a boolean.`);
|
|
249473
249693
|
}
|
|
249474
249694
|
if (config.effect !== undefined) {
|
|
249475
|
-
if (!isRecord(config.effect) || Object.keys(config.effect).some((key) => ![
|
|
249476
|
-
|
|
249477
|
-
|
|
249478
|
-
|
|
249695
|
+
if (!isRecord(config.effect) || Object.keys(config.effect).some((key) => ![
|
|
249696
|
+
"requireRouteEffects",
|
|
249697
|
+
"requireErrorMappings",
|
|
249698
|
+
"requireDependencies",
|
|
249699
|
+
"requireTaggedErrorTypes",
|
|
249700
|
+
"requireExactDependencyTypes",
|
|
249701
|
+
"requireTimeoutForDependencies",
|
|
249702
|
+
"forbidDirectRuntimeExecution",
|
|
249703
|
+
"forbidDirectThrows"
|
|
249704
|
+
].includes(key))) {
|
|
249705
|
+
throw new Error("effect contains an unsupported governance option.");
|
|
249706
|
+
}
|
|
249707
|
+
for (const key of [
|
|
249708
|
+
"requireRouteEffects",
|
|
249709
|
+
"requireErrorMappings",
|
|
249710
|
+
"requireDependencies",
|
|
249711
|
+
"requireTaggedErrorTypes",
|
|
249712
|
+
"requireExactDependencyTypes",
|
|
249713
|
+
"requireTimeoutForDependencies",
|
|
249714
|
+
"forbidDirectRuntimeExecution",
|
|
249715
|
+
"forbidDirectThrows"
|
|
249716
|
+
]) {
|
|
249479
249717
|
if (config.effect[key] !== undefined && typeof config.effect[key] !== "boolean") {
|
|
249480
249718
|
throw new Error(`effect.${key} must be boolean.`);
|
|
249481
249719
|
}
|
|
@@ -249650,7 +249888,7 @@ var starter_metadata_default = {
|
|
|
249650
249888
|
generatedBy: "supacloud/starter-metadata-v1",
|
|
249651
249889
|
packages: {
|
|
249652
249890
|
compiler: {
|
|
249653
|
-
version: "0.
|
|
249891
|
+
version: "0.42.0"
|
|
249654
249892
|
},
|
|
249655
249893
|
app: {
|
|
249656
249894
|
version: "0.24.0",
|
|
@@ -249659,7 +249897,7 @@ var starter_metadata_default = {
|
|
|
249659
249897
|
}
|
|
249660
249898
|
},
|
|
249661
249899
|
elysia: {
|
|
249662
|
-
version: "0.
|
|
249900
|
+
version: "0.29.0"
|
|
249663
249901
|
},
|
|
249664
249902
|
commands: {
|
|
249665
249903
|
version: "0.9.1"
|
|
@@ -250013,7 +250251,7 @@ function baseFiles(name, sdkDependencies) {
|
|
|
250013
250251
|
dependencies: {
|
|
250014
250252
|
"@supacloud/app": `^${appMetadata.version}`,
|
|
250015
250253
|
"@supacloud/elysia": `^${elysiaMetadata.version}`,
|
|
250016
|
-
effect: "
|
|
250254
|
+
effect: "4.0.2",
|
|
250017
250255
|
elysia: "2.0.0-beta.21",
|
|
250018
250256
|
...sdkDependencies ?? { rxjs: appMetadata.dependencies.rxjs }
|
|
250019
250257
|
},
|
|
@@ -250062,6 +250300,11 @@ export default defineSupacloudConfig({
|
|
|
250062
250300
|
requireRouteEffects: true,
|
|
250063
250301
|
requireErrorMappings: true,
|
|
250064
250302
|
requireDependencies: true,
|
|
250303
|
+
requireTaggedErrorTypes: true,
|
|
250304
|
+
requireExactDependencyTypes: true,
|
|
250305
|
+
requireTimeoutForDependencies: true,
|
|
250306
|
+
forbidDirectRuntimeExecution: true,
|
|
250307
|
+
forbidDirectThrows: true,
|
|
250065
250308
|
},
|
|
250066
250309
|
typeSafety: { scanProductionSource: true, noAnyInGenerated: true },
|
|
250067
250310
|
disallowControllerDirectDb: true,
|
|
@@ -250091,7 +250334,7 @@ export const CreateOrderBody = t.Object({ name: t.String({ minLength: 1 }) });
|
|
|
250091
250334
|
export const OrderResult = t.Object({ id: t.String(), name: t.String() });
|
|
250092
250335
|
export const HealthResult = t.Object({ ok: t.Boolean() });
|
|
250093
250336
|
`,
|
|
250094
|
-
"src/orders/orders.ts": `import
|
|
250337
|
+
"src/orders/orders.ts": `import { Effect } from "effect";
|
|
250095
250338
|
import { Body, Controller, Get, Param, Post, defineFeatureSlice } from "@supacloud/app";
|
|
250096
250339
|
import { status } from "elysia";
|
|
250097
250340
|
import { OrderParams, CreateOrderBody, OrderResult, HealthResult } from "./contracts";
|
|
@@ -250292,7 +250535,7 @@ function minimalTemplate(name, sdkDependencies) {
|
|
|
250292
250535
|
"src/features/health/contracts.ts": `import { t } from "elysia";
|
|
250293
250536
|
export const HealthResult = t.Object({ ok: t.Boolean() });
|
|
250294
250537
|
`,
|
|
250295
|
-
"src/features/health/health.ts": `import
|
|
250538
|
+
"src/features/health/health.ts": `import { Effect } from "effect";
|
|
250296
250539
|
import { Controller, Get, Module } from "@supacloud/app/core";
|
|
250297
250540
|
import { HealthResult } from "./contracts";
|
|
250298
250541
|
@Controller("/health")
|
|
@@ -250307,7 +250550,7 @@ export class HealthController {
|
|
|
250307
250550
|
@Module({ name: "health", tags: ["type:feature"], controllers: [HealthController] })
|
|
250308
250551
|
export class HealthModule {}
|
|
250309
250552
|
`,
|
|
250310
|
-
"src/features/health/health.test.ts": `import
|
|
250553
|
+
"src/features/health/health.test.ts": `import { Effect } from "effect";
|
|
250311
250554
|
import { expect, test } from "bun:test";
|
|
250312
250555
|
import { HealthController } from "./health";
|
|
250313
250556
|
|
|
@@ -250837,7 +251080,7 @@ export async function createDeliveryWorker(modules: CompiledModule[], lifecycle:
|
|
|
250837
251080
|
`;
|
|
250838
251081
|
|
|
250839
251082
|
// src/shared/tools/app-starter-upload.ts
|
|
250840
|
-
var STARTER_UPLOAD_FEATURE = `import
|
|
251083
|
+
var STARTER_UPLOAD_FEATURE = `import { Effect } from "effect";
|
|
250841
251084
|
import { Body, Controller, Inject, Injectable, Param, Post, DB_CLIENT, REQUEST_CONTEXT } from "@supacloud/app";
|
|
250842
251085
|
import { ApplicationError } from "@supacloud/elysia";
|
|
250843
251086
|
import { UploadBody, UploadLocation, UploadParams, UploadResult } from "./contracts";
|
|
@@ -250845,6 +251088,17 @@ import { UploadBody, UploadLocation, UploadParams, UploadResult } from "./contra
|
|
|
250845
251088
|
export interface ReviewUploadInput { reviewId: string; artifactId: string; expectedVersion: number }
|
|
250846
251089
|
export interface ReviewUploadLocation { artifactId: string; bucketId: string; objectPath: string }
|
|
250847
251090
|
export interface ReviewUploadResult { artifactId: string; runId: string; objectPath: string; sha256: string; bytes: number }
|
|
251091
|
+
type PreviewUploadError =
|
|
251092
|
+
| { readonly _tag: "AUTHENTICATION_REQUIRED" }
|
|
251093
|
+
| { readonly _tag: "UPLOAD_INPUT_INVALID" }
|
|
251094
|
+
| { readonly _tag: "UPLOAD_DENIED" }
|
|
251095
|
+
| { readonly _tag: "UPLOADS_UNAVAILABLE" };
|
|
251096
|
+
type RegisterUploadError =
|
|
251097
|
+
| PreviewUploadError
|
|
251098
|
+
| { readonly _tag: "UPLOAD_CONTENT_INVALID" }
|
|
251099
|
+
| { readonly _tag: "COMMAND_IDEMPOTENCY_CONFLICT" }
|
|
251100
|
+
| { readonly _tag: "COMMAND_REJECTED" }
|
|
251101
|
+
| { readonly _tag: "UPLOAD_OUTCOME_UNKNOWN" };
|
|
250848
251102
|
export interface ReviewUploadPort {
|
|
250849
251103
|
previewUpload(context: unknown, input: ReviewUploadInput): Promise<ReviewUploadLocation>;
|
|
250850
251104
|
registerUpload(context: unknown, input: ReviewUploadInput): Promise<ReviewUploadResult>;
|
|
@@ -250884,7 +251138,7 @@ export class ReviewUploadsController {
|
|
|
250884
251138
|
retry: "none",
|
|
250885
251139
|
},
|
|
250886
251140
|
})
|
|
250887
|
-
preview(@Param("id") id: string, @Body() body: { artifactId: string; expectedVersion: number }): Effect.Effect<ReviewUploadLocation,
|
|
251141
|
+
preview(@Param("id") id: string, @Body() body: { artifactId: string; expectedVersion: number }): Effect.Effect<ReviewUploadLocation, PreviewUploadError, never> {
|
|
250888
251142
|
return Effect.promise(() => this.uploads.preview({ reviewId: id, artifactId: body.artifactId, expectedVersion: body.expectedVersion }));
|
|
250889
251143
|
}
|
|
250890
251144
|
@Post("/:id/attachment-registration", {
|
|
@@ -250907,7 +251161,7 @@ export class ReviewUploadsController {
|
|
|
250907
251161
|
retry: "none",
|
|
250908
251162
|
},
|
|
250909
251163
|
})
|
|
250910
|
-
register(@Param("id") id: string, @Body() body: { artifactId: string; expectedVersion: number }): Effect.Effect<ReviewUploadResult,
|
|
251164
|
+
register(@Param("id") id: string, @Body() body: { artifactId: string; expectedVersion: number }): Effect.Effect<ReviewUploadResult, RegisterUploadError, never> {
|
|
250911
251165
|
return Effect.promise(() => this.uploads.register({ reviewId: id, artifactId: body.artifactId, expectedVersion: body.expectedVersion }));
|
|
250912
251166
|
}
|
|
250913
251167
|
}
|
|
@@ -251619,7 +251873,7 @@ function appStarterFiles(name) {
|
|
|
251619
251873
|
"drizzle-orm": dbMetadata.peerDependencies["drizzle-orm"],
|
|
251620
251874
|
"@supacloud/js": `^${sdkMetadata.version}`,
|
|
251621
251875
|
"@supabase/supabase-js": sdkMetadata.peerDependencies["@supabase/supabase-js"],
|
|
251622
|
-
effect: "
|
|
251876
|
+
effect: "4.0.2",
|
|
251623
251877
|
elysia: "2.0.0-beta.21",
|
|
251624
251878
|
rxjs: appMetadata2.dependencies.rxjs
|
|
251625
251879
|
},
|
|
@@ -251679,6 +251933,11 @@ export default defineSupacloudConfig({
|
|
|
251679
251933
|
requireRouteEffects: true,
|
|
251680
251934
|
requireErrorMappings: true,
|
|
251681
251935
|
requireDependencies: true,
|
|
251936
|
+
requireTaggedErrorTypes: true,
|
|
251937
|
+
requireExactDependencyTypes: true,
|
|
251938
|
+
requireTimeoutForDependencies: true,
|
|
251939
|
+
forbidDirectRuntimeExecution: true,
|
|
251940
|
+
forbidDirectThrows: true,
|
|
251682
251941
|
},
|
|
251683
251942
|
typeSafety: { scanProductionSource: true, noAnyInGenerated: true },
|
|
251684
251943
|
disallowControllerDirectDb: true,
|
|
@@ -251880,7 +252139,7 @@ export function createCompiledModules(): never {
|
|
|
251880
252139
|
throw new Error("Run bun run compile before starting the application");
|
|
251881
252140
|
}
|
|
251882
252141
|
`,
|
|
251883
|
-
"src/review/review.ts": `import
|
|
252142
|
+
"src/review/review.ts": `import { Effect } from "effect";
|
|
251884
252143
|
import {
|
|
251885
252144
|
Body, Command, Controller, DB_CLIENT, Get, Inject, Param, Post,
|
|
251886
252145
|
defineFeatureSlice, defineFeatureSpec, type Aspect,
|
|
@@ -251947,6 +252206,17 @@ export class ApproveReview {
|
|
|
251947
252206
|
}
|
|
251948
252207
|
}
|
|
251949
252208
|
|
|
252209
|
+
type ReviewError =
|
|
252210
|
+
| { readonly _tag: "AUTHENTICATION_REQUIRED" }
|
|
252211
|
+
| { readonly _tag: "REVIEW_NOT_FOUND" }
|
|
252212
|
+
| { readonly _tag: "REVIEW_PERMISSION_DENIED" }
|
|
252213
|
+
| { readonly _tag: "REVIEW_CONFLICT" }
|
|
252214
|
+
| { readonly _tag: "FEATURE_TRANSITION_CONFLICT" }
|
|
252215
|
+
| { readonly _tag: "COMMAND_IDEMPOTENCY_CONFLICT" }
|
|
252216
|
+
| { readonly _tag: "COMMAND_REJECTED" }
|
|
252217
|
+
| { readonly _tag: "REVIEW_COMMAND_UNSUPPORTED" }
|
|
252218
|
+
| { readonly _tag: "COMMAND_OUTCOME_UNKNOWN" };
|
|
252219
|
+
|
|
251950
252220
|
@Controller("/reviews")
|
|
251951
252221
|
export class ReviewController {
|
|
251952
252222
|
constructor(@Inject(ApproveReview) private readonly approveReview: ApproveReview) {}
|
|
@@ -251979,7 +252249,7 @@ export class ReviewController {
|
|
|
251979
252249
|
retry: "none",
|
|
251980
252250
|
},
|
|
251981
252251
|
})
|
|
251982
|
-
approve(@Param("id") id: string, @Body() body: { expectedVersion: number }): Effect.Effect<Review,
|
|
252252
|
+
approve(@Param("id") id: string, @Body() body: { expectedVersion: number }): Effect.Effect<Review, ReviewError, never> {
|
|
251983
252253
|
return Effect.promise(() => this.approveReview.execute(id, body.expectedVersion));
|
|
251984
252254
|
}
|
|
251985
252255
|
}
|
|
@@ -252519,7 +252789,7 @@ export class ${className}Service {
|
|
|
252519
252789
|
}
|
|
252520
252790
|
}
|
|
252521
252791
|
`,
|
|
252522
|
-
[`${name}.controller.ts`]: `import
|
|
252792
|
+
[`${name}.controller.ts`]: `import { Effect } from "effect";
|
|
252523
252793
|
import { Controller, Get, Inject, Param } from "@supacloud/app";
|
|
252524
252794
|
import { ${className}Params, ${className}Response, type ${className}Result } from "./${name}.model";
|
|
252525
252795
|
import { ${className}Service } from "./${name}.service";
|
|
@@ -252533,7 +252803,7 @@ export class ${className}Controller {
|
|
|
252533
252803
|
responses: { 200: ${className}Response },
|
|
252534
252804
|
effect: { required: true, dependencies: [], errors: [], retry: "none" },
|
|
252535
252805
|
})
|
|
252536
|
-
find(@Param("id") id: string): Effect.Effect<${className}Result,
|
|
252806
|
+
find(@Param("id") id: string): Effect.Effect<${className}Result, never, never> {
|
|
252537
252807
|
return Effect.promise(() => this.service.find(id));
|
|
252538
252808
|
}
|
|
252539
252809
|
}
|
|
@@ -252569,7 +252839,7 @@ test("${name} preserves permission denial without a privileged fallback", async
|
|
|
252569
252839
|
await expect(service.find("example")).rejects.toBe(denied);
|
|
252570
252840
|
});
|
|
252571
252841
|
`,
|
|
252572
|
-
[`${name}.controller.test.ts`]: `import
|
|
252842
|
+
[`${name}.controller.test.ts`]: `import { Effect } from "effect";
|
|
252573
252843
|
import { expect, test } from "bun:test";
|
|
252574
252844
|
import { ${className}Controller } from "./${name}.controller";
|
|
252575
252845
|
|
|
@@ -257123,7 +257393,7 @@ function registerRemoteDevTools(server2, options = {}) {
|
|
|
257123
257393
|
// package.json
|
|
257124
257394
|
var package_default = {
|
|
257125
257395
|
name: "@supacloud/cli",
|
|
257126
|
-
version: "0.
|
|
257396
|
+
version: "0.74.1",
|
|
257127
257397
|
description: "Project-scoped CLI for SupaCloud users",
|
|
257128
257398
|
type: "module",
|
|
257129
257399
|
main: "./dist/index.js",
|
|
@@ -257157,7 +257427,7 @@ var package_default = {
|
|
|
257157
257427
|
directory: "packages/cli"
|
|
257158
257428
|
},
|
|
257159
257429
|
dependencies: {
|
|
257160
|
-
"@supacloud/compiler": "^0.
|
|
257430
|
+
"@supacloud/compiler": "^0.42.0",
|
|
257161
257431
|
"@supacloud/delivery": "0.8.0",
|
|
257162
257432
|
"@supacloud/db": "^0.14.0",
|
|
257163
257433
|
fflate: "^0.8.3",
|
|
@@ -257170,7 +257440,7 @@ var package_default = {
|
|
|
257170
257440
|
devDependencies: {
|
|
257171
257441
|
"@supacloud/elysia": "^0.24.1",
|
|
257172
257442
|
"@types/bun": "^1.4.2",
|
|
257173
|
-
effect: "
|
|
257443
|
+
effect: "4.0.2",
|
|
257174
257444
|
elysia: "2.0.0-beta.21",
|
|
257175
257445
|
typescript: "^7.0.2"
|
|
257176
257446
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@supacloud/cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.74.1",
|
|
4
4
|
"description": "Project-scoped CLI for SupaCloud users",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -34,7 +34,7 @@
|
|
|
34
34
|
"directory": "packages/cli"
|
|
35
35
|
},
|
|
36
36
|
"dependencies": {
|
|
37
|
-
"@supacloud/compiler": "^0.
|
|
37
|
+
"@supacloud/compiler": "^0.42.0",
|
|
38
38
|
"@supacloud/delivery": "0.8.0",
|
|
39
39
|
"@supacloud/db": "^0.14.0",
|
|
40
40
|
"fflate": "^0.8.3",
|
|
@@ -47,7 +47,7 @@
|
|
|
47
47
|
"devDependencies": {
|
|
48
48
|
"@supacloud/elysia": "^0.24.1",
|
|
49
49
|
"@types/bun": "^1.4.2",
|
|
50
|
-
"effect": "
|
|
50
|
+
"effect": "4.0.2",
|
|
51
51
|
"elysia": "2.0.0-beta.21",
|
|
52
52
|
"typescript": "^7.0.2"
|
|
53
53
|
}
|