@nitra/cursor 12.8.3 → 12.8.5

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.
Files changed (125) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/bin/n-cursor.js +3 -2
  3. package/package.json +1 -1
  4. package/rules/abie/js/env_dns.mdc +33 -0
  5. package/rules/abie/js/firebase_hosting.mdc +3 -0
  6. package/rules/abie/js/hc_pairing.mdc +23 -0
  7. package/rules/abie/js/ua_http_route.mdc +47 -0
  8. package/rules/abie/js/ua_node_selector.mdc +27 -0
  9. package/rules/abie/main.mdc +53 -0
  10. package/rules/doc-files/js/docs/index.md +15 -15
  11. package/rules/js/docs/index.md +3 -3
  12. package/rules/js/docs/main.md +7 -7
  13. package/rules/js/js/docs/check.md +12 -16
  14. package/rules/js/js/docs/index.md +4 -4
  15. package/rules/js/js/docs/tooling.md +8 -8
  16. package/rules/js/js/docs/utils_imports.md +22 -200
  17. package/rules/npm-module/js/docs/index.md +5 -5
  18. package/rules/npm-module/js/docs/rule_meta.md +13 -13
  19. package/rules/npm-module/js/docs/skill_meta.md +19 -9
  20. package/rules/npm-module/js/rule_meta.mjs +9 -9
  21. package/rules/npm-module/js/skill_meta.mjs +6 -6
  22. package/rules/test/js/docs/index.md +7 -7
  23. package/rules/test/js/docs/stryker_config.md +18 -35
  24. package/rules/test/js/docs/vitest-config-pool-forks.md +20 -12
  25. package/schemas/v8r-catalog.json +4 -4
  26. package/scripts/docs/index.md +16 -16
  27. package/scripts/docs/sync-setup-bun-deps-action.md +14 -14
  28. package/scripts/lib/check-mdc-template-refs.mjs +2 -2
  29. package/scripts/lib/docs/check-mdc-template-refs.md +12 -214
  30. package/scripts/lib/docs/index.md +36 -36
  31. package/scripts/lib/docs/inline-template-links.md +13 -293
  32. package/scripts/lib/docs/mirror-parity.md +18 -157
  33. package/scripts/lib/docs/rule-meta.md +19 -22
  34. package/scripts/lib/docs/run-rule.md +11 -11
  35. package/scripts/lib/docs/skill-meta.md +17 -19
  36. package/scripts/lib/docs/timing-summary.md +7 -7
  37. package/scripts/lib/inline-template-links.mjs +31 -0
  38. package/scripts/lib/mirror-parity.mjs +6 -4
  39. package/scripts/lib/rule-meta.mjs +1 -1
  40. package/scripts/lib/run-rule.mjs +4 -4
  41. package/scripts/lib/skill-meta.mjs +1 -1
  42. package/scripts/utils/docs/index.md +14 -14
  43. package/scripts/utils/docs/resolve-js-root.md +12 -13
  44. package/types/bin/n-cursor.d.ts +1 -1
  45. package/rules/abie/abie.mdc +0 -181
  46. /package/rules/abie/{meta.json → main.json} +0 -0
  47. /package/rules/adr/{meta.json → main.json} +0 -0
  48. /package/rules/adr/{adr.mdc → main.mdc} +0 -0
  49. /package/rules/bun/{meta.json → main.json} +0 -0
  50. /package/rules/bun/{bun.mdc → main.mdc} +0 -0
  51. /package/rules/capacitor/{meta.json → main.json} +0 -0
  52. /package/rules/capacitor/{capacitor.mdc → main.mdc} +0 -0
  53. /package/rules/changelog/{meta.json → main.json} +0 -0
  54. /package/rules/changelog/{changelog.mdc → main.mdc} +0 -0
  55. /package/rules/ci4/{meta.json → main.json} +0 -0
  56. /package/rules/ci4/{ci4.mdc → main.mdc} +0 -0
  57. /package/rules/doc-files/{meta.json → main.json} +0 -0
  58. /package/rules/doc-files/{doc-files.mdc → main.mdc} +0 -0
  59. /package/rules/docker/{meta.json → main.json} +0 -0
  60. /package/rules/docker/{docker.mdc → main.mdc} +0 -0
  61. /package/rules/efes/{meta.json → main.json} +0 -0
  62. /package/rules/efes/{efes.mdc → main.mdc} +0 -0
  63. /package/rules/feedback/{meta.json → main.json} +0 -0
  64. /package/rules/feedback/{feedback.mdc → main.mdc} +0 -0
  65. /package/rules/ga/{meta.json → main.json} +0 -0
  66. /package/rules/ga/{ga.mdc → main.mdc} +0 -0
  67. /package/rules/graphql/{meta.json → main.json} +0 -0
  68. /package/rules/graphql/{graphql.mdc → main.mdc} +0 -0
  69. /package/rules/hasura/{meta.json → main.json} +0 -0
  70. /package/rules/hasura/{hasura.mdc → main.mdc} +0 -0
  71. /package/rules/image-avif/{meta.json → main.json} +0 -0
  72. /package/rules/image-avif/{image-avif.mdc → main.mdc} +0 -0
  73. /package/rules/image-compress/{meta.json → main.json} +0 -0
  74. /package/rules/image-compress/{image-compress.mdc → main.mdc} +0 -0
  75. /package/rules/js/{meta.json → main.json} +0 -0
  76. /package/rules/js/{js.mdc → main.mdc} +0 -0
  77. /package/rules/js-bun-db/{meta.json → main.json} +0 -0
  78. /package/rules/js-bun-db/{js-bun-db.mdc → main.mdc} +0 -0
  79. /package/rules/js-bun-redis/{meta.json → main.json} +0 -0
  80. /package/rules/js-bun-redis/{js-bun-redis.mdc → main.mdc} +0 -0
  81. /package/rules/js-mssql/{meta.json → main.json} +0 -0
  82. /package/rules/js-mssql/{js-mssql.mdc → main.mdc} +0 -0
  83. /package/rules/js-run/{meta.json → main.json} +0 -0
  84. /package/rules/js-run/{js-run.mdc → main.mdc} +0 -0
  85. /package/rules/k8s/{meta.json → main.json} +0 -0
  86. /package/rules/k8s/{k8s.mdc → main.mdc} +0 -0
  87. /package/rules/nginx-default-tpl/{meta.json → main.json} +0 -0
  88. /package/rules/nginx-default-tpl/{nginx-default-tpl.mdc → main.mdc} +0 -0
  89. /package/rules/npm-module/{meta.json → main.json} +0 -0
  90. /package/rules/npm-module/{npm-module.mdc → main.mdc} +0 -0
  91. /package/rules/php/{meta.json → main.json} +0 -0
  92. /package/rules/php/{php.mdc → main.mdc} +0 -0
  93. /package/rules/python/{meta.json → main.json} +0 -0
  94. /package/rules/python/{python.mdc → main.mdc} +0 -0
  95. /package/rules/rego/{meta.json → main.json} +0 -0
  96. /package/rules/rego/{rego.mdc → main.mdc} +0 -0
  97. /package/rules/release/{meta.json → main.json} +0 -0
  98. /package/rules/release/{release.mdc → main.mdc} +0 -0
  99. /package/rules/rust/{meta.json → main.json} +0 -0
  100. /package/rules/rust/{rust.mdc → main.mdc} +0 -0
  101. /package/rules/security/{meta.json → main.json} +0 -0
  102. /package/rules/security/{security.mdc → main.mdc} +0 -0
  103. /package/rules/style/{meta.json → main.json} +0 -0
  104. /package/rules/style/{style.mdc → main.mdc} +0 -0
  105. /package/rules/tauri/{meta.json → main.json} +0 -0
  106. /package/rules/tauri/{tauri.mdc → main.mdc} +0 -0
  107. /package/rules/test/{meta.json → main.json} +0 -0
  108. /package/rules/test/{test.mdc → main.mdc} +0 -0
  109. /package/rules/text/{meta.json → main.json} +0 -0
  110. /package/rules/text/{text.mdc → main.mdc} +0 -0
  111. /package/rules/tool-surface/{meta.json → main.json} +0 -0
  112. /package/rules/tool-surface/{tool-surface.mdc → main.mdc} +0 -0
  113. /package/rules/vue/{meta.json → main.json} +0 -0
  114. /package/rules/vue/{vue.mdc → main.mdc} +0 -0
  115. /package/rules/worktree/{meta.json → main.json} +0 -0
  116. /package/rules/worktree/{worktree.mdc → main.mdc} +0 -0
  117. /package/skills/adr-normalize/{meta.json → main.json} +0 -0
  118. /package/skills/coverage-fix/{meta.json → main.json} +0 -0
  119. /package/skills/doc-aggregate/{meta.json → main.json} +0 -0
  120. /package/skills/doc-files/{meta.json → main.json} +0 -0
  121. /package/skills/lint/{meta.json → main.json} +0 -0
  122. /package/skills/llm-patch/{meta.json → main.json} +0 -0
  123. /package/skills/publish-telegram/{meta.json → main.json} +0 -0
  124. /package/skills/start-check/{meta.json → main.json} +0 -0
  125. /package/skills/taze/{meta.json → main.json} +0 -0
@@ -3,305 +3,25 @@ type: JS Module
3
3
  title: inline-template-links.mjs
4
4
  resource: npm/scripts/lib/inline-template-links.mjs
5
5
  docgen:
6
- crc: 7726f0ef
6
+ crc: b659349c
7
+ model: omlx/gemma-4-e4b-it-OptiQ-4bit
8
+ score: 100
7
9
  ---
8
10
 
9
- Модуль `inline-template-links.mjs` — допоміжна утиліта для **build-кроку обробки `.mdc`-правил**. Її роль: у текстовому вмісті `.mdc`-документа знайти Markdown-посилання, які ведуть на template-файли (шлях містить сегмент `/template/` або `/templates/`), і **замінити** ці посилання на **інлайн fenced-блоки** з фактичним вмістом target-файлу.
11
+ ## Огляд
10
12
 
11
- Простими словами: замість того щоб у згенерованому правилі читач бачив посилання `[конфіг](./templates/package.json.snippet.json)`, він побачить безпосередньо назву реального файлу (`package.json`) і fenced-блок із його вмістом це робить правило «самодостатнім», без потреби клікати по лінках.
13
+ Цей модуль реалізує механізми вбудовування контенту в текстові документи, використовуючи конфігурації з `package.json.snippet.json` та `package.json`. Він замінює посилання в тексті на вміст файлів-шаблонів, розташованих у каталозі правил, а також на вміст файлів з розширенням .mdc, які не є шаблонами. Функції дозволяють вставляти посилання на шаблони (`inlineTemplateLinks`) та включати вміст Markdown (`inlineMarkdownIncludes`).
12
14
 
13
- Особливості:
15
+ ## Поведінка
14
16
 
15
- - Працює асинхронно (`async`), бо читає файли через `node:fs/promises`.
16
- - Робить **fail-loud** валідацію: якщо посилання вказує на неіснуючий файл кидає `Error` (а не мовчки пропускає), щоб автор правила одразу побачив проблему.
17
- - «Розгортає» спеціальні суфікси `.snippet.<ext>` / `.deny.<ext>` / `.contains.<ext>` до імені реального target-файлу, який вони описують (наприклад `package.json.snippet.json` → `package.json`).
18
- - Безпечно щодо ReDoS: усі regexp — статичні літерали з обмеженням довжини, без `new RegExp(variable)` із користувацьких даних.
17
+ inlineTemplateLinks замінює посилання в тексті на вбудовані блоки з вмістом файлів, що містять шаблони, якщо ці файли знаходяться в каталозі правил.
18
+ inlineMarkdownIncludes замінює посилання в тексті на вміст файлів з розширенням .mdc, якщо ці файли не є шаблонами.
19
19
 
20
- Модуль експортує єдину функцію `inlineTemplateLinks(text, ruleDir)`.
20
+ ## Публічний API
21
21
 
22
- ## Експорти / API
22
+ inlineTemplateLinks Замінює посилання у Markdown, що містять `/template/`, на вбудовані блокові конструкції, зчитуючи вміст з вказаного файлу. Викидає помилку, якщо цільове посилання не знайдено.
23
+ inlineMarkdownIncludes — Замінює посилання у Markdown, що закінчуються на `.mdc` (і не є шляхом `/template/`), на сирий вміст відповідного файлу Markdown. Викидає помилку, якщо цільове посилання не знайдено.
23
24
 
24
- | Експорт | Тип | Призначення |
25
- | --------------------- | ---------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------- |
26
- | `inlineTemplateLinks` | `async function(text: string, ruleDir: string): Promise<string>` | Замінює Markdown-посилання на template-файли в `.mdc`-тексті на інлайн fenced-блоки з фактичним вмістом цих файлів. |
25
+ ## Гарантії поведінки
27
26
 
28
- Усі інші імена в модулі (`langFromExt`, `normalizeTargetName`, константи `MD_LINK_RE`, `TEMPLATE_SEGMENT_RE`, `SLOT_SUFFIX_RES`) — **внутрішні** (не експортуються).
29
-
30
- ## Внутрішні константи
31
-
32
- ### `MD_LINK_RE`
33
-
34
- ```js
35
- ;/\[([^\]]{1,200})\]\((\.\/[^)]{1,500})\)/g
36
- ```
37
-
38
- Глобальний regexp, який ловить **Markdown-посилання вигляду `[label](./path)`** із обов'язковим префіксом `./` у href. Group 1 — текст посилання (до 200 символів), group 2 — шлях (до 500 символів, що починається з `./`). Обмеження довжин — захист від ReDoS / pathological input.
39
-
40
- ### `TEMPLATE_SEGMENT_RE`
41
-
42
- ```js
43
- ;/\/templates?\//
44
- ```
45
-
46
- Перевіряє, чи шлях містить сегмент `/template/` або `/templates/`. Тільки такі посилання вважаються «template-посиланнями» і підлягають заміні; інші Markdown-лінки залишаються недоторканими.
47
-
48
- ### `SLOT_SUFFIX_RES`
49
-
50
- Масив із трьох **статичних** regexp:
51
-
52
- ```js
53
- ;[/^(.+)\.snippet\.[^.]+$/, /^(.+)\.deny\.[^.]+$/, /^(.+)\.contains\.[^.]+$/]
54
- ```
55
-
56
- Кожен ловить ім'я файлу з суфіксом-«слотом»: `<name>.snippet.<ext>`, `<name>.deny.<ext>`, `<name>.contains.<ext>`. Group 1 — це ім'я реального target-файлу (без суфікса слоту і без власного розширення). Коментар у коді явно зазначає: regexp-літерали статичні, без `RegExp(variable)`.
57
-
58
- ## Функції
59
-
60
- ### `langFromExt(filePath)` — internal
61
-
62
- Сигнатура:
63
-
64
- ```js
65
- function langFromExt(filePath: string): string
66
- ```
67
-
68
- Параметри:
69
-
70
- - `filePath` — рядок зі шляхом до файлу (досить навіть базового імені, бо використовується лише розширення).
71
-
72
- Повертає:
73
-
74
- - Рядок-ідентифікатор мови для Markdown fenced-блока:
75
- - `'json'` — якщо розширення `.json`;
76
- - `'toml'` — якщо `.toml`;
77
- - `'yaml'` — якщо `.yml` або `.yaml`;
78
- - `''` (порожній рядок) — для будь-яких інших розширень.
79
-
80
- Side effects: жодних — чиста функція над рядком.
81
-
82
- Призначення: визначити, який мовний таг ставити після відкривального ` ``` ` у згенерованому fenced-блоці, щоб підсвічування синтаксису працювало коректно.
83
-
84
- ### `normalizeTargetName(fileBasename)` — internal
85
-
86
- Сигнатура:
87
-
88
- ```js
89
- function normalizeTargetName(fileBasename: string): string
90
- ```
91
-
92
- Параметри:
93
-
94
- - `fileBasename` — базове ім'я файлу (без шляху), наприклад `package.json.snippet.json`.
95
-
96
- Повертає:
97
-
98
- - Якщо ім'я **збігається з одним із regexp у `SLOT_SUFFIX_RES`** (тобто має суфікс `.snippet.<ext>`, `.deny.<ext>` або `.contains.<ext>`) — повертається **group 1** першого збігу (ім'я без слоту). Приклади:
99
- - `package.json.snippet.json` → `package.json`
100
- - `eslint.config.js.deny.js` → `eslint.config.js`
101
- - `Caddyfile.contains.txt` → `Caddyfile`
102
- - Якщо жоден з regexp не збігся — повертається оригінальне `fileBasename` без змін.
103
-
104
- Side effects: відсутні.
105
-
106
- Призначення: для template-файлу з суфіксом-слотом відновити **реальне ім'я target-файлу**, на який цей template посилається; саме це ім'я потім підставляється як заголовок перед fenced-блоком у згенерованому Markdown.
107
-
108
- Коментар над функцією у вихіднику прямо описує цю поведінку: «Strip `.<slot>.<ext>` suffix (slot ∈ snippet/deny/contains) to recover the real target file name».
109
-
110
- ### `inlineTemplateLinks(text, ruleDir)` — **exported**
111
-
112
- Сигнатура:
113
-
114
- ```js
115
- export async function inlineTemplateLinks(
116
- text: string,
117
- ruleDir: string,
118
- ): Promise<string>
119
- ```
120
-
121
- Параметри:
122
-
123
- - `text` — вміст `.mdc`-файлу (повний текст) як рядок.
124
- - `ruleDir` — **абсолютний** шлях до директорії правила (наприклад `.../npm/rules/security/`). Усі відносні href із `./` резолвляться **відносно цього каталогу**.
125
-
126
- Повертає:
127
-
128
- - `Promise<string>` — трансформований текст, у якому всі **template-посилання** замінено на блоки виду:
129
-
130
- ````text
131
- `<targetName>`:
132
-
133
- ```<lang>
134
- <contents>
135
- ````
136
-
137
- ```
138
-
139
- де `targetName` — результат `normalizeTargetName(basename(absPath))`, `lang` — результат `langFromExt(absPath)`, а `contents` — вміст файлу після `.trim()`.
140
-
141
- ```
142
-
143
- - Якщо у тексті немає жодного template-посилання — повертається **той самий `text` без змін** (early-exit).
144
-
145
- Алгоритм роботи:
146
-
147
- 1. Знаходимо **всі** збіги `MD_LINK_RE` у `text` через `text.matchAll(...)`.
148
- 2. Фільтруємо їх: залишаємо лише ті, у яких href (group 2) містить `/template/` або `/templates/` (через `TEMPLATE_SEGMENT_RE.test(m[2])`).
149
- 3. Якщо після фільтрації збігів **немає** — повертаємо `text` як є.
150
- 4. Кладемо стартовий результат `result = text`.
151
- 5. Для кожного збігу послідовно (`for ... of`, з `await` на читанні файлу):
152
- 1. Деструктуруємо: `const [fullMatch, , href] = match` (label не використовується, тому позиція пропущена).
153
- 2. Будуємо відносний шлях: `relPath = href.slice(2)` — обрізаємо префікс `./` (його гарантує regexp).
154
- 3. Збираємо абсолютний шлях: `absPath = join(ruleDir, relPath)`.
155
- 4. Перевіряємо існування: `existsSync(absPath)`. Якщо файлу немає — **кидаємо** `Error`:
156
-
157
- ```text
158
- inlineTemplateLinks: file not found: <absPath> (referenced from .mdc)
159
- ```
160
-
161
- Жодного fallback / тихого пропуску — це fail-loud за дизайном.
162
-
163
- 5. Читаємо файл: `raw = await readFile(absPath, 'utf8')`, далі `contents = raw.trim()` (прибираємо хвостові пробіли / переноси).
164
- 6. Обчислюємо `lang = langFromExt(absPath)`.
165
- 7. Обчислюємо `targetName = normalizeTargetName(basename(absPath))`.
166
- 8. Формуємо `replacement` — backtick-екранований заголовок, порожній рядок і fenced-блок із `lang`:
167
-
168
- ```js
169
- ;`\`${targetName}\`:\n\n\`\`\`${lang}\n${contents}\n\`\`\``
170
- ```
171
-
172
- 9. Робимо заміну: `result = result.replace(fullMatch, () => replacement)`. Передача **callback-форми** в `.replace` критично важлива: інакше спецсимволи у `replacement` (наприклад `$&`, `$1` із вмісту шаблону) трактувалися б як backreferences і зламали б вивід.
173
-
174
- 6. Повертаємо `result`.
175
-
176
- Side effects:
177
-
178
- - **Читання** файлів із диска (синхронна перевірка `existsSync` + асинхронне `readFile`).
179
- - **Кидання `Error`** при відсутності target-файлу — це навмисна поведінка («fail loud — user must know»), а не баг.
180
- - Запису на диск або мережевих викликів **не робить**.
181
-
182
- Складність та обмеження:
183
-
184
- - Цикл лінійний за кількістю template-посилань у тексті; для кожного — один `existsSync` і один `readFile`.
185
- - Файли читаються **послідовно** (через `await` у тілі `for...of`), а не паралельно через `Promise.all`. Це осмислений вибір: правил, як правило, мало, а послідовність робить порядок помилок передбачуваним.
186
- - Заміна виконується через простий `result.replace(fullMatch, ...)` — перший збіг `fullMatch` у `result`. Якщо однакове Markdown-посилання трапляється кілька разів — модифікується лише перше входження (фактичний `matchAll` дасть і інші входження, але кожен з них має той самий `fullMatch`, і їх теж замінить — по одному за крок ітерації; для повних дублікатів це працює коректно).
187
-
188
- ## Залежності
189
-
190
- ### Стандартна бібліотека Node.js
191
-
192
- - `node:fs` → `existsSync` — синхронна перевірка наявності файлу перед читанням.
193
- - `node:fs/promises` → `readFile` — асинхронне читання вмісту target-файлу як UTF-8.
194
- - `node:path` → `basename`, `extname`, `join` — робота з шляхами:
195
- - `extname` — у `langFromExt` для визначення мови;
196
- - `basename` — для отримання базового імені файлу, з якого `normalizeTargetName` витягне target-ім'я;
197
- - `join` — для побудови абсолютного шляху від `ruleDir` + `relPath`.
198
-
199
- ### Зовнішні залежності
200
-
201
- Жодних npm-пакетів. Модуль працює лише на Node.js стандарті.
202
-
203
- ### Споживачі модуля
204
-
205
- Файл лежить у `npm/scripts/lib/` поряд із іншими допоміжними утилітами для збірки правил, тому очікувані споживачі — build-скрипти у `npm/scripts/`, які генерують підсумкові `.mdc`-документи для cursor-rules / Claude-rules. Експортована функція `inlineTemplateLinks` викликається на проміжній стадії пайплайна обробки тексту `.mdc`-файлу разом із `ruleDir`, обчисленим від шляху до самого `.mdc`.
206
-
207
- ## Потік виконання / Використання
208
-
209
- Типовий сценарій інтеграції в build-скрипт:
210
-
211
- ```js
212
- import { readFile, writeFile } from 'node:fs/promises'
213
- import { dirname } from 'node:path'
214
-
215
- import { inlineTemplateLinks } from './lib/inline-template-links.mjs'
216
-
217
- const mdcPath = '/abs/path/to/npm/rules/security/n-security.mdc'
218
- const original = await readFile(mdcPath, 'utf8')
219
-
220
- const ruleDir = dirname(mdcPath) // важливо: каталог, де лежить .mdc
221
- const transformed = await inlineTemplateLinks(original, ruleDir)
222
-
223
- await writeFile(mdcPath, transformed, 'utf8')
224
- ```
225
-
226
- Що відбувається крок-за-кроком на прикладі.
227
-
228
- Вхідний `.mdc`-фрагмент (`ruleDir = .../npm/rules/security/`):
229
-
230
- ```text
231
- Snippet вимоги до `package.json` — див. [тут](./templates/package.json.snippet.json).
232
- ```
233
-
234
- Файл `.../npm/rules/security/templates/package.json.snippet.json`:
235
-
236
- ```json
237
- {
238
- "scripts": {
239
- "lint": "eslint ."
240
- }
241
- }
242
- ```
243
-
244
- Що зробить `inlineTemplateLinks`:
245
-
246
- 1. `matchAll(MD_LINK_RE)` знайде один збіг із href `./templates/package.json.snippet.json`.
247
- 2. `TEMPLATE_SEGMENT_RE` пропустить його (бо є `/templates/`).
248
- 3. `relPath = 'templates/package.json.snippet.json'`, `absPath = '.../npm/rules/security/templates/package.json.snippet.json'`.
249
- 4. `existsSync(absPath)` → `true`, файл читається.
250
- 5. `langFromExt(absPath)` → `'json'`.
251
- 6. `normalizeTargetName('package.json.snippet.json')` → `'package.json'` (спрацює regexp `/^(.+)\.snippet\.[^.]+$/`).
252
- 7. `replacement` буде:
253
-
254
- ````text
255
- `package.json`:
256
-
257
- ```json
258
- {
259
- "scripts": {
260
- "lint": "eslint ."
261
- }
262
- }
263
- ````
264
-
265
- ```
266
-
267
- ```
268
-
269
- 8. Результат заміняє оригінальний Markdown-лінк у тексті.
270
-
271
- Випадки помилок:
272
-
273
- - Якщо `href` веде на неіснуючий файл — кидається `Error` із повним абсолютним шляхом у повідомленні; build-скрипт має право або впасти, або зловити цю помилку.
274
- - Якщо у `text` немає Markdown-посилань або жодне з них не містить `/template(s)/` — функція повертає `text` без модифікацій.
275
- - Якщо template-файл має нерозпізнаване розширення (наприклад `.txt` або `.conf`) — `langFromExt` поверне порожній рядок, і fenced-блок буде без мовного тегу (Markdown це допускає).
276
- - Якщо ім'я template-файлу **не** має одного з суфіксів `.snippet.<ext>` / `.deny.<ext>` / `.contains.<ext>` — `normalizeTargetName` поверне його як є; це нормальна поведінка для «звичайних» template-файлів, у яких саме ім'я і є target-ім'ям.
277
-
278
- ## Rebuild Test
279
-
280
- Якщо видалити цей файл і відтворити його з нуля, мінімально достатній рецепт такий:
281
-
282
- 1. Створи модуль `inline-template-links.mjs` у `npm/scripts/lib/`.
283
- 2. Імпортуй з `node:fs` функцію `existsSync`, з `node:fs/promises` — `readFile`, з `node:path` — `basename`, `extname`, `join`.
284
- 3. Оголоси константи:
285
- - `MD_LINK_RE = /\[([^\]]{1,200})\]\((\.\/[^)]{1,500})\)/g` — глобальний regexp для Markdown-посилань `[label](./path)`.
286
- - `TEMPLATE_SEGMENT_RE = /\/templates?\//` — фільтр шляхів, що містять `/template/` чи `/templates/`.
287
- - `SLOT_SUFFIX_RES` — масив із трьох **статичних** regexp: `/^(.+)\.snippet\.[^.]+$/`, `/^(.+)\.deny\.[^.]+$/`, `/^(.+)\.contains\.[^.]+$/`. Принципово: жодного `new RegExp(variable)` — захист від ReDoS.
288
- 4. Реалізуй `langFromExt(filePath)`:
289
- - `extname(filePath)` → за вмістом повернути `'json' | 'toml' | 'yaml' | ''` (для `.yml` теж `'yaml'`).
290
- 5. Реалізуй `normalizeTargetName(fileBasename)`:
291
- - Пройди `SLOT_SUFFIX_RES` у заданому порядку; при першому збігу поверни `match[1]`. Інакше — оригінал.
292
- 6. Експортуй `async function inlineTemplateLinks(text, ruleDir)`:
293
- - `matchAll(MD_LINK_RE)` → відфільтруй за `TEMPLATE_SEGMENT_RE.test(href)`.
294
- - Якщо нічого не залишилося — поверни `text`.
295
- - Для кожного збігу: `relPath = href.slice(2)`, `absPath = join(ruleDir, relPath)`; якщо `!existsSync(absPath)` — `throw new Error('inlineTemplateLinks: file not found: <absPath> (referenced from .mdc)')`.
296
- - Читай файл `utf8`, роби `.trim()`, обчисли `lang` і `targetName`, побудуй `replacement = \`\\\`${targetName}\\\`:\\n\\n\\\`\\\`\\\`${lang}\\n${contents}\\n\\\`\\\`\\\``.
297
- - Заміни через `result = result.replace(fullMatch, () => replacement)` (саме callback-форма — щоб уникнути інтерпретації `$&`/`$1` у вмісті template-файлу).
298
- 7. Поверни `result`.
299
-
300
- Контракт, який має зберегтися:
301
-
302
- - Чиста функція над текстом + читання файлів (без записів і без мережі).
303
- - **Fail-loud** на відсутній target.
304
- - Підтримка трьох слот-суфіксів: `snippet`, `deny`, `contains`.
305
- - Підтримка мов підсвічування: `json`, `toml`, `yaml`, інакше — без таргу.
306
- - Жодного `RegExp(variable)`.
307
- - Префікс href повинен починатися з `./`, інакше посилання ігнорується (це закладено в `MD_LINK_RE`).
27
+ - Read-only: не виконує операцій запису (ФС/БД).
@@ -3,171 +3,32 @@ type: JS Module
3
3
  title: mirror-parity.mjs
4
4
  resource: npm/scripts/lib/mirror-parity.mjs
5
5
  docgen:
6
- crc: 15dc336a
6
+ crc: 5e366df1
7
+ model: omlx/gemma-4-e4b-it-OptiQ-4bit
8
+ score: 90
7
9
  ---
8
10
 
9
- Модуль `mirror-parity.mjs` забезпечує перевірку парності (parity) між «дзеркальними» файлами правил Cursor у `.cursor/rules/n-<id>.mdc` та їх канонічними джерелами у `npm/rules/<id>/<id>.mdc`. Ідея така: репозиторій містить два представлення одного й того ж правила:
11
+ ## Огляд
10
12
 
11
- - канонічне джерело правила лежить у `npm/rules/<id>/<id>.mdc` (з можливими посиланнями на шаблони у тій самій теці);
12
- - дзеркало лежить у `.cursor/rules/n-<id>.mdc` і повинно бути результатом застосування трансформу `inlineTemplateLinks` до канону (тобто посилання на шаблони мають бути вставлені «інлайном»).
13
+ Модуль порівнює вміст дзеркал правил (`.cursor/rules/n-<id>.mdc`) з канонічними версіями (`npm/rules/<id>/<id>.mdc`), які містять застосовані трансформації (трансформ, що застосовує `readBundledRuleContent` $\to$ `inlineTemplateLinks`). Він визначає список керованих дзеркал, формує очікуваний вміст для кожного дзеркала та виявляє дрейф — відхилення фактичного вмісту від очікуваного.
13
14
 
14
- Коли канонічний `.mdc` змінюють і забувають регенерувати дзеркало, виникає «дрейф» (drift). Цей модуль:
15
+ ## Поведінка
15
16
 
16
- - перелічує керовані дзеркала (ті, для яких знайдено канон);
17
- - обчислює очікуваний вміст дзеркала через ту саму трансформацію, що і синхронізатор;
18
- - виявляє ідентифікатори дрейфних правил (де `actual ≠ expected`).
17
+ listManagedMirrors
18
+ Визначає список керованих дзеркал правил, які мають відповідне канонічне джерело у `npm/rules`.
19
19
 
20
- Модуль використовується одночасно і як гард у тестах (очікувано `drift === []`), і для разової регенерації дзеркал. Контекст беклогу — адаптація flow #10.
20
+ expectedMirrorContent
21
+ Формує очікуваний вміст дзеркала, застосовуючи трансформації до канонічного файлу.
21
22
 
22
- ## Експорти / API
23
+ findMirrorDrift
24
+ Виявляє ідентифікатори дзеркал, чий фактичний вміст відрізняється від очікуваного вмісту, отриманого з канону.
23
25
 
24
- Модуль експортує три іменовані функції:
26
+ ## Публічний API
25
27
 
26
- - `listManagedMirrors(repoRoot)`синхронна; повертає опис керованих дзеркал.
27
- - `expectedMirrorContent(canonicalPath)`асинхронна (повертає `Promise<string>`); обчислює очікуваний вміст дзеркала.
28
- - `findMirrorDrift(repoRoot)`асинхронна; повертає масив id правил із дрейфом, відсортований за зростанням.
28
+ listManagedMirrors — перераховує керовані дзеркала, які мають визначене канонічне джерело.
29
+ expectedMirrorContent — визначає бажаний вміст дзеркала, використовуючи канон із вбудованими шаблонами.
30
+ findMirrorDrift — знаходить ідентифікатори дзеркал, у яких фактичний вміст відрізняється від очікуваного.
29
31
 
30
- Жодних дефолтних експортів, побічних ефектів на рівні модуля немає (лише імпорти стандартних модулів і одного локального).
32
+ ## Гарантії поведінки
31
33
 
32
- ## Функції
33
-
34
- ### `listManagedMirrors(repoRoot)`
35
-
36
- Сигнатура: `function listManagedMirrors(repoRoot: string): { id: string, mirrorPath: string, canonicalPath: string }[]`
37
-
38
- Параметри:
39
-
40
- - `repoRoot` — абсолютний шлях до кореня репозиторію (директорія, у якій лежать `.cursor/` та `npm/`).
41
-
42
- Повертає масив об'єктів-описів дзеркал, де:
43
-
44
- - `id` — ідентифікатор правила без префіксу `n-` і без розширення `.mdc` (наприклад, для файлу `n-bun.mdc` → `bun`);
45
- - `mirrorPath` — абсолютний шлях до файлу дзеркала всередині `.cursor/rules/`;
46
- - `canonicalPath` — абсолютний шлях до канонічного `.mdc` всередині `npm/rules/<id>/`.
47
-
48
- Алгоритм:
49
-
50
- 1. Будує шлях до `.cursor/rules`.
51
- 2. Якщо такої директорії не існує (`existsSync` false) — повертає порожній масив (раннє повернення, безпечне для свіжих репо).
52
- 3. Зчитує перелік файлів директорії синхронно (`readdirSync`).
53
- 4. Фільтрує файли: повинні починатися з префіксу `'n-'` (константа `MIRROR_PREFIX`) і завершуватися розширенням `'.mdc'` (константа `MDC_EXT`).
54
- 5. Мапить кожен файл у об'єкт із обчисленими шляхами: `id` отримується вирізанням префіксу і розширення (`f.slice(MIRROR_PREFIX.length, -MDC_EXT.length)`).
55
- 6. Залишає лише ті записи, для яких реально існує канонічне джерело `canonicalPath` (друга фільтрація через `existsSync`). Це дозволяє пропускати «зовнішні» дзеркала, які не мають канону в монорепо.
56
-
57
- Side effects: лише читання директорії з диску (синхронні `existsSync`, `readdirSync`). Жодних записів, мережевих викликів, мутацій глобального стану.
58
-
59
- Помилки: якщо файлова система кине помилку під час `readdirSync` (наприклад, права доступу) — вона прокинеться вгору; для типового сценарію відсутності директорії `.cursor/rules` функція не падає (гард через `existsSync`).
60
-
61
- ### `expectedMirrorContent(canonicalPath)`
62
-
63
- Сигнатура: `function expectedMirrorContent(canonicalPath: string): Promise<string>`
64
-
65
- Параметри:
66
-
67
- - `canonicalPath` — абсолютний шлях до канонічного `.mdc`-файлу (`npm/rules/<id>/<id>.mdc`).
68
-
69
- Повертає `Promise<string>` — очікуваний (нормалізований) текст дзеркала після інлайнінгу шаблонів. Хоча сама функція не використовує `async`/`await`, її результат типізовано як `Promise`, оскільки делегує виклик до `inlineTemplateLinks`, яка є асинхронною (її результат може бути thenable). Це означає, що споживач має `await`-нути повернене значення.
70
-
71
- Алгоритм:
72
-
73
- 1. Синхронно зчитує вміст канонічного файлу як UTF-8 (`readFileSync(canonicalPath, 'utf8')`).
74
- 2. Викликає `inlineTemplateLinks(content, dirname(canonicalPath))`, передаючи прочитаний текст та теку канону як base для розв'язання шляхів до шаблонів.
75
- 3. Повертає результат трансформації.
76
-
77
- Side effects: одне читання з диску. Подальша поведінка щодо шаблонів інкапсульована в `inlineTemplateLinks` (див. її документацію). Контракт: цей же самий трансформ застосовує синк, тому очікуваний вміст детермінований по канону.
78
-
79
- Помилки: якщо файл за `canonicalPath` не існує або недоступний — `readFileSync` кине помилку. Викличні сторони мають гарантувати існування (`listManagedMirrors` уже фільтрує неіснуючі канони).
80
-
81
- ### `findMirrorDrift(repoRoot)`
82
-
83
- Сигнатура: `async function findMirrorDrift(repoRoot: string): Promise<string[]>`
84
-
85
- Параметри:
86
-
87
- - `repoRoot` — абсолютний шлях до кореня репозиторію.
88
-
89
- Повертає `Promise<string[]>` — відсортований за зростанням масив `id` правил, де дзеркало розійшлося з очікуваним вмістом.
90
-
91
- Алгоритм:
92
-
93
- 1. Заводить порожній масив `drift`.
94
- 2. Послідовно (через `for...of`) обходить усі керовані дзеркала, отримані з `listManagedMirrors(repoRoot)`.
95
- 3. Для кожного дзеркала `m`:
96
- - `await`-ить очікуваний вміст через `expectedMirrorContent(m.canonicalPath)`;
97
- - синхронно зчитує фактичний вміст файлу дзеркала `readFileSync(m.mirrorPath, 'utf8')`;
98
- - якщо стрічки не дорівнюють — додає `m.id` до `drift`.
99
- 4. Повертає `drift.sort()` (лексикографічне сортування).
100
-
101
- Side effects: тільки читання з диску. Не записує жодних файлів, не пише в лог.
102
-
103
- Зауваження щодо порівняння: рівність стрічок є точною (строге `!==` по UTF-8 тексту). Будь-яка різниця в пробілах, символах кінця рядка чи порядку інлайнених шаблонів буде розцінена як дрейф.
104
-
105
- ## Залежності
106
-
107
- Стандартна бібліотека Node.js:
108
-
109
- - `node:fs` — `existsSync`, `readdirSync`, `readFileSync`. Усі виклики синхронні. Імпорт зроблено через `node:`-префікс для явної прив'язки до built-in модуля.
110
- - `node:path` — `dirname`, `join`. Використовуються для безпечного складання абсолютних шляхів і отримання батьківської теки канону.
111
-
112
- Локальні модулі:
113
-
114
- - `./inline-template-links.mjs` — імпорт іменованої функції `inlineTemplateLinks(content, baseDir)`. Це той самий трансформ, що його використовує синк дзеркал; саме завдяки спільному використанню досягається консистентність між «гардом дрейфу» і «генератором дзеркал».
115
-
116
- Зовнішніх npm-залежностей немає.
117
-
118
- Константи на рівні модуля:
119
-
120
- - `MIRROR_PREFIX = 'n-'` — префікс імені файлів дзеркал.
121
- - `MDC_EXT = '.mdc'` — розширення файлів правил Cursor.
122
-
123
- ## Потік виконання / Використання
124
-
125
- Типові сценарії використання модуля:
126
-
127
- 1. Тест-гард парності (CI). Споживач передає корінь репо й перевіряє, що дрейф порожній:
128
-
129
- ```js
130
- import { findMirrorDrift } from './mirror-parity.mjs'
131
-
132
- const drift = await findMirrorDrift(repoRoot)
133
- if (drift.length) {
134
- throw new Error(`Mirror drift detected for: ${drift.join(', ')}`)
135
- }
136
- ```
137
-
138
- 2. Разова регенерація дзеркал. Споживач отримує список керованих дзеркал і для кожного перезаписує файл дзеркала очікуваним вмістом:
139
-
140
- ```js
141
- import { writeFileSync } from 'node:fs'
142
- import { listManagedMirrors, expectedMirrorContent } from './mirror-parity.mjs'
143
-
144
- for (const m of listManagedMirrors(repoRoot)) {
145
- const expected = await expectedMirrorContent(m.canonicalPath)
146
- writeFileSync(m.mirrorPath, expected)
147
- }
148
- ```
149
-
150
- (Сам модуль `mirror-parity.mjs` записів не виконує — це задача викличного скрипта.)
151
-
152
- Внутрішній потік виконання `findMirrorDrift`:
153
-
154
- 1. Виклик `listManagedMirrors(repoRoot)`:
155
- - перевірка існування `.cursor/rules`;
156
- - читання списку файлів;
157
- - фільтрація за префіксом `n-` і розширенням `.mdc`;
158
- - обчислення `id`, `mirrorPath`, `canonicalPath`;
159
- - відкидання дзеркал без канону.
160
- 2. Послідовний обхід отриманих описів:
161
- - читання канону → `inlineTemplateLinks` → очікуваний текст;
162
- - читання дзеркала → порівняння;
163
- - накопичення id з дрейфом.
164
- 3. Сортування результату та повернення.
165
-
166
- Контрактна гарантія: якщо синк використовує ту ж саму функцію `inlineTemplateLinks` для генерації дзеркал, то регенерація закриває будь-який дрейф, виявлений `findMirrorDrift`. Якщо дрейф виявлено — він завжди вказує на людську помилку (зміна канону без регенерації) або на розбіжність версій трансформу.
167
-
168
- Обмеження:
169
-
170
- - Обхід послідовний (без `Promise.all`) — простіший контракт по помилках, але повільніший на великих наборах правил. Зважаючи на типову кількість правил у `.cursor/rules`, це не критично.
171
- - Усі читання файлів синхронні, попри асинхронну сигнатуру `findMirrorDrift` — асинхронність зумовлена тільки інтерфейсом `inlineTemplateLinks`.
172
- - Префікс `n-` зашитий константою — додавання інших префіксів дзеркал вимагатиме зміни модуля.
173
- - Зовнішні дзеркала (без канону в `npm/rules/<id>/`) автоматично пропускаються; модуль не повідомляє про них окремо.
34
+ - Read-only: не виконує операцій запису (ФС/БД).
@@ -3,36 +3,33 @@ type: JS Module
3
3
  title: rule-meta.mjs
4
4
  resource: npm/scripts/lib/rule-meta.mjs
5
5
  docgen:
6
- crc: fa5ca866
6
+ crc: 36aea8cb
7
+ model: omlx/gemma-4-e4b-it-OptiQ-4bit
8
+ score: 100
7
9
  ---
8
10
 
9
- Цей файл парсує метадані правил npm/rules/<id>/meta.json для автоматичного виявлення проблемних місць у коді. Він визначає, коли правило має бути активним, використовуючи різні специфікації, такі як завжди-ввімкнене правило, правило, яке активується після виявлення залежностей, або правило, яке активується на основі glob-шаблонів чи незводимих предикатів. Це дозволяє системі динамічно визначати та застосовувати правила, не потребуючи явного налаштування.
11
+ ## Огляд
12
+
13
+ Парсер інтерпретує метадані правил, що знаходяться у `npm/rules/<id>/meta.json`. Він реалізує data-driven логіку для визначення умов активації кожного детектора. Специфікації активації (`auto`) нормалізуються на основі вмісту `meta.json`, підтримуючи чотири типи: константа `RULE_ALWAYS` ("завжди"), залежності від інших правил, шаблони за допомогою `glob`, або незводимі предикати. Парсер не виконує операцій запису у файлову систему чи бази даних і забезпечує безпечну роботу, перехоплюючи помилки.
10
14
 
11
15
  ## Поведінка
12
16
 
13
- `RULE_ALWAYS`: визначає літерал для активації правила "завжди".
14
- `parseRuleAutoSpec`: нормалізує значення `meta.json.auto` у дискриміновану форму.
15
- `parseRuleLintPhase`: нормалізує значення `meta.json.lint` у фазу lint.
16
- `readRuleMetaRaw`: читає та парсить `meta.json` одного правила, повертаючи об’єкт або `null`.
17
+ RULE_ALWAYS константа, що позначає безумовну активацію правила.
18
+ parseRuleAutoSpec Нормалізує значення поля `auto` з метаданих правила у дискриміновану специфікацію активації.
19
+ parseRuleLintSpec Нормалізує значення поля `lint` з метаданих правила у специфікацію області застосування детектора.
20
+ readRuleMetaRaw Читає та парсить вміст `main.json` з каталогу правила, повертаючи його як об'єкт або `null` у разі помилки чи відсутності файлу.
17
21
 
18
22
  ## Публічний API
19
23
 
20
- - RULE_ALWAYS — Активує правило.
21
- - parseRuleAutoSpec — Перетворює значення `meta.json.auto` на конкретну опцію.
22
- - parseRuleLintPhase Перетворює значення `meta.json.lint` на фазу lint.
23
- - readRuleMetaRaw Зчитує та аналізує файл `meta.json` правила.
24
+ RULE_ALWAYS — константа, що вказує на безумовну активацію.
25
+ parseRuleAutoSpec — перетворює налаштування автоматичного визначення правил з `meta.json` у структурований формат.
26
+ parseRuleLintSpecперетворює налаштування лінтування з `meta.json` у визначення області дії детектора.
27
+ "per-file"визначає, що детектор працює з окремими зміненими файлами (порівняння змін з оригіналом).
28
+ "full" — вказує на повний, нероздільний аналіз між усіма файлами (активується лише при використанні прапорця `--full` або в CI).
29
+ readRuleMetaRaw — зчитує та обробляє метадані одного правила з файлу `meta.json`.
24
30
 
25
31
  ## Гарантії поведінки
26
32
 
27
- - `RULE_ALWAYS` повертає `true` якщо правило активне.
28
- - `parseRuleAutoSpec` повертає `true` якщо специфікація правила успішно розібрана.
29
- - `parseRuleAutoSpec` повертає `false` якщо специфікація правила не може бути розібрана.
30
- - `parseRuleLintPhase` повертає `true` якщо правило успішно розібрано на етапі linting.
31
- - `parseRuleLintPhase` повертає `false` якщо правило не може бути розібрано на етапі linting.
32
- - `readRuleMetaRaw` повертає `true` якщо метадані правила успішно прочитані.
33
- - `readRuleMetaRaw` повертає `false` якщо метадані правила не можуть бути прочитані.
34
- - У разі невдачі, функція повертає `false` або `null`.
35
- - Функції не кидають винятків.
36
- - Немає кешування.
37
- - Немає гарантій щодо стану файлів або каталогів.
38
- - Поля `worktree` правила не використовуються.
33
+ - Read-only: не виконує операцій запису (ФС/БД).
34
+ - Перехоплює помилки і не пропускає винятків назовні (fail-safe).
35
+ - За певних помилок повертає порожнє значення (напр. `null`) замість винятку.
@@ -3,27 +3,27 @@ type: JS Module
3
3
  title: run-rule.mjs
4
4
  resource: npm/scripts/lib/run-rule.mjs
5
5
  docgen:
6
- crc: 27060842
7
- score: 95
6
+ crc: 7d0585e1
7
+ model: omlx/gemma-4-e4b-it-OptiQ-4bit
8
+ score: 90
8
9
  ---
9
10
 
10
- Файл слугує оркестратором правила. Він координує виконання перевірок, включаючи перевірку гейту, JS-концернів та policy-концернів.
11
+ ## Огляд
12
+
13
+ Файл оркеструє виконання одного правила під CLI `fix`. Він послідовно застосовує гейт `applies` з `js/applies.mjs` для визначення придатності правила. Якщо гейт повертає `false`, правило не застосовується. Далі виконуються JS-концерни та Policy-концерни в алфавітному порядку. Резолвер `resolveTargetFiles` ділить кеш між концернами. Кожен concern створює власний репортер, а їхні exit-коди OR-уються в єдиний контракт, що забезпечує 0/1 результат для правила. Оркестратор спирається на конфігурації `target.json` та `.n-cursor.json`.
11
14
 
12
15
  ## Поведінка
13
16
 
14
- runTemplateSubsetConcern
15
- Перевіряє відповідність фактичного файлу канонічному шаблону.
17
+ runTemplateSubsetConcern виконує перевірку концерну, де канон визначається сніпетом у `target.json`, звіряючи його з актуальними файлами-таргетами.
16
18
 
17
- runRule
18
- Оркеструє виконання правила, включаючи перевірку гейту, JS-концернів та policy-концернів.
19
+ runRule оркеструє виконання одного правила, послідовно застосовуючи applies-гейт, виконуючи JS-концерни та запускаючи policy-концерни, а також перевіряючи відсутність markdown-посилань.
19
20
 
20
21
  ## Публічний API
21
22
 
22
- - runTemplateSubsetConcern — Snippet-driven перевірка концерну (`target.json:"check":"template"`): звіряє канон з фактичним файлом за допомогою глибокого підмножинного перевірки. Усі канонічні поля/елементи обов'язкові, зайві дозволені. Масиви співпадають за наявністю (незалежно від порядку). Зміна сніпета негайно впливає на примусовість.
23
- - runRule — Запускає одне правило: applies-гейт JS-концерни policy-концерни.
23
+ runTemplateSubsetConcern — Порівнює фактичний файл із канонічним шаблоном (з `target.json`) для перевірки, чи всі обов'язкові елементи присутні, дозволяючи додаткові.
24
+ runRule — Виконує окреме правило, яке проходить через перевірки застосовності, JavaScript-концерни та політики.
24
25
 
25
26
  ## Гарантії поведінки
26
27
 
27
- - Read-only: файл не виконує операцій запису у файлову систему.
28
+ - Read-only: не виконує операцій запису (ФС/БД).
28
29
  - Кешує результати в межах одного прогону.
29
- - Не звертається до мережі.