@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
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { logStep } from '../observability/trace.js';
|
|
2
2
|
import { PlatformApiError } from '../platform/errors.js';
|
|
3
3
|
import { ensureWorkflowErrorPath } from './errorPath.js';
|
|
4
|
-
import { TYPED_STYLE_COMPONENTS } from './styleResolve.js';
|
|
5
|
-
import {
|
|
4
|
+
import { DOCUMENT_STYLE_TYPES, TYPED_STYLE_COMPONENTS } from './styleResolve.js';
|
|
5
|
+
import { STATE_STORAGE_KINDS, STEP_ALIASES } from './platform/profile.js';
|
|
6
|
+
import { inlineStyleFields } from './componentCatalog.js';
|
|
6
7
|
const SORT_DEFAULT = { name: 'sortField', type: 'sortType', source: 'query-string' };
|
|
7
8
|
const LIMIT_DEFAULT = { name: 'limit', offset: 'offset', source: 'query-string' };
|
|
8
9
|
function asArray(value) {
|
|
@@ -270,12 +271,11 @@ export function normalizeWorkflowDefinition(definition) {
|
|
|
270
271
|
*/
|
|
271
272
|
/**
|
|
272
273
|
* Adım tipi sözlüğü backend'in, KOŞAN tipler barındırıcının: motor yedi tip register eder, web
|
|
273
|
-
* `Runtime.svelte` altı, mobil `
|
|
274
|
+
* `Runtime.svelte` altı, mobil `runtime-tasks.ts` yedi tip ekler ve aynı sayfa kaydı ikisinde de
|
|
274
275
|
* koşar. Register edilmemiş bir tipe gelen akış sessizce ölür (`flow-engine.ts` `process()`
|
|
275
276
|
* callback bulamazsa next/completed/stopped hiç çağrılmaz), üstelik `run()` promise'i
|
|
276
277
|
* çözülmediği için `__ngAppsFlowActivity.running` sıfıra inmez. İki hâl tamamen türetilebilir
|
|
277
|
-
* olduğu için lint değil NORMALİZASYON
|
|
278
|
-
* uygulama tarafında kapanabilir, `FLOW-023` onları uyarır.
|
|
278
|
+
* olduğu için lint değil NORMALİZASYON; `FLOW-023` kalan hâlleri uyarır.
|
|
279
279
|
*/
|
|
280
280
|
function normalizeFlowHostGaps(steps, list) {
|
|
281
281
|
const fixes = [];
|
|
@@ -289,12 +289,15 @@ function normalizeFlowHostGaps(steps, list) {
|
|
|
289
289
|
.map(step => String(step.stepId ?? ''))
|
|
290
290
|
.filter(id => id.length > 0));
|
|
291
291
|
for (const step of list) {
|
|
292
|
-
// navigate ile redirect'in argümanı AYNI ({ path, pageId, query, targetBlank })
|
|
293
|
-
// yalnız mobil register eder; redirect ise iki barındırıcıda da koşar (mobilde
|
|
294
|
-
// uygulamasına eşlenir), yani redirect kesin üstün.
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
292
|
+
// navigate ile redirect'in argümanı AYNI ({ path, pageId, query, targetBlank, presentation })
|
|
293
|
+
// ama navigate'i yalnız mobil register eder; redirect ise iki barındırıcıda da koşar (mobilde
|
|
294
|
+
// aynı navigate uygulamasına eşlenir), yani redirect kesin üstün. `presentation`
|
|
295
|
+
// (push | modal | replace) yalnız mobil barındırıcıda okunur; web yok sayar.
|
|
296
|
+
const alias = STEP_ALIASES[String(step.type ?? '')];
|
|
297
|
+
if (alias) {
|
|
298
|
+
const original = String(step.type);
|
|
299
|
+
step.type = alias;
|
|
300
|
+
fixes.push(`'${label(step)}': type ${original} → ${alias} yazıldı — argüman şekli aynı ({ path, pageId, query, targetBlank, presentation }) ama ${original}'i web barındırıcısı register etmez, ${alias} ikisinde de koşar`);
|
|
298
301
|
}
|
|
299
302
|
if (endStepIds.size === 0)
|
|
300
303
|
continue;
|
|
@@ -359,7 +362,6 @@ const REQUIRED_COLLECTIONS = ['events', 'validations', 'stateBindings', 'childre
|
|
|
359
362
|
*
|
|
360
363
|
* - `states`: DesignComponentManagementSchema.ToDomain:47 `States.IsSticky`'yi guard'sız okur;
|
|
361
364
|
* Appearance'ın satır 24'te varsayılanı var, States'in yok.
|
|
362
|
-
* - `blocks` doldurulmaz: konum ebeveynin düzenine bağlıdır, eksikse PAGE-037 ile reddedilir.
|
|
363
365
|
* - `events` / `validations` / `stateBindings`: yalnız **micro-op yolunda** zorunlu.
|
|
364
366
|
* `page_create` (POST /pages) bunlarsız 200 döner, `PATCH …/schema` NRE verir; canlı deneyde
|
|
365
367
|
* üçü birlikte verilince geçti, her biri tek başına verilince patladı (klinik H-005 eki).
|
|
@@ -371,9 +373,17 @@ const REQUIRED_COLLECTIONS = ['events', 'validations', 'stateBindings', 'childre
|
|
|
371
373
|
// PropValueType (DesignComponentProperty.cs:421) — 'translation' listeden düşünce çeviriye bağlı
|
|
372
374
|
// prop'un KENDİSİ veri sanılıp static Prop'a sarılıyordu: istek 200 dönüyor, prop bozuk kuruluyordu
|
|
373
375
|
// (09-04 Y-22). Enum burada eksiksiz tutulur.
|
|
374
|
-
export const PROP_VALUE_TYPES = new Set(['static', 'file', 'flow', 'widget', 'element', 'page', 'code', 'translation']);
|
|
376
|
+
export const PROP_VALUE_TYPES = new Set(['static', 'file', 'flow', 'widget', 'element', 'page', 'code', 'translation', 'connectData']);
|
|
377
|
+
// Prop'un TAŞIYICISI da aranır: `type` tek başına yetmez, çünkü liste öğesinin kendi `type` alanı
|
|
378
|
+
// olabilir ve değeri Prop tipi adıyla çakışabilir — datatable kolonunda dosya alanı tam olarak
|
|
379
|
+
// böyledir (`{ name, label, type: 'file', isFilterable }`). Yalnız `type`'a bakan sürüm o öğeyi
|
|
380
|
+
// kurulu bir Prop sanıp sarmadan bırakıyordu ve platform 500 JsonException veriyordu (koşu
|
|
381
|
+
// bulgusu S-2'nin gateway ayağı; Studio ayağı `staticFieldListItems` ile aynı kararı verdi).
|
|
375
382
|
function isPropShaped(value) {
|
|
376
|
-
|
|
383
|
+
if (!value || typeof value !== 'object' || Array.isArray(value))
|
|
384
|
+
return false;
|
|
385
|
+
const candidate = value;
|
|
386
|
+
return PROP_VALUE_TYPES.has(String(candidate.type)) && ('data' in candidate || 'ref' in candidate);
|
|
377
387
|
}
|
|
378
388
|
function wrapPropDictionaryValues(dictionary) {
|
|
379
389
|
let wrapped = false;
|
|
@@ -622,12 +632,16 @@ export function codePropTemplateErrors(ops) {
|
|
|
622
632
|
*
|
|
623
633
|
* Seçici anahtarlara (`&__body`, `:hover`, `--token`) DOKUNULMAZ; yalnız `a-b` biçimli düz CSS
|
|
624
634
|
* özellik adları camelCase'e çevrilir ve çevrim `gatewayNormalized` ile bildirilir.
|
|
635
|
+
*
|
|
636
|
+
* TB-129 — `inlineFields` verilirse o ÜST DÜZEY alanların altına hiç girilmez: `table.style.th`
|
|
637
|
+
* gibi dallar satır içi `style` attribute'una basılır ve kebab-case zorunludur
|
|
638
|
+
* (`INLINE_STYLE_FIELDS_BY_TYPE`). Çeviri orada stili öldürüyordu.
|
|
625
639
|
*/
|
|
626
640
|
const styleKeyPattern = /^[a-z][a-z0-9]*(-[a-z0-9]+)+$/;
|
|
627
641
|
export function isStylePropName(name) {
|
|
628
642
|
return name === 'style' || /Style$/.test(name);
|
|
629
643
|
}
|
|
630
|
-
export function normalizeStyleKeys(value, label, fixed) {
|
|
644
|
+
export function normalizeStyleKeys(value, label, fixed, inlineFields) {
|
|
631
645
|
if (Array.isArray(value)) {
|
|
632
646
|
for (const entry of value)
|
|
633
647
|
normalizeStyleKeys(entry, label, fixed);
|
|
@@ -636,7 +650,10 @@ export function normalizeStyleKeys(value, label, fixed) {
|
|
|
636
650
|
if (!value || typeof value !== 'object')
|
|
637
651
|
return;
|
|
638
652
|
const record = value;
|
|
653
|
+
const skipped = new Set(inlineFields ?? []);
|
|
639
654
|
for (const key of Object.keys(record)) {
|
|
655
|
+
if (skipped.has(key))
|
|
656
|
+
continue;
|
|
640
657
|
normalizeStyleKeys(record[key], label, fixed);
|
|
641
658
|
if (!styleKeyPattern.test(key))
|
|
642
659
|
continue;
|
|
@@ -658,10 +675,43 @@ export function styleKeyNote(fixed) {
|
|
|
658
675
|
+ 'değer kaydedilir ama hiç uygulanmaz ve tek işareti tarayıcı konsoludur (F-06). '
|
|
659
676
|
+ "Seçici anahtarlara ('&__body', ':hover', '--token') dokunulmadı."];
|
|
660
677
|
}
|
|
661
|
-
/**
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
678
|
+
/**
|
|
679
|
+
* Kabın `states.isDroppable`ı KATALOĞUN kararıdır. Gateway düğümü kurarken bu alanı
|
|
680
|
+
* yazmıyordu (`DEFAULT_STATES` hepsini `false` bırakır) ve Studio kanvası düğümü kap saymıyordu:
|
|
681
|
+
* imleç `scroll-view`un İÇİNDEYKEN bırakma planı sayfa kabına düşüyor, çizgi kabın üst kenarına
|
|
682
|
+
* çiziliyordu (ölçüldü 2026-09-21, `test/mobile app` › `Satislar`). Aynı tipi paletten ekleyen
|
|
683
|
+
* Studio katalog değerini yazdığı için tek şemada iki türlü davranan kap oluyordu.
|
|
684
|
+
*
|
|
685
|
+
* Değer uygulamanın KENDİ sözlüğünden gelir (`listComponents`; mobil uygulamada `type=mobile`
|
|
686
|
+
* satırları), yani kural platformdan bağımsızdır ve kuruluşun kendi komponentlerini de kapsar.
|
|
687
|
+
* Yalnız `true` yazılır: katalog bir tipi kap saymıyorsa düğüme dokunulmaz.
|
|
688
|
+
*/
|
|
689
|
+
export function normalizeDroppableStates(nodes, droppable) {
|
|
690
|
+
const fixed = [];
|
|
691
|
+
if (droppable.size === 0)
|
|
692
|
+
return fixed;
|
|
693
|
+
for (const node of asObjects(nodes)) {
|
|
694
|
+
const type = typeof node.type === 'string' ? node.type : '';
|
|
695
|
+
if (droppable.get(type) === true) {
|
|
696
|
+
// `states` normalde `normalizeComponentDefaults`tan geçmiş olur; gelmediyse burada da
|
|
697
|
+
// eksiksiz kurulur — yarım `states` platformda NullReference üretir.
|
|
698
|
+
if (!node.states || typeof node.states !== 'object' || Array.isArray(node.states)) {
|
|
699
|
+
node.states = { ...DEFAULT_STATES };
|
|
700
|
+
}
|
|
701
|
+
const states = node.states;
|
|
702
|
+
if (states.isDroppable !== true) {
|
|
703
|
+
states.isDroppable = true;
|
|
704
|
+
fixed.push(`'${String(node.elementId ?? node.name ?? type)}' (${type})`);
|
|
705
|
+
}
|
|
706
|
+
}
|
|
707
|
+
fixed.push(...normalizeDroppableStates(node.children, droppable));
|
|
708
|
+
}
|
|
709
|
+
return fixed;
|
|
710
|
+
}
|
|
711
|
+
export function droppableStatesNote(fixed) {
|
|
712
|
+
return fixed.length === 0
|
|
713
|
+
? []
|
|
714
|
+
: [`states.isDroppable katalogdan yazıldı (${fixed.join(' · ')}) — kataloğun kap saydığı düğüm Studio kanvasında da kap olmalı; alanı bilerek kapatmak için resize_component/set_states kullanın`];
|
|
665
715
|
}
|
|
666
716
|
/**
|
|
667
717
|
* Y-15 — adı eksik, null ya da boş/boşluk olan düğüm runtime'ın ifade ön sözünü bozar: `getNodeNames`
|
|
@@ -671,7 +721,6 @@ function isDerivableComponentName(name) {
|
|
|
671
721
|
* hiç denetlemez (`PageSchemaValidator.cs`); Studio her düğüme `name = elementId` yazar
|
|
672
722
|
* (`ScreenBuilderDroppable.svelte`). Kural deterministik olduğu için reddetmek yerine dolduruyoruz.
|
|
673
723
|
* Doldurulan ad da PAGE-009'dan geçmek zorunda: çağıran bunu denetimden ÖNCE koşar. Dolu ada dokunulmaz.
|
|
674
|
-
* `normalizeComponentDefaults` aynı boşluğu yalnız geçerli bir elementId için ikinci kez kapatır (güvenlik ağı).
|
|
675
724
|
*/
|
|
676
725
|
export function fillComponentNames(nodes) {
|
|
677
726
|
const filled = [];
|
|
@@ -696,16 +745,6 @@ export function normalizeComponentDefaults(nodes) {
|
|
|
696
745
|
const fixed = [];
|
|
697
746
|
for (const node of asObjects(nodes)) {
|
|
698
747
|
const missing = [];
|
|
699
|
-
// name'siz düğümü platform kabul eder ama sayfada bir akış ya da `{{ }}` kod prop'u varsa
|
|
700
|
-
// FlowEngine runCode "Cannot read properties of undefined (reading 'catch')" ile düşer, akış
|
|
701
|
-
// `running:1`'de takılır ve hata adsız düğümü işaret etmez (image ölçüm turu, 2026-10-04).
|
|
702
|
-
// Yalnız PAGE-009'un geçerli saydığı elementId'den türetilir: page_create/page_update/layout
|
|
703
|
-
// yollarında PAGE-009 denetimi bu normalizasyondan ÖNCE koşar; bozuk bir elementId'den türeyen
|
|
704
|
-
// ad denetimi atlatıp "Missing initializer in const declaration" ile sayfayı öldürürdü.
|
|
705
|
-
if ((typeof node.name !== 'string' || node.name.trim() === '') && typeof node.elementId === 'string' && isDerivableComponentName(node.elementId)) {
|
|
706
|
-
node.name = node.elementId;
|
|
707
|
-
missing.push('name');
|
|
708
|
-
}
|
|
709
748
|
if (!node.states || typeof node.states !== 'object') {
|
|
710
749
|
node.states = { ...DEFAULT_STATES };
|
|
711
750
|
missing.push('states');
|
|
@@ -741,8 +780,10 @@ export function normalizeComponentDefaults(nodes) {
|
|
|
741
780
|
if (node.props && typeof node.props === 'object' && !Array.isArray(node.props)) {
|
|
742
781
|
const props = node.props;
|
|
743
782
|
const styleKeys = [];
|
|
783
|
+
// Satır içi stil dalları komponent TİPİNE göre atlanır; tip bilinmiyorsa eski davranış.
|
|
784
|
+
const inlineFields = (name) => name === 'style' ? inlineStyleFields(typeof node.type === 'string' ? node.type : undefined) : undefined;
|
|
744
785
|
for (const name of Object.keys(props).filter(isStylePropName)) {
|
|
745
|
-
normalizeStyleKeys(props[name], `${String(node.elementId ?? node.name ?? '?')}.${name}`, styleKeys);
|
|
786
|
+
normalizeStyleKeys(props[name], `${String(node.elementId ?? node.name ?? '?')}.${name}`, styleKeys, inlineFields(name));
|
|
746
787
|
}
|
|
747
788
|
if (styleKeys.length > 0)
|
|
748
789
|
missing.push(`stil anahtarları camelCase'e çevrildi: ${styleKeys.join(' · ')}`);
|
|
@@ -906,12 +947,21 @@ function normalizeReplacePropValue(op) {
|
|
|
906
947
|
}
|
|
907
948
|
return notes;
|
|
908
949
|
}
|
|
950
|
+
// connectData sarmalı burada değiştirilmez: gövde duvarı (CD-005, `connectDataOpIssues`) op'u
|
|
951
|
+
// set_connect_data'ya yönlendirerek rejectedOps'a yazar; burada atmak listeyi tek hataya indirirdi.
|
|
952
|
+
if (propType === 'connectData')
|
|
953
|
+
return notes;
|
|
909
954
|
// Applier her sarmalın tipini korur, ama page/file/flow/widget/element taşıyıcıları başka alanlara
|
|
910
955
|
// (FlowId, RedirectPageId, dosya) yazılır ve bu op'la bağları kurulmaz. Remove+add önerilmez:
|
|
911
956
|
// düğümü yeniden kurmak ona yazan setter iteratee'lerinin element'ini siler (G-04, FLOW-018).
|
|
912
957
|
throw new PlatformApiError(`${label}: '${propType}' tipli Prop replace_prop ile kurulmaz — bu op static, code ve translation sarmalını taşır. Komponenti silip yeniden EKLEMEYİN: ona yazan setter adımlarının hedefi silinir (FLOW-018). Akış bağı için set_event, diğer tipler için page_update(schema) ile tam şema yazın.`, 400);
|
|
913
958
|
}
|
|
914
|
-
|
|
959
|
+
/**
|
|
960
|
+
* `typesByElementId` — TB-129: `replace_prop` op'u yalnız elementId taşır, stil çevirisinin
|
|
961
|
+
* komponent tipine bakabilmesi için haritayı çağıran (okuduğu şemadan) verir. Verilmezse çeviri
|
|
962
|
+
* tip ayrımı yapmaz; bu, harita okunamadığında yazmayı engellememek için bilinçli düşüştür.
|
|
963
|
+
*/
|
|
964
|
+
export function normalizeSchemaOps(ops, typesByElementId) {
|
|
915
965
|
const components = [];
|
|
916
966
|
const props = [];
|
|
917
967
|
const validations = [];
|
|
@@ -919,7 +969,10 @@ export function normalizeSchemaOps(ops) {
|
|
|
919
969
|
for (const op of asObjects(ops)) {
|
|
920
970
|
if (op.op === 'replace_prop') {
|
|
921
971
|
if (isStylePropName(String(op.prop ?? ''))) {
|
|
922
|
-
|
|
972
|
+
const inline = String(op.prop) === 'style'
|
|
973
|
+
? inlineStyleFields(typesByElementId?.get(String(op.target ?? '')))
|
|
974
|
+
: undefined;
|
|
975
|
+
normalizeStyleKeys(op.value, `'${String(op.target ?? '?')}.${String(op.prop)}'`, props, inline);
|
|
923
976
|
}
|
|
924
977
|
props.push(...normalizeReplacePropValue(op));
|
|
925
978
|
continue;
|
|
@@ -997,11 +1050,11 @@ export function pageContainerNode() {
|
|
|
997
1050
|
* çekilirlerse sayfa içeriğinin akışına girer ve katman olarak açılmazlar.
|
|
998
1051
|
*/
|
|
999
1052
|
const ROOT_LAYER_TYPES = new Set(['modal', 'drawer']);
|
|
1000
|
-
export function ensurePageContainer(schema) {
|
|
1053
|
+
export function ensurePageContainer(schema, rootContainer = pageContainerNode) {
|
|
1001
1054
|
const nodes = asObjects(schema);
|
|
1002
1055
|
if (nodes.some(node => node.elementId === PAGE_CONTAINER_ELEMENT_ID))
|
|
1003
1056
|
return { schema: nodes, fixes: [] };
|
|
1004
|
-
const container =
|
|
1057
|
+
const container = rootContainer();
|
|
1005
1058
|
const layers = nodes.filter(node => ROOT_LAYER_TYPES.has(String(node.type ?? '')));
|
|
1006
1059
|
container.children = nodes.filter(node => !ROOT_LAYER_TYPES.has(String(node.type ?? '')));
|
|
1007
1060
|
const moved = container.children.length;
|
|
@@ -1044,7 +1097,7 @@ export function clampBlockSizes(nodes, maxSizes) {
|
|
|
1044
1097
|
* başlatır (`ComponentHeightType.Fixed = 0`), yani "alan yok" = "sabit yükseklik". Studio'nun
|
|
1045
1098
|
* kendi bırakma yolu bunu ASLA yapmaz: katalogdaki `isDefaultAutoHeight`e bakıp
|
|
1046
1099
|
* `heightType: isDefaultAutoHeight ? 'auto' : 'fixed'` yazar
|
|
1047
|
-
* (`ScreenBuilderDroppable.svelte:701`, `store-converter.ts
|
|
1100
|
+
* (`ScreenBuilderDroppable.svelte:701`, `store-converter.ts` `createDefaultSchemaNode`). Katalogda varsayılan
|
|
1048
1101
|
* `true`'dur (`ZeyrekComponent.ts:62`); `false` diyen yalnız sekiz tip var — auth-layout, html,
|
|
1049
1102
|
* image, layout, qrcode, table, text-editor, video.
|
|
1050
1103
|
*
|
|
@@ -1830,19 +1883,18 @@ export function viewAliasNote(fixes) {
|
|
|
1830
1883
|
return [`view alanlarının entity bağı id'den alias'a çevrildi (${fixes.join(', ')}): yazma tarafı viewEntityAlias ister, okuma tarafı viewEntityId döndürür — alias boş kalsa platform tüm alanları ilk tabloya düşürürdü`];
|
|
1831
1884
|
}
|
|
1832
1885
|
/**
|
|
1833
|
-
*
|
|
1834
|
-
*
|
|
1835
|
-
*
|
|
1836
|
-
* `data`'nın **JSON string** olmasıdır: "[{\"key\":\"MyTest\",\"value\":{\"isActive\":true}}]".
|
|
1837
|
-
* `Data` tipi `object` olduğu için platform her gövdeyi kabul eder; alan adı tutmayan bir gövde
|
|
1838
|
-
* (name/type/defaultValue gibi) `Data`'yı null bırakır ve istek 200 döner ama HİÇBİR ŞEY yazılmaz
|
|
1839
|
-
* (17/Ç1). Okuma tarafı da aynı string'i geri verir (`AppVariablesDto.Map` → GetRawText).
|
|
1886
|
+
* `storage` kayıtta isteğe bağlıdır (`docs/adr/0004-state-saklama-turu.md`). Tanınmayan tür ya da
|
|
1887
|
+
* metin olmayan değer çalışma zamanında da yok sayılır (`@ng-apps/ui-native` `variableStorageKinds`);
|
|
1888
|
+
* gateway onu taşımaz ki bir sonraki yazım bozuk değeri kalıcılaştırmasın.
|
|
1840
1889
|
*/
|
|
1841
1890
|
export function parseVariableEntries(data) {
|
|
1842
1891
|
const source = typeof data === 'string' ? safeParse(data) : data;
|
|
1843
1892
|
return asObjects(source)
|
|
1844
1893
|
.filter(entry => typeof entry.key === 'string' && entry.key !== '')
|
|
1845
|
-
.map(entry =>
|
|
1894
|
+
.map(entry => {
|
|
1895
|
+
const storage = STATE_STORAGE_KINDS.find(kind => kind === entry.storage);
|
|
1896
|
+
return { key: String(entry.key), value: entry.value, ...(storage ? { storage } : {}) };
|
|
1897
|
+
});
|
|
1846
1898
|
}
|
|
1847
1899
|
function safeParse(value) {
|
|
1848
1900
|
try {
|
|
@@ -1857,17 +1909,29 @@ function safeParse(value) {
|
|
|
1857
1909
|
* mevcut seti okuyup anahtar bazında birleştirir; `replace: true` verilirse bilinçli olarak ezer.
|
|
1858
1910
|
*/
|
|
1859
1911
|
export function mergeVariableEntries(existing, incoming, replace) {
|
|
1912
|
+
const stored = (entry, current) => {
|
|
1913
|
+
const storage = entry.storage === undefined ? current?.storage : STATE_STORAGE_KINDS.find(kind => kind === entry.storage);
|
|
1914
|
+
const value = entry.value === undefined ? (current ? current.value : null) : entry.value;
|
|
1915
|
+
return { key: entry.key, value, ...(storage ? { storage } : {}) };
|
|
1916
|
+
};
|
|
1860
1917
|
if (replace)
|
|
1861
|
-
return { entries: incoming, fixes: [] };
|
|
1918
|
+
return { entries: incoming.map(entry => stored(entry)), fixes: [] };
|
|
1862
1919
|
const merged = [...existing];
|
|
1863
1920
|
const kept = [];
|
|
1921
|
+
const keptStorage = [];
|
|
1922
|
+
const keptValue = [];
|
|
1864
1923
|
for (const entry of incoming) {
|
|
1865
1924
|
const index = merged.findIndex(current => current.key === entry.key);
|
|
1866
1925
|
if (index === -1) {
|
|
1867
|
-
merged.push(entry);
|
|
1926
|
+
merged.push(stored(entry));
|
|
1868
1927
|
continue;
|
|
1869
1928
|
}
|
|
1870
|
-
merged[index]
|
|
1929
|
+
const current = merged[index];
|
|
1930
|
+
if (entry.storage === undefined && current?.storage)
|
|
1931
|
+
keptStorage.push(`'${entry.key}' → ${current.storage}`);
|
|
1932
|
+
if (entry.value === undefined)
|
|
1933
|
+
keptValue.push(`'${entry.key}'`);
|
|
1934
|
+
merged[index] = stored(entry, current);
|
|
1871
1935
|
}
|
|
1872
1936
|
for (const entry of existing) {
|
|
1873
1937
|
if (!incoming.some(current => current.key === entry.key))
|
|
@@ -1875,9 +1939,17 @@ export function mergeVariableEntries(existing, incoming, replace) {
|
|
|
1875
1939
|
}
|
|
1876
1940
|
return {
|
|
1877
1941
|
entries: merged,
|
|
1878
|
-
fixes:
|
|
1879
|
-
|
|
1880
|
-
|
|
1942
|
+
fixes: [
|
|
1943
|
+
...(kept.length > 0
|
|
1944
|
+
? [`mevcut değişkenler korundu (${kept.join(', ')}): uç seti komple değiştirir, gateway anahtar bazında birleştirdi — kasten silmek için replace: true verin`]
|
|
1945
|
+
: []),
|
|
1946
|
+
...(keptStorage.length > 0
|
|
1947
|
+
? [`saklama türü korundu (${keptStorage.join(', ')}): gelen değişken storage taşımıyordu, kayıttaki tür silinmedi — belleğe çevirmek için storage: 'memory' verin`]
|
|
1948
|
+
: []),
|
|
1949
|
+
...(keptValue.length > 0
|
|
1950
|
+
? [`değer korundu (${keptValue.join(', ')}): gelen değişken value taşımıyordu, kayıttaki değer silinmedi — boşaltmak için value: null verin`]
|
|
1951
|
+
: [])
|
|
1952
|
+
]
|
|
1881
1953
|
};
|
|
1882
1954
|
}
|
|
1883
1955
|
export function blockSizeNote(clamped) {
|
|
@@ -1958,12 +2030,22 @@ export function nestedRepeaterNodes(nodes) {
|
|
|
1958
2030
|
walk(asObjects(nodes), false);
|
|
1959
2031
|
return found;
|
|
1960
2032
|
}
|
|
1961
|
-
|
|
1962
|
-
|
|
2033
|
+
/**
|
|
2034
|
+
* Mobil profilde gruplu listenin (gün/kategori başlığı altında satırlar) hazır bileşeni vardır:
|
|
2035
|
+
* `grouped-list` (ui-native, ADR 0007). Web'de karşılığı yok — web mesajı textarea tarifinde kalır.
|
|
2036
|
+
*/
|
|
2037
|
+
export function nestedRepeaterMessage(ids, platform = 'web') {
|
|
2038
|
+
const measured = `PAGE-013 — repeater içinde repeater (istek gönderilmedi): ${ids.join(' · ')}. `
|
|
1963
2039
|
+ 'Ölçüldü (2026-08-19): iç repeater OKUMADA çalışır ama girilen değer dış satıra GERİ YAZILMAZ '
|
|
1964
2040
|
+ '(_repeaterValues null kalır) ve tek düzenlemeden sonra DOM bozulur. Sebep yapısal: şemada iç '
|
|
1965
|
-
+ 'repeater tek düğümdür, dış satır başına ayrı düğüm yoktur.
|
|
1966
|
-
|
|
2041
|
+
+ 'repeater tek düğümdür, dış satır başına ayrı düğüm yoktur. ';
|
|
2042
|
+
const textarea = "Satır içi listeyi tek bir textarea ile topla (satır başına bir öğe) ve kaydederken split('\n') ile böl — platform_guide('page') Kural 1g.";
|
|
2043
|
+
if (platform !== 'mobile')
|
|
2044
|
+
return measured + textarea;
|
|
2045
|
+
return measured
|
|
2046
|
+
+ 'Başlıklar altında gruplanmış liste (güne/kategoriye göre) için mobilde tek `grouped-list` kur: düz `rows` + '
|
|
2047
|
+
+ '`groupBy` alan yolu; çocuk 0 bölüm başlığı (`{{ Section.title }}`), çocuk 1 öğe şablonu (`Row`, `{{ item.<alan> }}`) — '
|
|
2048
|
+
+ "platform_guide('mobile') §Güne göre görev listesi. Satır içinde düzenlenen alt liste gerekiyorsa: " + textarea;
|
|
1967
2049
|
}
|
|
1968
2050
|
/**
|
|
1969
2051
|
* `style` prop'u düz CSS STRING verilmiş düğümler. Çalışır (emotion string'i CSS metni sayar) ama
|
|
@@ -1977,7 +2059,7 @@ export function stringStyleNodes(nodes) {
|
|
|
1977
2059
|
for (const node of list) {
|
|
1978
2060
|
const props = (node.props ?? {});
|
|
1979
2061
|
const style = props.style;
|
|
1980
|
-
if (style && typeof style.data === 'string' && style.data.includes(':')) {
|
|
2062
|
+
if (style && typeof style.data === 'string' && style.data.includes(':') && !DOCUMENT_STYLE_TYPES.has(String(node.type))) {
|
|
1981
2063
|
found.push(String(node.elementId ?? node.name ?? '?'));
|
|
1982
2064
|
}
|
|
1983
2065
|
walk(asObjects(node.children));
|
|
@@ -2108,6 +2190,95 @@ export function flowBindings(schema, hostEvents) {
|
|
|
2108
2190
|
visitNode(node);
|
|
2109
2191
|
return bindings;
|
|
2110
2192
|
}
|
|
2193
|
+
/**
|
|
2194
|
+
* TB-125 — op listesi UYGULANDIKTAN sonra şemada (ve düzey olaylarında) hâlâ referanslı olan
|
|
2195
|
+
* akışlar. Simülasyon yalnız akış referansını umursar: eklenen düğüm ağacın köküne konur (konum
|
|
2196
|
+
* sayıma girmez), silinen düğüm ağaçtan çıkarılır, olay op'ları olay dizisini günceller.
|
|
2197
|
+
* `batch` op'u iç listesiyle düzleştirilir.
|
|
2198
|
+
*/
|
|
2199
|
+
function survivingFlowRefs(nodes, hostEvents, ops) {
|
|
2200
|
+
const simulated = structuredClone(nodes);
|
|
2201
|
+
const simulatedHostEvents = structuredClone(hostEvents);
|
|
2202
|
+
const removeNode = (list, target) => {
|
|
2203
|
+
const index = list.findIndex(node => node.elementId === target);
|
|
2204
|
+
if (index >= 0) {
|
|
2205
|
+
list.splice(index, 1);
|
|
2206
|
+
return true;
|
|
2207
|
+
}
|
|
2208
|
+
for (const node of list) {
|
|
2209
|
+
if (Array.isArray(node.children) && removeNode(node.children, target))
|
|
2210
|
+
return true;
|
|
2211
|
+
}
|
|
2212
|
+
return false;
|
|
2213
|
+
};
|
|
2214
|
+
const eventsOf = (target) => {
|
|
2215
|
+
if (target === 'page')
|
|
2216
|
+
return simulatedHostEvents;
|
|
2217
|
+
const node = findNodeByElementId(simulated, target);
|
|
2218
|
+
if (!node)
|
|
2219
|
+
return undefined;
|
|
2220
|
+
if (!Array.isArray(node.events))
|
|
2221
|
+
node.events = [];
|
|
2222
|
+
return node.events;
|
|
2223
|
+
};
|
|
2224
|
+
const apply = (op) => {
|
|
2225
|
+
if (op.op === 'batch') {
|
|
2226
|
+
for (const inner of asObjects(op.ops))
|
|
2227
|
+
apply(inner);
|
|
2228
|
+
return;
|
|
2229
|
+
}
|
|
2230
|
+
if (op.op === 'add_component' && op.component && typeof op.component === 'object') {
|
|
2231
|
+
simulated.push(structuredClone(op.component));
|
|
2232
|
+
return;
|
|
2233
|
+
}
|
|
2234
|
+
const target = String(op.target ?? '');
|
|
2235
|
+
if (target === '')
|
|
2236
|
+
return;
|
|
2237
|
+
if (op.op === 'remove_component') {
|
|
2238
|
+
removeNode(simulated, target);
|
|
2239
|
+
return;
|
|
2240
|
+
}
|
|
2241
|
+
if (op.op !== 'set_event' && op.op !== 'add_event' && op.op !== 'remove_event')
|
|
2242
|
+
return;
|
|
2243
|
+
const events = eventsOf(target);
|
|
2244
|
+
if (events === undefined)
|
|
2245
|
+
return;
|
|
2246
|
+
const eventName = String(op.event ?? '');
|
|
2247
|
+
const eventValue = typeof op.eventValue === 'string' ? op.eventValue : undefined;
|
|
2248
|
+
const named = events.filter(event => String(event.name ?? '') === eventName && typeof event.value === 'string' && event.value !== '');
|
|
2249
|
+
if (op.op === 'add_event') {
|
|
2250
|
+
if (eventValue !== undefined)
|
|
2251
|
+
events.push({ name: eventName, value: eventValue });
|
|
2252
|
+
return;
|
|
2253
|
+
}
|
|
2254
|
+
if (op.op === 'set_event') {
|
|
2255
|
+
// set_event adın BÜTÜN bağlarını tek kayda indirir.
|
|
2256
|
+
for (const event of named)
|
|
2257
|
+
events.splice(events.indexOf(event), 1);
|
|
2258
|
+
if (eventValue !== undefined)
|
|
2259
|
+
events.push({ name: eventName, value: eventValue });
|
|
2260
|
+
return;
|
|
2261
|
+
}
|
|
2262
|
+
// Değersiz remove_event çoklu bağda platformda ambiguous-event ile düşer: hiçbir bağ kalkmaz.
|
|
2263
|
+
const removed = eventValue === undefined
|
|
2264
|
+
? (named.length === 1 ? named : [])
|
|
2265
|
+
: named.filter(event => event.value === eventValue);
|
|
2266
|
+
for (const event of removed)
|
|
2267
|
+
events.splice(events.indexOf(event), 1);
|
|
2268
|
+
};
|
|
2269
|
+
for (const op of ops)
|
|
2270
|
+
apply(op);
|
|
2271
|
+
const surviving = new Set();
|
|
2272
|
+
for (const event of simulatedHostEvents) {
|
|
2273
|
+
if (typeof event.value === 'string')
|
|
2274
|
+
surviving.add(event.value);
|
|
2275
|
+
}
|
|
2276
|
+
for (const node of simulated) {
|
|
2277
|
+
for (const flowId of collectFlowRefs(node))
|
|
2278
|
+
surviving.add(flowId);
|
|
2279
|
+
}
|
|
2280
|
+
return surviving;
|
|
2281
|
+
}
|
|
2111
2282
|
/**
|
|
2112
2283
|
* Bir yamanın SİLECEĞİ akışlar. Platform referanssız kalan akışı KALICI siler; uyarı yamadan sonra
|
|
2113
2284
|
* geldiğinde "önce yedekle" tavsiyesi uygulanamaz hâle geliyordu (09-05 koşusu: sekiz adımlı giriş
|
|
@@ -2116,26 +2287,15 @@ export function flowBindings(schema, hostEvents) {
|
|
|
2116
2287
|
*/
|
|
2117
2288
|
export function orphanedFlowPlan(schema, pageEvents, ops) {
|
|
2118
2289
|
const nodes = asObjects(schema);
|
|
2119
|
-
|
|
2290
|
+
// `batch` açılır: içindeki silme de plana girer (kapı yalnız üst düzeye bakınca batch'teki
|
|
2291
|
+
// `remove_component` akışı uyarısız siliyordu).
|
|
2292
|
+
const opList = asObjects(ops).flatMap(op => op.op === 'batch' ? asObjects(op.ops) : [op]);
|
|
2120
2293
|
const plan = [];
|
|
2121
|
-
|
|
2122
|
-
|
|
2123
|
-
|
|
2124
|
-
|
|
2125
|
-
|
|
2126
|
-
}
|
|
2127
|
-
collectFlowRefsExcept(nodes, skipElementId, surviving);
|
|
2128
|
-
if (skipEvent !== undefined) {
|
|
2129
|
-
const node = findNodeByElementId(nodes, skipElementId);
|
|
2130
|
-
for (const event of asObjects(node?.events)) {
|
|
2131
|
-
if (String(event.name ?? '') !== skipEvent && typeof event.value === 'string')
|
|
2132
|
-
surviving.add(event.value);
|
|
2133
|
-
}
|
|
2134
|
-
for (const flowId of collectFlowRefs({ ...(node ?? {}), events: [] }))
|
|
2135
|
-
surviving.add(flowId);
|
|
2136
|
-
}
|
|
2137
|
-
return surviving;
|
|
2138
|
-
};
|
|
2294
|
+
// TB-125 — hayatta kalan referanslar yamanın SONUCUNDAN okunur, op op özgün ağaçtan değil. Eski
|
|
2295
|
+
// hesap her op'a tek başına baktığı için aynı yamada başka bir düğüme bağlanan akışı da
|
|
2296
|
+
// "referanssız" sayıyor ve düğüm taşıyan her yamayı ikiye böldürüyordu (S2-35). İstek atomik
|
|
2297
|
+
// olduğuna göre doğru soru tektir: yama bittiğinde bu akışa referans kalıyor mu?
|
|
2298
|
+
const surviving = survivingFlowRefs(nodes, asObjects(pageEvents), opList);
|
|
2139
2299
|
for (const op of opList) {
|
|
2140
2300
|
const target = String(op.target ?? '');
|
|
2141
2301
|
if (target === '')
|
|
@@ -2145,7 +2305,6 @@ export function orphanedFlowPlan(schema, pageEvents, ops) {
|
|
|
2145
2305
|
if (!doomed)
|
|
2146
2306
|
continue;
|
|
2147
2307
|
const losing = collectFlowRefs(doomed);
|
|
2148
|
-
const surviving = survivingRefs(target);
|
|
2149
2308
|
const orphaned = [...losing].filter(flowId => !surviving.has(flowId));
|
|
2150
2309
|
if (orphaned.length > 0)
|
|
2151
2310
|
plan.push({ target, op: 'remove_component', flowIds: orphaned });
|
|
@@ -2167,10 +2326,7 @@ export function orphanedFlowPlan(schema, pageEvents, ops) {
|
|
|
2167
2326
|
// oraya bağlı akış kapıdan geçip kalıcı silinirdi. Düzey olayı olmayan widget'ta applier da
|
|
2168
2327
|
// düğüm aramasına düşer.
|
|
2169
2328
|
if (op.op === 'remove_event' && target === 'page' && Array.isArray(pageEvents)) {
|
|
2170
|
-
const
|
|
2171
|
-
const removed = removedBy(boundTo(hostEvents));
|
|
2172
|
-
const surviving = new Set(hostEvents.filter(event => !removed.includes(event) && typeof event.value === 'string').map(event => String(event.value)));
|
|
2173
|
-
collectFlowRefsExcept(nodes, '', surviving);
|
|
2329
|
+
const removed = removedBy(boundTo(asObjects(pageEvents)));
|
|
2174
2330
|
const flowIds = [...new Set(removed.map(event => String(event.value)))].filter(flowId => !surviving.has(flowId));
|
|
2175
2331
|
if (flowIds.length > 0)
|
|
2176
2332
|
plan.push({ target, op: 'remove_event', flowIds });
|
|
@@ -2182,11 +2338,6 @@ export function orphanedFlowPlan(schema, pageEvents, ops) {
|
|
|
2182
2338
|
const losing = op.op === 'set_event' ? named.filter(event => event.value !== eventValue) : removedBy(named);
|
|
2183
2339
|
if (losing.length === 0)
|
|
2184
2340
|
continue;
|
|
2185
|
-
const surviving = survivingRefs(target, eventName);
|
|
2186
|
-
for (const event of named) {
|
|
2187
|
-
if (!losing.includes(event))
|
|
2188
|
-
surviving.add(String(event.value));
|
|
2189
|
-
}
|
|
2190
2341
|
const flowIds = [...new Set(losing.map(event => String(event.value)))].filter(flowId => !surviving.has(flowId));
|
|
2191
2342
|
if (flowIds.length > 0)
|
|
2192
2343
|
plan.push({ target, op: String(op.op), flowIds });
|
|
@@ -357,3 +357,39 @@ export function functionOutline(definition) {
|
|
|
357
357
|
view: 'outline'
|
|
358
358
|
};
|
|
359
359
|
}
|
|
360
|
+
export function processOutline(payload) {
|
|
361
|
+
const model = (payload.definition && typeof payload.definition === 'object' ? payload.definition : payload);
|
|
362
|
+
const describeAssignee = (value) => {
|
|
363
|
+
if (!value || typeof value !== 'object' || Array.isArray(value))
|
|
364
|
+
return value ?? null;
|
|
365
|
+
const assignee = value;
|
|
366
|
+
const rule = [assignee.rule, assignee.arg].filter(part => part !== undefined && part !== null).join(':');
|
|
367
|
+
return assignee.of ? `${rule} of ${String(assignee.of)}` : rule;
|
|
368
|
+
};
|
|
369
|
+
const describeNodes = (nodes, depth) => asArray(nodes).flatMap(node => [{
|
|
370
|
+
id: node.id,
|
|
371
|
+
type: node.type,
|
|
372
|
+
depth,
|
|
373
|
+
editable: node.editable ?? 'locked',
|
|
374
|
+
description: node.description ?? null,
|
|
375
|
+
assignee: describeAssignee(node.assignee ?? node.to),
|
|
376
|
+
policy: node.policy ?? null,
|
|
377
|
+
sla: Array.isArray(node.sla) ? asArray(node.sla).map(sla => sla.duration) : node.sla ?? null,
|
|
378
|
+
on: asArray(node.on).map(handler => `${String(handler.event)} → ${String(handler.handler?.type ?? '?')}${handler.afterHandlers ? ` (${String(handler.afterHandlers)})` : ''}`)
|
|
379
|
+
}, ...describeNodes(node.nodes, depth + 1)]);
|
|
380
|
+
const nodes = describeNodes(model.nodes, 0);
|
|
381
|
+
return {
|
|
382
|
+
id: payload.id ?? null,
|
|
383
|
+
slug: model.slug ?? payload.slug,
|
|
384
|
+
name: model.name ?? payload.name,
|
|
385
|
+
version: payload.version ?? null,
|
|
386
|
+
activeVersionId: payload.activeVersionId ?? null,
|
|
387
|
+
versions: asArray(payload.versions).map(version => ({ number: version.number, state: version.state })),
|
|
388
|
+
nodeCount: nodes.length,
|
|
389
|
+
nodes,
|
|
390
|
+
edgeCount: asArray(model.edges).length,
|
|
391
|
+
on: asArray(model.on).map(handler => String(handler.event)),
|
|
392
|
+
view: 'outline',
|
|
393
|
+
hint: "Tek düğüm için process_definition_get view:'part', part:'definition.nodes[id=<düğüm>]'; değişiklik için process_definition_patch (yol /nodes[id=<düğüm>]/…)."
|
|
394
|
+
};
|
|
395
|
+
}
|