@appaflytech/zeyrek-ai-mcp 0.0.37 → 0.0.40
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/dist/knowledge/componentCatalog.js +389 -149
- package/dist/knowledge/componentEvents.js +16 -8
- package/dist/knowledge/connectDataBind.js +10 -2
- package/dist/knowledge/expressionPrelude.js +1 -0
- package/dist/knowledge/mobileComponentCatalog.js +16 -4
- package/dist/knowledge/mobileComponentEvents.js +21 -1
- package/dist/knowledge/mobileStore.js +75 -0
- package/dist/knowledge/normalize.js +22 -5
- package/dist/knowledge/platform/mobile.js +1 -1
- package/dist/knowledge/platform/mobileRules.js +1 -1
- package/dist/knowledge/platform/web.js +1 -1
- package/dist/knowledge/rules.js +79 -2
- package/dist/knowledge/styleResolve.js +27 -7
- package/dist/knowledge/styleResolveNative.js +4 -2
- package/dist/knowledge/themeResolve.js +1 -1
- package/dist/knowledge/themeSchema.js +2 -1
- package/dist/observability/trace.js +7 -2
- package/dist/platform/secrets.js +50 -2
- package/dist/resources/guides/component.md +1 -1
- package/dist/resources/guides/merge.md +5 -4
- package/dist/resources/guides/microservice.md +1 -1
- package/dist/resources/guides/middleware.md +1 -1
- package/dist/resources/guides/mobile-publish.md +24 -0
- package/dist/resources/guides/mobile.md +22 -6
- package/dist/resources/guides/page.md +14 -7
- package/dist/resources/guides/theme.md +1 -0
- package/dist/resources/schema-fingerprint.json +3 -3
- package/dist/resources/schemas/GlobalFunctionUpdateResultDto.json +2 -1
- package/dist/resources/schemas/OrganizationProjectDto.json +8 -0
- package/dist/resources/schemas/UpsertMobileStoreSettingsCommand.json +59 -0
- package/dist/tools/applications.js +6 -3
- package/dist/tools/common.js +9 -5
- package/dist/tools/environments.js +1 -1
- package/dist/tools/flows.js +5 -1
- package/dist/tools/mobileStore.js +21 -5
- package/dist/tools/pages.js +3 -3
- package/package.json +1 -1
|
@@ -164,6 +164,8 @@ export const TRANSFORM_RANGES = {
|
|
|
164
164
|
translateX: [-1000, 1000],
|
|
165
165
|
translateY: [-1000, 1000]
|
|
166
166
|
};
|
|
167
|
+
/** Frontend `TRANSFORM_FIELDS`: transform'lu ata `position: fixed` torununu hapsettiği için katmanlar ve kabuklar bu alanları `surfaceStyleGroup({ omit })` ile dışlar. */
|
|
168
|
+
const TRANSFORM_FIELDS = Object.keys(TRANSFORM_RANGES);
|
|
167
169
|
/** Sayı ya da sayısal metin, aralığın içinde; aksi `undefined` — frontend `transformValue`. */
|
|
168
170
|
export const transformValue = (value, field) => {
|
|
169
171
|
if (typeof value !== 'number' && (typeof value !== 'string' || value.trim() === ''))
|
|
@@ -434,7 +436,9 @@ const STATCARD_FLEX_ALIGN = {
|
|
|
434
436
|
left: 'flex-start',
|
|
435
437
|
center: 'center',
|
|
436
438
|
right: 'flex-end',
|
|
437
|
-
justify: 'stretch'
|
|
439
|
+
justify: 'stretch',
|
|
440
|
+
start: 'flex-start',
|
|
441
|
+
end: 'flex-end'
|
|
438
442
|
};
|
|
439
443
|
function composeStatCardParts(input, root) {
|
|
440
444
|
const value = input.valueStyle;
|
|
@@ -507,22 +511,26 @@ export const TYPED_STYLE_COMPONENTS = {
|
|
|
507
511
|
// onun altında kalıyordu (LAYOUT-006, 2026-09-29). Yüzey ve taşma köke.
|
|
508
512
|
// navigationStyle menüye gider, resolvedStyle'a girmez.
|
|
509
513
|
groups: ['surfaceStyle'],
|
|
510
|
-
ext: { overflowTarget: ROOT_TARGET }
|
|
514
|
+
ext: { overflowTarget: ROOT_TARGET },
|
|
515
|
+
omit: { surfaceStyle: TRANSFORM_FIELDS }
|
|
511
516
|
},
|
|
512
517
|
'layout-shell': {
|
|
513
518
|
// Aynı kök: zemin `.ng-layout-shell` KÖKÜNDE (SHELL-006, 2026-09-29).
|
|
514
519
|
groups: ['surfaceStyle'],
|
|
515
|
-
ext: { overflowTarget: ROOT_TARGET }
|
|
520
|
+
ext: { overflowTarget: ROOT_TARGET },
|
|
521
|
+
omit: { surfaceStyle: TRANSFORM_FIELDS }
|
|
516
522
|
},
|
|
517
523
|
'layout-region': {
|
|
518
524
|
// Zemin `.ng-layout-region` KÖKÜNDE, taşma kaydırılan gövdede (REG-002, 2026-09-29).
|
|
519
525
|
groups: ['surfaceStyle'],
|
|
520
|
-
ext: { overflowTarget: '&__body' }
|
|
526
|
+
ext: { overflowTarget: '&__body' },
|
|
527
|
+
omit: { surfaceStyle: TRANSFORM_FIELDS }
|
|
521
528
|
},
|
|
522
529
|
'auth-layout': {
|
|
523
530
|
// Zemin `.ng-auth` KÖKÜNDE (AUTH-006, 2026-09-29); yüzey ve taşma köke.
|
|
524
531
|
groups: ['surfaceStyle'],
|
|
525
|
-
ext: { overflowTarget: ROOT_TARGET }
|
|
532
|
+
ext: { overflowTarget: ROOT_TARGET },
|
|
533
|
+
omit: { surfaceStyle: TRANSFORM_FIELDS }
|
|
526
534
|
},
|
|
527
535
|
header: {
|
|
528
536
|
// Şeridin zemini ve alt çizgisi kökte (HDR-007, 2026-09-29).
|
|
@@ -535,6 +543,20 @@ export const TYPED_STYLE_COMPONENTS = {
|
|
|
535
543
|
groups: ['surfaceStyle'],
|
|
536
544
|
ext: { overflowTarget: ROOT_TARGET }
|
|
537
545
|
},
|
|
546
|
+
modal: {
|
|
547
|
+
// Panel portal ile düğüm kutusunun DIŞINA çizilir; yüzey panelin KÖKÜNDE
|
|
548
|
+
// derlenir, taşma da panelin köküne iner (modal ölçümü, 2026-10-05).
|
|
549
|
+
groups: ['surfaceStyle'],
|
|
550
|
+
ext: { overflowTarget: ROOT_TARGET },
|
|
551
|
+
omit: { surfaceStyle: TRANSFORM_FIELDS }
|
|
552
|
+
},
|
|
553
|
+
drawer: {
|
|
554
|
+
// Panel portal ile düğüm kutusunun DIŞINA çizilir; yüzey panelin KÖKÜNDE
|
|
555
|
+
// derlenir, taşma da panelin köküne iner (drawer ölçümü, 2026-10-05).
|
|
556
|
+
groups: ['surfaceStyle'],
|
|
557
|
+
ext: { overflowTarget: ROOT_TARGET },
|
|
558
|
+
omit: { surfaceStyle: TRANSFORM_FIELDS }
|
|
559
|
+
},
|
|
538
560
|
icon: {
|
|
539
561
|
// Yalnız hover: surface çipin (chipShape/chipColor), typography da boyutun
|
|
540
562
|
// (size) ve rengin (color) çift kanalı olurdu — görsel kararlar semantik
|
|
@@ -556,8 +578,6 @@ export const NODE_SURFACE_TYPES = new Set([
|
|
|
556
578
|
'table',
|
|
557
579
|
'datatable',
|
|
558
580
|
'collapsible-container',
|
|
559
|
-
'drawer',
|
|
560
|
-
'modal',
|
|
561
581
|
'popover',
|
|
562
582
|
'tabs',
|
|
563
583
|
'stepper',
|
|
@@ -456,8 +456,10 @@ const composeTypography = (typography, text, theme, context) => {
|
|
|
456
456
|
text.fontStyle = String(typography.fontStyle);
|
|
457
457
|
if (isSet(typography.textDecoration))
|
|
458
458
|
text.textDecorationLine = String(typography.textDecoration);
|
|
459
|
-
if (isSet(typography.textAlign))
|
|
460
|
-
|
|
459
|
+
if (isSet(typography.textAlign)) {
|
|
460
|
+
const align = String(typography.textAlign);
|
|
461
|
+
text.textAlign = align === 'start' ? 'left' : align === 'end' ? 'right' : align;
|
|
462
|
+
}
|
|
461
463
|
const letterSpacing = parsePanelLength(typography.letterSpacing, context);
|
|
462
464
|
if (typeof letterSpacing === 'number')
|
|
463
465
|
text.letterSpacing = letterSpacing;
|
|
@@ -29,7 +29,7 @@
|
|
|
29
29
|
* `theme_get` bunları `unknownKeys` olarak gösterir, `theme_update` şeması reddeder.
|
|
30
30
|
*/
|
|
31
31
|
export const THEME_TOP_LEVEL_KEYS = [
|
|
32
|
-
'mode', 'palette', 'paletteDark', 'fontFamily', 'headingFontFamily', 'typeScale',
|
|
32
|
+
'mode', 'direction', 'palette', 'paletteDark', 'fontFamily', 'headingFontFamily', 'typeScale',
|
|
33
33
|
'fontSize', 'fontWeight', 'borderRadius', 'radiusScale', 'spacing', 'spacingScale',
|
|
34
34
|
'shadow', 'defaultInputSize', 'colors', 'actions', 'presets', 'componentStyles',
|
|
35
35
|
'componentPresets'
|
|
@@ -30,7 +30,7 @@ export function summarizeTheme(theme) {
|
|
|
30
30
|
if (entries.length > 0)
|
|
31
31
|
overrides[key] = entries;
|
|
32
32
|
}
|
|
33
|
-
for (const key of ['fontFamily', 'headingFontFamily', 'fontSize', 'fontWeight', 'spacing', 'borderRadius', 'defaultInputSize']) {
|
|
33
|
+
for (const key of ['fontFamily', 'headingFontFamily', 'fontSize', 'fontWeight', 'spacing', 'borderRadius', 'defaultInputSize', 'direction']) {
|
|
34
34
|
if (record[key] !== undefined)
|
|
35
35
|
overrides[key] = record[key];
|
|
36
36
|
}
|
|
@@ -91,6 +91,7 @@ const ratioScale = (levels) => z.object(Object.fromEntries(levels.map(level => [
|
|
|
91
91
|
*/
|
|
92
92
|
export const themeInput = z.object({
|
|
93
93
|
mode: z.enum(['light', 'dark']),
|
|
94
|
+
direction: z.enum(['ltr', 'rtl', 'auto']),
|
|
94
95
|
palette: paletteSchema,
|
|
95
96
|
paletteDark: paletteSchema,
|
|
96
97
|
fontFamily: z.string().min(1),
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { AsyncLocalStorage } from 'node:async_hooks';
|
|
2
2
|
import { appendFileSync, existsSync, mkdirSync } from 'node:fs';
|
|
3
3
|
import { dirname, join } from 'node:path';
|
|
4
|
-
import { secretKeyPattern } from '../platform/secrets.js';
|
|
4
|
+
import { maskSecretLeaf, secretKeyPattern, secretPairName } from '../platform/secrets.js';
|
|
5
5
|
const defaultDirectory = join(process.cwd(), 'logs');
|
|
6
6
|
const activeTrace = new AsyncLocalStorage();
|
|
7
7
|
const activeRequest = new AsyncLocalStorage();
|
|
@@ -37,11 +37,16 @@ function clip(text, limit) {
|
|
|
37
37
|
function redact(value, depth = 0) {
|
|
38
38
|
if (depth > 8)
|
|
39
39
|
return '…';
|
|
40
|
+
// Günlük, yanıt maskesinin gördüğü ad/değer çiftini ve yaprak jetonu da görmeli: debug gövdesi ve
|
|
41
|
+
// `asUser` argümanı buraya ham gelir (g-01, 37 S-24).
|
|
42
|
+
if (typeof value === 'string')
|
|
43
|
+
return maskSecretLeaf(value);
|
|
40
44
|
if (Array.isArray(value))
|
|
41
45
|
return value.map(entry => redact(entry, depth + 1));
|
|
42
46
|
if (value && typeof value === 'object') {
|
|
47
|
+
const pairName = secretPairName(value);
|
|
43
48
|
return Object.fromEntries(Object.entries(value).map(([key, entry]) => {
|
|
44
|
-
if (secretKeyPattern.test(key))
|
|
49
|
+
if (secretKeyPattern.test(key === 'value' && pairName !== undefined ? pairName : key))
|
|
45
50
|
return [key, '***'];
|
|
46
51
|
if (/^stack(Trace)?$/i.test(key) && typeof entry === 'string') {
|
|
47
52
|
return [key, `${clip(entry.split(/\r?\n/)[0] ?? '', 240)} … (kalan stack kırpıldı)`];
|
package/dist/platform/secrets.js
CHANGED
|
@@ -1,8 +1,12 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Desen `trace.ts`'in eski kopyasından DARALTILDI: `pass` parçası `passed`/`bypass` gibi
|
|
3
3
|
* anahtarları da yakalıyordu. Günlükte zararsızdı, tool yanıtında veri kaybı olurdu.
|
|
4
|
+
*
|
|
5
|
+
* `cookie` yalnız TAM ad olarak eşleşir (g-01, 37 S-24): paketli şemalarda `StepRuleDto.cookieKey`
|
|
6
|
+
* çerezin ADINI taşır; parça eşleşmesi onu `***` yapıp workflow_get → update turunu bozardı (GW-001).
|
|
7
|
+
* `setcookie` de bilinçli dışarıda: workflow adım tipinin adıdır, sır değildir.
|
|
4
8
|
*/
|
|
5
|
-
export const secretKeyPattern = /password|passwd|secret|token|authorization|api[-_]?key|credential|connection[-_]?string
|
|
9
|
+
export const secretKeyPattern = /password|passwd|secret|token|authorization|api[-_]?key|credential|connection[-_]?string|^cookies?$/i;
|
|
6
10
|
/**
|
|
7
11
|
* Sırrın DEĞERİNİ değil adını, yerini ya da türünü taşıyan anahtarlar: `credentialRef`, `tokenUrl`,
|
|
8
12
|
* `apiKeyHeader`, `secretFields`, `credentialKind`. Maskelenirlerse süreç tanımındaki
|
|
@@ -14,18 +18,62 @@ import { looksLikeJwt, rememberToken } from './tokenVault.js';
|
|
|
14
18
|
const maskedValue = '***';
|
|
15
19
|
const maxPaths = 20;
|
|
16
20
|
const maxDepth = 12;
|
|
21
|
+
/**
|
|
22
|
+
* Ad/değer çiftinde `value`'nun yerine geçen ad (g-01, 37 S-24). Debug girdisi isteğin başlık ve
|
|
23
|
+
* çerezlerini `{ expression: 'Authorization', value: 'Bearer …', source, member }` olarak yazar; sırrın
|
|
24
|
+
* adı anahtarda değil kardeş alanda durduğu için anahtar kuralı hiçbirini görmüyordu.
|
|
25
|
+
*
|
|
26
|
+
* `type` ya da `order` taşıyan satır platform TANIMIDIR — `StepQueryParameterDto {name,value,type}`,
|
|
27
|
+
* `StepCustomParameterDto {name,value,order}`, `QueryParameter {name,type,value}`: değer bağlama
|
|
28
|
+
* ifadesidir (`input.token`), maskelenirse get → update turunda `***` geri yazılır (GW-001).
|
|
29
|
+
* `expression` + `value` hiçbir paketli şemada geçmez, o yüzden bu ayrımın dışındadır.
|
|
30
|
+
*/
|
|
31
|
+
export function secretPairName(record) {
|
|
32
|
+
if (!('value' in record))
|
|
33
|
+
return undefined;
|
|
34
|
+
if (typeof record.expression === 'string')
|
|
35
|
+
return record.expression;
|
|
36
|
+
if ('type' in record || 'order' in record)
|
|
37
|
+
return undefined;
|
|
38
|
+
if (typeof record.name === 'string')
|
|
39
|
+
return record.name;
|
|
40
|
+
return typeof record.key === 'string' ? record.key : undefined;
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Anahtarsız string yapraktaki kimlik (g-01): `eyJ…` ile başlayan JWT ve `Bearer <jeton>`.
|
|
44
|
+
* `maskSecretText`'ten DAR: o konsol metni içindir ve her üç parçalı noktalı adı JWT sayar —
|
|
45
|
+
* yanıtta `organization.something.localhost` gibi bir adı körleştirirdi. Bearer'dan sonra en az
|
|
46
|
+
* 16 karakter aranır; rehber metnindeki "Bearer olarak" maskelenmez.
|
|
47
|
+
*/
|
|
48
|
+
export function maskSecretLeaf(text) {
|
|
49
|
+
return text
|
|
50
|
+
.replace(/Bearer\s+[A-Za-z0-9._~+/=-]{16,}/gi, `Bearer ${maskedValue}`)
|
|
51
|
+
.replace(/\beyJ[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+\.[A-Za-z0-9_-]*/g, maskedValue);
|
|
52
|
+
}
|
|
17
53
|
function walk(value, path, masked, refs, depth) {
|
|
18
54
|
if (depth > maxDepth)
|
|
19
55
|
return value;
|
|
56
|
+
if (typeof value === 'string') {
|
|
57
|
+
const safe = maskSecretLeaf(value);
|
|
58
|
+
if (safe === value)
|
|
59
|
+
return value;
|
|
60
|
+
if (masked.length < maxPaths)
|
|
61
|
+
masked.push(path);
|
|
62
|
+
if (looksLikeJwt(value) && Object.keys(refs).length < maxPaths)
|
|
63
|
+
refs[path] = rememberToken(value);
|
|
64
|
+
return safe;
|
|
65
|
+
}
|
|
20
66
|
if (Array.isArray(value))
|
|
21
67
|
return value.map((entry, index) => walk(entry, `${path}[${index}]`, masked, refs, depth + 1));
|
|
22
68
|
if (value && typeof value === 'object') {
|
|
23
69
|
const result = {};
|
|
70
|
+
const pairName = secretPairName(value);
|
|
24
71
|
for (const [key, entry] of Object.entries(value)) {
|
|
25
72
|
const child = path === '' ? key : `${path}.${key}`;
|
|
73
|
+
const name = key === 'value' && pairName !== undefined ? pairName : key;
|
|
26
74
|
// Boole değer sır taşıyamaz: `hasExpoAccessToken: true` maskelenince ajan token'ın kayıtlı
|
|
27
75
|
// olup olmadığını öğrenemiyordu (push bildirimleri g-01, 2026-10-02; GW-001 ailesi).
|
|
28
|
-
if (secretKeyPattern.test(
|
|
76
|
+
if (secretKeyPattern.test(name) && !secretNameKeyPattern.test(name) && typeof entry !== 'boolean' && entry !== null && entry !== undefined && entry !== '') {
|
|
29
77
|
if (masked.length < maxPaths)
|
|
30
78
|
masked.push(child);
|
|
31
79
|
// Maskelenen değer bir JWT ise kimlik testinin yolu kapanmasın: jeton kasada tutulur ve
|
|
@@ -197,7 +197,7 @@ Her şema düğümünde iki sözlük vardır; ikisi de ortak render katmanında
|
|
|
197
197
|
|
|
198
198
|
| `appearance` | Değerler | Anlamı |
|
|
199
199
|
|---|---|---|
|
|
200
|
-
| `heightType` | `auto` · `fixed` · `fill` | `auto`: içerik kadar (önerilen — Kural 1d); `fixed`: `heightString` varsa o, yoksa `blocks.h × 10px`; `fill`: **kalanı doldur** — ebeveynin bıraktığı yer (ızgarada `height:100%`, flex sütununda `flex:1 1 auto`, flex satırında `align-self:stretch`). Tam ekran yerleşim kabuğu bununla kurulur, bkz. `platform_guide('layout')` |
|
|
200
|
+
| `heightType` | `auto` · `fixed` · `fill` | `auto`: içerik kadar (önerilen — Kural 1d); `fixed`: `heightString` varsa o, yoksa `blocks.h × 10px`; `fill`: **kalanı doldur** — ebeveynin bıraktığı yer (ızgarada `height:100%`, flex sütununda `flex:1 1 auto`, flex satırında `align-self:stretch`). Tam ekran yerleşim kabuğu bununla kurulur, bkz. `platform_guide('layout')`. **Mobilde (≤ 767 px) mobil bloğu olmayan her düğüme `height: auto !important` yazılır**: yüksekliği `height` olarak yazılan `fixed` düğüm (flex ya da CSS grid ebeveyni, stretch yokken) içeriğine göre çizilir; stretch'li satırdaki `min-height` ve 12 kolonlu eski ebeveynin `grid-row` yüksekliği bundan bağımsızdır. Yazılı yüksekliği mobilde de istiyorsan (`heightType: 'fixed'` iken) `appearance.className`'e ayrılmış **`ng-keep-height-mobile`** belirtecini yaz (`className: "hero ng-keep-height-mobile"`) |
|
|
201
201
|
| `widthType` | `cover` · `contain` · `auto` · `fixed` · `fill` | İlk ikisi görsel/medya sığdırma. Kalanı **flex (ve eski cssgrid) kabın çocuğunun genişlik modu** (2026-09-01): `auto` içerik kadar, `fixed` `widthString`'den, `fill` kabın tüm genişliği (satırda `flex:1 1 100%`, sütunda `align-self:stretch`). **Yazılmazsa eski 12 kolon `blocks.size.w` payı geçerlidir** — öncelik: `fill` > `fixed` > `auto` > span |
|
|
202
202
|
| `widthString` / `heightString` | CSS uzunluğu | Sabit ölçü; **yalnız ilgili `*Type: 'fixed'` iken okunur** (aksi PAGE-017 uyarısı). Kabul: çıplak sayı (px sayılır) · `px % vw vh vmin vmax rem em ch` birimli değer · `calc(...)`. Negatif/`pt`/`fr`/`auto` GEÇMEZ — platform kaydeder ama panel ve runtime sessizce düşürür (PAGE-018). Eski `widthPx` alanı 2026-09-01'de kaldırıldı |
|
|
203
203
|
| `marginType` / `marginValue` | `normal` · `none` · `custom` + px | Dış boşluk: **yazılmazsa `normal` sayılır** = her kenara 4px (`spacing.ts` `CANVAS_NORMAL_MARGIN`); custom=`marginValue`. Bileşenleri dip dibe dizmek için `none` ŞART — `gap:'none'` olsa bile normal margin komşular arasında 8px bırakır (sıkı dizilim tarifi: `platform_guide('page')` Kural 1f-2) |
|
|
@@ -60,14 +60,15 @@ Studio'nun runtime rotasının üç şeridi vardır ve **ortamı URL belirler**:
|
|
|
60
60
|
|---|---|
|
|
61
61
|
| `production` | `/view/{proje}/{app}/{sayfa}` |
|
|
62
62
|
| `development` | `/dev/{proje}/{app}/{sayfa}` |
|
|
63
|
-
| dal | `/branch/{
|
|
63
|
+
| dal | `/branch/{slug}/{proje}/{app}/{sayfa}` — slug, adın `/` yerine `-` taşıyan hâli (`feature/odeme` → `feature-odeme`) |
|
|
64
64
|
|
|
65
65
|
Sayfanın **bütün** istekleri o dala gider: sayfa şeması da, akışların uç çağrıları da. Yani kare
|
|
66
66
|
"yarı doğru" değildir — gördüğünüz ekran dalın gerçek ekranıdır.
|
|
67
67
|
|
|
68
|
-
Reddedilen tek şey **dal adı olamayacak bir ad**: harfle başlamayan, küçük harf/rakam/alt çizgi
|
|
69
|
-
dışında karakter taşıyan, 3-31 aralığının dışına çıkan ya da rezerve bir
|
|
70
|
-
`pg_*` …). Projedeki dalları `environment_list` verir
|
|
68
|
+
Reddedilen tek şey **dal adı olamayacak bir ad**: harfle başlamayan, küçük harf/rakam/alt çizgi/tire
|
|
69
|
+
ve `/` ile ayrılmış parçalar dışında karakter taşıyan, 3-31 aralığının dışına çıkan ya da rezerve bir
|
|
70
|
+
ad (`public`, `api`, `pg_*` …). Projedeki dalları ve slug'larını `environment_list` verir; tenant
|
|
71
|
+
çağrılarına `environment` olarak adı da slug'ı da geçebilirsiniz.
|
|
71
72
|
|
|
72
73
|
2026-09-09 öncesinde bu çağrı **reddediliyordu**: runtime rotası dal adı taşımıyordu, ortam
|
|
73
74
|
modül içi bir store'dan geliyordu ve dal adıyla istenen kare development'ı gösterirdi. O günlerin
|
|
@@ -515,7 +515,7 @@ hem `joins` ile getirin hem `selectFields`'a koyun; ondan sonra `filters[0][fiel
|
|
|
515
515
|
çalışır. m2m alanlar da desteklenir (`EXISTS` alt sorgusuna çevrilir).
|
|
516
516
|
|
|
517
517
|
> **Expression filtresi istek parametresi TAŞIYAMAZ.** Microservice'te expression'lar
|
|
518
|
-
> `workflowContext.
|
|
518
|
+
> `workflowContext.CreateJavascriptEngine()` ile eval edilir ve o bağlamda `input` global'i
|
|
519
519
|
> **yoktur** — `value: "input.doctor_id"` yazarsan uç `500 JavaScriptException: input is not
|
|
520
520
|
> defined` verir (`input` workflow table step'inin sözlüğüdür, microservice'in değil).
|
|
521
521
|
> Expression'ın okuyabildiği iki şey vardır: **sabit değerler** (`"'Aktif'"`, `new Date()`) ve
|
|
@@ -52,7 +52,7 @@ cevabı adımın ifadesinde gizlidir. Ayrıntı ve yazım ölçütü: `platform_
|
|
|
52
52
|
3. **Değişkenler istek boyunca yaşar.** `set-user` / `variable` step'lerinin kurduğu
|
|
53
53
|
değişkenler istek kapsamındaki `WorkflowContext`'e yazılır (`AddVariable`) ve her akış
|
|
54
54
|
koşusunun JS motoru o anki değişkenlerden kurulur (`WorkflowRunner` →
|
|
55
|
-
`
|
|
55
|
+
`CreateJavascriptEngine()`). Hedef microservice'in **expression filtrelerinde/alanlarında**
|
|
56
56
|
ve step ifadelerinde doğrudan okunur (`user.id`, `settings.auth_method`). Ayrıca microservice
|
|
57
57
|
yolunda middleware'in response gövdesindeki `user` / `data` anahtarları da değişkene
|
|
58
58
|
terfi eder (`ExecuteMicroserviceBehavior.cs:105` — sayfa yolunda bu terfi YOKTUR).
|
|
@@ -70,6 +70,7 @@ metni yeni numarayla yeniden dener.
|
|
|
70
70
|
| P-10 | `manual declarations_pending` | kullanıcı onayıyla `mobile_store_update { declarations }` (engellemez) |
|
|
71
71
|
| P-11 | push (yalnız `testflight` / `internal`): `warn push_incomplete` (kimlik eksik — derleme push'suz çıkar); `push_unreadable` (kimliğin şifresi çözülemiyor), `push_profile_stale` (profil APNs anahtarından önce üretilmiş), `push_firebase_mismatch` (`google-services.json` başka Firebase projesinin), `push_package_mismatch` (dosya yüklendikten sonra Android paket adı değişmiş), `push_shell_outdated` (kabuk paketi push taşımıyor) | Studio › Yayın › Hesaplar ve kimlik bilgileri (insan yükler); `push_profile_stale`: profili yeniden üret; `push_shell_outdated`: platform ayarı (destek). Push kapalı ya da hazırsa satır `ok`'tur ve dryRun'da görünmez — hazır olduğunu `mobile_store_get { manifest: true }` → `manifest.push` söyler |
|
|
72
72
|
| P-12 | izinler: `warn permissions_shell_outdated` (yayındaki kabuk izin kümesini taşımıyor: sabit izinler bildirilir, kamera çalışmaz, kipler uygulanmaz — başka P-12 satırı çıkmaz); kabuk taşıyorsa `fail permission_off_in_use` (kip `off` ama production o izni yazılı olarak kullanıyor) ya da `warn launch_permissions_shell_outdated` (açılışta sorulan izin var, kabuk açılışta sormuyor; kabuk kümeyi de taşımıyorsa bu cümle ilk uyarının iletisine eklenir) | `mobile_store_update { settings: { permissionModes } }` ya da kullanımı kaldırıp dağıtım; açılış uyarısında daha yeni kabuk ya da izni akış adımından isteme |
|
|
73
|
+
| P-13 | reklam (yalnız `ads.enabled`): `warn ads_app_id_missing` (bu platformun AdMob App ID'si yok — derleme reklamsız çıkar), `warn ads_shell_outdated` (yayındaki kabuk reklam taşımıyor), yükleme hedefinde `manual ads_test_mode` (test kipi açık: mağazada test reklamı görünür), değilse `ok ads_ready` (yerleşim sayısıyla) | `mobile_store_update { settings: { ads } }`; test kipini yayından önce `testMode: false` |
|
|
73
74
|
|
|
74
75
|
Satırın `message` ve `fixHint`'i backend'den, istek dilinde gelir.
|
|
75
76
|
|
|
@@ -98,9 +99,32 @@ sayfa · bileşen ya da akış adımı), `build` (production: `carried`, `reason
|
|
|
98
99
|
taşımayan son mağaza derlemelerini (TestFlight / Play iç test) listeler; izin kullanılıyorsa yanıt
|
|
99
100
|
`permissionWarnings` taşır — production kullanıyorsa yeni derleme şimdi, yalnız okunan ortam kullanıyorsa
|
|
100
101
|
dağıtımdan sonra gerekir.
|
|
102
|
+
- **İzleme izni (`tracking`, iOS ATT, kök ADR 0011):** derleme taşıyorsa App Store Connect App Privacy'de "Device
|
|
103
|
+
ID — Used to Track You", Play Console Data safety'de "Advertising ID" beyanı insanın işidir; izin penceresinden önce
|
|
104
|
+
teşvik ya da kendi ön pencerenizi göstermeyin (App Store 5.1.2). Görünüm bunu `permissionWarnings`'te söyler
|
|
105
|
+
(`MOB-027`). Taşınmayan ikiliye `expo-tracking-transparency` hiç bağlanmaz. Reklam açıksa izin, App ID'si olan platformun derlemesinde taşınır (yalnız Android'de reklam varsa iOS ikilisi ATT taşımaz).
|
|
101
106
|
- Tarama `setter` ile yazılan tetikleyici prop'u da sayar; `code` adımı, fonksiyon gövdesi, `webview` / `html`
|
|
102
107
|
içeriği ve dış bileşen görünmez — onlar için kip `on`.
|
|
103
108
|
|
|
109
|
+
## Reklamlar
|
|
110
|
+
|
|
111
|
+
Google Mobile Ads (kök ADR 0012) mağaza ayarıdır: `settings.ads = { enabled, iosAppId, androidAppId, testMode,
|
|
112
|
+
consentForm, appOpenUnit, units: [{ name, format, ios, android }] }`. App ID `ca-app-pub-…~…`, birim kimliği
|
|
113
|
+
`ca-app-pub-…/…`; `name` `^[a-z][A-Za-z0-9]{0,39}$` ve tekil (bileşen `adBanner.unit` ve adım `ad-show.value` bu adla
|
|
114
|
+
anar); `format` `banner` · `interstitial` · `rewarded` · `rewardedInterstitial` · `appOpen`; bir platformun kimliği boşsa
|
|
115
|
+
yerleşim o platformda yoktur. Biçim hatası `MOB-022` ile yazılmadan durur.
|
|
116
|
+
|
|
117
|
+
- SDK ikiliye yalnız `enabled` iken ve o platformun App ID'si varken bağlanır; o platformun derlemesine `tracking`'i de taşıtır (kip
|
|
118
|
+
`off` olamaz, 422).
|
|
119
|
+
- `testMode: true` (varsayılan) gerçek birim yerine Google test birimini gösterir — geliştirirken gerçek birime
|
|
120
|
+
tıklamak AdMob hesabını kapattırabilir. Yayından önce `false` (P-13 `manual`).
|
|
121
|
+
- `consentForm: true` (varsayılan) açılışta AdMob UMP rıza formunu gerekiyorsa gösterir; rıza mesajının kendisi AdMob
|
|
122
|
+
konsolunda kurulur (insan). Sıra: rıza → açılış izinleri (ATT) → SDK başlatma; rıza reklama izin vermezse uygulama
|
|
123
|
+
reklamsız sürer.
|
|
124
|
+
- `appOpenUnit` `appOpen` biçimli bir yerleşimin adı; soğuk açılışta bir kez gösterilir.
|
|
125
|
+
- `mobile_store_get` reklam açıkken `adsWarnings` döner (`MOB-026`): test kipi, App ID'si olan platformda birimi olmayan
|
|
126
|
+
yerleşim, konsol beyanları (App Privacy, Data safety, AdMob rıza mesajı — insanın işi).
|
|
127
|
+
|
|
104
128
|
## Hata kodları
|
|
105
129
|
|
|
106
130
|
`mobile_build_status` → `error.code`.
|
|
@@ -37,7 +37,7 @@ Mobil uygulamanın bileşen kataloğu ayrıdır (backend satırları `(slug, typ
|
|
|
37
37
|
mobil farkları: düşen prop, değişen etkileşim, mobile özgü prop notları) ve `mobileNotes` (bütün
|
|
38
38
|
mobil bileşenlere geçen notlar). Web'de karşılığı olmayan bileşende (`app-bar`, `safe-area`,
|
|
39
39
|
`scroll-view`, `choice-chips`, `tooltip`, `markdown`, `calendar`, `stack`, `list-item`, `fab`, `timer`,
|
|
40
|
-
`audio`, `lottie`, `webview`, `map`, `grouped-list`, `floating-container`, `skeleton`, `photo-viewer`) bilgi yalnız
|
|
40
|
+
`audio`, `lottie`, `webview`, `map`, `adBanner`, `grouped-list`, `floating-container`, `skeleton`, `photo-viewer`) bilgi yalnız
|
|
41
41
|
`mobileKnowledge`'tadır. Olay yükleri
|
|
42
42
|
(`events[].payload`) önce mobile özgü sözleşmeden okunur (`app-bar.clickAction`, `repeater.swipe`,
|
|
43
43
|
`choice-chips.change`…), yoksa web'inkinden.
|
|
@@ -91,6 +91,11 @@ mobil bileşenlere geçen notlar). Web'de karşılığı olmayan bileşende (`ap
|
|
|
91
91
|
OpenStreetMap): görünüm `latitude`/`longitude`/`zoom` sayılarıdır ve akış yazınca harita oraya gider;
|
|
92
92
|
kullanıcı haritayı hareket ettirince `regionChange { latitude, longitude, zoom }` düğüme geri yazılır,
|
|
93
93
|
işaretçiye dokunmak `markerClick { markerId }` yayar.
|
|
94
|
+
`adBanner` mobile özgüdür (Google Mobile Ads banner'ı, kök ADR 0012): `unit` mağaza ayarındaki reklam
|
|
95
|
+
yerleşiminin **adıdır** (birim kimliği değil), `size` `banner` 320×50 · `largeBanner` · `mediumRectangle` 300×250 ·
|
|
96
|
+
`fullBanner` · `leaderboard` · `adaptive` · `inlineAdaptive`. Reklamı yalnız mağaza sürümü çizer; kanvas, önizleme ve
|
|
97
|
+
oynatıcı boyutlu yer tutucu çizer ve çalışma kipinde `fail { code: 'unavailable' }` yayar. Mağaza sürümünde reklam
|
|
98
|
+
hazır değilken (rıza, SDK) yer kaplamaz; yerleşim yoksa ya da yüklenemezse `fail` yayar ve yer kaplamaz.
|
|
94
99
|
Dördüncü adım: `datepicker`, `timepicker` ve `datetimepicker` web ikizleridir — kutuya dokununca cihazda
|
|
95
100
|
işletim sisteminin seçicisi (iOS alttan açılan sayfa, seçim Tamam'la yazılır; Android iletişim kutusu),
|
|
96
101
|
önizlemede tarayıcının seçicisi açılır; değer ve `change` yükü web ile aynıdır. Sistem seçicisi tek tek
|
|
@@ -215,8 +220,8 @@ bölümleri bileşen render anında çıkarır.
|
|
|
215
220
|
`Section` boş nesnedir (`Row` gibi). Öğe repeater satırıyla aynıdır: `Row`, `RowIndex`,
|
|
216
221
|
`{{ item.<alan> }}`; akışta tıklanan satır `tasks.selectedItem`. Şablon çocukları setter hedefi
|
|
217
222
|
değildir — satır dışına (sayfadaki bir metne, modala) yazın.
|
|
218
|
-
4. **Olaylar.** `itemPress { index, row }` (yalnız `itemPressable`; öğe şablonu `
|
|
219
|
-
ise dokunuş onun `click`'ine gider), `swipe { key, index, row }` (`swipeActions`),
|
|
223
|
+
4. **Olaylar.** `itemPress { index, row }` (yalnız `itemPressable`; öğe şablonu `button` ya da `click`'i
|
|
224
|
+
bağlı `list-item` ise dokunuş onun `click`'ine gider — bağsız `list-item` basılamaz), `swipe { key, index, row }` (`swipeActions`),
|
|
220
225
|
`change { selectedIndex }`, `sectionPress` (yük `Section`). `index` `rows` içindeki DÜZ indekstir.
|
|
221
226
|
5. **Yapışkan gün başlığı.** `stickySectionHeaders: true` + `appearance.heightType: 'fixed'` +
|
|
222
227
|
`heightString`. Sayfanın kendisi kaydırıcıdır; `auto` ya da `fill` liste içerik kadar uzar ve başlık
|
|
@@ -418,8 +423,9 @@ Motorun kendi adımları (`setter`, `condition`, `microservice`, `code`, `functi
|
|
|
418
423
|
| `notification-permission` | bildirim izni ister (sorulmuşsa yeniden sormaz, kalıcı red yeniden sorulmaz). Sonuç `Steps.<adım>.data.status`: `granted` · `denied` · `undetermined`; red hata değildir, hep `always` |
|
|
419
424
|
| `notification-schedule` | yerel bildirim planlar (`expo-notifications`; uzak push değil). `args.name` kimlik (düz metin; aynı kimlik eskisinin yerini alır), `args.title` başlık (düz metin), `args.message` gövde (**ifade**), `args.value` zaman (**ifade**): sonucu `'HH:mm'` → her gün, `{ weekday: 1–7, time: 'HH:mm' }` → her hafta (1 = Pazartesi), ISO tarih-saat → bir kez. `args.path` dokununca açılacak sayfanın slug'ı (+ `args.query` ifade), boşsa uygulama açılır. İzin yoksa `error.status: 'denied'`, geçmiş ya da okunamayan zaman `invalid`: `error` dalı → hata akışı → `always`. Web'de desteklenmez (`unsupported`) — sayfa kapalıyken tarayıcı gösteremez |
|
|
420
425
|
| `notification-cancel` | `args.name` kimlikli planlı bildirimi siler, `'*'` hepsini; hep `always` |
|
|
421
|
-
| `permission` | bir cihaz iznine bakar ya da ister. `args.value` izin türü, **düz metin — ifade olamaz** (mağaza derlemesinin taşıyacağı izinler bu değerden, akış koşmadan hesaplanır; `FLOW-038`); `args.name` kip: `check` yalnız okur (sistem penceresi açılmaz), `request` verilmemiş ve hâlâ sorulabilen izni ister (boş → `request`). Sonuç `Steps.<adım>.data = { status, canAskAgain }`: `granted` · `denied` · `undetermined` · `unavailable` (derleme bu izni taşımıyor ya da web önizlemesi); `canAskAgain: false` → yalnız Ayarlar'dan değişir. Red hata değildir, hep `always`. Türler: `mobile_store_get { detail: ['permissions'] }` (`support: supported` satırlarının `key`'i) |
|
|
426
|
+
| `permission` | bir cihaz iznine bakar ya da ister. `args.value` izin türü, **düz metin — ifade olamaz** (mağaza derlemesinin taşıyacağı izinler bu değerden, akış koşmadan hesaplanır; `FLOW-038`); `args.name` kip: `check` yalnız okur (sistem penceresi açılmaz), `request` verilmemiş ve hâlâ sorulabilen izni ister (boş → `request`). Sonuç `Steps.<adım>.data = { status, canAskAgain }`: `granted` · `denied` · `undetermined` · `unavailable` (derleme bu izni taşımıyor ya da web önizlemesi); `canAskAgain: false` → yalnız Ayarlar'dan değişir. `tracking` (iOS App Tracking Transparency) sonucu ayrıca `advertisingId` taşır: izin `granted` iken IDFA (iOS) / AAID (Android), değilse `null`; oynatıcı `tracking`'i hiç taşımaz (`unavailable`). Red hata değildir, hep `always`. Türler: `mobile_store_get { detail: ['permissions'] }` (`support: supported` satırlarının `key`'i) |
|
|
422
427
|
| `open-settings` | uygulamanın sistem ayar sayfasını açar; `Steps.<adım>.data.opened`. Argüman almaz |
|
|
428
|
+
| `ad-show` | tam ekran reklam (kök ADR 0012). `args.value` mağaza ayarındaki yerleşimin **adı**, düz metin (`FLOW-039`); biçimi `interstitial` · `rewarded` · `rewardedInterstitial` olmalı. Reklam yüklenip gösterilir, kullanıcı kapatınca `Steps.<adım>.data = { shown: true, reward }` ile sürer — `reward` ödüllü reklamı sonuna kadar izleyende `{ type, amount }`, değilse `null` (ödül ayrı dal değil, `condition` ile okunur). 10 sn'de reklam yoksa, SDK hata verirse, derleme reklam taşımıyorsa ya da rıza yoksa `error.status: 'unavailable'`; tanımsız ya da banner yerleşimi `invalid`: `error` dalı → hata akışı → `always`. Oynatıcı ve web önizlemesi her zaman `unavailable` — reklamsız dalı orada sınayın. Paletten yalnız mobilde |
|
|
423
429
|
|
|
424
430
|
Bildirim adımları paletten yalnız mobil uygulamada sunulur. Tarif — S04 "her gün hatırlat": önce
|
|
425
431
|
`notification-permission` (S03 düğmesi), sonra `timepicker`'ın değeriyle `notification-schedule`
|
|
@@ -434,6 +440,15 @@ kamera/galeri kaynağı izni kendileri de ister — `permission` adımı *ne zam
|
|
|
434
440
|
okumak içindir. `unavailable` bir hata değil, derlemenin o izni taşımadığının haberidir
|
|
435
441
|
(`platform_guide('mobile-publish')` §İzinler).
|
|
436
442
|
|
|
443
|
+
**Reklam tarifi** (kök ADR 0012): (1) `mobile_store_update { settings: { ads: { enabled: true, iosAppId, androidAppId,
|
|
444
|
+
testMode: true, units: [{ name, format, ios, android }] } } }` — reklam, App ID'si olan platformun derlemesine `tracking`'i de sokar (ATT metni
|
|
445
|
+
gider, kip `off` olamaz); (2) banner için sayfaya `adBanner` `unit: '<banner yerleşimi>'`; (3) geçiş/ödüllü reklam için
|
|
446
|
+
düğme akışına `ad-show` `value: '<yerleşim>'`, `error` dalında reklamsız devam; (4) ödül `condition`:
|
|
447
|
+
`Steps.<adım>.data?.reward` doluysa ödülü ver. Açılış reklamı adım değil ayardır (`ads.appOpenUnit`). Sıra cihazda:
|
|
448
|
+
rıza formu → açılış izinleri (ATT) → SDK; iOS'ta izleme izni yoksa reklam kişiselleştirilmeden istenir. Yerleşim adının
|
|
449
|
+
ayarda tanımlı olduğu sayfa/akış yazımında denetlenemez — `mobile_store_get` `settings.ads.units` ve `adsWarnings`
|
|
450
|
+
(`MOB-026`); yayın ayrıntısı `platform_guide('mobile-publish')` §Reklamlar.
|
|
451
|
+
|
|
437
452
|
`style`, `accuracy`, `reverseGeocode` gibi anahtarlar sözleşmede yoktur ve platform onları sessizce düşürür
|
|
438
453
|
(`FLOW-029`). "Haritada aç" için adım yoktur: `map`'in `press` olayı düğüme `mapsUrl` yazar, akış
|
|
439
454
|
`redirect` `isRaw: true`, `path: '<harita>.mapsUrl'` ile açar. Tarif — L05 "mevcut konumumu kullan":
|
|
@@ -544,6 +559,7 @@ yaptıkları. Derlenen uygulama
|
|
|
544
559
|
- Uygulama içi tema seçimi (Sistem / Açık / Koyu) bir ayardır, bileşen değil: akış `storage` adımıyla
|
|
545
560
|
`zeyrek:theme-mode` anahtarına `system` · `light` · `dark` yazar (ör. segmentli `choice-chips`'in `change`'i,
|
|
546
561
|
`value: '<çipler>.value'`); barındırıcı anahtarı dinler, tema anında döner ve cihazda kalıcıdır — durum çubuğu da
|
|
547
|
-
izler.
|
|
548
|
-
`
|
|
562
|
+
izler. Seçim yazılmadıysa (ilk açılış) tema **açıktır**; cihazın şeması yalnız `system` açıkça seçilince izlenir.
|
|
563
|
+
Web'in `theme-toggle`'ı mobilde yoktur. Seçili düğme saklanan seçimi `LocalStorage`'dan okur:
|
|
564
|
+
`{{ LocalStorage['zeyrek:theme-mode'] || 'light' }}` (ARC-147).
|
|
549
565
|
- Dar ekran kırılımı (`hideOn: 'mobile'`) mobil uygulamada anlamsızdır: ekran zaten tek boydur.
|
|
@@ -135,9 +135,9 @@ alanlar denetlenmez); katalog okunamazsa denetim susar ve yazma eskisi gibi geç
|
|
|
135
135
|
**Düğümü silip yeniden kurmayın:** o yol ona yazan akış setter'larını sessizce öldürür
|
|
136
136
|
(`FLOW-018`, aşağıda Kural 2).
|
|
137
137
|
- **Aynı ızgara hücrelerini iki komponente veremezsin.** Studio'da komponent tuvale bırakıldığında
|
|
138
|
-
çakışan kardeşleri aşağı itme işini
|
|
139
|
-
(`
|
|
140
|
-
/ `page_update` o
|
|
138
|
+
çakışan kardeşleri aşağı itme işini Studio'nun ızgara motoru yapar
|
|
139
|
+
(`@ng-apps/screen-builder` `moveItem`); komple şema yazan `page_create`
|
|
140
|
+
/ `page_update` o motordan geçmez, yani verdiğin koordinatlar olduğu gibi kayda girer ve tuvalde
|
|
141
141
|
komponentler üst üste biner. **Gateway aynı algoritmayı şemanın tamamına uyguluyor** (kök dizi +
|
|
142
142
|
her `children`, yalnız desktop `blocks[]` girdisi; `x`/`w`/`h` ve dizi sırası korunur, çakışanın
|
|
143
143
|
`position.y`'si aşağı kayar) ve yanıtta `gatewayNormalized: "çakışan komponentler ızgarada aşağı
|
|
@@ -174,10 +174,9 @@ alanlar denetlenmez); katalog okunamazsa denetim susar ve yazma eskisi gibi geç
|
|
|
174
174
|
## Kural 1b — `page_build` YOK
|
|
175
175
|
|
|
176
176
|
Entity'den otomatik sayfa üreten `page_build` tool'u **kaldırıldı**. Platformun
|
|
177
|
-
`POST /pages/{app}/build/{page}` ucu
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
platformda zaten var → doğru yol yukarıdaki micro-op hattıdır.
|
|
177
|
+
`POST /pages/{app}/build/{page}` ucu ve onun delege ettiği sayfa üretme servisi de 2026-09-18'de
|
|
178
|
+
kaldırıldı; otomatik sayfa üretimi yok. Sayfa/komponent şemasının tam karşılığı platformda zaten
|
|
179
|
+
var → doğru yol yukarıdaki micro-op hattıdır.
|
|
181
180
|
|
|
182
181
|
## Kural 1c — Komponente VERİ bağlama: `mount` olayı + flow (setter adımı)
|
|
183
182
|
|
|
@@ -879,6 +878,14 @@ Tire yerine `_` kullan.
|
|
|
879
878
|
> `component_schema_patch` yamalarında), `layout_create`, `layout_update(views)` ve `layout_patch`;
|
|
880
879
|
> `page_schema_validate` aynı kararı verir. Ad doldurulduktan sonra denetlenir: geçersiz bir
|
|
881
880
|
> elementId adsız gelirse o da reddedilir.
|
|
881
|
+
>
|
|
882
|
+
> **`name` hiç verilmezse** gateway onu `elementId` ile tamamlar ve `gatewayNormalized`'da `name`
|
|
883
|
+
> diye bildirir: adsız düğümü platform kabul eder ama sayfada bir akış ya da `{{ }}` kod prop'u
|
|
884
|
+
> varsa FlowEngine `runCode` "Cannot read properties of undefined (reading 'catch')" ile düşer, akış
|
|
885
|
+
> `running:1`'de takılır ve hata adsız düğümü işaret etmez. Tamamlama PAGE-009 denetiminden ÖNCE
|
|
886
|
+
> koşar, yani `elementId` bu kuralın geçerli saydığı bir ad değilse (`'Ana Baslik'`, `'1abc'`,
|
|
887
|
+
> `'Functions'`) tamamlanan ad da reddedilir. Yine de her `add_component` gövdesine
|
|
888
|
+
> `name = elementId` yaz; tamamlama bir güvenlik ağıdır.
|
|
882
889
|
|
|
883
890
|
## Kural 1d — Sayfa iskeleti: `page_container` ve boyutlandırma
|
|
884
891
|
|
|
@@ -18,6 +18,7 @@ yazmalarının aksine burada "önce development'ta dene" diye bir ayrım YOKTUR.
|
|
|
18
18
|
| Alan | Ne yapar | Ürettiği değişken(ler) |
|
|
19
19
|
|---|---|---|
|
|
20
20
|
| `mode` | `'light'` (varsayılan) \| `'dark'` — yayınlanan uygulamanın modu | palet tabanını seçer |
|
|
21
|
+
| `direction` | `'ltr'` \| `'rtl'` \| `'auto'` — sayfanın okuma yönü; `auto` uygulama dilinden türetir (`ar` `he` `fa` `ur` `ps` `sd` `ug` `yi` `dv` → rtl). Ayar YOKSA `dir` hiç yazılmaz (sayfa üst öğenin yönünü miras alır): `ar` ile kurulmuş mevcut proje bu ayar açılmadan aynalanmaz. Sayfa oluşturucu tuvali yönü uygulamaz. Grid yerleşimi kendiliğinden aynalanır; bileşenlerin ok simgeleri ve fiziksel `left/right` CSS'i bileşen bazında düzeltilir (ADR 0006) | CSS değişkeni üretmez, kap `dir` özniteliğine yazılır |
|
|
21
22
|
| `palette` | AYDINLIK moddaki renk override'ları; `accent.onPrimary` isteğe bağlıdır — accent ÜSTÜNDEKİ metin, verilmezse kontrasttan türer (`#4787FA` gibi açık bir mavi siyah metin üretir, tasarımda beyazsa buraya yazın) | `--ng-accent-*` · `--ng-background-*` · `--ng-text-*` · `--ng-border-*` · `--ng-status-*` · `--ng-text-on-accent` |
|
|
22
23
|
| `paletteDark` | KARANLIK moddaki renk override'ları | aynı küme |
|
|
23
24
|
| `fontFamily` · `headingFontFamily` | Gövde ve başlık yazı tipi (Google Fonts adı) | `--ng-font` · `--ng-font-heading` |
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
|
-
"hash": "
|
|
3
|
-
"generatedAt": "2026-10-
|
|
4
|
-
"types":
|
|
2
|
+
"hash": "63d0b9efc3b8d0c0d8ef9c8b7478b3abf8cb482e4e80276202b3faed73cedddf",
|
|
3
|
+
"generatedAt": "2026-10-08T09:32:48.956Z",
|
|
4
|
+
"types": 337,
|
|
5
5
|
"files": 167,
|
|
6
6
|
"note": "Şemaları üreten backend kaynaklarının parmak izi. Elle düzenlemeyin: 'npm run generate-schemas' yazar, 'npm run schema:check' denetler.",
|
|
7
7
|
"missing": [
|
|
@@ -117,6 +117,9 @@
|
|
|
117
117
|
"review": {
|
|
118
118
|
"$ref": "#/components/schemas/NgApps.Domain.Contexts.Organization.MobileStore.MobileStoreReviewModel"
|
|
119
119
|
},
|
|
120
|
+
"ads": {
|
|
121
|
+
"$ref": "#/components/schemas/NgApps.Domain.Contexts.Organization.MobileStore.MobileStoreAdsModel"
|
|
122
|
+
},
|
|
120
123
|
"expectedUpdatedAt": {
|
|
121
124
|
"type": "string",
|
|
122
125
|
"format": "date-time",
|
|
@@ -255,6 +258,40 @@
|
|
|
255
258
|
},
|
|
256
259
|
"additionalProperties": false
|
|
257
260
|
},
|
|
261
|
+
"NgApps.Domain.Contexts.Organization.MobileStore.MobileStoreAdsModel": {
|
|
262
|
+
"type": "object",
|
|
263
|
+
"properties": {
|
|
264
|
+
"enabled": {
|
|
265
|
+
"type": "boolean"
|
|
266
|
+
},
|
|
267
|
+
"iosAppId": {
|
|
268
|
+
"type": "string",
|
|
269
|
+
"nullable": true
|
|
270
|
+
},
|
|
271
|
+
"androidAppId": {
|
|
272
|
+
"type": "string",
|
|
273
|
+
"nullable": true
|
|
274
|
+
},
|
|
275
|
+
"testMode": {
|
|
276
|
+
"type": "boolean"
|
|
277
|
+
},
|
|
278
|
+
"consentForm": {
|
|
279
|
+
"type": "boolean"
|
|
280
|
+
},
|
|
281
|
+
"appOpenUnit": {
|
|
282
|
+
"type": "string",
|
|
283
|
+
"nullable": true
|
|
284
|
+
},
|
|
285
|
+
"units": {
|
|
286
|
+
"type": "array",
|
|
287
|
+
"items": {
|
|
288
|
+
"$ref": "#/components/schemas/NgApps.Domain.Contexts.Organization.MobileStore.MobileStoreAdUnitModel"
|
|
289
|
+
},
|
|
290
|
+
"nullable": true
|
|
291
|
+
}
|
|
292
|
+
},
|
|
293
|
+
"additionalProperties": false
|
|
294
|
+
},
|
|
258
295
|
"NgApps.Domain.Contexts.Organization.MobileStore.MobileStoreAppleCategoriesModel": {
|
|
259
296
|
"type": "object",
|
|
260
297
|
"properties": {
|
|
@@ -268,6 +305,28 @@
|
|
|
268
305
|
}
|
|
269
306
|
},
|
|
270
307
|
"additionalProperties": false
|
|
308
|
+
},
|
|
309
|
+
"NgApps.Domain.Contexts.Organization.MobileStore.MobileStoreAdUnitModel": {
|
|
310
|
+
"type": "object",
|
|
311
|
+
"properties": {
|
|
312
|
+
"name": {
|
|
313
|
+
"type": "string",
|
|
314
|
+
"nullable": true
|
|
315
|
+
},
|
|
316
|
+
"format": {
|
|
317
|
+
"type": "string",
|
|
318
|
+
"nullable": true
|
|
319
|
+
},
|
|
320
|
+
"ios": {
|
|
321
|
+
"type": "string",
|
|
322
|
+
"nullable": true
|
|
323
|
+
},
|
|
324
|
+
"android": {
|
|
325
|
+
"type": "string",
|
|
326
|
+
"nullable": true
|
|
327
|
+
}
|
|
328
|
+
},
|
|
329
|
+
"additionalProperties": false
|
|
271
330
|
}
|
|
272
331
|
}
|
|
273
332
|
}
|
|
@@ -3,11 +3,14 @@ import { handle } from '../platform/responses.js';
|
|
|
3
3
|
import { forgetApplication, readApplicationPlatform, resolveApplication } from '../knowledge/platform/resolve.js';
|
|
4
4
|
import { mobileTabsProblems, readMobileTabs } from '../knowledge/platform/mobileTabs.js';
|
|
5
5
|
import { findingLine } from '../knowledge/platform/profile.js';
|
|
6
|
+
import { listComponents } from './components.js';
|
|
6
7
|
import { z } from 'zod';
|
|
7
8
|
/**
|
|
8
9
|
* MOB-007 (sekme ayarı) — `properties.mobile.tabs` yazan mobil uygulama çağrısının denetimi. Yazım
|
|
9
10
|
* platforma YİNE gider (D3; backend `properties`'i doğrulamaz): bulgu `platformWarnings`'e düşer.
|
|
10
|
-
* Sayfa ve widget listesi okunamazsa o denetimler atlanır, biçim denetimi yine koşar.
|
|
11
|
+
* Sayfa ve widget listesi okunamazsa o denetimler atlanır, biçim denetimi yine koşar. Widget listesi
|
|
12
|
+
* `listComponents`'tan gelir: uç `type` verilmezse yalnız web bileşenlerini döner ve mobil widget'ı
|
|
13
|
+
* gösteren sekme ayarı yanlış MOB-007 alırdı (ARC-245).
|
|
11
14
|
*/
|
|
12
15
|
export async function mobileTabsWarnings(platform, applicationId, projectSlug, homepageId, properties) {
|
|
13
16
|
const tabs = readMobileTabs(properties);
|
|
@@ -19,8 +22,8 @@ export async function mobileTabsWarnings(platform, applicationId, projectSlug, h
|
|
|
19
22
|
.catch(() => undefined)
|
|
20
23
|
: undefined;
|
|
21
24
|
const widgetIds = applicationId && projectSlug
|
|
22
|
-
? await platform
|
|
23
|
-
.then(
|
|
25
|
+
? await listComponents(platform, projectSlug, undefined, applicationId)
|
|
26
|
+
.then(components => new Set(components.filter(entry => entry.kind === 'widget').map(entry => String(entry.id ?? ''))))
|
|
24
27
|
.catch(() => undefined)
|
|
25
28
|
: undefined;
|
|
26
29
|
return mobileTabsProblems(tabs, {
|