@tstdl/base 0.93.138 → 0.93.140

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 (138) hide show
  1. package/README.md +166 -0
  2. package/ai/genkit/multi-region.plugin.js +5 -3
  3. package/ai/genkit/tests/multi-region.test.d.ts +1 -0
  4. package/ai/genkit/tests/multi-region.test.js +5 -2
  5. package/ai/parser/parser.js +2 -2
  6. package/ai/prompts/build.js +1 -0
  7. package/ai/prompts/instructions-formatter.d.ts +15 -2
  8. package/ai/prompts/instructions-formatter.js +36 -31
  9. package/ai/prompts/prompt-builder.js +5 -5
  10. package/ai/prompts/steering.d.ts +3 -2
  11. package/ai/prompts/steering.js +3 -1
  12. package/ai/tests/instructions-formatter.test.js +1 -0
  13. package/api/README.md +403 -0
  14. package/api/client/client.js +7 -13
  15. package/api/client/tests/api-client.test.js +10 -10
  16. package/api/default-error-handlers.js +1 -1
  17. package/api/response.d.ts +2 -2
  18. package/api/response.js +22 -33
  19. package/api/server/api-controller.d.ts +1 -1
  20. package/api/server/api-controller.js +3 -3
  21. package/api/server/api-request-token.provider.d.ts +1 -0
  22. package/api/server/api-request-token.provider.js +1 -0
  23. package/api/server/middlewares/allowed-methods.middleware.js +2 -1
  24. package/api/server/middlewares/content-type.middleware.js +2 -1
  25. package/api/types.d.ts +3 -2
  26. package/application/README.md +240 -0
  27. package/application/application.js +2 -2
  28. package/audit/README.md +267 -0
  29. package/authentication/README.md +288 -0
  30. package/authentication/client/authentication.service.d.ts +12 -11
  31. package/authentication/client/authentication.service.js +21 -21
  32. package/authentication/client/http-client.middleware.js +2 -2
  33. package/authentication/tests/authentication.client-error-handling.test.js +2 -1
  34. package/authentication/tests/authentication.client-service-refresh.test.js +5 -3
  35. package/browser/README.md +401 -0
  36. package/cancellation/README.md +156 -0
  37. package/cancellation/tests/coverage.test.d.ts +1 -0
  38. package/cancellation/tests/coverage.test.js +49 -0
  39. package/cancellation/tests/leak.test.d.ts +1 -0
  40. package/cancellation/tests/leak.test.js +35 -0
  41. package/cancellation/tests/token.test.d.ts +1 -0
  42. package/cancellation/tests/token.test.js +136 -0
  43. package/cancellation/token.d.ts +53 -177
  44. package/cancellation/token.js +132 -201
  45. package/context/README.md +174 -0
  46. package/cookie/README.md +161 -0
  47. package/css/README.md +157 -0
  48. package/data-structures/README.md +320 -0
  49. package/decorators/README.md +140 -0
  50. package/distributed-loop/README.md +231 -0
  51. package/distributed-loop/distributed-loop.js +1 -1
  52. package/document-management/README.md +403 -0
  53. package/document-management/server/services/document-management.service.js +9 -7
  54. package/document-management/tests/document-management-core.test.js +2 -7
  55. package/document-management/tests/document-management.api.test.js +6 -7
  56. package/document-management/tests/document-statistics.service.test.js +11 -12
  57. package/document-management/tests/document.service.test.js +3 -3
  58. package/document-management/tests/enum-helpers.test.js +2 -3
  59. package/dom/README.md +213 -0
  60. package/enumerable/README.md +259 -0
  61. package/enumeration/README.md +121 -0
  62. package/errors/README.md +267 -0
  63. package/file/README.md +191 -0
  64. package/formats/README.md +210 -0
  65. package/function/README.md +144 -0
  66. package/http/README.md +318 -0
  67. package/http/client/adapters/undici.adapter.js +1 -1
  68. package/http/client/http-client-request.d.ts +6 -5
  69. package/http/client/http-client-request.js +8 -9
  70. package/http/server/node/node-http-server.js +1 -2
  71. package/image-service/README.md +137 -0
  72. package/injector/README.md +491 -0
  73. package/injector/injector.d.ts +1 -0
  74. package/injector/injector.js +17 -5
  75. package/injector/tests/leak.test.d.ts +1 -0
  76. package/injector/tests/leak.test.js +45 -0
  77. package/intl/README.md +113 -0
  78. package/json-path/README.md +182 -0
  79. package/jsx/README.md +154 -0
  80. package/key-value-store/README.md +191 -0
  81. package/lock/README.md +249 -0
  82. package/lock/web/web-lock.js +119 -47
  83. package/logger/README.md +287 -0
  84. package/mail/README.md +256 -0
  85. package/memory/README.md +144 -0
  86. package/message-bus/README.md +244 -0
  87. package/message-bus/message-bus-base.js +1 -1
  88. package/module/README.md +182 -0
  89. package/module/module.d.ts +1 -1
  90. package/module/module.js +77 -17
  91. package/module/modules/web-server.module.js +1 -1
  92. package/notification/tests/notification-type.service.test.js +24 -15
  93. package/object-storage/README.md +300 -0
  94. package/openid-connect/README.md +274 -0
  95. package/orm/README.md +423 -0
  96. package/package.json +8 -6
  97. package/password/README.md +164 -0
  98. package/pdf/README.md +246 -0
  99. package/polyfills.js +1 -0
  100. package/pool/README.md +198 -0
  101. package/process/README.md +237 -0
  102. package/promise/README.md +252 -0
  103. package/promise/cancelable-promise.js +1 -1
  104. package/random/README.md +193 -0
  105. package/reflection/README.md +305 -0
  106. package/rpc/README.md +386 -0
  107. package/rxjs-utils/README.md +262 -0
  108. package/schema/README.md +342 -0
  109. package/serializer/README.md +342 -0
  110. package/signals/implementation/README.md +134 -0
  111. package/sse/README.md +278 -0
  112. package/task-queue/README.md +300 -0
  113. package/task-queue/postgres/task-queue.d.ts +2 -1
  114. package/task-queue/postgres/task-queue.js +32 -2
  115. package/task-queue/task-context.js +1 -1
  116. package/task-queue/task-queue.d.ts +17 -0
  117. package/task-queue/task-queue.js +103 -44
  118. package/task-queue/tests/complex.test.js +4 -4
  119. package/task-queue/tests/dependencies.test.js +4 -2
  120. package/task-queue/tests/queue.test.js +111 -0
  121. package/task-queue/tests/worker.test.js +21 -13
  122. package/templates/README.md +287 -0
  123. package/testing/README.md +157 -0
  124. package/text/README.md +346 -0
  125. package/threading/README.md +238 -0
  126. package/types/README.md +311 -0
  127. package/utils/README.md +322 -0
  128. package/utils/async-iterable-helpers/observable-iterable.d.ts +1 -1
  129. package/utils/async-iterable-helpers/observable-iterable.js +4 -8
  130. package/utils/async-iterable-helpers/take-until.js +4 -4
  131. package/utils/backoff.js +89 -30
  132. package/utils/retry-with-backoff.js +1 -1
  133. package/utils/timer.d.ts +1 -1
  134. package/utils/timer.js +5 -7
  135. package/utils/timing.d.ts +1 -1
  136. package/utils/timing.js +2 -4
  137. package/utils/z-base32.d.ts +1 -0
  138. package/utils/z-base32.js +1 -0
package/pdf/README.md ADDED
@@ -0,0 +1,246 @@
1
+ # @tstdl/base/pdf
2
+
3
+ A comprehensive module for generating high-fidelity PDFs from HTML, URLs, and templates using a headless browser, along with utilities for merging, inspecting, and converting PDF files.
4
+
5
+ ## Table of Contents
6
+
7
+ - [✨ Features](#-features)
8
+ - [Core Concepts](#core-concepts)
9
+ - [🚀 Basic Usage](#-basic-usage)
10
+ - [Prerequisites](#prerequisites)
11
+ - [Rendering HTML to PDF](#rendering-html-to-pdf)
12
+ - [Rendering a URL to PDF](#rendering-a-url-to-pdf)
13
+ - [🔧 Advanced Topics](#-advanced-topics)
14
+ - [Template-Based Generation](#template-based-generation)
15
+ - [Streaming Support](#streaming-support)
16
+ - [Merging PDFs](#merging-pdfs)
17
+ - [PDF Utilities (Page Count & Image Conversion)](#pdf-utilities-page-count--image-conversion)
18
+ - [Configuration & Options](#configuration--options)
19
+ - [📚 API](#-api)
20
+
21
+ ## ✨ Features
22
+
23
+ - **Headless Browser Rendering**: Utilizes a managed browser instance to render modern HTML, CSS, and JavaScript into PDFs with pixel-perfect accuracy.
24
+ - **Multiple Sources**: Generate PDFs from raw HTML strings, live URLs, or structured templates.
25
+ - **Template Integration**: Seamlessly integrates with `@tstdl/base/templates` for dynamic, data-driven document generation.
26
+ - **Stream-First Architecture**: All rendering methods offer streaming counterparts for efficient memory usage with large documents.
27
+ - **Manipulation Utilities**: Includes tools to merge PDFs, count pages, and rasterize PDF pages into images (PNG, JPEG, etc.).
28
+ - **Customizable**: Extensive options for page formats, margins, locales, and network idle behaviors.
29
+
30
+ ## Core Concepts
31
+
32
+ ### PdfService
33
+
34
+ The `PdfService` is the main entry point for generation. It orchestrates a `BrowserController` to manage headless browser contexts and pages. When you request a PDF render, the service handles the lifecycle of opening a page, setting content, waiting for the network to settle, printing to PDF, and cleaning up resources.
35
+
36
+ ### External Tools
37
+
38
+ While the core rendering logic uses a browser, the manipulation utilities (`mergePdfs`, `getPdfPageCount`, `pdfToImage`) rely on efficient external command-line tools (`qpdf`, `poppler-utils`) to perform operations without the overhead of a browser engine.
39
+
40
+ ## 🚀 Basic Usage
41
+
42
+ ### Prerequisites
43
+
44
+ To use the **utility functions** (`mergePdfs`, `getPdfPageCount`, `pdfToImage`), you must have the following tools installed on your system. The core `PdfService` rendering does **not** require these, but it does require a valid browser environment (managed by `@tstdl/base/browser`).
45
+
46
+ - **`qpdf`**: Required for `getPdfPageCount`.
47
+ - **`poppler-utils`**: Required for `mergePdfs` (uses `pdfunite`) and `pdfToImage` (uses `pdftocairo`).
48
+
49
+ **Debian/Ubuntu:**
50
+
51
+ ```bash
52
+ sudo apt-get update && sudo apt-get install -y qpdf poppler-utils
53
+ ```
54
+
55
+ ### Rendering HTML to PDF
56
+
57
+ Inject the `PdfService` and use `renderHtml` to convert an HTML string directly into a PDF `Uint8Array`.
58
+
59
+ ```typescript
60
+ import { PdfService } from '@tstdl/base/pdf';
61
+ import { inject } from '@tstdl/base/injector';
62
+ import { writeFile } from 'node:fs/promises';
63
+
64
+ const pdfService = inject(PdfService);
65
+
66
+ const html = `
67
+ <html>
68
+ <body>
69
+ <h1>Hello World</h1>
70
+ <p>This is a PDF generated from HTML.</p>
71
+ </body>
72
+ </html>
73
+ `;
74
+
75
+ const pdfBytes = await pdfService.renderHtml(html, {
76
+ format: 'A4',
77
+ margin: { top: '2cm', right: '2cm', bottom: '2cm', left: '2cm' },
78
+ });
79
+
80
+ await writeFile('output.pdf', pdfBytes);
81
+ ```
82
+
83
+ ### Rendering a URL to PDF
84
+
85
+ Capture a live webpage as a PDF.
86
+
87
+ ```typescript
88
+ import { PdfService } from '@tstdl/base/pdf';
89
+ import { inject } from '@tstdl/base/injector';
90
+ import { writeFile } from 'node:fs/promises';
91
+
92
+ const pdfService = inject(PdfService);
93
+
94
+ // Renders the target URL
95
+ const pdfBytes = await pdfService.renderUrl('https://example.com', {
96
+ format: 'Letter',
97
+ renderBackground: true,
98
+ });
99
+
100
+ await writeFile('website.pdf', pdfBytes);
101
+ ```
102
+
103
+ ## 🔧 Advanced Topics
104
+
105
+ ### Template-Based Generation
106
+
107
+ For complex reports, use `pdfTemplate` to define the structure and `@tstdl/base/templates` to handle data injection.
108
+
109
+ ```typescript
110
+ import { PdfService, pdfTemplate } from '@tstdl/base/pdf';
111
+ import { stringTemplateField } from '@tstdl/base/templates';
112
+ import { inject } from '@tstdl/base/injector';
113
+ import { writeFile } from 'node:fs/promises';
114
+
115
+ const pdfService = inject(PdfService);
116
+
117
+ // Define the template structure
118
+ const invoiceTemplate = pdfTemplate(
119
+ 'invoice',
120
+ {
121
+ header: stringTemplateField({
122
+ template: '<div style="font-size: 10px; text-align: center;">Invoice #{{ invoiceId }}</div>',
123
+ }),
124
+ body: stringTemplateField({
125
+ template: '<h1>Total Due: {{ amount }} {{ currency }}</h1><p>Customer: {{ customer }}</p>',
126
+ }),
127
+ footer: stringTemplateField({
128
+ template: '<div style="font-size: 10px;">Page <span class="pageNumber"></span> of <span class="totalPages"></span></div>',
129
+ }),
130
+ },
131
+ {
132
+ displayHeaderFooter: true,
133
+ margin: { top: '2cm', bottom: '2cm' },
134
+ },
135
+ );
136
+
137
+ // Render with context data
138
+ const context = {
139
+ invoiceId: 'INV-2024-001',
140
+ amount: 150.0,
141
+ currency: 'USD',
142
+ customer: 'Acme Corp',
143
+ };
144
+
145
+ const pdfBytes = await pdfService.renderTemplate(invoiceTemplate, context);
146
+ await writeFile('invoice.pdf', pdfBytes);
147
+ ```
148
+
149
+ ### Streaming Support
150
+
151
+ For large documents or high-throughput scenarios, use the `*Stream` methods to handle data as it is generated, reducing memory footprint.
152
+
153
+ ```typescript
154
+ import { PdfService } from '@tstdl/base/pdf';
155
+ import { inject } from '@tstdl/base/injector';
156
+ import { createWriteStream } from 'node:fs';
157
+ import { Writable } from 'node:stream';
158
+
159
+ const pdfService = inject(PdfService);
160
+
161
+ // Get a ReadableStream<Uint8Array>
162
+ const pdfStream = pdfService.renderHtmlStream('<h1>Large Document</h1>...');
163
+
164
+ // Pipe to a file (Node.js stream adapter required for direct piping in some envs,
165
+ // or simply iterate the web stream)
166
+ const fileStream = createWriteStream('streamed-output.pdf');
167
+ const writer = Writable.toWeb(fileStream);
168
+
169
+ await pdfStream.pipeTo(writer);
170
+ ```
171
+
172
+ ### Merging PDFs
173
+
174
+ Combine multiple PDF sources (file paths, buffers, or streams) into a single document.
175
+
176
+ ```typescript
177
+ import { mergePdfs } from '@tstdl/base/pdf';
178
+ import { readFile, writeFile } from 'node:fs/promises';
179
+
180
+ const coverPage = await readFile('cover.pdf');
181
+ const contentPath = '/tmp/content.pdf';
182
+
183
+ // Merge a buffer and a file path
184
+ const mergedBytes = await mergePdfs([coverPage, contentPath]);
185
+
186
+ await writeFile('full-report.pdf', mergedBytes);
187
+ ```
188
+
189
+ ### PDF Utilities (Page Count & Image Conversion)
190
+
191
+ Inspect and convert existing PDFs.
192
+
193
+ ```typescript
194
+ import { getPdfPageCount, pdfToImage } from '@tstdl/base/pdf';
195
+ import { writeFile } from 'node:fs/promises';
196
+
197
+ // 1. Get Page Count
198
+ const pages = await getPdfPageCount('report.pdf');
199
+ console.log(`Document has ${pages} pages.`);
200
+
201
+ // 2. Convert Page 1 to a JPEG image
202
+ const imageBytes = await pdfToImage('report.pdf', 1, 1024, 'jpeg');
203
+ await writeFile('page-1-preview.jpg', imageBytes);
204
+ ```
205
+
206
+ ### Configuration & Options
207
+
208
+ The `PdfServiceRenderOptions` interface allows fine-grained control over the output. You can also configure the `PdfService` globally by providing `PdfServiceArgument` during injection (e.g., setting a default `locale` or custom `browserArguments`).
209
+
210
+ ```typescript
211
+ const options = {
212
+ format: 'A4',
213
+ landscape: true,
214
+ scale: 0.8,
215
+ margin: { top: '1cm', right: '1cm', bottom: '1cm', left: '1cm' },
216
+ renderBackground: true,
217
+ waitForNetworkIdle: true, // Wait for network requests to finish
218
+ delay: 100, // Additional delay in ms before printing
219
+ locale: 'de-DE', // Set browser locale for date/currency formatting
220
+ log: true, // Enable logging for the browser page (Trace level by default)
221
+ browserContext: existingContext, // Optional: reuse an existing browser context
222
+ };
223
+
224
+ await pdfService.renderHtml(html, options);
225
+ ```
226
+
227
+ ## 📚 API
228
+
229
+ | Export | Type | Description |
230
+ | :-------------------------------- | :--------- | :---------------------------------------------------------------------- |
231
+ | **`PdfService`** | `class` | Singleton service for generating PDFs via headless browser. |
232
+ | `PdfService.renderHtml` | `method` | Renders HTML string to `Promise<Uint8Array>`. |
233
+ | `PdfService.renderHtmlStream` | `method` | Renders HTML string to `ReadableStream<Uint8Array>`. |
234
+ | `PdfService.renderUrl` | `method` | Renders a URL to `Promise<Uint8Array>`. |
235
+ | `PdfService.renderUrlStream` | `method` | Renders a URL to `ReadableStream<Uint8Array>`. |
236
+ | `PdfService.renderTemplate` | `method` | Renders a `PdfTemplate` to `Promise<Uint8Array>`. |
237
+ | `PdfService.renderTemplateStream` | `method` | Renders a `PdfTemplate` to `ReadableStream<Uint8Array>`. |
238
+ | **`pdfTemplate`** | `function` | Factory to create a `PdfTemplate` definition. |
239
+ | **`getPdfPageCount`** | `function` | Returns the number of pages in a PDF. Requires `qpdf`. |
240
+ | **`mergePdfs`** | `function` | Merges multiple PDFs into one `Uint8Array`. Requires `pdfunite`. |
241
+ | **`mergePdfsStream`** | `function` | Merges multiple PDFs into a `ReadableStream`. Requires `pdfunite`. |
242
+ | **`pdfToImage`** | `function` | Converts a specific PDF page to an image buffer. Requires `pdftocairo`. |
243
+ | `PdfServiceRenderOptions` | `class` | Configuration object for rendering (extends browser PDF options). |
244
+ | `PdfTemplate` | `class` | Class representing a PDF template structure. |
245
+ | `PdfServiceOptions` | `type` | Global configuration options for `PdfService`. |
246
+ | `PdfServiceArgument` | `type` | Full argument type for `PdfService` injection. |
package/polyfills.js CHANGED
@@ -1 +1,2 @@
1
1
  import 'disposablestack/auto';
2
+ globalThis.Symbol.observable ??= Symbol('observable');
package/pool/README.md ADDED
@@ -0,0 +1,198 @@
1
+ # Pool
2
+
3
+ A robust, asynchronous object pooling implementation for managing reusable resources. It handles instantiation, lifecycle management, and concurrency limits to optimize performance and resource usage.
4
+
5
+ ## Table of Contents
6
+
7
+ - [✨ Features](#-features)
8
+ - [Core Concepts](#core-concepts)
9
+ - [🚀 Basic Usage](#-basic-usage)
10
+ - [🔧 Advanced Topics](#-advanced-topics)
11
+ - [Manual Resource Management](#manual-resource-management)
12
+ - [Error Handling Strategies](#error-handling-strategies)
13
+ - [Pool Lifecycle](#pool-lifecycle)
14
+ - [Resource Verification & Ownership](#resource-verification--ownership)
15
+ - [📚 API](#-api)
16
+
17
+ ## ✨ Features
18
+
19
+ - **Async Lifecycle**: Supports asynchronous factory and disposer functions for complex resource initialization.
20
+ - **Concurrency Control**: Automatically limits the number of active instances. Defaults to half of CPU cores (or 4 if unknown).
21
+ - **Queueing**: Waits for available instances when the pool is exhausted.
22
+ - **Scoped Usage**: Provides a `use()` method that automatically handles acquisition and release (RAII-style).
23
+ - **Error Handling**: Configurable options to dispose of instances that encounter errors instead of returning them to the pool.
24
+ - **Modern Standards**: Implements `AsyncDisposable` for integration with the `using` keyword.
25
+ - **Resource Ownership**: Track which resources belong to the pool to prevent accidental double-releases or cross-pool contamination.
26
+
27
+ ## Core Concepts
28
+
29
+ Object pooling is a design pattern used to improve performance and efficiency when the cost of initializing a class instance is high, or when the number of instances in use at any one time needs to be limited (e.g., database connections, worker threads).
30
+
31
+ The `Pool` class manages a collection of objects:
32
+
33
+ 1. **Factory**: Creates new instances when the pool is not full and no free instances are available.
34
+ 2. **Disposer**: Cleans up instances when they are removed from the pool or when the pool is destroyed.
35
+ 3. **Acquisition**: Clients `get` an instance, removing it from the available set.
36
+ 4. **Release**: Clients `release` an instance, returning it to the available set for reuse.
37
+
38
+ ## 🚀 Basic Usage
39
+
40
+ The most common and recommended way to use the pool is via the `use()` method. This ensures that resources are always released back to the pool, even if an error occurs.
41
+
42
+ ```ts
43
+ import { Pool } from '@tstdl/base/pool';
44
+ import { ConsoleLogger } from '@tstdl/base/logger'; // Assuming a logger implementation
45
+
46
+ // 1. Define the resource you want to pool
47
+ class Worker {
48
+ readonly id = Math.random().toString(36).slice(2);
49
+
50
+ async process(data: string): Promise<string> {
51
+ return `Worker ${this.id} processed ${data}`;
52
+ }
53
+ }
54
+
55
+ // 2. Create the pool
56
+ const logger = new ConsoleLogger();
57
+
58
+ const workerPool = new Pool<Worker>(
59
+ // Factory: Creates a new instance
60
+ () => new Worker(),
61
+ // Disposer: Cleans up the instance (optional logic here)
62
+ (worker) => console.log(`Disposing worker ${worker.id}`),
63
+ logger,
64
+ { size: 2 }, // Limit to 2 concurrent workers
65
+ );
66
+
67
+ async function main() {
68
+ // 3. Use the pool
69
+ // The 'use' method acquires a worker, runs the callback, and releases the worker.
70
+ const result = await workerPool.use(async (worker) => {
71
+ console.log(`Acquired worker ${worker.id}`);
72
+ return await worker.process('Hello World');
73
+ });
74
+
75
+ console.log(result);
76
+
77
+ // Clean up the pool when done
78
+ await workerPool.dispose();
79
+ }
80
+
81
+ main();
82
+ ```
83
+
84
+ ## 🔧 Advanced Topics
85
+
86
+ ### Manual Resource Management
87
+
88
+ If you need fine-grained control over when a resource is acquired and released, you can use `get()` and `release()` directly. **Note:** You must ensure `release()` is called, usually within a `finally` block.
89
+
90
+ ```ts
91
+ import { Pool } from '@tstdl/base/pool';
92
+ import { ConsoleLogger } from '@tstdl/base/logger';
93
+
94
+ const pool = new Pool<object>(
95
+ () => ({}),
96
+ () => {},
97
+ new ConsoleLogger(),
98
+ );
99
+
100
+ async function manualFlow() {
101
+ // Acquire
102
+ const instance = await pool.get();
103
+
104
+ try {
105
+ // Do work
106
+ console.log('Using instance manually');
107
+ } catch (error) {
108
+ // Handle error
109
+ } finally {
110
+ // Release back to pool
111
+ await pool.release(instance);
112
+ }
113
+ }
114
+ ```
115
+
116
+ ### Error Handling Strategies
117
+
118
+ By default, if an error occurs inside `use()`, the instance is released back to the pool for reuse. However, for some resources (like network sockets), an error might indicate the instance is corrupt. You can configure the pool to dispose of instances on error.
119
+
120
+ **Global Configuration:**
121
+
122
+ ```ts
123
+ const pool = new Pool<object>(
124
+ factory,
125
+ disposer,
126
+ logger,
127
+ { disposeOnError: true }, // Always dispose if use() throws
128
+ );
129
+ ```
130
+
131
+ **Per-Call Configuration:**
132
+
133
+ ```ts
134
+ await pool.use(
135
+ async (conn) => {
136
+ throw new Error('Connection lost');
137
+ },
138
+ { disposeOnError: true }, // Override for this specific call
139
+ );
140
+ // The instance used above will be disposed, not released.
141
+ ```
142
+
143
+ ### Pool Lifecycle
144
+
145
+ The pool implements `AsyncDisposable`. When disposed, it clears all free instances and marks itself as disposed. Any currently used instances will be disposed when they are returned.
146
+
147
+ ```ts
148
+ await using pool = new Pool<object>(...);
149
+
150
+ // ... use pool ...
151
+
152
+ // End of scope: pool.dispose() is called automatically.
153
+ // All idle resources are destroyed.
154
+ ```
155
+
156
+ ### Resource Verification & Ownership
157
+
158
+ The pool provides methods to track and manage instances explicitly. This is useful for debugging or when integrating with external systems that might attempt to return objects to the wrong pool.
159
+
160
+ ```ts
161
+ const isOwned = workerPool.owns(manualWorker);
162
+ const currentPoolSize = workerPool.length;
163
+
164
+ if (manualWorker.isCorrupt) {
165
+ // Removes from pool and calls the disposer
166
+ await workerPool.disposeInstance(manualWorker);
167
+ }
168
+ ```
169
+
170
+ ## 📚 API
171
+
172
+ ### Classes
173
+
174
+ | Class | Description |
175
+ | :-------- | :--------------------------------------------------------- |
176
+ | `Pool<T>` | The main class for managing a pool of objects of type `T`. |
177
+
178
+ ### Types & Interfaces
179
+
180
+ | Type | Description |
181
+ | :------------------------ | :----------------------------------------------------------------------------- |
182
+ | `PoolOptions` | Configuration options for the pool (size, error handling). |
183
+ | `PoolUseOptions` | Options for the `use` method (override error handling). |
184
+ | `PoolInstanceFactory<T>` | Function type for creating new instances: `() => T \| Promise<T>`. |
185
+ | `PoolInstanceDisposer<T>` | Function type for disposing instances: `(instance: T) => any \| Promise<any>`. |
186
+
187
+ ### Pool Properties & Methods
188
+
189
+ | Member | Signature | Description |
190
+ | :---------------- | :------------------------------------------------------------------------------------- | :-------------------------------------------------------------------------- |
191
+ | `length` | `number` (getter) | The total number of instances (free + used + being created). |
192
+ | `get` | `(): Promise<T>` | Acquires an instance from the pool. Waits if none are available. |
193
+ | `release` | `(instance: T): Promise<void>` | Returns an instance to the pool. |
194
+ | `use` | `<R>(handler: (instance: T) => R \| Promise<R>, options?: PoolUseOptions): Promise<R>` | Acquires an instance, executes the handler, and releases the instance. |
195
+ | `disposeInstance` | `(instance: T): Promise<void>` | Removes a specific instance from the pool and disposes of it. |
196
+ | `owns` | `(instance: T): boolean` | Checks if an instance belongs to this pool (either in-use or idle). |
197
+ | `dispose` | `(): Promise<void>` | Shuts down the pool and disposes of all idle instances. |
198
+ | `[asyncDispose]` | `(): Promise<void>` | Implementation of `AsyncDisposable` for use with the `using` keyword. |
@@ -0,0 +1,237 @@
1
+ # Process (`@tstdl/base/process`)
2
+
3
+ A modern, promise- and stream-based wrapper for spawning child processes in Node.js, built on the Web Streams API. It simplifies process interaction by treating processes as transform streams and providing ergonomic async/await helpers.
4
+
5
+ ## Table of Contents
6
+
7
+ - [✨ Features](#-features)
8
+ - [Core Concepts](#core-concepts)
9
+ - [🚀 Basic Usage](#-basic-usage)
10
+ - [Spawn and Stay Connected](#spawn-and-stay-connected)
11
+ - [Spawn and Wait](#spawn-and-wait)
12
+ - [Spawn, Wait and Read](#spawn-wait-and-read)
13
+ - [🔧 Advanced Topics](#-advanced-topics)
14
+ - [Writing to Standard Input](#writing-to-standard-input)
15
+ - [Piping Processes](#piping-processes)
16
+ - [Handling Errors and Exit Codes](#handling-errors-and-exit-codes)
17
+ - [Binary Output](#binary-output)
18
+ - [📚 API](#-api)
19
+
20
+ ## ✨ Features
21
+
22
+ - **Web Streams Interface**: Exposes the child process as a `TransformStream`, where the writable side is `stdin` and the readable side is `stdout`.
23
+ - **Promise-based API**: Fully async/await compatible for spawning and waiting for termination.
24
+ - **Convenience Helpers**: Built-in methods to read `stdout` and `stderr` as strings or byte arrays, and to write strings, buffers, or streams to `stdin`.
25
+ - **One-liner Helpers**: `spawnWaitCommand` and `spawnWaitReadCommand` for common tasks without manual process management.
26
+ - **Error Management**: Configurable handling of non-zero exit codes via the `wait()` method.
27
+ - **Type Safety**: Fully typed with TypeScript.
28
+
29
+ ## Core Concepts
30
+
31
+ The primary entry point is the `spawnCommand` function. Unlike the standard Node.js `spawn`, this function returns a `SpawnCommandResult` object which implements the `TransformStream` interface.
32
+
33
+ - **Writable Side (`stdin`)**: You can write to the process's standard input via the `writable` property or the `write()` helper.
34
+ - **Readable Side (`stdout`)**: You can read from the process's standard output via the `readable` property or `readOutput()` helpers.
35
+ - **`stderr`**: Available as a separate `ReadableStream` or via `readError()` helpers.
36
+
37
+ This design allows you to seamlessly pipe data through processes using standard Web Streams API methods like `pipeThrough` and `pipeTo`.
38
+
39
+ ## 🚀 Basic Usage
40
+
41
+ ### Spawn and Stay Connected
42
+
43
+ Spawn a command and interact with it while it's running.
44
+
45
+ ```typescript
46
+ import { spawnCommand } from '@tstdl/base/process';
47
+
48
+ async function listFiles() {
49
+ // Spawn 'ls -la'
50
+ const command = await spawnCommand('ls', ['-la']);
51
+
52
+ // Helper to read the entire stdout as a UTF-8 string
53
+ const output = await command.readOutput();
54
+ console.log('Output:\n', output);
55
+
56
+ // Wait for the process to exit.
57
+ // This will throw an error if the exit code is not 0.
58
+ const { code } = await command.wait();
59
+ console.log(`Process finished with code ${code}`);
60
+ }
61
+ ```
62
+
63
+ ### Spawn and Wait
64
+
65
+ If you only care about the exit code and signal, use `spawnWaitCommand`.
66
+
67
+ ```typescript
68
+ import { spawnWaitCommand } from '@tstdl/base/process';
69
+
70
+ async function checkFile() {
71
+ const { code } = await spawnWaitCommand('test', ['-f', 'config.json']);
72
+ console.log(code === 0 ? 'File exists' : 'File missing');
73
+ }
74
+ ```
75
+
76
+ ### Spawn, Wait and Read
77
+
78
+ Use `spawnWaitReadCommand` to run a command and get its full output in one go.
79
+
80
+ ```typescript
81
+ import { spawnWaitReadCommand } from '@tstdl/base/process';
82
+
83
+ async function getUptime() {
84
+ const result = await spawnWaitReadCommand('string', 'uptime');
85
+ console.log('System uptime:', result.output.trim());
86
+ }
87
+ ```
88
+
89
+ ## 🔧 Advanced Topics
90
+
91
+ ### Writing to Standard Input
92
+
93
+ You can write data to the process using the `write` helper, which accepts strings, `Uint8Array`s, or `ReadableStream`s. It handles encoding and stream closing automatically.
94
+
95
+ ```typescript
96
+ import { spawnCommand } from '@tstdl/base/process';
97
+
98
+ async function grepText() {
99
+ const grep = await spawnCommand('grep', ['world']);
100
+
101
+ // Write text to stdin. The promise resolves when writing is complete.
102
+ await grep.write('hello\nworld\nfoo\nbar');
103
+
104
+ // Read the filtered output
105
+ const result = await grep.readOutput();
106
+ console.log('Grep Result:', result.trim()); // Output: "world"
107
+
108
+ await grep.wait();
109
+ }
110
+ ```
111
+
112
+ ### Piping Processes
113
+
114
+ Because `SpawnCommandResult` is a `TransformStream`, you can pipe the output of one command directly into the input of another.
115
+
116
+ ```typescript
117
+ import { spawnCommand } from '@tstdl/base/process';
118
+
119
+ async function pipeExample() {
120
+ const ls = await spawnCommand('ls', ['-la']);
121
+ const grep = await spawnCommand('grep', ['.ts']);
122
+
123
+ // Pipe ls stdout -> grep stdin
124
+ await ls.readable.pipeTo(grep.writable);
125
+
126
+ // Read the final output from grep
127
+ const output = await grep.readOutput();
128
+ console.log('TypeScript files:\n', output);
129
+
130
+ // Wait for both to finish
131
+ await Promise.all([ls.wait(), grep.wait()]);
132
+ }
133
+ ```
134
+
135
+ ### Handling Errors and Exit Codes
136
+
137
+ By default, `wait()` throws an error if the process exits with a non-zero code. The error message attempts to capture the content of `stderr`.
138
+
139
+ ```typescript
140
+ import { spawnCommand } from '@tstdl/base/process';
141
+
142
+ async function errorHandling() {
143
+ const cat = await spawnCommand('cat', ['non-existent-file.txt']);
144
+
145
+ try {
146
+ await cat.wait();
147
+ } catch (error) {
148
+ console.error('Command failed!');
149
+ console.error((error as Error).message);
150
+ // Output: "cat: non-existent-file.txt: No such file or directory"
151
+ }
152
+ }
153
+
154
+ // Disable automatic throwing
155
+ async function manualErrorHandling() {
156
+ const cat = await spawnCommand('cat', ['non-existent-file.txt']);
157
+ const { code } = await cat.wait({ throwOnNonZeroExitCode: false });
158
+
159
+ if (code !== 0) {
160
+ const errorOutput = await cat.readError();
161
+ console.error(`Exited with ${code}. Error: ${errorOutput}`);
162
+ }
163
+ }
164
+ ```
165
+
166
+ ### Binary Output
167
+
168
+ For commands that produce binary data (like images or compressed files), use `readOutputBytes` or `spawnWaitReadCommand` with the `'binary'` format.
169
+
170
+ ```typescript
171
+ import { spawnCommand, spawnWaitReadCommand } from '@tstdl/base/process';
172
+
173
+ async function getBinary() {
174
+ const result = await spawnWaitReadCommand('binary', 'gzip', ['-c', 'file.txt']);
175
+ console.log(`Compressed size: ${result.output.length} bytes`);
176
+ }
177
+ ```
178
+
179
+ ## 📚 API
180
+
181
+ ### Functions
182
+
183
+ | Function | Description |
184
+ | :--------------------- | :--------------------------------------------------------------------------------------- |
185
+ | `spawnCommand` | Spawns a command and returns an interactive `SpawnCommandResult`. |
186
+ | `spawnWaitCommand` | Spawns a command and waits for its termination. Returns `WaitResult`. |
187
+ | `spawnWaitReadCommand` | Spawns a command, waits for termination, and returns `WaitReadResult` with full output. |
188
+
189
+ ### `SpawnCommandResult`
190
+
191
+ The object returned by `spawnCommand`. It implements `TransformStream<Uint8Array, Uint8Array>` and provides additional helpers:
192
+
193
+ | Property / Method | Type | Description |
194
+ | :---------------------------- | :------------------------------------------------------------------------------------------ | :-------------------------------------------------------------------------------------------- |
195
+ | `process` | `ChildProcess` | The underlying Node.js `ChildProcess` instance. |
196
+ | `stderr` | `ReadableStream<Uint8Array>` | Stream for the process's standard error. |
197
+ | `write(data, options?)` | `Promise<void>` | Writes `string`, `Uint8Array`, or `ReadableStream` to `stdin`. Resolves when writing is done. |
198
+ | `writeInBackground(data, opts)`| `void` | Fire-and-forget version of `write`. Errors are handled by closing streams. |
199
+ | `readOutput()` | `Promise<string>` | Reads all data from `stdout` as a UTF-8 string. |
200
+ | `readOutputBytes()` | `Promise<Uint8Array>` | Reads all data from `stdout` as a byte array. |
201
+ | `readError()` | `Promise<string>` | Reads all data from `stderr` as a UTF-8 string. |
202
+ | `readErrorBytes()` | `Promise<Uint8Array>` | Reads all data from `stderr` as a byte array. |
203
+ | `wait(options?)` | `Promise<WaitResult>` | Waits for the process to exit. Throws if exit code is non-zero unless configured otherwise. |
204
+ | `waitRead(format, options?)` | `Promise<WaitReadResult>` | Waits for the process to exit and returns all output/error data. |
205
+ | `handleNonZeroExitCode()` | `void` | Manually triggers the internal error handling logic (aborting streams) if the process failed. |
206
+
207
+ ### Types
208
+
209
+ #### `SpawnOptions`
210
+
211
+ | Property | Type | Description |
212
+ | :----------------- | :----------------------- | :----------------------------------------- |
213
+ | `arguments` | `string[]` | Arguments for the command. |
214
+ | `workingDirectory` | `string` | Current working directory for the process. |
215
+ | `environment` | `Record<string, string>` | Environment variables for the process. |
216
+
217
+ #### `WaitOptions`
218
+
219
+ | Property | Type | Default | Description |
220
+ | :----------------------- | :-------- | :------ | :-------------------------------------------------------------------------------------------- |
221
+ | `throwOnNonZeroExitCode` | `boolean` | `true` | If true, `wait()` rejects with an error containing `stderr` output if the exit code is not 0. |
222
+
223
+ #### `WaitResult`
224
+
225
+ | Property | Type | Description |
226
+ | :------- | :--------------- | :---------------------------------------------------- |
227
+ | `code` | `number \| null` | The exit code if the child exited on its own. |
228
+ | `signal` | `string \| null` | The signal by which the child process was terminated. |
229
+
230
+ #### `WaitReadResult`
231
+
232
+ Extends `WaitResult` with:
233
+
234
+ | Property | Type | Description |
235
+ | :------- | :---------------------- | :----------------------------------------- |
236
+ | `output` | `string \| Uint8Array` | The full content of `stdout`. |
237
+ | `error` | `string \| Uint8Array` | The full content of `stderr`. |