@appaflytech/zeyrek-ai-mcp 0.0.28 → 0.0.30
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 +494 -34
- package/dist/knowledge/componentEvents.js +132 -7
- package/dist/knowledge/connectDataBind.js +819 -0
- package/dist/knowledge/connectDataDefinition.js +341 -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 +859 -0
- package/dist/knowledge/mobileComponentEvents.js +239 -0
- package/dist/knowledge/normalize.js +236 -67
- 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 +319 -21
- package/dist/knowledge/schemaGuard.js +11 -0
- package/dist/knowledge/styleResolve.js +50 -4
- package/dist/knowledge/styleResolveNative.js +527 -0
- package/dist/knowledge/themeResolve.js +3 -3
- 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 +333 -0
- package/dist/platform/capture/browser.js +1 -0
- package/dist/platform/{screenshot.js → capture/capture.js} +105 -519
- 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 +162 -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 +17 -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/mobile-app-shell-drawer.json +243 -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 +672 -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 +796 -0
- package/dist/resources/guides/bpm.md +686 -0
- package/dist/resources/guides/case.md +153 -0
- package/dist/resources/guides/component.md +91 -4
- 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/microservice.md +81 -2
- package/dist/resources/guides/middleware.md +44 -2
- package/dist/resources/guides/mobile-layout.md +193 -0
- package/dist/resources/guides/mobile.md +495 -0
- package/dist/resources/guides/page.md +454 -75
- package/dist/resources/guides/theme.md +8 -2
- 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 +143 -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 +21 -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 +22 -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/StepDto.json +22 -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 +143 -1
- package/dist/resources/schemas/ValidateProcessDefinitionQuery.json +1339 -0
- package/dist/server.js +27 -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 +7 -0
- package/dist/tools/components.js +118 -14
- package/dist/tools/connectData.js +449 -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 +2 -2
- 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 +122 -14
- 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 +1 -1
- package/dist/tools/microservices.js +77 -25
- package/dist/tools/mobile.js +221 -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 +27 -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 +37 -8
- 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/knowledge/rules.js
CHANGED
|
@@ -4,7 +4,9 @@ import { reservedTenantTables } from './reservedTables.js';
|
|
|
4
4
|
import { headersAccessNames, isHeadersAccessGuarded } from './flowGuards.js';
|
|
5
5
|
import { callsItself, functionCallRefs, isValidParameterName, mentionsAwait, nameProblem, sourceOf, callPrefix } from './functionGuards.js';
|
|
6
6
|
import { backendPolicyRules } from './backendConstraints.js';
|
|
7
|
+
import { accessPolicyRules } from './access.js';
|
|
7
8
|
import { themePolicyRules } from './themeRules.js';
|
|
9
|
+
import { processPolicyRules } from './process.js';
|
|
8
10
|
const terminalStepTypes = new Set(['response', 'convert', 'revert']);
|
|
9
11
|
const DESCRIPTION_EXEMPT_STEPS = new Set(['start']);
|
|
10
12
|
const stepKindMatrix = {
|
|
@@ -19,6 +21,7 @@ const stepKindMatrix = {
|
|
|
19
21
|
email: ['workflow', 'automation', 'middleware'],
|
|
20
22
|
'file-upload': ['workflow', 'automation', 'middleware'],
|
|
21
23
|
microfunction: ['workflow', 'automation', 'middleware'],
|
|
24
|
+
process: ['workflow', 'automation'],
|
|
22
25
|
variable: ['middleware'],
|
|
23
26
|
'set-user': ['middleware'],
|
|
24
27
|
setcookie: ['middleware'],
|
|
@@ -146,13 +149,22 @@ export function readsBeforeGuard(source, root) {
|
|
|
146
149
|
* code.ts:49). Eşleşme yoksa flow-engine.ts:79 sessizce durur. Studio tarafında da zincir
|
|
147
150
|
* `sourceBranch === 'always'` ile çizilir (EventActionsStart.svelte:47, EventActionsItem.svelte:298).
|
|
148
151
|
* condition dalları rule id'sidir (serbest), microservice'te 'success' yalnız eski şema için
|
|
149
|
-
* yaşatılır (microservice.ts:
|
|
152
|
+
* yaşatılır (microservice.ts:174 `isHasSuccess`). 'error' dalı 2026-09-26'dan beri motorun
|
|
153
|
+
* KENDİSİ tanır (ADR 0004, ZM-07/g-02): bağlıysa hata anında `always` hiç koşmaz, sürer
|
|
154
|
+
* (microservice.ts:182 `hasBranch`) — eski motor tanımadığı dalı hiç aramadığı için bu ekleme
|
|
155
|
+
* geriye dönük zararsızdır.
|
|
150
156
|
*/
|
|
151
157
|
const flowBranchNames = {
|
|
152
158
|
start: ['always'],
|
|
153
|
-
microservice: ['always', 'success'],
|
|
159
|
+
microservice: ['always', 'success', 'error'],
|
|
154
160
|
confirm: ['yes', 'no', 'always'],
|
|
155
161
|
function: ['always'],
|
|
162
|
+
haptic: ['always'],
|
|
163
|
+
location: ['always', 'error'],
|
|
164
|
+
'notification-permission': ['always'],
|
|
165
|
+
'notification-schedule': ['always', 'error'],
|
|
166
|
+
'notification-cancel': ['always'],
|
|
167
|
+
bpm: ['always', 'success', 'fail'],
|
|
156
168
|
end: []
|
|
157
169
|
};
|
|
158
170
|
/**
|
|
@@ -163,8 +175,16 @@ const flowBranchNames = {
|
|
|
163
175
|
*/
|
|
164
176
|
export const knownFlowStepTypes = new Set([
|
|
165
177
|
'start', 'end', 'query', 'setter', 'download', 'condition', 'redirect', 'setcookie', 'alert', 'confirm',
|
|
166
|
-
'microservice', 'validation', 'flow-trigger', 'storage', 'navigate', 'code', 'function'
|
|
178
|
+
'microservice', 'validation', 'flow-trigger', 'storage', 'navigate', 'code', 'function', 'share',
|
|
179
|
+
'haptic', 'location', 'notification-permission', 'notification-schedule', 'notification-cancel', 'bpm'
|
|
167
180
|
]);
|
|
181
|
+
/**
|
|
182
|
+
* Cihaz yeteneği adımlarının kapalı kümeleri — `@ng-apps/flow` `tasks/device/device.functions.ts`
|
|
183
|
+
* `HAPTIC_STYLES`/`LOCATION_ACCURACIES`'in aynası (`test/flow-step-parity.test.ts` bağlar); backend
|
|
184
|
+
* `FlowDtoValidator` aynı kümeyle 400 döner. Kök `docs/adr/0003-cihaz-yetenegi-adimlari.md`.
|
|
185
|
+
*/
|
|
186
|
+
export const hapticStyles = new Set(['success', 'warning', 'error', 'light', 'medium', 'heavy', 'selection']);
|
|
187
|
+
export const locationAccuracies = new Set(['low', 'balanced', 'high']);
|
|
168
188
|
const flowFreeBranchTypes = new Set(['condition']);
|
|
169
189
|
/** `condition.types.ts` ConditionArgumentRuleType — motorun tanıdığı üçlü. */
|
|
170
190
|
const conditionRuleTypes = new Set(['if', 'else-if', 'else']);
|
|
@@ -176,16 +196,17 @@ const conditionRuleIdPattern = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}
|
|
|
176
196
|
// Runtime.svelte'de HAM verilir (`argument: step.args`), yani bu alanlarda ifade değerlendirilmez.
|
|
177
197
|
const flowLiteralTextFields = new Map([
|
|
178
198
|
['alert', ['message', 'description']],
|
|
179
|
-
['confirm', ['message', 'title', 'confirmText', 'cancelText']]
|
|
199
|
+
['confirm', ['message', 'title', 'confirmText', 'cancelText']],
|
|
200
|
+
['share', ['title']],
|
|
201
|
+
['notification-schedule', ['name', 'title', 'path']],
|
|
202
|
+
['notification-cancel', ['name']]
|
|
180
203
|
]);
|
|
181
204
|
const flowExpressionLooking = /\$\{|\b(?:Steps|Page|Row|Widget|LocalStorage|state|self|item|event|error|cookie)\s*(?:\?\.|\.|\[)/;
|
|
182
|
-
// Barındırıcıya özel tipler
|
|
183
|
-
//
|
|
184
|
-
//
|
|
185
|
-
//
|
|
186
|
-
//
|
|
187
|
-
// (`ApplicationType.cs`) ve gateway hangisi olduğunu bilmez. Mobil ayağı uygulama tarafında
|
|
188
|
-
// kapanır (`ScreenRuntime.tsx`), lint'te değil.
|
|
205
|
+
// Barındırıcıya özel tipler BURADA kural üretmez. `navigate`i gateway normalizasyonu zaten
|
|
206
|
+
// `redirect`e çeviriyor (normalize.ts `normalizeFlowHostGaps`) — `redirect` iki barındırıcıda da
|
|
207
|
+
// koşar, yani vaka uyarılmaz, ORTADAN KALKAR. Gateway uygulamanın tipini platform profilinden bilir
|
|
208
|
+
// ve barındırıcı sözlüğü `platform/*.ts`'tedir (`profile.steps`); mobil farkları orada `MOB-005`
|
|
209
|
+
// olarak konuşulur (`platform/lint.ts`). Yeni mobil runtime `confirm`/`download`'ı kaydeder.
|
|
189
210
|
const flowUnregisteredStepTypes = new Set(['end', 'query']);
|
|
190
211
|
const flowSetterIterateeTypes = new Set(['change', 'fill']);
|
|
191
212
|
function flowStepList(value) {
|
|
@@ -305,25 +326,71 @@ function writesRolesWithoutSlug(entityId, parameters) {
|
|
|
305
326
|
}
|
|
306
327
|
const rolesSlugMessage = "Built-in 'roles' entity'sine slug YAZMADAN rol satırı ÜRETİLİYOR (operation create/upsert). Insert 200 döner, satır role_list'te görünür ve page.roleIds ile bağlanır — ama yetkilendirme SLUG karşılaştırır: platformun get-user sorgusu roles+user_roles join'inden slug dizisi üretir ve Authorized(user.roles) o rol için hiçbir zaman tutmaz. fields'a slug'ı ekleyin ('super-admin' slug'ını vermeyin — platform süper yöneticiyi o slug ile arar). Upsert'te slug'ı İFADEYLE üretmeyin: UPDATE yolunda ifade yeniden değerlendirilir ve var olan rolün slug'ı sessizce değişir (MS-016) — üretilen slug'ı yalnız create ucunda yazın. Rolü kullanıcıya bağlamak ayrı bir user_roles satırıdır.";
|
|
307
328
|
const contractKeys = ['returnDtoId', 'parameterDtoId', 'returnBehavior', 'parameterBehavior'];
|
|
308
|
-
|
|
329
|
+
/**
|
|
330
|
+
* Filtre işleci sözlüğü — backend `OperatorType` enum'u (paketlenmiş `Filter` şeması; eşitliği
|
|
331
|
+
* `test/filter-operators.test.ts` kilitler). Studio filtre ARAYÜZÜNÜN sözlüğü (`notContains`, `between`…,
|
|
332
|
+
* `componentCatalog.ts`) ayrı bir kavramdır: arayüz onları bu işleçlere çevirir.
|
|
333
|
+
*/
|
|
334
|
+
export const filterOperators = new Set(['eq', 'neq', 'lt', 'lte', 'gt', 'gte', 'contains', 'icontains', 'in', 'notin', 'startswith', 'endswith', 'istartswith', 'iendswith', 'isnull', 'isnotnull']);
|
|
335
|
+
/** Ajanların sık yazdığı yanlış adlar → doğru işleç. Yalnız ÖNERİLİR, sessizce çevrilmez. */
|
|
336
|
+
const filterOperatorSuggestions = {
|
|
337
|
+
ne: 'neq', '!=': 'neq', '<>': 'neq', noteq: 'neq', notequal: 'neq', notequals: 'neq', not_eq: 'neq',
|
|
338
|
+
'=': 'eq', '==': 'eq', equal: 'eq', equals: 'eq',
|
|
339
|
+
'<': 'lt', '<=': 'lte', '>': 'gt', '>=': 'gte',
|
|
340
|
+
like: 'contains', ilike: 'icontains', nin: 'notin', 'not in': 'notin', not_in: 'notin'
|
|
341
|
+
};
|
|
309
342
|
function flattenFilterTree(filters) {
|
|
310
343
|
return filters
|
|
311
344
|
.filter((filter) => Boolean(filter) && typeof filter === 'object')
|
|
312
345
|
.flatMap(filter => [filter, ...flattenFilterTree(asArray(filter.children))]);
|
|
313
346
|
}
|
|
347
|
+
/**
|
|
348
|
+
* Bilinmeyen işleçler, öneriyle: `status: 'ne' (neq mı demek istedin?)`. Grup çocukları da okunur. Ad
|
|
349
|
+
* büyük/küçük harfe DUYARLI eşlenir — backend de öyle eşler (`GetValueFromDescription` `==`); `NEQ`
|
|
350
|
+
* bilinmeyendir ve küçük harfli adı önerilir.
|
|
351
|
+
*/
|
|
352
|
+
function unknownFilterOperators(filters) {
|
|
353
|
+
return flattenFilterTree(filters)
|
|
354
|
+
.filter(filter => filter.operator !== undefined && filter.operator !== null && !filterOperators.has(String(filter.operator)))
|
|
355
|
+
.map(filter => {
|
|
356
|
+
const operator = String(filter.operator);
|
|
357
|
+
const lower = operator.toLowerCase();
|
|
358
|
+
const suggestion = filterOperators.has(lower) ? lower : filterOperatorSuggestions[lower];
|
|
359
|
+
return `${String(filter.field ?? filter.fieldSlug ?? filter.fieldId ?? '?')}: '${operator}'${suggestion ? ` (${suggestion} mı demek istedin?)` : ''}`;
|
|
360
|
+
});
|
|
361
|
+
}
|
|
362
|
+
const unknownOperatorConsequence = "Bilinmeyen işleç yola ve backend sürümüne göre ya 400 alır ya sessizce 'eq'e düşer ve sorgu yanlış sonuç döner (2026-09-27'den sonraki backend uç çağrısının `filters` parametresini 400 ile reddeder; workflow tablo adımı hâlâ sessizce 'eq' yapar); ad büyük/küçük harfe duyarlıdır — doğru işleci yazın";
|
|
314
363
|
function unknownOperatorFindings(ruleId, owner, filters) {
|
|
315
|
-
const unknownOperators =
|
|
316
|
-
.filter(filter => filter.operator !== undefined && filter.operator !== null && !filterOperators.has(String(filter.operator).toLowerCase()))
|
|
317
|
-
.map(filter => `${String(filter.fieldSlug ?? filter.fieldId ?? '?')}: '${String(filter.operator)}'`);
|
|
364
|
+
const unknownOperators = unknownFilterOperators(filters);
|
|
318
365
|
return unknownOperators.length > 0
|
|
319
366
|
? [{
|
|
320
367
|
ruleId,
|
|
321
368
|
severity: 'block',
|
|
322
|
-
message: `${owner}: bilinmeyen filtre
|
|
369
|
+
message: `${owner}: bilinmeyen filtre işleci — ${unknownOperators.join(', ')}. ${unknownOperatorConsequence}. Geçerli sözlük: ${[...filterOperators].join(' · ')} ('like' yok — metin araması için 'contains'/'icontains').`,
|
|
323
370
|
guide: 'ngapps://guides/microservice'
|
|
324
371
|
}]
|
|
325
372
|
: [];
|
|
326
373
|
}
|
|
374
|
+
/**
|
|
375
|
+
* Çalışma anında gelen filtre (`row_list.filters`, `microservice_execute` `filters` parametresi): dizi ya
|
|
376
|
+
* da JSON dizesi. Bilinmeyen işleç varsa hata METNİ döner — araç isteği göndermeden 400 verir
|
|
377
|
+
* (plans/mobil-plantcare-acigi g-03). Ayrıştırılamayan değer backend'e bırakılır.
|
|
378
|
+
*/
|
|
379
|
+
export function filterOperatorError(owner, filters) {
|
|
380
|
+
let parsed = filters;
|
|
381
|
+
if (typeof filters === 'string') {
|
|
382
|
+
try {
|
|
383
|
+
parsed = JSON.parse(filters);
|
|
384
|
+
}
|
|
385
|
+
catch {
|
|
386
|
+
return undefined;
|
|
387
|
+
}
|
|
388
|
+
}
|
|
389
|
+
const unknownOperators = Array.isArray(parsed) ? unknownFilterOperators(parsed) : [];
|
|
390
|
+
return unknownOperators.length > 0
|
|
391
|
+
? `${owner}: bilinmeyen filtre işleci — ${unknownOperators.join(', ')}. İstek GÖNDERİLMEDİ. ${unknownOperatorConsequence}. Geçerli sözlük: ${[...filterOperators].join(' · ')}.`
|
|
392
|
+
: undefined;
|
|
393
|
+
}
|
|
327
394
|
function isReadOperation(parameters) {
|
|
328
395
|
const operation = String(parameters?.operation ?? '').toLowerCase();
|
|
329
396
|
return !writeOperations.has(operation);
|
|
@@ -433,6 +500,27 @@ const isRecord = (value) => Boolean(value) && typeof value === 'object' && !Arra
|
|
|
433
500
|
const functionsGuide = 'ngapps://guides/functions';
|
|
434
501
|
/** FN-006: bu kadar dolu satırdan uzun gövde yorumsuz kalmaz; tek satırlık `return` yorum istemez. */
|
|
435
502
|
const FUNCTION_COMMENT_MIN_LINES = 5;
|
|
503
|
+
/**
|
|
504
|
+
* BPM-022: `process.*` sistem işlemi karşılığı olan `Page.bpm` üyeleri (backend
|
|
505
|
+
* `SystemOperationConstants`, zeyrek-backend/docs/bpm/sablon-paketleri.md §Sistem işlemleri).
|
|
506
|
+
* `start`, `cases`, `documents`, `analytics`, `exportLog`, `offline`, `legalHold(s)` karşılıksızdır;
|
|
507
|
+
* onları kullanan akışa uyarı vermek yol gösteremeyen gürültüdür.
|
|
508
|
+
*/
|
|
509
|
+
const PAGE_BPM_SYSTEM_MEMBERS = new Set([
|
|
510
|
+
'definitions', 'users', 'directory', 'tasks', 'task', 'instances', 'instance', 'timeline', 'signal',
|
|
511
|
+
'batch', 'batchOperation', 'delegations', 'delegate', 'delegation', 'handover', 'notifications',
|
|
512
|
+
'notification', 'preferences', 'override', 'report'
|
|
513
|
+
]);
|
|
514
|
+
/**
|
|
515
|
+
* BPM-022: karşılığı olan üyenin sistem işlemi OLMAYAN alt çağrıları (bpm.runtime.ts). Bunlar ve
|
|
516
|
+
* `batch('start', …)` eski yolda kalır; taze bağlam incelemesi (2026-09-23) üye düzeyindeki bakışın
|
|
517
|
+
* `instance(id).exportLedger()` gibi rehberin kendi önerdiği çağrıları uyardığını buldu.
|
|
518
|
+
*/
|
|
519
|
+
const PAGE_BPM_UNMAPPED_CALLS = new Set([
|
|
520
|
+
'tasks.calendar', 'task.comment', 'task.workedMinutes', 'task.assistant', 'instance.exportLedger',
|
|
521
|
+
'instance.watch', 'instance.unwatch', 'instance.comments', 'handover.history', 'override.conflicts',
|
|
522
|
+
'batchOperation.get'
|
|
523
|
+
]);
|
|
436
524
|
export const policyRules = [
|
|
437
525
|
{
|
|
438
526
|
id: 'MS-001',
|
|
@@ -712,7 +800,7 @@ export const policyRules = [
|
|
|
712
800
|
return [{
|
|
713
801
|
ruleId: 'WF-005',
|
|
714
802
|
severity: 'block',
|
|
715
|
-
message: `'${stepLabel(step)}' step'i (${step.type}) ${kind} tipinde kullanılamaz. İzin matrisi: variable/set-user/setcookie/redirect yalnız middleware; convert yalnız workflow; revert yalnız automation; response workflow+middleware.`,
|
|
803
|
+
message: `'${stepLabel(step)}' step'i (${step.type}) ${kind} tipinde kullanılamaz. İzin matrisi: variable/set-user/setcookie/redirect yalnız middleware; convert yalnız workflow; revert yalnız automation; response workflow+middleware; process workflow+automation.`,
|
|
716
804
|
guide: 'ngapps://guides/workflow'
|
|
717
805
|
}];
|
|
718
806
|
});
|
|
@@ -2251,6 +2339,25 @@ export const policyRules = [
|
|
|
2251
2339
|
}];
|
|
2252
2340
|
})
|
|
2253
2341
|
},
|
|
2342
|
+
{
|
|
2343
|
+
id: 'FLOW-028',
|
|
2344
|
+
module: 'flow',
|
|
2345
|
+
appliesTo: ['create', 'update'],
|
|
2346
|
+
sourceRef: "Yetki şablonu (access.json 1.7.0) 33 kod adımında platformun oturum uçlarına ham fetch atıyordu ('/api/' + project + '/runtime/' + application + '/auth/me' ve '/auth/sign-in'). Ham istek ng-apps-flow/src/api/instance.ts'in yaptıklarını atlar: X-Environment çözümü (resolveRuntimeEnvironment; elle ayrıştırma /preview şeridinde index -1 verir), CSRF başlığı (çerezli POST 403 CsrfTokenMismatchException — S2-37 · S3-7), 401'de tek refresh + yeniden deneme, ve ExecuteMicroserviceBehavior'ın microservice.execute kararı ile gölge günlüğü. Düzeltme 2026-09-22: giriş ve oturum bilgisi access.session.create / access.session.get system operation'larına taşındı (backend ADR 0004). Dış API'ye fetch meşrudur; kural yalnız göreli platform yollarını (/api/, /m/, /runtime/) görür",
|
|
2347
|
+
check: definition => flowAllSteps(definition)
|
|
2348
|
+
.filter(step => step.type === 'code')
|
|
2349
|
+
.flatMap((step) => {
|
|
2350
|
+
const body = String((step.args ?? {}).value ?? '');
|
|
2351
|
+
if (!/\bfetch\s*\(|XMLHttpRequest/.test(body) || !/['"`]\/(api|m|runtime)\/|\/runtime\//.test(body))
|
|
2352
|
+
return [];
|
|
2353
|
+
return [{
|
|
2354
|
+
ruleId: 'FLOW-028',
|
|
2355
|
+
severity: 'warn',
|
|
2356
|
+
message: `'${flowStepLabel(step)}' code adımı platformun kendi ucuna ham istek atıyor. Bu yol runtime'ın istek katmanını atlar: X-Environment elle kurulur, CSRF başlığı yoktur (çerezli POST 403 CsrfTokenMismatchException), 401'de oturum yenilenmez ve istek microservice.execute kararını ve gölge günlüğünü hiç görmez. Platform davranışını çağırmanın yolu microservice adımıdır: karşılığı olan bir system operation varsa (microservice_system_operation_list — giriş için access.session.create, oturumdaki kullanıcı için access.session.get) source:'system' bir uç açın ve akışa type:'microservice' adımı koyun; sonuç Steps.<adım>.data'dadır, kod adımı yalnız onu biçimler.`,
|
|
2357
|
+
guide: 'ngapps://guides/page'
|
|
2358
|
+
}];
|
|
2359
|
+
})
|
|
2360
|
+
},
|
|
2254
2361
|
{
|
|
2255
2362
|
id: 'FLOW-020',
|
|
2256
2363
|
module: 'flow',
|
|
@@ -2374,7 +2481,7 @@ export const policyRules = [
|
|
|
2374
2481
|
id: 'FLOW-022',
|
|
2375
2482
|
module: 'flow',
|
|
2376
2483
|
appliesTo: ['create', 'update'],
|
|
2377
|
-
sourceRef: "alert.ts `const { message, alertType, description } = task.argument` → toast.error(message, { description }) / dialog.alert(message); confirm.ts `dialog.confirm(message, { title, confirmText, cancelText })` (boş çerçeve metni dialog.functions.ts resolveDialogFrame'de sabit metne düşer). Argüman Runtime.svelte'de HAM geçer (`argument: step.args`) — bu alanlarda hiçbir ifade değerlendirilmez, yazılan metin ekranda harfi harfine görünür (2026-09-06·2 koşusu S-13'ün ikinci turu: rehber hata mesajını göstermek için alert adımını öneriyordu)",
|
|
2484
|
+
sourceRef: "alert.ts `const { message, alertType, description } = task.argument` → toast.error(message, { description }) / dialog.alert(message); confirm.ts `dialog.confirm(message, { title, confirmText, cancelText })` (boş çerçeve metni dialog.functions.ts resolveDialogFrame'de sabit metne düşer). Argüman Runtime.svelte'de HAM geçer (`argument: step.args`) — bu alanlarda hiçbir ifade değerlendirilmez, yazılan metin ekranda harfi harfine görünür (2026-09-06·2 koşusu S-13'ün ikinci turu: rehber hata mesajını göstermek için alert adımını öneriyordu). share adımında (2026-09-25) yalnız `title` düz metindir: @ng-apps/flow resolveShareContent onu olduğu gibi verir, `message` ve `path` ifadedir",
|
|
2378
2485
|
check: definition => flowAllSteps(definition)
|
|
2379
2486
|
.flatMap((step) => {
|
|
2380
2487
|
const type = String(step.type ?? '');
|
|
@@ -2387,16 +2494,164 @@ export const policyRules = [
|
|
|
2387
2494
|
.map(field => ({
|
|
2388
2495
|
ruleId: 'FLOW-022',
|
|
2389
2496
|
severity: 'warn',
|
|
2390
|
-
message:
|
|
2497
|
+
message: type.startsWith('notification-')
|
|
2498
|
+
? `'${flowStepLabel(step)}' ${type} adımının '${field}' alanı ifade içeriyor ama bu alan DÜZ METİNDİR (kök ADR 0003): ${field === 'name' ? 'bildirimin kimliği' : field === 'path' ? 'açılacak sayfanın slug\'ı' : 'bildirim başlığı'} olduğu gibi kullanılır, ifade değerlendirilmez. Değişen içeriği ifade olan 'message' (gövde) ya da 'query' (sayfanın sorgusu) alanına yazın.`
|
|
2499
|
+
: type === 'share'
|
|
2500
|
+
? `'${flowStepLabel(step)}' share adımının '${field}' alanı ifade içeriyor ama bu alan DÜZ METİNDİR: başlık paylaşım penceresine olduğu gibi gider (iOS'ta e-posta konusu, Android'de seçici başlığı), ifade değerlendirilmez. Değişen içeriği ifade olan 'message' alanına yazın — ör. message: "'Şuna bak: ' + Page.properties.name"; bağlantı 'path' alanındadır.`
|
|
2501
|
+
: `'${flowStepLabel(step)}' ${type} adımının '${field}' alanı ifade içeriyor ama bu alan DÜZ METİNDİR: ${type}.ts task.argument.${field}'i doğrudan ekrana geçirir ve args hiçbir yerde değerlendirilmez (Runtime.svelte \`argument: step.args\`). Kullanıcı ifadenin sonucunu değil ifadenin KENDİSİNİ görür. Ucun mesajını göstermek için bir setter (ya da code) adımı kullanın — value { type: 'code', ref: 'Steps.error?.message' }; hata metninin nerede durduğu page.md §hata yolu'nda.`,
|
|
2391
2502
|
guide: 'ngapps://guides/page'
|
|
2392
2503
|
}));
|
|
2393
2504
|
})
|
|
2394
2505
|
},
|
|
2506
|
+
{
|
|
2507
|
+
id: 'FLOW-035',
|
|
2508
|
+
module: 'flow',
|
|
2509
|
+
appliesTo: ['create', 'update'],
|
|
2510
|
+
sourceRef: "share adımının argümanı StepManagementArgument'ın Title/Message/Path alanlarında saklanır (StepType.cs `share`, kök docs/adr/0001-share-akis-adimi.md); url/link/text gibi anahtarlar sözleşmede YOKTUR ve platform onları 200 ile sessizce düşürür — adım hiçbir şey paylaşmadan akış sürer. `message` ve `path` İFADEDİR (@ng-apps/flow resolveShareContent → runCode): tırnaksız 'https://…' JS'te etiket + yorumdur, undefined döner",
|
|
2511
|
+
check: definition => flowAllSteps(definition)
|
|
2512
|
+
.filter(step => step.type === 'share')
|
|
2513
|
+
.flatMap((step) => {
|
|
2514
|
+
const args = (step.args ?? {});
|
|
2515
|
+
const filled = (key) => typeof args[key] === 'string' && String(args[key]).trim().length > 0;
|
|
2516
|
+
const findings = [];
|
|
2517
|
+
const strayLinks = ['url', 'link', 'href'].filter(filled);
|
|
2518
|
+
const strayTexts = ['text', 'body', 'content'].filter(filled);
|
|
2519
|
+
if (strayLinks.length > 0) {
|
|
2520
|
+
findings.push({
|
|
2521
|
+
ruleId: 'FLOW-035',
|
|
2522
|
+
severity: filled('path') ? 'warn' : 'block',
|
|
2523
|
+
message: `'${flowStepLabel(step)}' share adımının bağlantısı args.${strayLinks.join('/args.')} alanında: bu anahtar StepManagementArgument sözleşmesinde YOK, platform onu 200 ile sessizce düşürür ve adım bağlantısız kalır. Bağlantıyı args.path'e ifade olarak yazın — ör. "'https://ornek.app/urun/' + Page.queryParams.id".`,
|
|
2524
|
+
guide: 'ngapps://guides/page'
|
|
2525
|
+
});
|
|
2526
|
+
}
|
|
2527
|
+
if (strayTexts.length > 0) {
|
|
2528
|
+
findings.push({
|
|
2529
|
+
ruleId: 'FLOW-035',
|
|
2530
|
+
severity: filled('message') ? 'warn' : 'block',
|
|
2531
|
+
message: `'${flowStepLabel(step)}' share adımının metni args.${strayTexts.join('/args.')} alanında: bu anahtar StepManagementArgument sözleşmesinde YOK, platform onu sessizce düşürür. Metni args.message'a ifade olarak yazın — ör. "'Şuna bak: ' + Page.properties.name".`,
|
|
2532
|
+
guide: 'ngapps://guides/page'
|
|
2533
|
+
});
|
|
2534
|
+
}
|
|
2535
|
+
for (const key of ['message', 'path']) {
|
|
2536
|
+
const value = typeof args[key] === 'string' ? String(args[key]).trim() : '';
|
|
2537
|
+
if (/^[a-z][a-z\d+\-.]*:\/\//i.test(value)) {
|
|
2538
|
+
findings.push({
|
|
2539
|
+
ruleId: 'FLOW-035',
|
|
2540
|
+
severity: 'block',
|
|
2541
|
+
message: `'${flowStepLabel(step)}' share adımının '${key}' alanı tırnaksız bir adres: '${value}'. Bu alan İFADEDİR — tırnaksız adres JS'te etiket + yorumdur, undefined döner ve hiçbir şey paylaşılmaz. Dize olarak yazın: "'${value}'".`,
|
|
2542
|
+
guide: 'ngapps://guides/page'
|
|
2543
|
+
});
|
|
2544
|
+
}
|
|
2545
|
+
}
|
|
2546
|
+
return findings;
|
|
2547
|
+
})
|
|
2548
|
+
},
|
|
2549
|
+
{
|
|
2550
|
+
id: 'FLOW-029',
|
|
2551
|
+
module: 'flow',
|
|
2552
|
+
appliesTo: ['create', 'update'],
|
|
2553
|
+
sourceRef: "Cihaz yeteneği adımlarının argümanı StepManagementArgument'ın mevcut alanlarındadır (kök docs/adr/0003-cihaz-yetenegi-adimlari.md): haptic.value = stil, location.value = doğruluk, location.isRaw = adres çözülmesin. İkisi de DÜZ METİNDİR (@ng-apps/flow hapticStyle/locationAccuracy), kümede olmayan değer backend FlowDtoValidator'da 400 alır; style/accuracy/reverseGeocode gibi anahtarlar sözleşmede YOKTUR ve platform onları 200 ile sessizce düşürür — adım varsayılanla (medium / balanced / adresli) koşar",
|
|
2554
|
+
check: definition => flowAllSteps(definition)
|
|
2555
|
+
.filter(step => step.type === 'haptic' || step.type === 'location')
|
|
2556
|
+
.flatMap((step) => {
|
|
2557
|
+
const args = (step.args ?? {});
|
|
2558
|
+
// Backend (FlowDtoValidator) yalnız tamamen boşluk olanı boş sayar; kümeyle karşılaştırmayı kırpmadan yapar.
|
|
2559
|
+
const raw = typeof args.value === 'string' ? args.value : '';
|
|
2560
|
+
const value = raw.trim() ? raw : '';
|
|
2561
|
+
const haptic = step.type === 'haptic';
|
|
2562
|
+
const allowed = haptic ? hapticStyles : locationAccuracies;
|
|
2563
|
+
const field = haptic ? 'stil' : 'doğruluk';
|
|
2564
|
+
const findings = [];
|
|
2565
|
+
const stray = (haptic ? ['style', 'type', 'feedback'] : ['accuracy', 'precision'])
|
|
2566
|
+
.filter(key => args[key] !== undefined && args[key] !== null && args[key] !== '');
|
|
2567
|
+
if (value && !allowed.has(value)) {
|
|
2568
|
+
const unquoted = value.replace(/^['"`](.*)['"`]$/, '$1');
|
|
2569
|
+
const trimmed = value.trim();
|
|
2570
|
+
findings.push({
|
|
2571
|
+
ruleId: 'FLOW-029',
|
|
2572
|
+
severity: 'block',
|
|
2573
|
+
message: allowed.has(unquoted)
|
|
2574
|
+
? `'${flowStepLabel(step)}' ${step.type} adımının ${field} değeri tırnaklı yazılmış: ${value}. Bu alan DÜZ METİNDİR, ifade değil — args.value: '${unquoted}' yazın.`
|
|
2575
|
+
: allowed.has(trimmed)
|
|
2576
|
+
? `'${flowStepLabel(step)}' ${step.type} adımının ${field} değerinin çevresinde boşluk var: '${value}'. Platform kırpmadan karşılaştırır ve 400 döner — args.value: '${trimmed}' yazın.`
|
|
2577
|
+
: `'${flowStepLabel(step)}' ${step.type} adımının ${field} değeri '${value}' tanınmıyor; platform 400 döner. Kullanılabilir: ${[...allowed].join(', ')} (boş → ${haptic ? 'medium' : 'balanced'}).`,
|
|
2578
|
+
guide: 'ngapps://guides/mobile'
|
|
2579
|
+
});
|
|
2580
|
+
}
|
|
2581
|
+
if (stray.length > 0) {
|
|
2582
|
+
findings.push({
|
|
2583
|
+
ruleId: 'FLOW-029',
|
|
2584
|
+
severity: value ? 'warn' : 'block',
|
|
2585
|
+
message: `'${flowStepLabel(step)}' ${step.type} adımının ${field} değeri args.${stray.join('/args.')} alanında: bu anahtar StepManagementArgument sözleşmesinde YOK, platform onu 200 ile sessizce düşürür ve adım varsayılanla (${haptic ? 'medium' : 'balanced'}) koşar. Değeri args.value'ya düz metin olarak yazın — ör. args.value: '${haptic ? 'success' : 'high'}'.`,
|
|
2586
|
+
guide: 'ngapps://guides/mobile'
|
|
2587
|
+
});
|
|
2588
|
+
}
|
|
2589
|
+
if (!haptic && ['reverseGeocode', 'address', 'geocode'].some(key => args[key] !== undefined)) {
|
|
2590
|
+
findings.push({
|
|
2591
|
+
ruleId: 'FLOW-029',
|
|
2592
|
+
severity: 'warn',
|
|
2593
|
+
message: `'${flowStepLabel(step)}' location adımında adres anahtarı sözleşmede YOK ve sessizce düşer. Adres varsayılan olarak çözülür (cihazda; web'de hep null); kapatmak için args.isRaw: true yazın.`,
|
|
2594
|
+
guide: 'ngapps://guides/mobile'
|
|
2595
|
+
});
|
|
2596
|
+
}
|
|
2597
|
+
return findings;
|
|
2598
|
+
})
|
|
2599
|
+
},
|
|
2600
|
+
{
|
|
2601
|
+
id: 'FLOW-030',
|
|
2602
|
+
module: 'flow',
|
|
2603
|
+
appliesTo: ['create', 'update'],
|
|
2604
|
+
sourceRef: "Yerel bildirim adımlarının argümanı StepManagementArgument'ın mevcut alanlarındadır (kök docs/adr/0003-cihaz-yetenegi-adimlari.md): notification-schedule name = kimlik (düz metin, aynı kimlik eskisinin yerini alır), title düz metin, message ve value İFADE, path sayfa; notification-cancel name = kimlik ('*' hepsi). Zamanın türü value'nun sonucunun şeklinden okunur (@ng-apps/flow notificationTrigger): 'HH:mm' her gün, { weekday: 1-7, time: 'HH:mm' } her hafta (1 = Pazartesi), ISO tarih-saat bir kez. Eksik name/value backend FlowDtoValidator'da 400 alır; id/at/daily/weekly/body gibi anahtarlar sözleşmede YOKTUR ve platform onları 200 ile sessizce düşürür",
|
|
2605
|
+
check: definition => flowAllSteps(definition)
|
|
2606
|
+
.filter(step => step.type === 'notification-schedule' || step.type === 'notification-cancel')
|
|
2607
|
+
.flatMap((step) => {
|
|
2608
|
+
const args = (step.args ?? {});
|
|
2609
|
+
const text = (key) => typeof args[key] === 'string' ? String(args[key]).trim() : '';
|
|
2610
|
+
const findings = [];
|
|
2611
|
+
const schedule = step.type === 'notification-schedule';
|
|
2612
|
+
const stray = ['id', 'identifier', 'at', 'daily', 'weekly', 'time', 'body', 'text']
|
|
2613
|
+
.filter(key => args[key] !== undefined && args[key] !== null && args[key] !== '');
|
|
2614
|
+
if (!text('name')) {
|
|
2615
|
+
findings.push({
|
|
2616
|
+
ruleId: 'FLOW-030',
|
|
2617
|
+
severity: 'block',
|
|
2618
|
+
message: `'${flowStepLabel(step)}' ${step.type} adımının kimliği yok: args.name düz metin olarak yazılmalı (ör. 'sulama')${schedule ? ' — aynı kimlikle yeniden planlama eskisinin yerini alır' : " — '*' bütün planlı bildirimleri siler"}. Platform eksik kimliği 400 ile reddeder.`,
|
|
2619
|
+
guide: 'ngapps://guides/mobile'
|
|
2620
|
+
});
|
|
2621
|
+
}
|
|
2622
|
+
if (schedule && !text('value')) {
|
|
2623
|
+
findings.push({
|
|
2624
|
+
ruleId: 'FLOW-030',
|
|
2625
|
+
severity: 'block',
|
|
2626
|
+
message: `'${flowStepLabel(step)}' notification-schedule adımının zamanı yok: args.value bir İFADEDİR ve sonucu 'HH:mm' (her gün), { weekday: 1-7, time: 'HH:mm' } (her hafta, 1 = Pazartesi) ya da ISO tarih-saattir (bir kez) — ör. "tp_saat.value" ya da "'08:30'".`,
|
|
2627
|
+
guide: 'ngapps://guides/mobile'
|
|
2628
|
+
});
|
|
2629
|
+
}
|
|
2630
|
+
const literal = /^'([^']*)'$/.exec(text('value'))?.[1];
|
|
2631
|
+
if (schedule && literal !== undefined && !/^([01]?\d|2[0-3]):[0-5]\d$/.test(literal) && Number.isNaN(Date.parse(literal))) {
|
|
2632
|
+
findings.push({
|
|
2633
|
+
ruleId: 'FLOW-030',
|
|
2634
|
+
severity: 'block',
|
|
2635
|
+
message: `'${flowStepLabel(step)}' notification-schedule adımının zamanı '${literal}' okunamaz: 'HH:mm' (00:00–23:59), ISO tarih-saat ya da { weekday, time } nesnesi yazın. Çalışmada adım error dalına düşer (status: 'invalid').`,
|
|
2636
|
+
guide: 'ngapps://guides/mobile'
|
|
2637
|
+
});
|
|
2638
|
+
}
|
|
2639
|
+
if (stray.length > 0) {
|
|
2640
|
+
findings.push({
|
|
2641
|
+
ruleId: 'FLOW-030',
|
|
2642
|
+
severity: 'warn',
|
|
2643
|
+
message: `'${flowStepLabel(step)}' ${step.type} adımında args.${stray.join('/args.')} sözleşmede YOK ve platform onu sessizce düşürür. Kimlik args.name, zaman args.value (ifade), metin args.message (ifade), başlık args.title.`,
|
|
2644
|
+
guide: 'ngapps://guides/mobile'
|
|
2645
|
+
});
|
|
2646
|
+
}
|
|
2647
|
+
return findings;
|
|
2648
|
+
})
|
|
2649
|
+
},
|
|
2395
2650
|
{
|
|
2396
2651
|
id: 'FLOW-023',
|
|
2397
2652
|
module: 'flow',
|
|
2398
2653
|
appliesTo: ['create', 'update'],
|
|
2399
|
-
sourceRef: "Adım tipi sözlüğü backend'indir (StepType.cs) ama koşan tipler barındırıcıdan gelir: flow-engine.ts start·setter·code·condition·microservice·validation·flow-trigger register eder, web Runtime.svelte alert·confirm·redirect·download·storage·setcookie ekler, mobil
|
|
2654
|
+
sourceRef: "Adım tipi sözlüğü backend'indir (StepType.cs) ama koşan tipler barındırıcıdan gelir: flow-engine.ts start·setter·code·condition·microservice·validation·flow-trigger register eder, web Runtime.svelte alert·confirm·redirect·download·storage·setcookie ekler, mobil @ng-apps/ui-native runtime-tasks.ts alert·confirm·redirect·navigate·storage·setcookie·download ekler. navigate web'de YOKTUR; end ve query hiçbir barındırıcıda yoktur (Studio kataloğu da bunu visibility ile söyler: navigate 'mobile', End eklenebilir adımlar arasında değil — EventActionsAdd.svelte). Register edilmemiş tipe gelen akış SESSİZCE durur: flow-engine.ts process() callback bulamazsa hiçbir şey yapmaz (next yok, completed/stopped yok, hata yok, log yok), run() Promise'i çözülmez ve flowRunFinished() hiç koşmaz — __ngAppsFlowActivity.running sıfıra inmez, page_screenshot'ın hazır bekleyişi her eylemde bütçesini sonuna kadar yakar",
|
|
2400
2655
|
check: definition => {
|
|
2401
2656
|
const steps = flowAllSteps(definition);
|
|
2402
2657
|
// Studio her akış tuvaline dekoratif bir End düğümü koyar ve onu sildirmez; zararlı olan
|
|
@@ -2722,6 +2977,29 @@ export const policyRules = [
|
|
|
2722
2977
|
return findings;
|
|
2723
2978
|
})
|
|
2724
2979
|
},
|
|
2980
|
+
{
|
|
2981
|
+
id: 'BPM-022',
|
|
2982
|
+
module: 'flow',
|
|
2983
|
+
appliesTo: ['create', 'update'],
|
|
2984
|
+
sourceRef: "bpm-management 1.1.0 sayfaları süreç çalışma zamanına yalnız source: system mikroservislerle gider (plans/bpm-sistem-mikroservis D-1 a, ADR-0004); Page.bpm aynı uçlara mikroservissiz gider, rehberi okuyan ajanlar yine de yazdı (plans/bpm/demo koşuları)",
|
|
2985
|
+
check: definition => flowAllSteps(definition).flatMap((step) => {
|
|
2986
|
+
const calls = [...new Set(flowExpressionSites(step)
|
|
2987
|
+
.flatMap(site => [...site.expression.matchAll(/\bPage\s*\.\s*bpm\b(?:\s*\.\s*([A-Za-z_$][\w$]*))?(\s*\((?:[^()]|\([^()]*\))*\))?(?:\s*\.\s*([A-Za-z_$][\w$]*))?/g)])
|
|
2988
|
+
.filter(([, member = '', callArguments = '', subCall]) => member === ''
|
|
2989
|
+
|| (PAGE_BPM_SYSTEM_MEMBERS.has(member)
|
|
2990
|
+
&& !(subCall && PAGE_BPM_UNMAPPED_CALLS.has(`${member}.${subCall}`))
|
|
2991
|
+
&& !(member === 'batch' && /^\s*\(\s*['"]start['"]/.test(callArguments))))
|
|
2992
|
+
.map(([, member, , subCall]) => (member ? `Page.bpm.${member}${subCall ? `(…).${subCall}` : ''}` : 'Page.bpm')))];
|
|
2993
|
+
return calls.length === 0
|
|
2994
|
+
? []
|
|
2995
|
+
: [{
|
|
2996
|
+
ruleId: 'BPM-022',
|
|
2997
|
+
severity: 'warn',
|
|
2998
|
+
message: `'${flowStepLabel(step)}' adımı Page.bpm çağırıyor (${calls.join(', ')}). Sayfa akışı süreç çalışma zamanıyla source: system mikroservis üzerinden konuşur: microservice_system_operation_list ile process.* işlemini seçin, microservice_create { source: 'system', systemOperation } ile ucu kurun ve çağrıyı bir microservice adımına taşıyın; sonucu okuyan code adımı Steps.<adım>.data / .error'dan okur. Page.bpm eski yoldur — izin ve görünürlük kararı sistem işleminde aynı servisten gelir, uç Studio'da ve microservice_execute ile görünür.`,
|
|
2999
|
+
guide: 'ngapps://guides/bpm'
|
|
3000
|
+
}];
|
|
3001
|
+
})
|
|
3002
|
+
},
|
|
2725
3003
|
{
|
|
2726
3004
|
id: 'MS-013',
|
|
2727
3005
|
module: 'microservice',
|
|
@@ -2963,10 +3241,30 @@ export const policyRules = [
|
|
|
2963
3241
|
}];
|
|
2964
3242
|
}
|
|
2965
3243
|
},
|
|
3244
|
+
{
|
|
3245
|
+
id: 'MW-006',
|
|
3246
|
+
module: 'workflow',
|
|
3247
|
+
appliesTo: ['create', 'update', 'preview'],
|
|
3248
|
+
sourceRef: "Kimlik devri değeri triggerSource'ta DEĞİL context'te durur: WorkflowContext.SetTriggerSource triggerSource'a { type, id, name, slug, roles, pathAndQuery }, context'e { type, name, roles, path, impersonatedUserId, language } yazar (WorkflowContext.cs:216-235). triggerSource.impersonatedUserId her zaman undefined okunur ve önizleme hep jeton yoluna düşer (ZM-22, koşu 37)",
|
|
3249
|
+
check: (definition) => {
|
|
3250
|
+
const readers = workflowSteps(definition).filter(step => /\btriggerSource\s*\.\s*impersonatedUserId\b/.test(JSON.stringify(step.rule ?? {})));
|
|
3251
|
+
return readers.length === 0
|
|
3252
|
+
? []
|
|
3253
|
+
: [{
|
|
3254
|
+
ruleId: 'MW-006',
|
|
3255
|
+
severity: 'warn',
|
|
3256
|
+
message: `'${readers.map(stepLabel).join(', ')}' kimlik devrini triggerSource.impersonatedUserId'den okuyor; bu alan yok, değer hep undefined gelir. Doğrusu context.impersonatedUserId — ve yalnız start adımının impersonation dalında: değer yalnız doğrulanmış Studio önizlemesinde dolar, başka yerde yetki kararı için kullanılmaz (platform_guide('middleware') Tarif 1c).`,
|
|
3257
|
+
guide: 'ngapps://guides/middleware'
|
|
3258
|
+
}];
|
|
3259
|
+
}
|
|
3260
|
+
},
|
|
2966
3261
|
// Backend doğrulayıcılarının aynası (ENT-015…018, MS-017, WF-026, QRY/VIEW/DTO/ROW/DS/PRJ/TR/FLD/FILE/EXT):
|
|
2967
3262
|
// tek liste `backendConstraints.ts`'te durur, buradan yalnız politika motoruna katılır.
|
|
2968
3263
|
...themePolicyRules,
|
|
2969
|
-
...backendPolicyRules()
|
|
3264
|
+
...backendPolicyRules(),
|
|
3265
|
+
// Organizasyon şeması, erişim politikası ve alan koruması (ORG/ACC/PROT) — `access.ts`.
|
|
3266
|
+
...accessPolicyRules(),
|
|
3267
|
+
...processPolicyRules()
|
|
2970
3268
|
];
|
|
2971
3269
|
/**
|
|
2972
3270
|
* WF-MF-001'in ikinci yarısı. Bir adımın koşup koşmayacağı yalnız GÖVDEDEN anlaşılamaz: karar
|
|
@@ -49,6 +49,13 @@ function referenceName(node) {
|
|
|
49
49
|
return referenceName(allOf[0]);
|
|
50
50
|
return undefined;
|
|
51
51
|
}
|
|
52
|
+
/**
|
|
53
|
+
* `System.Text.Json.Nodes.JsonNode` gibi serbest JSON değerlerinin üretilen şeması katıdır
|
|
54
|
+
* (`options/parent/root`); içine inilirse meşru anahtarlar "platform yok sayıyor" diye raporlanır.
|
|
55
|
+
*/
|
|
56
|
+
function isFreeFormJson(node) {
|
|
57
|
+
return /(^|.)Json(Node|Object|Array|Value|Element)$/.test(referenceName(node) ?? '');
|
|
58
|
+
}
|
|
52
59
|
function resolve(document, node) {
|
|
53
60
|
if (!node || typeof node !== 'object')
|
|
54
61
|
return undefined;
|
|
@@ -76,6 +83,8 @@ function walk(document, schema, value, path, into) {
|
|
|
76
83
|
if (!schema || !value || typeof value !== 'object')
|
|
77
84
|
return;
|
|
78
85
|
if (Array.isArray(value)) {
|
|
86
|
+
if (isFreeFormJson(schema.items))
|
|
87
|
+
return;
|
|
79
88
|
const items = resolve(document, schema.items);
|
|
80
89
|
value.forEach((entry, index) => walk(document, items, entry, `${path}[${index}]`, into));
|
|
81
90
|
return;
|
|
@@ -95,6 +104,8 @@ function walk(document, schema, value, path, into) {
|
|
|
95
104
|
into.push(child);
|
|
96
105
|
continue;
|
|
97
106
|
}
|
|
107
|
+
if (isFreeFormJson(properties[key]))
|
|
108
|
+
continue;
|
|
98
109
|
walk(document, resolve(document, properties[key]), entry, child, into);
|
|
99
110
|
}
|
|
100
111
|
}
|
|
@@ -154,6 +154,24 @@ const resolveShadow = (shadow) => {
|
|
|
154
154
|
return undefined;
|
|
155
155
|
return SHADOW_VALUES[shadow] ?? String(shadow);
|
|
156
156
|
};
|
|
157
|
+
/**
|
|
158
|
+
* Döndür / ölçekle / kaydır alanlarının kabul aralığı — frontend `style-groups/transform-fields.ts`
|
|
159
|
+
* `TRANSFORM_RANGES`'in kopyası (altın vakalar kilitler). Aralık dışı ve birimli değer yazılmaz.
|
|
160
|
+
*/
|
|
161
|
+
export const TRANSFORM_RANGES = {
|
|
162
|
+
rotate: [-360, 360],
|
|
163
|
+
scale: [10, 500],
|
|
164
|
+
translateX: [-1000, 1000],
|
|
165
|
+
translateY: [-1000, 1000]
|
|
166
|
+
};
|
|
167
|
+
/** Sayı ya da sayısal metin, aralığın içinde; aksi `undefined` — frontend `transformValue`. */
|
|
168
|
+
export const transformValue = (value, field) => {
|
|
169
|
+
if (typeof value !== 'number' && (typeof value !== 'string' || value.trim() === ''))
|
|
170
|
+
return undefined;
|
|
171
|
+
const parsed = Number(value);
|
|
172
|
+
const [min, max] = TRANSFORM_RANGES[field];
|
|
173
|
+
return Number.isFinite(parsed) && parsed >= min && parsed <= max ? parsed : undefined;
|
|
174
|
+
};
|
|
157
175
|
/**
|
|
158
176
|
* Grupların alan listeleri — frontend `style-groups/style-group-schemas.ts`
|
|
159
177
|
* `STYLE_GROUP_FIELDS`'in kopyası. Parite testi `components.json`'daki gerçek
|
|
@@ -170,7 +188,11 @@ export const STYLE_GROUP_FIELDS = {
|
|
|
170
188
|
'borderLeft',
|
|
171
189
|
'shadow',
|
|
172
190
|
'contentOverflow',
|
|
173
|
-
'opacity'
|
|
191
|
+
'opacity',
|
|
192
|
+
'rotate',
|
|
193
|
+
'scale',
|
|
194
|
+
'translateX',
|
|
195
|
+
'translateY'
|
|
174
196
|
],
|
|
175
197
|
typographyStyle: [
|
|
176
198
|
'textColor',
|
|
@@ -220,6 +242,21 @@ export function composeTypedStyle(input, ext) {
|
|
|
220
242
|
if (opacity !== undefined && opacity >= 0 && opacity < 100) {
|
|
221
243
|
groups.opacity = String(opacity / 100);
|
|
222
244
|
}
|
|
245
|
+
// Döndür / ölçekle / kaydır (ADR 0005): kaydır → döndür → ölçekle, köken merkez;
|
|
246
|
+
// ölçek yüzdedir, nötr değer ve aralık dışı yazılmaz — frontend `composeSurfaceTransform`.
|
|
247
|
+
const transform = [];
|
|
248
|
+
const x = transformValue(surface.translateX, 'translateX') ?? 0;
|
|
249
|
+
const y = transformValue(surface.translateY, 'translateY') ?? 0;
|
|
250
|
+
if (x !== 0 || y !== 0)
|
|
251
|
+
transform.push(`translate(${x}px, ${y}px)`);
|
|
252
|
+
const rotate = transformValue(surface.rotate, 'rotate');
|
|
253
|
+
if (rotate !== undefined && rotate !== 0)
|
|
254
|
+
transform.push(`rotate(${rotate}deg)`);
|
|
255
|
+
const scale = transformValue(surface.scale, 'scale');
|
|
256
|
+
if (scale !== undefined && scale !== 100)
|
|
257
|
+
transform.push(`scale(${scale / 100})`);
|
|
258
|
+
if (transform.length)
|
|
259
|
+
groups.transform = transform.join(' ');
|
|
223
260
|
// `ROOT_TARGET` (`&`) kökün kendisidir: kendi derleyicisi olmayan kaplarda
|
|
224
261
|
// yüzey düğüm kutusuna yazılır ve kaydırılan da odur.
|
|
225
262
|
if (isSet(surface.contentOverflow) && ext?.overflowTarget) {
|
|
@@ -251,8 +288,10 @@ export function composeTypedStyle(input, ext) {
|
|
|
251
288
|
assign(rules, 'borderColor', hover.borderColor);
|
|
252
289
|
assign(rules, 'boxShadow', resolveShadow(hover.shadow));
|
|
253
290
|
const lift = asDimension(hover.lift);
|
|
254
|
-
|
|
255
|
-
|
|
291
|
+
// Kaldırma düğümün kendi transform'unu ezmez, önüne eklenir.
|
|
292
|
+
if (lift !== undefined) {
|
|
293
|
+
rules.transform = `translateY(calc(-1 * ${lift}))${typeof groups.transform === 'string' ? ` ${groups.transform}` : ''}`;
|
|
294
|
+
}
|
|
256
295
|
if (Object.keys(rules).length) {
|
|
257
296
|
const duration = asNumber(hover.transitionMs) ?? 160;
|
|
258
297
|
groups.transition = `background-color ${duration}ms ease, border-color ${duration}ms ease, box-shadow ${duration}ms ease, transform ${duration}ms ease`;
|
|
@@ -500,6 +539,13 @@ export const NODE_SURFACE_TYPES = new Set([
|
|
|
500
539
|
'qm-sidebar',
|
|
501
540
|
'auth-layout'
|
|
502
541
|
]);
|
|
542
|
+
/**
|
|
543
|
+
* `style`'ı kendi içeriğinin CSS'i olarak alan tipler: `html`in `style`'ı uzun metindir ve yazarın
|
|
544
|
+
* işaretlemesine kapsamlı girer — web'de emotion sınıfı (`html.functions.ts` `resolveHtmlStyleClass`),
|
|
545
|
+
* mobilde korumalı belgenin stil bloğu (plans/mobil-plantcare-acigi f-20). "Düz CSS string style"
|
|
546
|
+
* (PAGE-014) ve "ham CSS çizilmez" (MOB-002) uyarıları bu tiplerde yanlıştır.
|
|
547
|
+
*/
|
|
548
|
+
export const DOCUMENT_STYLE_TYPES = new Set(['html']);
|
|
503
549
|
const NODE_SURFACE_EXTENSION = { overflowTarget: ROOT_TARGET };
|
|
504
550
|
/** Düğüm kutusuna inen yüzey; kap tipi değilse ya da grup boşsa yok. */
|
|
505
551
|
export function composeNodeSurfaceStyle(type, surfaceStyle) {
|
|
@@ -539,7 +585,7 @@ const unwrapProp = (value) => {
|
|
|
539
585
|
result[key] = unwrapProp(item);
|
|
540
586
|
return result;
|
|
541
587
|
};
|
|
542
|
-
const rawProp = (props, name) => {
|
|
588
|
+
export const rawProp = (props, name) => {
|
|
543
589
|
const prop = props?.[name];
|
|
544
590
|
// İfadeye bağlı prop'un değeri çalışma anında oluşur, burada çözülemez.
|
|
545
591
|
return prop && typeof prop === 'object' && 'data' in prop ? unwrapProp(prop.data) : undefined;
|