@nitra/cursor 12.8.4 → 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.
- package/CHANGELOG.md +6 -0
- package/bin/n-cursor.js +3 -2
- package/package.json +1 -1
- package/rules/abie/js/env_dns.mdc +33 -0
- package/rules/abie/js/firebase_hosting.mdc +3 -0
- package/rules/abie/js/hc_pairing.mdc +23 -0
- package/rules/abie/js/ua_http_route.mdc +47 -0
- package/rules/abie/js/ua_node_selector.mdc +27 -0
- package/rules/abie/main.mdc +5 -133
- package/rules/doc-files/js/docs/index.md +15 -15
- package/rules/js/docs/main.md +6 -6
- package/rules/js/js/docs/check.md +11 -10
- package/rules/js/js/docs/tooling.md +7 -7
- package/rules/js/js/docs/utils_imports.md +13 -12
- package/rules/test/js/docs/stryker_config.md +12 -12
- package/rules/test/js/docs/vitest-config-pool-forks.md +13 -7
- package/scripts/docs/sync-setup-bun-deps-action.md +6 -5
- package/scripts/lib/docs/inline-template-links.md +13 -293
- package/scripts/lib/docs/mirror-parity.md +9 -9
- package/scripts/lib/docs/timing-summary.md +6 -6
- package/scripts/lib/inline-template-links.mjs +31 -0
- package/scripts/lib/mirror-parity.mjs +5 -3
- package/scripts/utils/docs/resolve-js-root.md +4 -4
- package/types/bin/n-cursor.d.ts +1 -1
package/CHANGELOG.md
CHANGED
package/bin/n-cursor.js
CHANGED
|
@@ -74,7 +74,7 @@ import { fileURLToPath } from 'node:url'
|
|
|
74
74
|
|
|
75
75
|
import { buildAgentsCommandBulletItems } from '../scripts/build-agents-commands.mjs'
|
|
76
76
|
import { formatGeneratedMarkdownLines, renderAgentsTemplate } from '../scripts/lib/generated-markdown.mjs'
|
|
77
|
-
import { inlineTemplateLinks } from '../scripts/lib/inline-template-links.mjs'
|
|
77
|
+
import { inlineMarkdownIncludes, inlineTemplateLinks } from '../scripts/lib/inline-template-links.mjs'
|
|
78
78
|
import {
|
|
79
79
|
detectAutoRules,
|
|
80
80
|
detectLegacyRuleIds,
|
|
@@ -422,7 +422,8 @@ async function readBundledRuleContent(rule, bundledRulesDir = BUNDLED_RULES_DIR)
|
|
|
422
422
|
)
|
|
423
423
|
}
|
|
424
424
|
const text = await readFile(bundledPath, 'utf8')
|
|
425
|
-
|
|
425
|
+
const withTemplates = await inlineTemplateLinks(text, dirname(bundledPath))
|
|
426
|
+
return inlineMarkdownIncludes(withTemplates, dirname(bundledPath))
|
|
426
427
|
}
|
|
427
428
|
|
|
428
429
|
/**
|
package/package.json
CHANGED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
## Внутрішньокластерні URL у env-файлах (dev / ua)
|
|
2
|
+
|
|
3
|
+
Правило стосується **будь-якого** внутрішньокластерного URL у env-файлах abie-проєкту, а не лише `HASURA_GRAPHQL_ENDPOINT`. Це може бути URL до Hasura, KVCMS, `auth-run-hl`, `file-link-hl` чи будь-якого іншого Service у кластері — у всіх випадках DNS-суфікс і namespace-префікс мають відповідати **середовищу** з імені env-файлу.
|
|
4
|
+
|
|
5
|
+
abie-проєкти живуть у **двох GKE-кластерах** (dev / ua), тож DNS-суфікс і namespace у URL відрізняються між `*.env`-файлами:
|
|
6
|
+
|
|
7
|
+
| env-файл (basename) | namespace-префікс у URL | DNS-суфікс кластера | примітка |
|
|
8
|
+
| --- | --- | --- | --- |
|
|
9
|
+
| `dev.env`, `.dev.env` | `dev-…` | `abie-dev.internal` | GKE-кластер dev |
|
|
10
|
+
| `ua.env`, `.ua.env` | `ua-…` | `abie-ua.internal` | GKE-кластер ua |
|
|
11
|
+
|
|
12
|
+
Канонічна форма URL — `http://<service>.<namespace>.svc.<cluster-dns-suffix>:<port>`. Суфікс — `<cluster>.internal`.
|
|
13
|
+
|
|
14
|
+
Приклади для одного env-файлу з двома сервісами (Hasura + KVCMS):
|
|
15
|
+
|
|
16
|
+
```env title="hasura/.dev.env"
|
|
17
|
+
HASURA_GRAPHQL_ENDPOINT=http://apruv-h-hl.dev-apruv.svc.abie-dev.internal:8080
|
|
18
|
+
KVCMS_URL=http://kvcms-hl.dev-apruv.svc.abie-dev.internal:8080
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
```env title="hasura/.ua.env"
|
|
22
|
+
HASURA_GRAPHQL_ENDPOINT=http://apruv-h-hl.ua-apruv.svc.abie-ua.internal:8080
|
|
23
|
+
KVCMS_URL=http://kvcms-hl.ua-apruv.svc.abie-ua.internal:8080
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
`<namespace>` (наприклад `dev-apruv` / `ua-apruv`) — `metadata.name` цільового namespace після kustomize-overlay для відповідного середовища; `<service>` — `metadata.name` headless Service (`-hl`) того сервісу, до якого йде URL.
|
|
27
|
+
|
|
28
|
+
**Перевірка `js/env_dns.mjs`** сканує всі `*.env` файли, basename яких збігається з `dev.env` / `ua.env` (з провідною крапкою чи без), знаходить **усі** internal URL (`http://<svc>.<ns>.svc.<dns>` — як для Hasura-ендпоінта, так і для KVCMS чи будь-якого іншого) і вимагає, щоб для кожного:
|
|
29
|
+
|
|
30
|
+
- DNS-суфікс відповідав env: `abie-dev.internal` / `abie-ua.internal`;
|
|
31
|
+
- namespace починався з `dev-` / `ua-` відповідно.
|
|
32
|
+
|
|
33
|
+
Загальне правило про **внутрішній** URL (не публічний домен) для `HASURA_GRAPHQL_ENDPOINT` лишається у **`hasura.mdc`** (для nitra і abie) — `rules/hasura/fix.mjs` приймає кластерний DNS-формат `<cluster>.internal`.
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
## Firebase Hosting
|
|
2
|
+
|
|
3
|
+
У **кожному** підкаталозі, що лежить **безпосередньо** в корені репозиторію, не тримати конфіг і кеш **Firebase Hosting**: у таких каталогах не повинно бути **`.firebaserc`**, **`firebase.json`** та каталогу **`.firebase/`** (у **самому** корені репозиторію ці імена перевіркою abie **не** розглядаються; `node_modules` / `.git` зі скану вилучаються).
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
## k8s: `hc.yaml` поруч із Deployment
|
|
2
|
+
|
|
3
|
+
Якщо під **`k8s`** є **Deployment**, у **тій самій директорії** має бути **`hc.yaml`** з **HealthCheckPolicy** (**`networking.gke.io/v1`**): коректний modeline **`$schema`**, **`httpHealthCheck.requestPath`** — непорожній шлях від кореня (рядок, що починається з **`/`**: канонічно **`/healthz`**, але також допустимі **`/IsAlive`**, **`/api/live`** тощо — узгоджується з реальним endpoint сервісу), порт **8080**, **`targetRef.name`** — **headless** **Service** з суфіксом **`-hl`** (узгоджено з парою **`svc.yaml`** / **`svc-hl.yaml`** у **k8s.mdc**): або **`${metadata.name}-hl`**, або те саме ім'я, якщо **`metadata.name`** уже з **`-hl`**.
|
|
4
|
+
|
|
5
|
+
```yaml title="hc.yaml"
|
|
6
|
+
# yaml-language-server: $schema=https://datreeio.github.io/CRDs-catalog/networking.gke.io/healthcheckpolicy_v1.json
|
|
7
|
+
apiVersion: networking.gke.io/v1
|
|
8
|
+
kind: HealthCheckPolicy
|
|
9
|
+
metadata:
|
|
10
|
+
name: СЕРВІС
|
|
11
|
+
namespace: dev # kustomize overlay
|
|
12
|
+
spec:
|
|
13
|
+
default:
|
|
14
|
+
config:
|
|
15
|
+
type: HTTP
|
|
16
|
+
httpHealthCheck:
|
|
17
|
+
requestPath: /healthz
|
|
18
|
+
port: 8080
|
|
19
|
+
targetRef:
|
|
20
|
+
group: ''
|
|
21
|
+
kind: Service
|
|
22
|
+
name: СЕРВІС-hl
|
|
23
|
+
```
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
## k8s: overlay **HTTPRoute** (**ua**)
|
|
2
|
+
|
|
3
|
+
За наявності **Deployment** під **k8s** і наявності **Vite** (**`vite.config.js`**, **`vite.config.mjs`** або **`vite.config.ts`** у каталозі пакета) у **`ua/kustomization.yaml`** цього пакета потрібні **inline JSON6902** у **`patches`**: **target** **`kind: HTTPRoute`**, **непорожній `name`** (як у маніфесті маршруту). Мають бути зміни **`/spec/hostnames`** (домени abie — у скрипті) та **`/spec/parentRefs/0/namespace`** (**`ua`**, також дозволені префікси **`ua-*`**, наприклад **`ua-b2b`**). Як обирати **`op`** (**add** / **replace** тощо) у patch — **k8s.mdc** (розділ про JSON patch у kustomization).
|
|
4
|
+
|
|
5
|
+
### HTTPRoute: спільні сервіси **`auth-run-hl`**, **`file-link-hl`**
|
|
6
|
+
|
|
7
|
+
У **HTTPRoute** у шляху з **`…/k8s/base/…`** у **`spec.hostnames`** дозволені лише **`aiml.live`**, **`*.aiml.live`** та інші піддомени **aiml.live** (перевірка — Rego-пакет **`abie.http_route_base`**, див. розділ нижче).
|
|
8
|
+
|
|
9
|
+
Ці **Service** (headless **`-hl`**) живуть у **базовому** неймспейсі **`dev`**. У маніфесті **HTTPRoute** під **`k8s`** (шар без **`ua/`** — наприклад **`…/k8s/base/hr.yaml`**) для кожного **`backendRefs`** до такого сервісу явно вкажи **`namespace: dev`** і порт **8080**:
|
|
10
|
+
|
|
11
|
+
```yaml title="…/k8s/base/hr.yaml (фрагмент)"
|
|
12
|
+
spec:
|
|
13
|
+
rules:
|
|
14
|
+
- matches:
|
|
15
|
+
- path:
|
|
16
|
+
type: PathPrefix
|
|
17
|
+
value: /
|
|
18
|
+
backendRefs:
|
|
19
|
+
- name: auth-run-hl
|
|
20
|
+
namespace: dev
|
|
21
|
+
port: 8080
|
|
22
|
+
- name: file-link-hl
|
|
23
|
+
namespace: dev
|
|
24
|
+
port: 8080
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
У **`ua/kustomization.yaml`** додай до того самого **inline** patch на **`HTTPRoute`** (той самий **`target.name`**) операції **JSON6902** з **`path`**: **`/spec/rules/<i>/backendRefs/<j>/namespace`**, де **`<i>`** / **`<j>`** — індекси відповідно до порядку **`spec.rules`** та **`backendRefs`** у base-файлі; **`value`**: **`ua`** (також дозволені **`ua-*`**). Якщо кілька таких **`backendRefs`**, потрібна окрема операція для кожного.
|
|
28
|
+
|
|
29
|
+
```yaml title="…/ua/kustomization.yaml (фрагмент)"
|
|
30
|
+
- target:
|
|
31
|
+
kind: HTTPRoute
|
|
32
|
+
name: my-httproute
|
|
33
|
+
patch: |-
|
|
34
|
+
- op: replace
|
|
35
|
+
path: /spec/hostnames
|
|
36
|
+
value:
|
|
37
|
+
- "abie.app" # зокрема vybeerai.com.ua, *.vybeerai.com.ua, *.abie.app
|
|
38
|
+
- op: replace
|
|
39
|
+
path: /spec/parentRefs/0/namespace
|
|
40
|
+
value: ua
|
|
41
|
+
- op: replace
|
|
42
|
+
path: /spec/rules/0/backendRefs/0/namespace
|
|
43
|
+
value: ua
|
|
44
|
+
- op: replace
|
|
45
|
+
path: /spec/rules/0/backendRefs/1/namespace
|
|
46
|
+
value: ua
|
|
47
|
+
```
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
## k8s: overlay **ua** і nodeSelector
|
|
2
|
+
|
|
3
|
+
У **`…/ua/kustomization.yaml`** того пакета, у дереві **`k8s`** якого є **Deployment**, потрібен patch на **`kind: Deployment`**: **`spec.template.spec.nodeSelector`** з **`preem: false`**. Форму **JSON6902** (шлях **`/spec/template/spec/nodeSelector`**, **`op`**) див. **k8s.mdc**.
|
|
4
|
+
|
|
5
|
+
```yaml title="…/ua/kustomization.yaml (фрагмент)"
|
|
6
|
+
patches:
|
|
7
|
+
- target:
|
|
8
|
+
kind: Deployment
|
|
9
|
+
name: my-app
|
|
10
|
+
patch: |-
|
|
11
|
+
- op: add
|
|
12
|
+
path: /spec/template/spec/nodeSelector
|
|
13
|
+
value:
|
|
14
|
+
preem: 'false'
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
### Базовий Deployment (`…/base/`)
|
|
18
|
+
|
|
19
|
+
Якщо **Deployment** у YAML під **`k8s`** лежить у шляху з сегментом **`base`**, у **`spec.template.spec.nodeSelector`** має бути **`preem`** зі значенням **істинно** (**`true`** або рядок **`'true'`**); overlay **ua** підміняє селектор.
|
|
20
|
+
|
|
21
|
+
```yaml title="…/base/deploy.yaml (фрагмент)"
|
|
22
|
+
spec:
|
|
23
|
+
template:
|
|
24
|
+
spec:
|
|
25
|
+
nodeSelector:
|
|
26
|
+
preem: 'true' # буде замінено через kustomize
|
|
27
|
+
```
|
package/rules/abie/main.mdc
CHANGED
|
@@ -6,139 +6,13 @@ version: '1.22'
|
|
|
6
6
|
|
|
7
7
|
Правило **abie** для споживачів **@nitra/cursor**: **k8s** (Deployment + **HealthCheckPolicy** у **`hc.yaml`**, overlay **ua** — **nodeSelector**, **HTTPRoute** (будь-який непорожній **`target.name`**, для спільних сервісів **`auth-run-hl`** / **`file-link-hl`** — **`namespace: dev`** у base та patch **`…/backendRefs/…/namespace`** у **ua**)), гілки **dev**, **ua** у **clean-merged-branch**, а також заборона тримати артефакти **Firebase Hosting** у **підкаталогах першого рівня** (безпосередні діти кореня репозиторію; у самому корені ці імена не вимагаються до видалення).
|
|
8
8
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
Якщо під **`k8s`** є **Deployment**, у **тій самій директорії** має бути **`hc.yaml`** з **HealthCheckPolicy** (**`networking.gke.io/v1`**): коректний modeline **`$schema`**, **`httpHealthCheck.requestPath`** — непорожній шлях від кореня (рядок, що починається з **`/`**: канонічно **`/healthz`**, але також допустимі **`/IsAlive`**, **`/api/live`** тощо — узгоджується з реальним endpoint сервісу), порт **8080**, **`targetRef.name`** — **headless** **Service** з суфіксом **`-hl`** (узгоджено з парою **`svc.yaml`** / **`svc-hl.yaml`** у **k8s.mdc**): або **`${metadata.name}-hl`**, або те саме ім’я, якщо **`metadata.name`** уже з **`-hl`**.
|
|
12
|
-
|
|
13
|
-
```yaml title="hc.yaml"
|
|
14
|
-
# yaml-language-server: $schema=https://datreeio.github.io/CRDs-catalog/networking.gke.io/healthcheckpolicy_v1.json
|
|
15
|
-
apiVersion: networking.gke.io/v1
|
|
16
|
-
kind: HealthCheckPolicy
|
|
17
|
-
metadata:
|
|
18
|
-
name: СЕРВІС
|
|
19
|
-
namespace: dev # kustomize overlay
|
|
20
|
-
spec:
|
|
21
|
-
default:
|
|
22
|
-
config:
|
|
23
|
-
type: HTTP
|
|
24
|
-
httpHealthCheck:
|
|
25
|
-
requestPath: /healthz
|
|
26
|
-
port: 8080
|
|
27
|
-
targetRef:
|
|
28
|
-
group: ''
|
|
29
|
-
kind: Service
|
|
30
|
-
name: СЕРВІС-hl
|
|
31
|
-
```
|
|
32
|
-
|
|
33
|
-
## k8s: overlay **HTTPRoute** (**ua**)
|
|
34
|
-
|
|
35
|
-
За наявності **Deployment** під **k8s** і наявності **Vite** (**`vite.config.js`**, **`vite.config.mjs`** або **`vite.config.ts`** у каталозі пакета) у **`ua/kustomization.yaml`** цього пакета потрібні **inline JSON6902** у **`patches`**: **target** **`kind: HTTPRoute`**, **непорожній `name`** (як у маніфесті маршруту). Мають бути зміни **`/spec/hostnames`** (домени abie — у скрипті) та **`/spec/parentRefs/0/namespace`** (**`ua`**, також дозволені префікси **`ua-*`**, наприклад **`ua-b2b`**). Як обирати **`op`** (**add** / **replace** тощо) у patch — **k8s.mdc** (розділ про JSON patch у kustomization).
|
|
36
|
-
|
|
37
|
-
### HTTPRoute: спільні сервіси **`auth-run-hl`**, **`file-link-hl`**
|
|
38
|
-
|
|
39
|
-
У **HTTPRoute** у шляху з **`…/k8s/base/…`** у **`spec.hostnames`** дозволені лише **`aiml.live`**, **`*.aiml.live`** та інші піддомени **aiml.live** (перевірка — Rego-пакет **`abie.http_route_base`**, див. розділ нижче).
|
|
40
|
-
|
|
41
|
-
Ці **Service** (headless **`-hl`**) живуть у **базовому** неймспейсі **`dev`**. У маніфесті **HTTPRoute** під **`k8s`** (шар без **`ua/`** — наприклад **`…/k8s/base/hr.yaml`**) для кожного **`backendRefs`** до такого сервісу явно вкажи **`namespace: dev`** і порт **8080**:
|
|
42
|
-
|
|
43
|
-
```yaml title="…/k8s/base/hr.yaml (фрагмент)"
|
|
44
|
-
spec:
|
|
45
|
-
rules:
|
|
46
|
-
- matches:
|
|
47
|
-
- path:
|
|
48
|
-
type: PathPrefix
|
|
49
|
-
value: /
|
|
50
|
-
backendRefs:
|
|
51
|
-
- name: auth-run-hl
|
|
52
|
-
namespace: dev
|
|
53
|
-
port: 8080
|
|
54
|
-
- name: file-link-hl
|
|
55
|
-
namespace: dev
|
|
56
|
-
port: 8080
|
|
57
|
-
```
|
|
58
|
-
|
|
59
|
-
У **`ua/kustomization.yaml`** додай до того самого **inline** patch на **`HTTPRoute`** (той самий **`target.name`**) операції **JSON6902** з **`path`**: **`/spec/rules/<i>/backendRefs/<j>/namespace`**, де **`<i>`** / **`<j>`** — індекси відповідно до порядку **`spec.rules`** та **`backendRefs`** у base-файлі; **`value`**: **`ua`** (також дозволені **`ua-*`**). Якщо кілька таких **`backendRefs`**, потрібна окрема операція для кожного.
|
|
60
|
-
|
|
61
|
-
```yaml title="…/ua/kustomization.yaml (фрагмент)"
|
|
62
|
-
- target:
|
|
63
|
-
kind: HTTPRoute
|
|
64
|
-
name: my-httproute
|
|
65
|
-
patch: |-
|
|
66
|
-
- op: replace
|
|
67
|
-
path: /spec/hostnames
|
|
68
|
-
value:
|
|
69
|
-
- "abie.app" # зокрема vybeerai.com.ua, *.vybeerai.com.ua, *.abie.app
|
|
70
|
-
- op: replace
|
|
71
|
-
path: /spec/parentRefs/0/namespace
|
|
72
|
-
value: ua
|
|
73
|
-
- op: replace
|
|
74
|
-
path: /spec/rules/0/backendRefs/0/namespace
|
|
75
|
-
value: ua
|
|
76
|
-
- op: replace
|
|
77
|
-
path: /spec/rules/0/backendRefs/1/namespace
|
|
78
|
-
value: ua
|
|
79
|
-
```
|
|
80
|
-
|
|
81
|
-
## k8s: overlay **ua** і nodeSelector
|
|
82
|
-
|
|
83
|
-
У **`…/ua/kustomization.yaml`** того пакета, у дереві **`k8s`** якого є **Deployment**, потрібен patch на **`kind: Deployment`**: **`spec.template.spec.nodeSelector`** з **`preem: false`**. Форму **JSON6902** (шлях **`/spec/template/spec/nodeSelector`**, **`op`**) див. **k8s.mdc**.
|
|
84
|
-
|
|
85
|
-
```yaml title="…/ua/kustomization.yaml (фрагмент)"
|
|
86
|
-
patches:
|
|
87
|
-
- target:
|
|
88
|
-
kind: Deployment
|
|
89
|
-
name: my-app
|
|
90
|
-
patch: |-
|
|
91
|
-
- op: add
|
|
92
|
-
path: /spec/template/spec/nodeSelector
|
|
93
|
-
value:
|
|
94
|
-
preem: 'false'
|
|
95
|
-
```
|
|
96
|
-
|
|
97
|
-
### Базовий Deployment (`…/base/`)
|
|
9
|
+
[k8s-hc-yaml](./js/hc_pairing.mdc)
|
|
98
10
|
|
|
99
|
-
|
|
11
|
+
[k8s-http-route-ua](./js/ua_http_route.mdc)
|
|
100
12
|
|
|
101
|
-
|
|
102
|
-
spec:
|
|
103
|
-
template:
|
|
104
|
-
spec:
|
|
105
|
-
nodeSelector:
|
|
106
|
-
preem: 'true' # буде замінено через kustomize
|
|
107
|
-
```
|
|
108
|
-
|
|
109
|
-
## Внутрішньокластерні URL у env-файлах (dev / ua)
|
|
110
|
-
|
|
111
|
-
Правило стосується **будь-якого** внутрішньокластерного URL у env-файлах abie-проєкту, а не лише `HASURA_GRAPHQL_ENDPOINT`. Це може бути URL до Hasura, KVCMS, `auth-run-hl`, `file-link-hl` чи будь-якого іншого Service у кластері — у всіх випадках DNS-суфікс і namespace-префікс мають відповідати **середовищу** з імені env-файлу.
|
|
112
|
-
|
|
113
|
-
abie-проєкти живуть у **двох GKE-кластерах** (dev / ua), тож DNS-суфікс і namespace у URL відрізняються між `*.env`-файлами:
|
|
13
|
+
[k8s-nodeselector](./js/ua_node_selector.mdc)
|
|
114
14
|
|
|
115
|
-
|
|
116
|
-
| --- | --- | --- | --- |
|
|
117
|
-
| `dev.env`, `.dev.env` | `dev-…` | `abie-dev.internal` | GKE-кластер dev |
|
|
118
|
-
| `ua.env`, `.ua.env` | `ua-…` | `abie-ua.internal` | GKE-кластер ua |
|
|
119
|
-
|
|
120
|
-
Канонічна форма URL — `http://<service>.<namespace>.svc.<cluster-dns-suffix>:<port>`. Суфікс — `<cluster>.internal`.
|
|
121
|
-
|
|
122
|
-
Приклади для одного env-файлу з двома сервісами (Hasura + KVCMS):
|
|
123
|
-
|
|
124
|
-
```env title="hasura/.dev.env"
|
|
125
|
-
HASURA_GRAPHQL_ENDPOINT=http://apruv-h-hl.dev-apruv.svc.abie-dev.internal:8080
|
|
126
|
-
KVCMS_URL=http://kvcms-hl.dev-apruv.svc.abie-dev.internal:8080
|
|
127
|
-
```
|
|
128
|
-
|
|
129
|
-
```env title="hasura/.ua.env"
|
|
130
|
-
HASURA_GRAPHQL_ENDPOINT=http://apruv-h-hl.ua-apruv.svc.abie-ua.internal:8080
|
|
131
|
-
KVCMS_URL=http://kvcms-hl.ua-apruv.svc.abie-ua.internal:8080
|
|
132
|
-
```
|
|
133
|
-
|
|
134
|
-
`<namespace>` (наприклад `dev-apruv` / `ua-apruv`) — `metadata.name` цільового namespace після kustomize-overlay для відповідного середовища; `<service>` — `metadata.name` headless Service (`-hl`) того сервісу, до якого йде URL.
|
|
135
|
-
|
|
136
|
-
**Перевірка `js/env_dns.mjs`** сканує всі `*.env` файли, basename яких збігається з `dev.env` / `ua.env` (з провідною крапкою чи без), знаходить **усі** internal URL (`http://<svc>.<ns>.svc.<dns>` — як для Hasura-ендпоінта, так і для KVCMS чи будь-якого іншого) і вимагає, щоб для кожного:
|
|
137
|
-
|
|
138
|
-
- DNS-суфікс відповідав env: `abie-dev.internal` / `abie-ua.internal`;
|
|
139
|
-
- namespace починався з `dev-` / `ua-` відповідно.
|
|
140
|
-
|
|
141
|
-
Загальне правило про **внутрішній** URL (не публічний домен) для `HASURA_GRAPHQL_ENDPOINT` лишається у **`hasura.mdc`** (для nitra і abie) — `rules/hasura/fix.mjs` приймає кластерний DNS-формат `<cluster>.internal`.
|
|
15
|
+
[env-dns](./js/env_dns.mdc)
|
|
142
16
|
|
|
143
17
|
## `@nitra/abie-shared` у `devDependencies`
|
|
144
18
|
|
|
@@ -148,9 +22,7 @@ KVCMS_URL=http://kvcms-hl.ua-apruv.svc.abie-ua.internal:8080
|
|
|
148
22
|
bun add -d @nitra/abie-shared
|
|
149
23
|
```
|
|
150
24
|
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
У **кожному** підкаталозі, що лежить **безпосередньо** в корені репозиторію, не тримати конфіг і кеш **Firebase Hosting**: у таких каталогах не повинно бути **`.firebaserc`**, **`firebase.json`** та каталогу **`.firebase/`** (у **самому** корені репозиторію ці імена перевіркою abie **не** розглядаються; `node_modules` / `.git` зі скану вилучаються).
|
|
25
|
+
[firebase](./js/firebase_hosting.mdc)
|
|
154
26
|
|
|
155
27
|
## Git branches
|
|
156
28
|
|
|
@@ -6,19 +6,19 @@ resource: npm/rules/doc-files/js/
|
|
|
6
6
|
|
|
7
7
|
# npm/rules/doc-files/js
|
|
8
8
|
|
|
9
|
-
| Файл
|
|
10
|
-
|
|
11
|
-
| [docgen-crc.mjs](docgen-crc.md)
|
|
9
|
+
| Файл | Тип |
|
|
10
|
+
| ------------------------------------------------------- | --------- |
|
|
11
|
+
| [docgen-crc.mjs](docgen-crc.md) | JS Module |
|
|
12
12
|
| [docgen-extract-anchors.mjs](docgen-extract-anchors.md) | JS Module |
|
|
13
|
-
| [docgen-extract.mjs](docgen-extract.md)
|
|
14
|
-
| [docgen-files-batch.mjs](docgen-files-batch.md)
|
|
15
|
-
| [docgen-gen.mjs](docgen-gen.md)
|
|
16
|
-
| [docgen-ignore.mjs](docgen-ignore.md)
|
|
17
|
-
| [docgen-judge-measure.mjs](docgen-judge-measure.md)
|
|
18
|
-
| [docgen-judge.mjs](docgen-judge.md)
|
|
19
|
-
| [docgen-prompts.mjs](docgen-prompts.md)
|
|
20
|
-
| [docgen-scan.mjs](docgen-scan.md)
|
|
21
|
-
| [run-lint.mjs](run-lint.md)
|
|
22
|
-
| [units-js.mjs](units-js.md)
|
|
23
|
-
| [units-rs.mjs](units-rs.md)
|
|
24
|
-
| [units.mjs](units.md)
|
|
13
|
+
| [docgen-extract.mjs](docgen-extract.md) | JS Module |
|
|
14
|
+
| [docgen-files-batch.mjs](docgen-files-batch.md) | JS Module |
|
|
15
|
+
| [docgen-gen.mjs](docgen-gen.md) | JS Module |
|
|
16
|
+
| [docgen-ignore.mjs](docgen-ignore.md) | JS Module |
|
|
17
|
+
| [docgen-judge-measure.mjs](docgen-judge-measure.md) | JS Module |
|
|
18
|
+
| [docgen-judge.mjs](docgen-judge.md) | JS Module |
|
|
19
|
+
| [docgen-prompts.mjs](docgen-prompts.md) | JS Module |
|
|
20
|
+
| [docgen-scan.mjs](docgen-scan.md) | JS Module |
|
|
21
|
+
| [run-lint.mjs](run-lint.md) | JS Module |
|
|
22
|
+
| [units-js.mjs](units-js.md) | JS Module |
|
|
23
|
+
| [units-rs.mjs](units-rs.md) | JS Module |
|
|
24
|
+
| [units.mjs](units.md) | JS Module |
|
package/rules/js/docs/main.md
CHANGED
|
@@ -10,19 +10,19 @@ docgen:
|
|
|
10
10
|
|
|
11
11
|
## Огляд
|
|
12
12
|
|
|
13
|
-
|
|
13
|
+
Модуль забезпечує виконання функцій `run`, `filterJsFiles` та `lint` для аналізу кодової бази. Він виконує стандартну перевірку проєкту, відбираючи файли з розширеннями JavaScript за допомогою `filterJsFiles` та запускаючи перевірку JS-коду за допомогою `lint`.
|
|
14
14
|
|
|
15
15
|
## Поведінка
|
|
16
16
|
|
|
17
17
|
run виконує стандартну перевірку проєкту.
|
|
18
|
-
filterJsFiles відбирає з наданого списку лише
|
|
19
|
-
lint запускає перевірку
|
|
18
|
+
filterJsFiles відбирає з наданого списку лише файли з розширеннями, характерними для JavaScript.
|
|
19
|
+
lint запускає перевірку JS-коду, виконуючи або повний аналіз проєкту, або класифіковану перевірку змінених файлів.
|
|
20
20
|
|
|
21
21
|
## Публічний API
|
|
22
22
|
|
|
23
|
-
run —
|
|
24
|
-
filterJsFiles — відбирає лише
|
|
25
|
-
lint — запускає інструменти для
|
|
23
|
+
run — основна точка входу для виконання правил, що включає перевірку логіки застосування, логіки політик та посилань на метадані.
|
|
24
|
+
filterJsFiles — відбирає лише файли, схожі на JavaScript, з наданого списку.
|
|
25
|
+
lint — запускає інструменти oxlint та eslint (для окремих файлів або всього проекту) та jscpd+knip (тільки для всього проекту), з можливістю автоматичного виправлення або лише виявлення проблем.
|
|
26
26
|
|
|
27
27
|
## Гарантії поведінки
|
|
28
28
|
|
|
@@ -5,23 +5,24 @@ resource: npm/rules/js/js/check.mjs
|
|
|
5
5
|
docgen:
|
|
6
6
|
crc: 7ad4aa59
|
|
7
7
|
model: omlx/gemma-4-e4b-it-OptiQ-4bit
|
|
8
|
-
score:
|
|
8
|
+
score: 100
|
|
9
9
|
---
|
|
10
10
|
|
|
11
11
|
## Огляд
|
|
12
12
|
|
|
13
|
-
Модуль
|
|
13
|
+
Модуль виконує валідацію конфігураційних файлів, включаючи `package.json`, `.oxlintrc.json`, `knip.json`, `knip-canonical.json` та `.eslintrc.json`. Він перевіряє відповідність структури та конфігурацій встановленим стандартам. (js.mdc) (text.mdc)
|
|
14
14
|
|
|
15
15
|
## Поведінка
|
|
16
16
|
|
|
17
|
-
1.
|
|
18
|
-
2.
|
|
19
|
-
3.
|
|
20
|
-
4.
|
|
21
|
-
5.
|
|
22
|
-
6.
|
|
23
|
-
7.
|
|
24
|
-
8.
|
|
17
|
+
1. Викликається функція check.
|
|
18
|
+
2. Ініціалізується механізм збору результатів перевірок.
|
|
19
|
+
3. Перевіряється конфігураційний файл ESLint.
|
|
20
|
+
4. Перевіряється структура пакетів у workspace'ах, включаючи наявність `"type": "module"` та вимоги до версій Node та Bun у `package.json` кожного пакета.
|
|
21
|
+
5. Перевіряється конфігураційний файл `.oxlintrc.json` на відповідність канонічному шаблону.
|
|
22
|
+
6. Перевіряється структура файлів у робочих процесах GitHub, зокрема наявність `lint-js.yml` та відсутність дублювання кроків лінтингу в `lint.yml`.
|
|
23
|
+
7. Перевіряється наявність `knip.json` у корені проєкту. Якщо відсутній, він створюється з канонічного шаблону.
|
|
24
|
+
8. Після виконання всіх перевірок, сканується проєкт на наявність застарілих конфігураційних файлів ESLint.
|
|
25
|
+
9. Повертається код виходу, що відображає загальний статус виконання перевірок.
|
|
25
26
|
|
|
26
27
|
## Публічний API
|
|
27
28
|
|
|
@@ -10,19 +10,19 @@ docgen:
|
|
|
10
10
|
|
|
11
11
|
## Огляд
|
|
12
12
|
|
|
13
|
-
|
|
13
|
+
Визначає шляхи до канонічних JSON-файлів для інструментів oxlint та knip через OXLINT_CANONICAL_JSON_PATH та KNIP_CANONICAL_JSON_PATH. Також перевіряє відповідність конфігураційного файлу .oxlintrc.json значенням, встановленим у oxlint-canonical.json, за допомогою verifyOxlintRcAgainstCanonical.
|
|
14
14
|
|
|
15
15
|
## Поведінка
|
|
16
16
|
|
|
17
|
-
OXLINT_CANONICAL_JSON_PATH — Вказує
|
|
18
|
-
KNIP_CANONICAL_JSON_PATH — Вказує
|
|
19
|
-
verifyOxlintRcAgainstCanonical — Перевіряє
|
|
17
|
+
OXLINT_CANONICAL_JSON_PATH — Вказує шлях до канонічного JSON-файлу для oxlint у цьому пакеті.
|
|
18
|
+
KNIP_CANONICAL_JSON_PATH — Вказує шлях до канонічного JSON-файлу для knip у цьому пакеті.
|
|
19
|
+
verifyOxlintRcAgainstCanonical — Перевіряє конфігураційний файл `.oxlintrc.json` на відповідність канонічним значенням, визначеним у `oxlint-canonical.json`.
|
|
20
20
|
|
|
21
21
|
## Публічний API
|
|
22
22
|
|
|
23
|
-
OXLINT_CANONICAL_JSON_PATH —
|
|
24
|
-
KNIP_CANONICAL_JSON_PATH —
|
|
25
|
-
verifyOxlintRcAgainstCanonical —
|
|
23
|
+
OXLINT_CANONICAL_JSON_PATH — Вказує на файл з еталонними налаштуваннями oxlint для валідації.
|
|
24
|
+
KNIP_CANONICAL_JSON_PATH — Шлях до еталонних налаштувань knip, які копіюються у корінь проєкту, якщо їх немає.
|
|
25
|
+
verifyOxlintRcAgainstCanonical — Порівнює конфігураційний файл `.oxlintrc.json` з еталоном, перевіряючи, чи всі правила з еталону присутні, а інші поля збігаються з каноном.
|
|
26
26
|
|
|
27
27
|
## Гарантії поведінки
|
|
28
28
|
|
|
@@ -10,21 +10,22 @@ docgen:
|
|
|
10
10
|
|
|
11
11
|
## Огляд
|
|
12
12
|
|
|
13
|
-
Модуль сканує
|
|
13
|
+
Модуль сканує монорепозиторій для пошуку каталогів `utils` та аналізу їхнього вмісту. Аналіз файлів JS/TS у цих каталогах здійснюється на відповідність шаблону забороненого відносного імпорту, що базується на конфігурації, визначеній у `.n-cursor.json`. При виявленні порушень, система генерує повідомлення, позначене як (js.mdc).
|
|
14
14
|
|
|
15
15
|
## Поведінка
|
|
16
16
|
|
|
17
|
-
1. Визначає
|
|
18
|
-
2. Зчитує
|
|
19
|
-
3.
|
|
20
|
-
4.
|
|
21
|
-
5.
|
|
22
|
-
6. Для кожного зібраного
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
17
|
+
1. Визначає кореневий каталог для аналізу.
|
|
18
|
+
2. Зчитує шляхи, які слід ігнорувати, на основі конфігурації .n-cursor.json.
|
|
19
|
+
3. Знаходить усі каталоги з назвою `utils` у межах кореневих каталогів пакетів монорепозиторію, ігноруючи типові артефакти (наприклад, node_modules, .git, dist).
|
|
20
|
+
4. Якщо знайдено жодних каталогів `utils`, перевірка вважається успішною і завершується.
|
|
21
|
+
5. Для кожного знайденого каталогу `utils` збирає всі джерела файлів, які відповідають шаблону JS/TS, виключаючи тестові файли та файли у каталогах `tests/` чи `__fixtures__/`.
|
|
22
|
+
6. Для кожного зібраного файлу:
|
|
23
|
+
а. Зчитує вміст файлу.
|
|
24
|
+
б. Витягує всі рядкові імпорти (статичні, динамічні, `require`) з коду.
|
|
25
|
+
в. Перевіряє кожен витягнутий імпорт на відповідність шаблону забороненого відносного імпорту.
|
|
26
|
+
г. Якщо імпорт є забороненим, фіксує порушення, вказуючи повний шлях до файлу та сам імпорт.
|
|
27
|
+
7. Після перевірки всіх файлів, якщо порушень не знайдено, перевірка вважається успішною (js.mdc).
|
|
28
|
+
8. Повертає код завершення, що відображає успіх або виявлені порушення (js.mdc).
|
|
28
29
|
|
|
29
30
|
## Гарантії поведінки
|
|
30
31
|
|
|
@@ -10,22 +10,22 @@ docgen:
|
|
|
10
10
|
|
|
11
11
|
## Огляд
|
|
12
12
|
|
|
13
|
-
Модуль
|
|
13
|
+
Модуль перевіряє готовність JavaScript-проєктів до виконання тестів. Він збирає кореневі каталоги проєктів, використовуючи публічну функцію `check` для валідації. Перевірка гарантує наявність необхідних конфігураційних файлів, зокрема `mutation.json` та `package.json`, у кожному знайденому проєкті. Модуль свідомо пропускає каталоги `node_modules`. Він також додає відповідні артефакти тестів до `.gitignore` у коренях проєктів. (test.mdc)
|
|
14
14
|
|
|
15
15
|
## Поведінка
|
|
16
16
|
|
|
17
17
|
1. Викликається `check` для ініціалізації процесу перевірки.
|
|
18
|
-
2. Перевіряється, чи у конфігурації дозволено
|
|
19
|
-
3.
|
|
20
|
-
4. Перевіряється наявність усіх канонічних
|
|
21
|
-
5. Для кожного знайденого кореневого каталогу
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
6. Виконується
|
|
28
|
-
7. Процес завершується з
|
|
18
|
+
2. Перевіряється, чи у конфігурації дозволено виконання перевірок для JavaScript. Якщо ні, процес завершується з кодом, що вказує на пропуск.
|
|
19
|
+
3. Збираються всі кореневі каталоги, що містять JavaScript-проєкти. Якщо жоден не знайдено, процес завершується з повідомленням про помилку (test.mdc).
|
|
20
|
+
4. Перевіряється наявність усіх канонічних файлів конфігурації (baseline) у системі. Якщо хоча б один відсутній, процес завершується з повідомленням про помилку.
|
|
21
|
+
5. Для кожного знайденого кореневого каталогу проєкту виконується наступне:
|
|
22
|
+
а. Визначається, чи містить проєкт файли `.vue`.
|
|
23
|
+
б. Створюється або оновлюється файл `stryker.config.mjs` у корені проєкту, копіюючи відповідний канонічний baseline. У цьому файлі замінюється посилання на конфігурацію Vitest на фактичне ім'я конфігурації проєкту.
|
|
24
|
+
в. Якщо проєкт містить файли `.vue` і файл `stryker.config.mjs` вже існував, виконується аугментація цього файлу. Аугментація додає плагін `vue-macros` до конфігурації, якщо він відсутній, зберігаючи при цьому форматування користувача.
|
|
25
|
+
г. Створюється або оновлюється файл `stryker-vue-macros-ignorer.mjs` у корені проєкту.
|
|
26
|
+
д. Створюється або оновлюється файл конфігурації Vitest у корені проєкту, використовуючи відповідний канонічний baseline.
|
|
27
|
+
6. Виконується гарантування, що файли, що містять артефакти тестів Stryker та покриття (наприклад, `**/reports/stryker/`, `**/coverage/`), додані до `.gitignore` у корені проєкту. Якщо додано нові записи, виводиться повідомлення (test.mdc).
|
|
28
|
+
7. Процес завершується з кодом, що вказує на успішне виконання або на виявлені порушення.
|
|
29
29
|
|
|
30
30
|
## Гарантії поведінки
|
|
31
31
|
|
|
@@ -10,16 +10,22 @@ docgen:
|
|
|
10
10
|
|
|
11
11
|
## Огляд
|
|
12
12
|
|
|
13
|
-
|
|
13
|
+
Визначає шлях до конфігураційного файлу Vitest. Перевіряє, чи встановлено в цьому файлі `pool: 'forks'`. (test.mdc)
|
|
14
|
+
|
|
15
|
+
Поведінка:
|
|
16
|
+
|
|
17
|
+
- Якщо конфігураційний файл не знайдено, завершує роботу з кодом, що вказує на помилку. (test.mdc)
|
|
18
|
+
- Якщо конфігураційний файл знайдено, але в ньому відсутній ключ `pool: 'forks'`, завершує роботу з кодом, що вказує на помилку. (test.mdc)
|
|
19
|
+
- Якщо конфігураційний файл знайдено і містить `pool: 'forks'`, завершує роботу з кодом 0. (test.mdc)
|
|
14
20
|
|
|
15
21
|
## Поведінка
|
|
16
22
|
|
|
17
|
-
1. Визначається
|
|
18
|
-
2. Якщо конфігураційний файл відсутній, виконується пропуск перевірки, і повертається код
|
|
19
|
-
3. Якщо
|
|
20
|
-
4. Перевіряється, чи містить вміст конфігураційного файлу рядок, що вказує на
|
|
21
|
-
5. Якщо рядок знайдено,
|
|
22
|
-
6. Якщо рядок не знайдено,
|
|
23
|
+
1. Визначається шлях до файлу конфігурації Vitest, шукаючи спочатку `vitest.config.mjs`, а потім `vitest.config.js` у корені репозиторію.
|
|
24
|
+
2. Якщо конфігураційний файл відсутній, виконується пропуск перевірки, і повертається код виходу 0.
|
|
25
|
+
3. Якщо файл знайдено, його вміст зчитується.
|
|
26
|
+
4. Перевіряється, чи містить вміст конфігураційного файлу рядок, що вказує на встановлення `pool: 'forks'`.
|
|
27
|
+
5. Якщо рядок знайдено, виконується успішне повідомлення (test.mdc).
|
|
28
|
+
6. Якщо рядок не знайдено, виконується повідомлення про помилку (test.mdc), що вказує на необхідність додавання `pool: 'forks'` для захисту від гонки в умовах паралельного виконання тестів.
|
|
23
29
|
7. Функція `check` повертає код виходу, що відображає результат перевірки (0 для успіху/пропуску, 1 для помилки).
|
|
24
30
|
|
|
25
31
|
## Публічний API
|
|
@@ -10,19 +10,20 @@ docgen:
|
|
|
10
10
|
|
|
11
11
|
## Огляд
|
|
12
12
|
|
|
13
|
-
Копіює composite GitHub Action `setup-bun-deps` з каталогу `github-actions/setup-bun-deps/` у цільовий репозиторій
|
|
13
|
+
Копіює composite GitHub Action `setup-bun-deps` з каталогу `github-actions/setup-bun-deps/` у корені tarball пакету `@nitra/cursor` у цільовий репозиторій за шлях `.github/actions/setup-bun-deps/action.yml`. Це забезпечує можливість для workflow з правил `ga`, `js` або `text` викликати цей action для налаштування залежностей Bun одразу після виконання `actions/checkout@v6`.
|
|
14
14
|
|
|
15
15
|
## Поведінка
|
|
16
16
|
|
|
17
17
|
1. Перевіряє наявність шаблону composite action у корені встановленого пакету `@nitra/cursor`.
|
|
18
|
-
2. Створює необхідну директорію
|
|
19
|
-
3. Зчитує вміст шаблону composite action
|
|
20
|
-
4. Записує вміст шаблону у цільовий шлях
|
|
18
|
+
2. Створює необхідну директорію у корені цільового репозиторію для розміщення composite action.
|
|
19
|
+
3. Зчитує вміст шаблону composite action з кореня встановленого пакету.
|
|
20
|
+
4. Записує вміст шаблону composite action у цільовий шлях у корені репозиторію.
|
|
21
21
|
5. Повертає підтвердження успішного запису та повний шлях до файлу.
|
|
22
|
+
6. Не перевіряє шляхи `.github` чи `.git`.
|
|
22
23
|
|
|
23
24
|
## Публічний API
|
|
24
25
|
|
|
25
|
-
syncSetupBunDepsAction — фіксує
|
|
26
|
+
syncSetupBunDepsAction — фіксує у `projectRoot` композитну дію, що вказує на корінь встановленого `@nitra/cursor`.
|
|
26
27
|
|
|
27
28
|
## Гарантії поведінки
|
|
28
29
|
|
|
@@ -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:
|
|
6
|
+
crc: b659349c
|
|
7
|
+
model: omlx/gemma-4-e4b-it-OptiQ-4bit
|
|
8
|
+
score: 100
|
|
7
9
|
---
|
|
8
10
|
|
|
9
|
-
|
|
11
|
+
## Огляд
|
|
10
12
|
|
|
11
|
-
|
|
13
|
+
Цей модуль реалізує механізми вбудовування контенту в текстові документи, використовуючи конфігурації з `package.json.snippet.json` та `package.json`. Він замінює посилання в тексті на вміст файлів-шаблонів, розташованих у каталозі правил, а також на вміст файлів з розширенням .mdc, які не є шаблонами. Функції дозволяють вставляти посилання на шаблони (`inlineTemplateLinks`) та включати вміст Markdown (`inlineMarkdownIncludes`).
|
|
12
14
|
|
|
13
|
-
|
|
15
|
+
## Поведінка
|
|
14
16
|
|
|
15
|
-
|
|
16
|
-
|
|
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
|
-
|
|
20
|
+
## Публічний API
|
|
21
21
|
|
|
22
|
-
|
|
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
|
-
|
|
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,31 +3,31 @@ type: JS Module
|
|
|
3
3
|
title: mirror-parity.mjs
|
|
4
4
|
resource: npm/scripts/lib/mirror-parity.mjs
|
|
5
5
|
docgen:
|
|
6
|
-
crc:
|
|
6
|
+
crc: 5e366df1
|
|
7
7
|
model: omlx/gemma-4-e4b-it-OptiQ-4bit
|
|
8
|
-
score:
|
|
8
|
+
score: 90
|
|
9
9
|
---
|
|
10
10
|
|
|
11
11
|
## Огляд
|
|
12
12
|
|
|
13
|
-
Модуль
|
|
13
|
+
Модуль порівнює вміст дзеркал правил (`.cursor/rules/n-<id>.mdc`) з канонічними версіями (`npm/rules/<id>/<id>.mdc`), які містять застосовані трансформації (трансформ, що застосовує `readBundledRuleContent` $\to$ `inlineTemplateLinks`). Він визначає список керованих дзеркал, формує очікуваний вміст для кожного дзеркала та виявляє дрейф — відхилення фактичного вмісту від очікуваного.
|
|
14
14
|
|
|
15
15
|
## Поведінка
|
|
16
16
|
|
|
17
17
|
listManagedMirrors
|
|
18
|
-
Визначає список керованих дзеркал правил, які мають канонічне джерело у `npm/rules`.
|
|
18
|
+
Визначає список керованих дзеркал правил, які мають відповідне канонічне джерело у `npm/rules`.
|
|
19
19
|
|
|
20
20
|
expectedMirrorContent
|
|
21
|
-
Формує очікуваний вміст дзеркала, застосовуючи
|
|
21
|
+
Формує очікуваний вміст дзеркала, застосовуючи трансформації до канонічного файлу.
|
|
22
22
|
|
|
23
23
|
findMirrorDrift
|
|
24
|
-
Виявляє
|
|
24
|
+
Виявляє ідентифікатори дзеркал, чий фактичний вміст відрізняється від очікуваного вмісту, отриманого з канону.
|
|
25
25
|
|
|
26
26
|
## Публічний API
|
|
27
27
|
|
|
28
|
-
listManagedMirrors — перераховує дзеркала, які мають визначене
|
|
29
|
-
expectedMirrorContent —
|
|
30
|
-
findMirrorDrift —
|
|
28
|
+
listManagedMirrors — перераховує керовані дзеркала, які мають визначене канонічне джерело.
|
|
29
|
+
expectedMirrorContent — визначає бажаний вміст дзеркала, використовуючи канон із вбудованими шаблонами.
|
|
30
|
+
findMirrorDrift — знаходить ідентифікатори дзеркал, у яких фактичний вміст відрізняється від очікуваного.
|
|
31
31
|
|
|
32
32
|
## Гарантії поведінки
|
|
33
33
|
|
|
@@ -5,22 +5,22 @@ resource: npm/scripts/lib/timing-summary.mjs
|
|
|
5
5
|
docgen:
|
|
6
6
|
crc: 47660e16
|
|
7
7
|
model: omlx/gemma-4-e4b-it-OptiQ-4bit
|
|
8
|
-
score:
|
|
8
|
+
score: 95
|
|
9
9
|
---
|
|
10
10
|
|
|
11
11
|
## Огляд
|
|
12
12
|
|
|
13
|
-
|
|
13
|
+
Форматує тривалість у форматі `<ціла>.<десята>s`. Генерує таблицю-резюме часу виконання для оркестратора `fix` або `lint`, яка включає детальні записи та загальний час прогону. Таблиця містить маркер `❌` для позначення невдач. Функція повертає готовий рядок із фінальним `\n`; друк здійснюється на стороні виклику.
|
|
14
14
|
|
|
15
15
|
## Поведінка
|
|
16
16
|
|
|
17
|
-
formatDurationMs форматує тривалість у мілісекундах
|
|
18
|
-
formatTimingSummary генерує багаторядковий
|
|
17
|
+
formatDurationMs форматує тривалість у мілісекундах у рядок у форматі `<ціла>.<десята>s`.
|
|
18
|
+
formatTimingSummary генерує багаторядковий рядок із таблицею-резюме часу виконання, включаючи окремі записи та загальний час.
|
|
19
19
|
|
|
20
20
|
## Публічний API
|
|
21
21
|
|
|
22
|
-
⏱ formatDurationMs: Перетворює мілісекунди у формат `<sec>.<десята>s`, використовуючи округлення
|
|
23
|
-
⏱ formatTimingSummary: Генерує багаторядковий
|
|
22
|
+
⏱ formatDurationMs: Перетворює мілісекунди у формат `<sec>.<десята>s`, використовуючи нижнє округлення для забезпечення консистентності виводу.
|
|
23
|
+
⏱ formatTimingSummary: Генерує багаторядковий текстовий звіт про час виконання, структурований як таблиця з сумарними даними.
|
|
24
24
|
|
|
25
25
|
## Гарантії поведінки
|
|
26
26
|
|
|
@@ -4,6 +4,7 @@ import { basename, extname, join } from 'node:path'
|
|
|
4
4
|
|
|
5
5
|
const MD_LINK_RE = /\[([^\]]{1,200})\]\((\.\/[^)]{1,500})\)/g
|
|
6
6
|
const TEMPLATE_SEGMENT_RE = /\/templates?\//
|
|
7
|
+
const MDC_EXT_RE = /\.mdc$/
|
|
7
8
|
/** Статичні regexp-літерали `^(.+)\.<slot>\.<ext>$` — без `RegExp(variable)`. */
|
|
8
9
|
const SLOT_SUFFIX_RES = [/^(.+)\.snippet\.[^.]+$/, /^(.+)\.deny\.[^.]+$/, /^(.+)\.contains\.[^.]+$/]
|
|
9
10
|
|
|
@@ -66,3 +67,33 @@ export async function inlineTemplateLinks(text, ruleDir) {
|
|
|
66
67
|
|
|
67
68
|
return result
|
|
68
69
|
}
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Finds markdown links whose href ends with `.mdc` (and is not a /template/ path) and
|
|
73
|
+
* replaces them with the raw markdown content of the linked file (no fencing).
|
|
74
|
+
* Intended for per-concern section files living alongside their .mjs implementations.
|
|
75
|
+
* Throws Error if a matched link target doesn't exist (fail loud).
|
|
76
|
+
* @param {string} text .mdc file contents (after inlineTemplateLinks)
|
|
77
|
+
* @param {string} ruleDir absolute path to the rule directory
|
|
78
|
+
* @returns {Promise<string>} transformed text
|
|
79
|
+
*/
|
|
80
|
+
export async function inlineMarkdownIncludes(text, ruleDir) {
|
|
81
|
+
const matches = [...text.matchAll(MD_LINK_RE)].filter(m => MDC_EXT_RE.test(m[2]) && !TEMPLATE_SEGMENT_RE.test(m[2]))
|
|
82
|
+
if (matches.length === 0) return text
|
|
83
|
+
|
|
84
|
+
let result = text
|
|
85
|
+
for (const match of matches) {
|
|
86
|
+
const [fullMatch, , href] = match
|
|
87
|
+
const relPath = href.slice(2) // strip leading ./
|
|
88
|
+
const absPath = join(ruleDir, relPath)
|
|
89
|
+
|
|
90
|
+
if (!existsSync(absPath)) {
|
|
91
|
+
throw new Error(`inlineMarkdownIncludes: file not found: ${absPath} (referenced from .mdc)`)
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
const raw = await readFile(absPath, 'utf8')
|
|
95
|
+
result = result.replace(fullMatch, () => raw.trim())
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
return result
|
|
99
|
+
}
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
import { existsSync, readdirSync, readFileSync } from 'node:fs'
|
|
10
10
|
import { dirname, join } from 'node:path'
|
|
11
11
|
|
|
12
|
-
import { inlineTemplateLinks } from './inline-template-links.mjs'
|
|
12
|
+
import { inlineMarkdownIncludes, inlineTemplateLinks } from './inline-template-links.mjs'
|
|
13
13
|
|
|
14
14
|
const MIRROR_PREFIX = 'n-'
|
|
15
15
|
const MDC_EXT = '.mdc'
|
|
@@ -41,8 +41,10 @@ export function listManagedMirrors(repoRoot) {
|
|
|
41
41
|
* @param {string} canonicalPath абсолютний шлях `npm/rules/<id>/<id>.mdc`
|
|
42
42
|
* @returns {Promise<string>} очікуваний текст дзеркала
|
|
43
43
|
*/
|
|
44
|
-
export function expectedMirrorContent(canonicalPath) {
|
|
45
|
-
|
|
44
|
+
export async function expectedMirrorContent(canonicalPath) {
|
|
45
|
+
const dir = dirname(canonicalPath)
|
|
46
|
+
const withTemplates = await inlineTemplateLinks(readFileSync(canonicalPath, 'utf8'), dir)
|
|
47
|
+
return inlineMarkdownIncludes(withTemplates, dir)
|
|
46
48
|
}
|
|
47
49
|
|
|
48
50
|
/**
|
|
@@ -10,17 +10,17 @@ docgen:
|
|
|
10
10
|
|
|
11
11
|
## Огляд
|
|
12
12
|
|
|
13
|
-
Визначає корінь JS-коду
|
|
13
|
+
Визначає корінь JS-коду в проєкті, відповідно до логіки для workspace-projects (перший workspace з підтримкою glob-патернів `cf/*`) або single-package (корінь cwd). Ця утиліта є спільною для coverage-провайдера JS та test-концерну `stryker_config` (DRY). Публічні функції дозволяють отримати один або повний список шляхів до всіх JS-коренів, при цьому свідомо виключаються каталоги `.git` та `node_modules`. Код спирається на конфігураційні файли `package.json` та `.n-cursor.json`.
|
|
14
14
|
|
|
15
15
|
## Поведінка
|
|
16
16
|
|
|
17
|
-
resolveJsRoot повертає абсолютний шлях до першого JS-кореня
|
|
18
|
-
resolveAllJsRoots повертає масив абсолютних шляхів до всіх JS-коренів
|
|
17
|
+
resolveJsRoot повертає абсолютний шлях до першого JS-кореня проєкту. Якщо кореневий `package.json` відсутній, повертає null.
|
|
18
|
+
resolveAllJsRoots повертає масив абсолютних шляхів до всіх JS-коренів проєкту. Ігнорує каталоги `.git` та `node_modules`.
|
|
19
19
|
|
|
20
20
|
## Публічний API
|
|
21
21
|
|
|
22
22
|
resolveJsRoot — знаходить кореневий каталог JavaScript-проєкту.
|
|
23
|
-
resolveAllJsRoots — повертає
|
|
23
|
+
resolveAllJsRoots — повертає всі каталоги JavaScript-проєктів у робочому просторі.
|
|
24
24
|
|
|
25
25
|
## Гарантії поведінки
|
|
26
26
|
|
package/types/bin/n-cursor.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
export {}
|
|
2
|
+
export {};
|