@nitra/cursor 11.4.0 → 11.4.1

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/CHANGELOG.md CHANGED
@@ -1,5 +1,11 @@
1
1
  # Changelog
2
2
 
3
+ ## [11.4.1] - 2026-06-16
4
+
5
+ ### Fixed
6
+
7
+ - release: commit-back більше не «вдається» мовчки за відхиленого push — результат `git push` тепер ЯВНО перевіряється (раніше тихий runGit повертав null, а реліз рапортував успіх, через що npm міг піти попереду git). За non-fast-forward (паралельний push у ту саму гілку) release-коміт автоматично rebase-иться на свіжий upstream, теги пересуваються на новий HEAD і push повторюється (до 5 спроб); без upstream або при rebase-конфлікті — кидаємо помилку (exit 1), тож CI-публікація не відбувається без приземленого commit-back.
8
+
3
9
  ## [11.4.0] - 2026-06-15
4
10
 
5
11
  ### Changed
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nitra/cursor",
3
- "version": "11.4.0",
3
+ "version": "11.4.1",
4
4
  "description": "CLI для завантаження cursor-правил (префікс n-) у локальний репозиторій",
5
5
  "keywords": [
6
6
  "cli",
@@ -64,6 +64,45 @@ async function collectChangeFiles(cwd, manifest, runGit) {
64
64
  return [{ file: null, entry: synthesized }]
65
65
  }
66
66
 
67
+ /**
68
+ * Пушить release-коміт (із тегами) у апстрім, переживаючи паралельні push у ту саму гілку.
69
+ * `runGit` — ТИХИЙ раннер (повертає null при помилці), тож non-fast-forward push не кидає, а
70
+ * повертає null; цей хелпер ЯВНО перевіряє результат, щоб реліз не «вдався» без приземленого
71
+ * commit-back (саме така мовчазна поразка лишала npm попереду git). За відмовою push:
72
+ * fetch + rebase release-коміту на свіжий апстрім, пересунути теги на новий HEAD і повторити
73
+ * (до `attempts` разів). Без апстріму або при rebase-конфлікті — кидаємо, а не маскуємо.
74
+ * @param {(args: string[]) => Promise<string | null>} runGit git-раннер
75
+ * @param {string[]} tags теги релізу (вже створені на поточному HEAD)
76
+ * @param {number} [attempts] максимум спроб push
77
+ * @returns {Promise<void>} результат; кидає, якщо push так і не приземлився
78
+ */
79
+ async function pushReleaseWithRetry(runGit, tags, attempts = 5) {
80
+ for (let attempt = 1; attempt <= attempts; attempt++) {
81
+ const pushed = await runGit(['push', '--follow-tags'])
82
+ if (pushed !== null) return
83
+ if (attempt === attempts) break
84
+ // push відхилено (найімовірніше non-fast-forward — апстрім пішов уперед) → інтегруємо й пробуємо ще
85
+ const upstream = (await runGit(['rev-parse', '--abbrev-ref', '--symbolic-full-name', '@{u}']))?.trim()
86
+ if (!upstream) {
87
+ throw new Error('release: git push відхилено, а upstream для rebase немає — commit-back не приземлився')
88
+ }
89
+ const remote = upstream.includes('/') ? upstream.slice(0, upstream.indexOf('/')) : 'origin'
90
+ await runGit(['fetch', remote])
91
+ const rebased = await runGit(['rebase', upstream])
92
+ if (rebased === null) {
93
+ await runGit(['rebase', '--abort'])
94
+ throw new Error(`release: push відхилено і rebase на ${upstream} дав конфлікт — розв'яжи вручну`)
95
+ }
96
+ // після rebase хеш release-коміту змінився → пересуваємо теги на новий HEAD
97
+ for (const tag of tags) {
98
+ await runGit(['tag', '-f', tag])
99
+ }
100
+ }
101
+ throw new Error(
102
+ `release: git push не вдався після ${attempts} спроб (non-fast-forward?) — commit-back не приземлився, реліз неуспішний`
103
+ )
104
+ }
105
+
67
106
  /**
68
107
  * @param {object} [opts] опції
69
108
  * @param {string} [opts.cwd] корінь
@@ -112,7 +151,7 @@ export async function release(opts = {}) {
112
151
  for (const tag of tags) {
113
152
  await runGit(['tag', tag])
114
153
  }
115
- await runGit(['push', '--follow-tags'])
154
+ await pushReleaseWithRetry(runGit, tags)
116
155
  }
117
156
  return released
118
157
  }
package/rules/vue/vue.mdc CHANGED
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  description: Vue
3
- version: '2.2'
3
+ version: '2.3'
4
4
  globs: "**/*.vue"
5
5
  alwaysApply: false
6
6
  ---
@@ -110,6 +110,24 @@ const additionalInstructions = `
110
110
  - **debounce/throttle** для частих подій.
111
111
  - Після ручних **addEventListener** / підписок — прибирай у **onUnmounted**.
112
112
 
113
+ ### Функції в шаблоні
114
+
115
+ Виклики функцій у шаблоні дозволені **лише** в обробниках подій (`@click`, `@change` тощо). У всіх інших місцях — `v-if`, `v-show`, атрибутах (`:prop`), інтерполяціях (`{{ }}`) — замінюй функції на `computed`-властивості: функція виконується при **кожному** render-і, тоді як `computed` кешується і перераховується лише при зміні залежностей.
116
+
117
+ ```vue
118
+ <!-- ❌ функція в умові, атрибуті та інтерполяції -->
119
+ <q-item v-if="getItems(order).length" :label="getLabel(item)">
120
+ {{ formatName(user) }}
121
+ </q-item>
122
+
123
+ <!-- ✅ реактивні змінні / computed / props -->
124
+ <q-item v-if="itemsMap[order.id].length" :label="item.label">
125
+ {{ user.displayName }}
126
+ </q-item>
127
+ <!-- обробник події — виклик функції дозволений -->
128
+ <q-btn @click="doSomething(item)" />
129
+ ```
130
+
113
131
  ### Безпека
114
132
 
115
133
  - Не довіряй **v-html** без санітизації; для форм/API — **CSRF**-захист за потреби; валідація **на сервері** обов’язкова.