@supacloud/compiler 0.21.1 → 0.23.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 +39 -1
- package/dist/cli.js +699 -359
- package/dist/config.d.ts +1 -1
- package/dist/database-contracts.d.ts +48 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +675 -342
- package/dist/sql-safety.d.ts +17 -0
- package/dist/static-di.d.ts +4 -0
- package/dist/type-safety.d.ts +8 -0
- package/package.json +3 -1
package/dist/index.js
CHANGED
|
@@ -196,10 +196,24 @@ function renderApplication(graph, options) {
|
|
|
196
196
|
""
|
|
197
197
|
].join(`
|
|
198
198
|
`);
|
|
199
|
+
const commandGovernance = graph.modules.flatMap((module) => module.commands.map((command) => ({
|
|
200
|
+
module: module.name,
|
|
201
|
+
className: command.className,
|
|
202
|
+
name: command.name,
|
|
203
|
+
permission: command.permission ?? null,
|
|
204
|
+
rpc: command.rpc ?? null,
|
|
205
|
+
transaction: command.transaction ?? null,
|
|
206
|
+
audit: command.audit ?? null,
|
|
207
|
+
idempotency: command.idempotency ?? null
|
|
208
|
+
}))).sort((left, right) => `${left.module}:${left.name}`.localeCompare(`${right.module}:${right.name}`));
|
|
199
209
|
const manifest = {
|
|
200
210
|
version: 1,
|
|
201
211
|
modules: graph.modules,
|
|
202
|
-
externalTokens: graph.externalTokens
|
|
212
|
+
externalTokens: graph.externalTokens,
|
|
213
|
+
commandGovernance: {
|
|
214
|
+
defaults: { authorization: "required", audit: "required", idempotency: "required", transaction: "required" },
|
|
215
|
+
commands: commandGovernance
|
|
216
|
+
}
|
|
203
217
|
};
|
|
204
218
|
const clientCode = options.generateClient ? renderClient(graph, options) : undefined;
|
|
205
219
|
const openApiCode = options.generateOpenApi ? renderOpenApi(graph, options) : undefined;
|
|
@@ -354,7 +368,7 @@ class ModuleGenerator {
|
|
|
354
368
|
this.imports = imports;
|
|
355
369
|
this.pascal = pascalName(module.name);
|
|
356
370
|
if (module.providers.some((provider) => (provider.functionalInjects?.length ?? 0) > 0) || module.controllers.some((controller) => (controller.functionalInjects?.length ?? 0) > 0)) {
|
|
357
|
-
|
|
371
|
+
throw new Error("SC2012: Compiled DI requires constructor injection; property inject() is not supported.");
|
|
358
372
|
}
|
|
359
373
|
}
|
|
360
374
|
renderFactories() {
|
|
@@ -388,6 +402,7 @@ class ModuleGenerator {
|
|
|
388
402
|
lines.push(` jobs: ${this.renderJobs()},`);
|
|
389
403
|
if (this.module.aspects && this.module.aspects.length > 0) {
|
|
390
404
|
lines.push(` aspects: ${this.renderAspects(this.module.aspects)},`);
|
|
405
|
+
lines.push(` aspectPipeline: ${this.renderAspectPipeline(this.module.aspects)},`);
|
|
391
406
|
}
|
|
392
407
|
lines.push(`}`);
|
|
393
408
|
return lines.join(`
|
|
@@ -464,6 +479,7 @@ class ModuleGenerator {
|
|
|
464
479
|
}
|
|
465
480
|
if (route.aspects && route.aspects.length > 0) {
|
|
466
481
|
fields.push(`aspects: ${this.renderAspects(route.aspects)}`);
|
|
482
|
+
fields.push(`aspectPipeline: ${this.renderAspectPipeline(route.aspects)}`);
|
|
467
483
|
}
|
|
468
484
|
const invokerArgs = (route.handlerParams ?? []).map((hp) => {
|
|
469
485
|
if (hp.kind === "param") {
|
|
@@ -544,7 +560,7 @@ ${indent(item, 2)}`).join(",")}
|
|
|
544
560
|
`idempotency: ${JSON.stringify(command.idempotency)}`,
|
|
545
561
|
...command.rpc ? [`rpc: ${JSON.stringify(command.rpc)}`] : [],
|
|
546
562
|
...command.standalone ? ["standalone: true"] : [],
|
|
547
|
-
...command.aspects && command.aspects.length > 0 ? [`aspects: ${this.renderAspects(command.aspects)}`] : []
|
|
563
|
+
...command.aspects && command.aspects.length > 0 ? [`aspects: ${this.renderAspects(command.aspects)}`, `aspectPipeline: ${this.renderAspectPipeline(command.aspects)}`] : []
|
|
548
564
|
];
|
|
549
565
|
return `{ ${fields.join(", ")} }`;
|
|
550
566
|
}).join(", ")}]`;
|
|
@@ -577,6 +593,7 @@ ${indent(item, 2)}`).join(",")}
|
|
|
577
593
|
fields.push(`idempotency: ${JSON.stringify(job.idempotency)}`);
|
|
578
594
|
if (job.aspects && job.aspects.length > 0) {
|
|
579
595
|
fields.push(`aspects: ${this.renderAspects(job.aspects)}`);
|
|
596
|
+
fields.push(`aspectPipeline: ${this.renderAspectPipeline(job.aspects)}`);
|
|
580
597
|
}
|
|
581
598
|
return `{ ${fields.join(", ")}, }`;
|
|
582
599
|
}).join(", ")}]`;
|
|
@@ -584,6 +601,24 @@ ${indent(item, 2)}`).join(",")}
|
|
|
584
601
|
renderAspects(aspects) {
|
|
585
602
|
return `[${aspects.map((aspect) => this.imports.add(aspect.name, aspect.importPath, aspect.importModule)).join(", ")}]`;
|
|
586
603
|
}
|
|
604
|
+
renderAspectPipeline(aspects) {
|
|
605
|
+
const lines = [
|
|
606
|
+
`async (context, next, observe) => {`,
|
|
607
|
+
` const state = { active: true };`,
|
|
608
|
+
` const step${aspects.length} = compiledAspectNext(next, state);`
|
|
609
|
+
];
|
|
610
|
+
for (let index = aspects.length - 1;index >= 0; index--) {
|
|
611
|
+
const aspect = aspects[index];
|
|
612
|
+
if (!aspect)
|
|
613
|
+
continue;
|
|
614
|
+
const name = this.imports.add(aspect.name, aspect.importPath, aspect.importModule);
|
|
615
|
+
const stage = JSON.stringify(`aspect[${index}]:${aspect.name}`);
|
|
616
|
+
lines.push(` const step${index} = compiledAspectNext(() => observeCompiledAspect(observe, ${stage}, () => ${name}(context, step${index + 1})), state);`);
|
|
617
|
+
}
|
|
618
|
+
lines.push(` try { return await step0(); } finally { state.active = false; }`, `}`);
|
|
619
|
+
return lines.join(`
|
|
620
|
+
`);
|
|
621
|
+
}
|
|
587
622
|
renderServicesFactory() {
|
|
588
623
|
return [
|
|
589
624
|
`function create${this.pascal}Services(`,
|
|
@@ -698,7 +733,7 @@ ${indent(item, 2)}`).join(",")}
|
|
|
698
733
|
const args = provider.deps.map((dep, index) => this.typedDepExpr(dep, kind, this.depOptions(provider, dep), `ConstructorParameters<typeof ${useClass}>[${index}]`)).join(", ");
|
|
699
734
|
const local = this.localVar(isMulti ? provider.useClass ?? `${provider.token}Item` : provider.token, kind);
|
|
700
735
|
return {
|
|
701
|
-
constLine: `const ${local} = ${
|
|
736
|
+
constLine: `const ${local} = new ${useClass}(${args});`,
|
|
702
737
|
key,
|
|
703
738
|
expr: local
|
|
704
739
|
};
|
|
@@ -734,32 +769,11 @@ ${indent(item, 2)}`).join(",")}
|
|
|
734
769
|
const key = camelName(controller.className);
|
|
735
770
|
const local = this.localVar(controller.className, kind);
|
|
736
771
|
return {
|
|
737
|
-
constLine: `const ${local} = ${
|
|
772
|
+
constLine: `const ${local} = new ${className}(${args});`,
|
|
738
773
|
key,
|
|
739
774
|
expr: local
|
|
740
775
|
};
|
|
741
776
|
}
|
|
742
|
-
instantiate(className, args, kind, functionalInjects) {
|
|
743
|
-
if (!functionalInjects || functionalInjects.length === 0) {
|
|
744
|
-
return `new ${className}(${args})`;
|
|
745
|
-
}
|
|
746
|
-
const clauses = functionalInjects.map((entry) => {
|
|
747
|
-
const token = this.imports.add(entry.expression, entry.importPath, entry.importModule);
|
|
748
|
-
const value = this.depExpr(entry.token, kind, entry);
|
|
749
|
-
return `if (token === ${token}) return ${value} as T;`;
|
|
750
|
-
});
|
|
751
|
-
const missing = `if (options?.optional) return undefined; throw new Error("Static inject token not available: " + String(token));`;
|
|
752
|
-
const injector = [
|
|
753
|
-
`{`,
|
|
754
|
-
`get<T>(token: unknown, options?: { optional?: boolean; self?: boolean; skipSelf?: boolean; host?: boolean }): T | undefined {`,
|
|
755
|
-
...clauses,
|
|
756
|
-
missing,
|
|
757
|
-
`},`,
|
|
758
|
-
`}`
|
|
759
|
-
].join(`
|
|
760
|
-
`);
|
|
761
|
-
return `runInInjectionContext(${injector}, () => new ${className}(${args}))`;
|
|
762
|
-
}
|
|
763
777
|
localVar(token, kind) {
|
|
764
778
|
const locals = this.locals[kind];
|
|
765
779
|
const existing = locals.get(token);
|
|
@@ -1932,6 +1946,7 @@ var HEADER = "// GENERATED BY @supacloud/compiler — do not edit", INTERFACES =
|
|
|
1932
1946
|
title?: string;
|
|
1933
1947
|
data?: Record<string, unknown>;
|
|
1934
1948
|
aspects?: CompiledAspect[];
|
|
1949
|
+
aspectPipeline?: CompiledAspectPipeline;
|
|
1935
1950
|
invoker?: (
|
|
1936
1951
|
controller: unknown,
|
|
1937
1952
|
request: {
|
|
@@ -1955,6 +1970,7 @@ export interface CompiledCommand {
|
|
|
1955
1970
|
idempotency: "required" | "none";
|
|
1956
1971
|
standalone?: boolean;
|
|
1957
1972
|
aspects?: CompiledAspect[];
|
|
1973
|
+
aspectPipeline?: CompiledAspectPipeline;
|
|
1958
1974
|
}
|
|
1959
1975
|
|
|
1960
1976
|
export interface CompiledJob {
|
|
@@ -1969,6 +1985,7 @@ export interface CompiledJob {
|
|
|
1969
1985
|
maxAttempts?: number;
|
|
1970
1986
|
idempotency?: "required" | "none";
|
|
1971
1987
|
aspects?: CompiledAspect[];
|
|
1988
|
+
aspectPipeline?: CompiledAspectPipeline;
|
|
1972
1989
|
}
|
|
1973
1990
|
|
|
1974
1991
|
export interface CompiledAspectContext {
|
|
@@ -2016,7 +2033,24 @@ export interface CompiledModule {
|
|
|
2016
2033
|
commands: CompiledCommand[];
|
|
2017
2034
|
jobs: CompiledJob[];
|
|
2018
2035
|
aspects?: CompiledAspect[];
|
|
2019
|
-
|
|
2036
|
+
aspectPipeline?: CompiledAspectPipeline;
|
|
2037
|
+
}`, TYPE_GUARDS = `type CompiledAspectObserver = (stage: string, run: () => unknown | Promise<unknown>) => unknown | Promise<unknown>;
|
|
2038
|
+
type CompiledAspectPipeline = (context: CompiledAspectContext, next: () => unknown | Promise<unknown>, observe?: CompiledAspectObserver) => unknown | Promise<unknown>;
|
|
2039
|
+
|
|
2040
|
+
function compiledAspectNext(next: () => unknown | Promise<unknown>, state: { active: boolean }): () => Promise<unknown> {
|
|
2041
|
+
let called = false;
|
|
2042
|
+
return async () => {
|
|
2043
|
+
if (!state.active) throw new Error("Aspect continuation is closed");
|
|
2044
|
+
if (called) throw new Error("Aspect continuation called multiple times");
|
|
2045
|
+
called = true;
|
|
2046
|
+
return await next();
|
|
2047
|
+
};
|
|
2048
|
+
}
|
|
2049
|
+
function observeCompiledAspect(observe: CompiledAspectObserver | undefined, stage: string, run: () => unknown | Promise<unknown>): unknown | Promise<unknown> {
|
|
2050
|
+
return observe ? observe(stage, run) : run();
|
|
2051
|
+
}
|
|
2052
|
+
|
|
2053
|
+
function isRecord(value: unknown): value is Record<string, unknown> {
|
|
2020
2054
|
return typeof value === "object" && value !== null;
|
|
2021
2055
|
}
|
|
2022
2056
|
|
|
@@ -2198,7 +2232,7 @@ var init_graphql_options = __esm(() => {
|
|
|
2198
2232
|
|
|
2199
2233
|
// src/graphql-inputs.ts
|
|
2200
2234
|
import { resolve as resolve3 } from "node:path";
|
|
2201
|
-
import * as
|
|
2235
|
+
import * as ts7 from "@typescript/typescript6";
|
|
2202
2236
|
function graphqlInputPaths(options) {
|
|
2203
2237
|
if (!options.graphql)
|
|
2204
2238
|
return [];
|
|
@@ -2209,7 +2243,7 @@ function graphqlInputPaths(options) {
|
|
|
2209
2243
|
}
|
|
2210
2244
|
const root = resolve3(options.rootDir);
|
|
2211
2245
|
const schema = resolve3(root, options.graphql.schema);
|
|
2212
|
-
const documents =
|
|
2246
|
+
const documents = ts7.sys.readDirectory(root, [".graphql", ".gql"], ["**/node_modules/**", "**/.git/**", resolve3(options.outDir)], options.graphql.documents ?? ["**/*.graphql", "**/*.gql"]).map((path) => resolve3(path)).filter((path) => path !== schema);
|
|
2213
2247
|
return [schema, ...[...new Set(documents)].sort()];
|
|
2214
2248
|
}
|
|
2215
2249
|
var init_graphql_inputs = __esm(() => {
|
|
@@ -2217,7 +2251,7 @@ var init_graphql_inputs = __esm(() => {
|
|
|
2217
2251
|
});
|
|
2218
2252
|
|
|
2219
2253
|
// src/graphql-runtime.ts
|
|
2220
|
-
import * as
|
|
2254
|
+
import * as ts8 from "@typescript/typescript6";
|
|
2221
2255
|
import { resolve as resolve4 } from "node:path";
|
|
2222
2256
|
function renderGraphqlValidators(source, operationNames) {
|
|
2223
2257
|
const fileName = resolve4("/__supacloud_graphql__/contracts.ts");
|
|
@@ -2229,18 +2263,18 @@ function renderGraphqlValidators(source, operationNames) {
|
|
|
2229
2263
|
noPropertyAccessFromIndexSignature: true,
|
|
2230
2264
|
noFallthroughCasesInSwitch: true,
|
|
2231
2265
|
skipLibCheck: false,
|
|
2232
|
-
target:
|
|
2266
|
+
target: ts8.ScriptTarget.ES2022,
|
|
2233
2267
|
lib: ["lib.es2022.d.ts"],
|
|
2234
2268
|
types: [],
|
|
2235
2269
|
noEmit: true
|
|
2236
2270
|
};
|
|
2237
|
-
const host =
|
|
2271
|
+
const host = ts8.createCompilerHost(options);
|
|
2238
2272
|
const getSourceFile = host.getSourceFile.bind(host);
|
|
2239
|
-
host.getSourceFile = (path, languageVersion, onError, shouldCreateNewSourceFile) => path === fileName ?
|
|
2240
|
-
const program =
|
|
2241
|
-
const diagnostics =
|
|
2273
|
+
host.getSourceFile = (path, languageVersion, onError, shouldCreateNewSourceFile) => path === fileName ? ts8.createSourceFile(path, source, languageVersion, true) : getSourceFile(path, languageVersion, onError, shouldCreateNewSourceFile);
|
|
2274
|
+
const program = ts8.createProgram([fileName], options, host);
|
|
2275
|
+
const diagnostics = ts8.getPreEmitDiagnostics(program);
|
|
2242
2276
|
if (diagnostics.length) {
|
|
2243
|
-
throw new Error(`Generated GraphQL types cannot be validated: ${diagnostics.map((item) =>
|
|
2277
|
+
throw new Error(`Generated GraphQL types cannot be validated: ${diagnostics.map((item) => ts8.flattenDiagnosticMessageText(item.messageText, `
|
|
2244
2278
|
`)).join("; ")}`);
|
|
2245
2279
|
}
|
|
2246
2280
|
const checker = program.getTypeChecker();
|
|
@@ -2270,25 +2304,25 @@ function renderGraphqlValidators(source, operationNames) {
|
|
|
2270
2304
|
return name;
|
|
2271
2305
|
}
|
|
2272
2306
|
function expression(type) {
|
|
2273
|
-
if (type.flags &
|
|
2307
|
+
if (type.flags & ts8.TypeFlags.Any)
|
|
2274
2308
|
return unsupported(type);
|
|
2275
|
-
if (type.flags &
|
|
2309
|
+
if (type.flags & ts8.TypeFlags.Unknown)
|
|
2276
2310
|
return "true";
|
|
2277
|
-
if (type.flags &
|
|
2311
|
+
if (type.flags & ts8.TypeFlags.Never)
|
|
2278
2312
|
return "false";
|
|
2279
|
-
if (type.flags &
|
|
2313
|
+
if (type.flags & ts8.TypeFlags.Null)
|
|
2280
2314
|
return "value === null";
|
|
2281
|
-
if (type.flags &
|
|
2315
|
+
if (type.flags & ts8.TypeFlags.Undefined)
|
|
2282
2316
|
return "value === undefined";
|
|
2283
2317
|
if (type.isStringLiteral() || type.isNumberLiteral())
|
|
2284
2318
|
return `value === ${JSON.stringify(type.value)}`;
|
|
2285
|
-
if (type.flags &
|
|
2319
|
+
if (type.flags & ts8.TypeFlags.BooleanLiteral)
|
|
2286
2320
|
return `value === ${checker.typeToString(type)}`;
|
|
2287
|
-
if (type.flags &
|
|
2321
|
+
if (type.flags & ts8.TypeFlags.String)
|
|
2288
2322
|
return 'typeof value === "string"';
|
|
2289
|
-
if (type.flags &
|
|
2323
|
+
if (type.flags & ts8.TypeFlags.Number)
|
|
2290
2324
|
return 'typeof value === "number" && Number.isFinite(value)';
|
|
2291
|
-
if (type.flags &
|
|
2325
|
+
if (type.flags & ts8.TypeFlags.Boolean)
|
|
2292
2326
|
return 'typeof value === "boolean"';
|
|
2293
2327
|
if (type.isUnion())
|
|
2294
2328
|
return type.types.map((part) => `${reference(part)}(value)`).join(" || ");
|
|
@@ -2297,16 +2331,16 @@ function renderGraphqlValidators(source, operationNames) {
|
|
|
2297
2331
|
if (checker.isTupleType(type))
|
|
2298
2332
|
return unsupported(type);
|
|
2299
2333
|
if (checker.isArrayType(type)) {
|
|
2300
|
-
const item = checker.getIndexTypeOfType(type,
|
|
2334
|
+
const item = checker.getIndexTypeOfType(type, ts8.IndexKind.Number);
|
|
2301
2335
|
if (!item)
|
|
2302
2336
|
return unsupported(type);
|
|
2303
2337
|
return `isGraphqlArray(value) && Array.from(value).every(${reference(item)})`;
|
|
2304
2338
|
}
|
|
2305
|
-
if (type.flags &
|
|
2339
|
+
if (type.flags & ts8.TypeFlags.Object) {
|
|
2306
2340
|
if (type.getCallSignatures().length || type.getConstructSignatures().length)
|
|
2307
2341
|
return unsupported(type);
|
|
2308
2342
|
const indexes = checker.getIndexInfosOfType(type);
|
|
2309
|
-
if (indexes.some((index) => !(index.keyType.flags &
|
|
2343
|
+
if (indexes.some((index) => !(index.keyType.flags & ts8.TypeFlags.String)))
|
|
2310
2344
|
return unsupported(type);
|
|
2311
2345
|
const properties = checker.getPropertiesOfType(type).map((property) => {
|
|
2312
2346
|
const declaration = property.valueDeclaration ?? property.declarations?.[0];
|
|
@@ -2315,7 +2349,7 @@ function renderGraphqlValidators(source, operationNames) {
|
|
|
2315
2349
|
const check = reference(checker.getTypeOfSymbolAtLocation(property, declaration));
|
|
2316
2350
|
const key = JSON.stringify(property.name);
|
|
2317
2351
|
const present = `Object.prototype.hasOwnProperty.call(value, ${key})`;
|
|
2318
|
-
return property.flags &
|
|
2352
|
+
return property.flags & ts8.SymbolFlags.Optional ? `(!${present} || ${check}(value[${key}]))` : `(${present} && ${check}(value[${key}]))`;
|
|
2319
2353
|
});
|
|
2320
2354
|
const indexedValues = indexes.map((index) => `Object.values(value).every(${reference(index.type)})`);
|
|
2321
2355
|
return ["isGraphqlRecord(value)", ...properties, ...indexedValues].join(" && ");
|
|
@@ -2642,10 +2676,155 @@ var init_graphql_schema = __esm(() => {
|
|
|
2642
2676
|
init_graphql_options();
|
|
2643
2677
|
});
|
|
2644
2678
|
|
|
2679
|
+
// src/database-contracts.ts
|
|
2680
|
+
import { createHash as createHash10 } from "node:crypto";
|
|
2681
|
+
import { mkdir as mkdir6, readFile as readFile12 } from "node:fs/promises";
|
|
2682
|
+
import { dirname as dirname11, relative as relative12, resolve as resolve16 } from "node:path";
|
|
2683
|
+
import * as ts13 from "@typescript/typescript6";
|
|
2684
|
+
function hash2(value) {
|
|
2685
|
+
return createHash10("sha256").update(value).digest("hex");
|
|
2686
|
+
}
|
|
2687
|
+
function importPath(out, path) {
|
|
2688
|
+
const value = relative12(out, path).replaceAll("\\", "/").replace(/\.(?:d\.)?[cm]?ts$/, "");
|
|
2689
|
+
return value.startsWith(".") ? value : `./${value}`;
|
|
2690
|
+
}
|
|
2691
|
+
function parseDatabaseContractsOptions(value, directory) {
|
|
2692
|
+
if (!value || typeof value !== "object" || Array.isArray(value))
|
|
2693
|
+
throw new TypeError("Expected database contracts configuration");
|
|
2694
|
+
const allowed = ["rootDir", "outDir", "postgrestTypes", "drizzleSchema", "role", "graphql", "migrations"];
|
|
2695
|
+
if (Object.keys(value).some((name) => !allowed.includes(name)))
|
|
2696
|
+
throw new TypeError("Unknown database contracts option");
|
|
2697
|
+
const field = (name) => {
|
|
2698
|
+
const result = Reflect.get(value, name);
|
|
2699
|
+
if (typeof result !== "string" || !result.trim())
|
|
2700
|
+
throw new TypeError(`Missing database contracts ${name}`);
|
|
2701
|
+
return result;
|
|
2702
|
+
};
|
|
2703
|
+
const graphql = Reflect.get(value, "graphql");
|
|
2704
|
+
assertGraphqlOptions(graphql);
|
|
2705
|
+
const migrations = Reflect.get(value, "migrations");
|
|
2706
|
+
if (!Array.isArray(migrations) || !migrations.every((entry) => typeof entry === "string" && entry.endsWith(".sql"))) {
|
|
2707
|
+
throw new TypeError("migrations must be an ordered list of SQL files");
|
|
2708
|
+
}
|
|
2709
|
+
return {
|
|
2710
|
+
rootDir: resolve16(directory, field("rootDir")),
|
|
2711
|
+
outDir: resolve16(directory, field("outDir")),
|
|
2712
|
+
postgrestTypes: resolve16(directory, field("postgrestTypes")),
|
|
2713
|
+
drizzleSchema: resolve16(directory, field("drizzleSchema")),
|
|
2714
|
+
role: field("role"),
|
|
2715
|
+
graphql: { ...graphql, schema: resolve16(directory, graphql.schema) },
|
|
2716
|
+
migrations: migrations.map((file) => resolve16(directory, file))
|
|
2717
|
+
};
|
|
2718
|
+
}
|
|
2719
|
+
async function generateDatabaseContracts(options, check = false) {
|
|
2720
|
+
const rootDir = resolve16(options.rootDir), outDir = resolve16(options.outDir);
|
|
2721
|
+
const postgrestTypes = resolve16(rootDir, options.postgrestTypes), drizzleSchema = resolve16(rootDir, options.drizzleSchema);
|
|
2722
|
+
const snapshot = await readFile12(postgrestTypes, "utf8");
|
|
2723
|
+
const syntax = ts13.createSourceFile(postgrestTypes, snapshot, ts13.ScriptTarget.Latest, true);
|
|
2724
|
+
const database = syntax.statements.find((node) => (ts13.isTypeAliasDeclaration(node) || ts13.isInterfaceDeclaration(node)) && node.name.text === "Database" && node.modifiers?.some((modifier) => modifier.kind === ts13.SyntaxKind.ExportKeyword));
|
|
2725
|
+
if (!database)
|
|
2726
|
+
throw new Error("PostgREST snapshot must export Database from the official type generator");
|
|
2727
|
+
const program = ts13.createProgram([postgrestTypes], {
|
|
2728
|
+
strict: true,
|
|
2729
|
+
noEmit: true,
|
|
2730
|
+
skipLibCheck: true,
|
|
2731
|
+
types: [],
|
|
2732
|
+
target: ts13.ScriptTarget.ES2022,
|
|
2733
|
+
module: ts13.ModuleKind.ESNext,
|
|
2734
|
+
moduleResolution: ts13.ModuleResolutionKind.Bundler
|
|
2735
|
+
});
|
|
2736
|
+
if (ts13.getPreEmitDiagnostics(program).length)
|
|
2737
|
+
throw new Error("PostgREST snapshot has TypeScript errors");
|
|
2738
|
+
const artifacts = await renderGraphql({
|
|
2739
|
+
rootDir,
|
|
2740
|
+
outDir,
|
|
2741
|
+
graphql: options.graphql
|
|
2742
|
+
});
|
|
2743
|
+
if (artifacts.diagnostics.some((item) => item.severity === "error")) {
|
|
2744
|
+
throw new Error(artifacts.diagnostics.map((item) => item.message).join(`
|
|
2745
|
+
`));
|
|
2746
|
+
}
|
|
2747
|
+
const inputs = {};
|
|
2748
|
+
const addInput = async (path) => {
|
|
2749
|
+
const absolute = resolve16(rootDir, path);
|
|
2750
|
+
inputs[relative12(rootDir, absolute).replaceAll("\\", "/")] = hash2(await readFile12(absolute, "utf8"));
|
|
2751
|
+
};
|
|
2752
|
+
await addInput(postgrestTypes);
|
|
2753
|
+
await addInput(drizzleSchema);
|
|
2754
|
+
const drizzleProgram = ts13.createProgram([drizzleSchema], {
|
|
2755
|
+
noEmit: true,
|
|
2756
|
+
moduleResolution: ts13.ModuleResolutionKind.Bundler,
|
|
2757
|
+
module: ts13.ModuleKind.ESNext,
|
|
2758
|
+
target: ts13.ScriptTarget.ES2022,
|
|
2759
|
+
types: [],
|
|
2760
|
+
skipLibCheck: true
|
|
2761
|
+
});
|
|
2762
|
+
for (const source of drizzleProgram.getSourceFiles()) {
|
|
2763
|
+
if (!source.isDeclarationFile && !source.fileName.includes("/node_modules/"))
|
|
2764
|
+
await addInput(source.fileName);
|
|
2765
|
+
}
|
|
2766
|
+
await addInput(resolve16(rootDir, options.graphql.schema));
|
|
2767
|
+
if (new Set(options.migrations.map((path) => resolve16(rootDir, path))).size !== options.migrations.length) {
|
|
2768
|
+
throw new Error("Duplicate migration in database contracts configuration");
|
|
2769
|
+
}
|
|
2770
|
+
for (const path of options.migrations)
|
|
2771
|
+
await addInput(path);
|
|
2772
|
+
const files = {
|
|
2773
|
+
...artifacts.files,
|
|
2774
|
+
"database.ts": [
|
|
2775
|
+
"// GENERATED BY @supacloud/compiler database-contracts. Do not edit.",
|
|
2776
|
+
`export type { Database } from ${JSON.stringify(importPath(outDir, postgrestTypes))};`,
|
|
2777
|
+
'export type { QueryData, QueryResult, QueryError } from "@supabase/supabase-js";',
|
|
2778
|
+
`export type DrizzleSchema = typeof import(${JSON.stringify(importPath(outDir, drizzleSchema))});`,
|
|
2779
|
+
'export * from "./graphql";',
|
|
2780
|
+
""
|
|
2781
|
+
].join(`
|
|
2782
|
+
`)
|
|
2783
|
+
};
|
|
2784
|
+
const manifest = {
|
|
2785
|
+
version: 1,
|
|
2786
|
+
role: options.role,
|
|
2787
|
+
inputs: Object.fromEntries(Object.entries(inputs).sort(([a], [b]) => a.localeCompare(b))),
|
|
2788
|
+
migrationOrder: options.migrations.map((path) => relative12(rootDir, resolve16(rootDir, path)).replaceAll("\\", "/")),
|
|
2789
|
+
outputs: Object.fromEntries(Object.entries(files).sort(([a], [b]) => a.localeCompare(b)).map(([file, text]) => [file, hash2(text)]))
|
|
2790
|
+
};
|
|
2791
|
+
files["database.manifest.json"] = JSON.stringify(manifest, null, 2) + `
|
|
2792
|
+
`;
|
|
2793
|
+
const mismatches = [];
|
|
2794
|
+
for (const [file, content] of Object.entries(files)) {
|
|
2795
|
+
const path = resolve16(outDir, file);
|
|
2796
|
+
let current;
|
|
2797
|
+
try {
|
|
2798
|
+
current = await readFile12(path, "utf8");
|
|
2799
|
+
} catch (error) {
|
|
2800
|
+
if (!(error instanceof Error && ("code" in error) && error.code === "ENOENT"))
|
|
2801
|
+
throw error;
|
|
2802
|
+
}
|
|
2803
|
+
if (current !== content)
|
|
2804
|
+
mismatches.push(file);
|
|
2805
|
+
}
|
|
2806
|
+
if (!check) {
|
|
2807
|
+
await mkdir6(outDir, { recursive: true });
|
|
2808
|
+
for (const [file, content] of Object.entries(files))
|
|
2809
|
+
await writeFileIfChanged(resolve16(outDir, file), content);
|
|
2810
|
+
}
|
|
2811
|
+
return { upToDate: mismatches.length === 0, mismatches, written: check ? [] : mismatches, manifest };
|
|
2812
|
+
}
|
|
2813
|
+
async function runDatabaseContractsFile(path, check = false) {
|
|
2814
|
+
const absolute = resolve16(path);
|
|
2815
|
+
const value = JSON.parse(await readFile12(absolute, "utf8"));
|
|
2816
|
+
return generateDatabaseContracts(parseDatabaseContractsOptions(value, dirname11(absolute)), check);
|
|
2817
|
+
}
|
|
2818
|
+
var init_database_contracts = __esm(() => {
|
|
2819
|
+
init_graphql();
|
|
2820
|
+
init_generate();
|
|
2821
|
+
init_graphql_options();
|
|
2822
|
+
});
|
|
2823
|
+
|
|
2645
2824
|
// src/analyze.ts
|
|
2646
2825
|
import { createHash as createHash3 } from "node:crypto";
|
|
2647
2826
|
import { relative as relative2, resolve as resolvePath, sep as sep2 } from "node:path";
|
|
2648
|
-
import * as
|
|
2827
|
+
import * as ts4 from "@typescript/typescript6";
|
|
2649
2828
|
|
|
2650
2829
|
// src/program.ts
|
|
2651
2830
|
import { createHash as createHash2 } from "node:crypto";
|
|
@@ -3295,6 +3474,7 @@ var COMPILER_DIAGNOSTIC_CODES = {
|
|
|
3295
3474
|
"missing-token-factory": { code: "SC2009", docsUrl: "https://supacloud.dev/errors/SC2009" },
|
|
3296
3475
|
"provider-type-mismatch": { code: "SC2010", docsUrl: "https://supacloud.dev/errors/SC2010" },
|
|
3297
3476
|
"unsupported-provider-helper": { code: "SC2011", docsUrl: "https://supacloud.dev/errors/SC2011" },
|
|
3477
|
+
"runtime-injection-disallowed": { code: "SC2012", docsUrl: "https://supacloud.dev/errors/SC2012" },
|
|
3298
3478
|
"command-missing-permission": { code: "SC4001", docsUrl: "https://supacloud.dev/errors/SC4001" },
|
|
3299
3479
|
"duplicate-command": { code: "SC4002", docsUrl: "https://supacloud.dev/errors/SC4002" },
|
|
3300
3480
|
"route-command-unresolved": { code: "SC4003", docsUrl: "https://supacloud.dev/errors/SC4003" },
|
|
@@ -3341,6 +3521,20 @@ var COMPILER_DIAGNOSTIC_CODES = {
|
|
|
3341
3521
|
function validateGraph(graph, options = false) {
|
|
3342
3522
|
const strict = typeof options === "boolean" ? options : options.strict ?? false;
|
|
3343
3523
|
const diagnostics = [];
|
|
3524
|
+
for (const module of graph.modules) {
|
|
3525
|
+
for (const owner of [...module.providers, ...module.controllers]) {
|
|
3526
|
+
if (owner.functionalInjects?.length)
|
|
3527
|
+
diagnostics.push({
|
|
3528
|
+
severity: "error",
|
|
3529
|
+
code: "runtime-injection-disallowed",
|
|
3530
|
+
errorCode: "SC2012",
|
|
3531
|
+
docsUrl: "https://supacloud.dev/errors/SC2012",
|
|
3532
|
+
file: owner.file,
|
|
3533
|
+
message: "Property inject() requires runtime token resolution. Compiled applications require constructor injection.",
|
|
3534
|
+
suggestion: "Move injected fields into typed constructor parameters with @Inject(TOKEN) where needed."
|
|
3535
|
+
});
|
|
3536
|
+
}
|
|
3537
|
+
}
|
|
3344
3538
|
let moduleBoundaries;
|
|
3345
3539
|
if (typeof options === "object") {
|
|
3346
3540
|
try {
|
|
@@ -3768,7 +3962,7 @@ function validateGraph(graph, options = false) {
|
|
|
3768
3962
|
if (typeof options === "object" && options.commandCapabilities) {
|
|
3769
3963
|
const hostCaps = options.commandCapabilities;
|
|
3770
3964
|
const rpcCaps = command.rpc && Object.hasOwn(hostCaps.rpc ?? {}, command.rpc) ? hostCaps.rpc?.[command.rpc] : undefined;
|
|
3771
|
-
if (hostCaps.requirePersistentAdapters && (!rpcCaps?.boundary || rpcCaps.audit !== true || rpcCaps.idempotency !== true || hostCaps.permission !== true || !command.permission || !command.audit || command.idempotency !== "required" || rpcCaps
|
|
3965
|
+
if (hostCaps.requirePersistentAdapters && (command.rpc !== undefined && (!rpcCaps?.boundary || rpcCaps.audit !== true || rpcCaps.idempotency !== true) || hostCaps.permission !== true || !command.permission || !command.audit || command.idempotency !== "required" || command.rpc !== undefined && rpcCaps?.boundary === "database" && (rpcCaps.transaction !== true || command.transaction !== "required") || command.rpc === undefined && (hostCaps.audit !== true || hostCaps.idempotency !== true || hostCaps.transaction !== true || command.transaction !== "required"))) {
|
|
3772
3966
|
error("command-persistence-required", `Command ${command.name} requires an explicit persistent adapter, permission, audit and idempotency policy.`, module.file, module.line, "Register a named database/external adapter, enable permission checks and declare permission, audit and required idempotency on the command.");
|
|
3773
3967
|
}
|
|
3774
3968
|
if (rpcCaps?.boundary === "external" && (command.transaction === "required" || rpcCaps.transaction === true)) {
|
|
@@ -4121,6 +4315,67 @@ function detectOrphanModules(graph) {
|
|
|
4121
4315
|
}
|
|
4122
4316
|
return diagnostics;
|
|
4123
4317
|
}
|
|
4318
|
+
// src/static-di.ts
|
|
4319
|
+
import * as ts3 from "@typescript/typescript6";
|
|
4320
|
+
var runtimeApis = new Set([
|
|
4321
|
+
"inject",
|
|
4322
|
+
"createEnvironmentInjector",
|
|
4323
|
+
"runInInjectionContext",
|
|
4324
|
+
"EnvironmentInjector",
|
|
4325
|
+
"bootstrapBun",
|
|
4326
|
+
"runInScope",
|
|
4327
|
+
"runInRequestContext",
|
|
4328
|
+
"runInJobContext",
|
|
4329
|
+
"runInTransactionContext"
|
|
4330
|
+
]);
|
|
4331
|
+
function scanRuntimeDi(source, file) {
|
|
4332
|
+
const diagnostics = [];
|
|
4333
|
+
const namespaces = new Set;
|
|
4334
|
+
const report = (node) => diagnostics.push({
|
|
4335
|
+
severity: "error",
|
|
4336
|
+
code: "runtime-injection-disallowed",
|
|
4337
|
+
errorCode: "SC2012",
|
|
4338
|
+
docsUrl: "https://supacloud.dev/errors/SC2012",
|
|
4339
|
+
file,
|
|
4340
|
+
line: source.getLineAndCharacterOfPosition(node.getStart()).line + 1,
|
|
4341
|
+
message: "Compiled applications cannot import runtime DI. Use explicit constructors and generated scope factories."
|
|
4342
|
+
});
|
|
4343
|
+
for (const statement of source.statements) {
|
|
4344
|
+
if (!(ts3.isImportDeclaration(statement) || ts3.isExportDeclaration(statement)) || !statement.moduleSpecifier || !ts3.isStringLiteral(statement.moduleSpecifier) || !/^@supacloud\/app(?:\/|$)/.test(statement.moduleSpecifier.text))
|
|
4345
|
+
continue;
|
|
4346
|
+
if (ts3.isImportDeclaration(statement)) {
|
|
4347
|
+
if (statement.importClause?.isTypeOnly)
|
|
4348
|
+
continue;
|
|
4349
|
+
const binding = statement.importClause?.namedBindings;
|
|
4350
|
+
if (binding && ts3.isNamespaceImport(binding))
|
|
4351
|
+
namespaces.add(binding.name.text);
|
|
4352
|
+
if (binding && ts3.isNamedImports(binding))
|
|
4353
|
+
for (const item of binding.elements) {
|
|
4354
|
+
if (!item.isTypeOnly && runtimeApis.has((item.propertyName ?? item.name).text))
|
|
4355
|
+
report(item);
|
|
4356
|
+
}
|
|
4357
|
+
} else if (!statement.isTypeOnly) {
|
|
4358
|
+
if (!statement.exportClause)
|
|
4359
|
+
report(statement);
|
|
4360
|
+
else if (ts3.isNamedExports(statement.exportClause))
|
|
4361
|
+
for (const item of statement.exportClause.elements) {
|
|
4362
|
+
if (!item.isTypeOnly && runtimeApis.has((item.propertyName ?? item.name).text))
|
|
4363
|
+
report(item);
|
|
4364
|
+
}
|
|
4365
|
+
}
|
|
4366
|
+
}
|
|
4367
|
+
const visit = (node) => {
|
|
4368
|
+
if (ts3.isPropertyAccessExpression(node) && ts3.isIdentifier(node.expression) && namespaces.has(node.expression.text) && runtimeApis.has(node.name.text))
|
|
4369
|
+
report(node);
|
|
4370
|
+
if (ts3.isElementAccessExpression(node) && ts3.isIdentifier(node.expression) && namespaces.has(node.expression.text) && (!ts3.isStringLiteral(node.argumentExpression) || runtimeApis.has(node.argumentExpression.text)))
|
|
4371
|
+
report(node);
|
|
4372
|
+
if (ts3.isVariableDeclaration(node) && node.initializer && ts3.isIdentifier(node.initializer) && namespaces.has(node.initializer.text))
|
|
4373
|
+
report(node);
|
|
4374
|
+
ts3.forEachChild(node, visit);
|
|
4375
|
+
};
|
|
4376
|
+
visit(source);
|
|
4377
|
+
return diagnostics;
|
|
4378
|
+
}
|
|
4124
4379
|
|
|
4125
4380
|
// src/analyze.ts
|
|
4126
4381
|
var DEFAULT_INCLUDE = ["**/*.module.ts", "**/*.ts"];
|
|
@@ -4162,26 +4417,26 @@ function lineOf(node) {
|
|
|
4162
4417
|
return sourceFile.getLineAndCharacterOfPosition(node.getStart(sourceFile)).line + 1;
|
|
4163
4418
|
}
|
|
4164
4419
|
function variableName(decl) {
|
|
4165
|
-
return
|
|
4420
|
+
return ts4.isIdentifier(decl.name) ? decl.name.text : nodeText(decl.name);
|
|
4166
4421
|
}
|
|
4167
4422
|
function propertyName(name) {
|
|
4168
|
-
if (
|
|
4423
|
+
if (ts4.isIdentifier(name) || ts4.isPrivateIdentifier(name))
|
|
4169
4424
|
return name.text;
|
|
4170
|
-
if (
|
|
4425
|
+
if (ts4.isStringLiteral(name) || ts4.isNumericLiteral(name))
|
|
4171
4426
|
return name.text;
|
|
4172
4427
|
return nodeText(name);
|
|
4173
4428
|
}
|
|
4174
4429
|
function parameterName(param) {
|
|
4175
|
-
return
|
|
4430
|
+
return ts4.isIdentifier(param.name) ? param.name.text : nodeText(param.name);
|
|
4176
4431
|
}
|
|
4177
4432
|
function decoratorsOf(node) {
|
|
4178
|
-
return
|
|
4433
|
+
return ts4.canHaveDecorators(node) ? ts4.getDecorators(node) ?? [] : [];
|
|
4179
4434
|
}
|
|
4180
4435
|
function decoratorArguments(dec) {
|
|
4181
|
-
return
|
|
4436
|
+
return ts4.isCallExpression(dec.expression) ? dec.expression.arguments : [];
|
|
4182
4437
|
}
|
|
4183
4438
|
function hasMethod(cls, name) {
|
|
4184
|
-
return cls.members.some((member) => (
|
|
4439
|
+
return cls.members.some((member) => (ts4.isMethodDeclaration(member) || ts4.isGetAccessorDeclaration(member) || ts4.isSetAccessorDeclaration(member)) && member.name !== undefined && propertyName(member.name) === name);
|
|
4185
4440
|
}
|
|
4186
4441
|
function hasDestroyHook(cls) {
|
|
4187
4442
|
return hasMethod(cls, "onDestroy") || hasMethod(cls, "ngOnDestroy");
|
|
@@ -4191,7 +4446,7 @@ function descendantsOfKind(root, predicate) {
|
|
|
4191
4446
|
const visit = (node) => {
|
|
4192
4447
|
if (predicate(node))
|
|
4193
4448
|
result.push(node);
|
|
4194
|
-
|
|
4449
|
+
ts4.forEachChild(node, visit);
|
|
4195
4450
|
};
|
|
4196
4451
|
visit(root);
|
|
4197
4452
|
return result;
|
|
@@ -4200,7 +4455,7 @@ async function analyzeProject(rootDir, include, cache, changedPaths) {
|
|
|
4200
4455
|
const session = cache?.programSession ?? createIncrementalProgramSession(rootDir);
|
|
4201
4456
|
if (cache)
|
|
4202
4457
|
cache.programSession = session;
|
|
4203
|
-
const rootNames =
|
|
4458
|
+
const rootNames = ts4.sys.readDirectory(rootDir, [".ts", ".tsx"], ["node_modules", "dist"], include ?? DEFAULT_INCLUDE);
|
|
4204
4459
|
const update = session.update(rootNames, changedPaths);
|
|
4205
4460
|
const program = update.program;
|
|
4206
4461
|
const checker = program.getTypeChecker();
|
|
@@ -4224,13 +4479,16 @@ async function analyzeProject(rootDir, include, cache, changedPaths) {
|
|
|
4224
4479
|
nativeTraitFiles.set(trait.file, kinds);
|
|
4225
4480
|
}
|
|
4226
4481
|
for (const sf of sourceFiles) {
|
|
4482
|
+
if (!/\.(?:test|spec)\.[cm]?tsx?$/.test(sf.fileName)) {
|
|
4483
|
+
ctx.diagnostics.push(...scanRuntimeDi(sf, sourcePath(rootDir, sf.fileName)));
|
|
4484
|
+
}
|
|
4227
4485
|
indexFile(sf, ctx);
|
|
4228
4486
|
}
|
|
4229
4487
|
const candidates = [];
|
|
4230
4488
|
for (const sf of sourceFiles) {
|
|
4231
4489
|
const traits = nativeTraitFiles.get(sf.fileName);
|
|
4232
4490
|
if (!cache || traits?.has("module")) {
|
|
4233
|
-
for (const cls of sf.statements.filter(
|
|
4491
|
+
for (const cls of sf.statements.filter(ts4.isClassDeclaration)) {
|
|
4234
4492
|
const moduleDec = findDecorator(cls, "Module");
|
|
4235
4493
|
if (!moduleDec)
|
|
4236
4494
|
continue;
|
|
@@ -4247,14 +4505,14 @@ async function analyzeProject(rootDir, include, cache, changedPaths) {
|
|
|
4247
4505
|
}
|
|
4248
4506
|
}
|
|
4249
4507
|
if (!cache || traits?.has("defineModule") || traits?.has("defineFeatureSlice")) {
|
|
4250
|
-
for (const call of descendantsOfKind(sf,
|
|
4508
|
+
for (const call of descendantsOfKind(sf, ts4.isCallExpression)) {
|
|
4251
4509
|
if (!["defineModule", "defineFeatureSlice"].includes(nodeText(call.expression)))
|
|
4252
4510
|
continue;
|
|
4253
4511
|
const parent = call.parent;
|
|
4254
|
-
if (!parent || !
|
|
4512
|
+
if (!parent || !ts4.isVariableDeclaration(parent))
|
|
4255
4513
|
continue;
|
|
4256
4514
|
const arg = call.arguments[0];
|
|
4257
|
-
if (!arg || !
|
|
4515
|
+
if (!arg || !ts4.isObjectLiteralExpression(arg))
|
|
4258
4516
|
continue;
|
|
4259
4517
|
candidates.push({
|
|
4260
4518
|
node: parent,
|
|
@@ -4398,7 +4656,7 @@ async function analyzeProject(rootDir, include, cache, changedPaths) {
|
|
|
4398
4656
|
const controllerDec = findDecorator(classInfo.decl, "Controller");
|
|
4399
4657
|
if (controllerDec) {
|
|
4400
4658
|
const arg = decoratorArguments(controllerDec)[0];
|
|
4401
|
-
const isStandalone = arg &&
|
|
4659
|
+
const isStandalone = arg && ts4.isObjectLiteralExpression(arg) && booleanProp(arg, "standalone");
|
|
4402
4660
|
if (isStandalone) {
|
|
4403
4661
|
const ctrl = parseController(classInfo.decl, ctx);
|
|
4404
4662
|
if (ctrl)
|
|
@@ -4523,16 +4781,16 @@ function collectModuleSourceClosure(module, ctx) {
|
|
|
4523
4781
|
ownedFiles.add(relativeFile);
|
|
4524
4782
|
for (const statement of sourceFile.statements) {
|
|
4525
4783
|
let moduleName;
|
|
4526
|
-
if (
|
|
4784
|
+
if (ts4.isImportDeclaration(statement) && ts4.isStringLiteral(statement.moduleSpecifier)) {
|
|
4527
4785
|
moduleName = statement.moduleSpecifier.text;
|
|
4528
|
-
} else if (
|
|
4786
|
+
} else if (ts4.isExportDeclaration(statement) && statement.moduleSpecifier && ts4.isStringLiteral(statement.moduleSpecifier)) {
|
|
4529
4787
|
moduleName = statement.moduleSpecifier.text;
|
|
4530
|
-
} else if (
|
|
4788
|
+
} else if (ts4.isImportEqualsDeclaration(statement) && ts4.isExternalModuleReference(statement.moduleReference) && ts4.isStringLiteral(statement.moduleReference.expression)) {
|
|
4531
4789
|
moduleName = statement.moduleReference.expression.text;
|
|
4532
4790
|
}
|
|
4533
4791
|
if (!moduleName || moduleName.startsWith("node:"))
|
|
4534
4792
|
continue;
|
|
4535
|
-
const resolved =
|
|
4793
|
+
const resolved = ts4.resolveModuleName(moduleName, sourceFile.fileName, ctx.program.getCompilerOptions(), ts4.sys).resolvedModule?.resolvedFileName;
|
|
4536
4794
|
if (resolved && isProjectSourcePath(resolved, ctx.rootDir) && !enqueued.has(resolved)) {
|
|
4537
4795
|
enqueued.add(resolved);
|
|
4538
4796
|
queue.push(resolved);
|
|
@@ -4554,15 +4812,15 @@ function isProjectSourceFile(sourceFile, rootDir) {
|
|
|
4554
4812
|
return isProjectSourcePath(sourceFile.fileName, rootDir) && /\.(tsx?|mts|cts)$/.test(sourceFile.fileName);
|
|
4555
4813
|
}
|
|
4556
4814
|
function indexFile(sf, ctx) {
|
|
4557
|
-
for (const cls of sf.statements.filter(
|
|
4815
|
+
for (const cls of sf.statements.filter(ts4.isClassDeclaration)) {
|
|
4558
4816
|
const name = cls.name?.text;
|
|
4559
4817
|
if (name && !ctx.classesByName.has(name)) {
|
|
4560
4818
|
ctx.classesByName.set(name, { name, decl: cls, file: sf.fileName });
|
|
4561
4819
|
}
|
|
4562
4820
|
}
|
|
4563
|
-
for (const statement of sf.statements.filter(
|
|
4821
|
+
for (const statement of sf.statements.filter(ts4.isVariableStatement)) {
|
|
4564
4822
|
for (const decl of statement.declarationList.declarations) {
|
|
4565
|
-
if (
|
|
4823
|
+
if (ts4.isIdentifier(decl.name) && !ctx.variablesByName.has(decl.name.text)) {
|
|
4566
4824
|
ctx.variablesByName.set(decl.name.text, decl);
|
|
4567
4825
|
}
|
|
4568
4826
|
const info = parseTokenVariable(decl, sf.fileName);
|
|
@@ -4574,16 +4832,16 @@ function indexFile(sf, ctx) {
|
|
|
4574
4832
|
}
|
|
4575
4833
|
function parseTokenVariable(decl, file) {
|
|
4576
4834
|
const init = decl.initializer;
|
|
4577
|
-
if (!init || !
|
|
4835
|
+
if (!init || !ts4.isNewExpression(init))
|
|
4578
4836
|
return;
|
|
4579
4837
|
if (nodeText(init.expression) !== "InjectionToken")
|
|
4580
4838
|
return;
|
|
4581
4839
|
const [nameArg, optionsArg] = init.arguments ?? [];
|
|
4582
4840
|
const info = { name: variableName(decl), file, line: lineOf(decl) };
|
|
4583
|
-
if (nameArg &&
|
|
4841
|
+
if (nameArg && ts4.isStringLiteral(nameArg)) {
|
|
4584
4842
|
info.stringName = nameArg.text;
|
|
4585
4843
|
}
|
|
4586
|
-
if (optionsArg &&
|
|
4844
|
+
if (optionsArg && ts4.isObjectLiteralExpression(optionsArg)) {
|
|
4587
4845
|
const scope = stringLiteralProp(optionsArg, "scope");
|
|
4588
4846
|
if (scope && isScope(scope)) {
|
|
4589
4847
|
info.scope = scope;
|
|
@@ -4603,22 +4861,22 @@ function parseModule(candidate, nameByNode, ctx) {
|
|
|
4603
4861
|
const { options, className, file, line } = candidate;
|
|
4604
4862
|
const name = nameByNode.get(candidate.node) ?? className;
|
|
4605
4863
|
const featureSpec = parseFeatureSpec(getProp(options, "spec"), ctx);
|
|
4606
|
-
const tags = arrayProp(options, "tags").map((el) =>
|
|
4864
|
+
const tags = arrayProp(options, "tags").map((el) => ts4.isStringLiteral(el) ? el.text : nodeText(el).replace(/['"]/g, "")).filter(Boolean);
|
|
4607
4865
|
const aspects = parseAspectRefs(getProp(options, "aspects"), ctx, `module ${name}`);
|
|
4608
4866
|
const imports = arrayProp(options, "imports").map((el) => {
|
|
4609
4867
|
const unwrapped = unwrapForwardRef(el);
|
|
4610
|
-
const decl =
|
|
4868
|
+
const decl = ts4.isIdentifier(unwrapped) ? resolveDeclaration(unwrapped, ctx)[0] : undefined;
|
|
4611
4869
|
if (decl) {
|
|
4612
4870
|
const known = nameByNode.get(decl);
|
|
4613
4871
|
if (known)
|
|
4614
4872
|
return known;
|
|
4615
|
-
if (
|
|
4873
|
+
if (ts4.isClassDeclaration(decl)) {
|
|
4616
4874
|
const dec = findDecorator(decl, "Module");
|
|
4617
4875
|
const decOptions = dec && decoratorObjectArg(dec);
|
|
4618
4876
|
const decName = decOptions && stringLiteralProp(decOptions, "name");
|
|
4619
4877
|
return decName ?? decl.name?.text ?? nodeText(el);
|
|
4620
4878
|
}
|
|
4621
|
-
if (
|
|
4879
|
+
if (ts4.isVariableDeclaration(decl))
|
|
4622
4880
|
return variableName(decl);
|
|
4623
4881
|
}
|
|
4624
4882
|
return nodeText(el);
|
|
@@ -4632,7 +4890,7 @@ function parseModule(candidate, nameByNode, ctx) {
|
|
|
4632
4890
|
providers.push(...parsedProviders);
|
|
4633
4891
|
continue;
|
|
4634
4892
|
}
|
|
4635
|
-
if (
|
|
4893
|
+
if (ts4.isCallExpression(el)) {
|
|
4636
4894
|
const helper = nodeText(el.expression).split(".").pop() ?? nodeText(el.expression);
|
|
4637
4895
|
warn(ctx, "unsupported-provider-helper", `无法静态展开 provider helper '${helper}';请改用显式 Provider 或实现编译器支持的 helper`, sourcePath(ctx.rootDir, el.getSourceFile().fileName), lineOf(el));
|
|
4638
4896
|
continue;
|
|
@@ -4642,10 +4900,10 @@ function parseModule(candidate, nameByNode, ctx) {
|
|
|
4642
4900
|
providers.push(provider);
|
|
4643
4901
|
}
|
|
4644
4902
|
for (const el of arrayProp(options, "jobs")) {
|
|
4645
|
-
if (!
|
|
4903
|
+
if (!ts4.isIdentifier(el))
|
|
4646
4904
|
continue;
|
|
4647
4905
|
const decl = resolveDeclaration(el, ctx)[0];
|
|
4648
|
-
if (!decl || !
|
|
4906
|
+
if (!decl || !ts4.isClassDeclaration(decl))
|
|
4649
4907
|
continue;
|
|
4650
4908
|
const className = decl.name?.text ?? el.text;
|
|
4651
4909
|
const registeredProvider = providers.find((provider) => provider.token === className || provider.useClass === className);
|
|
@@ -4682,18 +4940,18 @@ function parseModule(candidate, nameByNode, ctx) {
|
|
|
4682
4940
|
const handlerClasses = [];
|
|
4683
4941
|
const seenHandlers = new Set;
|
|
4684
4942
|
const collectHandler = (expr) => {
|
|
4685
|
-
if (!
|
|
4943
|
+
if (!ts4.isIdentifier(expr))
|
|
4686
4944
|
return;
|
|
4687
4945
|
const decl = resolveDeclaration(expr, ctx)[0];
|
|
4688
|
-
if (decl &&
|
|
4946
|
+
if (decl && ts4.isClassDeclaration(decl) && !seenHandlers.has(decl.name?.text ?? "")) {
|
|
4689
4947
|
seenHandlers.add(decl.name?.text ?? "");
|
|
4690
4948
|
handlerClasses.push(decl);
|
|
4691
4949
|
}
|
|
4692
4950
|
};
|
|
4693
4951
|
for (const el of arrayProp(options, "providers")) {
|
|
4694
|
-
if (
|
|
4952
|
+
if (ts4.isIdentifier(el))
|
|
4695
4953
|
collectHandler(el);
|
|
4696
|
-
if (
|
|
4954
|
+
if (ts4.isObjectLiteralExpression(el)) {
|
|
4697
4955
|
const useClass = getProp(el, "useClass");
|
|
4698
4956
|
if (useClass)
|
|
4699
4957
|
collectHandler(useClass);
|
|
@@ -4793,17 +5051,17 @@ function parseFeatureSpec(input, ctx, seen = new Set) {
|
|
|
4793
5051
|
if (seen.has(input))
|
|
4794
5052
|
return;
|
|
4795
5053
|
seen.add(input);
|
|
4796
|
-
if (
|
|
4797
|
-
const local = input.getSourceFile().statements.flatMap((statement) =>
|
|
5054
|
+
if (ts4.isIdentifier(input)) {
|
|
5055
|
+
const local = input.getSourceFile().statements.flatMap((statement) => ts4.isVariableStatement(statement) ? [...statement.declarationList.declarations] : []);
|
|
4798
5056
|
const resolved = resolveDeclaration(input, ctx)[0];
|
|
4799
|
-
const decl = (resolved &&
|
|
4800
|
-
if (decl &&
|
|
5057
|
+
const decl = (resolved && ts4.isVariableDeclaration(resolved) ? resolved : undefined) ?? ctx.variablesByName.get(input.text) ?? local.find((candidate) => ts4.isIdentifier(candidate.name) && candidate.name.text === input.text) ?? descendantsOfKind(input.getSourceFile(), ts4.isVariableDeclaration).find((candidate) => ts4.isIdentifier(candidate.name) && candidate.name.text === input.text);
|
|
5058
|
+
if (decl && ts4.isVariableDeclaration(decl))
|
|
4801
5059
|
return parseFeatureSpec(decl.initializer, ctx, seen);
|
|
4802
5060
|
}
|
|
4803
|
-
if (
|
|
5061
|
+
if (ts4.isCallExpression(input) && nodeText(input.expression) === "defineFeatureSpec") {
|
|
4804
5062
|
return parseFeatureSpec(input.arguments[0], ctx, seen);
|
|
4805
5063
|
}
|
|
4806
|
-
if (
|
|
5064
|
+
if (ts4.isAsExpression(input) || ts4.isSatisfiesExpression(input) || ts4.isParenthesizedExpression(input)) {
|
|
4807
5065
|
return parseFeatureSpec(input.expression, ctx, seen);
|
|
4808
5066
|
}
|
|
4809
5067
|
const invalid = () => {
|
|
@@ -4816,28 +5074,28 @@ function parseFeatureSpec(input, ctx, seen = new Set) {
|
|
|
4816
5074
|
});
|
|
4817
5075
|
return;
|
|
4818
5076
|
};
|
|
4819
|
-
if (!
|
|
5077
|
+
if (!ts4.isObjectLiteralExpression(input))
|
|
4820
5078
|
return invalid();
|
|
4821
5079
|
const name = stringLiteralProp(input, "name");
|
|
4822
5080
|
const statesExpr = getProp(input, "states");
|
|
4823
5081
|
const transitionObject = getProp(input, "transitions");
|
|
4824
|
-
if (!name || !statesExpr || !
|
|
5082
|
+
if (!name || !statesExpr || !ts4.isArrayLiteralExpression(statesExpr) || statesExpr.elements.some((state) => !ts4.isStringLiteral(state)) || !transitionObject || !ts4.isObjectLiteralExpression(transitionObject) || input.properties.some((property) => !ts4.isPropertyAssignment(property))) {
|
|
4825
5083
|
return invalid();
|
|
4826
5084
|
}
|
|
4827
5085
|
const states = [];
|
|
4828
5086
|
for (const state of statesExpr.elements) {
|
|
4829
|
-
if (!
|
|
5087
|
+
if (!ts4.isStringLiteral(state))
|
|
4830
5088
|
return invalid();
|
|
4831
5089
|
states.push(state.text);
|
|
4832
5090
|
}
|
|
4833
5091
|
const transitions = [];
|
|
4834
5092
|
for (const property of transitionObject.properties) {
|
|
4835
|
-
if (!
|
|
5093
|
+
if (!ts4.isPropertyAssignment(property) || ts4.isComputedPropertyName(property.name) || !ts4.isObjectLiteralExpression(property.initializer))
|
|
4836
5094
|
return invalid();
|
|
4837
5095
|
const options = property.initializer;
|
|
4838
5096
|
const from = stringLiteralProp(options, "from");
|
|
4839
5097
|
const to = stringLiteralProp(options, "to");
|
|
4840
|
-
if (!from || !to || options.properties.some((prop) => !
|
|
5098
|
+
if (!from || !to || options.properties.some((prop) => !ts4.isPropertyAssignment(prop)) || ["permission", "command", "route", "audit"].some((key) => getProp(options, key) && !stringLiteralProp(options, key)) || ["transaction", "idempotency"].some((key) => getProp(options, key) && !commandModeProp(options, key))) {
|
|
4841
5099
|
return invalid();
|
|
4842
5100
|
}
|
|
4843
5101
|
const permission = stringLiteralProp(options, "permission");
|
|
@@ -4864,21 +5122,21 @@ function resolveStaticObjectLiteral(input, ctx, seen = new Set) {
|
|
|
4864
5122
|
if (!input || seen.has(input))
|
|
4865
5123
|
return;
|
|
4866
5124
|
seen.add(input);
|
|
4867
|
-
if (
|
|
5125
|
+
if (ts4.isAsExpression(input) || ts4.isSatisfiesExpression(input) || ts4.isParenthesizedExpression(input)) {
|
|
4868
5126
|
return resolveStaticObjectLiteral(input.expression, ctx, seen);
|
|
4869
5127
|
}
|
|
4870
|
-
if (
|
|
4871
|
-
const declaration = resolveDeclaration(input, ctx).find(
|
|
5128
|
+
if (ts4.isIdentifier(input)) {
|
|
5129
|
+
const declaration = resolveDeclaration(input, ctx).find(ts4.isVariableDeclaration);
|
|
4872
5130
|
return declaration?.initializer ? resolveStaticObjectLiteral(declaration.initializer, ctx, seen) : undefined;
|
|
4873
5131
|
}
|
|
4874
|
-
if (
|
|
5132
|
+
if (ts4.isCallExpression(input)) {
|
|
4875
5133
|
const expressionName = nodeText(input.expression);
|
|
4876
5134
|
if (expressionName === "defineRouteContract" || expressionName.endsWith(".defineRouteContract")) {
|
|
4877
5135
|
return resolveStaticObjectLiteral(input.arguments[0], ctx, seen);
|
|
4878
5136
|
}
|
|
4879
5137
|
return;
|
|
4880
5138
|
}
|
|
4881
|
-
return
|
|
5139
|
+
return ts4.isObjectLiteralExpression(input) ? input : undefined;
|
|
4882
5140
|
}
|
|
4883
5141
|
function commandModeProp(object, name) {
|
|
4884
5142
|
const value = stringLiteralProp(object, name);
|
|
@@ -4913,9 +5171,9 @@ function parseProvider(el, exportsSet, ctx) {
|
|
|
4913
5171
|
const file = sourcePath(ctx.rootDir, el.getSourceFile().fileName);
|
|
4914
5172
|
const line = lineOf(el);
|
|
4915
5173
|
const unwrappedEl = unwrapForwardRef(el);
|
|
4916
|
-
if (
|
|
5174
|
+
if (ts4.isIdentifier(unwrappedEl)) {
|
|
4917
5175
|
const decl = resolveDeclaration(unwrappedEl, ctx)[0];
|
|
4918
|
-
const cls = decl &&
|
|
5176
|
+
const cls = decl && ts4.isClassDeclaration(decl) ? decl : undefined;
|
|
4919
5177
|
const className = cls?.name?.text ?? unwrappedEl.text;
|
|
4920
5178
|
const { deps, optionalDeps, selfDeps, skipSelfDeps, hostDeps, functionalInjects, missing } = cls ? classDeps(cls, ctx) : { deps: [], optionalDeps: [], selfDeps: [], skipSelfDeps: [], hostDeps: [], functionalInjects: [], missing: false };
|
|
4921
5179
|
const injectable = cls ? parseInjectableOptions(cls, ctx) : undefined;
|
|
@@ -4942,7 +5200,7 @@ function parseProvider(el, exportsSet, ctx) {
|
|
|
4942
5200
|
...cls ? { importPath: modulePath(ctx.rootDir, cls.getSourceFile().fileName) } : {}
|
|
4943
5201
|
};
|
|
4944
5202
|
}
|
|
4945
|
-
if (!
|
|
5203
|
+
if (!ts4.isObjectLiteralExpression(el))
|
|
4946
5204
|
return;
|
|
4947
5205
|
const provideExpr = getProp(el, "provide");
|
|
4948
5206
|
if (!provideExpr)
|
|
@@ -4957,8 +5215,8 @@ function parseProvider(el, exportsSet, ctx) {
|
|
|
4957
5215
|
const useExistingExpr = getProp(el, "useExisting");
|
|
4958
5216
|
if (useClassExpr) {
|
|
4959
5217
|
const unwrappedClass = unwrapForwardRef(useClassExpr);
|
|
4960
|
-
const decl =
|
|
4961
|
-
const cls = decl &&
|
|
5218
|
+
const decl = ts4.isIdentifier(unwrappedClass) ? resolveDeclaration(unwrappedClass, ctx)[0] : undefined;
|
|
5219
|
+
const cls = decl && ts4.isClassDeclaration(decl) ? decl : undefined;
|
|
4962
5220
|
const useClass = cls?.name?.text ?? nodeText(unwrappedClass);
|
|
4963
5221
|
let deps = explicitDeps;
|
|
4964
5222
|
let optionalDeps = [];
|
|
@@ -5014,7 +5272,7 @@ function parseProvider(el, exportsSet, ctx) {
|
|
|
5014
5272
|
}
|
|
5015
5273
|
if (useValueExpr) {
|
|
5016
5274
|
validateProviderCompatibility(provideExpr, useValueExpr, "value", token, ctx, file, line);
|
|
5017
|
-
const importPath =
|
|
5275
|
+
const importPath = ts4.isIdentifier(useValueExpr) ? importPathOf(useValueExpr, ctx) : undefined;
|
|
5018
5276
|
return {
|
|
5019
5277
|
token,
|
|
5020
5278
|
tokenKind,
|
|
@@ -5030,12 +5288,12 @@ function parseProvider(el, exportsSet, ctx) {
|
|
|
5030
5288
|
};
|
|
5031
5289
|
}
|
|
5032
5290
|
if (useFactoryExpr) {
|
|
5033
|
-
const factoryName =
|
|
5291
|
+
const factoryName = ts4.isIdentifier(useFactoryExpr) ? (() => {
|
|
5034
5292
|
const decl = resolveDeclaration(useFactoryExpr, ctx)[0];
|
|
5035
|
-
return decl && (
|
|
5293
|
+
return decl && (ts4.isFunctionDeclaration(decl) || ts4.isVariableDeclaration(decl)) ? (ts4.isFunctionDeclaration(decl) ? decl.name?.text : variableName(decl)) ?? useFactoryExpr.text : useFactoryExpr.text;
|
|
5036
5294
|
})() : nodeText(useFactoryExpr);
|
|
5037
5295
|
validateProviderCompatibility(provideExpr, useFactoryExpr, "factory", token, ctx, file, line);
|
|
5038
|
-
const importPath =
|
|
5296
|
+
const importPath = ts4.isIdentifier(useFactoryExpr) ? importPathOf(useFactoryExpr, ctx) : undefined;
|
|
5039
5297
|
return {
|
|
5040
5298
|
token,
|
|
5041
5299
|
tokenKind,
|
|
@@ -5071,20 +5329,20 @@ function parseProvider(el, exportsSet, ctx) {
|
|
|
5071
5329
|
function expandProviderExpressions(expressions, ctx, seen = new Set) {
|
|
5072
5330
|
const result = [];
|
|
5073
5331
|
for (const expression of expressions) {
|
|
5074
|
-
if (
|
|
5332
|
+
if (ts4.isSpreadElement(expression)) {
|
|
5075
5333
|
result.push(...expandProviderExpressions([expression.expression], ctx, seen));
|
|
5076
5334
|
continue;
|
|
5077
5335
|
}
|
|
5078
|
-
if (
|
|
5336
|
+
if (ts4.isIdentifier(expression)) {
|
|
5079
5337
|
const declaration = resolveDeclaration(expression, ctx)[0];
|
|
5080
|
-
if (declaration &&
|
|
5338
|
+
if (declaration && ts4.isVariableDeclaration(declaration) && declaration.initializer) {
|
|
5081
5339
|
const key = `${declaration.getSourceFile().fileName}:${declaration.pos}`;
|
|
5082
5340
|
if (seen.has(key))
|
|
5083
5341
|
continue;
|
|
5084
5342
|
const initializer = declaration.initializer;
|
|
5085
|
-
if (
|
|
5343
|
+
if (ts4.isCallExpression(initializer) && isProviderHelper(initializer, "makeEnvironmentProviders")) {
|
|
5086
5344
|
const nested = initializer.arguments[0];
|
|
5087
|
-
if (nested &&
|
|
5345
|
+
if (nested && ts4.isArrayLiteralExpression(nested)) {
|
|
5088
5346
|
seen.add(key);
|
|
5089
5347
|
result.push(...expandProviderExpressions([...nested.elements], ctx, seen));
|
|
5090
5348
|
seen.delete(key);
|
|
@@ -5093,9 +5351,9 @@ function expandProviderExpressions(expressions, ctx, seen = new Set) {
|
|
|
5093
5351
|
}
|
|
5094
5352
|
}
|
|
5095
5353
|
}
|
|
5096
|
-
if (
|
|
5354
|
+
if (ts4.isCallExpression(expression) && isProviderHelper(expression, "makeEnvironmentProviders")) {
|
|
5097
5355
|
const nested = expression.arguments[0];
|
|
5098
|
-
if (nested &&
|
|
5356
|
+
if (nested && ts4.isArrayLiteralExpression(nested)) {
|
|
5099
5357
|
result.push(...expandProviderExpressions([...nested.elements], ctx, seen));
|
|
5100
5358
|
continue;
|
|
5101
5359
|
}
|
|
@@ -5108,7 +5366,7 @@ function isProviderHelper(expression, name) {
|
|
|
5108
5366
|
return nodeText(expression.expression).split(".").pop() === name;
|
|
5109
5367
|
}
|
|
5110
5368
|
function parseFunctionalProvider(expression, exportsSet, ctx) {
|
|
5111
|
-
if (!
|
|
5369
|
+
if (!ts4.isCallExpression(expression))
|
|
5112
5370
|
return;
|
|
5113
5371
|
const helper = nodeText(expression.expression).split(".").pop();
|
|
5114
5372
|
const args = expression.arguments;
|
|
@@ -5121,7 +5379,7 @@ function parseFunctionalProvider(expression, exportsSet, ctx) {
|
|
|
5121
5379
|
return [];
|
|
5122
5380
|
const { name: token, kind: tokenKind } = tokenNameOf(tokenExpr, ctx);
|
|
5123
5381
|
validateProviderCompatibility(tokenExpr, valueExpr, "value", token, ctx, file, line);
|
|
5124
|
-
const importPath =
|
|
5382
|
+
const importPath = ts4.isIdentifier(valueExpr) ? importPathOf(valueExpr, ctx) : undefined;
|
|
5125
5383
|
return [{
|
|
5126
5384
|
token,
|
|
5127
5385
|
tokenKind,
|
|
@@ -5140,7 +5398,7 @@ function parseFunctionalProvider(expression, exportsSet, ctx) {
|
|
|
5140
5398
|
if (!initializer)
|
|
5141
5399
|
return [];
|
|
5142
5400
|
const token = helper === "provideAppInitializer" ? "APP_INITIALIZER" : "ENVIRONMENT_INITIALIZER";
|
|
5143
|
-
const importPath =
|
|
5401
|
+
const importPath = ts4.isIdentifier(initializer) ? importPathOf(initializer, ctx) : undefined;
|
|
5144
5402
|
return [{
|
|
5145
5403
|
token,
|
|
5146
5404
|
tokenKind: "injection-token",
|
|
@@ -5159,7 +5417,7 @@ function parseFunctionalProvider(expression, exportsSet, ctx) {
|
|
|
5159
5417
|
const providers = [];
|
|
5160
5418
|
const routes = args[0];
|
|
5161
5419
|
if (routes) {
|
|
5162
|
-
const importPath =
|
|
5420
|
+
const importPath = ts4.isIdentifier(routes) ? importPathOf(routes, ctx) : undefined;
|
|
5163
5421
|
providers.push({
|
|
5164
5422
|
token: "ROUTE_CONFIG",
|
|
5165
5423
|
tokenKind: "injection-token",
|
|
@@ -5174,7 +5432,7 @@ function parseFunctionalProvider(expression, exportsSet, ctx) {
|
|
|
5174
5432
|
});
|
|
5175
5433
|
}
|
|
5176
5434
|
for (const feature of args.slice(1)) {
|
|
5177
|
-
if (!
|
|
5435
|
+
if (!ts4.isCallExpression(feature))
|
|
5178
5436
|
continue;
|
|
5179
5437
|
const featureName = nodeText(feature.expression).split(".").pop();
|
|
5180
5438
|
if (featureName === "withRouterConfig" && feature.arguments[0]) {
|
|
@@ -5191,8 +5449,8 @@ function parseFunctionalProvider(expression, exportsSet, ctx) {
|
|
|
5191
5449
|
});
|
|
5192
5450
|
} else if (featureName === "withTitleStrategy" && feature.arguments[0]) {
|
|
5193
5451
|
const strategy = feature.arguments[0];
|
|
5194
|
-
const isClass =
|
|
5195
|
-
const importPath =
|
|
5452
|
+
const isClass = ts4.isIdentifier(strategy) && Boolean(resolveDeclaration(strategy, ctx).find((declaration) => ts4.isClassDeclaration(declaration)));
|
|
5453
|
+
const importPath = ts4.isIdentifier(strategy) ? importPathOf(strategy, ctx) : undefined;
|
|
5196
5454
|
providers.push({
|
|
5197
5455
|
token: "TITLE_STRATEGY",
|
|
5198
5456
|
tokenKind: "injection-token",
|
|
@@ -5224,14 +5482,14 @@ function parseFunctionalProvider(expression, exportsSet, ctx) {
|
|
|
5224
5482
|
importModule: "@supacloud/app"
|
|
5225
5483
|
}];
|
|
5226
5484
|
for (const feature of args) {
|
|
5227
|
-
if (!
|
|
5485
|
+
if (!ts4.isCallExpression(feature))
|
|
5228
5486
|
continue;
|
|
5229
5487
|
const featureName = nodeText(feature.expression).split(".").pop();
|
|
5230
5488
|
if (featureName === "withInterceptors") {
|
|
5231
5489
|
for (const interceptorArg of feature.arguments) {
|
|
5232
|
-
const values =
|
|
5490
|
+
const values = ts4.isArrayLiteralExpression(interceptorArg) ? [...interceptorArg.elements] : [interceptorArg];
|
|
5233
5491
|
for (const value of values) {
|
|
5234
|
-
const importPath =
|
|
5492
|
+
const importPath = ts4.isIdentifier(value) ? importPathOf(value, ctx) : undefined;
|
|
5235
5493
|
providers.push({
|
|
5236
5494
|
token: "HTTP_INTERCEPTORS",
|
|
5237
5495
|
tokenKind: "injection-token",
|
|
@@ -5278,9 +5536,9 @@ function providerTokenValueType(expr, ctx) {
|
|
|
5278
5536
|
const typeArguments = typeArgumentsOf(type, ctx);
|
|
5279
5537
|
if (typeArguments.length > 0)
|
|
5280
5538
|
return typeArguments[0];
|
|
5281
|
-
if (
|
|
5539
|
+
if (ts4.isIdentifier(expr)) {
|
|
5282
5540
|
const declaration = resolveDeclaration(expr, ctx)[0];
|
|
5283
|
-
if (declaration &&
|
|
5541
|
+
if (declaration && ts4.isClassDeclaration(declaration)) {
|
|
5284
5542
|
return declaredClassType(declaration, ctx);
|
|
5285
5543
|
}
|
|
5286
5544
|
}
|
|
@@ -5288,9 +5546,9 @@ function providerTokenValueType(expr, ctx) {
|
|
|
5288
5546
|
}
|
|
5289
5547
|
function providerImplementationType(expr, kind, ctx) {
|
|
5290
5548
|
if (kind === "class" || kind === "existing") {
|
|
5291
|
-
if (
|
|
5549
|
+
if (ts4.isIdentifier(expr)) {
|
|
5292
5550
|
const declaration = resolveDeclaration(expr, ctx)[0];
|
|
5293
|
-
if (declaration &&
|
|
5551
|
+
if (declaration && ts4.isClassDeclaration(declaration)) {
|
|
5294
5552
|
return declaredClassType(declaration, ctx);
|
|
5295
5553
|
}
|
|
5296
5554
|
}
|
|
@@ -5300,7 +5558,7 @@ function providerImplementationType(expr, kind, ctx) {
|
|
|
5300
5558
|
}
|
|
5301
5559
|
if (kind === "factory") {
|
|
5302
5560
|
const type = ctx.checker.getTypeAtLocation(expr);
|
|
5303
|
-
const signature = ctx.checker.getSignaturesOfType(type,
|
|
5561
|
+
const signature = ctx.checker.getSignaturesOfType(type, ts4.SignatureKind.Call)[0];
|
|
5304
5562
|
return signature?.getReturnType();
|
|
5305
5563
|
}
|
|
5306
5564
|
return ctx.checker.getTypeAtLocation(expr);
|
|
@@ -5319,16 +5577,16 @@ function isTypeReference(type) {
|
|
|
5319
5577
|
return "target" in type;
|
|
5320
5578
|
}
|
|
5321
5579
|
function isUnknownOrAny(type) {
|
|
5322
|
-
return (type.flags & (
|
|
5580
|
+
return (type.flags & (ts4.TypeFlags.Any | ts4.TypeFlags.Unknown)) !== 0;
|
|
5323
5581
|
}
|
|
5324
5582
|
function parseController(input, ctx) {
|
|
5325
5583
|
let decl;
|
|
5326
|
-
if (
|
|
5584
|
+
if (ts4.isClassDeclaration(input)) {
|
|
5327
5585
|
decl = input;
|
|
5328
5586
|
} else {
|
|
5329
5587
|
const unwrapped = unwrapForwardRef(input);
|
|
5330
|
-
const resolved =
|
|
5331
|
-
if (resolved &&
|
|
5588
|
+
const resolved = ts4.isIdentifier(unwrapped) ? resolveDeclaration(unwrapped, ctx)[0] : undefined;
|
|
5589
|
+
if (resolved && ts4.isClassDeclaration(resolved)) {
|
|
5332
5590
|
decl = resolved;
|
|
5333
5591
|
}
|
|
5334
5592
|
}
|
|
@@ -5341,9 +5599,9 @@ function parseController(input, ctx) {
|
|
|
5341
5599
|
let standalone;
|
|
5342
5600
|
const pathArg = decoratorArguments(controllerDec)[0];
|
|
5343
5601
|
if (pathArg) {
|
|
5344
|
-
if (
|
|
5602
|
+
if (ts4.isStringLiteral(pathArg)) {
|
|
5345
5603
|
path = pathArg.text;
|
|
5346
|
-
} else if (
|
|
5604
|
+
} else if (ts4.isObjectLiteralExpression(pathArg)) {
|
|
5347
5605
|
const p = stringLiteralProp(pathArg, "path");
|
|
5348
5606
|
if (p)
|
|
5349
5607
|
path = p;
|
|
@@ -5366,7 +5624,7 @@ function parseController(input, ctx) {
|
|
|
5366
5624
|
}
|
|
5367
5625
|
}
|
|
5368
5626
|
}
|
|
5369
|
-
for (const method of decl.members.filter(
|
|
5627
|
+
for (const method of decl.members.filter(ts4.isMethodDeclaration)) {
|
|
5370
5628
|
for (const dec of decoratorsOf(method)) {
|
|
5371
5629
|
const name = decoratorName2(dec);
|
|
5372
5630
|
const httpMethod = name ? ROUTE_DECORATORS[name] : undefined;
|
|
@@ -5374,7 +5632,7 @@ function parseController(input, ctx) {
|
|
|
5374
5632
|
continue;
|
|
5375
5633
|
const args = decoratorArguments(dec);
|
|
5376
5634
|
const pathArg = args[0];
|
|
5377
|
-
const routePath = pathArg &&
|
|
5635
|
+
const routePath = pathArg && ts4.isStringLiteral(pathArg) ? pathArg.text : "/";
|
|
5378
5636
|
const route = {
|
|
5379
5637
|
method: httpMethod,
|
|
5380
5638
|
path: routePath,
|
|
@@ -5445,12 +5703,12 @@ function parseController(input, ctx) {
|
|
|
5445
5703
|
} else if (dName === "Headers") {
|
|
5446
5704
|
hasBindingDecorator = true;
|
|
5447
5705
|
const argument = dArgs[0];
|
|
5448
|
-
const bindingName = argument !== undefined &&
|
|
5706
|
+
const bindingName = argument !== undefined && ts4.isStringLiteral(argument) ? argument.text : undefined;
|
|
5449
5707
|
paramNode = { name: pName, kind: "headers", ...bindingName === undefined ? {} : { bindingName } };
|
|
5450
5708
|
} else if (dName === "Cookie") {
|
|
5451
5709
|
hasBindingDecorator = true;
|
|
5452
5710
|
const argument = dArgs[0];
|
|
5453
|
-
const bindingName = argument !== undefined &&
|
|
5711
|
+
const bindingName = argument !== undefined && ts4.isStringLiteral(argument) ? argument.text : undefined;
|
|
5454
5712
|
paramNode = { name: pName, kind: "cookie", ...bindingName === undefined ? {} : { bindingName } };
|
|
5455
5713
|
}
|
|
5456
5714
|
}
|
|
@@ -5512,20 +5770,20 @@ function parseController(input, ctx) {
|
|
|
5512
5770
|
}
|
|
5513
5771
|
} else if (dName === "Title") {
|
|
5514
5772
|
const tArg = mArgs[0];
|
|
5515
|
-
if (tArg &&
|
|
5773
|
+
if (tArg && ts4.isStringLiteral(tArg)) {
|
|
5516
5774
|
route.title = tArg.text;
|
|
5517
5775
|
}
|
|
5518
5776
|
} else if (dName === "Data") {
|
|
5519
5777
|
const dArg = mArgs[0];
|
|
5520
|
-
if (dArg &&
|
|
5778
|
+
if (dArg && ts4.isObjectLiteralExpression(dArg)) {
|
|
5521
5779
|
route.data = { ...route.data, ...parseObjectLiteralValues(dArg) };
|
|
5522
5780
|
}
|
|
5523
5781
|
} else if (dName === "Resolve") {
|
|
5524
5782
|
const rArg = mArgs[0];
|
|
5525
|
-
if (rArg &&
|
|
5783
|
+
if (rArg && ts4.isObjectLiteralExpression(rArg)) {
|
|
5526
5784
|
const resolvers = route.resolvers ?? {};
|
|
5527
5785
|
for (const prop of rArg.properties) {
|
|
5528
|
-
if (
|
|
5786
|
+
if (ts4.isPropertyAssignment(prop)) {
|
|
5529
5787
|
const rName = propertyName(prop.name);
|
|
5530
5788
|
const init = prop.initializer;
|
|
5531
5789
|
if (init)
|
|
@@ -5556,7 +5814,7 @@ function parseController(input, ctx) {
|
|
|
5556
5814
|
});
|
|
5557
5815
|
}
|
|
5558
5816
|
const contract = getProp(optionsObject, "contract");
|
|
5559
|
-
if (contract &&
|
|
5817
|
+
if (contract && ts4.isObjectLiteralExpression(contract)) {
|
|
5560
5818
|
route.contract = {};
|
|
5561
5819
|
for (const field of ["body", "response", "evidence"]) {
|
|
5562
5820
|
const value = stringLiteralProp(contract, field);
|
|
@@ -5574,15 +5832,15 @@ function parseController(input, ctx) {
|
|
|
5574
5832
|
}
|
|
5575
5833
|
for (const field of ["body", "params", "query", "headers", "cookie", "response"]) {
|
|
5576
5834
|
const schemaExpr = getProp(optionsObject, field);
|
|
5577
|
-
if (schemaExpr &&
|
|
5835
|
+
if (schemaExpr && ts4.isIdentifier(schemaExpr)) {
|
|
5578
5836
|
route[field] = nodeText(schemaExpr);
|
|
5579
5837
|
const importPath = importPathOf(schemaExpr, ctx);
|
|
5580
5838
|
if (importPath)
|
|
5581
5839
|
schemaImports[schemaExpr.text] = importPath;
|
|
5582
5840
|
const declaration = resolveDeclaration(schemaExpr, ctx)[0];
|
|
5583
5841
|
const typeName = ctx.checker.typeToString(ctx.checker.getTypeAtLocation(schemaExpr));
|
|
5584
|
-
const initializer = declaration &&
|
|
5585
|
-
const opaque = /\bT(?:Unknown|Any)\b/.test(typeName) || initializer &&
|
|
5842
|
+
const initializer = declaration && ts4.isVariableDeclaration(declaration) ? declaration.initializer : undefined;
|
|
5843
|
+
const opaque = /\bT(?:Unknown|Any)\b/.test(typeName) || initializer && ts4.isCallExpression(initializer) && ts4.isPropertyAccessExpression(initializer.expression) && ["Unknown", "Any"].includes(initializer.expression.name.text);
|
|
5586
5844
|
(route.schemaKinds ??= {})[field] = opaque ? "opaque" : "declared";
|
|
5587
5845
|
}
|
|
5588
5846
|
}
|
|
@@ -5591,7 +5849,7 @@ function parseController(input, ctx) {
|
|
|
5591
5849
|
const responses = {};
|
|
5592
5850
|
const selectors = new Map;
|
|
5593
5851
|
for (const property of responsesObject.properties) {
|
|
5594
|
-
if (!
|
|
5852
|
+
if (!ts4.isPropertyAssignment(property) || ts4.isComputedPropertyName(property.name))
|
|
5595
5853
|
continue;
|
|
5596
5854
|
const status = propertyName(property.name);
|
|
5597
5855
|
if (!isRouteResponseSelector(status)) {
|
|
@@ -5623,7 +5881,7 @@ function parseController(input, ctx) {
|
|
|
5623
5881
|
}
|
|
5624
5882
|
selectors.set(canonical, status);
|
|
5625
5883
|
const schemaExpr = property.initializer;
|
|
5626
|
-
if (!
|
|
5884
|
+
if (!ts4.isIdentifier(schemaExpr)) {
|
|
5627
5885
|
ctx.diagnostics.push({
|
|
5628
5886
|
severity: "error",
|
|
5629
5887
|
code: "invalid-route-response-map",
|
|
@@ -5638,8 +5896,8 @@ function parseController(input, ctx) {
|
|
|
5638
5896
|
schemaImports[schemaExpr.text] = importPath;
|
|
5639
5897
|
const declaration = resolveDeclaration(schemaExpr, ctx)[0];
|
|
5640
5898
|
const typeName = ctx.checker.typeToString(ctx.checker.getTypeAtLocation(schemaExpr));
|
|
5641
|
-
const initializer = declaration &&
|
|
5642
|
-
const opaque = /\bT(?:Unknown|Any)\b/.test(typeName) || initializer &&
|
|
5899
|
+
const initializer = declaration && ts4.isVariableDeclaration(declaration) ? declaration.initializer : undefined;
|
|
5900
|
+
const opaque = /\bT(?:Unknown|Any)\b/.test(typeName) || initializer && ts4.isCallExpression(initializer) && ts4.isPropertyAccessExpression(initializer.expression) && ["Unknown", "Any"].includes(initializer.expression.name.text);
|
|
5643
5901
|
const previousKind = route.schemaKinds?.response;
|
|
5644
5902
|
(route.schemaKinds ??= {}).response = opaque || previousKind === "opaque" ? "opaque" : "declared";
|
|
5645
5903
|
}
|
|
@@ -5647,18 +5905,18 @@ function parseController(input, ctx) {
|
|
|
5647
5905
|
route.responses = responses;
|
|
5648
5906
|
}
|
|
5649
5907
|
const commandExpr = getProp(optionsObject, "command");
|
|
5650
|
-
if (commandExpr &&
|
|
5908
|
+
if (commandExpr && ts4.isIdentifier(commandExpr)) {
|
|
5651
5909
|
const commandDecl = resolveDeclaration(commandExpr, ctx)[0];
|
|
5652
|
-
route.command = commandDecl &&
|
|
5910
|
+
route.command = commandDecl && ts4.isClassDeclaration(commandDecl) ? commandDecl.name?.text ?? commandExpr.text : commandExpr.text;
|
|
5653
5911
|
}
|
|
5654
5912
|
const guardsExpr = getProp(optionsObject, "guards");
|
|
5655
|
-
if (guardsExpr &&
|
|
5913
|
+
if (guardsExpr && ts4.isArrayLiteralExpression(guardsExpr)) {
|
|
5656
5914
|
for (const el of guardsExpr.elements) {
|
|
5657
5915
|
routeGuards.push(tokenText(el, ctx));
|
|
5658
5916
|
}
|
|
5659
5917
|
}
|
|
5660
5918
|
const canMatchExpr = getProp(optionsObject, "canMatch");
|
|
5661
|
-
if (canMatchExpr &&
|
|
5919
|
+
if (canMatchExpr && ts4.isArrayLiteralExpression(canMatchExpr)) {
|
|
5662
5920
|
const canMatchList = [];
|
|
5663
5921
|
for (const el of canMatchExpr.elements) {
|
|
5664
5922
|
canMatchList.push(tokenText(el, ctx));
|
|
@@ -5668,16 +5926,16 @@ function parseController(input, ctx) {
|
|
|
5668
5926
|
}
|
|
5669
5927
|
}
|
|
5670
5928
|
const canDeactivateExpr = getProp(optionsObject, "canDeactivate");
|
|
5671
|
-
if (canDeactivateExpr &&
|
|
5929
|
+
if (canDeactivateExpr && ts4.isArrayLiteralExpression(canDeactivateExpr)) {
|
|
5672
5930
|
for (const el of canDeactivateExpr.elements) {
|
|
5673
5931
|
routeCanDeactivate.push(tokenText(el, ctx));
|
|
5674
5932
|
}
|
|
5675
5933
|
}
|
|
5676
5934
|
const resolversExpr = getProp(optionsObject, "resolvers");
|
|
5677
|
-
if (resolversExpr &&
|
|
5935
|
+
if (resolversExpr && ts4.isObjectLiteralExpression(resolversExpr)) {
|
|
5678
5936
|
const resolvers = {};
|
|
5679
5937
|
for (const prop of resolversExpr.properties) {
|
|
5680
|
-
if (
|
|
5938
|
+
if (ts4.isPropertyAssignment(prop)) {
|
|
5681
5939
|
const rName = propertyName(prop.name);
|
|
5682
5940
|
const init = prop.initializer;
|
|
5683
5941
|
if (init)
|
|
@@ -5689,22 +5947,22 @@ function parseController(input, ctx) {
|
|
|
5689
5947
|
}
|
|
5690
5948
|
}
|
|
5691
5949
|
const redirectToExpr = getProp(optionsObject, "redirectTo");
|
|
5692
|
-
if (redirectToExpr &&
|
|
5950
|
+
if (redirectToExpr && ts4.isStringLiteral(redirectToExpr)) {
|
|
5693
5951
|
route.redirectTo = redirectToExpr.text;
|
|
5694
5952
|
}
|
|
5695
5953
|
const pathMatchExpr = getProp(optionsObject, "pathMatch");
|
|
5696
|
-
if (pathMatchExpr &&
|
|
5954
|
+
if (pathMatchExpr && ts4.isStringLiteral(pathMatchExpr)) {
|
|
5697
5955
|
const val = pathMatchExpr.text;
|
|
5698
5956
|
if (val === "full" || val === "prefix") {
|
|
5699
5957
|
route.pathMatch = val;
|
|
5700
5958
|
}
|
|
5701
5959
|
}
|
|
5702
5960
|
const titleExpr = getProp(optionsObject, "title");
|
|
5703
|
-
if (titleExpr &&
|
|
5961
|
+
if (titleExpr && ts4.isStringLiteral(titleExpr)) {
|
|
5704
5962
|
route.title = titleExpr.text;
|
|
5705
5963
|
}
|
|
5706
5964
|
const dataExpr = getProp(optionsObject, "data");
|
|
5707
|
-
if (dataExpr &&
|
|
5965
|
+
if (dataExpr && ts4.isObjectLiteralExpression(dataExpr)) {
|
|
5708
5966
|
route.data = { ...route.data, ...parseObjectLiteralValues(dataExpr) };
|
|
5709
5967
|
}
|
|
5710
5968
|
const aspects = parseAspectRefs(getProp(optionsObject, "aspects"), ctx, `route ${httpMethod} ${routePath}`);
|
|
@@ -5759,7 +6017,7 @@ function parseJobOptions(meta, owner, ctx) {
|
|
|
5759
6017
|
const expression = getProp(meta, field);
|
|
5760
6018
|
if (!expression)
|
|
5761
6019
|
continue;
|
|
5762
|
-
if (!
|
|
6020
|
+
if (!ts4.isIdentifier(expression)) {
|
|
5763
6021
|
jobOptionError(ctx, "invalid-job-schema", `${owner} 的 ${field} schema 必须是可静态解析的标识符引用,不能使用内联调用或动态表达式`, expression, "SC4019", `将 schema 提取为命名导出,例如 ${field}: ${field === "input" ? "JobInput" : "JobOutput"}。`);
|
|
5764
6022
|
continue;
|
|
5765
6023
|
}
|
|
@@ -5783,7 +6041,7 @@ function parseJobEnum(meta, field, allowed, owner, ctx, code, errorCode) {
|
|
|
5783
6041
|
const expression = getProp(meta, field);
|
|
5784
6042
|
if (!expression)
|
|
5785
6043
|
return;
|
|
5786
|
-
if (!
|
|
6044
|
+
if (!ts4.isStringLiteral(expression) || !allowed.includes(expression.text)) {
|
|
5787
6045
|
jobOptionError(ctx, code, `${owner} 的 ${field} 必须是 ${allowed.map((value) => JSON.stringify(value)).join(" 或 ")} 字符串字面量`, expression, errorCode);
|
|
5788
6046
|
return;
|
|
5789
6047
|
}
|
|
@@ -5793,7 +6051,7 @@ function parseJobInteger(meta, field, min, max, owner, ctx, code, errorCode) {
|
|
|
5793
6051
|
const expression = getProp(meta, field);
|
|
5794
6052
|
if (!expression)
|
|
5795
6053
|
return;
|
|
5796
|
-
const value =
|
|
6054
|
+
const value = ts4.isNumericLiteral(expression) ? Number(expression.text) : Number.NaN;
|
|
5797
6055
|
if (!Number.isSafeInteger(value) || value < min || value > max) {
|
|
5798
6056
|
jobOptionError(ctx, code, `${owner} 的 ${field} 必须是 ${min} 到 ${max} 之间的安全整数`, expression, errorCode);
|
|
5799
6057
|
return;
|
|
@@ -5814,8 +6072,8 @@ function jobOptionError(ctx, code, message, node, errorCode, suggestion) {
|
|
|
5814
6072
|
}
|
|
5815
6073
|
function jobSchemaKind(identifier, declaration, ctx) {
|
|
5816
6074
|
const typeName = ctx.checker.typeToString(ctx.checker.getTypeAtLocation(identifier));
|
|
5817
|
-
const initializer =
|
|
5818
|
-
const opaque = /\bT(?:Unknown|Any)\b/.test(typeName) || initializer &&
|
|
6075
|
+
const initializer = ts4.isVariableDeclaration(declaration) ? declaration.initializer : undefined;
|
|
6076
|
+
const opaque = /\bT(?:Unknown|Any)\b/.test(typeName) || initializer && ts4.isCallExpression(initializer) && ts4.isPropertyAccessExpression(initializer.expression) && ["Unknown", "Any"].includes(initializer.expression.name.text);
|
|
5819
6077
|
return opaque ? "opaque" : "declared";
|
|
5820
6078
|
}
|
|
5821
6079
|
function checkedRpc(meta, ctx) {
|
|
@@ -5837,7 +6095,7 @@ function checkedRpc(meta, ctx) {
|
|
|
5837
6095
|
}
|
|
5838
6096
|
function classDeps(cls, ctx) {
|
|
5839
6097
|
const injectable = parseInjectableOptions(cls, ctx);
|
|
5840
|
-
const ctor = cls.members.find(
|
|
6098
|
+
const ctor = cls.members.find(ts4.isConstructorDeclaration);
|
|
5841
6099
|
const deps = injectable?.deps ? [...injectable.deps] : [];
|
|
5842
6100
|
const optionalDeps = [];
|
|
5843
6101
|
const selfDeps = [];
|
|
@@ -5871,23 +6129,23 @@ function classDeps(cls, ctx) {
|
|
|
5871
6129
|
}
|
|
5872
6130
|
});
|
|
5873
6131
|
}
|
|
5874
|
-
for (const prop of cls.members.filter(
|
|
6132
|
+
for (const prop of cls.members.filter(ts4.isPropertyDeclaration)) {
|
|
5875
6133
|
const init = prop.initializer;
|
|
5876
|
-
if (init &&
|
|
6134
|
+
if (init && ts4.isCallExpression(init)) {
|
|
5877
6135
|
const callName = nodeText(init.expression).split(".").pop();
|
|
5878
6136
|
if (callName === "inject") {
|
|
5879
6137
|
const [tokenArg, optionsArg] = init.arguments;
|
|
5880
6138
|
if (tokenArg) {
|
|
5881
6139
|
const tokenName = tokenText(tokenArg, ctx);
|
|
5882
6140
|
const unwrappedToken = unwrapForwardRef(tokenArg);
|
|
5883
|
-
const known =
|
|
6141
|
+
const known = ts4.isStringLiteral(unwrappedToken) || ts4.isIdentifier(unwrappedToken) && (ctx.tokensByName.has(tokenName) || ctx.classesByName.has(tokenName));
|
|
5884
6142
|
if (!known) {
|
|
5885
6143
|
missing = true;
|
|
5886
6144
|
continue;
|
|
5887
6145
|
}
|
|
5888
6146
|
if (!deps.includes(tokenName))
|
|
5889
6147
|
deps.push(tokenName);
|
|
5890
|
-
const options = optionsArg &&
|
|
6148
|
+
const options = optionsArg && ts4.isObjectLiteralExpression(optionsArg) ? {
|
|
5891
6149
|
optional: booleanProp(optionsArg, "optional") ?? false,
|
|
5892
6150
|
self: booleanProp(optionsArg, "self") ?? false,
|
|
5893
6151
|
skipSelf: booleanProp(optionsArg, "skipSelf") ?? false,
|
|
@@ -5902,9 +6160,9 @@ function classDeps(cls, ctx) {
|
|
|
5902
6160
|
if (options.host && !hostDeps.includes(tokenName))
|
|
5903
6161
|
hostDeps.push(tokenName);
|
|
5904
6162
|
if (!functionalInjects.some((entry) => entry.token === tokenName)) {
|
|
5905
|
-
const declaration =
|
|
6163
|
+
const declaration = ts4.isIdentifier(unwrappedToken) ? resolveDeclaration(unwrappedToken, ctx)[0] : undefined;
|
|
5906
6164
|
const localFile = declaration && isProjectSourcePath(declaration.getSourceFile().fileName, ctx.rootDir) ? declaration.getSourceFile().fileName : undefined;
|
|
5907
|
-
const importModule = declaration && !localFile &&
|
|
6165
|
+
const importModule = declaration && !localFile && ts4.isIdentifier(unwrappedToken) ? importModuleOf(unwrappedToken, ctx) : undefined;
|
|
5908
6166
|
functionalInjects.push({
|
|
5909
6167
|
token: tokenName,
|
|
5910
6168
|
expression: nodeText(unwrappedToken),
|
|
@@ -5948,7 +6206,7 @@ function parseInjectableOptions(cls, ctx) {
|
|
|
5948
6206
|
}
|
|
5949
6207
|
function parseInjectParams(cls, ctx) {
|
|
5950
6208
|
const result = new Map;
|
|
5951
|
-
const ctor = cls.members.find(
|
|
6209
|
+
const ctor = cls.members.find(ts4.isConstructorDeclaration);
|
|
5952
6210
|
if (!ctor)
|
|
5953
6211
|
return result;
|
|
5954
6212
|
ctor.parameters.forEach((param, index) => {
|
|
@@ -5964,7 +6222,7 @@ function parseInjectParams(cls, ctx) {
|
|
|
5964
6222
|
}
|
|
5965
6223
|
function parseOptionalParams(cls) {
|
|
5966
6224
|
const result = new Set;
|
|
5967
|
-
const ctor = cls.members.find(
|
|
6225
|
+
const ctor = cls.members.find(ts4.isConstructorDeclaration);
|
|
5968
6226
|
if (!ctor)
|
|
5969
6227
|
return result;
|
|
5970
6228
|
ctor.parameters.forEach((param, index) => {
|
|
@@ -5979,7 +6237,7 @@ function parseOptionalParams(cls) {
|
|
|
5979
6237
|
}
|
|
5980
6238
|
function parseModifierParams(cls, modifierName) {
|
|
5981
6239
|
const result = new Set;
|
|
5982
|
-
const ctor = cls.members.find(
|
|
6240
|
+
const ctor = cls.members.find(ts4.isConstructorDeclaration);
|
|
5983
6241
|
if (!ctor)
|
|
5984
6242
|
return result;
|
|
5985
6243
|
ctor.parameters.forEach((param, index) => {
|
|
@@ -5991,13 +6249,13 @@ function parseModifierParams(cls, modifierName) {
|
|
|
5991
6249
|
return result;
|
|
5992
6250
|
}
|
|
5993
6251
|
function unwrapForwardRef(expr) {
|
|
5994
|
-
if (
|
|
6252
|
+
if (ts4.isCallExpression(expr)) {
|
|
5995
6253
|
const exprText = nodeText(expr.expression);
|
|
5996
6254
|
if (exprText === "forwardRef" || exprText.endsWith(".forwardRef")) {
|
|
5997
6255
|
const arg = expr.arguments[0];
|
|
5998
|
-
if (arg && (
|
|
6256
|
+
if (arg && (ts4.isArrowFunction(arg) || ts4.isFunctionExpression(arg))) {
|
|
5999
6257
|
const body = arg.body;
|
|
6000
|
-
if (body &&
|
|
6258
|
+
if (body && ts4.isExpression(body)) {
|
|
6001
6259
|
return unwrapForwardRef(body);
|
|
6002
6260
|
}
|
|
6003
6261
|
}
|
|
@@ -6007,13 +6265,13 @@ function unwrapForwardRef(expr) {
|
|
|
6007
6265
|
}
|
|
6008
6266
|
function tokenText(expr, ctx) {
|
|
6009
6267
|
const unwrapped = unwrapForwardRef(expr);
|
|
6010
|
-
if (
|
|
6268
|
+
if (ts4.isStringLiteral(unwrapped))
|
|
6011
6269
|
return unwrapped.text;
|
|
6012
|
-
if (
|
|
6270
|
+
if (ts4.isIdentifier(unwrapped)) {
|
|
6013
6271
|
const decl = resolveDeclaration(unwrapped, ctx)[0];
|
|
6014
|
-
if (decl &&
|
|
6272
|
+
if (decl && ts4.isClassDeclaration(decl))
|
|
6015
6273
|
return decl.name?.text ?? unwrapped.text;
|
|
6016
|
-
if (decl &&
|
|
6274
|
+
if (decl && ts4.isVariableDeclaration(decl))
|
|
6017
6275
|
return variableName(decl);
|
|
6018
6276
|
}
|
|
6019
6277
|
return nodeText(unwrapped);
|
|
@@ -6033,12 +6291,12 @@ function resolveScope(input, ctx) {
|
|
|
6033
6291
|
}
|
|
6034
6292
|
function tokenNameOf(expr, ctx) {
|
|
6035
6293
|
const unwrapped = unwrapForwardRef(expr);
|
|
6036
|
-
if (
|
|
6294
|
+
if (ts4.isIdentifier(unwrapped)) {
|
|
6037
6295
|
const decl = resolveDeclaration(unwrapped, ctx)[0];
|
|
6038
|
-
if (decl &&
|
|
6296
|
+
if (decl && ts4.isClassDeclaration(decl)) {
|
|
6039
6297
|
return { name: decl.name?.text ?? nodeText(expr), kind: "class" };
|
|
6040
6298
|
}
|
|
6041
|
-
if (decl &&
|
|
6299
|
+
if (decl && ts4.isVariableDeclaration(decl)) {
|
|
6042
6300
|
const name = variableName(decl);
|
|
6043
6301
|
return { name, kind: ctx.tokensByName.has(name) ? "injection-token" : "class" };
|
|
6044
6302
|
}
|
|
@@ -6054,10 +6312,10 @@ function resolveDeclaration(id, ctx) {
|
|
|
6054
6312
|
return [];
|
|
6055
6313
|
let declarations = symbol.declarations ?? [];
|
|
6056
6314
|
for (let guard = 0;guard < 4; guard += 1) {
|
|
6057
|
-
const isAlias = declarations.some((d) =>
|
|
6315
|
+
const isAlias = declarations.some((d) => ts4.isImportSpecifier(d) || ts4.isImportClause(d) || ts4.isNamespaceImport(d));
|
|
6058
6316
|
if (!isAlias)
|
|
6059
6317
|
break;
|
|
6060
|
-
if (!(symbol.flags &
|
|
6318
|
+
if (!(symbol.flags & ts4.SymbolFlags.Alias))
|
|
6061
6319
|
break;
|
|
6062
6320
|
const aliased = ctx.checker.getAliasedSymbol(symbol);
|
|
6063
6321
|
symbol = aliased;
|
|
@@ -6077,9 +6335,9 @@ function importModuleOf(id, ctx) {
|
|
|
6077
6335
|
for (const declaration of declarations) {
|
|
6078
6336
|
let current = declaration;
|
|
6079
6337
|
while (current) {
|
|
6080
|
-
if (
|
|
6338
|
+
if (ts4.isImportDeclaration(current)) {
|
|
6081
6339
|
const moduleSpecifier = current.moduleSpecifier;
|
|
6082
|
-
return
|
|
6340
|
+
return ts4.isStringLiteral(moduleSpecifier) ? moduleSpecifier.text : undefined;
|
|
6083
6341
|
}
|
|
6084
6342
|
current = current.parent;
|
|
6085
6343
|
}
|
|
@@ -6091,27 +6349,27 @@ function findDecorator(cls, name) {
|
|
|
6091
6349
|
}
|
|
6092
6350
|
function decoratorName2(dec) {
|
|
6093
6351
|
const expr = dec.expression;
|
|
6094
|
-
if (
|
|
6352
|
+
if (ts4.isCallExpression(expr)) {
|
|
6095
6353
|
return nodeText(expr.expression).split(".").pop();
|
|
6096
6354
|
}
|
|
6097
|
-
if (
|
|
6355
|
+
if (ts4.isIdentifier(expr))
|
|
6098
6356
|
return expr.text;
|
|
6099
6357
|
return;
|
|
6100
6358
|
}
|
|
6101
6359
|
function decoratorObjectArg(dec) {
|
|
6102
6360
|
const expr = dec.expression;
|
|
6103
|
-
if (!
|
|
6361
|
+
if (!ts4.isCallExpression(expr))
|
|
6104
6362
|
return;
|
|
6105
6363
|
const arg = expr.arguments[0];
|
|
6106
|
-
return arg &&
|
|
6364
|
+
return arg && ts4.isObjectLiteralExpression(arg) ? arg : undefined;
|
|
6107
6365
|
}
|
|
6108
6366
|
function getProp(obj, name) {
|
|
6109
|
-
const prop = obj.properties.find((item) => (
|
|
6367
|
+
const prop = obj.properties.find((item) => (ts4.isPropertyAssignment(item) || ts4.isShorthandPropertyAssignment(item)) && propertyName(item.name) === name);
|
|
6110
6368
|
if (!prop)
|
|
6111
6369
|
return;
|
|
6112
|
-
if (
|
|
6370
|
+
if (ts4.isPropertyAssignment(prop))
|
|
6113
6371
|
return prop.initializer;
|
|
6114
|
-
if (
|
|
6372
|
+
if (ts4.isShorthandPropertyAssignment(prop))
|
|
6115
6373
|
return prop.name;
|
|
6116
6374
|
return;
|
|
6117
6375
|
}
|
|
@@ -6119,10 +6377,10 @@ function toCompilerDiagnostic(diagnostic, rootDir) {
|
|
|
6119
6377
|
const file = diagnostic.file;
|
|
6120
6378
|
const position = file && diagnostic.start !== undefined ? file.getLineAndCharacterOfPosition(diagnostic.start) : undefined;
|
|
6121
6379
|
return {
|
|
6122
|
-
severity: diagnostic.category ===
|
|
6380
|
+
severity: diagnostic.category === ts4.DiagnosticCategory.Error ? "error" : "warn",
|
|
6123
6381
|
code: `typescript-${diagnostic.code}`,
|
|
6124
6382
|
errorCode: `TS${diagnostic.code}`,
|
|
6125
|
-
message:
|
|
6383
|
+
message: ts4.flattenDiagnosticMessageText(diagnostic.messageText, `
|
|
6126
6384
|
`),
|
|
6127
6385
|
...file ? { file: sourcePath(rootDir, file.fileName) } : {},
|
|
6128
6386
|
...position ? { line: position.line + 1 } : {}
|
|
@@ -6130,16 +6388,16 @@ function toCompilerDiagnostic(diagnostic, rootDir) {
|
|
|
6130
6388
|
}
|
|
6131
6389
|
function stringLiteralProp(obj, name) {
|
|
6132
6390
|
const expr = getProp(obj, name);
|
|
6133
|
-
return expr &&
|
|
6391
|
+
return expr && ts4.isStringLiteral(expr) ? expr.text : undefined;
|
|
6134
6392
|
}
|
|
6135
6393
|
function arrayProp(obj, name) {
|
|
6136
6394
|
const expr = getProp(obj, name);
|
|
6137
|
-
return expr &&
|
|
6395
|
+
return expr && ts4.isArrayLiteralExpression(expr) ? [...expr.elements] : [];
|
|
6138
6396
|
}
|
|
6139
6397
|
function parseAspectRefs(expression, ctx, owner) {
|
|
6140
6398
|
if (!expression)
|
|
6141
6399
|
return [];
|
|
6142
|
-
if (!
|
|
6400
|
+
if (!ts4.isArrayLiteralExpression(expression)) {
|
|
6143
6401
|
ctx.diagnostics.push({
|
|
6144
6402
|
severity: "error",
|
|
6145
6403
|
code: "dynamic-aspect-reference",
|
|
@@ -6154,7 +6412,7 @@ function parseAspectRefs(expression, ctx, owner) {
|
|
|
6154
6412
|
}
|
|
6155
6413
|
const refs = [];
|
|
6156
6414
|
for (const element of expression.elements) {
|
|
6157
|
-
if (
|
|
6415
|
+
if (ts4.isSpreadElement(element) || !ts4.isIdentifier(element)) {
|
|
6158
6416
|
ctx.diagnostics.push({
|
|
6159
6417
|
severity: "error",
|
|
6160
6418
|
code: "dynamic-aspect-reference",
|
|
@@ -6167,7 +6425,7 @@ function parseAspectRefs(expression, ctx, owner) {
|
|
|
6167
6425
|
});
|
|
6168
6426
|
continue;
|
|
6169
6427
|
}
|
|
6170
|
-
const declaration = resolveDeclaration(element, ctx).find((candidate) =>
|
|
6428
|
+
const declaration = resolveDeclaration(element, ctx).find((candidate) => ts4.isFunctionDeclaration(candidate) || ts4.isVariableDeclaration(candidate) && candidate.initializer !== undefined && (ts4.isArrowFunction(candidate.initializer) || ts4.isFunctionExpression(candidate.initializer)));
|
|
6171
6429
|
if (!declaration) {
|
|
6172
6430
|
ctx.diagnostics.push({
|
|
6173
6431
|
severity: "error",
|
|
@@ -6181,7 +6439,7 @@ function parseAspectRefs(expression, ctx, owner) {
|
|
|
6181
6439
|
});
|
|
6182
6440
|
continue;
|
|
6183
6441
|
}
|
|
6184
|
-
const name =
|
|
6442
|
+
const name = ts4.isFunctionDeclaration(declaration) ? declaration.name?.text : ts4.isVariableDeclaration(declaration) ? variableName(declaration) : undefined;
|
|
6185
6443
|
if (!name)
|
|
6186
6444
|
continue;
|
|
6187
6445
|
const declaredFile = declaration.getSourceFile().fileName;
|
|
@@ -6201,9 +6459,9 @@ function booleanProp(obj, name) {
|
|
|
6201
6459
|
const expr = getProp(obj, name);
|
|
6202
6460
|
if (!expr)
|
|
6203
6461
|
return;
|
|
6204
|
-
if (expr.kind ===
|
|
6462
|
+
if (expr.kind === ts4.SyntaxKind.TrueKeyword)
|
|
6205
6463
|
return true;
|
|
6206
|
-
if (expr.kind ===
|
|
6464
|
+
if (expr.kind === ts4.SyntaxKind.FalseKeyword)
|
|
6207
6465
|
return false;
|
|
6208
6466
|
return;
|
|
6209
6467
|
}
|
|
@@ -6217,15 +6475,15 @@ function parseBindingOptions(args, defaultName) {
|
|
|
6217
6475
|
let defaultValue;
|
|
6218
6476
|
const first = args[0];
|
|
6219
6477
|
const second = args[1];
|
|
6220
|
-
if (first &&
|
|
6478
|
+
if (first && ts4.isStringLiteral(first)) {
|
|
6221
6479
|
name = first.text;
|
|
6222
|
-
} else if (first &&
|
|
6480
|
+
} else if (first && ts4.isObjectLiteralExpression(first)) {
|
|
6223
6481
|
const nameProp = getProp(first, "name");
|
|
6224
|
-
if (nameProp &&
|
|
6482
|
+
if (nameProp && ts4.isStringLiteral(nameProp)) {
|
|
6225
6483
|
name = nameProp.text;
|
|
6226
6484
|
}
|
|
6227
6485
|
const trProp = getProp(first, "transform");
|
|
6228
|
-
if (trProp &&
|
|
6486
|
+
if (trProp && ts4.isStringLiteral(trProp)) {
|
|
6229
6487
|
const val = trProp.text;
|
|
6230
6488
|
if (val === "number" || val === "boolean" || val === "string") {
|
|
6231
6489
|
transform = val;
|
|
@@ -6236,9 +6494,9 @@ function parseBindingOptions(args, defaultName) {
|
|
|
6236
6494
|
defaultValue = parseLiteralValue(defProp);
|
|
6237
6495
|
}
|
|
6238
6496
|
}
|
|
6239
|
-
if (second &&
|
|
6497
|
+
if (second && ts4.isObjectLiteralExpression(second)) {
|
|
6240
6498
|
const trProp = getProp(second, "transform");
|
|
6241
|
-
if (trProp &&
|
|
6499
|
+
if (trProp && ts4.isStringLiteral(trProp)) {
|
|
6242
6500
|
const val = trProp.text;
|
|
6243
6501
|
if (val === "number" || val === "boolean" || val === "string") {
|
|
6244
6502
|
transform = val;
|
|
@@ -6252,18 +6510,18 @@ function parseBindingOptions(args, defaultName) {
|
|
|
6252
6510
|
return { name, ...transform ? { transform } : {}, default: defaultValue };
|
|
6253
6511
|
}
|
|
6254
6512
|
function parseLiteralValue(node) {
|
|
6255
|
-
if (
|
|
6513
|
+
if (ts4.isStringLiteral(node))
|
|
6256
6514
|
return node.text;
|
|
6257
|
-
if (
|
|
6515
|
+
if (ts4.isNumericLiteral(node))
|
|
6258
6516
|
return Number(node.text);
|
|
6259
|
-
if (node.kind ===
|
|
6517
|
+
if (node.kind === ts4.SyntaxKind.TrueKeyword)
|
|
6260
6518
|
return true;
|
|
6261
|
-
if (node.kind ===
|
|
6519
|
+
if (node.kind === ts4.SyntaxKind.FalseKeyword)
|
|
6262
6520
|
return false;
|
|
6263
|
-
if (
|
|
6521
|
+
if (ts4.isArrayLiteralExpression(node)) {
|
|
6264
6522
|
return node.elements.map(parseLiteralValue);
|
|
6265
6523
|
}
|
|
6266
|
-
if (
|
|
6524
|
+
if (ts4.isObjectLiteralExpression(node)) {
|
|
6267
6525
|
return parseObjectLiteralValues(node);
|
|
6268
6526
|
}
|
|
6269
6527
|
return;
|
|
@@ -6271,7 +6529,7 @@ function parseLiteralValue(node) {
|
|
|
6271
6529
|
function parseObjectLiteralValues(obj) {
|
|
6272
6530
|
const result = {};
|
|
6273
6531
|
for (const prop of obj.properties) {
|
|
6274
|
-
if (
|
|
6532
|
+
if (ts4.isPropertyAssignment(prop)) {
|
|
6275
6533
|
const name = propertyName(prop.name);
|
|
6276
6534
|
const init = prop.initializer;
|
|
6277
6535
|
if (init) {
|
|
@@ -6307,7 +6565,50 @@ import { join as join4 } from "node:path";
|
|
|
6307
6565
|
// src/type-safety.ts
|
|
6308
6566
|
import { existsSync as existsSync2, readFileSync as readFileSync2 } from "node:fs";
|
|
6309
6567
|
import { dirname as dirname3, join as join3, relative as relative3, resolve as resolve2, sep as sep3 } from "node:path";
|
|
6310
|
-
import * as
|
|
6568
|
+
import * as ts6 from "@typescript/typescript6";
|
|
6569
|
+
|
|
6570
|
+
// src/sql-safety.ts
|
|
6571
|
+
import * as ts5 from "@typescript/typescript6";
|
|
6572
|
+
var SQL_SAFETY_DIAGNOSTIC_CODES = {
|
|
6573
|
+
"sql-result-assertion": { errorCode: "SC6007", docsUrl: "https://supacloud.dev/errors/SC6007" },
|
|
6574
|
+
"sql-raw-dynamic": { errorCode: "SC6008", docsUrl: "https://supacloud.dev/errors/SC6008" }
|
|
6575
|
+
};
|
|
6576
|
+
function scanDrizzleSql(sourceFile, checker, file, strict) {
|
|
6577
|
+
const diagnostics = [];
|
|
6578
|
+
const importedSql = (expression) => {
|
|
6579
|
+
const symbol = checker.getSymbolAtLocation(ts5.isPropertyAccessExpression(expression) ? expression.name : expression);
|
|
6580
|
+
if (!symbol)
|
|
6581
|
+
return false;
|
|
6582
|
+
const target = symbol.flags & ts5.SymbolFlags.Alias ? checker.getAliasedSymbol(symbol) : symbol;
|
|
6583
|
+
return target.name === "sql" && (target.declarations ?? []).some((declaration) => /(?:^|\/)node_modules\/drizzle-orm\//.test(declaration.getSourceFile().fileName.replaceAll("\\", "/")));
|
|
6584
|
+
};
|
|
6585
|
+
const report = (code, node, message) => {
|
|
6586
|
+
diagnostics.push({
|
|
6587
|
+
severity: strict ? "error" : "warn",
|
|
6588
|
+
code,
|
|
6589
|
+
...SQL_SAFETY_DIAGNOSTIC_CODES[code],
|
|
6590
|
+
message,
|
|
6591
|
+
file,
|
|
6592
|
+
line: sourceFile.getLineAndCharacterOfPosition(node.getStart()).line + 1
|
|
6593
|
+
});
|
|
6594
|
+
};
|
|
6595
|
+
const visit = (node) => {
|
|
6596
|
+
if (ts5.isTaggedTemplateExpression(node) && importedSql(node.tag) && node.typeArguments?.some((type) => type.kind !== ts5.SyntaxKind.UnknownKeyword)) {
|
|
6597
|
+
report("sql-result-assertion", node, "Drizzle sql<T> asserts a result type without checking SQL or decoding rows. Use sql<unknown> and a schema decoder at the result boundary.");
|
|
6598
|
+
}
|
|
6599
|
+
if (ts5.isCallExpression(node) && ts5.isPropertyAccessExpression(node.expression) && node.expression.name.text === "raw" && importedSql(node.expression.expression)) {
|
|
6600
|
+
const argument = node.arguments[0];
|
|
6601
|
+
if (!argument || !ts5.isStringLiteral(argument) && !ts5.isNoSubstitutionTemplateLiteral(argument)) {
|
|
6602
|
+
report("sql-raw-dynamic", node, "Dynamic sql.raw bypasses parameter binding. Interpolate values with sql templates; keep reviewed static DDL in migrations.");
|
|
6603
|
+
}
|
|
6604
|
+
}
|
|
6605
|
+
ts5.forEachChild(node, visit);
|
|
6606
|
+
};
|
|
6607
|
+
visit(sourceFile);
|
|
6608
|
+
return diagnostics;
|
|
6609
|
+
}
|
|
6610
|
+
|
|
6611
|
+
// src/type-safety.ts
|
|
6311
6612
|
var DEFAULT_EXCLUDES = [
|
|
6312
6613
|
"**/*.test.ts",
|
|
6313
6614
|
"**/*.spec.ts",
|
|
@@ -6320,6 +6621,7 @@ var DEFAULT_EXCLUDES = [
|
|
|
6320
6621
|
"**/*.d.ts"
|
|
6321
6622
|
];
|
|
6322
6623
|
var TYPE_SAFETY_DIAGNOSTIC_CODES = {
|
|
6624
|
+
...SQL_SAFETY_DIAGNOSTIC_CODES,
|
|
6323
6625
|
"generated-any": { errorCode: "SC6001", docsUrl: "https://supacloud.dev/errors/SC6001" },
|
|
6324
6626
|
"source-any": { errorCode: "SC6002", docsUrl: "https://supacloud.dev/errors/SC6002" },
|
|
6325
6627
|
"source-type-assertion": { errorCode: "SC6003", docsUrl: "https://supacloud.dev/errors/SC6003" },
|
|
@@ -6332,7 +6634,7 @@ function scanGeneratedArtifacts(artifacts, strict = true) {
|
|
|
6332
6634
|
for (const [file, content] of Object.entries(artifacts)) {
|
|
6333
6635
|
if (content === undefined)
|
|
6334
6636
|
continue;
|
|
6335
|
-
const sourceFile =
|
|
6637
|
+
const sourceFile = ts6.createSourceFile(file, content, ts6.ScriptTarget.Latest, true, ts6.ScriptKind.TS);
|
|
6336
6638
|
for (const node of descendantsOfKind2(sourceFile, isAnyKeyword)) {
|
|
6337
6639
|
diagnostics.push(makeDiagnostic("generated-any", `生成产物 ${file} 包含 any;严格生成模式要求使用 unknown、具体接口或泛型约束。`, sourceFile, node, strict));
|
|
6338
6640
|
}
|
|
@@ -6341,30 +6643,30 @@ function scanGeneratedArtifacts(artifacts, strict = true) {
|
|
|
6341
6643
|
}
|
|
6342
6644
|
function scanProductionSource(options) {
|
|
6343
6645
|
const rootDir = resolve2(options.rootDir);
|
|
6344
|
-
const configPath =
|
|
6646
|
+
const configPath = ts6.findConfigFile(rootDir, ts6.sys.fileExists) ?? join3(rootDir, "tsconfig.json");
|
|
6345
6647
|
const projectConfig = existsSync2(configPath) ? readProjectConfig2(configPath) : {
|
|
6346
6648
|
options: {
|
|
6347
6649
|
strict: true,
|
|
6348
6650
|
skipLibCheck: true,
|
|
6349
|
-
target:
|
|
6350
|
-
module:
|
|
6351
|
-
moduleResolution:
|
|
6651
|
+
target: ts6.ScriptTarget.ES2022,
|
|
6652
|
+
module: ts6.ModuleKind.ESNext,
|
|
6653
|
+
moduleResolution: ts6.ModuleResolutionKind.Bundler
|
|
6352
6654
|
},
|
|
6353
6655
|
errors: []
|
|
6354
6656
|
};
|
|
6355
6657
|
const include = options.include ?? ["**/*.ts", "**/*.tsx", "**/*.mts", "**/*.cts"];
|
|
6356
|
-
const rootNames =
|
|
6658
|
+
const rootNames = ts6.sys.readDirectory(rootDir, [".ts", ".tsx", ".mts", ".cts"], ["node_modules", "dist"], include).filter((file) => isProductionSourcePath(rootDir, file, [...DEFAULT_EXCLUDES, ...options.exclude ?? []]));
|
|
6357
6659
|
const compilerOptions = { ...projectConfig.options, noEmit: true };
|
|
6358
|
-
const host =
|
|
6660
|
+
const host = ts6.createCompilerHost(compilerOptions);
|
|
6359
6661
|
host.getCurrentDirectory = () => dirname3(configPath);
|
|
6360
|
-
const program =
|
|
6662
|
+
const program = ts6.createProgram(rootNames, compilerOptions, host);
|
|
6361
6663
|
const outDir = options.outDir ? normalizeRelative(rootDir, options.outDir) : undefined;
|
|
6362
6664
|
const excludes = [...DEFAULT_EXCLUDES, ...options.exclude ?? []];
|
|
6363
6665
|
const sourceFiles = program.getSourceFiles().filter((sourceFile) => isProductionSource(rootDir, sourceFile, excludes, outDir));
|
|
6364
6666
|
const diagnostics = [...projectConfig.errors, ...program.getOptionsDiagnostics()].map((diagnostic) => ({
|
|
6365
6667
|
severity: "error",
|
|
6366
6668
|
code: "source-config",
|
|
6367
|
-
message:
|
|
6669
|
+
message: ts6.flattenDiagnosticMessageText(diagnostic.messageText, `
|
|
6368
6670
|
`),
|
|
6369
6671
|
file: diagnostic.file ? normalizeRelative(rootDir, diagnostic.file.fileName) : normalizeRelative(rootDir, configPath),
|
|
6370
6672
|
...diagnostic.file && diagnostic.start !== undefined ? { line: diagnostic.file.getLineAndCharacterOfPosition(diagnostic.start).line + 1 } : {},
|
|
@@ -6381,7 +6683,7 @@ function scanProductionSource(options) {
|
|
|
6381
6683
|
diagnostics.push({
|
|
6382
6684
|
severity: "error",
|
|
6383
6685
|
code: "source-typescript",
|
|
6384
|
-
message:
|
|
6686
|
+
message: ts6.flattenDiagnosticMessageText(diagnostic.messageText, `
|
|
6385
6687
|
`),
|
|
6386
6688
|
...diagnostic.file ? { file: normalizeRelative(rootDir, diagnostic.file.fileName) } : {},
|
|
6387
6689
|
...diagnostic.file && diagnostic.start !== undefined ? { line: diagnostic.file.getLineAndCharacterOfPosition(diagnostic.start).line + 1 } : {},
|
|
@@ -6390,9 +6692,10 @@ function scanProductionSource(options) {
|
|
|
6390
6692
|
}
|
|
6391
6693
|
for (const sourceFile of sourceFiles) {
|
|
6392
6694
|
scanSourceFile(sourceFile, checker, rootDir, diagnostics, options.strict ?? false);
|
|
6393
|
-
|
|
6394
|
-
|
|
6395
|
-
|
|
6695
|
+
diagnostics.push(...scanDrizzleSql(sourceFile, checker, normalizeRelative(rootDir, sourceFile.fileName), options.strict ?? false));
|
|
6696
|
+
const scanner = ts6.createScanner(ts6.ScriptTarget.Latest, false, sourceFile.languageVariant, sourceFile.text);
|
|
6697
|
+
for (let kind = scanner.scan();kind !== ts6.SyntaxKind.EndOfFileToken; kind = scanner.scan()) {
|
|
6698
|
+
if ((kind === ts6.SyntaxKind.SingleLineCommentTrivia || kind === ts6.SyntaxKind.MultiLineCommentTrivia) && /@ts-(?:ignore|nocheck|expect-error)\b/.test(scanner.getTokenText())) {
|
|
6396
6699
|
diagnostics.push({
|
|
6397
6700
|
severity: "error",
|
|
6398
6701
|
code: "source-type-suppression",
|
|
@@ -6412,22 +6715,22 @@ function scanSourceFile(sourceFile, checker, rootDir, diagnostics, strict) {
|
|
|
6412
6715
|
diagnostics.push(makeDiagnostic("source-any", "生产源码使用了显式 any;请改用 unknown、具体接口或泛型约束。", sourceFile, node, strict, rootDir));
|
|
6413
6716
|
}
|
|
6414
6717
|
for (const node of descendants(sourceFile)) {
|
|
6415
|
-
if (
|
|
6416
|
-
if (
|
|
6718
|
+
if (ts6.isAsExpression(node)) {
|
|
6719
|
+
if (ts6.isAsExpression(node.parent) || ts6.isTypeAssertionExpression(node.parent))
|
|
6417
6720
|
continue;
|
|
6418
6721
|
const assertedType = node.type.getText(sourceFile);
|
|
6419
6722
|
if (assertedType === "const")
|
|
6420
6723
|
continue;
|
|
6421
6724
|
diagnostics.push(makeDiagnostic("source-type-assertion", `生产源码包含类型断言 ${node.getText(sourceFile)};请优先使用类型守卫、satisfies 或显式边界解析。`, sourceFile, node, strict, rootDir));
|
|
6422
|
-
} else if (
|
|
6423
|
-
if (
|
|
6725
|
+
} else if (ts6.isTypeAssertionExpression(node)) {
|
|
6726
|
+
if (ts6.isAsExpression(node.parent) || ts6.isTypeAssertionExpression(node.parent))
|
|
6424
6727
|
continue;
|
|
6425
6728
|
diagnostics.push(makeDiagnostic("source-type-assertion", `生产源码包含类型断言 ${node.getText(sourceFile)};请优先使用类型守卫、satisfies 或显式边界解析。`, sourceFile, node, strict, rootDir));
|
|
6426
|
-
} else if (
|
|
6729
|
+
} else if (ts6.isNonNullExpression(node)) {
|
|
6427
6730
|
diagnostics.push(makeDiagnostic("source-non-null-assertion", `生产源码包含非空断言 ${node.getText(sourceFile)};请显式处理 null/undefined。`, sourceFile, node, strict, rootDir));
|
|
6428
6731
|
}
|
|
6429
6732
|
}
|
|
6430
|
-
for (const declaration of descendantsOfKind2(sourceFile,
|
|
6733
|
+
for (const declaration of descendantsOfKind2(sourceFile, ts6.isVariableDeclaration)) {
|
|
6431
6734
|
const initializer = declaration.initializer;
|
|
6432
6735
|
if (!initializer || declaration.type)
|
|
6433
6736
|
continue;
|
|
@@ -6443,11 +6746,11 @@ function scanSourceFile(sourceFile, checker, rootDir, diagnostics, strict) {
|
|
|
6443
6746
|
if (isLetDeclaration(declaration) && isLiteralSyntax(initializer) && !isLiteralType(declarationType)) {
|
|
6444
6747
|
diagnostics.push(makeDiagnostic("source-implicit-widening", `变量 ${declaration.name.getText(sourceFile)} 的字面量类型从 ${checker.typeToString(initializerType, initializer)} 隐式宽化为 ${checker.typeToString(declarationType, declaration)};请补充类型或使用 const。`, sourceFile, declaration, strict, rootDir));
|
|
6445
6748
|
}
|
|
6446
|
-
if (
|
|
6749
|
+
if (ts6.isObjectLiteralExpression(initializer) && isConstDeclaration(declaration) && initializer.getText(sourceFile).length > 0 && initializer.properties.some((property) => ts6.isPropertyAssignment(property) && property.initializer !== undefined && !ts6.isAsExpression(property.initializer) && isLiteralExpression(property.initializer))) {
|
|
6447
6750
|
diagnostics.push(makeDiagnostic("source-implicit-widening", `常量对象 ${declaration.name.getText(sourceFile)} 的字面量属性会隐式宽化;请补充对象类型或使用 as const。`, sourceFile, declaration, strict, rootDir));
|
|
6448
6751
|
}
|
|
6449
6752
|
}
|
|
6450
|
-
for (const parameter of descendantsOfKind2(sourceFile,
|
|
6753
|
+
for (const parameter of descendantsOfKind2(sourceFile, ts6.isParameter)) {
|
|
6451
6754
|
if (parameter.type)
|
|
6452
6755
|
continue;
|
|
6453
6756
|
for (const name of bindingNames(parameter.name)) {
|
|
@@ -6458,10 +6761,10 @@ function scanSourceFile(sourceFile, checker, rootDir, diagnostics, strict) {
|
|
|
6458
6761
|
}
|
|
6459
6762
|
}
|
|
6460
6763
|
function readProjectConfig2(configPath) {
|
|
6461
|
-
const config =
|
|
6764
|
+
const config = ts6.readConfigFile(configPath, (file) => readFileSync2(file, "utf8"));
|
|
6462
6765
|
if (config.error)
|
|
6463
6766
|
return { options: {}, errors: [config.error] };
|
|
6464
|
-
const parsed =
|
|
6767
|
+
const parsed = ts6.parseJsonConfigFileContent(config.config, ts6.sys, dirname3(configPath));
|
|
6465
6768
|
return { options: parsed.options, errors: parsed.errors };
|
|
6466
6769
|
}
|
|
6467
6770
|
function isProductionSource(rootDir, sourceFile, excludes, outDir) {
|
|
@@ -6481,42 +6784,42 @@ function globMatches(value, pattern) {
|
|
|
6481
6784
|
return new RegExp(`^${escaped}$`).test(value);
|
|
6482
6785
|
}
|
|
6483
6786
|
function bindingNames(name) {
|
|
6484
|
-
if (
|
|
6787
|
+
if (ts6.isIdentifier(name))
|
|
6485
6788
|
return [name];
|
|
6486
|
-
return name.elements.flatMap((element) =>
|
|
6789
|
+
return name.elements.flatMap((element) => ts6.isBindingElement(element) ? bindingNames(element.name) : []);
|
|
6487
6790
|
}
|
|
6488
6791
|
function isLiteralExpression(node) {
|
|
6489
6792
|
if (!node)
|
|
6490
6793
|
return false;
|
|
6491
6794
|
return [
|
|
6492
|
-
|
|
6493
|
-
|
|
6494
|
-
|
|
6495
|
-
|
|
6795
|
+
ts6.SyntaxKind.StringLiteral,
|
|
6796
|
+
ts6.SyntaxKind.NumericLiteral,
|
|
6797
|
+
ts6.SyntaxKind.TrueKeyword,
|
|
6798
|
+
ts6.SyntaxKind.FalseKeyword
|
|
6496
6799
|
].includes(node.kind);
|
|
6497
6800
|
}
|
|
6498
6801
|
function isLiteralSyntax(node) {
|
|
6499
|
-
return
|
|
6802
|
+
return ts6.isStringLiteral(node) || ts6.isNumericLiteral(node) || node.kind === ts6.SyntaxKind.TrueKeyword || node.kind === ts6.SyntaxKind.FalseKeyword;
|
|
6500
6803
|
}
|
|
6501
6804
|
function isLiteralType(type) {
|
|
6502
|
-
return (type.flags & (
|
|
6805
|
+
return (type.flags & (ts6.TypeFlags.StringLiteral | ts6.TypeFlags.NumberLiteral | ts6.TypeFlags.BooleanLiteral | ts6.TypeFlags.BigIntLiteral)) !== 0;
|
|
6503
6806
|
}
|
|
6504
6807
|
function isAnyType(type) {
|
|
6505
|
-
return (type.flags &
|
|
6808
|
+
return (type.flags & ts6.TypeFlags.Any) !== 0;
|
|
6506
6809
|
}
|
|
6507
6810
|
function isLetDeclaration(declaration) {
|
|
6508
|
-
return
|
|
6811
|
+
return ts6.isVariableDeclarationList(declaration.parent) && (declaration.parent.flags & ts6.NodeFlags.Let) !== 0;
|
|
6509
6812
|
}
|
|
6510
6813
|
function isConstDeclaration(declaration) {
|
|
6511
|
-
return
|
|
6814
|
+
return ts6.isVariableDeclarationList(declaration.parent) && (declaration.parent.flags & ts6.NodeFlags.Const) !== 0;
|
|
6512
6815
|
}
|
|
6513
6816
|
function descendants(root) {
|
|
6514
6817
|
const result = [];
|
|
6515
6818
|
const visit = (node) => {
|
|
6516
6819
|
result.push(node);
|
|
6517
|
-
|
|
6820
|
+
ts6.forEachChild(node, visit);
|
|
6518
6821
|
};
|
|
6519
|
-
|
|
6822
|
+
ts6.forEachChild(root, visit);
|
|
6520
6823
|
return result;
|
|
6521
6824
|
}
|
|
6522
6825
|
function descendantsOfKind2(root, predicate) {
|
|
@@ -6524,9 +6827,9 @@ function descendantsOfKind2(root, predicate) {
|
|
|
6524
6827
|
const visit = (node) => {
|
|
6525
6828
|
if (predicate(node))
|
|
6526
6829
|
result.push(node);
|
|
6527
|
-
|
|
6830
|
+
ts6.forEachChild(node, visit);
|
|
6528
6831
|
};
|
|
6529
|
-
|
|
6832
|
+
ts6.forEachChild(root, visit);
|
|
6530
6833
|
return result;
|
|
6531
6834
|
}
|
|
6532
6835
|
function makeDiagnostic(code, message, fileOrSourceFile, node, strict, rootDir) {
|
|
@@ -6547,7 +6850,7 @@ function normalizeRelative(rootDir, filePath) {
|
|
|
6547
6850
|
return relative3(rootDir, filePath).split(sep3).join("/").replace(/^\.\//, "");
|
|
6548
6851
|
}
|
|
6549
6852
|
function isAnyKeyword(node) {
|
|
6550
|
-
return node.kind ===
|
|
6853
|
+
return node.kind === ts6.SyntaxKind.AnyKeyword;
|
|
6551
6854
|
}
|
|
6552
6855
|
|
|
6553
6856
|
// src/route-contracts.ts
|
|
@@ -6631,15 +6934,28 @@ function validateRouteContracts(graph) {
|
|
|
6631
6934
|
}
|
|
6632
6935
|
|
|
6633
6936
|
// src/compile.ts
|
|
6937
|
+
function withDefaultGovernance(options) {
|
|
6938
|
+
return options.commandCapabilities === undefined ? { ...options, commandCapabilities: {
|
|
6939
|
+
requirePersistentAdapters: true,
|
|
6940
|
+
permission: true,
|
|
6941
|
+
audit: true,
|
|
6942
|
+
idempotency: true,
|
|
6943
|
+
transaction: true
|
|
6944
|
+
} } : options;
|
|
6945
|
+
}
|
|
6634
6946
|
async function renderOptionalGraphql(options) {
|
|
6635
6947
|
return options.graphql ? (await Promise.resolve().then(() => (init_graphql(), exports_graphql))).renderGraphql(options) : { diagnostics: [], files: {} };
|
|
6636
6948
|
}
|
|
6637
6949
|
async function compileProject(options) {
|
|
6950
|
+
options = withDefaultGovernance(options);
|
|
6638
6951
|
const graph = await analyzeProject(options.rootDir, options.include, options.cache, options.changedPaths);
|
|
6639
6952
|
const diagnostics = [
|
|
6640
6953
|
...graph.diagnostics ?? [],
|
|
6641
6954
|
...validateGraph(graph, options)
|
|
6642
6955
|
];
|
|
6956
|
+
if (diagnostics.some((item) => item.code === "runtime-injection-disallowed")) {
|
|
6957
|
+
return { diagnostics, graph, written: [] };
|
|
6958
|
+
}
|
|
6643
6959
|
if (options.strict) {
|
|
6644
6960
|
for (const diagnostic of diagnostics) {
|
|
6645
6961
|
if (diagnostic.severity === "warn")
|
|
@@ -6693,11 +7009,15 @@ async function compileProject(options) {
|
|
|
6693
7009
|
return { diagnostics, graph, written, ...stats ? { stats } : {} };
|
|
6694
7010
|
}
|
|
6695
7011
|
async function checkProject(options) {
|
|
7012
|
+
options = withDefaultGovernance(options);
|
|
6696
7013
|
const graph = await analyzeProject(options.rootDir, options.include, options.cache, options.changedPaths);
|
|
6697
7014
|
const diagnostics = [
|
|
6698
7015
|
...graph.diagnostics ?? [],
|
|
6699
7016
|
...validateGraph(graph, options)
|
|
6700
7017
|
];
|
|
7018
|
+
if (diagnostics.some((item) => item.code === "runtime-injection-disallowed")) {
|
|
7019
|
+
return { diagnostics, graph, upToDate: false, mismatches: ["Runtime DI must be migrated to constructor injection."] };
|
|
7020
|
+
}
|
|
6701
7021
|
if (options.strict) {
|
|
6702
7022
|
for (const diagnostic of diagnostics) {
|
|
6703
7023
|
if (diagnostic.severity === "warn")
|
|
@@ -10248,7 +10568,7 @@ function formatDeliveryPlan(result) {
|
|
|
10248
10568
|
import { mkdir as mkdir3, mkdtemp, readFile as readFile5, realpath as realpath2, rename as rename3, rm as rm2 } from "node:fs/promises";
|
|
10249
10569
|
import { readFileSync as readFileSync3 } from "node:fs";
|
|
10250
10570
|
import { dirname as dirname5, join as join6, relative as relative6, resolve as resolve7, sep as sep6 } from "node:path";
|
|
10251
|
-
import * as
|
|
10571
|
+
import * as ts10 from "@typescript/typescript6";
|
|
10252
10572
|
|
|
10253
10573
|
// src/delivery-files.ts
|
|
10254
10574
|
import { createHash as createHash7 } from "node:crypto";
|
|
@@ -10482,19 +10802,19 @@ function renderDeliveryTarget(graph, target, options) {
|
|
|
10482
10802
|
import { isBuiltin } from "node:module";
|
|
10483
10803
|
import { readFile as readFile4 } from "node:fs/promises";
|
|
10484
10804
|
import { resolve as resolve6 } from "node:path";
|
|
10485
|
-
import * as
|
|
10805
|
+
import * as ts9 from "@typescript/typescript6";
|
|
10486
10806
|
function checkStaticImports(path, contents) {
|
|
10487
10807
|
if (!/\.[cm]?[jt]sx?$/.test(path))
|
|
10488
10808
|
return;
|
|
10489
|
-
const source =
|
|
10809
|
+
const source = ts9.createSourceFile(path, new TextDecoder().decode(contents), ts9.ScriptTarget.Latest, true);
|
|
10490
10810
|
function visit(node) {
|
|
10491
|
-
if (
|
|
10811
|
+
if (ts9.isCallExpression(node) && (node.expression.kind === ts9.SyntaxKind.ImportKeyword || ts9.isIdentifier(node.expression) && node.expression.text === "require")) {
|
|
10492
10812
|
const argument = node.arguments[0];
|
|
10493
|
-
if (!argument || !
|
|
10813
|
+
if (!argument || !ts9.isStringLiteral(argument) && !ts9.isNoSubstitutionTemplateLiteral(argument)) {
|
|
10494
10814
|
throw new Error("Computed module loading is not supported in independent delivery bundles.");
|
|
10495
10815
|
}
|
|
10496
10816
|
}
|
|
10497
|
-
|
|
10817
|
+
ts9.forEachChild(node, visit);
|
|
10498
10818
|
}
|
|
10499
10819
|
visit(source);
|
|
10500
10820
|
}
|
|
@@ -10583,7 +10903,7 @@ async function buildDeliveryProject(options, delivery) {
|
|
|
10583
10903
|
const settings = parseDeliveryOptions(delivery);
|
|
10584
10904
|
if (typeof Bun === "undefined")
|
|
10585
10905
|
throw new Error("Independent delivery builds require Bun.");
|
|
10586
|
-
const configPath =
|
|
10906
|
+
const configPath = ts10.findConfigFile(resolve7(options.rootDir), ts10.sys.fileExists);
|
|
10587
10907
|
if (!configPath)
|
|
10588
10908
|
throw new Error("Independent delivery builds require a project tsconfig.json.");
|
|
10589
10909
|
const lexicalProject = dirname5(configPath);
|
|
@@ -10599,8 +10919,8 @@ async function buildDeliveryProject(options, delivery) {
|
|
|
10599
10919
|
if (inside(generatedRoot, sourceRoot))
|
|
10600
10920
|
throw new Error("Output must not contain the application source root.");
|
|
10601
10921
|
const configInputs = new Map;
|
|
10602
|
-
const parsedConfig =
|
|
10603
|
-
...
|
|
10922
|
+
const parsedConfig = ts10.getParsedCommandLineOfConfigFile(await realpath2(configPath), {}, {
|
|
10923
|
+
...ts10.sys,
|
|
10604
10924
|
readFile(path) {
|
|
10605
10925
|
const contents = readFileSync3(path, "utf8");
|
|
10606
10926
|
configInputs.set(resolve7(path), digest(contents));
|
|
@@ -10686,7 +11006,7 @@ async function buildDeliveryProject(options, delivery) {
|
|
|
10686
11006
|
for (const [name, contents] of Object.entries(generated))
|
|
10687
11007
|
await writeArtifact(stage, `generated/${name}`, contents);
|
|
10688
11008
|
{
|
|
10689
|
-
const program =
|
|
11009
|
+
const program = ts10.createProgram({
|
|
10690
11010
|
rootNames: [
|
|
10691
11011
|
...parsedConfig.fileNames.filter((path) => !inside(generatedRoot, path)),
|
|
10692
11012
|
...Object.keys(generated).map((name) => join6(stage, "generated", name))
|
|
@@ -10694,12 +11014,12 @@ async function buildDeliveryProject(options, delivery) {
|
|
|
10694
11014
|
options: { ...parsedConfig.options, rootDir: project },
|
|
10695
11015
|
...parsedConfig.projectReferences ? { projectReferences: parsedConfig.projectReferences } : {}
|
|
10696
11016
|
});
|
|
10697
|
-
const diagnostics =
|
|
10698
|
-
if (diagnostics.some((item) => item.category ===
|
|
10699
|
-
return failed(diagnostics.filter((item) => item.category ===
|
|
11017
|
+
const diagnostics = ts10.getPreEmitDiagnostics(program);
|
|
11018
|
+
if (diagnostics.some((item) => item.category === ts10.DiagnosticCategory.Error)) {
|
|
11019
|
+
return failed(diagnostics.filter((item) => item.category === ts10.DiagnosticCategory.Error).map((item) => ({
|
|
10700
11020
|
severity: "error",
|
|
10701
11021
|
code: "delivery-generated-type-error",
|
|
10702
|
-
message:
|
|
11022
|
+
message: ts10.flattenDiagnosticMessageText(item.messageText, `
|
|
10703
11023
|
`),
|
|
10704
11024
|
...item.file ? { file: item.file.fileName } : {}
|
|
10705
11025
|
})));
|
|
@@ -10831,7 +11151,7 @@ async function buildDeliveryProject(options, delivery) {
|
|
|
10831
11151
|
import { randomUUID } from "node:crypto";
|
|
10832
11152
|
import { lstat as lstat2, readFile as readFile6, realpath as realpath3, rename as rename4, unlink as unlink2, writeFile as writeFile3 } from "node:fs/promises";
|
|
10833
11153
|
import { dirname as dirname6, isAbsolute as isAbsolute2, relative as relative7, resolve as resolve8, sep as sep7 } from "node:path";
|
|
10834
|
-
import * as
|
|
11154
|
+
import * as ts11 from "@typescript/typescript6";
|
|
10835
11155
|
async function applyDiagnosticFix(fix, options = {}) {
|
|
10836
11156
|
if (!fix || typeof fix.targetFile !== "string")
|
|
10837
11157
|
throw new Error("Invalid DiagnosticFix");
|
|
@@ -10856,7 +11176,7 @@ async function applyDiagnosticFix(fix, options = {}) {
|
|
|
10856
11176
|
if (!current || current.initializer.getText(source) !== fix.expectedExpression) {
|
|
10857
11177
|
throw new Error("Command mode changed since diagnosis; analyze the project again");
|
|
10858
11178
|
}
|
|
10859
|
-
content = replaceProperty(source, object, fix.property,
|
|
11179
|
+
content = replaceProperty(source, object, fix.property, ts11.factory.createStringLiteral(fix.value));
|
|
10860
11180
|
break;
|
|
10861
11181
|
}
|
|
10862
11182
|
case "add_module_import": {
|
|
@@ -10867,15 +11187,15 @@ async function applyDiagnosticFix(fix, options = {}) {
|
|
|
10867
11187
|
source = parse3(file, withImport);
|
|
10868
11188
|
const object = unique(moduleObjects(source).filter((candidate) => !fix.targetModule || stringProperty(candidate, "name") === fix.targetModule), "target module");
|
|
10869
11189
|
const imports = property(object, "imports");
|
|
10870
|
-
if (imports && !
|
|
11190
|
+
if (imports && !ts11.isArrayLiteralExpression(imports.initializer)) {
|
|
10871
11191
|
throw new Error("Module imports must be a static array");
|
|
10872
11192
|
}
|
|
10873
|
-
const values = imports &&
|
|
10874
|
-
if (values.some(
|
|
11193
|
+
const values = imports && ts11.isArrayLiteralExpression(imports.initializer) ? imports.initializer.elements : [];
|
|
11194
|
+
if (values.some(ts11.isSpreadElement))
|
|
10875
11195
|
throw new Error("Module imports cannot contain spread elements");
|
|
10876
|
-
content = values.some((value) =>
|
|
11196
|
+
content = values.some((value) => ts11.isIdentifier(value) && value.text === fix.symbol) ? withImport : replaceProperty(source, object, "imports", ts11.factory.createArrayLiteralExpression([
|
|
10877
11197
|
...values,
|
|
10878
|
-
|
|
11198
|
+
ts11.factory.createIdentifier(fix.symbol)
|
|
10879
11199
|
]));
|
|
10880
11200
|
break;
|
|
10881
11201
|
}
|
|
@@ -10887,24 +11207,24 @@ async function applyDiagnosticFix(fix, options = {}) {
|
|
|
10887
11207
|
const command = findClass(source, fix.command);
|
|
10888
11208
|
const object = decoratorObject(command, "Command");
|
|
10889
11209
|
const current = property(object, "permission");
|
|
10890
|
-
if (current && (!
|
|
11210
|
+
if (current && (!ts11.isStringLiteral(current.initializer) || current.initializer.text !== permission)) {
|
|
10891
11211
|
throw new Error("Command permission already exists with a different value");
|
|
10892
11212
|
}
|
|
10893
|
-
content = current ? original : replaceProperty(source, object, "permission",
|
|
11213
|
+
content = current ? original : replaceProperty(source, object, "permission", ts11.factory.createStringLiteral(permission));
|
|
10894
11214
|
break;
|
|
10895
11215
|
}
|
|
10896
11216
|
case "add_route_parameter_binding": {
|
|
10897
11217
|
const controller = findClass(source, fix.controller);
|
|
10898
|
-
const method = unique(controller.members.filter((member) =>
|
|
10899
|
-
const parameter = unique(method.parameters.filter((candidate) =>
|
|
11218
|
+
const method = unique(controller.members.filter((member) => ts11.isMethodDeclaration(member) && nameOf(member.name) === fix.route), "route handler");
|
|
11219
|
+
const parameter = unique(method.parameters.filter((candidate) => ts11.isIdentifier(candidate.name) && candidate.name.text === fix.parameter), "same-named route parameter");
|
|
10900
11220
|
const binding = fix.binding === "param" ? "Param" : fix.binding === "query" ? "Query" : undefined;
|
|
10901
11221
|
if (!binding)
|
|
10902
11222
|
throw new Error("Invalid route binding");
|
|
10903
|
-
const decorators =
|
|
11223
|
+
const decorators = ts11.getDecorators(parameter) ?? [];
|
|
10904
11224
|
if (decorators.length > 0)
|
|
10905
11225
|
throw new Error("Parameter already has a decorator");
|
|
10906
|
-
const framework = unique(source.statements.filter((statement) =>
|
|
10907
|
-
if (!
|
|
11226
|
+
const framework = unique(source.statements.filter((statement) => ts11.isImportDeclaration(statement) && statement.importClause?.namedBindings && ts11.isNamedImports(statement.importClause.namedBindings) && statement.importClause.namedBindings.elements.some((element) => ["Controller", "Get", "Post", "Put", "Patch", "Delete", "Head", "Options"].includes(element.name.text))), "framework import");
|
|
11227
|
+
if (!ts11.isImportDeclaration(framework) || !ts11.isStringLiteral(framework.moduleSpecifier)) {
|
|
10908
11228
|
throw new Error("Framework import must be static");
|
|
10909
11229
|
}
|
|
10910
11230
|
const edited = original.slice(0, parameter.getStart(source)) + `@${binding}(${JSON.stringify(fix.parameter)}) ` + original.slice(parameter.getStart(source));
|
|
@@ -10930,15 +11250,15 @@ async function applyDiagnosticFix(fix, options = {}) {
|
|
|
10930
11250
|
return result;
|
|
10931
11251
|
}
|
|
10932
11252
|
function parse3(file, text) {
|
|
10933
|
-
const result =
|
|
11253
|
+
const result = ts11.transpileModule(text, {
|
|
10934
11254
|
fileName: file,
|
|
10935
11255
|
reportDiagnostics: true,
|
|
10936
|
-
compilerOptions: { target:
|
|
11256
|
+
compilerOptions: { target: ts11.ScriptTarget.ESNext, experimentalDecorators: true }
|
|
10937
11257
|
});
|
|
10938
|
-
if (result.diagnostics?.some((item) => item.category ===
|
|
11258
|
+
if (result.diagnostics?.some((item) => item.category === ts11.DiagnosticCategory.Error)) {
|
|
10939
11259
|
throw new Error("Cannot fix syntactically invalid TypeScript");
|
|
10940
11260
|
}
|
|
10941
|
-
return
|
|
11261
|
+
return ts11.createSourceFile(file, text, ts11.ScriptTarget.Latest, true, ts11.ScriptKind.TS);
|
|
10942
11262
|
}
|
|
10943
11263
|
function unique(items, description) {
|
|
10944
11264
|
if (items.length !== 1)
|
|
@@ -10952,50 +11272,50 @@ function identifier(value) {
|
|
|
10952
11272
|
function nameOf(name) {
|
|
10953
11273
|
if (!name)
|
|
10954
11274
|
return "";
|
|
10955
|
-
return
|
|
11275
|
+
return ts11.isIdentifier(name) || ts11.isStringLiteral(name) || ts11.isNumericLiteral(name) ? name.text : "";
|
|
10956
11276
|
}
|
|
10957
11277
|
function property(object, key) {
|
|
10958
|
-
if (object.properties.some((item) => !
|
|
11278
|
+
if (object.properties.some((item) => !ts11.isPropertyAssignment(item) || ts11.isComputedPropertyName(item.name))) {
|
|
10959
11279
|
throw new Error("Fix requires explicit static object properties");
|
|
10960
11280
|
}
|
|
10961
|
-
const values = object.properties.filter((item) =>
|
|
11281
|
+
const values = object.properties.filter((item) => ts11.isPropertyAssignment(item) && nameOf(item.name) === key);
|
|
10962
11282
|
if (values.length > 1)
|
|
10963
11283
|
throw new Error(`Duplicate '${key}' property`);
|
|
10964
11284
|
return values[0];
|
|
10965
11285
|
}
|
|
10966
11286
|
function stringProperty(object, key) {
|
|
10967
11287
|
const value = property(object, key)?.initializer;
|
|
10968
|
-
return value &&
|
|
11288
|
+
return value && ts11.isStringLiteral(value) ? value.text : undefined;
|
|
10969
11289
|
}
|
|
10970
11290
|
function replaceProperty(source, object, key, value) {
|
|
10971
11291
|
const previous = property(object, key);
|
|
10972
|
-
const replacement =
|
|
11292
|
+
const replacement = ts11.factory.createPropertyAssignment(key, value);
|
|
10973
11293
|
const properties = object.properties.map((item) => item === previous ? replacement : item);
|
|
10974
11294
|
if (!previous)
|
|
10975
11295
|
properties.push(replacement);
|
|
10976
|
-
const updated =
|
|
10977
|
-
return source.text.slice(0, object.getStart(source)) +
|
|
11296
|
+
const updated = ts11.factory.updateObjectLiteralExpression(object, properties);
|
|
11297
|
+
return source.text.slice(0, object.getStart(source)) + ts11.createPrinter().printNode(ts11.EmitHint.Expression, updated, source) + source.text.slice(object.end);
|
|
10978
11298
|
}
|
|
10979
11299
|
function findClass(source, name) {
|
|
10980
|
-
return unique(source.statements.filter((statement) =>
|
|
11300
|
+
return unique(source.statements.filter((statement) => ts11.isClassDeclaration(statement) && statement.name?.text === name), `class '${name}'`);
|
|
10981
11301
|
}
|
|
10982
11302
|
function decoratorObject(node, name) {
|
|
10983
|
-
const decorator = unique((
|
|
10984
|
-
const argument =
|
|
10985
|
-
if (!argument || !
|
|
11303
|
+
const decorator = unique((ts11.getDecorators(node) ?? []).filter((item) => ts11.isCallExpression(item.expression) && item.expression.expression.getText() === name), `@${name} decorator`);
|
|
11304
|
+
const argument = ts11.isCallExpression(decorator.expression) ? decorator.expression.arguments[0] : undefined;
|
|
11305
|
+
if (!argument || !ts11.isObjectLiteralExpression(argument))
|
|
10986
11306
|
throw new Error(`@${name} requires a static object`);
|
|
10987
11307
|
return argument;
|
|
10988
11308
|
}
|
|
10989
11309
|
function moduleObjects(source) {
|
|
10990
11310
|
const result = [];
|
|
10991
11311
|
for (const statement of source.statements) {
|
|
10992
|
-
if (
|
|
11312
|
+
if (ts11.isClassDeclaration(statement) && (ts11.getDecorators(statement) ?? []).some((item) => ts11.isCallExpression(item.expression) && item.expression.expression.getText() === "Module")) {
|
|
10993
11313
|
result.push(decoratorObject(statement, "Module"));
|
|
10994
11314
|
}
|
|
10995
|
-
if (
|
|
11315
|
+
if (ts11.isVariableStatement(statement)) {
|
|
10996
11316
|
for (const declaration of statement.declarationList.declarations) {
|
|
10997
11317
|
const call = declaration.initializer;
|
|
10998
|
-
if (call &&
|
|
11318
|
+
if (call && ts11.isCallExpression(call) && ["defineModule", "defineFeatureSlice"].includes(call.expression.getText()) && call.arguments[0] && ts11.isObjectLiteralExpression(call.arguments[0])) {
|
|
10999
11319
|
result.push(call.arguments[0]);
|
|
11000
11320
|
}
|
|
11001
11321
|
}
|
|
@@ -11009,19 +11329,19 @@ function importSymbol(source, path, symbol) {
|
|
|
11009
11329
|
const target = resolve8(dirname6(source.fileName), path).replace(/\.(tsx?|mts|cts)$/, "");
|
|
11010
11330
|
if (current === target)
|
|
11011
11331
|
return source.text;
|
|
11012
|
-
const matches = source.statements.filter((item) =>
|
|
11332
|
+
const matches = source.statements.filter((item) => ts11.isImportDeclaration(item) && ts11.isStringLiteral(item.moduleSpecifier) && item.moduleSpecifier.text === path);
|
|
11013
11333
|
if (matches.length > 1)
|
|
11014
11334
|
throw new Error(`Ambiguous imports from '${path}'`);
|
|
11015
11335
|
const match = matches[0];
|
|
11016
|
-
if (match &&
|
|
11336
|
+
if (match && ts11.isImportDeclaration(match) && match.importClause?.namedBindings && ts11.isNamedImports(match.importClause.namedBindings) && !match.importClause.isTypeOnly) {
|
|
11017
11337
|
if (match.importClause.namedBindings.elements.some((item) => item.name.text === symbol))
|
|
11018
11338
|
return source.text;
|
|
11019
11339
|
const bindings = match.importClause.namedBindings;
|
|
11020
|
-
const updated =
|
|
11340
|
+
const updated = ts11.factory.updateNamedImports(bindings, [
|
|
11021
11341
|
...bindings.elements,
|
|
11022
|
-
|
|
11342
|
+
ts11.factory.createImportSpecifier(false, undefined, ts11.factory.createIdentifier(symbol))
|
|
11023
11343
|
]);
|
|
11024
|
-
return source.text.slice(0, bindings.getStart(source)) +
|
|
11344
|
+
return source.text.slice(0, bindings.getStart(source)) + ts11.createPrinter().printNode(ts11.EmitHint.Unspecified, updated, source) + source.text.slice(bindings.end);
|
|
11025
11345
|
}
|
|
11026
11346
|
if (match)
|
|
11027
11347
|
throw new Error(`Import from '${path}' is not a named value import`);
|
|
@@ -11436,7 +11756,7 @@ function watchProject(options) {
|
|
|
11436
11756
|
// src/migrations.ts
|
|
11437
11757
|
import { rename as rename5, readFile as readFile9, writeFile as writeFile4, rm as rm3 } from "node:fs/promises";
|
|
11438
11758
|
import { relative as relative10, resolve as resolve12 } from "node:path";
|
|
11439
|
-
import * as
|
|
11759
|
+
import * as ts12 from "@typescript/typescript6";
|
|
11440
11760
|
|
|
11441
11761
|
// src/migration-policy.ts
|
|
11442
11762
|
import { readFile as readFile8 } from "node:fs/promises";
|
|
@@ -11455,9 +11775,9 @@ function compilerVersion() {
|
|
|
11455
11775
|
}
|
|
11456
11776
|
function migrationDependencies() {
|
|
11457
11777
|
return {
|
|
11458
|
-
"@supacloud/app": "0.
|
|
11778
|
+
"@supacloud/app": "0.15.0",
|
|
11459
11779
|
"@supacloud/compiler": compilerVersion(),
|
|
11460
|
-
"@supacloud/elysia": "0.
|
|
11780
|
+
"@supacloud/elysia": "0.18.0",
|
|
11461
11781
|
elysia: "1.4.30",
|
|
11462
11782
|
typescript: "7.0.2"
|
|
11463
11783
|
};
|
|
@@ -11480,29 +11800,29 @@ async function checkMigrationDependencies(rootDir) {
|
|
|
11480
11800
|
// src/migrations.ts
|
|
11481
11801
|
var ROUTE_DECORATORS2 = new Set(["Get", "Post", "Put", "Patch", "Delete", "Head", "Options"]);
|
|
11482
11802
|
var MIGRATION_COMPILER_OPTIONS = {
|
|
11483
|
-
target:
|
|
11484
|
-
module:
|
|
11485
|
-
moduleResolution:
|
|
11803
|
+
target: ts12.ScriptTarget.ES2022,
|
|
11804
|
+
module: ts12.ModuleKind.ESNext,
|
|
11805
|
+
moduleResolution: ts12.ModuleResolutionKind.Bundler,
|
|
11486
11806
|
noEmit: true,
|
|
11487
11807
|
skipLibCheck: true
|
|
11488
11808
|
};
|
|
11489
11809
|
function migrationCompilerOptions(rootDir) {
|
|
11490
11810
|
if (!rootDir)
|
|
11491
11811
|
return MIGRATION_COMPILER_OPTIONS;
|
|
11492
|
-
const configPath =
|
|
11812
|
+
const configPath = ts12.findConfigFile(rootDir, ts12.sys.fileExists);
|
|
11493
11813
|
if (!configPath)
|
|
11494
11814
|
return MIGRATION_COMPILER_OPTIONS;
|
|
11495
11815
|
const configHost = {
|
|
11496
|
-
...
|
|
11816
|
+
...ts12.sys,
|
|
11497
11817
|
onUnRecoverableConfigFileDiagnostic: (_diagnostic) => {}
|
|
11498
11818
|
};
|
|
11499
|
-
const parsed =
|
|
11819
|
+
const parsed = ts12.getParsedCommandLineOfConfigFile(configPath, {}, configHost);
|
|
11500
11820
|
if (!parsed || parsed.errors.length > 0)
|
|
11501
11821
|
return MIGRATION_COMPILER_OPTIONS;
|
|
11502
11822
|
return { ...parsed.options, noEmit: true, skipLibCheck: true };
|
|
11503
11823
|
}
|
|
11504
11824
|
function propertyName2(property) {
|
|
11505
|
-
if (
|
|
11825
|
+
if (ts12.isIdentifier(property) || ts12.isStringLiteral(property) || ts12.isNumericLiteral(property))
|
|
11506
11826
|
return property.text;
|
|
11507
11827
|
return;
|
|
11508
11828
|
}
|
|
@@ -11512,7 +11832,7 @@ function lineOf2(sourceFile, node) {
|
|
|
11512
11832
|
function resolveSymbol(symbol, checker) {
|
|
11513
11833
|
if (!symbol)
|
|
11514
11834
|
return;
|
|
11515
|
-
for (let guard = 0;guard < 4 && (symbol.flags &
|
|
11835
|
+
for (let guard = 0;guard < 4 && (symbol.flags & ts12.SymbolFlags.Alias) !== 0; guard += 1) {
|
|
11516
11836
|
const aliased = checker.getAliasedSymbol(symbol);
|
|
11517
11837
|
if (aliased === symbol)
|
|
11518
11838
|
break;
|
|
@@ -11521,12 +11841,12 @@ function resolveSymbol(symbol, checker) {
|
|
|
11521
11841
|
return symbol;
|
|
11522
11842
|
}
|
|
11523
11843
|
function symbolForExpression(expression, checker) {
|
|
11524
|
-
const location =
|
|
11844
|
+
const location = ts12.isIdentifier(expression) ? expression : ts12.isPropertyAccessExpression(expression) ? expression.name : ts12.isElementAccessExpression(expression) && expression.argumentExpression && ts12.isStringLiteral(expression.argumentExpression) ? expression : undefined;
|
|
11525
11845
|
return location ? resolveSymbol(checker.getSymbolAtLocation(location), checker) : undefined;
|
|
11526
11846
|
}
|
|
11527
11847
|
function unwrapExpression(expression) {
|
|
11528
11848
|
let current = expression;
|
|
11529
|
-
while (
|
|
11849
|
+
while (ts12.isAsExpression(current) || ts12.isSatisfiesExpression(current) || ts12.isParenthesizedExpression(current) || ts12.isTypeAssertionExpression(current)) {
|
|
11530
11850
|
current = current.expression;
|
|
11531
11851
|
}
|
|
11532
11852
|
return current;
|
|
@@ -11539,7 +11859,7 @@ function isDefineRouteContractCall(node, checker) {
|
|
|
11539
11859
|
}
|
|
11540
11860
|
function isRouteDecoratorCall(node, checker) {
|
|
11541
11861
|
const name = node.expression.getText(node.getSourceFile());
|
|
11542
|
-
if (
|
|
11862
|
+
if (ts12.isIdentifier(node.expression) && ROUTE_DECORATORS2.has(node.expression.text))
|
|
11543
11863
|
return true;
|
|
11544
11864
|
if (ROUTE_DECORATORS2.has(name))
|
|
11545
11865
|
return true;
|
|
@@ -11553,14 +11873,14 @@ function resolveStaticObjectLiteral2(input, checker, seen = new Set) {
|
|
|
11553
11873
|
if (seen.has(expression))
|
|
11554
11874
|
return;
|
|
11555
11875
|
seen.add(expression);
|
|
11556
|
-
if (
|
|
11876
|
+
if (ts12.isObjectLiteralExpression(expression))
|
|
11557
11877
|
return expression;
|
|
11558
|
-
if (
|
|
11878
|
+
if (ts12.isCallExpression(expression) && isDefineRouteContractCall(expression, checker)) {
|
|
11559
11879
|
return resolveStaticObjectLiteral2(expression.arguments[0], checker, seen);
|
|
11560
11880
|
}
|
|
11561
11881
|
const symbol = symbolForExpression(expression, checker);
|
|
11562
11882
|
for (const declaration of symbol?.declarations ?? []) {
|
|
11563
|
-
if (
|
|
11883
|
+
if (ts12.isVariableDeclaration(declaration) && declaration.initializer) {
|
|
11564
11884
|
const resolved = resolveStaticObjectLiteral2(declaration.initializer, checker, seen);
|
|
11565
11885
|
if (resolved)
|
|
11566
11886
|
return resolved;
|
|
@@ -11574,7 +11894,7 @@ function displayFile(rootDir, fileName) {
|
|
|
11574
11894
|
}
|
|
11575
11895
|
function createMigrationProgram(fileNames, sourceOverrides, rootDir) {
|
|
11576
11896
|
const compilerOptions = migrationCompilerOptions(rootDir);
|
|
11577
|
-
const host =
|
|
11897
|
+
const host = ts12.createCompilerHost(compilerOptions);
|
|
11578
11898
|
const getSourceFile = host.getSourceFile.bind(host);
|
|
11579
11899
|
const fileExists = host.fileExists.bind(host);
|
|
11580
11900
|
const readFile = host.readFile.bind(host);
|
|
@@ -11583,16 +11903,16 @@ function createMigrationProgram(fileNames, sourceOverrides, rootDir) {
|
|
|
11583
11903
|
host.readFile = (fileName) => sourceOverrides.get(resolve12(fileName)) ?? readFile(fileName);
|
|
11584
11904
|
host.getSourceFile = (fileName, languageVersion, onError, shouldCreateNewSourceFile) => {
|
|
11585
11905
|
const source = sourceOverrides.get(resolve12(fileName));
|
|
11586
|
-
return source === undefined ? getSourceFile(fileName, languageVersion, onError, shouldCreateNewSourceFile) :
|
|
11906
|
+
return source === undefined ? getSourceFile(fileName, languageVersion, onError, shouldCreateNewSourceFile) : ts12.createSourceFile(fileName, source, languageVersion, true);
|
|
11587
11907
|
};
|
|
11588
11908
|
host.getCurrentDirectory = () => rootDir ?? currentDirectory();
|
|
11589
|
-
return
|
|
11909
|
+
return ts12.createProgram(fileNames, compilerOptions, host);
|
|
11590
11910
|
}
|
|
11591
11911
|
function routeResponseProperties(object) {
|
|
11592
|
-
const responseProperties = object.properties.filter((property) =>
|
|
11912
|
+
const responseProperties = object.properties.filter((property) => ts12.isPropertyAssignment(property) && propertyName2(property.name) === "response");
|
|
11593
11913
|
return {
|
|
11594
11914
|
response: responseProperties[0],
|
|
11595
|
-
hasResponses: object.properties.some((property) => (
|
|
11915
|
+
hasResponses: object.properties.some((property) => (ts12.isPropertyAssignment(property) || ts12.isShorthandPropertyAssignment(property)) && propertyName2(property.name) === "responses"),
|
|
11596
11916
|
duplicateResponse: responseProperties.length > 1
|
|
11597
11917
|
};
|
|
11598
11918
|
}
|
|
@@ -11612,7 +11932,7 @@ function planRouteResponseMigration(sourceFiles, checker, rootDir, includedFiles
|
|
|
11612
11932
|
if (!includedFiles.has(sourcePath))
|
|
11613
11933
|
continue;
|
|
11614
11934
|
const visit = (node) => {
|
|
11615
|
-
if (
|
|
11935
|
+
if (ts12.isCallExpression(node) && isRouteDecoratorCall(node, checker)) {
|
|
11616
11936
|
const options = node.arguments[1];
|
|
11617
11937
|
const object = options && resolveStaticObjectLiteral2(options, checker);
|
|
11618
11938
|
if (object) {
|
|
@@ -11632,7 +11952,7 @@ function planRouteResponseMigration(sourceFiles, checker, rootDir, includedFiles
|
|
|
11632
11952
|
}
|
|
11633
11953
|
}
|
|
11634
11954
|
}
|
|
11635
|
-
|
|
11955
|
+
ts12.forEachChild(node, visit);
|
|
11636
11956
|
};
|
|
11637
11957
|
visit(sourceFile);
|
|
11638
11958
|
}
|
|
@@ -11792,7 +12112,7 @@ async function migrateProject(options) {
|
|
|
11792
12112
|
}
|
|
11793
12113
|
}
|
|
11794
12114
|
const include = options.include ?? ["**/*.ts", "**/*.tsx", "**/*.mts", "**/*.cts"];
|
|
11795
|
-
const files =
|
|
12115
|
+
const files = ts12.sys.readDirectory(rootDir, [".ts", ".tsx", ".mts", ".cts"], ["node_modules", "dist", "generated"], include).sort();
|
|
11796
12116
|
const results = [];
|
|
11797
12117
|
const issues = [];
|
|
11798
12118
|
const pendingWrites = new Map;
|
|
@@ -12678,6 +12998,13 @@ var DEFAULT_SUPACLOUD_CONFIG = {
|
|
|
12678
12998
|
generateOpenApi: true,
|
|
12679
12999
|
generatePermissions: true,
|
|
12680
13000
|
treeShakeUnusedProviders: true,
|
|
13001
|
+
commandCapabilities: {
|
|
13002
|
+
requirePersistentAdapters: true,
|
|
13003
|
+
permission: true,
|
|
13004
|
+
audit: true,
|
|
13005
|
+
idempotency: true,
|
|
13006
|
+
transaction: true
|
|
13007
|
+
},
|
|
12681
13008
|
moduleBoundaryPreset: "modular-monolith"
|
|
12682
13009
|
};
|
|
12683
13010
|
function defineSupacloudConfig(config = {}) {
|
|
@@ -12761,7 +13088,7 @@ function resolveSupacloudConfig(config = {}, cwd = process.cwd()) {
|
|
|
12761
13088
|
...resolved.openApi === undefined ? {} : { openApi: resolved.openApi },
|
|
12762
13089
|
generatePermissions: resolved.generatePermissions ?? DEFAULT_SUPACLOUD_CONFIG.generatePermissions,
|
|
12763
13090
|
moduleBoundaryPreset: resolved.moduleBoundaryPreset ?? DEFAULT_SUPACLOUD_CONFIG.moduleBoundaryPreset,
|
|
12764
|
-
commandCapabilities: resolved.commandCapabilities,
|
|
13091
|
+
commandCapabilities: resolved.commandCapabilities ?? DEFAULT_SUPACLOUD_CONFIG.commandCapabilities,
|
|
12765
13092
|
...resolved.moduleBoundaries ? { moduleBoundaries: resolved.moduleBoundaries } : {},
|
|
12766
13093
|
...resolved.typeSafety ? { typeSafety: resolved.typeSafety } : {},
|
|
12767
13094
|
...resolved.allowRouteCommandBindings === undefined ? {} : { allowRouteCommandBindings: resolved.allowRouteCommandBindings },
|
|
@@ -12793,6 +13120,7 @@ function compileOptionsFromConfig(config, cwd = process.cwd()) {
|
|
|
12793
13120
|
|
|
12794
13121
|
// src/index.ts
|
|
12795
13122
|
init_graphql_schema();
|
|
13123
|
+
init_database_contracts();
|
|
12796
13124
|
export {
|
|
12797
13125
|
ANGULAR_ENTERPRISE_RULES,
|
|
12798
13126
|
CLEAN_ARCHITECTURE_RULES,
|
|
@@ -12809,6 +13137,7 @@ export {
|
|
|
12809
13137
|
MODULE_BOUNDARY_PROFILES,
|
|
12810
13138
|
ModuleDependencyGraph,
|
|
12811
13139
|
OpenApiDocumentError,
|
|
13140
|
+
SQL_SAFETY_DIAGNOSTIC_CODES,
|
|
12812
13141
|
SUPACLOUD_MIGRATIONS,
|
|
12813
13142
|
TYPE_SAFETY_DIAGNOSTIC_CODES,
|
|
12814
13143
|
TraitCompiler,
|
|
@@ -12837,6 +13166,7 @@ export {
|
|
|
12837
13166
|
formatGraph,
|
|
12838
13167
|
formatOpenApiDiff,
|
|
12839
13168
|
generateApplication,
|
|
13169
|
+
generateDatabaseContracts,
|
|
12840
13170
|
generateFeatureSource,
|
|
12841
13171
|
getModuleBoundaryPreset,
|
|
12842
13172
|
getModuleBoundaryProfile,
|
|
@@ -12845,6 +13175,7 @@ export {
|
|
|
12845
13175
|
loadSupacloudConfig,
|
|
12846
13176
|
migrateProject,
|
|
12847
13177
|
migrateRouteResponse,
|
|
13178
|
+
parseDatabaseContractsOptions,
|
|
12848
13179
|
parseDeliveryBuildManifest,
|
|
12849
13180
|
parseDeliveryBuildResult,
|
|
12850
13181
|
parseDeliveryOptions,
|
|
@@ -12858,6 +13189,8 @@ export {
|
|
|
12858
13189
|
renderOpenApi,
|
|
12859
13190
|
resolveModuleBoundaries,
|
|
12860
13191
|
resolveSupacloudConfig,
|
|
13192
|
+
runDatabaseContractsFile,
|
|
13193
|
+
scanDrizzleSql,
|
|
12861
13194
|
scanGeneratedArtifacts,
|
|
12862
13195
|
scanProductionSource,
|
|
12863
13196
|
serializeOpenApiJson,
|