Merge branch 'release'

This commit is contained in:
Xu Chang 2024-04-23 20:36:42 +08:00
commit 3e2b2b10a4
14 changed files with 422 additions and 151 deletions

View File

@ -574,7 +574,7 @@ function outputIntializeDev(cwd, dependencies, briefNames, sourceFile, printer,
* @param printer
* @param filename
*/
function outputFeatureIndex(dependencies, briefNames, sourceFile, printer, filename) {
function outputFeatureIndex(dependencies, briefNames, sourceFile, printer, filename, isModule) {
const { statements } = sourceFile;
let statements2 = [];
if (dependencies.length > 0) {
@ -605,6 +605,20 @@ function outputFeatureIndex(dependencies, briefNames, sourceFile, printer, filen
...importStatements,
...statements.slice(5)
];
if (isModule) {
statements2.push(factory.createFunctionDeclaration([
factory.createToken(ts.SyntaxKind.ExportKeyword),
factory.createToken(ts.SyntaxKind.AsyncKeyword)
], undefined, factory.createIdentifier("initialize"), [
factory.createTypeParameterDeclaration(undefined, factory.createIdentifier("ED"), factory.createTypeReferenceNode(factory.createIdentifier("EntityDict"), undefined), undefined)
], [
factory.createParameterDeclaration(undefined, undefined, factory.createIdentifier("features"), undefined, factory.createIntersectionTypeNode([
factory.createTypeReferenceNode(factory.createIdentifier("FeatureDict"), [factory.createTypeReferenceNode(factory.createIdentifier("ED"), undefined)]),
factory.createTypeReferenceNode(factory.createIdentifier("BasicFeatures"), [factory.createTypeReferenceNode(factory.createIdentifier("ED"), undefined)]),
...briefNames.map((ele) => factory.createTypeReferenceNode(factory.createIdentifier(`${(0, string_1.firstLetterUpperCase)(ele)}FeatureDict`), [factory.createTypeReferenceNode(factory.createIdentifier("ED"), undefined)]))
]), undefined)
], undefined, factory.createBlock([], true)));
}
}
else {
statements2 = [...statements];
@ -786,12 +800,47 @@ function tryCopyModuleTemplateFiles(cwd, dependencies, briefNames, printer) {
injectDataIndexFile(join(cwd, 'src', 'data', 'index.ts'), injectDataIndexFileBriefNames, printer);
}
}
/**
* 对于module类型的项目在feature/index.ts中注入initialize函数
* @param cwd
* @param dependencies
* @param briefNames
*/
function injectInitializeToFeatureIndex(cwd, dependencies, briefNames, printer) {
const featureIndexFile = join(cwd, 'src', 'features', 'index.ts');
const sourceFile = ts.createSourceFile('index.ts', (0, fs_1.readFileSync)(featureIndexFile, 'utf-8'), ts.ScriptTarget.Latest, false, ts.ScriptKind.TS);
const { statements } = sourceFile;
const initializeStmt = statements.find((stmt) => ts.isFunctionDeclaration(stmt) && ts.isIdentifier(stmt.name) && stmt.name.text === 'initialize');
if (!initializeStmt) {
const statements2 = [
...(dependencies.map((dep, idx) => factory.createImportDeclaration(undefined, factory.createImportClause(false, undefined, factory.createNamedImports([factory.createImportSpecifier(false, factory.createIdentifier("FeatureDict"), factory.createIdentifier(`${(0, string_1.firstLetterUpperCase)(briefNames[idx])}FeatureDict`))])), factory.createStringLiteral(dep), undefined))),
...statements,
factory.createFunctionDeclaration([
factory.createToken(ts.SyntaxKind.ExportKeyword),
factory.createToken(ts.SyntaxKind.AsyncKeyword)
], undefined, factory.createIdentifier("initialize"), [
factory.createTypeParameterDeclaration(undefined, factory.createIdentifier("ED"), factory.createTypeReferenceNode(factory.createIdentifier("EntityDict"), undefined), undefined)
], [
factory.createParameterDeclaration(undefined, undefined, factory.createIdentifier("features"), undefined, factory.createIntersectionTypeNode([
factory.createTypeReferenceNode(factory.createIdentifier("FeatureDict"), [factory.createTypeReferenceNode(factory.createIdentifier("ED"), undefined)]),
factory.createTypeReferenceNode(factory.createIdentifier("BasicFeatures"), [factory.createTypeReferenceNode(factory.createIdentifier("ED"), undefined)]),
...briefNames.map((ele) => factory.createTypeReferenceNode(factory.createIdentifier(`${(0, string_1.firstLetterUpperCase)(ele)}FeatureDict`), [factory.createTypeReferenceNode(factory.createIdentifier("ED"), undefined)]))
]), undefined)
], undefined, factory.createBlock([], true))
];
const result = printer.printList(ts.ListFormat.SourceFileStatements, factory.createNodeArray(statements2), sourceFile);
(0, fs_1.writeFileSync)(featureIndexFile, result, { flag: 'w' });
console.log(`注入${featureIndexFile}文件成功用户可以自己修正initialize函数的参数和逻辑`);
}
}
/**
* 本函数用于构建src/initialize.dev, src/initialize.prod, src/initializeFeatures, src/context/FrontendContext, src/contextBackendContext
* 这些和dependency相关的项目文件
*/
function buildDependency(rebuild) {
const cwd = process.cwd();
const webDir = join(cwd, 'web');
const isModule = !(0, fs_1.existsSync)(webDir); // 如果没有web目录说明是module不需要处理模块级别的文件注入
const depConfigFile = join(cwd, 'src', 'configuration', 'dependency.ts');
if (!(0, fs_1.existsSync)(depConfigFile)) {
console.error(`${depConfigFile}不存在,无法构建启动文件`);
@ -801,8 +850,8 @@ function buildDependency(rebuild) {
const dependencies = depGraph.ascOrder;
const briefNames = dependencies.map((dep, idx) => `${dep.split('-').map(ele => ele[0]).join('')}${idx}`);
const templateFileList = [
join(cwd, 'node_modules', env_1.OAK_CLI_MODULE_NAME, 'templateFiles', 'initialize.dev.ts'),
join(cwd, 'node_modules', env_1.OAK_CLI_MODULE_NAME, 'templateFiles', 'initialize.prod.ts'),
join(cwd, 'node_modules', env_1.OAK_CLI_MODULE_NAME, 'templateFiles', 'initialize.frontend.ts'),
join(cwd, 'node_modules', env_1.OAK_CLI_MODULE_NAME, 'templateFiles', 'initialize.server.ts'),
join(cwd, 'node_modules', env_1.OAK_CLI_MODULE_NAME, 'templateFiles', 'initializeFeatures.ts'),
join(cwd, 'node_modules', env_1.OAK_CLI_MODULE_NAME, 'templateFiles', 'RuntimeCxt.ts'),
join(cwd, 'node_modules', env_1.OAK_CLI_MODULE_NAME, 'templateFiles', 'DependentExceptions.ts'),
@ -811,26 +860,28 @@ function buildDependency(rebuild) {
];
const program = ts.createProgram(templateFileList, {});
const printer = ts.createPrinter({ newLine: ts.NewLineKind.LineFeed });
const initDevFile = join(cwd, 'src', 'initialize.dev.ts');
if ((0, fs_1.existsSync)(initDevFile) && !rebuild) {
console.log(`[${initDevFile}]文件已经存在,无需构建启动文件`);
}
else {
outputIntializeDev(cwd, dependencies, briefNames, program.getSourceFile(templateFileList[0]), printer, initDevFile);
}
const initProdFile = join(cwd, 'src', 'initialize.prod.ts');
if ((0, fs_1.existsSync)(initProdFile) && !rebuild) {
console.log(`[${initProdFile}]文件已经存在,无需构建启动文件`);
}
else {
outputIntializeProd(cwd, dependencies, briefNames, program.getSourceFile(templateFileList[1]), printer, initProdFile);
}
const initFeaturesFile = join(cwd, 'src', 'initializeFeatures.ts');
if ((0, fs_1.existsSync)(initFeaturesFile) && !rebuild) {
console.log(`[${initFeaturesFile}]文件已经存在,无需构建启动文件`);
}
else {
outputIntializeFeatures(cwd, dependencies, briefNames, program.getSourceFile(templateFileList[2]), printer, initFeaturesFile);
if (!isModule) {
const initDevFile = join(cwd, 'src', 'initialize.frontend.ts');
if ((0, fs_1.existsSync)(initDevFile) && !rebuild) {
console.log(`[${initDevFile}]文件已经存在,无需构建启动文件`);
}
else {
outputIntializeDev(cwd, dependencies, briefNames, program.getSourceFile(templateFileList[0]), printer, initDevFile);
}
const initProdFile = join(cwd, 'src', 'initialize.server.ts');
if ((0, fs_1.existsSync)(initProdFile) && !rebuild) {
console.log(`[${initProdFile}]文件已经存在,无需构建启动文件`);
}
else {
outputIntializeProd(cwd, dependencies, briefNames, program.getSourceFile(templateFileList[1]), printer, initProdFile);
}
const initFeaturesFile = join(cwd, 'src', 'initializeFeatures.ts');
if ((0, fs_1.existsSync)(initFeaturesFile) && !rebuild) {
console.log(`[${initFeaturesFile}]文件已经存在,无需构建启动文件`);
}
else {
outputIntializeFeatures(cwd, dependencies, briefNames, program.getSourceFile(templateFileList[2]), printer, initFeaturesFile);
}
}
const dependentContextFile = join(cwd, 'src', 'context', 'DependentContext.ts');
if ((0, fs_1.existsSync)(dependentContextFile) && !rebuild) {
@ -865,9 +916,11 @@ function buildDependency(rebuild) {
console.log(`[${featureIndexFile}]文件已经存在,无需构建启动文件`);
}
else {
outputFeatureIndex(dependencies, briefNames, program.getSourceFile(templateFileList[6]), printer, featureIndexFile);
outputFeatureIndex(dependencies, briefNames, program.getSourceFile(templateFileList[6]), printer, featureIndexFile, isModule);
}
// 把各个依赖项目的一些初始化的文件拷贝过去
tryCopyModuleTemplateFiles(cwd, dependencies, briefNames, printer);
if (!isModule) {
tryCopyModuleTemplateFiles(cwd, dependencies, briefNames, printer);
}
}
exports.default = buildDependency;

View File

@ -14,8 +14,8 @@ export declare abstract class AsyncContext<ED extends EntityDict> implements Con
opResult: OperationResult<ED>;
private message?;
events: {
commit: Array<() => Promise<void>>;
rollback: Array<() => Promise<void>>;
commit: Array<(records: OpRecord<ED>[], cxtStr: string) => Promise<void>>;
rollback: Array<(records: OpRecord<ED>[], cxtStr: string) => Promise<void>>;
};
/**
*
@ -27,7 +27,7 @@ export declare abstract class AsyncContext<ED extends EntityDict> implements Con
getScene(): string | undefined;
setScene(scene?: string): void;
private resetEvents;
on(event: 'commit' | 'rollback', callback: () => Promise<void>): void;
on(event: 'commit' | 'rollback', callback: (records: OpRecord<ED>[], cxtStr: string) => Promise<void>): void;
saveOpRecord<T extends keyof ED>(entity: T, operation: ED[T]['Operation']): void;
/**
* context中不应该有并发的事务使

View File

@ -119,9 +119,12 @@ class AsyncContext {
if (this.uuid) {
await this.rowStore.commit(this.uuid);
const { commit: commitEvents } = this.events;
for (const e of commitEvents) {
/* for (const e of commitEvents) {
await e();
}
} */
// 提交时不能等在跨事务trigger上
const cxtStr = await this.toString();
commitEvents.forEach(evt => evt(this.opRecords, cxtStr));
this.uuid = undefined;
this.resetEvents();
this.opRecords = [];
@ -133,9 +136,9 @@ class AsyncContext {
if (this.uuid) {
await this.rowStore.rollback(this.uuid);
const { rollback: rollbackEvents } = this.events;
for (const e of rollbackEvents) {
await e();
}
// 回退时不能等在跨事务trigger上
const cxtStr = await this.toString();
rollbackEvents.forEach(evt => evt(this.opRecords, cxtStr));
this.uuid = undefined;
this.opRecords = [];
this.opResult = {};

View File

@ -221,19 +221,22 @@ class TriggerExecutor {
}
}
postCommitTrigger(entity, operation, trigger, context, option) {
context.on('commit', async () => {
context.on('commit', async (opRecords, cxtStr) => {
let ids = [];
let cxtStr = await context.toString();
const { opRecords } = context;
let cxtStr2 = cxtStr;
const { data } = operation;
if (operation.action === 'create') {
if (data instanceof Array) {
ids = data.map(ele => ele.id);
cxtStr = data[0].$$triggerData$$?.cxtStr || await context.toString();
if (data[0].$$triggerData$$?.cxtStr) {
cxtStr2 = data[0].$$triggerData$$?.cxtStr;
}
}
else {
ids = [data.id];
cxtStr = data.$$triggerData$$?.cxtStr || await context.toString();
if (data.$$triggerData$$?.cxtStr) {
cxtStr2 = data.$$triggerData$$?.cxtStr;
}
}
}
else {
@ -242,7 +245,9 @@ class TriggerExecutor {
* 若trigger是takeEasy只会在事务提交时做一次使用当前context应也无大问题
* 暂时先这样设计若当前提交事务中改变了cxt内容也许会有问题by Xc 20240319
*/
cxtStr = data.$$triggerData$$?.cxtStr || await context.toString();
if (data.$$triggerData$$?.cxtStr) {
cxtStr2 = data.$$triggerData$$?.cxtStr;
}
const record = opRecords.find(ele => ele.id === operation.id);
// 目前框架在operation时一定会将ids记录在operation当中见CascadeStore中的doUpdateSingleRowAsync函数
(0, assert_1.default)(record && record.a !== 'c');
@ -250,7 +255,7 @@ class TriggerExecutor {
ids = f.id.$in;
}
// 此时项目的上下文和执行此trigger时的上下文可能不一致rootMode采用当时的上下文cxtStr来执行
this.onVolatileTrigger(entity, trigger, ids, cxtStr, option);
this.onVolatileTrigger(entity, trigger, ids, cxtStr2, option);
});
}
preOperation(entity, operation, context, option) {
@ -335,6 +340,8 @@ class TriggerExecutor {
const { fn } = trigger;
const callback = await fn({ ids }, context, option);
if (trigger.strict === 'makeSure') {
// 这里开root模式否则还可能有权限问题
context.openRootMode();
await context.operate(entity, {
id: await (0, uuid_1.generateNewIdAsync)(),
action: 'update',

View File

@ -1,14 +1,12 @@
import { AuthDeduceRelationMap, EntityDict } from './Entity';
import { EntityDict as BaseEntityDict } from "../base-app-domain";
import { AsyncContext } from '../store/AsyncRowStore';
import { SyncConfig } from "./Sync";
import { AttrUpdateMatrix } from './EntityDesc';
import { ActionDefDict } from './Action';
import { StyleDict } from './Style';
/**
*
*/
export type ServerConfiguration<ED extends BaseEntityDict & EntityDict, Cxt extends AsyncContext<ED>> = {
export type ServerConfiguration = {
database: {
type: 'mysql';
host: string;
@ -22,7 +20,20 @@ export type ServerConfiguration<ED extends BaseEntityDict & EntityDict, Cxt exte
workDir: {
path: string;
};
sync?: SyncConfig<ED, Cxt>;
port: number;
hostname: string;
nginx?: {
ssl: boolean;
apiPath: string;
port?: number;
socketPath: string;
};
cors?: {
origin: string;
headers?: string[];
methods?: string[];
};
internalExceptionMask?: string;
};
/**
* 访

View File

@ -351,83 +351,83 @@ function makeException(data) {
break;
}
case 'OakUserException': {
const e = new OakUserException(data.message);
e = new OakUserException(data.message);
break;
}
case 'OakRowInconsistencyException': {
const e = new OakRowInconsistencyException(data.data, data.message);
e = new OakRowInconsistencyException(data.data, data.message);
break;
}
case 'OakInputIllegalException': {
const e = new OakInputIllegalException(data.entity, data.attributes, data.message);
e = new OakInputIllegalException(data.entity, data.attributes, data.message);
break;
}
case 'OakAttrCantUpdateException': {
const e = new OakAttrCantUpdateException(data.entity, data.attributes, data.message);
e = new OakAttrCantUpdateException(data.entity, data.attributes, data.message);
break;
}
case 'OakUserUnpermittedException': {
const e = new OakUserUnpermittedException(data.entity, data.operation, data.message);
e = new OakUserUnpermittedException(data.entity, data.operation, data.message);
break;
}
case 'OakUserInvisibleException': {
const e = new OakUserInvisibleException(data.entity, data.operation, data.message);
e = new OakUserInvisibleException(data.entity, data.operation, data.message);
break;
}
case 'OakUnloggedInException': {
const e = new OakUnloggedInException(data.message);
e = new OakUnloggedInException(data.message);
break;
}
case 'OakCongruentRowExists': {
const e = new OakCongruentRowExists(data.entity, data.data, data.message);
e = new OakCongruentRowExists(data.entity, data.data, data.message);
break;
}
case 'OakRowLockedException': {
const e = new OakRowLockedException(data.message);
e = new OakRowLockedException(data.message);
break;
}
case 'OakRowUnexistedException': {
const e = new OakRowUnexistedException(data.rows);
e = new OakRowUnexistedException(data.rows);
break;
}
case 'OakDeadlock': {
const e = new OakDeadlock(data.message);
e = new OakDeadlock(data.message);
break;
}
case 'OakDataException': {
const e = new OakDataException(data.message);
e = new OakDataException(data.message);
break;
}
case 'OakNoRelationDefException': {
const e = new OakNoRelationDefException(data.entity, data.action, data.message);
e = new OakNoRelationDefException(data.entity, data.action, data.message);
break;
}
case 'OakUniqueViolationException': {
const e = new OakUniqueViolationException(data.rows, data.message);
e = new OakUniqueViolationException(data.rows, data.message);
break;
}
case 'OakImportDataParseException': {
const e = new OakImportDataParseException(data.message, data.line, data.header);
e = new OakImportDataParseException(data.message, data.line, data.header);
break;
}
case 'OakPreConditionUnsetException': {
const e = new OakPreConditionUnsetException(data.message, data.entity, data.code);
e = new OakPreConditionUnsetException(data.message, data.entity, data.code);
break;
}
case 'OakAttrNotNullException': {
const e = new OakAttrNotNullException(data.entity, data.attributes, data.message);
e = new OakAttrNotNullException(data.entity, data.attributes, data.message);
break;
}
case 'OakExternalException': {
const e = new OakExternalException(data.source, data.code, data.message, data.data);
e = new OakExternalException(data.source, data.code, data.message, data.data);
break;
}
case 'OakNetworkException': {
const e = new OakNetworkException(data.message);
e = new OakNetworkException(data.message);
break;
}
case 'OakServerProxyException': {
const e = new OakServerProxyException(data.message);
e = new OakServerProxyException(data.message);
break;
}
default:

View File

@ -137,16 +137,9 @@ class SimpleConnector {
const responseType = response.headers.get('Content-Type') ||
response.headers.get('content-type');
if (responseType?.toLocaleLowerCase().match(/application\/json/i)) {
const { url, path, port, namespace } = await response.json();
let url2 = url || this.serverUrl;
if (port) {
url2 += `:${port}`;
}
if (namespace) {
url2 += namespace;
}
const { url, path } = await response.json();
return {
url: url2,
url,
path,
};
}

View File

@ -1,6 +1,6 @@
{
"name": "oak-domain",
"version": "5.0.0",
"version": "5.0.1",
"author": {
"name": "XuChang"
},

View File

@ -1088,7 +1088,8 @@ function outputFeatureIndex(
briefNames: string[],
sourceFile: ts.SourceFile,
printer: ts.Printer,
filename: string
filename: string,
isModule: boolean,
) {
const { statements } = sourceFile;
let statements2: ts.Statement[] = [];
@ -1142,6 +1143,69 @@ function outputFeatureIndex(
...importStatements,
...statements.slice(5)
];
if (isModule) {
statements2.push(
factory.createFunctionDeclaration(
[
factory.createToken(ts.SyntaxKind.ExportKeyword),
factory.createToken(ts.SyntaxKind.AsyncKeyword)
],
undefined,
factory.createIdentifier("initialize"),
[
factory.createTypeParameterDeclaration(
undefined,
factory.createIdentifier("ED"),
factory.createTypeReferenceNode(
factory.createIdentifier("EntityDict"),
undefined
),
undefined
)
],
[
factory.createParameterDeclaration(
undefined,
undefined,
factory.createIdentifier("features"),
undefined,
factory.createIntersectionTypeNode([
factory.createTypeReferenceNode(
factory.createIdentifier("FeatureDict"),
[factory.createTypeReferenceNode(
factory.createIdentifier("ED"),
undefined
)]
),
factory.createTypeReferenceNode(
factory.createIdentifier("BasicFeatures"),
[factory.createTypeReferenceNode(
factory.createIdentifier("ED"),
undefined
)]
),
...briefNames.map(
(ele) => factory.createTypeReferenceNode(
factory.createIdentifier(`${firstLetterUpperCase(ele)}FeatureDict`),
[factory.createTypeReferenceNode(
factory.createIdentifier("ED"),
undefined
)]
)
)
]),
undefined
)
],
undefined,
factory.createBlock(
[],
true
)
)
);
}
}
else {
statements2 = [...statements];
@ -1305,7 +1369,7 @@ function outputIntializeFeatures(
function injectDataIndexFile(
dataIndexFile: string,
briefNames: string[],
printer: ts.Printer,
printer: ts.Printer
) {
const sourceFile = ts.createSourceFile('index.ts', readFileSync(dataIndexFile, 'utf-8'), ts.ScriptTarget.Latest, false, ts.ScriptKind.TS);
@ -1457,6 +1521,121 @@ function tryCopyModuleTemplateFiles(
}
}
/**
* module类型的项目feature/index.ts中注入initialize函数
* @param cwd
* @param dependencies
* @param briefNames
*/
function injectInitializeToFeatureIndex(
cwd: string,
dependencies: string[],
briefNames: string[],
printer: ts.Printer,
) {
const featureIndexFile = join(cwd, 'src', 'features', 'index.ts');
const sourceFile = ts.createSourceFile('index.ts', readFileSync(featureIndexFile, 'utf-8'), ts.ScriptTarget.Latest, false, ts.ScriptKind.TS);
const { statements } = sourceFile;
const initializeStmt = statements.find(
(stmt) => ts.isFunctionDeclaration(stmt) && ts.isIdentifier(stmt.name!) && stmt.name.text === 'initialize'
);
if (!initializeStmt) {
const statements2 = [
...(
dependencies.map(
(dep, idx) => factory.createImportDeclaration(
undefined,
factory.createImportClause(
false,
undefined,
factory.createNamedImports(
[factory.createImportSpecifier(
false,
factory.createIdentifier("FeatureDict"),
factory.createIdentifier(`${firstLetterUpperCase(briefNames[idx])}FeatureDict`)
)]
)
),
factory.createStringLiteral(dep),
undefined
)
)
),
...statements,
factory.createFunctionDeclaration(
[
factory.createToken(ts.SyntaxKind.ExportKeyword),
factory.createToken(ts.SyntaxKind.AsyncKeyword)
],
undefined,
factory.createIdentifier("initialize"),
[
factory.createTypeParameterDeclaration(
undefined,
factory.createIdentifier("ED"),
factory.createTypeReferenceNode(
factory.createIdentifier("EntityDict"),
undefined
),
undefined
)
],
[
factory.createParameterDeclaration(
undefined,
undefined,
factory.createIdentifier("features"),
undefined,
factory.createIntersectionTypeNode([
factory.createTypeReferenceNode(
factory.createIdentifier("FeatureDict"),
[factory.createTypeReferenceNode(
factory.createIdentifier("ED"),
undefined
)]
),
factory.createTypeReferenceNode(
factory.createIdentifier("BasicFeatures"),
[factory.createTypeReferenceNode(
factory.createIdentifier("ED"),
undefined
)]
),
...briefNames.map(
(ele) => factory.createTypeReferenceNode(
factory.createIdentifier(`${firstLetterUpperCase(ele)}FeatureDict`),
[factory.createTypeReferenceNode(
factory.createIdentifier("ED"),
undefined
)]
)
)
]),
undefined
)
],
undefined,
factory.createBlock(
[],
true
)
)
];
const result = printer.printList(
ts.ListFormat.SourceFileStatements,
factory.createNodeArray(statements2),
sourceFile);
writeFileSync(featureIndexFile, result, { flag: 'w' });
console.log(`注入${featureIndexFile}文件成功用户可以自己修正initialize函数的参数和逻辑`);
}
}
/**
* src/initialize.dev, src/initialize.prod, src/initializeFeatures, src/context/FrontendContext, src/contextBackendContext
* dependency相关的项目文件
@ -1464,6 +1643,10 @@ function tryCopyModuleTemplateFiles(
export default function buildDependency(rebuild?: boolean) {
const cwd = process.cwd();
const webDir = join(cwd, 'web');
const isModule = !existsSync(webDir); // 如果没有web目录说明是module不需要处理模块级别的文件注入
const depConfigFile = join(cwd, 'src', 'configuration', 'dependency.ts');
if (!existsSync(depConfigFile)) {
console.error(`${depConfigFile}不存在,无法构建启动文件`);
@ -1478,8 +1661,8 @@ export default function buildDependency(rebuild?: boolean) {
);
const templateFileList = [
join(cwd, 'node_modules', OAK_CLI_MODULE_NAME, 'templateFiles', 'initialize.dev.ts'),
join(cwd, 'node_modules', OAK_CLI_MODULE_NAME, 'templateFiles', 'initialize.prod.ts'),
join(cwd, 'node_modules', OAK_CLI_MODULE_NAME, 'templateFiles', 'initialize.frontend.ts'),
join(cwd, 'node_modules', OAK_CLI_MODULE_NAME, 'templateFiles', 'initialize.server.ts'),
join(cwd, 'node_modules', OAK_CLI_MODULE_NAME, 'templateFiles', 'initializeFeatures.ts'),
join(cwd, 'node_modules', OAK_CLI_MODULE_NAME, 'templateFiles', 'RuntimeCxt.ts'),
join(cwd, 'node_modules', OAK_CLI_MODULE_NAME, 'templateFiles', 'DependentExceptions.ts'),
@ -1490,30 +1673,31 @@ export default function buildDependency(rebuild?: boolean) {
const program = ts.createProgram(templateFileList, {});
const printer = ts.createPrinter({ newLine: ts.NewLineKind.LineFeed });
if (!isModule) {
const initDevFile = join(cwd, 'src', 'initialize.frontend.ts');
if (existsSync(initDevFile) && !rebuild) {
console.log(`[${initDevFile}]文件已经存在,无需构建启动文件`);
}
else {
outputIntializeDev(cwd, dependencies, briefNames, program.getSourceFile(templateFileList[0])!, printer, initDevFile);
}
const initDevFile = join(cwd, 'src', 'initialize.dev.ts');
if (existsSync(initDevFile) && !rebuild) {
console.log(`[${initDevFile}]文件已经存在,无需构建启动文件`);
}
else {
outputIntializeDev(cwd, dependencies, briefNames, program.getSourceFile(templateFileList[0])!, printer, initDevFile);
}
const initProdFile = join(cwd, 'src', 'initialize.prod.ts');
if (existsSync(initProdFile) && !rebuild) {
console.log(`[${initProdFile}]文件已经存在,无需构建启动文件`);
}
else {
outputIntializeProd(cwd, dependencies, briefNames, program.getSourceFile(templateFileList[1])!, printer, initProdFile);
}
const initProdFile = join(cwd, 'src', 'initialize.server.ts');
if (existsSync(initProdFile) && !rebuild) {
console.log(`[${initProdFile}]文件已经存在,无需构建启动文件`);
}
else {
outputIntializeProd(cwd, dependencies, briefNames, program.getSourceFile(templateFileList[1])!, printer, initProdFile);
}
const initFeaturesFile = join(cwd, 'src', 'initializeFeatures.ts');
if (existsSync(initFeaturesFile) && !rebuild) {
console.log(`[${initFeaturesFile}]文件已经存在,无需构建启动文件`);
}
else {
outputIntializeFeatures(cwd, dependencies, briefNames, program.getSourceFile(templateFileList[2])!, printer, initFeaturesFile);
const initFeaturesFile = join(cwd, 'src', 'initializeFeatures.ts');
if (existsSync(initFeaturesFile) && !rebuild) {
console.log(`[${initFeaturesFile}]文件已经存在,无需构建启动文件`);
}
else {
outputIntializeFeatures(cwd, dependencies, briefNames, program.getSourceFile(templateFileList[2])!, printer, initFeaturesFile);
}
}
const dependentContextFile = join(cwd, 'src', 'context', 'DependentContext.ts');
@ -1553,9 +1737,11 @@ export default function buildDependency(rebuild?: boolean) {
console.log(`[${featureIndexFile}]文件已经存在,无需构建启动文件`);
}
else {
outputFeatureIndex(dependencies, briefNames, program.getSourceFile(templateFileList[6])!, printer, featureIndexFile);
outputFeatureIndex(dependencies, briefNames, program.getSourceFile(templateFileList[6])!, printer, featureIndexFile, isModule);
}
// 把各个依赖项目的一些初始化的文件拷贝过去
tryCopyModuleTemplateFiles(cwd, dependencies, briefNames, printer);
if (!isModule) {
tryCopyModuleTemplateFiles(cwd, dependencies, briefNames, printer);
}
}

View File

@ -17,8 +17,8 @@ export abstract class AsyncContext<ED extends EntityDict> implements Context {
opResult: OperationResult<ED>;
private message?: string;
events: {
commit: Array<() => Promise<void>>;
rollback: Array<() => Promise<void>>;
commit: Array<(records: OpRecord<ED>[], cxtStr: string) => Promise<void>>;
rollback: Array<(records: OpRecord<ED>[], cxtStr: string) => Promise<void>>;
}
/**
@ -82,7 +82,7 @@ export abstract class AsyncContext<ED extends EntityDict> implements Context {
};
}
on(event: 'commit' | 'rollback', callback: () => Promise<void>): void {
on(event: 'commit' | 'rollback', callback: (records: OpRecord<ED>[], cxtStr: string) => Promise<void>): void {
this.uuid && this.events[event].push(callback);
}
@ -136,9 +136,15 @@ export abstract class AsyncContext<ED extends EntityDict> implements Context {
if (this.uuid) {
await this.rowStore.commit(this.uuid!);
const { commit: commitEvents } = this.events;
for (const e of commitEvents) {
/* for (const e of commitEvents) {
await e();
}
} */
// 提交时不能等在跨事务trigger上
const cxtStr = await this.toString();
commitEvents.forEach(
evt => evt(this.opRecords, cxtStr)
);
this.uuid = undefined;
this.resetEvents();
this.opRecords = [];
@ -150,9 +156,12 @@ export abstract class AsyncContext<ED extends EntityDict> implements Context {
if (this.uuid) {
await this.rowStore.rollback(this.uuid!);
const { rollback: rollbackEvents } = this.events;
for (const e of rollbackEvents) {
await e();
}
// 回退时不能等在跨事务trigger上
const cxtStr = await this.toString();
rollbackEvents.forEach(
evt => evt(this.opRecords, cxtStr)
);
this.uuid = undefined;
this.opRecords = [];
this.opResult = {};

View File

@ -287,19 +287,22 @@ export class TriggerExecutor<ED extends EntityDict & BaseEntityDict, Cxt extends
context: Cxt,
option: OperateOption
) {
context.on('commit', async () => {
context.on('commit', async (opRecords, cxtStr) => {
let ids = [] as string[];
let cxtStr = await context.toString();
const { opRecords } = context;
let cxtStr2 = cxtStr;
const { data } = operation;
if (operation.action === 'create') {
if (data instanceof Array) {
ids = data.map(ele => ele.id!);
cxtStr = data[0].$$triggerData$$?.cxtStr || await context.toString();
if (data[0].$$triggerData$$?.cxtStr) {
cxtStr2 = data[0].$$triggerData$$?.cxtStr;
}
}
else {
ids = [data.id!];
cxtStr = data.$$triggerData$$?.cxtStr || await context.toString();
if (data.$$triggerData$$?.cxtStr) {
cxtStr2 = data.$$triggerData$$?.cxtStr;
}
}
}
else {
@ -308,7 +311,9 @@ export class TriggerExecutor<ED extends EntityDict & BaseEntityDict, Cxt extends
* trigger是takeEasy使context应也无大问题
* cxt内容by Xc 20240319
*/
cxtStr = (<ED[T]['Update']['data']>data).$$triggerData$$?.cxtStr || await context.toString();
if ((<ED[T]['Update']['data']>data).$$triggerData$$?.cxtStr) {
cxtStr2 = (<ED[T]['Update']['data']>data).$$triggerData$$?.cxtStr;
}
const record = opRecords.find(
ele => (ele as CreateOpResult<ED, keyof ED>).id === operation.id,
);
@ -318,7 +323,7 @@ export class TriggerExecutor<ED extends EntityDict & BaseEntityDict, Cxt extends
ids = f!.id!.$in;
}
// 此时项目的上下文和执行此trigger时的上下文可能不一致rootMode采用当时的上下文cxtStr来执行
this.onVolatileTrigger(entity, trigger, ids, cxtStr, option);
this.onVolatileTrigger(entity, trigger, ids, cxtStr2, option);
});
}
@ -426,6 +431,8 @@ export class TriggerExecutor<ED extends EntityDict & BaseEntityDict, Cxt extends
const { fn } = trigger as VolatileTrigger<ED, T, Cxt>;
const callback = await fn({ ids }, context, option);
if (trigger.strict === 'makeSure') {
// 这里开root模式否则还可能有权限问题
context.openRootMode();
await context.operate(entity, {
id: await generateNewIdAsync(),
action: 'update',

View File

@ -1,17 +1,14 @@
// 将项目的所有配置规范化到一起未完成by Xc 20240207
import { AuthDeduceRelationMap, EntityDict } from './Entity';
import { EntityDict as BaseEntityDict } from "../base-app-domain";
import { AsyncContext } from '../store/AsyncRowStore';
import { SyncConfig } from "./Sync";
import { AttrUpdateMatrix } from './EntityDesc';
import { ActionDefDict } from './Action';
import { StyleDict } from './Style';
import { Exportation, Importation } from './Port';
/**
*
*/
export type ServerConfiguration<ED extends BaseEntityDict & EntityDict, Cxt extends AsyncContext<ED>> = {
export type ServerConfiguration = {
database: {
type: 'mysql',
host: string;
@ -25,7 +22,20 @@ export type ServerConfiguration<ED extends BaseEntityDict & EntityDict, Cxt exte
workDir: {
path: string;
},
sync?: SyncConfig<ED, Cxt>;
port: number;
hostname: string;
nginx?: {
ssl: boolean;
apiPath: string;
port?: number;
socketPath: string;
},
cors?: {
origin: string;
headers?: string[];
methods?: string[];
},
internalExceptionMask?: string;
};
/**

View File

@ -392,71 +392,71 @@ export function makeException<ED extends EntityDict>(data: {
break;
}
case 'OakUserException': {
const e = new OakUserException(data.message);
e = new OakUserException(data.message);
break;
}
case 'OakRowInconsistencyException': {
const e = new OakRowInconsistencyException(data.data, data.message);
e = new OakRowInconsistencyException(data.data, data.message);
break;
}
case 'OakInputIllegalException': {
const e = new OakInputIllegalException(data.entity, data.attributes, data.message);
e = new OakInputIllegalException(data.entity, data.attributes, data.message);
break;
}
case 'OakAttrCantUpdateException': {
const e = new OakAttrCantUpdateException(data.entity, data.attributes, data.message);
e = new OakAttrCantUpdateException(data.entity, data.attributes, data.message);
break;
}
case 'OakUserUnpermittedException': {
const e = new OakUserUnpermittedException(data.entity, data.operation, data.message);
e = new OakUserUnpermittedException(data.entity, data.operation, data.message);
break;
}
case 'OakUserInvisibleException': {
const e = new OakUserInvisibleException(data.entity, data.operation, data.message);
e = new OakUserInvisibleException(data.entity, data.operation, data.message);
break;
}
case 'OakUnloggedInException': {
const e = new OakUnloggedInException(data.message);
e = new OakUnloggedInException(data.message);
break;
}
case 'OakCongruentRowExists': {
const e = new OakCongruentRowExists(data.entity, data.data, data.message);
e = new OakCongruentRowExists(data.entity, data.data, data.message);
break;
}
case 'OakRowLockedException': {
const e = new OakRowLockedException(data.message);
e = new OakRowLockedException(data.message);
break;
}
case 'OakRowUnexistedException': {
const e = new OakRowUnexistedException(data.rows);
e = new OakRowUnexistedException(data.rows);
break;
}
case 'OakDeadlock': {
const e = new OakDeadlock(data.message);
e = new OakDeadlock(data.message);
break;
}
case 'OakDataException': {
const e = new OakDataException(data.message);
e = new OakDataException(data.message);
break;
}
case 'OakNoRelationDefException': {
const e = new OakNoRelationDefException(data.entity, data.action, data.message);
e = new OakNoRelationDefException(data.entity, data.action, data.message);
break;
}
case 'OakUniqueViolationException': {
const e = new OakUniqueViolationException(data.rows, data.message);
e = new OakUniqueViolationException(data.rows, data.message);
break;
}
case 'OakImportDataParseException': {
const e = new OakImportDataParseException(data.message!, data.line, data.header);
e = new OakImportDataParseException(data.message!, data.line, data.header);
break;
}
case 'OakPreConditionUnsetException': {
const e = new OakPreConditionUnsetException(data.message, data.entity, data.code);
e = new OakPreConditionUnsetException(data.message, data.entity, data.code);
break;
}
case 'OakAttrNotNullException': {
const e = new OakAttrNotNullException(
e = new OakAttrNotNullException(
data.entity,
data.attributes,
data.message
@ -464,15 +464,15 @@ export function makeException<ED extends EntityDict>(data: {
break;
}
case 'OakExternalException': {
const e = new OakExternalException(data.source, data.code, data.message, data.data);
e = new OakExternalException(data.source, data.code, data.message, data.data);
break;
}
case 'OakNetworkException': {
const e = new OakNetworkException(data.message);
e = new OakNetworkException(data.message);
break;
}
case 'OakServerProxyException': {
const e = new OakServerProxyException(data.message);
e = new OakServerProxyException(data.message);
break;
}
default:

View File

@ -161,18 +161,10 @@ export default class SimpleConnector<ED extends EntityDict, FrontCxt extends Syn
response.headers.get('Content-Type') ||
response.headers.get('content-type');
if (responseType?.toLocaleLowerCase().match(/application\/json/i)) {
const { url, path, port, namespace } = await response.json();
let url2 = url || this.serverUrl;
if (port) {
url2 += `:${port}`;
}
if (namespace) {
url2 += namespace;
}
const { url, path } = await response.json();
return {
url: url2,
url,
path,
};
} else {