@appaflytech/zeyrek-ai-mcp 0.0.30 → 0.0.32
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/.env.example +0 -15
- package/README.md +5 -5
- package/dist/config.js +0 -37
- package/dist/index.js +0 -2
- package/dist/knowledge/backendConstraints.js +1 -18
- package/dist/knowledge/boxSections.js +184 -0
- package/dist/knowledge/componentCatalog.js +232 -562
- package/dist/knowledge/componentEvents.js +21 -141
- package/dist/knowledge/layoutSummary.js +2 -140
- package/dist/knowledge/normalize.js +67 -236
- package/dist/knowledge/outlines.js +0 -36
- package/dist/knowledge/policy.js +4 -18
- package/dist/knowledge/reservedTables.js +0 -79
- package/dist/knowledge/rules.js +21 -319
- package/dist/knowledge/schemaGuard.js +0 -11
- package/dist/knowledge/styleResolve.js +38 -55
- package/dist/knowledge/themeResolve.js +3 -3
- package/dist/observability/trace.js +3 -6
- package/dist/platform/diagnostics.js +4 -10
- package/dist/platform/domSummary.js +1 -1
- package/dist/platform/errors.js +10 -162
- package/dist/platform/http.js +8 -29
- package/dist/platform/responses.js +5 -39
- package/dist/platform/{capture/capture.js → screenshot.js} +519 -105
- package/dist/platform/secrets.js +1 -17
- package/dist/platform/strictArguments.js +12 -68
- package/dist/platform/tokenVault.js +4 -187
- package/dist/resources/examples/layout-app-shell.json +707 -229
- package/dist/resources/examples/page-detail-profile.json +1 -2
- package/dist/resources/examples/page-dynamic-form.json +2 -3
- package/dist/resources/examples/page-flex-sections.json +1 -1
- package/dist/resources/examples/page-html-embed.json +1 -1
- package/dist/resources/examples/workflow-basic.json +2 -8
- package/dist/resources/guides/component.md +4 -91
- package/dist/resources/guides/entity.md +8 -50
- package/dist/resources/guides/expressions.md +5 -14
- package/dist/resources/guides/functions.md +1 -1
- package/dist/resources/guides/getting-started.md +1 -38
- package/dist/resources/guides/layout.md +58 -55
- package/dist/resources/guides/microservice.md +2 -81
- package/dist/resources/guides/middleware.md +2 -44
- package/dist/resources/guides/page.md +75 -454
- package/dist/resources/guides/theme.md +2 -8
- package/dist/resources/guides/translation.md +1 -1
- package/dist/resources/guides/workflow.md +1 -58
- package/dist/resources/rules.js +5 -11
- package/dist/resources/schema-fingerprint.json +5 -7
- package/dist/resources/schemas/CreateEntityCommand.json +0 -118
- package/dist/resources/schemas/CreateMicroserviceCommand.json +1 -13
- package/dist/resources/schemas/CreateQueryCommand.json +0 -4
- package/dist/resources/schemas/CreateWorkflowCommand.json +1 -143
- package/dist/resources/schemas/DataJobDto.json +1 -6
- package/dist/resources/schemas/GlobalFunctionUpdateResultDto.json +1 -21
- package/dist/resources/schemas/MicroserviceDto.json +3 -15
- package/dist/resources/schemas/PreviewMicroserviceCommand.json +1 -10
- package/dist/resources/schemas/PreviewWorkflowCommand.json +1 -22
- package/dist/resources/schemas/StepDto.json +1 -22
- package/dist/resources/schemas/UpdateEntityCommand.json +0 -118
- package/dist/resources/schemas/UpdateMicroserviceCommand.json +1 -13
- package/dist/resources/schemas/UpdateQueryCommand.json +0 -4
- package/dist/resources/schemas/UpdateViewCommand.json +2 -0
- package/dist/resources/schemas/UpdateWorkflowCommand.json +1 -143
- package/dist/server.js +1 -27
- package/dist/tools/access.js +2 -369
- package/dist/tools/applications.js +3 -60
- package/dist/tools/common.js +0 -7
- package/dist/tools/components.js +14 -118
- package/dist/tools/datasources.js +3 -3
- package/dist/tools/dependencies.js +1 -1
- package/dist/tools/deployments.js +2 -2
- package/dist/tools/designSchemaPatch.js +18 -34
- package/dist/tools/dtos.js +5 -36
- package/dist/tools/entities.js +12 -79
- package/dist/tools/externalComponents.js +5 -26
- package/dist/tools/flows.js +14 -122
- package/dist/tools/folders.js +1 -1
- package/dist/tools/imports.js +2 -5
- package/dist/tools/layouts.js +17 -50
- package/dist/tools/merges.js +1 -1
- package/dist/tools/microservices.js +25 -77
- package/dist/tools/pages.js +112 -465
- package/dist/tools/platformInfo.js +8 -27
- package/dist/tools/projects.js +9 -71
- package/dist/tools/rows.js +13 -69
- package/dist/tools/schemaOps.js +1 -61
- package/dist/tools/screenWrite.js +8 -37
- package/dist/tools/screens.js +5 -95
- package/dist/tools/templates.js +1 -26
- package/dist/tools/translations.js +1 -1
- package/dist/tools/variables.js +5 -11
- package/dist/tools/workflows.js +5 -10
- package/package.json +2 -2
- package/dist/knowledge/access.js +0 -802
- package/dist/knowledge/case.js +0 -41
- package/dist/knowledge/connectDataBind.js +0 -819
- package/dist/knowledge/connectDataDefinition.js +0 -341
- package/dist/knowledge/connectDataExecute.js +0 -93
- package/dist/knowledge/connectDataRules.js +0 -128
- package/dist/knowledge/decision.js +0 -121
- package/dist/knowledge/expressionPrelude.js +0 -59
- package/dist/knowledge/microserviceParameters.js +0 -58
- package/dist/knowledge/mining.js +0 -94
- package/dist/knowledge/mobileComponentCatalog.js +0 -859
- package/dist/knowledge/mobileComponentEvents.js +0 -239
- package/dist/knowledge/platform/lint.js +0 -385
- package/dist/knowledge/platform/mobile.js +0 -142
- package/dist/knowledge/platform/mobileLayoutLint.js +0 -324
- package/dist/knowledge/platform/mobileRules.js +0 -231
- package/dist/knowledge/platform/mobileSpacing.js +0 -82
- package/dist/knowledge/platform/mobileTabs.js +0 -108
- package/dist/knowledge/platform/nativeBoxModel.js +0 -44
- package/dist/knowledge/platform/profile.js +0 -25
- package/dist/knowledge/platform/resolve.js +0 -122
- package/dist/knowledge/platform/web.js +0 -39
- package/dist/knowledge/process.js +0 -510
- package/dist/knowledge/rowFilter.js +0 -43
- package/dist/knowledge/styleResolveNative.js +0 -527
- package/dist/platform/argumentAliases.js +0 -128
- package/dist/platform/bodyFile.js +0 -103
- package/dist/platform/capture/actions.js +0 -333
- package/dist/platform/capture/browser.js +0 -1
- package/dist/platform/capture/console-log.js +0 -122
- package/dist/platform/capture/index.js +0 -11
- package/dist/platform/capture/readiness.js +0 -103
- package/dist/platform/capture/summary.js +0 -77
- package/dist/platform/capture/target.js +0 -5
- package/dist/platform/capture/targets/studio-mobile.js +0 -15
- package/dist/platform/capture/targets/studio-web.js +0 -5
- package/dist/platform/impersonation.js +0 -45
- package/dist/platform/jsonArray.js +0 -24
- package/dist/platform/schemaReadMemory.js +0 -60
- package/dist/resources/examples/bpm-gorev-kontrol.json +0 -294
- package/dist/resources/examples/bpm-iso-dokuman.json +0 -228
- package/dist/resources/examples/bpm-satinalma.json +0 -376
- package/dist/resources/examples/connect-data-convert-verify.json +0 -107
- package/dist/resources/examples/connect-data-dropdown-roles.json +0 -66
- package/dist/resources/examples/connect-data-edit-m2m.json +0 -73
- package/dist/resources/examples/connect-data-query-params.json +0 -50
- package/dist/resources/examples/connect-data-table-entity.json +0 -147
- package/dist/resources/examples/connect-data-workflow-microservice.json +0 -65
- package/dist/resources/examples/mobile-app-shell-drawer.json +0 -243
- package/dist/resources/examples/mobile-app-shell-stack.json +0 -157
- package/dist/resources/examples/mobile-app-shell-tab.json +0 -208
- package/dist/resources/examples/mobile-form-screen.json +0 -365
- package/dist/resources/examples/mobile-list-detail-form.json +0 -672
- package/dist/resources/guides/access.md +0 -796
- package/dist/resources/guides/bpm.md +0 -686
- package/dist/resources/guides/case.md +0 -153
- package/dist/resources/guides/decision.md +0 -185
- package/dist/resources/guides/mobile-layout.md +0 -193
- package/dist/resources/guides/mobile.md +0 -495
- package/dist/resources/read.js +0 -41
- package/dist/resources/schemas/AccessExplanationDto.json +0 -144
- package/dist/resources/schemas/AccessMatrixDto.json +0 -170
- package/dist/resources/schemas/AccessPermissionDto.json +0 -57
- package/dist/resources/schemas/AccessPolicyDto.json +0 -223
- package/dist/resources/schemas/AccessResourceDto.json +0 -86
- package/dist/resources/schemas/AppSessionDto.json +0 -53
- package/dist/resources/schemas/ConnectDataDescribeDto.json +0 -344
- package/dist/resources/schemas/ConnectDataRequestModel.json +0 -202
- package/dist/resources/schemas/ConnectDataResultDto.json +0 -421
- package/dist/resources/schemas/ConnectorCredentialDto.json +0 -70
- package/dist/resources/schemas/ConvertConnectDataToMicroserviceCommand.json +0 -37
- package/dist/resources/schemas/CreateAccessPolicyCommand.json +0 -209
- package/dist/resources/schemas/CreateDecisionCommand.json +0 -221
- package/dist/resources/schemas/CreateProcessDefinitionCommand.json +0 -1337
- package/dist/resources/schemas/DecisionDto.json +0 -302
- package/dist/resources/schemas/DetachConnectDataMicroserviceCommand.json +0 -28
- package/dist/resources/schemas/ExplainAccessQuery.json +0 -33
- package/dist/resources/schemas/GenerateProcessDocumentCommand.json +0 -116
- package/dist/resources/schemas/OrganizationProjectDto.json +0 -335
- package/dist/resources/schemas/OrganizationSchemaDto.json +0 -196
- package/dist/resources/schemas/OrganizationSchemaVersionDto.json +0 -207
- package/dist/resources/schemas/PatchProcessDefinitionCommand.json +0 -76
- package/dist/resources/schemas/ProcessDefinitionDto.json +0 -1480
- package/dist/resources/schemas/ProcessSimulationDto.json +0 -96
- package/dist/resources/schemas/SaveConnectorCredentialCommand.json +0 -90
- package/dist/resources/schemas/SaveProcessEventSourceCommand.json +0 -61
- package/dist/resources/schemas/SaveProcessMailSourceCommand.json +0 -89
- package/dist/resources/schemas/SignProcessTaskCommand.json +0 -73
- package/dist/resources/schemas/SimulateProcessDefinitionQuery.json +0 -92
- package/dist/resources/schemas/UpdateAccessPolicyCommand.json +0 -209
- package/dist/resources/schemas/UpdateDecisionCommand.json +0 -216
- package/dist/resources/schemas/UpdateProcessDefinitionCommand.json +0 -1357
- package/dist/resources/schemas/ValidateProcessDefinitionQuery.json +0 -1339
- package/dist/tools/accessOperations.js +0 -135
- package/dist/tools/accessPolicies.js +0 -170
- package/dist/tools/accessRuntime.js +0 -203
- package/dist/tools/bpm.js +0 -343
- package/dist/tools/bpmGovernance.js +0 -266
- package/dist/tools/bpmOperations.js +0 -230
- package/dist/tools/bpmReporting.js +0 -154
- package/dist/tools/bpmRouting.js +0 -150
- package/dist/tools/bpmRuntime.js +0 -396
- package/dist/tools/case.js +0 -178
- package/dist/tools/caseRuntime.js +0 -89
- package/dist/tools/connectData.js +0 -449
- package/dist/tools/connectors.js +0 -193
- package/dist/tools/decisions.js +0 -206
- package/dist/tools/mobile.js +0 -221
- package/dist/tools/modules.js +0 -48
- package/dist/tools/organizationStructure.js +0 -137
- package/dist/tools/profileWrite.js +0 -92
|
@@ -1,39 +0,0 @@
|
|
|
1
|
-
import { ENGINE_STEP_TYPES, STEP_ALIASES } from './profile.js';
|
|
2
|
-
import { pageContainerNode } from '../normalize.js';
|
|
3
|
-
/**
|
|
4
|
-
* Web profili = gateway'in profil öncesi davranışı. Buradaki her değer bugün kodda bir yerde
|
|
5
|
-
* örtük olarak vardı; `test/platform-profile.test.ts` golden'ı bu profilin çıktıyı değiştirmediğini
|
|
6
|
-
* kilitler. Değer değiştirmek web davranışını değiştirir — golden kırmızıya döner.
|
|
7
|
-
*/
|
|
8
|
-
export const webProfile = Object.freeze({
|
|
9
|
-
platform: 'web',
|
|
10
|
-
catalogType: 'web',
|
|
11
|
-
layout: {
|
|
12
|
-
modes: new Set(['grid', 'flex', 'cssgrid']),
|
|
13
|
-
blocksMeaningful: true,
|
|
14
|
-
rootContainer: pageContainerNode,
|
|
15
|
-
orderBy: 'block-position'
|
|
16
|
-
},
|
|
17
|
-
appearance: {
|
|
18
|
-
ignoredKeys: new Set(),
|
|
19
|
-
sizeUnits: new Set(['px', '%', 'em', 'rem', 'calc'])
|
|
20
|
-
},
|
|
21
|
-
style: { freeStyle: true, hoverStyle: true, resolver: 'css' },
|
|
22
|
-
steps: {
|
|
23
|
-
registered: new Set([...ENGINE_STEP_TYPES, 'alert', 'confirm', 'redirect', 'download', 'storage', 'setcookie', 'share', 'haptic', 'location', 'notification-permission', 'notification-schedule', 'notification-cancel']),
|
|
24
|
-
aliases: STEP_ALIASES,
|
|
25
|
-
infoOnly: new Set()
|
|
26
|
-
},
|
|
27
|
-
shell: {
|
|
28
|
-
components: new Set(['navigations', 'sidebar', 'header']),
|
|
29
|
-
suggestions: {},
|
|
30
|
-
legacyShellNodes: new Set()
|
|
31
|
-
},
|
|
32
|
-
externalComponents: true,
|
|
33
|
-
state: { storageKinds: new Set() },
|
|
34
|
-
capture: {
|
|
35
|
-
target: 'studio-web',
|
|
36
|
-
devices: { desktop: { width: 1280, height: 800, dpr: 1 } },
|
|
37
|
-
defaultDevice: 'desktop'
|
|
38
|
-
}
|
|
39
|
-
});
|
|
@@ -1,510 +0,0 @@
|
|
|
1
|
-
import { resolvePatchOperations } from './outlines.js';
|
|
2
|
-
import fastJsonPatch from 'fast-json-patch';
|
|
3
|
-
function asRecords(value) {
|
|
4
|
-
return Array.isArray(value) ? value.filter((entry) => Boolean(entry) && typeof entry === 'object' && !Array.isArray(entry)) : [];
|
|
5
|
-
}
|
|
6
|
-
function hasText(value) {
|
|
7
|
-
if (typeof value === 'string')
|
|
8
|
-
return value.trim() !== '';
|
|
9
|
-
if (value && typeof value === 'object' && !Array.isArray(value))
|
|
10
|
-
return Object.values(value).some(entry => typeof entry === 'string' && entry.trim() !== '');
|
|
11
|
-
return false;
|
|
12
|
-
}
|
|
13
|
-
/** Backend `AssigneeModelConverter.Parse` ile aynı kısa biçim çözümü: `rule:arg of ref@unit:code`. */
|
|
14
|
-
export function parseProcessAssignee(shorthand) {
|
|
15
|
-
const assignee = {};
|
|
16
|
-
let text = shorthand.trim();
|
|
17
|
-
const ofIndex = text.indexOf(' of ');
|
|
18
|
-
if (ofIndex >= 0) {
|
|
19
|
-
assignee.of = text.slice(ofIndex + 4).trim();
|
|
20
|
-
text = text.slice(0, ofIndex).trim();
|
|
21
|
-
}
|
|
22
|
-
const unitIndex = text.indexOf('@unit:');
|
|
23
|
-
if (unitIndex >= 0) {
|
|
24
|
-
assignee.unit = text.slice(unitIndex + 6).trim();
|
|
25
|
-
text = text.slice(0, unitIndex).trim();
|
|
26
|
-
}
|
|
27
|
-
if (typeof assignee.of === 'string' && assignee.of.includes('@unit:')) {
|
|
28
|
-
const ofUnitIndex = assignee.of.indexOf('@unit:');
|
|
29
|
-
assignee.unit = assignee.of.slice(ofUnitIndex + 6).trim();
|
|
30
|
-
assignee.of = assignee.of.slice(0, ofUnitIndex).trim();
|
|
31
|
-
}
|
|
32
|
-
if (text === 'chain:until-authority')
|
|
33
|
-
return { ...assignee, rule: 'chain-until-authority' };
|
|
34
|
-
const separatorIndex = text.indexOf(':');
|
|
35
|
-
assignee.rule = separatorIndex < 0 ? text : text.slice(0, separatorIndex);
|
|
36
|
-
if (separatorIndex >= 0)
|
|
37
|
-
assignee.arg = text.slice(separatorIndex + 1);
|
|
38
|
-
return assignee;
|
|
39
|
-
}
|
|
40
|
-
/** Backend `AssigneeModelConverter.Read` nesne dalının aynası: `chain` + `until-authority` ve `rule` içindeki kısa biçim. */
|
|
41
|
-
function assigneeRule(value) {
|
|
42
|
-
if (typeof value === 'string')
|
|
43
|
-
return String(parseProcessAssignee(value).rule);
|
|
44
|
-
if (Array.isArray(value))
|
|
45
|
-
return 'list';
|
|
46
|
-
if (!value || typeof value !== 'object')
|
|
47
|
-
return undefined;
|
|
48
|
-
const { rule, arg } = value;
|
|
49
|
-
if (typeof rule !== 'string')
|
|
50
|
-
return undefined;
|
|
51
|
-
if (rule === 'chain' && arg === 'until-authority')
|
|
52
|
-
return 'chain-until-authority';
|
|
53
|
-
if (arg === undefined && rule.includes(':'))
|
|
54
|
-
return String(parseProcessAssignee(rule).rule);
|
|
55
|
-
return rule;
|
|
56
|
-
}
|
|
57
|
-
/**
|
|
58
|
-
* Backend `DefinitionValidator` bölge sınırının okuduğu kurallar: atamanın kendi kuralı ve `items` kalemlerinin
|
|
59
|
-
* kuralları; kalem listesi varken `list` sayılmaz (dizi kısa biçimi `list` + kalemlerdir).
|
|
60
|
-
*/
|
|
61
|
-
function assigneeRules(value) {
|
|
62
|
-
const items = Array.isArray(value) ? value : Array.isArray(value?.items) ? value.items : null;
|
|
63
|
-
return [Array.isArray(value) ? 'list' : assigneeRule(value), ...(items ?? []).map(assigneeRule)]
|
|
64
|
-
.filter((rule) => !!rule && (rule !== 'list' || items === null));
|
|
65
|
-
}
|
|
66
|
-
function walkNodes(nodes, visit, region) {
|
|
67
|
-
for (const node of asRecords(nodes)) {
|
|
68
|
-
visit(node, region);
|
|
69
|
-
if (node.type === 'region')
|
|
70
|
-
walkNodes(node.nodes, visit, node);
|
|
71
|
-
}
|
|
72
|
-
}
|
|
73
|
-
/**
|
|
74
|
-
* Kısa biçimleri platformun sakladığı tam biçime çevirir; backend de aynısını yapar ama gateway
|
|
75
|
-
* önceden çevirince writeDiff sahte "düştü" satırı üretmez (M0 §3 `gatewayNormalized`).
|
|
76
|
-
*/
|
|
77
|
-
export function normalizeProcessDefinition(definition) {
|
|
78
|
-
const fixes = [];
|
|
79
|
-
const plainTextKeys = ['name', 'description'];
|
|
80
|
-
const localizedKeys = ['caution'];
|
|
81
|
-
const normalizeEdges = (owner, label) => {
|
|
82
|
-
if (!Array.isArray(owner.edges))
|
|
83
|
-
return;
|
|
84
|
-
owner.edges = owner.edges.map((edge, index) => {
|
|
85
|
-
if (!Array.isArray(edge))
|
|
86
|
-
return edge;
|
|
87
|
-
fixes.push(`${label}edges[${index}]: [from, to] kısa biçimi { from, to } nesnesine çevrildi`);
|
|
88
|
-
return { from: edge[0], to: edge[1] };
|
|
89
|
-
});
|
|
90
|
-
};
|
|
91
|
-
const normalizeLocalized = (owner, key, label) => {
|
|
92
|
-
if (typeof owner[key] !== 'string')
|
|
93
|
-
return;
|
|
94
|
-
owner[key] = { default: owner[key] };
|
|
95
|
-
fixes.push(`${label}${key}: düz metin { default } çok dilli biçimine çevrildi`);
|
|
96
|
-
};
|
|
97
|
-
const normalizePlainText = (owner, key, label) => {
|
|
98
|
-
const value = owner[key];
|
|
99
|
-
if (!value || typeof value !== 'object' || Array.isArray(value))
|
|
100
|
-
return;
|
|
101
|
-
const texts = value;
|
|
102
|
-
const text = [texts.default, ...Object.values(texts)].find(candidate => typeof candidate === 'string' && candidate.trim() !== '');
|
|
103
|
-
if (typeof text !== 'string')
|
|
104
|
-
return;
|
|
105
|
-
owner[key] = text;
|
|
106
|
-
fixes.push(`${label}${key}: çok dilli nesne düz metne çevrildi (süreç ve düğüm adı/açıklaması tek dillidir)`);
|
|
107
|
-
};
|
|
108
|
-
const normalizeAssignee = (owner, key, label) => {
|
|
109
|
-
if (typeof owner[key] !== 'string')
|
|
110
|
-
return;
|
|
111
|
-
owner[key] = parseProcessAssignee(owner[key]);
|
|
112
|
-
fixes.push(`${label}${key}: kısa biçim atama nesnesine çözüldü (${JSON.stringify(owner[key])})`);
|
|
113
|
-
};
|
|
114
|
-
const normalizeSla = (owner, label) => {
|
|
115
|
-
if (typeof owner.sla === 'string') {
|
|
116
|
-
owner.sla = [{ duration: owner.sla }];
|
|
117
|
-
fixes.push(`${label}sla: "${String(owner.sla[0]?.duration)}" tek sayaç kısa biçimi [{ duration }] listesine çevrildi`);
|
|
118
|
-
}
|
|
119
|
-
else if (owner.sla && typeof owner.sla === 'object' && !Array.isArray(owner.sla)) {
|
|
120
|
-
owner.sla = [owner.sla];
|
|
121
|
-
fixes.push(`${label}sla: tek sayaç nesnesi [{ … }] listesine çevrildi`);
|
|
122
|
-
}
|
|
123
|
-
};
|
|
124
|
-
const normalizeTemplate = (owner, label) => {
|
|
125
|
-
const template = owner.template;
|
|
126
|
-
if (!template || typeof template !== 'object' || Array.isArray(template))
|
|
127
|
-
return;
|
|
128
|
-
normalizeLocalized(template, 'subject', `${label}template.`);
|
|
129
|
-
normalizeLocalized(template, 'body', `${label}template.`);
|
|
130
|
-
};
|
|
131
|
-
const normalizeHandlers = (owner, label) => {
|
|
132
|
-
asRecords(owner.on).forEach((handler, index) => {
|
|
133
|
-
const target = handler.handler;
|
|
134
|
-
if (!target || typeof target !== 'object' || Array.isArray(target))
|
|
135
|
-
return;
|
|
136
|
-
const handlerLabel = `${label}on[${index}].handler.`;
|
|
137
|
-
normalizeAssignee(target, 'to', handlerLabel);
|
|
138
|
-
normalizeAssignee(target, 'assignee', handlerLabel);
|
|
139
|
-
normalizeTemplate(target, handlerLabel);
|
|
140
|
-
});
|
|
141
|
-
};
|
|
142
|
-
for (const key of plainTextKeys) {
|
|
143
|
-
normalizePlainText(definition, key, '');
|
|
144
|
-
}
|
|
145
|
-
for (const key of localizedKeys) {
|
|
146
|
-
normalizeLocalized(definition, key, '');
|
|
147
|
-
}
|
|
148
|
-
normalizeSla(definition, '');
|
|
149
|
-
normalizeHandlers(definition, '');
|
|
150
|
-
normalizeEdges(definition, '');
|
|
151
|
-
walkNodes(definition.nodes, node => {
|
|
152
|
-
const label = `nodes[id=${String(node.id)}]`;
|
|
153
|
-
for (const key of plainTextKeys) {
|
|
154
|
-
normalizePlainText(node, key, `${label}.`);
|
|
155
|
-
}
|
|
156
|
-
for (const key of localizedKeys) {
|
|
157
|
-
normalizeLocalized(node, key, `${label}.`);
|
|
158
|
-
}
|
|
159
|
-
for (const key of ['assignee', 'to']) {
|
|
160
|
-
normalizeAssignee(node, key, `${label}.`);
|
|
161
|
-
}
|
|
162
|
-
normalizeSla(node, `${label}.`);
|
|
163
|
-
normalizeTemplate(node, `${label}.`);
|
|
164
|
-
normalizeHandlers(node, `${label}.`);
|
|
165
|
-
asRecords(node.actions).forEach((action, index) => normalizeLocalized(action, 'label', `${label}.actions[${index}].`));
|
|
166
|
-
if (node.type === 'region')
|
|
167
|
-
normalizeEdges(node, `${label}.`);
|
|
168
|
-
});
|
|
169
|
-
return fixes;
|
|
170
|
-
}
|
|
171
|
-
const commonFields = {
|
|
172
|
-
id: "kebab-case, tanımda benzersiz; kenarların anahtarıdır, ifadede okunmaz — user-task/approval sonucu snake_case resultKey ile noktayla okunur (vars.tasks.ik_kaydi.action), vars.tasks['ik-kaydi'] değil",
|
|
173
|
-
type: 'düğüm türü',
|
|
174
|
-
name: 'çok dilli ad ({ tr, en } ya da düz metin)',
|
|
175
|
-
description: 'ZORUNLU (BPM-002): düğümün ne işe yaradığı, 1-2 cümle',
|
|
176
|
-
caution: 'opsiyonel tuzak notu',
|
|
177
|
-
analysis: "ZORUNLU (BPM-023): { what, data, why, example } — her biri { en, tr }; iş diliyle 1-3 cümle: bu adımda ne yapılır, hangi değerler kullanılır, neden gerekir, gerçek hayattan küçük bir örnek (platform_guide('bpm', section: 'Analiz dokümanı'))",
|
|
178
|
-
editable: 'locked | params | open (open yalnız region, BPM-003)',
|
|
179
|
-
writeBack: '{ alan: "<Jint>" } — düğüm çıkışında iş satırına yazılır'
|
|
180
|
-
};
|
|
181
|
-
export const processNodeSchemas = [
|
|
182
|
-
{ type: 'start', purpose: 'Sürecin giriş noktası; tanımda üst düzeyde tam bir tane', required: ['id', 'type', 'description'], fields: { ...commonFields, trigger: 'row-event | manual | api | schedule | sub-process | message | signal', entity: 'row-event için entity slug', operation: 'create | update | delete', condition: 'Jint (row, previousRow)' }, example: { id: 'baslangic', type: 'start', description: 'Satır oluşunca başlar.', trigger: 'row-event', entity: 'gorevler', operation: 'create' } },
|
|
183
|
-
{ type: 'user-task', purpose: 'İnsan görevi; kutuya düşer', required: ['id', 'type', 'description', 'assignee'], fields: { ...commonFields, resultKey: 'sonucun adı: motor görev sonucunu { outcome, action, completedBy, payload } olarak vars.tasks.<resultKey> altına yazar (yoksa id snake_case yazılır: ik-kaydi → vars.tasks.ik_kaydi; eski ifadeler için kebab-case id altına da yazılır). Her user-task düğümüne snake_case verin (teslimat_kontrol) — kebab-case/slug değil: tire kabul edilmez, aynı tipte benzersizdir. Geçit vars.tasks.teslimat_kontrol.action === "sorunlu" diye okur; yazılmamış bir ad (vars.tasks.teslimatKontrol) undefined döner', form: '{ dtoId } görev formunun DTO kimliği (dto_list) — { pageId } sayfa formu, { formKey } dış form anahtarı. SLUG KABUL EDİLMEZ: dtoSlug yazmak sessizce düşer (BPM-021)', assignee: 'atama sözlüğü (ör. "subject-field:atanan_id", "role:finans")', mode: 'claim (varsayılan) | individual', join: 'all | any | force-complete (individual)', actions: '[{ id, label, completes, emits, evidence }]', completeOn: '{ trigger: row-event, operation, condition }', sla: '"P2D" ya da [{ name, duration, onBreach }]', requestInfo: 'bool', allowReassign: 'bool', external: 'bool — dış kullanıcı görevi', revisionBound: 'bool — bölge yeniden başlarsa eski görev superseded', undoWindow: 'ISO süre', assessment: '{ questionBank { entity, where, questionField, optionsField, answerField }, count, passScore (1..count, BPM-020), attempts, random, completeAction } — okuma kanıtından önce sınav' }, example: { id: 'gorevi-yap', type: 'user-task', resultKey: 'gorevi_yap', description: 'Atanan görevi yapar.', assignee: 'subject-field:atanan_id', sla: 'P3D' } },
|
|
184
|
-
{ type: 'approval', purpose: 'Onay görevi ve politika kararı', required: ['id', 'type', 'description', 'assignee'], fields: { ...commonFields, resultKey: 'kararın adı: motor sonucu vars.approvals.<resultKey> altına yazar (yoksa id snake_case yazılır: yonetici-onayi → vars.approvals.yonetici_onayi). Her approval düğümüne snake_case verin (yonetici_onayi) — kebab-case/slug değil: tire kabul edilmez, aynı tipte benzersizdir', form: '{ dtoId } görev formunun DTO kimliği (dto_list) — { pageId } sayfa formu, { formKey } dış form anahtarı. SLUG KABUL EDİLMEZ: dtoSlug yazmak sessizce düşer (BPM-021)', assignee: 'atama sözlüğü', policy: 'sequential | all | any | quorum(n) | majority | weighted(0.6)', rejectPolicy: 'any-reject | quorum(n) | majority', onReject: 'end | back-to:<düğüm> | restart', commentRequired: 'bool ya da Jint ("outcome === \'rejected\'")', selfApproval: 'escalate | skip | allow', weights: '{ "role:x": 2 }', veto: '["role:yk-baskani"]', abstain: 'bool', countAs: 'vars yolu' }, example: { id: 'kontrol-onay', type: 'approval', resultKey: 'kontrol_onay', description: 'Birim yöneticisi onaylar.', assignee: 'unit-manager of subject-field:atanan_id', policy: 'any', onReject: 'back-to:iade' } },
|
|
185
|
-
{ type: 'service', purpose: 'Workflow, bağlayıcı, dış motor ya da AI çağrısı (yan etkili iş)', required: ['id', 'type', 'description'], fields: { ...commonFields, workflowId: 'workflow GUID — workflowId | workflowSlug | connector | externalEngine | ai alanlarından biri zorunlu', connector: '{ name, operation, credentialRef, input }', externalEngine: '{ engine: camunda8 | camunda7 | flowable, processKey, credentialRef, input }', input: '{ ad: "<Jint>" }', output: 'vars yolu ("vars.seviye")', retry: '{ count, backoff, delay }', runAs: 'system ise caution zorunlu (BPM-005)', ai: '{ operation: summarize | classify | extract | suggest-assignee | suggest-decision, labels, schema, files, input, instructions, autonomy: suggest | act-in-adhoc (BPM-019) } — öneri output yoluna yazılır, karar vermez' }, example: { id: 'seviye-hesapla', type: 'service', description: 'Tutardan onay seviyesini hesaplar.', workflowId: '<guid>', input: { tutar: 'row.tutar' }, output: 'vars.seviye' } },
|
|
186
|
-
{ type: 'notify', purpose: 'Bildirim (kutu, e-posta outbox)', required: ['id', 'type', 'description', 'to'], fields: { ...commonFields, to: 'atama sözlüğü', channels: '["inbox", "email"]', template: '{ subject, body } — {{row.x}} yer tutucuları', ackRequired: 'bool' }, example: { id: 'bildir', type: 'notify', description: 'Yöneticiye bildirim.', to: 'unit-manager of subject-field:atanan_id', channels: ['inbox'] } },
|
|
187
|
-
{ type: 'gateway', purpose: 'Dallanma ve birleşme', required: ['id', 'type', 'description', 'kind'], fields: { ...commonFields, kind: 'exclusive | parallel | inclusive (koşulu tutan her dal; birleşmede hâlâ ulaşabilecek dallar beklenir) | event-based (yalnız wait/timer düğümlerine gider; ilk gelen olay kazanır, diğer bekleyişler iptal)', default: 'exclusive/inclusive için hiçbir koşul tutmazsa gidilen hedef' }, example: { id: 'butce-karari', type: 'gateway', kind: 'exclusive', description: 'Bütçe uygun mu?', default: 'reddedildi' } },
|
|
188
|
-
{ type: 'timer', purpose: 'Süre ya da tarih bekleme', required: ['id', 'type', 'description'], fields: { ...commonFields, duration: 'ISO süre', until: 'Jint tarih ifadesi', calendar: 'iş takvimi slug' }, example: { id: 'bekle', type: 'timer', description: 'Bir gün bekler.', duration: 'P1D' } },
|
|
189
|
-
{ type: 'wait', purpose: 'Mesaj ya da sinyal bekleme', required: ['id', 'type', 'description'], fields: { ...commonFields, message: '{ name, correlationKey } — tamponda bekleyen eşleşen mesaj varsa hemen tüketilir', output: 'mesaj gövdesinin yazılacağı vars yolu', signal: '<ad> ya da signal:<ad> — POST bpm/signals ile gelir', link: 'aynı kapsamdaki send { link } düğümünün sıçradığı yakalama noktası (gelen kenarı olmaz); studio bu kipte adımı "Bağlantı noktası" diye adlandırır (BPMN link catch event)' }, example: { id: 'odeme-bekle', type: 'wait', description: 'Ödeme mesajını bekler.', message: { name: 'odeme-alindi', correlationKey: 'vars.siparisNo' } } },
|
|
190
|
-
{ type: 'send', purpose: 'Mesaj atar (kuyruktan korelasyon) ya da link ile aynı kapsamdaki yakalamaya sıçrar. BPMN intermediate throw event; bildirim (notify) DEĞİLDİR, e-posta göndermez (e-posta notify channels email işidir). Studio adı kipine göre: message → "Mesaj gönder", link → "Bağlantıya atla"; palette kutucuğu "Mesaj". Kullanıcıya bu adlarla anlat, "Gönder adımı" deme', required: ['id', 'type', 'description'], fields: { ...commonFields, message: '{ name, correlationKey: "<Jint>" } — link yoksa zorunlu', input: '{ ad: "<Jint>" } — mesaj gövdesi', timeout: 'ISO süre — alıcı yoksa mesajın tamponda bekleyeceği süre (en çok P1D)', link: 'aynı kapsamda wait { link } düğümüne sıçrar' }, example: { id: 'odeme-bildir', type: 'send', description: 'Ödeme mesajını sipariş sürecine atar.', message: { name: 'odeme-alindi', correlationKey: 'vars.siparisNo' }, input: { tutar: 'row.tutar' }, timeout: 'PT10M' } },
|
|
191
|
-
{ type: 'manual', purpose: 'Sistem dışı iş; görev satırı açmaz, yönetici atlatana (process_instance_update skip) kadar bekler', required: ['id', 'type', 'description'], fields: { ...commonFields, sla: 'bekleme SLA süresi; skipOnDeadline ile süre dolunca geçer', skipOnDeadline: 'bool' }, example: { id: 'arsivle', type: 'manual', description: 'Kâğıt dosyayı arşive kaldır.' } },
|
|
192
|
-
{ type: 'script', purpose: 'Jint betiği; vars değiştirir, dosya/HTTP/CLR erişimi yok (2 sn, 10 000 deyim)', required: ['id', 'type', 'description', 'code'], fields: { ...commonFields, code: 'Jint kodu — vars, row, previousRow, initiator okunur; vars.x = … yazılır', output: 'son ifadenin değerinin yazılacağı vars yolu' }, example: { id: 'toplam-hesapla', type: 'script', description: 'Kalemleri toplar.', code: 'vars.toplam = vars.a + vars.b; vars.toplam', output: 'vars.sonuc' } },
|
|
193
|
-
{ type: 'sub-process', purpose: 'Alt süreç (farklı iş nesnesi, forEach partili)', required: ['id', 'type', 'description', 'processSlug'], fields: { ...commonFields, processSlug: 'çocuk tanımın slug\'ı', subject: '{ rowId: "<Jint>" }', input: '{ ad: "<Jint>" }', output: 'vars yolu', binding: 'latest (varsayılan) | deployed | v:<n>', forEach: '{ entity, where: "item.x === …", batch }' }, example: { id: 'tedarikci-degerlendir', type: 'sub-process', description: 'Tedarikçi değerlendirmesi.', processSlug: 'tedarikci-degerlendirme', subject: { rowId: 'vars.tedarikciId' } } },
|
|
194
|
-
{ type: 'case', purpose: 'Yayındaki CMMN vakasını başlatır ve beklemez (akış sürer); vaka kimliği output yoluna, defterde case-started', required: ['id', 'type', 'description', 'caseSlug'], fields: { ...commonFields, caseSlug: "yayındaki vaka tanımının slug'ı (yoksa 422 CaseUnknown; platform_guide('case'))", input: '{ vakaDosyasıÖğesi: "<Jint>" } — vaka dosyası başlangıç değerleri', output: 'vaka kimliğinin yazılacağı vars yolu' }, example: { id: 'sikayet-ac', type: 'case', description: 'Şikâyet vakasını açar.', caseSlug: 'musteri-sikayeti', input: { musteriCevabi: "'yok'" }, output: 'vars.sikayetVakasi' } },
|
|
195
|
-
{ type: 'region', purpose: 'Uyarlanabilir blok ve olay kapsamı', required: ['id', 'type', 'description', 'nodes'], fields: { ...commonFields, nodes: 'iç düğümler (start yok; girişler gelen kenarı olmayanlar)', edges: 'iç kenarlar', limits: '{ maxSteps, nodeTypes, assigneeRules } (BPM-004)', on: '[{ event, interrupt, handler: { type: sub-process | notify | append-task }, afterHandlers, restartWhen, restartIf, restartScope, cancelStale }]' }, example: { id: 'kontrol-bolgesi', type: 'region', description: 'Onay zinciri.', editable: 'open', limits: { maxSteps: 3, assigneeRules: ['unit-manager', 'role'] }, nodes: [], edges: [] } },
|
|
196
|
-
{ type: 'end', purpose: 'Bitiş; bölge içindeyse yalnız o kolu kapatır', required: ['id', 'type', 'description'], fields: { ...commonFields, outcome: 'sonuç adı', kind: 'terminate (bütün dallar iptal, onCancel koşar, sonuç "terminated"; bölgede yalnız o bölge) | error (code ile; en yakın bölgenin on[] "error:<code>" + afterHandlers exit-region işleyicisine, yoksa süreç "error:<code>" sonucuyla biter — üst sürecin sub-process düğümü aynı işleyiciyle yakalar, yoksa faulted)', code: 'kind error için hata kodu', reopenWindow: 'ISO süre — process_instance_update { operation: reopen }' }, example: { id: 'onaylandi', type: 'end', description: 'Onaylandı.', outcome: 'approved', writeBack: { durum: "'onaylandi'" } } }
|
|
197
|
-
];
|
|
198
|
-
export function processPolicyRules() {
|
|
199
|
-
return [
|
|
200
|
-
{
|
|
201
|
-
id: 'BPM-002',
|
|
202
|
-
module: 'process',
|
|
203
|
-
appliesTo: ['create', 'update'],
|
|
204
|
-
sourceRef: "DefinitionValidator description zorunluluğu; WF-024 emsali. Tasarımcı ve kutu 'bu adım ne işe yarar' metnini buradan okur",
|
|
205
|
-
check: definition => {
|
|
206
|
-
const findings = [];
|
|
207
|
-
if (!hasText(definition.description)) {
|
|
208
|
-
findings.push({ ruleId: 'BPM-002', severity: 'block', message: "Tanımın 'description' alanı boş. Sürecin ne işe yaradığını 1-2 cümleyle yazın.", guide: 'ngapps://guides/bpm' });
|
|
209
|
-
}
|
|
210
|
-
const undescribed = [];
|
|
211
|
-
walkNodes(definition.nodes, node => {
|
|
212
|
-
if (!hasText(node.description))
|
|
213
|
-
undescribed.push(String(node.id ?? '?'));
|
|
214
|
-
});
|
|
215
|
-
if (undescribed.length > 0) {
|
|
216
|
-
findings.push({ ruleId: 'BPM-002', severity: 'block', message: `Açıklamasız düğüm: ${undescribed.join(', ')}. Her düğüm 'description' taşır — kutuda ve tasarımcıda görevin ne istediğini anlatır.`, guide: 'ngapps://guides/bpm' });
|
|
217
|
-
}
|
|
218
|
-
return findings;
|
|
219
|
-
}
|
|
220
|
-
},
|
|
221
|
-
{
|
|
222
|
-
id: 'BPM-023',
|
|
223
|
-
module: 'process',
|
|
224
|
-
appliesTo: ['create', 'update'],
|
|
225
|
-
sourceRef: "Studio Analiz dokümanı sekmesi (zeyrek-backend/docs/adr/0007): AI'ın yazdığı süreçte her adım 'ne / hangi değer / neden' sorusunu iki dilde cevaplar; kullanıcının çizdiği süreç şablonla dolar",
|
|
226
|
-
check: definition => {
|
|
227
|
-
const missing = [];
|
|
228
|
-
const requireTexts = (owner, keys, label) => {
|
|
229
|
-
const analysis = owner && typeof owner === 'object' && !Array.isArray(owner) ? owner : {};
|
|
230
|
-
for (const key of keys) {
|
|
231
|
-
const texts = analysis[key] && typeof analysis[key] === 'object' && !Array.isArray(analysis[key]) ? analysis[key] : {};
|
|
232
|
-
for (const language of ['en', 'tr']) {
|
|
233
|
-
if (!hasText(texts[language]))
|
|
234
|
-
missing.push(`${label}analysis.${key}.${language}`);
|
|
235
|
-
}
|
|
236
|
-
}
|
|
237
|
-
};
|
|
238
|
-
requireTexts(definition.analysis, ['purpose', 'summary'], '');
|
|
239
|
-
walkNodes(definition.nodes, node => requireTexts(node.analysis, ['what', 'data', 'why', 'example'], `nodes[id=${String(node.id ?? '?')}].`));
|
|
240
|
-
return missing.length === 0
|
|
241
|
-
? []
|
|
242
|
-
: [{ ruleId: 'BPM-023', severity: 'block', message: `Analiz dokümanı eksik: ${missing.slice(0, 12).join(', ')}${missing.length > 12 ? ` ve ${missing.length - 12} alan daha` : ''}. Süreç 'analysis: { purpose, summary }', her düğüm 'analysis: { what, data, why, example }' taşır; her metin { en, tr } — iş diliyle, teknik ad yazmadan; example bu adımın gerçek hayattan küçük bir vakası. Studio'nun Analiz dokümanı sekmesi bunları gösterir.`, guide: 'ngapps://guides/bpm' }];
|
|
243
|
-
}
|
|
244
|
-
},
|
|
245
|
-
{
|
|
246
|
-
id: 'BPM-003',
|
|
247
|
-
module: 'process',
|
|
248
|
-
appliesTo: ['create', 'update'],
|
|
249
|
-
sourceRef: 'DefinitionValidator: editable open yalnız region düğümünde geçerli',
|
|
250
|
-
check: definition => {
|
|
251
|
-
const offenders = [];
|
|
252
|
-
walkNodes(definition.nodes, node => {
|
|
253
|
-
if (node.editable === 'open' && node.type !== 'region')
|
|
254
|
-
offenders.push(String(node.id ?? '?'));
|
|
255
|
-
});
|
|
256
|
-
return offenders.length === 0
|
|
257
|
-
? []
|
|
258
|
-
: [{ ruleId: 'BPM-003', severity: 'block', message: `'editable: open' yalnız bölgede geçerli; bölge dışı düğüm: ${offenders.join(', ')}. Düğümü bir 'region' içine alın ya da 'params' kullanın.`, guide: 'ngapps://guides/bpm' }];
|
|
259
|
-
}
|
|
260
|
-
},
|
|
261
|
-
{
|
|
262
|
-
id: 'BPM-004',
|
|
263
|
-
module: 'process',
|
|
264
|
-
appliesTo: ['create', 'update'],
|
|
265
|
-
sourceRef: 'DefinitionValidator RuleNotAllowed: bölgedeki atama kuralı limits.assigneeRules kümesinden seçilir',
|
|
266
|
-
check: definition => {
|
|
267
|
-
const offenders = [];
|
|
268
|
-
walkNodes(definition.nodes, (node, region) => {
|
|
269
|
-
const allowedRules = asRecords([region?.limits])[0]?.assigneeRules;
|
|
270
|
-
if (!Array.isArray(allowedRules) || allowedRules.length === 0)
|
|
271
|
-
return;
|
|
272
|
-
for (const rule of [...assigneeRules(node.assignee), ...assigneeRules(node.to)]) {
|
|
273
|
-
if (!allowedRules.includes(rule))
|
|
274
|
-
offenders.push(`${String(node.id)} (${rule})`);
|
|
275
|
-
}
|
|
276
|
-
});
|
|
277
|
-
return offenders.length === 0
|
|
278
|
-
? []
|
|
279
|
-
: [{ ruleId: 'BPM-004', severity: 'block', message: `Bölgenin limits.assigneeRules kümesi dışında atama kuralı: ${offenders.join(', ')}. Platform 422 RuleNotAllowed döner.`, guide: 'ngapps://guides/bpm' }];
|
|
280
|
-
}
|
|
281
|
-
},
|
|
282
|
-
{
|
|
283
|
-
id: 'BPM-005',
|
|
284
|
-
module: 'process',
|
|
285
|
-
appliesTo: ['create', 'update'],
|
|
286
|
-
sourceRef: "ACC-004 emsali: runAs system satır kapsamını atlar; gerekçe düğümün caution'ında durur",
|
|
287
|
-
check: definition => {
|
|
288
|
-
const offenders = [];
|
|
289
|
-
walkNodes(definition.nodes, node => {
|
|
290
|
-
if (node.type === 'service' && node.runAs === 'system' && !hasText(node.caution))
|
|
291
|
-
offenders.push(String(node.id ?? '?'));
|
|
292
|
-
});
|
|
293
|
-
return offenders.length === 0
|
|
294
|
-
? []
|
|
295
|
-
: [{ ruleId: 'BPM-005', severity: 'warn', message: `'runAs: system' gerekçesiz: ${offenders.join(', ')}. Satır kapsamı atlanır; nedenini düğümün 'caution' alanına yazın.`, guide: 'ngapps://guides/bpm' }];
|
|
296
|
-
}
|
|
297
|
-
},
|
|
298
|
-
{
|
|
299
|
-
id: 'BPM-019',
|
|
300
|
-
module: 'process',
|
|
301
|
-
appliesTo: ['create', 'update'],
|
|
302
|
-
sourceRef: "DefinitionValidator: ai.autonomy 'act-in-adhoc' yalnız süreç adHoc: blocking | non-blocking iken geçerli (b-105); motor ad-hoc iş yürütmediği için öneriden öteye geçmez",
|
|
303
|
-
check: definition => {
|
|
304
|
-
const offenders = [];
|
|
305
|
-
if (definition.adHoc === 'blocking' || definition.adHoc === 'non-blocking')
|
|
306
|
-
return [];
|
|
307
|
-
walkNodes(definition.nodes, node => {
|
|
308
|
-
if (asRecords([node.ai])[0]?.autonomy === 'act-in-adhoc')
|
|
309
|
-
offenders.push(String(node.id ?? '?'));
|
|
310
|
-
});
|
|
311
|
-
return offenders.length === 0
|
|
312
|
-
? []
|
|
313
|
-
: [{ ruleId: 'BPM-019', severity: 'block', message: `'ai.autonomy: act-in-adhoc' ad-hoc işe izin vermeyen süreçte: ${offenders.join(', ')}. Tanıma adHoc: blocking | non-blocking yazın ya da autonomy'yi 'suggest' bırakın; platform 422 DefinitionInvalid döner.`, guide: 'ngapps://guides/bpm' }];
|
|
314
|
-
}
|
|
315
|
-
},
|
|
316
|
-
{
|
|
317
|
-
id: 'BPM-020',
|
|
318
|
-
module: 'process',
|
|
319
|
-
appliesTo: ['create', 'update'],
|
|
320
|
-
sourceRef: 'DefinitionValidator: assessment.passScore 1 ile soru sayısı (count) arasında, count ≥ 1 (b-103)',
|
|
321
|
-
check: definition => {
|
|
322
|
-
const offenders = [];
|
|
323
|
-
walkNodes(definition.nodes, node => {
|
|
324
|
-
const assessment = asRecords([node.assessment])[0];
|
|
325
|
-
if (!assessment)
|
|
326
|
-
return;
|
|
327
|
-
const count = typeof assessment.count === 'number' ? assessment.count : undefined;
|
|
328
|
-
const passScore = typeof assessment.passScore === 'number' ? assessment.passScore : undefined;
|
|
329
|
-
if ((passScore !== undefined && passScore < 1) || (count !== undefined && count < 1) || (passScore !== undefined && count !== undefined && passScore > count)) {
|
|
330
|
-
offenders.push(`${String(node.id ?? '?')} (passScore ${String(passScore)}, count ${String(count)})`);
|
|
331
|
-
}
|
|
332
|
-
});
|
|
333
|
-
return offenders.length === 0
|
|
334
|
-
? []
|
|
335
|
-
: [{ ruleId: 'BPM-020', severity: 'block', message: `Değerlendirmede ulaşılamaz geçme puanı: ${offenders.join(', ')}. passScore 1 ile count arasında olmalı; aksi hâlde kimse okuma kanıtını veremez ve platform 422 DefinitionInvalid döner.`, guide: 'ngapps://guides/bpm' }];
|
|
336
|
-
}
|
|
337
|
-
},
|
|
338
|
-
{
|
|
339
|
-
id: 'BPM-021',
|
|
340
|
-
module: 'process',
|
|
341
|
-
appliesTo: ['create', 'update'],
|
|
342
|
-
sourceRef: 'FormModel yalnız pageId · dtoId · formKey taşır; başka anahtar komut şemasında yok ve platform onu hata vermeden yok sayar (ölçüldü 2026-09-21, S-3 koşusu I-2)',
|
|
343
|
-
check: definition => {
|
|
344
|
-
const offenders = [];
|
|
345
|
-
walkNodes(definition.nodes, node => {
|
|
346
|
-
const form = asRecords([node.form])[0];
|
|
347
|
-
if (!form)
|
|
348
|
-
return;
|
|
349
|
-
const unknownKeys = Object.keys(form).filter(key => !['pageId', 'dtoId', 'formKey'].includes(key));
|
|
350
|
-
if (unknownKeys.length > 0)
|
|
351
|
-
offenders.push(`${String(node.id ?? '?')} (${unknownKeys.join(', ')})`);
|
|
352
|
-
});
|
|
353
|
-
return offenders.length === 0
|
|
354
|
-
? []
|
|
355
|
-
: [{ ruleId: 'BPM-021', severity: 'block', message: `Görev formunda tanınmayan anahtar: ${offenders.join(', ')}. form yalnız { dtoId } (dto_list kimliği), { pageId } ya da { formKey } taşır — dtoSlug gibi bir anahtar platformda SESSİZCE düşer ve görev formsuz yayınlanır.`, guide: 'ngapps://guides/bpm' }];
|
|
356
|
-
}
|
|
357
|
-
}
|
|
358
|
-
];
|
|
359
|
-
}
|
|
360
|
-
const lockedOverrideFields = new Set(['id', 'type', 'editable', 'editableParams', 'limits']);
|
|
361
|
-
function recordAt(value, index) {
|
|
362
|
-
const entry = Array.isArray(value) && Number.isInteger(index) ? value[index] : undefined;
|
|
363
|
-
return entry && typeof entry === 'object' && !Array.isArray(entry) ? entry : undefined;
|
|
364
|
-
}
|
|
365
|
-
function hasNoDegree(node) {
|
|
366
|
-
return node.editable === undefined || node.editable === null;
|
|
367
|
-
}
|
|
368
|
-
/** Backend `OverrideMergeService.FindLock` aynası: seçicileri çözülmüş (sayısal) yolun kilit kodu, izinliyse undefined. */
|
|
369
|
-
function overrideLockCode(document, operation) {
|
|
370
|
-
const op = String(operation.op ?? '');
|
|
371
|
-
if (op === 'move' || op === 'copy')
|
|
372
|
-
return 'RegionLocked';
|
|
373
|
-
const segments = String(operation.path ?? '').slice(1).split('/');
|
|
374
|
-
const step = asRecords([operation.value])[0];
|
|
375
|
-
const isPlainStep = !!step
|
|
376
|
-
&& !('editable' in step) && !('editableParams' in step) && !('limits' in step)
|
|
377
|
-
&& (step.nodes === undefined || step.nodes === null)
|
|
378
|
-
&& (step.edges === undefined || step.edges === null)
|
|
379
|
-
&& step.type !== 'region';
|
|
380
|
-
if (segments[0] === 'params') {
|
|
381
|
-
const param = segments.length > 2 ? asRecords([document.params?.[segments[1]]])[0] : undefined;
|
|
382
|
-
return (param?.editable === 'params' || param?.editable === 'open') && segments[2] === 'default' ? undefined : 'ParamNotEditable';
|
|
383
|
-
}
|
|
384
|
-
if (segments[0] !== 'nodes' || segments.length < 3 || !/^\d+$/.test(segments[1]))
|
|
385
|
-
return 'RegionLocked';
|
|
386
|
-
let node = recordAt(document.nodes, Number(segments[1]));
|
|
387
|
-
let region;
|
|
388
|
-
let index = 2;
|
|
389
|
-
while (node?.type === 'region' && (segments[index] === 'nodes' || segments[index] === 'edges')) {
|
|
390
|
-
const isRegionOpen = node.editable === 'open';
|
|
391
|
-
if (segments[index] === 'edges')
|
|
392
|
-
return isRegionOpen ? undefined : 'RegionLocked';
|
|
393
|
-
if (index + 1 >= segments.length)
|
|
394
|
-
return 'RegionLocked';
|
|
395
|
-
if (segments[index + 1] === '-')
|
|
396
|
-
return isRegionOpen && op === 'add' && isPlainStep ? undefined : 'RegionLocked';
|
|
397
|
-
const child = /^\d+$/.test(segments[index + 1]) ? recordAt(node.nodes, Number(segments[index + 1])) : undefined;
|
|
398
|
-
if (!child)
|
|
399
|
-
return 'RegionLocked';
|
|
400
|
-
if (index + 2 >= segments.length) {
|
|
401
|
-
const isSameStep = isPlainStep && step.id === child.id && step.type === child.type;
|
|
402
|
-
return isRegionOpen && (hasNoDegree(child) || child.editable === 'open') && (op === 'remove' || isSameStep) ? undefined : 'RegionLocked';
|
|
403
|
-
}
|
|
404
|
-
region = node;
|
|
405
|
-
node = child;
|
|
406
|
-
index += 2;
|
|
407
|
-
}
|
|
408
|
-
if (!node || lockedOverrideFields.has(segments[index]) || segments[index] === 'nodes' || segments[index] === 'edges')
|
|
409
|
-
return 'RegionLocked';
|
|
410
|
-
if (node.editable === 'params')
|
|
411
|
-
return (Array.isArray(node.editableParams) ? node.editableParams : []).includes(segments[index]) ? undefined : 'ParamNotEditable';
|
|
412
|
-
return hasNoDegree(node) && region?.editable === 'open' ? undefined : 'RegionLocked';
|
|
413
|
-
}
|
|
414
|
-
/**
|
|
415
|
-
* Uyarlama patch'inin istek gitmeden denetimi (g-31): BPM-010 kilitli yol (backend `FindLock` aynası),
|
|
416
|
-
* BPM-011 bölgenin izin vermediği atama kuralı, BPM-012 bölge adım sayısı ve düğüm türü sınırı.
|
|
417
|
-
* Patch temel (aktif sürüm) modeline sırayla uygulanır; yerelde çözülemeyen yol denetimi durdurur ve
|
|
418
|
-
* notlara yazılır — karar platform 422'sine kalır.
|
|
419
|
-
*/
|
|
420
|
-
export function overridePatchFindings(baseModel, patch) {
|
|
421
|
-
const document = structuredClone(baseModel);
|
|
422
|
-
const findings = [];
|
|
423
|
-
const notes = [];
|
|
424
|
-
for (const operation of patch) {
|
|
425
|
-
const originalPath = typeof operation.path === 'string' ? operation.path : '';
|
|
426
|
-
let resolved;
|
|
427
|
-
try {
|
|
428
|
-
resolved = resolvePatchOperations(document, [structuredClone(operation)], 'process_override_get').operations[0];
|
|
429
|
-
}
|
|
430
|
-
catch (error) {
|
|
431
|
-
notes.push(`uyarlama denetimi '${originalPath}' yolunda durdu (yol yerelde çözülemedi: ${error instanceof Error ? error.message : String(error)}); platform doğrulaması yine çalışır`);
|
|
432
|
-
return { findings, notes };
|
|
433
|
-
}
|
|
434
|
-
const lockCode = overrideLockCode(document, resolved);
|
|
435
|
-
if (lockCode) {
|
|
436
|
-
findings.push({ ruleId: 'BPM-010', severity: 'block', message: `Uyarlama kilitli yola dokunuyor: '${originalPath}' (${lockCode}). Düzenlenebilir kapsamlar process_override_get yanıtının editableScopes alanında; platform 422 ${lockCode} döner.`, guide: 'ngapps://guides/bpm' });
|
|
437
|
-
continue;
|
|
438
|
-
}
|
|
439
|
-
try {
|
|
440
|
-
fastJsonPatch.applyOperation(document, resolved, false, true);
|
|
441
|
-
}
|
|
442
|
-
catch (error) {
|
|
443
|
-
notes.push(`uyarlama denetimi '${originalPath}' işleminde durdu (yerelde uygulanamadı: ${error instanceof Error ? error.message : String(error)}); platform doğrulaması yine çalışır`);
|
|
444
|
-
return { findings, notes };
|
|
445
|
-
}
|
|
446
|
-
}
|
|
447
|
-
walkNodes(document.nodes, (node, region) => {
|
|
448
|
-
const limits = asRecords([node.limits])[0];
|
|
449
|
-
const maxSteps = typeof limits?.maxSteps === 'number' ? limits.maxSteps : undefined;
|
|
450
|
-
const stepCount = asRecords(node.nodes).length;
|
|
451
|
-
if (node.type === 'region' && maxSteps !== undefined && stepCount > maxSteps) {
|
|
452
|
-
findings.push({ ruleId: 'BPM-012', severity: 'block', message: `Bölge '${String(node.id)}' adım sınırını aşıyor: ${stepCount} > ${maxSteps} (limits.maxSteps). Platform 422 MaxStepsExceeded döner.`, guide: 'ngapps://guides/bpm' });
|
|
453
|
-
}
|
|
454
|
-
const regionLimits = asRecords([region?.limits])[0];
|
|
455
|
-
if (!regionLimits)
|
|
456
|
-
return;
|
|
457
|
-
const nodeTypes = Array.isArray(regionLimits.nodeTypes) ? regionLimits.nodeTypes : [];
|
|
458
|
-
if (nodeTypes.length > 0 && !nodeTypes.includes(node.type)) {
|
|
459
|
-
findings.push({ ruleId: 'BPM-012', severity: 'block', message: `Bölge '${String(region?.id)}' bu düğüm türüne izin vermiyor: ${String(node.id)} (${String(node.type)}). İzinli türler: ${nodeTypes.join(', ')}. Platform 422 NodeTypeNotAllowed döner.`, guide: 'ngapps://guides/bpm' });
|
|
460
|
-
}
|
|
461
|
-
const allowedRules = Array.isArray(regionLimits.assigneeRules) ? regionLimits.assigneeRules : [];
|
|
462
|
-
if (allowedRules.length === 0)
|
|
463
|
-
return;
|
|
464
|
-
for (const rule of [...assigneeRules(node.assignee), ...assigneeRules(node.to)]) {
|
|
465
|
-
if (!allowedRules.includes(rule)) {
|
|
466
|
-
findings.push({ ruleId: 'BPM-011', severity: 'block', message: `Bölge '${String(region?.id)}' bu atama kuralına izin vermiyor: ${String(node.id)} (${rule}). İzinli kurallar: ${allowedRules.join(', ')}. Platform 422 RuleNotAllowed döner.`, guide: 'ngapps://guides/bpm' });
|
|
467
|
-
}
|
|
468
|
-
}
|
|
469
|
-
});
|
|
470
|
-
return { findings, notes };
|
|
471
|
-
}
|
|
472
|
-
const ZEYREK_BPMN_NAMESPACE = 'https://zeyrek.dev/bpmn';
|
|
473
|
-
const BPMN_FLOW_ELEMENTS = /<(?:[\w.-]+:)?(?:task|userTask|manualTask|serviceTask|sendTask|receiveTask|scriptTask|businessRuleTask|callActivity|subProcess|transaction|adHocSubProcess|startEvent|endEvent|intermediateCatchEvent|intermediateThrowEvent|exclusiveGateway|parallelGateway|inclusiveGateway|eventBasedGateway|complexGateway)[\s/>]/g;
|
|
474
|
-
/**
|
|
475
|
-
* BPM-016 (g-71): `zeyrek:` uzantısı taşımayan akış öğeleri (başka modelleyiciden) öğe türünden türetilir:
|
|
476
|
-
* derece `locked`, insan adımının ataması yer tutucu `initiator`, SLA/politika boş (backend `BpmnXmlConverter`
|
|
477
|
-
* uzantısız yol). Uzantı önekle değil ad alanı URI'siyle tanınır; uzantılı ve uzantısız öğeler karışık olabilir.
|
|
478
|
-
*/
|
|
479
|
-
export function bpmnExtensionFindings(xml) {
|
|
480
|
-
const prefix = new RegExp(`xmlns:([\\w.-]+)\\s*=\\s*["']${ZEYREK_BPMN_NAMESPACE.replace(/[./]/g, match => `\\${match}`)}["']`).exec(xml)?.[1];
|
|
481
|
-
const flowElementCount = xml.match(BPMN_FLOW_ELEMENTS)?.length ?? 0;
|
|
482
|
-
const extendedCount = prefix ? xml.match(new RegExp(`<${prefix}:node[\\s>]`, 'g'))?.length ?? 0 : 0;
|
|
483
|
-
const missingCount = prefix ? Math.max(flowElementCount - extendedCount, 0) : flowElementCount;
|
|
484
|
-
if (prefix && missingCount === 0)
|
|
485
|
-
return [];
|
|
486
|
-
return [{
|
|
487
|
-
ruleId: 'BPM-016',
|
|
488
|
-
severity: 'warn',
|
|
489
|
-
message: `${prefix ? `${missingCount} akış öğesi` : "XML'deki akış öğeleri"} zeyrek: uzantısı (${ZEYREK_BPMN_NAMESPACE}) taşımıyor (başka modelleyiciden). Bu düğümler öğe türünden türetilir: derece locked (uyarlamaya kapalı), insan adımının ataması yer tutucu 'initiator', SLA/politika boş. process_definition_get { view: 'outline' } ile okuyun, atamaları process_definition_patch ile düzeltip process_definition_preview ile doğrulayın.`,
|
|
490
|
-
guide: 'ngapps://guides/bpm'
|
|
491
|
-
}];
|
|
492
|
-
}
|
|
493
|
-
/**
|
|
494
|
-
* BPM-015 (g-71): yayından önce taslak sağlamasında yeşil koşmamış senaryo. Backend kapısı yalnız
|
|
495
|
-
* `requireGreenScenarios: true` tanımda 422 `ScenarioFailed` döner; diğerlerinde gateway uyarır.
|
|
496
|
-
*/
|
|
497
|
-
export function staleScenarioFindings(definition, scenarios) {
|
|
498
|
-
const draft = asRecords(definition.versions).find(version => version.state === 'draft');
|
|
499
|
-
if (!draft)
|
|
500
|
-
return [];
|
|
501
|
-
const stale = asRecords(scenarios).filter(scenario => scenario.lastRunPassed !== true || scenario.lastRunChecksum !== draft.checksum);
|
|
502
|
-
if (stale.length === 0)
|
|
503
|
-
return [];
|
|
504
|
-
return [{
|
|
505
|
-
ruleId: 'BPM-015',
|
|
506
|
-
severity: 'warn',
|
|
507
|
-
message: `Taslak v${String(draft.number)} için yeşil koşmamış ${stale.length} senaryo var: ${stale.slice(0, 5).map(scenario => `'${String(scenario.name)}' (${scenario.lastRunPassed === false && scenario.lastRunChecksum === draft.checksum ? 'kırmızı' : 'bu taslakta koşulmadı'})`).join(', ')}. Önce process_scenario_execute { definitionId } koşun; tanımda requireGreenScenarios: true ise yayın 422 ScenarioFailed döner.`,
|
|
508
|
-
guide: 'ngapps://guides/bpm'
|
|
509
|
-
}];
|
|
510
|
-
}
|
|
@@ -1,43 +0,0 @@
|
|
|
1
|
-
import { readFileSync } from 'node:fs';
|
|
2
|
-
import { dirname, join } from 'node:path';
|
|
3
|
-
import { fileURLToPath } from 'node:url';
|
|
4
|
-
import { z } from 'zod';
|
|
5
|
-
const FALLBACK = {
|
|
6
|
-
operators: ['eq', 'neq', 'lt', 'lte', 'gt', 'gte', 'contains', 'icontains', 'in', 'notin', 'startswith', 'endswith', 'istartswith', 'iendswith', 'isnull', 'isnotnull'],
|
|
7
|
-
filterTypes: ['single', 'group'],
|
|
8
|
-
conditions: ['and', 'or']
|
|
9
|
-
};
|
|
10
|
-
/**
|
|
11
|
-
* Platform `Filter` gövdesinin enumları paketlenmiş şemadan (`resources/schemas/Filter.json`, `refresh-schemas`):
|
|
12
|
-
* işleç sözlüğü backend enum'undan tek kaynakla gelir (K-9). Dosya okunamazsa (bozuk paket) bugünkü enum.
|
|
13
|
-
*/
|
|
14
|
-
export function rowFilterEnums(schemaPath = join(dirname(fileURLToPath(import.meta.url)), '..', 'resources', 'schemas', 'Filter.json')) {
|
|
15
|
-
try {
|
|
16
|
-
const schemas = JSON.parse(readFileSync(schemaPath, 'utf8')).components?.schemas ?? {};
|
|
17
|
-
const enumOf = (suffix, fallback) => {
|
|
18
|
-
const values = Object.entries(schemas).find(([name]) => name.endsWith(`.${suffix}`))?.[1]?.enum;
|
|
19
|
-
return values && values.length > 0 ? values : fallback;
|
|
20
|
-
};
|
|
21
|
-
return { operators: enumOf('OperatorType', FALLBACK.operators), filterTypes: enumOf('FilterType', FALLBACK.filterTypes), conditions: enumOf('ConditionType', FALLBACK.conditions) };
|
|
22
|
-
}
|
|
23
|
-
catch {
|
|
24
|
-
return FALLBACK;
|
|
25
|
-
}
|
|
26
|
-
}
|
|
27
|
-
/**
|
|
28
|
-
* `row_list` / `row_export` filtresi: tekil koşul (`field` + `operator` + `value`) ya da `children`'lı grup
|
|
29
|
-
* (`conditionType` and/or). Ham SQL alanları (`rawSql`, `rawBindings`) kabul edilmez — platformun iç alanlarıdır.
|
|
30
|
-
*/
|
|
31
|
-
export function rowFilterSchema(enums = rowFilterEnums()) {
|
|
32
|
-
const node = z.lazy(() => z.object({
|
|
33
|
-
filterType: z.enum(enums.filterTypes).optional(),
|
|
34
|
-
field: z.string().min(1).nullable().optional(),
|
|
35
|
-
// Sözlük dışı işleç şemada değil araçta reddedilir (`filterOperatorError`): ret aynı, istek yine gitmez, ama mesaj doğru adı önerir.
|
|
36
|
-
operator: z.string().min(1).optional().describe(`İşleç: ${enums.operators.join(' · ')}`),
|
|
37
|
-
value: z.unknown().optional(),
|
|
38
|
-
conditionType: z.enum(enums.conditions).optional(),
|
|
39
|
-
children: z.array(node).nullable().optional(),
|
|
40
|
-
isParentRequired: z.boolean().optional()
|
|
41
|
-
}).strict());
|
|
42
|
-
return node;
|
|
43
|
-
}
|