@appaflytech/zeyrek-ai-mcp 0.0.35 → 0.0.37
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 +15 -0
- package/README.md +5 -5
- package/dist/config.js +37 -0
- package/dist/index.js +2 -0
- package/dist/knowledge/access.js +802 -0
- package/dist/knowledge/backendConstraints.js +18 -1
- package/dist/knowledge/case.js +41 -0
- package/dist/knowledge/componentCatalog.js +655 -424
- package/dist/knowledge/componentEvents.js +140 -23
- package/dist/knowledge/connectDataBind.js +843 -0
- package/dist/knowledge/connectDataDefinition.js +344 -0
- package/dist/knowledge/connectDataExecute.js +93 -0
- package/dist/knowledge/connectDataRules.js +128 -0
- package/dist/knowledge/decision.js +121 -0
- package/dist/knowledge/expressionPrelude.js +59 -0
- package/dist/knowledge/layoutSummary.js +140 -2
- package/dist/knowledge/microserviceParameters.js +58 -0
- package/dist/knowledge/mining.js +94 -0
- package/dist/knowledge/mobileComponentCatalog.js +864 -0
- package/dist/knowledge/mobileComponentEvents.js +239 -0
- package/dist/knowledge/mobileStore.js +321 -0
- package/dist/knowledge/normalize.js +235 -84
- package/dist/knowledge/outlines.js +36 -0
- package/dist/knowledge/platform/lint.js +385 -0
- package/dist/knowledge/platform/mobile.js +142 -0
- package/dist/knowledge/platform/mobileLayoutLint.js +324 -0
- package/dist/knowledge/platform/mobileRules.js +231 -0
- package/dist/knowledge/platform/mobileSpacing.js +82 -0
- package/dist/knowledge/platform/mobileTabs.js +108 -0
- package/dist/knowledge/platform/nativeBoxModel.js +44 -0
- package/dist/knowledge/platform/profile.js +25 -0
- package/dist/knowledge/platform/resolve.js +122 -0
- package/dist/knowledge/platform/web.js +39 -0
- package/dist/knowledge/policy.js +18 -4
- package/dist/knowledge/process.js +510 -0
- package/dist/knowledge/reservedTables.js +79 -0
- package/dist/knowledge/rowFilter.js +43 -0
- package/dist/knowledge/rules.js +423 -37
- package/dist/knowledge/schemaGuard.js +11 -0
- package/dist/knowledge/styleResolve.js +53 -19
- package/dist/knowledge/styleResolveNative.js +527 -0
- package/dist/knowledge/themeResolve.js +4 -4
- package/dist/knowledge/themeSchema.js +1 -2
- package/dist/observability/trace.js +6 -3
- package/dist/platform/argumentAliases.js +128 -0
- package/dist/platform/bodyFile.js +103 -0
- package/dist/platform/capture/actions.js +398 -0
- package/dist/platform/capture/browser.js +1 -0
- package/dist/platform/capture/capture.js +683 -0
- package/dist/platform/capture/console-log.js +122 -0
- package/dist/platform/capture/index.js +11 -0
- package/dist/platform/capture/readiness.js +103 -0
- package/dist/platform/capture/summary.js +77 -0
- package/dist/platform/capture/target.js +5 -0
- package/dist/platform/capture/targets/studio-mobile.js +15 -0
- package/dist/platform/capture/targets/studio-web.js +5 -0
- package/dist/platform/diagnostics.js +10 -4
- package/dist/platform/domSummary.js +1 -1
- package/dist/platform/errors.js +163 -10
- package/dist/platform/http.js +29 -8
- package/dist/platform/impersonation.js +45 -0
- package/dist/platform/jsonArray.js +24 -0
- package/dist/platform/responses.js +39 -5
- package/dist/platform/schemaReadMemory.js +60 -0
- package/dist/platform/secrets.js +19 -1
- package/dist/platform/strictArguments.js +68 -12
- package/dist/platform/tokenVault.js +187 -4
- package/dist/resources/examples/bpm-gorev-kontrol.json +294 -0
- package/dist/resources/examples/bpm-iso-dokuman.json +228 -0
- package/dist/resources/examples/bpm-satinalma.json +376 -0
- package/dist/resources/examples/connect-data-convert-verify.json +107 -0
- package/dist/resources/examples/connect-data-dropdown-roles.json +66 -0
- package/dist/resources/examples/connect-data-edit-m2m.json +73 -0
- package/dist/resources/examples/connect-data-query-params.json +50 -0
- package/dist/resources/examples/connect-data-table-entity.json +147 -0
- package/dist/resources/examples/connect-data-workflow-microservice.json +65 -0
- package/dist/resources/examples/middleware-authenticate.json +1 -1
- package/dist/resources/examples/mobile-app-shell-drawer.json +244 -0
- package/dist/resources/examples/mobile-app-shell-stack.json +157 -0
- package/dist/resources/examples/mobile-app-shell-tab.json +208 -0
- package/dist/resources/examples/mobile-form-screen.json +365 -0
- package/dist/resources/examples/mobile-list-detail-form.json +703 -0
- package/dist/resources/examples/page-detail-profile.json +2 -1
- package/dist/resources/examples/page-dynamic-form.json +3 -2
- 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 +8 -2
- package/dist/resources/guides/access.md +810 -0
- package/dist/resources/guides/bpm.md +691 -0
- package/dist/resources/guides/case.md +153 -0
- package/dist/resources/guides/component.md +93 -6
- package/dist/resources/guides/decision.md +185 -0
- package/dist/resources/guides/entity.md +50 -8
- package/dist/resources/guides/expressions.md +14 -5
- package/dist/resources/guides/functions.md +1 -1
- package/dist/resources/guides/getting-started.md +38 -1
- package/dist/resources/guides/layout.md +11 -7
- package/dist/resources/guides/merge.md +37 -3
- package/dist/resources/guides/microservice.md +87 -2
- package/dist/resources/guides/middleware.md +68 -4
- package/dist/resources/guides/mobile-layout.md +193 -0
- package/dist/resources/guides/mobile-publish.md +124 -0
- package/dist/resources/guides/mobile.md +549 -0
- package/dist/resources/guides/page.md +463 -83
- package/dist/resources/guides/theme.md +8 -3
- package/dist/resources/guides/translation.md +1 -1
- package/dist/resources/guides/workflow.md +58 -1
- package/dist/resources/read.js +41 -0
- package/dist/resources/rules.js +11 -5
- package/dist/resources/schema-fingerprint.json +7 -5
- package/dist/resources/schemas/AccessExplanationDto.json +144 -0
- package/dist/resources/schemas/AccessMatrixDto.json +170 -0
- package/dist/resources/schemas/AccessPermissionDto.json +57 -0
- package/dist/resources/schemas/AccessPolicyDto.json +223 -0
- package/dist/resources/schemas/AccessResourceDto.json +86 -0
- package/dist/resources/schemas/AppSessionDto.json +53 -0
- package/dist/resources/schemas/ConnectDataDescribeDto.json +344 -0
- package/dist/resources/schemas/ConnectDataRequestModel.json +202 -0
- package/dist/resources/schemas/ConnectDataResultDto.json +421 -0
- package/dist/resources/schemas/ConnectorCredentialDto.json +70 -0
- package/dist/resources/schemas/ConvertConnectDataToMicroserviceCommand.json +37 -0
- package/dist/resources/schemas/CreateAccessPolicyCommand.json +209 -0
- package/dist/resources/schemas/CreateDecisionCommand.json +221 -0
- package/dist/resources/schemas/CreateEntityCommand.json +118 -0
- package/dist/resources/schemas/CreateMicroserviceCommand.json +13 -1
- package/dist/resources/schemas/CreateProcessDefinitionCommand.json +1337 -0
- package/dist/resources/schemas/CreateQueryCommand.json +4 -0
- package/dist/resources/schemas/CreateWorkflowCommand.json +146 -1
- package/dist/resources/schemas/DataJobDto.json +6 -1
- package/dist/resources/schemas/DecisionDto.json +302 -0
- package/dist/resources/schemas/DetachConnectDataMicroserviceCommand.json +28 -0
- package/dist/resources/schemas/ExplainAccessQuery.json +33 -0
- package/dist/resources/schemas/GenerateProcessDocumentCommand.json +116 -0
- package/dist/resources/schemas/GlobalFunctionUpdateResultDto.json +25 -1
- package/dist/resources/schemas/MicroserviceDto.json +15 -3
- package/dist/resources/schemas/OrganizationProjectDto.json +335 -0
- package/dist/resources/schemas/OrganizationSchemaDto.json +196 -0
- package/dist/resources/schemas/OrganizationSchemaVersionDto.json +207 -0
- package/dist/resources/schemas/PatchProcessDefinitionCommand.json +76 -0
- package/dist/resources/schemas/PreviewMicroserviceCommand.json +10 -1
- package/dist/resources/schemas/PreviewWorkflowCommand.json +25 -1
- package/dist/resources/schemas/ProcessDefinitionDto.json +1480 -0
- package/dist/resources/schemas/ProcessSimulationDto.json +96 -0
- package/dist/resources/schemas/SaveConnectorCredentialCommand.json +90 -0
- package/dist/resources/schemas/SaveProcessEventSourceCommand.json +61 -0
- package/dist/resources/schemas/SaveProcessMailSourceCommand.json +89 -0
- package/dist/resources/schemas/SignProcessTaskCommand.json +73 -0
- package/dist/resources/schemas/SimulateProcessDefinitionQuery.json +92 -0
- package/dist/resources/schemas/StartMobileBuildCommand.json +54 -0
- package/dist/resources/schemas/StepDto.json +25 -1
- package/dist/resources/schemas/UpdateAccessPolicyCommand.json +209 -0
- package/dist/resources/schemas/UpdateDecisionCommand.json +216 -0
- package/dist/resources/schemas/UpdateEntityCommand.json +118 -0
- package/dist/resources/schemas/UpdateMicroserviceCommand.json +13 -1
- package/dist/resources/schemas/UpdateProcessDefinitionCommand.json +1357 -0
- package/dist/resources/schemas/UpdateQueryCommand.json +4 -0
- package/dist/resources/schemas/UpdateViewCommand.json +0 -2
- package/dist/resources/schemas/UpdateWorkflowCommand.json +146 -1
- package/dist/resources/schemas/UpsertMobileStoreSettingsCommand.json +274 -0
- package/dist/resources/schemas/ValidateProcessDefinitionQuery.json +1339 -0
- package/dist/server.js +31 -1
- package/dist/tools/access.js +369 -2
- package/dist/tools/accessOperations.js +135 -0
- package/dist/tools/accessPolicies.js +170 -0
- package/dist/tools/accessRuntime.js +203 -0
- package/dist/tools/applications.js +60 -3
- package/dist/tools/bpm.js +343 -0
- package/dist/tools/bpmGovernance.js +266 -0
- package/dist/tools/bpmOperations.js +230 -0
- package/dist/tools/bpmReporting.js +154 -0
- package/dist/tools/bpmRouting.js +150 -0
- package/dist/tools/bpmRuntime.js +396 -0
- package/dist/tools/case.js +178 -0
- package/dist/tools/caseRuntime.js +89 -0
- package/dist/tools/common.js +10 -1
- package/dist/tools/components.js +118 -14
- package/dist/tools/connectData.js +450 -0
- package/dist/tools/connectors.js +193 -0
- package/dist/tools/datasources.js +3 -3
- package/dist/tools/decisions.js +206 -0
- package/dist/tools/dependencies.js +1 -1
- package/dist/tools/deployments.js +38 -3
- package/dist/tools/designSchemaPatch.js +34 -9
- package/dist/tools/dtos.js +36 -5
- package/dist/tools/entities.js +79 -12
- package/dist/tools/externalComponents.js +26 -5
- package/dist/tools/flows.js +119 -17
- package/dist/tools/folders.js +1 -1
- package/dist/tools/imports.js +5 -2
- package/dist/tools/layouts.js +50 -17
- package/dist/tools/merges.js +2 -2
- package/dist/tools/microservices.js +77 -25
- package/dist/tools/mobile.js +280 -0
- package/dist/tools/mobileStore.js +556 -0
- package/dist/tools/modules.js +48 -0
- package/dist/tools/organizationStructure.js +137 -0
- package/dist/tools/pages.js +465 -98
- package/dist/tools/platformInfo.js +28 -8
- package/dist/tools/profileWrite.js +92 -0
- package/dist/tools/projects.js +71 -9
- package/dist/tools/rows.js +69 -13
- package/dist/tools/schemaOps.js +61 -1
- package/dist/tools/screenWrite.js +36 -7
- package/dist/tools/screens.js +95 -5
- package/dist/tools/templates.js +26 -1
- package/dist/tools/translations.js +1 -1
- package/dist/tools/variables.js +11 -5
- package/dist/tools/workflows.js +10 -5
- package/package.json +2 -2
- package/dist/platform/screenshot.js +0 -1162
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
const guide = 'ngapps://guides/decision';
|
|
2
|
+
/**
|
|
3
|
+
* b-81 `FeelExpressionResolver.FunctionNames` ile birebir. Backend doğrulayıcısı her ifadeyi YAZIMDA
|
|
4
|
+
* ayrıştırır ve listede olmayan çağrıyı 422 ile reddeder (içe aktarma dahil); gateway istek gitmeden
|
|
5
|
+
* uyarır (DMN-002). Denetim sözcük tabanlı ve sezgisel olduğu için block değil warn.
|
|
6
|
+
*/
|
|
7
|
+
export const feelFunctionNames = new Set([
|
|
8
|
+
'years and months duration', 'date and time', 'string length', 'upper case', 'lower case', 'starts with', 'ends with',
|
|
9
|
+
'list contains', 'substring before', 'substring after', 'distinct values', 'index of', 'get value',
|
|
10
|
+
'date', 'time', 'duration', 'substring', 'contains', 'matches', 'replace', 'string', 'number', 'not',
|
|
11
|
+
'count', 'sum', 'product', 'min', 'max', 'mean', 'all', 'any', 'append', 'concatenate', 'flatten', 'reverse',
|
|
12
|
+
'abs', 'floor', 'ceiling', 'decimal', 'modulo', 'sqrt', 'odd', 'even'
|
|
13
|
+
]);
|
|
14
|
+
const feelKeywords = new Set(['if', 'then', 'else', 'and', 'or', 'in', 'between', 'return', 'satisfies', 'for', 'some', 'every', 'function', 'instance', 'of']);
|
|
15
|
+
const wildcardEntries = new Set(['', '-']);
|
|
16
|
+
function asObjects(value) {
|
|
17
|
+
return (Array.isArray(value) ? value : []).filter((entry) => Boolean(entry) && typeof entry === 'object' && !Array.isArray(entry));
|
|
18
|
+
}
|
|
19
|
+
function asStrings(value) {
|
|
20
|
+
return (Array.isArray(value) ? value : []).map(entry => typeof entry === 'string' ? entry : '');
|
|
21
|
+
}
|
|
22
|
+
function normalizedEntry(entry) {
|
|
23
|
+
return entry.replace(/\s+/g, ' ').trim();
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Çok sözcüklü FEEL işlev adı ("string length(") sözcük dizisinin SONEKİdir: önündeki "if"
|
|
27
|
+
* gibi anahtar sözcükler ve değişken adları çağrının parçası değildir. Bilinen bir sonek yoksa
|
|
28
|
+
* anahtar sözcükler atılıp kalan dizi bilinmeyen işlev adı sayılır.
|
|
29
|
+
*/
|
|
30
|
+
export function unknownFeelFunctions(expression) {
|
|
31
|
+
const withoutStrings = expression.replace(/"(?:[^"\\]|\\.)*"/g, '""');
|
|
32
|
+
const unknown = [];
|
|
33
|
+
for (const match of withoutStrings.matchAll(/([A-Za-z_][A-Za-z0-9_]*(?:[ \t]+[A-Za-z_][A-Za-z0-9_]*)*)[ \t]*\(/g)) {
|
|
34
|
+
const words = (match[1] ?? '').split(/[ \t]+/);
|
|
35
|
+
if (feelKeywords.has(words[words.length - 1]))
|
|
36
|
+
continue;
|
|
37
|
+
const isKnown = words.some((_, index) => feelFunctionNames.has(words.slice(index).join(' ')));
|
|
38
|
+
if (isKnown)
|
|
39
|
+
continue;
|
|
40
|
+
let firstNameIndex = 0;
|
|
41
|
+
while (firstNameIndex < words.length && feelKeywords.has(words[firstNameIndex]))
|
|
42
|
+
firstNameIndex++;
|
|
43
|
+
const name = words.slice(firstNameIndex).join(' ');
|
|
44
|
+
if (name !== '' && !unknown.includes(name))
|
|
45
|
+
unknown.push(name);
|
|
46
|
+
}
|
|
47
|
+
return unknown;
|
|
48
|
+
}
|
|
49
|
+
function decisionExpressions(decision) {
|
|
50
|
+
const expressions = [];
|
|
51
|
+
asObjects(decision.inputs).forEach((input, index) => {
|
|
52
|
+
if (typeof input.expression === 'string')
|
|
53
|
+
expressions.push({ path: `/inputs/${index}/expression`, expression: input.expression });
|
|
54
|
+
if (typeof input.allowedValues === 'string')
|
|
55
|
+
expressions.push({ path: `/inputs/${index}/allowedValues`, expression: input.allowedValues });
|
|
56
|
+
});
|
|
57
|
+
asObjects(decision.outputs).forEach((output, index) => {
|
|
58
|
+
if (typeof output.defaultOutputEntry === 'string')
|
|
59
|
+
expressions.push({ path: `/outputs/${index}/defaultOutputEntry`, expression: output.defaultOutputEntry });
|
|
60
|
+
});
|
|
61
|
+
asObjects(decision.rules).forEach((rule, ruleIndex) => {
|
|
62
|
+
asStrings(rule.inputEntries).forEach((entry, entryIndex) => expressions.push({ path: `/rules/${ruleIndex}/inputEntries/${entryIndex}`, expression: entry }));
|
|
63
|
+
asStrings(rule.outputEntries).forEach((entry, entryIndex) => expressions.push({ path: `/rules/${ruleIndex}/outputEntries/${entryIndex}`, expression: entry }));
|
|
64
|
+
});
|
|
65
|
+
if (typeof decision.literalExpression === 'string')
|
|
66
|
+
expressions.push({ path: '/literalExpression', expression: decision.literalExpression });
|
|
67
|
+
return expressions;
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* DMN-001 (block): `unique` tabloda iki kural her girdi sütununda aynı testi ya da joker (`-`)
|
|
71
|
+
* taşıyorsa aynı girdi ikisini de eşler ve motor 422 `DecisionHitPolicyViolated` döner. Denetim
|
|
72
|
+
* statik ve temkinlidir: yalnız KESİN çakışmayı yakalar, kesişen aralıkları (`< 10` ve `< 20`)
|
|
73
|
+
* yakalamaz — o çakışma `decision_preview` ile görülür.
|
|
74
|
+
* DMN-002 (warn): b-81 FEEL alt kümesinde olmayan işlev çağrısı; backend yazımı 422 ile reddeder.
|
|
75
|
+
*/
|
|
76
|
+
export function decisionFindings(decision) {
|
|
77
|
+
const findings = [];
|
|
78
|
+
const hitPolicy = typeof decision.hitPolicy === 'string' ? decision.hitPolicy : 'unique';
|
|
79
|
+
const kind = typeof decision.kind === 'string' ? decision.kind : 'table';
|
|
80
|
+
if (kind === 'table' && hitPolicy === 'unique') {
|
|
81
|
+
const rules = asObjects(decision.rules);
|
|
82
|
+
const columnCount = asObjects(decision.inputs).length;
|
|
83
|
+
for (let left = 0; left < rules.length; left++) {
|
|
84
|
+
for (let right = left + 1; right < rules.length; right++) {
|
|
85
|
+
const leftEntries = asStrings(rules[left].inputEntries).map(normalizedEntry);
|
|
86
|
+
const rightEntries = asStrings(rules[right].inputEntries).map(normalizedEntry);
|
|
87
|
+
const overlaps = Array.from({ length: Math.max(columnCount, leftEntries.length, rightEntries.length) }, (_, column) => {
|
|
88
|
+
const leftEntry = leftEntries[column] ?? '';
|
|
89
|
+
const rightEntry = rightEntries[column] ?? '';
|
|
90
|
+
return wildcardEntries.has(leftEntry) || wildcardEntries.has(rightEntry) || leftEntry === rightEntry;
|
|
91
|
+
}).every(Boolean);
|
|
92
|
+
if (!overlaps)
|
|
93
|
+
continue;
|
|
94
|
+
const leftId = String(rules[left].id ?? `#${left + 1}`);
|
|
95
|
+
const rightId = String(rules[right].id ?? `#${right + 1}`);
|
|
96
|
+
findings.push({
|
|
97
|
+
ruleId: 'DMN-001',
|
|
98
|
+
severity: 'block',
|
|
99
|
+
message: `'${String(decision.slug ?? '')}' unique tablosunda '${leftId}' ve '${rightId}' kuralları aynı girdiyi eşler (her sütunda aynı test ya da '-'); değerlendirme 422 DecisionHitPolicyViolated döner. Kuralları ayırın ya da hitPolicy 'first' / 'priority' / 'collect' seçin.`,
|
|
100
|
+
guide
|
|
101
|
+
});
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
const unknownByName = new Map();
|
|
106
|
+
for (const { path, expression } of decisionExpressions(decision)) {
|
|
107
|
+
for (const name of unknownFeelFunctions(expression)) {
|
|
108
|
+
if (!unknownByName.has(name))
|
|
109
|
+
unknownByName.set(name, path);
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
for (const [name, path] of unknownByName) {
|
|
113
|
+
findings.push({
|
|
114
|
+
ruleId: 'DMN-002',
|
|
115
|
+
severity: 'warn',
|
|
116
|
+
message: `FEEL işlevi '${name}' (${path}) motorun alt kümesinde yok; backend yazımı (create, update, patch, DMN içe aktarma) 422 ile reddeder. Desteklenenler platform_guide('decision') §FEEL; ör. yuvarlama için decimal(x, 0), floor, ceiling.`,
|
|
117
|
+
guide
|
|
118
|
+
});
|
|
119
|
+
}
|
|
120
|
+
return findings;
|
|
121
|
+
}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { FUNCTIONS_NAMESPACE } from './functionGuards.js';
|
|
2
|
+
/**
|
|
3
|
+
* İstemci ifade preamble'ının ayrılmış adları — `zeyrek-frontend/packages/ng-apps-flow/src/
|
|
4
|
+
* flow-execution.functions.ts` `EXPRESSION_PRELUDE_NAMES` ile BİREBİR (elle aynalanır, sürüklenmeyi
|
|
5
|
+
* `test/expression-prelude-parity.test.ts` yakalar). Runtime `Components` anahtarlarını
|
|
6
|
+
* `key.replace('-', '')` (YALNIZ ilk tire) ile tanımlayıcıya çevirir ve bu kümedeki adı uyarıyla
|
|
7
|
+
* ELER: o komponent sayfadaki hiçbir ifadede görünmez (ARC-143, PAGE-009).
|
|
8
|
+
*/
|
|
9
|
+
export const EXPRESSION_PRELUDE_NAMES = [
|
|
10
|
+
'payload',
|
|
11
|
+
FUNCTIONS_NAMESPACE,
|
|
12
|
+
'machine',
|
|
13
|
+
'params',
|
|
14
|
+
'_PROXY_TAG',
|
|
15
|
+
'_STATE_PROXY_TAG',
|
|
16
|
+
'Components',
|
|
17
|
+
'Page',
|
|
18
|
+
'_stripProxy',
|
|
19
|
+
'_buildPath',
|
|
20
|
+
'_resolveStateProxy',
|
|
21
|
+
'_stripStateProxy',
|
|
22
|
+
'log',
|
|
23
|
+
'_statePathValue',
|
|
24
|
+
'createStateScopeProxy',
|
|
25
|
+
'state',
|
|
26
|
+
'Steps',
|
|
27
|
+
'error',
|
|
28
|
+
'Widget',
|
|
29
|
+
'LocalStorage',
|
|
30
|
+
'Device',
|
|
31
|
+
'Section',
|
|
32
|
+
'Row',
|
|
33
|
+
'RowIndex',
|
|
34
|
+
'_eventSourceId',
|
|
35
|
+
'_eventSourceNode',
|
|
36
|
+
'event',
|
|
37
|
+
'item',
|
|
38
|
+
'self',
|
|
39
|
+
'addSetters',
|
|
40
|
+
'_rawComponents',
|
|
41
|
+
'_eventParams',
|
|
42
|
+
'repeaterId',
|
|
43
|
+
'setComponentProperty',
|
|
44
|
+
'getComponentProperty',
|
|
45
|
+
'resetForm',
|
|
46
|
+
't',
|
|
47
|
+
'cookie',
|
|
48
|
+
'__fnArgs'
|
|
49
|
+
];
|
|
50
|
+
const RESERVED_EXPRESSION_NAMES = new Set(EXPRESSION_PRELUDE_NAMES);
|
|
51
|
+
/**
|
|
52
|
+
* Komponent adının runtime'daki ifade tanımlayıcısı ayrılmışsa o tanımlayıcıyı döndürür. Tire
|
|
53
|
+
* işleme runtime'la aynıdır (yalnız ilk `-` silinir): `ite-m` → `item` gizlenir, `i-te-m` →
|
|
54
|
+
* `ite-m` gizlenmez.
|
|
55
|
+
*/
|
|
56
|
+
export function reservedExpressionName(componentName) {
|
|
57
|
+
const identifier = componentName.replace('-', '');
|
|
58
|
+
return RESERVED_EXPRESSION_NAMES.has(identifier) ? identifier : undefined;
|
|
59
|
+
}
|
|
@@ -1,10 +1,148 @@
|
|
|
1
|
-
|
|
1
|
+
import { MOBILE_DROPPABLE_TYPES, MOBILE_INTRINSIC_WIDTH_TYPES, MOBILE_OUT_OF_FLOW_TYPES, MOBILE_TRANSPARENT_WRAPPER_TYPES } from './mobileComponentCatalog.js';
|
|
2
|
+
const SPACING_SCALE = [0, 4, 8, 16, 24, 32];
|
|
3
|
+
const EDGE_TOLERANCE = 1;
|
|
4
|
+
const WIDTH_TOLERANCE = 2;
|
|
5
|
+
export function summarizeLayout(boxes, viewport, options = {}) {
|
|
2
6
|
const outside = (box) => box.x + box.width <= 0 || box.y + box.height <= 0 || box.x >= viewport.width || box.y >= viewport.height;
|
|
7
|
+
const alignment = options.alignment === true ? summarizeAlignment(boxes, viewport) : undefined;
|
|
3
8
|
return {
|
|
4
9
|
nodeCount: boxes.length,
|
|
5
10
|
overflowing: boxes.filter(box => box.overflowX || box.overflowY).map(box => box.elementId),
|
|
6
11
|
outsideViewport: boxes.filter(box => box.width > 0 && box.height > 0 && outside(box)).map(box => box.elementId),
|
|
7
12
|
zeroSized: boxes.filter(box => box.width === 0 || box.height === 0).map(box => box.elementId),
|
|
8
|
-
truncated: boxes.length >= 500
|
|
13
|
+
truncated: boxes.length >= 500,
|
|
14
|
+
...(alignment ? { alignment } : {})
|
|
9
15
|
};
|
|
10
16
|
}
|
|
17
|
+
function summarizeAlignment(boxes, viewport) {
|
|
18
|
+
// Tip ve ata sırası olmayan kutular (eski runtime/betik) ağaç kurmaz; hiza uydurulmaz.
|
|
19
|
+
if (!boxes.some(box => typeof box.type === 'string' && box.parentIndex !== undefined))
|
|
20
|
+
return undefined;
|
|
21
|
+
// Ağaç sırayla kurulur, kimlikle değil: `repeater` aynı elementId'yi her satırda yeniden çizer.
|
|
22
|
+
const parentOf = (box) => typeof box.parentIndex === 'number' ? boxes[box.parentIndex] : undefined;
|
|
23
|
+
// İç roller (`__trigger`, `__press`) tip taşımaz: bileşenin parçasıdır, düğüm değildir.
|
|
24
|
+
const typedParent = (box) => {
|
|
25
|
+
for (let current = parentOf(box); current; current = parentOf(current))
|
|
26
|
+
if (current.type)
|
|
27
|
+
return current;
|
|
28
|
+
return undefined;
|
|
29
|
+
};
|
|
30
|
+
const ancestors = (box) => {
|
|
31
|
+
const chain = [];
|
|
32
|
+
for (let current = typedParent(box); current; current = typedParent(current))
|
|
33
|
+
chain.push(current);
|
|
34
|
+
return chain;
|
|
35
|
+
};
|
|
36
|
+
// Ana ağacın kökü en büyük atasız kutudur. Öteki atasız kutular portala çizilir (floating-container,
|
|
37
|
+
// toast, modal içeriği) ve akışta değildir; sayılsalar ana kökü "yan yana kardeş" yapar, ekran satır sayılırdı.
|
|
38
|
+
// Sayfanın ağacını taşıyan kök seçilir (kabuklu sayfada shell_root, kabuksuzda page_container); bulunamazsa
|
|
39
|
+
// en büyük kök, eşitlikte sonraki — portal içeriği DOM'da uygulamadan önce durur.
|
|
40
|
+
const roots = boxes.filter(box => box.type && !typedParent(box));
|
|
41
|
+
const pageContainer = boxes.find(box => box.elementId === 'page_container');
|
|
42
|
+
const mainRoot = (pageContainer ? [pageContainer, ...ancestors(pageContainer)].at(-1) : undefined)
|
|
43
|
+
?? [...roots].reverse().sort((a, b) => b.width * b.height - a.width * a.height)[0];
|
|
44
|
+
// Saydam sarmalayıcının (tooltip) kutusu görünmez; dikey aralık onun görünen içeriğinden ölçülür.
|
|
45
|
+
const visibleExtent = (box) => {
|
|
46
|
+
const children = MOBILE_TRANSPARENT_WRAPPER_TYPES.includes(box.type ?? '') ? boxes.filter(child => child.type && typedParent(child) === box && child.height > 0) : [];
|
|
47
|
+
return children.length === 0
|
|
48
|
+
? { top: box.y, bottom: box.y + box.height }
|
|
49
|
+
: { top: Math.min(...children.map(child => child.y)), bottom: Math.max(...children.map(child => child.y + child.height)) };
|
|
50
|
+
};
|
|
51
|
+
const outOfFlow = (box) => {
|
|
52
|
+
const chain = [box, ...ancestors(box)];
|
|
53
|
+
return chain.some(item => MOBILE_OUT_OF_FLOW_TYPES.includes(item.type ?? '')) || chain[chain.length - 1] !== mainRoot;
|
|
54
|
+
};
|
|
55
|
+
// Yatayda ekrana sığmayan kutu (carousel ya da yatay kaydırmanın öteki sayfaları) sütun akışında değildir.
|
|
56
|
+
const onScreen = (box) => box.x >= -EDGE_TOLERANCE && box.x + box.width <= viewport.width + EDGE_TOLERANCE;
|
|
57
|
+
const flow = boxes.filter(box => box.type && box.width > 0 && box.height > 0 && onScreen(box) && !outOfFlow(box));
|
|
58
|
+
const inFlow = new Set(flow);
|
|
59
|
+
const parents = new Set(flow.map(typedParent).filter(Boolean));
|
|
60
|
+
const siblings = (box) => flow.filter(other => other !== box && typedParent(other) === typedParent(box));
|
|
61
|
+
const besideSibling = (box) => siblings(box).some(other => other.y < box.y + box.height - EDGE_TOLERANCE && box.y < other.y + other.height - EDGE_TOLERANCE);
|
|
62
|
+
// Kapsam, kendi kapsamından dar bir yüzeydir. Kapsamı boydan boya kaplayan zemin (boyanmış sayfa kökü,
|
|
63
|
+
// tam genişlik bant) yeni kapsam açmaz; içeriği ekranın kenar ölçüsüne girer.
|
|
64
|
+
const scopeCache = new Map();
|
|
65
|
+
const scopeOf = (box) => {
|
|
66
|
+
if (scopeCache.has(box))
|
|
67
|
+
return scopeCache.get(box);
|
|
68
|
+
const scope = ancestors(box).find(item => inFlow.has(item) && opensScope(item));
|
|
69
|
+
scopeCache.set(box, scope);
|
|
70
|
+
return scope;
|
|
71
|
+
};
|
|
72
|
+
const opensScope = (box) => box.surface === true && box.width < (scopeOf(box)?.width ?? viewport.width) - WIDTH_TOLERANCE;
|
|
73
|
+
// Yan yana dizilen öğe (satır kabının çocuğu ya da onun içindeki sütun) sütun hizasına girmez.
|
|
74
|
+
const inRow = (box) => {
|
|
75
|
+
const scope = scopeOf(box);
|
|
76
|
+
const chain = [box, ...ancestors(box)];
|
|
77
|
+
return chain.slice(0, scope ? chain.indexOf(scope) : chain.length).some(besideSibling);
|
|
78
|
+
};
|
|
79
|
+
const isContent = (box) => parents.has(box)
|
|
80
|
+
? opensScope(box)
|
|
81
|
+
: box.surface === true || !MOBILE_DROPPABLE_TYPES.includes(box.type ?? '');
|
|
82
|
+
const scopes = new Map([[undefined, []]]);
|
|
83
|
+
for (const box of flow.filter(isContent)) {
|
|
84
|
+
const scope = scopeOf(box);
|
|
85
|
+
if (box.width >= (scope?.width ?? viewport.width) / 2 && !inRow(box))
|
|
86
|
+
scopes.set(scope, [...(scopes.get(scope) ?? []), box]);
|
|
87
|
+
}
|
|
88
|
+
const reports = [...scopes].map(([scope, members]) => ({ scope, ...scopeAlignment(members) }));
|
|
89
|
+
const screen = reports.find(report => report.scope === undefined);
|
|
90
|
+
return {
|
|
91
|
+
gutter: screenGutter(screen?.dominantLeft, screen?.dominantRight, viewport.width),
|
|
92
|
+
leftEdges: screen?.leftEdges ?? [],
|
|
93
|
+
surfaces: reports.filter(report => report.scope !== undefined && report.leftEdges.length > 1).map(report => ({ elementId: report.scope?.elementId ?? '', leftEdges: report.leftEdges })),
|
|
94
|
+
widthOutliers: reports.flatMap(report => report.widthOutliers),
|
|
95
|
+
offScaleGaps: offScaleGaps(flow, typedParent, visibleExtent)
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
function scopeAlignment(members) {
|
|
99
|
+
const leftEdges = clusters(members, box => box.x);
|
|
100
|
+
const sized = members.filter(box => !MOBILE_INTRINSIC_WIDTH_TYPES.includes(box.type ?? ''));
|
|
101
|
+
// Yalnız içerik genişlikli öğe varsa (metin listesi) sağ kenar onlardan okunur; sapma yine sayılmaz.
|
|
102
|
+
const rightEdges = clusters(sized.length > 0 ? sized : members, box => box.x + box.width);
|
|
103
|
+
// Eşitlikte en soldaki sol kenar ve en sağdaki sağ kenar kazanır: sözleşmenin normu `fill`dir.
|
|
104
|
+
const dominantLeft = [...leftEdges].sort((a, b) => b.elementIds.length - a.elementIds.length || a.x - b.x)[0]?.x;
|
|
105
|
+
const dominantRight = [...rightEdges].sort((a, b) => b.elementIds.length - a.elementIds.length || b.x - a.x)[0]?.x;
|
|
106
|
+
const widthOutliers = dominantLeft === undefined || dominantRight === undefined
|
|
107
|
+
? []
|
|
108
|
+
: unique(sized.filter(box => Math.abs(box.x + box.width - dominantRight) > WIDTH_TOLERANCE), box => box.elementId)
|
|
109
|
+
.map(box => ({ elementId: box.elementId, insetLeft: box.x - dominantLeft, insetRight: dominantRight - (box.x + box.width) }));
|
|
110
|
+
return { leftEdges, dominantLeft, dominantRight, widthOutliers };
|
|
111
|
+
}
|
|
112
|
+
const unique = (items, key) => items.filter((item, index) => items.findIndex(other => key(other) === key(item)) === index);
|
|
113
|
+
function clusters(members, edge) {
|
|
114
|
+
const result = [];
|
|
115
|
+
for (const box of members) {
|
|
116
|
+
const match = result.find(cluster => Math.abs(cluster.x - edge(box)) <= EDGE_TOLERANCE);
|
|
117
|
+
if (match && !match.elementIds.includes(box.elementId))
|
|
118
|
+
match.elementIds.push(box.elementId);
|
|
119
|
+
else if (!match)
|
|
120
|
+
result.push({ x: edge(box), elementIds: [box.elementId] });
|
|
121
|
+
}
|
|
122
|
+
return result.sort((a, b) => a.x - b.x);
|
|
123
|
+
}
|
|
124
|
+
function screenGutter(left, right, width) {
|
|
125
|
+
if (left === undefined || right === undefined)
|
|
126
|
+
return null;
|
|
127
|
+
const rightGutter = width - right;
|
|
128
|
+
return Math.abs(left - rightGutter) <= EDGE_TOLERANCE ? left : { left, right: rightGutter };
|
|
129
|
+
}
|
|
130
|
+
/** Aynı atanın alt alta duran ardışık çocukları arasındaki boşluk ölçekte mi (yan yana olanlar sayılmaz). */
|
|
131
|
+
function offScaleGaps(flow, typedParent, extent) {
|
|
132
|
+
const groups = new Map();
|
|
133
|
+
for (const box of flow) {
|
|
134
|
+
const key = typedParent(box);
|
|
135
|
+
groups.set(key, [...(groups.get(key) ?? []), box]);
|
|
136
|
+
}
|
|
137
|
+
return [...groups.values()].flatMap(group => {
|
|
138
|
+
const sorted = [...group].sort((a, b) => a.y - b.y);
|
|
139
|
+
return sorted.slice(1).flatMap((next, index) => {
|
|
140
|
+
const previous = sorted[index];
|
|
141
|
+
const gap = extent(next).top - extent(previous).bottom;
|
|
142
|
+
const stacked = gap >= -EDGE_TOLERANCE && previous.x < next.x + next.width && next.x < previous.x + previous.width;
|
|
143
|
+
return stacked && !SPACING_SCALE.some(step => Math.abs(step - gap) <= EDGE_TOLERANCE)
|
|
144
|
+
? [{ between: [previous.elementId, next.elementId], gap }]
|
|
145
|
+
: [];
|
|
146
|
+
});
|
|
147
|
+
});
|
|
148
|
+
}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MS-023 — tablo kaynaklı ucun ÇAĞRI PARAMETRESİ adı ve kök filtre alias'ı.
|
|
3
|
+
*
|
|
4
|
+
* Runtime kuralı (`PostgreSqlRelationHandler`): bir filtrenin alanı önce alias'tan çözülür —
|
|
5
|
+
* `GetField` alias'ı ya ANA ENTITY'nin SLUG'ıyla ya da bir join alias'ıyla eşler, başka hiçbir
|
|
6
|
+
* değeri tanımaz. Çözülen filtrenin çağrı parametresi ise `ResolveSingleField`'da
|
|
7
|
+
* `alias == mainEntity.Slug ? field.Slug : '<alias>.<field.Slug>'` olur. Tasarım zamanındaki
|
|
8
|
+
* `parameterScheme` üretimi (`Microservice.SourceEntityParameterScheme`) AYNI kuralı uygular, yani
|
|
9
|
+
* uç çağrılabilir olduğu sürece şemadaki ad runtime'ın beklediği addır.
|
|
10
|
+
*
|
|
11
|
+
* Kusur tek bir yerde doğar: kök filtrenin alias'ı entity slug'ından FARKLI verilirse
|
|
12
|
+
* (`sourceParameters.alias`'ı entity slug'ı sanmak yaygın hata) filtre runtime'da hiç çözülmez.
|
|
13
|
+
* Zorunlu filtrede sonuç 400 `FilterFieldNotResolvedException`'dır ve parametre HANGİ adla
|
|
14
|
+
* gönderilirse gönderilsin değişmez; `parameterScheme` ise nitelikli adı (`kredi.id`) gösterip
|
|
15
|
+
* "adı yanlış yazmışım" izlenimi verir.
|
|
16
|
+
*
|
|
17
|
+
* Ölçüm 2026-09-20 (`rehber-basit/tb119-get`, entity slug `tb119_urun`):
|
|
18
|
+
* alias 'u' → parameterScheme 'u.kod' · çağrı 400 FilterFieldNotResolvedException
|
|
19
|
+
* alias 'tb119_urun' → parameterScheme 'kod' · çağrı 200
|
|
20
|
+
*
|
|
21
|
+
* Bu yüzden gateway adı YENİDEN ADLANDIRMAZ (çalışmayan bir ucu çalışıyormuş gibi gösterirdi);
|
|
22
|
+
* çözülmeyen alias'ı işaretler ve düzeltince parametrenin hangi ad olacağını söyler.
|
|
23
|
+
*/
|
|
24
|
+
function collectJoinAliases(joins, into) {
|
|
25
|
+
for (const join of Array.isArray(joins) ? joins : []) {
|
|
26
|
+
if (!join || typeof join !== 'object')
|
|
27
|
+
continue;
|
|
28
|
+
const record = join;
|
|
29
|
+
if (typeof record.alias === 'string' && record.alias !== '')
|
|
30
|
+
into.add(record.alias);
|
|
31
|
+
collectJoinAliases(record.joins, into);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
export function unresolvedParameterAliases(definition) {
|
|
35
|
+
if (String(definition.source ?? '') !== 'table')
|
|
36
|
+
return [];
|
|
37
|
+
const sourceParameters = (definition.sourceParameters ?? {});
|
|
38
|
+
const joinAliases = new Set();
|
|
39
|
+
collectJoinAliases(sourceParameters.joins, joinAliases);
|
|
40
|
+
const scheme = Array.isArray(definition.parameterScheme) ? definition.parameterScheme : [];
|
|
41
|
+
return scheme
|
|
42
|
+
.map(entry => String(entry?.name ?? ''))
|
|
43
|
+
.filter(name => name.includes('.'))
|
|
44
|
+
.map(name => ({ name, alias: name.slice(0, name.indexOf('.')), field: name.slice(name.indexOf('.') + 1) }))
|
|
45
|
+
.filter(entry => entry.alias !== '' && !joinAliases.has(entry.alias));
|
|
46
|
+
}
|
|
47
|
+
export function parameterAliasWarning(entries, entitySlug) {
|
|
48
|
+
if (entries.length === 0)
|
|
49
|
+
return undefined;
|
|
50
|
+
const lines = entries.map(entry => `'${entry.name}' (alias '${entry.alias}')`);
|
|
51
|
+
const target = entitySlug === undefined
|
|
52
|
+
? "entity'nin SLUG'ı (entity_get ile okuyun; sourceParameters.alias DEĞİL)"
|
|
53
|
+
: `'${entitySlug}'`;
|
|
54
|
+
return `MS-023 — bu ucun ${lines.join(' · ')} parametresi runtime'da ÇÖZÜLMEZ: nitelikli adın alias'ı ne bir join alias'ı ne de entity slug'ı. `
|
|
55
|
+
+ `Kök filtrenin alias'ı ${target} olmak zorundadır (PostgreSqlRelationHandler.GetField); bu hâliyle zorunlu filtre 400 FilterFieldNotResolvedException verir ve parametreyi HANGİ adla gönderirseniz gönderin sonuç değişmez. `
|
|
56
|
+
+ `Alias düzeltilince çağrı parametresi nitelenmemiş alan slug'ı olur: ${entries.map(entry => `'${entry.field}'`).join(' · ')}. `
|
|
57
|
+
+ "Düzeltme: microservice_patch { op: 'replace', path: '/sourceParameters/filters/<i>/alias', value: '<entity slug>' }. Ölçüldü 2026-09-20.";
|
|
58
|
+
}
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* b-91 süreç madenciliği dışa aktarımının gateway tarafı denetimi. Resmî IEEE XES XSD'si ve OCEL 2.0
|
|
3
|
+
* JSON şeması için doğrulayıcı kütüphane (libxml, ajv) paket onayı ister; bu yüzden denetim
|
|
4
|
+
* **yapısaldır** ve yanıtta `validation: 'structural'` diye adlandırılır — dış aracın açıp
|
|
5
|
+
* açmayacağının kanıtı değildir, bozuk ya da eksik dosyayı ilk elden yakalar.
|
|
6
|
+
*/
|
|
7
|
+
const maxProblems = 20;
|
|
8
|
+
/** ISO 8601 tarih-saat; `Date.parse` tek başına '1' gibi değerleri de tarih sayar */
|
|
9
|
+
const isIsoTimestamp = (value) => !!value && /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}(:\d{2}(\.\d+)?)?(Z|[+-]\d{2}:?\d{2})$/.test(value) && !Number.isNaN(Date.parse(value));
|
|
10
|
+
/**
|
|
11
|
+
* XES: kök `log` ve `xes.version`, her iz `trace` en az bir `concept:name` taşır, her olayda
|
|
12
|
+
* `concept:name` ve ayrıştırılabilir `time:timestamp` vardır.
|
|
13
|
+
*/
|
|
14
|
+
export function checkXes(xml) {
|
|
15
|
+
const problems = [];
|
|
16
|
+
const root = /^\s*(?:<\?xml[^>]*\?>\s*)?(?:<!--[\s\S]*?-->\s*)*<log\b([^>]*)>/.exec(xml);
|
|
17
|
+
if (!root)
|
|
18
|
+
problems.push("kök öğe 'log' değil");
|
|
19
|
+
else if (!/\bxes\.version="[^"]+"/.test(root[1] ?? ''))
|
|
20
|
+
problems.push("'log' öğesinde xes.version yok");
|
|
21
|
+
const traces = [...xml.matchAll(/<trace\b[^>]*>([\s\S]*?)<\/trace>/g)];
|
|
22
|
+
let eventCount = 0;
|
|
23
|
+
traces.forEach((trace, traceIndex) => {
|
|
24
|
+
const body = trace[1] ?? '';
|
|
25
|
+
const events = [...body.matchAll(/<event\b[^>]*>([\s\S]*?)<\/event>/g)];
|
|
26
|
+
const traceAttributes = body.replace(/<event\b[^>]*>[\s\S]*?<\/event>/g, '');
|
|
27
|
+
const topLevelAttributes = traceAttributes.replace(/<(string|date|int|float|boolean|id|list|container)\b[^>]*[^/]>[\s\S]*?<\/\1>/g, '');
|
|
28
|
+
if (!/<string\b[^>]*key="concept:name"/.test(topLevelAttributes))
|
|
29
|
+
problems.push(`iz ${traceIndex + 1}: concept:name yok`);
|
|
30
|
+
events.forEach((event, eventIndex) => {
|
|
31
|
+
const eventBody = event[1] ?? '';
|
|
32
|
+
const timestamp = /<date\b[^>]*key="time:timestamp"[^>]*value="([^"]*)"/.exec(eventBody)?.[1];
|
|
33
|
+
eventCount++;
|
|
34
|
+
if (!/key="concept:name"/.test(eventBody))
|
|
35
|
+
problems.push(`iz ${traceIndex + 1} olay ${eventIndex + 1}: concept:name yok`);
|
|
36
|
+
if (!isIsoTimestamp(timestamp))
|
|
37
|
+
problems.push(`iz ${traceIndex + 1} olay ${eventIndex + 1}: time:timestamp yok ya da tarih değil`);
|
|
38
|
+
});
|
|
39
|
+
});
|
|
40
|
+
return { validated: problems.length === 0, validation: 'structural', problems: problems.slice(0, maxProblems), traceCount: traces.length, eventCount };
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* OCEL 2.0 JSON: dört dizi var; her nesne ve olay türü bildirilmiş; olay zamanı tarih; her
|
|
44
|
+
* ilişki var olan bir nesneye bakar.
|
|
45
|
+
*/
|
|
46
|
+
export function checkOcel(text) {
|
|
47
|
+
let parsed;
|
|
48
|
+
try {
|
|
49
|
+
parsed = JSON.parse(text);
|
|
50
|
+
}
|
|
51
|
+
catch {
|
|
52
|
+
return { validated: false, validation: 'structural', problems: ['JSON ayrıştırılamadı'], objectCount: 0, eventCount: 0 };
|
|
53
|
+
}
|
|
54
|
+
if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) {
|
|
55
|
+
return { validated: false, validation: 'structural', problems: ['kök değer nesne değil'], objectCount: 0, eventCount: 0 };
|
|
56
|
+
}
|
|
57
|
+
const log = parsed;
|
|
58
|
+
const problems = [];
|
|
59
|
+
for (const key of ['objectTypes', 'eventTypes', 'objects', 'events']) {
|
|
60
|
+
if (!Array.isArray(log[key]))
|
|
61
|
+
problems.push(`'${key}' dizisi yok`);
|
|
62
|
+
}
|
|
63
|
+
const entries = (value, name) => {
|
|
64
|
+
const list = Array.isArray(value) ? value : [];
|
|
65
|
+
const invalid = list.filter(entry => !entry || typeof entry !== 'object').length;
|
|
66
|
+
if (invalid > 0)
|
|
67
|
+
problems.push(`'${name}' içinde nesne olmayan ${invalid} kayıt`);
|
|
68
|
+
return list.filter(entry => entry && typeof entry === 'object');
|
|
69
|
+
};
|
|
70
|
+
const objects = entries(log.objects, 'objects');
|
|
71
|
+
const events = entries(log.events, 'events');
|
|
72
|
+
const objectTypes = new Set(entries(log.objectTypes, 'objectTypes').map(objectType => objectType.name));
|
|
73
|
+
const eventTypes = new Set(entries(log.eventTypes, 'eventTypes').map(eventType => eventType.name));
|
|
74
|
+
const objectIds = new Set(objects.map(ocelObject => ocelObject.id));
|
|
75
|
+
for (const ocelObject of objects) {
|
|
76
|
+
if (!objectTypes.has(ocelObject.type))
|
|
77
|
+
problems.push(`nesne ${ocelObject.id}: tür '${ocelObject.type}' objectTypes'ta yok`);
|
|
78
|
+
for (const relationship of ocelObject.relationships ?? []) {
|
|
79
|
+
if (!objectIds.has(relationship.objectId))
|
|
80
|
+
problems.push(`nesne ${ocelObject.id}: ilişki '${relationship.objectId}' nesnelerde yok`);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
for (const event of events) {
|
|
84
|
+
if (!eventTypes.has(event.type))
|
|
85
|
+
problems.push(`olay ${event.id}: tür '${event.type}' eventTypes'ta yok`);
|
|
86
|
+
if (!isIsoTimestamp(event.time))
|
|
87
|
+
problems.push(`olay ${event.id}: time ISO 8601 tarih-saat değil`);
|
|
88
|
+
for (const relationship of event.relationships ?? []) {
|
|
89
|
+
if (!objectIds.has(relationship.objectId))
|
|
90
|
+
problems.push(`olay ${event.id}: ilişki '${relationship.objectId}' nesnelerde yok`);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
return { validated: problems.length === 0, validation: 'structural', problems: problems.slice(0, maxProblems), objectCount: objects.length, eventCount: events.length };
|
|
94
|
+
}
|