@llm-translate/cli 1.0.0-next.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.
Files changed (157) hide show
  1. package/.dockerignore +51 -0
  2. package/.env.example +33 -0
  3. package/.github/workflows/docs-pages.yml +57 -0
  4. package/.github/workflows/release.yml +49 -0
  5. package/.translaterc.json +44 -0
  6. package/CLAUDE.md +243 -0
  7. package/Dockerfile +55 -0
  8. package/README.md +371 -0
  9. package/RFC.md +1595 -0
  10. package/dist/cli/index.d.ts +2 -0
  11. package/dist/cli/index.js +4494 -0
  12. package/dist/cli/index.js.map +1 -0
  13. package/dist/index.d.ts +1152 -0
  14. package/dist/index.js +3841 -0
  15. package/dist/index.js.map +1 -0
  16. package/docker-compose.yml +56 -0
  17. package/docs/.vitepress/config.ts +161 -0
  18. package/docs/api/agent.md +262 -0
  19. package/docs/api/engine.md +274 -0
  20. package/docs/api/index.md +171 -0
  21. package/docs/api/providers.md +304 -0
  22. package/docs/changelog.md +64 -0
  23. package/docs/cli/dir.md +243 -0
  24. package/docs/cli/file.md +213 -0
  25. package/docs/cli/glossary.md +273 -0
  26. package/docs/cli/index.md +129 -0
  27. package/docs/cli/init.md +158 -0
  28. package/docs/cli/serve.md +211 -0
  29. package/docs/glossary.json +235 -0
  30. package/docs/guide/chunking.md +272 -0
  31. package/docs/guide/configuration.md +139 -0
  32. package/docs/guide/cost-optimization.md +237 -0
  33. package/docs/guide/docker.md +371 -0
  34. package/docs/guide/getting-started.md +150 -0
  35. package/docs/guide/glossary.md +241 -0
  36. package/docs/guide/index.md +86 -0
  37. package/docs/guide/ollama.md +515 -0
  38. package/docs/guide/prompt-caching.md +221 -0
  39. package/docs/guide/providers.md +232 -0
  40. package/docs/guide/quality-control.md +206 -0
  41. package/docs/guide/vitepress-integration.md +265 -0
  42. package/docs/index.md +63 -0
  43. package/docs/ja/api/agent.md +262 -0
  44. package/docs/ja/api/engine.md +274 -0
  45. package/docs/ja/api/index.md +171 -0
  46. package/docs/ja/api/providers.md +304 -0
  47. package/docs/ja/changelog.md +64 -0
  48. package/docs/ja/cli/dir.md +243 -0
  49. package/docs/ja/cli/file.md +213 -0
  50. package/docs/ja/cli/glossary.md +273 -0
  51. package/docs/ja/cli/index.md +111 -0
  52. package/docs/ja/cli/init.md +158 -0
  53. package/docs/ja/guide/chunking.md +271 -0
  54. package/docs/ja/guide/configuration.md +139 -0
  55. package/docs/ja/guide/cost-optimization.md +30 -0
  56. package/docs/ja/guide/getting-started.md +150 -0
  57. package/docs/ja/guide/glossary.md +214 -0
  58. package/docs/ja/guide/index.md +32 -0
  59. package/docs/ja/guide/ollama.md +410 -0
  60. package/docs/ja/guide/prompt-caching.md +221 -0
  61. package/docs/ja/guide/providers.md +232 -0
  62. package/docs/ja/guide/quality-control.md +137 -0
  63. package/docs/ja/guide/vitepress-integration.md +265 -0
  64. package/docs/ja/index.md +58 -0
  65. package/docs/ko/api/agent.md +262 -0
  66. package/docs/ko/api/engine.md +274 -0
  67. package/docs/ko/api/index.md +171 -0
  68. package/docs/ko/api/providers.md +304 -0
  69. package/docs/ko/changelog.md +64 -0
  70. package/docs/ko/cli/dir.md +243 -0
  71. package/docs/ko/cli/file.md +213 -0
  72. package/docs/ko/cli/glossary.md +273 -0
  73. package/docs/ko/cli/index.md +111 -0
  74. package/docs/ko/cli/init.md +158 -0
  75. package/docs/ko/guide/chunking.md +271 -0
  76. package/docs/ko/guide/configuration.md +139 -0
  77. package/docs/ko/guide/cost-optimization.md +30 -0
  78. package/docs/ko/guide/getting-started.md +150 -0
  79. package/docs/ko/guide/glossary.md +214 -0
  80. package/docs/ko/guide/index.md +32 -0
  81. package/docs/ko/guide/ollama.md +410 -0
  82. package/docs/ko/guide/prompt-caching.md +221 -0
  83. package/docs/ko/guide/providers.md +232 -0
  84. package/docs/ko/guide/quality-control.md +137 -0
  85. package/docs/ko/guide/vitepress-integration.md +265 -0
  86. package/docs/ko/index.md +58 -0
  87. package/docs/zh/api/agent.md +262 -0
  88. package/docs/zh/api/engine.md +274 -0
  89. package/docs/zh/api/index.md +171 -0
  90. package/docs/zh/api/providers.md +304 -0
  91. package/docs/zh/changelog.md +64 -0
  92. package/docs/zh/cli/dir.md +243 -0
  93. package/docs/zh/cli/file.md +213 -0
  94. package/docs/zh/cli/glossary.md +273 -0
  95. package/docs/zh/cli/index.md +111 -0
  96. package/docs/zh/cli/init.md +158 -0
  97. package/docs/zh/guide/chunking.md +271 -0
  98. package/docs/zh/guide/configuration.md +139 -0
  99. package/docs/zh/guide/cost-optimization.md +30 -0
  100. package/docs/zh/guide/getting-started.md +150 -0
  101. package/docs/zh/guide/glossary.md +214 -0
  102. package/docs/zh/guide/index.md +32 -0
  103. package/docs/zh/guide/ollama.md +410 -0
  104. package/docs/zh/guide/prompt-caching.md +221 -0
  105. package/docs/zh/guide/providers.md +232 -0
  106. package/docs/zh/guide/quality-control.md +137 -0
  107. package/docs/zh/guide/vitepress-integration.md +265 -0
  108. package/docs/zh/index.md +58 -0
  109. package/package.json +91 -0
  110. package/release.config.mjs +15 -0
  111. package/schemas/glossary.schema.json +110 -0
  112. package/src/cli/commands/dir.ts +469 -0
  113. package/src/cli/commands/file.ts +291 -0
  114. package/src/cli/commands/glossary.ts +221 -0
  115. package/src/cli/commands/init.ts +68 -0
  116. package/src/cli/commands/serve.ts +60 -0
  117. package/src/cli/index.ts +64 -0
  118. package/src/cli/options.ts +59 -0
  119. package/src/core/agent.ts +1119 -0
  120. package/src/core/chunker.ts +391 -0
  121. package/src/core/engine.ts +634 -0
  122. package/src/errors.ts +188 -0
  123. package/src/index.ts +147 -0
  124. package/src/integrations/vitepress.ts +549 -0
  125. package/src/parsers/markdown.ts +383 -0
  126. package/src/providers/claude.ts +259 -0
  127. package/src/providers/interface.ts +109 -0
  128. package/src/providers/ollama.ts +379 -0
  129. package/src/providers/openai.ts +308 -0
  130. package/src/providers/registry.ts +153 -0
  131. package/src/server/index.ts +152 -0
  132. package/src/server/middleware/auth.ts +93 -0
  133. package/src/server/middleware/logger.ts +90 -0
  134. package/src/server/routes/health.ts +84 -0
  135. package/src/server/routes/translate.ts +210 -0
  136. package/src/server/types.ts +138 -0
  137. package/src/services/cache.ts +899 -0
  138. package/src/services/config.ts +217 -0
  139. package/src/services/glossary.ts +247 -0
  140. package/src/types/analysis.ts +164 -0
  141. package/src/types/index.ts +265 -0
  142. package/src/types/modes.ts +121 -0
  143. package/src/types/mqm.ts +157 -0
  144. package/src/utils/logger.ts +141 -0
  145. package/src/utils/tokens.ts +116 -0
  146. package/tests/fixtures/glossaries/ml-glossary.json +53 -0
  147. package/tests/fixtures/input/lynq-installation.ko.md +350 -0
  148. package/tests/fixtures/input/lynq-installation.md +350 -0
  149. package/tests/fixtures/input/simple.ko.md +27 -0
  150. package/tests/fixtures/input/simple.md +27 -0
  151. package/tests/unit/chunker.test.ts +229 -0
  152. package/tests/unit/glossary.test.ts +146 -0
  153. package/tests/unit/markdown.test.ts +205 -0
  154. package/tests/unit/tokens.test.ts +81 -0
  155. package/tsconfig.json +28 -0
  156. package/tsup.config.ts +34 -0
  157. package/vitest.config.ts +16 -0
@@ -0,0 +1,350 @@
1
+ # Installation Guide
2
+
3
+ This guide covers various installation methods for Lynq.
4
+
5
+ [[toc]]
6
+
7
+ ::: tip Trying it locally?
8
+ Use the [Quick Start with Minikube](quickstart.md) guide for an automated setup tailored to first-time users.
9
+ :::
10
+
11
+ ## Prerequisites
12
+
13
+ ### Required
14
+
15
+ | Component | Minimum version | Notes |
16
+ | --- | --- | --- |
17
+ | Kubernetes cluster | v1.11.3+ | API compatibility tested with recent releases |
18
+ | `kubectl` | Matches cluster | Must target the cluster where you deploy |
19
+ | **cert-manager** | **v1.13.0+** | **REQUIRED for all installations** (production, development, local) |
20
+
21
+ ::: danger cert-manager is REQUIRED
22
+ **cert-manager v1.13.0+** is **REQUIRED for ALL installations** (production, development, and local environments). It provisions webhook TLS certificates, handles automatic renewal, and injects CA bundles into webhook configurations.
23
+
24
+ **Webhooks are no longer optional.** They provide essential validation and defaulting at admission time.
25
+
26
+ Install before deploying Lynq:
27
+ ```bash
28
+ kubectl apply -f https://github.com/cert-manager/cert-manager/releases/download/v1.13.0/cert-manager.yaml
29
+ ```
30
+ :::
31
+
32
+ ### Optional
33
+
34
+ - **MySQL database** for node data source (PostgreSQL support planned for v1.2)
35
+
36
+ ## Kubernetes Compatibility
37
+
38
+ ### Supported Versions
39
+
40
+ The operator relies only on GA/stable Kubernetes APIs and controller-runtime patterns, making it compatible across the supported upstream version skew.
41
+
42
+ **Validated versions** (end-to-end tested and production-verified):
43
+
44
+ | Kubernetes Version | Status |
45
+ |--------------------|--------|
46
+ | v1.28 | ✅ Validated |
47
+ | v1.29 | ✅ Validated |
48
+ | v1.30 | ✅ Validated |
49
+ | v1.31 | ✅ Validated |
50
+ | v1.32 | ✅ Validated |
51
+ | v1.33 | ✅ Validated |
52
+ | Other GA releases | ⚠️ Expected to work |
53
+
54
+ ::: tip Compatibility Philosophy
55
+ The operator is designed to work across Kubernetes version skew. Earlier or newer versions are expected to function, but validate in a staging environment before rolling out broadly.
56
+ :::
57
+
58
+ ## Installation Methods
59
+
60
+ ### Method 1: Install with Helm (Recommended)
61
+
62
+ **cert-manager is REQUIRED** for all installations.
63
+
64
+ ```bash
65
+ # Step 1: Install cert-manager (REQUIRED)
66
+ kubectl apply -f https://github.com/cert-manager/cert-manager/releases/download/v1.13.0/cert-manager.yaml
67
+
68
+ # Step 2: Wait for cert-manager to be ready
69
+ kubectl wait --for=condition=Available --timeout=300s -n cert-manager \
70
+ deployment/cert-manager \
71
+ deployment/cert-manager-webhook \
72
+ deployment/cert-manager-cainjector
73
+
74
+ # Step 3: Add Helm repository
75
+ helm repo add lynq https://k8s-lynq.github.io/lynq
76
+ helm repo update
77
+
78
+ # Step 4: Install Lynq
79
+ helm install lynq lynq/lynq \
80
+ --namespace lynq-system \
81
+ --create-namespace
82
+ ```
83
+
84
+ See the [Helm Chart README](https://github.com/k8s-lynq/lynq/blob/main/chart/README.md) for detailed configuration options.
85
+
86
+ ---
87
+
88
+ ### Method 2: Install with Kustomize
89
+
90
+ **cert-manager is REQUIRED** for webhook TLS certificate management.
91
+
92
+ ```bash
93
+ # Step 1: Install cert-manager (if not already installed)
94
+ kubectl apply -f https://github.com/cert-manager/cert-manager/releases/download/v1.13.0/cert-manager.yaml
95
+
96
+ # Step 2: Wait for cert-manager to be ready
97
+ kubectl wait --for=condition=Available --timeout=300s -n cert-manager deployment/cert-manager
98
+ kubectl wait --for=condition=Available --timeout=300s -n cert-manager deployment/cert-manager-webhook
99
+
100
+ # Step 3: Install Lynq
101
+ # cert-manager will automatically issue and manage webhook TLS certificates
102
+ kubectl apply -k https://github.com/k8s-lynq/lynq/config/default
103
+ ```
104
+
105
+ ::: info What cert-manager handles
106
+ - Issues TLS certificates for the webhook server
107
+ - Renews certificates before expiration
108
+ - Injects the CA bundle into webhook configurations
109
+ - Provides battle-tested certificate automation for Kubernetes clusters
110
+ :::
111
+
112
+ ### Method 3: Install from Source
113
+
114
+ ```bash
115
+ # Clone repository
116
+ git clone https://github.com/k8s-lynq/lynq.git
117
+ cd lynq
118
+
119
+ # Install CRDs
120
+ make install
121
+
122
+ # Install cert-manager first if not already installed
123
+ kubectl apply -f https://github.com/cert-manager/cert-manager/releases/download/v1.13.0/cert-manager.yaml
124
+
125
+ # Deploy operator
126
+ make deploy IMG=ghcr.io/k8s-lynq/lynq:latest
127
+ ```
128
+
129
+ ::: warning Remember TLS
130
+ Even when deploying from source, install cert-manager before applying the operator manifests, otherwise webhooks will fail to start.
131
+ :::
132
+
133
+ ### Method 4: Local Development with Minikube
134
+
135
+ For local development, use Minikube with automated setup scripts. **cert-manager is automatically installed** by the setup script.
136
+
137
+ See [Local Development with Minikube](local-development-minikube.md) for detailed instructions.
138
+
139
+ ```bash
140
+ # Quick setup (cert-manager included)
141
+ ./scripts/setup-minikube.sh # Create cluster with cert-manager
142
+ ./scripts/deploy-to-minikube.sh # Build and deploy operator
143
+ ```
144
+
145
+ ::: tip cert-manager in Local Development
146
+ The setup script automatically installs cert-manager. You don't need to install it manually for local development when using the provided scripts.
147
+ :::
148
+
149
+ ## Verification
150
+
151
+ Check that the operator is running:
152
+
153
+ ```bash
154
+ # Check operator deployment
155
+ kubectl get deployment -n lynq-system lynq-controller-manager
156
+
157
+ # Check operator logs
158
+ kubectl logs -n lynq-system deployment/lynq-controller-manager -f
159
+
160
+ # Verify CRDs are installed
161
+ kubectl get crd | grep operator.lynq.sh
162
+ ```
163
+
164
+ Expected output:
165
+ ```
166
+ lynqhubs.operator.lynq.sh 2025-01-15T10:00:00Z
167
+ lynqnodes.operator.lynq.sh 2025-01-15T10:00:00Z
168
+ lynqforms.operator.lynq.sh 2025-01-15T10:00:00Z
169
+ ```
170
+
171
+ ::: tip Troubleshooting
172
+ If the deployment is not ready, inspect `kubectl describe deployment/lynq-controller-manager` for webhook, RBAC, or image issues.
173
+ :::
174
+
175
+ ## Configuration Options
176
+
177
+ ### Webhook TLS Configuration
178
+
179
+ Webhook TLS is managed automatically by cert-manager. The default configuration includes:
180
+
181
+ ```yaml
182
+ # config/default/kustomization.yaml
183
+ # Webhook patches are enabled by default
184
+ patches:
185
+ - path: manager_webhook_patch.yaml
186
+ - path: webhookcainjection_patch.yaml
187
+ ```
188
+
189
+ ::: info cert-manager responsibilities
190
+ - Issue TLS certificates for the webhook server
191
+ - Inject CA bundles into webhook configurations
192
+ - Renew certificates before expiration
193
+ :::
194
+
195
+ ### Resource Limits
196
+
197
+ Adjust operator resource limits based on your cluster size:
198
+
199
+ ```yaml
200
+ # config/manager/manager.yaml
201
+ resources:
202
+ limits:
203
+ cpu: 500m # Increase for large clusters
204
+ memory: 512Mi # Increase for many nodes
205
+ requests:
206
+ cpu: 100m
207
+ memory: 128Mi
208
+ ```
209
+
210
+ ### Concurrency Settings
211
+
212
+ Configure concurrent reconciliation workers:
213
+
214
+ ```yaml
215
+ spec:
216
+ template:
217
+ spec:
218
+ containers:
219
+ - name: manager
220
+ args:
221
+ - --node-concurrency=10 # Concurrent LynqNode reconciliations (default: 10)
222
+ - --form-concurrency=5 # Concurrent Template reconciliations (default: 5)
223
+ - --hub-concurrency=3 # Concurrent Hub syncs (default: 3)
224
+ - --leader-elect # Enable leader election
225
+ ```
226
+
227
+ ## Multi-Platform Support
228
+
229
+ The operator supports multiple architectures:
230
+
231
+ - `linux/amd64` (Intel/AMD 64-bit)
232
+ - `linux/arm64` (ARM 64-bit, Apple Silicon)
233
+
234
+ Container images are automatically pulled for your platform.
235
+
236
+ ## Namespace Isolation
237
+
238
+ By default, the operator is installed in `lynq-system` namespace:
239
+
240
+ ```bash
241
+ # Check operator namespace
242
+ kubectl get all -n lynq-system
243
+
244
+ # View RBAC
245
+ kubectl get clusterrole | grep lynq
246
+ kubectl get clusterrolebinding | grep lynq
247
+ ```
248
+
249
+ ## Upgrading
250
+
251
+ ### Upgrade CRDs First
252
+
253
+ ```bash
254
+ # Upgrade CRDs (safe, preserves existing data)
255
+ make install
256
+
257
+ # Or with kubectl
258
+ kubectl apply -f config/crd/bases/
259
+ ```
260
+
261
+ ### Upgrade Operator
262
+
263
+ ```bash
264
+ # Update operator deployment
265
+ kubectl set image -n lynq-system \
266
+ deployment/lynq-controller-manager \
267
+ manager=ghcr.io/k8s-lynq/lynq:v1.1.0
268
+
269
+ # Or use make
270
+ make deploy IMG=ghcr.io/k8s-lynq/lynq:v1.1.0
271
+ ```
272
+
273
+ ### Rolling Back
274
+
275
+ ```bash
276
+ # Rollback to previous version
277
+ kubectl rollout undo -n lynq-system \
278
+ deployment/lynq-controller-manager
279
+
280
+ # Check rollout status
281
+ kubectl rollout status -n lynq-system \
282
+ deployment/lynq-controller-manager
283
+ ```
284
+
285
+ ## Uninstallation
286
+
287
+ ```bash
288
+ # Delete operator deployment
289
+ kubectl delete -k config/default
290
+
291
+ # Or with make
292
+ make undeploy
293
+
294
+ # Delete CRDs (WARNING: This deletes all LynqNode data!)
295
+ make uninstall
296
+
297
+ # Or with kubectl
298
+ kubectl delete crd lynqhubs.operator.lynq.sh
299
+ kubectl delete crd lynqforms.operator.lynq.sh
300
+ kubectl delete crd lynqnodes.operator.lynq.sh
301
+ ```
302
+
303
+ **Warning:** Deleting CRDs will delete all LynqHub, LynqForm, and LynqNode resources. Ensure you have backups if needed.
304
+
305
+ ## Troubleshooting Installation
306
+
307
+ ### Webhook TLS Errors
308
+
309
+ **Error:** `open /tmp/k8s-webhook-server/serving-certs/tls.crt: no such file or directory`
310
+
311
+ **Solution:** Install cert-manager to automatically manage webhook TLS certificates.
312
+
313
+ ```bash
314
+ # Install cert-manager
315
+ kubectl apply -f https://github.com/cert-manager/cert-manager/releases/download/v1.13.0/cert-manager.yaml
316
+
317
+ # Wait for cert-manager to be ready
318
+ kubectl wait --for=condition=Available --timeout=300s -n cert-manager deployment/cert-manager
319
+
320
+ # Restart operator to pick up certificates
321
+ kubectl rollout restart -n lynq-system deployment/lynq-controller-manager
322
+ ```
323
+
324
+ ### CRD Already Exists
325
+
326
+ **Error:** `Error from server (AlreadyExists): customresourcedefinitions.apiextensions.k8s.io "lynqnodes.operator.lynq.sh" already exists`
327
+
328
+ **Solution:** This is normal during upgrades. CRD updates are applied automatically.
329
+
330
+ ### Image Pull Errors
331
+
332
+ **Error:** `Failed to pull image "ghcr.io/k8s-lynq/lynq:latest"`
333
+
334
+ **Solution:** Ensure your cluster can access GitHub Container Registry (ghcr.io). Check network policies and image pull secrets if needed.
335
+
336
+ ### Permission Denied
337
+
338
+ **Error:** `Error from server (Forbidden): User "system:serviceaccount:lynq-system:lynq-controller-manager" cannot create resource`
339
+
340
+ **Solution:** Ensure RBAC resources are installed:
341
+ ```bash
342
+ kubectl apply -f config/rbac/
343
+ ```
344
+
345
+ ## Next Steps
346
+
347
+ - [Create your first LynqHub](quickstart.md#step-4-deploy-lynqhub)
348
+ - [Learn about Templates](templates.md)
349
+ - [Configure Monitoring](monitoring.md)
350
+ - [Set up Security](security.md)
@@ -0,0 +1,27 @@
1
+ # 머신러닝 시작하기
2
+
3
+ 이 가이드는 인공지능과 딥러닝의 기초를 소개합니다.
4
+
5
+ ## 사전 요구사항
6
+
7
+ - Python 3.8+
8
+ - OpenAI의 API 키
9
+
10
+ ## 설치
11
+
12
+ ```bash
13
+ pip install tensorflow
14
+ ```
15
+
16
+ 신경망 모델을 생성하세요:
17
+
18
+ ```python
19
+ model = Sequential([
20
+ Dense(128, activation='relu'),
21
+ Dense(10, activation='softmax')
22
+ ])
23
+ ```
24
+
25
+ ## 다음 단계
26
+
27
+ 딥러닝 기법과 SDK 사용 방법에 대해 더 자세히 알아보세요.
@@ -0,0 +1,27 @@
1
+ # Getting Started with Machine Learning
2
+
3
+ This guide introduces the basics of artificial intelligence and deep learning.
4
+
5
+ ## Prerequisites
6
+
7
+ - Python 3.8+
8
+ - An API key from OpenAI
9
+
10
+ ## Installation
11
+
12
+ ```bash
13
+ pip install tensorflow
14
+ ```
15
+
16
+ Create a neural network model:
17
+
18
+ ```python
19
+ model = Sequential([
20
+ Dense(128, activation='relu'),
21
+ Dense(10, activation='softmax')
22
+ ])
23
+ ```
24
+
25
+ ## Next Steps
26
+
27
+ Learn more about deep learning techniques and how to use the SDK.
@@ -0,0 +1,229 @@
1
+ import { describe, it, expect } from "vitest";
2
+ import {
3
+ chunkContent,
4
+ reassembleChunks,
5
+ getChunkStats,
6
+ } from "../../src/core/chunker.js";
7
+
8
+ describe("chunkContent", () => {
9
+ it("should return single chunk for small content", () => {
10
+ const content = "This is a small piece of text.";
11
+ const chunks = chunkContent(content, { maxTokens: 1000 });
12
+
13
+ expect(chunks).toHaveLength(1);
14
+ expect(chunks[0]?.type).toBe("translatable");
15
+ expect(chunks[0]?.content).toBe(content);
16
+ });
17
+
18
+ it("should preserve code blocks as separate chunks", () => {
19
+ const content = `# Title
20
+
21
+ Some text before code.
22
+
23
+ \`\`\`javascript
24
+ const x = 1;
25
+ console.log(x);
26
+ \`\`\`
27
+
28
+ Some text after code.`;
29
+
30
+ const chunks = chunkContent(content, { maxTokens: 1000 });
31
+
32
+ // Should have at least one preserved chunk (code block)
33
+ const preservedChunks = chunks.filter((c) => c.type === "preserve");
34
+ expect(preservedChunks.length).toBeGreaterThanOrEqual(1);
35
+
36
+ // Code block should contain the JS code
37
+ const codeChunk = preservedChunks.find((c) =>
38
+ c.content.includes("const x = 1")
39
+ );
40
+ expect(codeChunk).toBeDefined();
41
+ });
42
+
43
+ it("should split large content into multiple chunks", () => {
44
+ // Create content that exceeds token limit
45
+ const paragraph =
46
+ "This is a paragraph with enough words to make it substantial. ".repeat(
47
+ 50
48
+ );
49
+ const content = `${paragraph}\n\n${paragraph}\n\n${paragraph}`;
50
+
51
+ const chunks = chunkContent(content, { maxTokens: 100 });
52
+
53
+ expect(chunks.length).toBeGreaterThan(1);
54
+ });
55
+
56
+ it("should extract header hierarchy metadata", () => {
57
+ const content = `# Main Title
58
+
59
+ Introduction text.
60
+
61
+ ## Section One
62
+
63
+ Content in section one.
64
+
65
+ ### Subsection 1.1
66
+
67
+ More detailed content.
68
+
69
+ ## Section Two
70
+
71
+ Content in section two.`;
72
+
73
+ const chunks = chunkContent(content, { maxTokens: 1000 });
74
+
75
+ // Find chunk containing "More detailed content"
76
+ const detailChunk = chunks.find((c) =>
77
+ c.content.includes("More detailed content")
78
+ );
79
+ expect(detailChunk?.metadata?.headerHierarchy).toBeDefined();
80
+
81
+ // Should have headers in hierarchy
82
+ if (detailChunk?.metadata?.headerHierarchy) {
83
+ expect(detailChunk.metadata.headerHierarchy.length).toBeGreaterThan(0);
84
+ }
85
+ });
86
+
87
+ it("should include previousContext for subsequent chunks", () => {
88
+ const paragraph =
89
+ "This is the first paragraph with important context. ".repeat(30);
90
+ const content = `${paragraph}\n\nThis is the second paragraph that should have previous context.`;
91
+
92
+ const chunks = chunkContent(content, { maxTokens: 100 });
93
+
94
+ // If there are multiple chunks, later ones should have previousContext
95
+ if (chunks.length > 1) {
96
+ const laterChunk = chunks[chunks.length - 1];
97
+ expect(laterChunk?.metadata?.previousContext).toBeDefined();
98
+ }
99
+ });
100
+
101
+ it("should handle empty content", () => {
102
+ const chunks = chunkContent("", { maxTokens: 1000 });
103
+ expect(chunks).toHaveLength(0);
104
+ });
105
+
106
+ it("should handle content with only code blocks", () => {
107
+ const content = `\`\`\`python
108
+ print("Hello")
109
+ \`\`\``;
110
+
111
+ const chunks = chunkContent(content, { maxTokens: 1000 });
112
+
113
+ expect(chunks).toHaveLength(1);
114
+ expect(chunks[0]?.type).toBe("preserve");
115
+ });
116
+ });
117
+
118
+ describe("reassembleChunks", () => {
119
+ it("should reassemble chunks in correct order", () => {
120
+ const content = "First part. Second part. Third part.";
121
+ const chunks = chunkContent(content, { maxTokens: 1000 });
122
+ const reassembled = reassembleChunks(chunks);
123
+
124
+ expect(reassembled).toBe(content);
125
+ });
126
+
127
+ it("should handle multiple chunks correctly", () => {
128
+ const content = `# Title
129
+
130
+ First paragraph.
131
+
132
+ \`\`\`code
133
+ block
134
+ \`\`\`
135
+
136
+ Second paragraph.`;
137
+
138
+ const chunks = chunkContent(content, { maxTokens: 1000 });
139
+ const reassembled = reassembleChunks(chunks);
140
+
141
+ expect(reassembled).toContain("# Title");
142
+ expect(reassembled).toContain("First paragraph");
143
+ expect(reassembled).toContain("```code");
144
+ expect(reassembled).toContain("Second paragraph");
145
+ });
146
+
147
+ it("should preserve line breaks between code blocks and text", () => {
148
+ const content = `\`\`\`yaml
149
+ key: value
150
+ \`\`\`
151
+
152
+ ::: info Note
153
+ Some info here
154
+ :::`;
155
+
156
+ const chunks = chunkContent(content, { maxTokens: 1000 });
157
+ const reassembled = reassembleChunks(chunks);
158
+
159
+ // Should preserve the empty line between code block and info block
160
+ expect(reassembled).toBe(content);
161
+ expect(reassembled).toContain("```\n\n:::");
162
+ });
163
+
164
+ it("should preserve whitespace-only segments between preserved sections", () => {
165
+ const content = `\`\`\`js
166
+ code1
167
+ \`\`\`
168
+
169
+ \`\`\`js
170
+ code2
171
+ \`\`\``;
172
+
173
+ const chunks = chunkContent(content, { maxTokens: 1000 });
174
+ const reassembled = reassembleChunks(chunks);
175
+
176
+ // Should preserve the empty lines between code blocks
177
+ expect(reassembled).toBe(content);
178
+ });
179
+
180
+ it("should preserve exact line breaks between code block and following text (issue #187)", () => {
181
+ // This is the exact pattern from lynq-installation.md line 187
182
+ const content = `\`\`\`yaml
183
+ patches:
184
+ - path: manager_webhook_patch.yaml
185
+ \`\`\`
186
+
187
+ ::: info cert-manager responsibilities
188
+ - Issue TLS certificates
189
+ :::`;
190
+
191
+ const chunks = chunkContent(content, { maxTokens: 1000 });
192
+ const reassembled = reassembleChunks(chunks);
193
+
194
+ // The reassembled content must be exactly equal to original
195
+ expect(reassembled).toBe(content);
196
+ // Specifically verify the line break between code block and info block
197
+ expect(reassembled).toContain("```\n\n:::");
198
+ expect(reassembled).not.toContain("```:::"); // No missing line break
199
+ });
200
+ });
201
+
202
+ describe("getChunkStats", () => {
203
+ it("should return correct statistics", () => {
204
+ const content = `Some text before.
205
+
206
+ \`\`\`js
207
+ code block
208
+ \`\`\`
209
+
210
+ Some text after.`;
211
+
212
+ const chunks = chunkContent(content, { maxTokens: 1000 });
213
+ const stats = getChunkStats(chunks);
214
+
215
+ expect(stats.totalChunks).toBe(chunks.length);
216
+ expect(stats.preservedChunks).toBeGreaterThanOrEqual(1);
217
+ expect(stats.translatableChunks).toBeGreaterThanOrEqual(1);
218
+ expect(stats.totalTokens).toBeGreaterThan(0);
219
+ });
220
+
221
+ it("should handle empty chunk array", () => {
222
+ const stats = getChunkStats([]);
223
+
224
+ expect(stats.totalChunks).toBe(0);
225
+ expect(stats.preservedChunks).toBe(0);
226
+ expect(stats.translatableChunks).toBe(0);
227
+ expect(stats.averageTokens).toBe(0);
228
+ });
229
+ });