@tstdl/base 0.93.139 → 0.93.141
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/README.md +166 -0
- package/ai/genkit/multi-region.plugin.js +5 -3
- package/ai/genkit/tests/multi-region.test.d.ts +1 -0
- package/ai/genkit/tests/multi-region.test.js +5 -2
- package/ai/parser/parser.js +2 -2
- package/ai/prompts/build.js +1 -0
- package/ai/prompts/instructions-formatter.d.ts +15 -2
- package/ai/prompts/instructions-formatter.js +36 -31
- package/ai/prompts/prompt-builder.js +5 -5
- package/ai/prompts/steering.d.ts +3 -2
- package/ai/prompts/steering.js +3 -1
- package/ai/tests/instructions-formatter.test.js +1 -0
- package/api/README.md +403 -0
- package/api/client/client.js +7 -13
- package/api/client/tests/api-client.test.js +10 -10
- package/api/default-error-handlers.js +1 -1
- package/api/response.d.ts +2 -2
- package/api/response.js +22 -33
- package/api/server/api-controller.d.ts +1 -1
- package/api/server/api-controller.js +3 -3
- package/api/server/api-request-token.provider.d.ts +1 -0
- package/api/server/api-request-token.provider.js +1 -0
- package/api/server/middlewares/allowed-methods.middleware.js +2 -1
- package/api/server/middlewares/content-type.middleware.js +2 -1
- package/api/types.d.ts +3 -2
- package/application/README.md +240 -0
- package/application/application.d.ts +1 -1
- package/application/application.js +3 -3
- package/application/providers.d.ts +20 -2
- package/application/providers.js +34 -7
- package/audit/README.md +267 -0
- package/audit/module.d.ts +5 -0
- package/audit/module.js +9 -1
- package/authentication/README.md +288 -0
- package/authentication/client/authentication.service.d.ts +12 -11
- package/authentication/client/authentication.service.js +21 -21
- package/authentication/client/http-client.middleware.js +2 -2
- package/authentication/server/module.d.ts +5 -0
- package/authentication/server/module.js +9 -1
- package/authentication/tests/authentication.api-controller.test.js +1 -1
- package/authentication/tests/authentication.api-request-token.provider.test.js +1 -1
- package/authentication/tests/authentication.client-error-handling.test.js +2 -1
- package/authentication/tests/authentication.client-service-refresh.test.js +5 -3
- package/authentication/tests/authentication.client-service.test.js +1 -1
- package/browser/README.md +401 -0
- package/cancellation/README.md +156 -0
- package/cancellation/tests/coverage.test.d.ts +1 -0
- package/cancellation/tests/coverage.test.js +49 -0
- package/cancellation/tests/leak.test.js +24 -29
- package/cancellation/tests/token.test.d.ts +1 -0
- package/cancellation/tests/token.test.js +136 -0
- package/cancellation/token.d.ts +53 -177
- package/cancellation/token.js +132 -208
- package/circuit-breaker/postgres/module.d.ts +1 -0
- package/circuit-breaker/postgres/module.js +5 -1
- package/context/README.md +174 -0
- package/cookie/README.md +161 -0
- package/css/README.md +157 -0
- package/data-structures/README.md +320 -0
- package/decorators/README.md +140 -0
- package/distributed-loop/README.md +231 -0
- package/distributed-loop/distributed-loop.js +1 -1
- package/document-management/README.md +403 -0
- package/document-management/server/configure.js +5 -1
- package/document-management/server/module.d.ts +1 -1
- package/document-management/server/module.js +1 -1
- package/document-management/server/services/document-management-ancillary.service.js +1 -1
- package/document-management/server/services/document-management.service.js +9 -7
- package/document-management/tests/ai-config-hierarchy.test.js +0 -5
- package/document-management/tests/document-management-ai-overrides.test.js +0 -1
- package/document-management/tests/document-management-core.test.js +2 -7
- package/document-management/tests/document-management.api.test.js +6 -7
- package/document-management/tests/document-statistics.service.test.js +11 -12
- package/document-management/tests/document-validation-ai-overrides.test.js +0 -1
- package/document-management/tests/document.service.test.js +3 -3
- package/document-management/tests/enum-helpers.test.js +2 -3
- package/dom/README.md +213 -0
- package/enumerable/README.md +259 -0
- package/enumeration/README.md +121 -0
- package/errors/README.md +267 -0
- package/examples/document-management/main.d.ts +1 -0
- package/examples/document-management/main.js +14 -11
- package/file/README.md +191 -0
- package/formats/README.md +210 -0
- package/function/README.md +144 -0
- package/http/README.md +318 -0
- package/http/client/adapters/undici.adapter.js +1 -1
- package/http/client/http-client-request.d.ts +6 -5
- package/http/client/http-client-request.js +8 -9
- package/http/server/node/node-http-server.js +1 -2
- package/image-service/README.md +137 -0
- package/injector/README.md +491 -0
- package/intl/README.md +113 -0
- package/json-path/README.md +182 -0
- package/jsx/README.md +154 -0
- package/key-value-store/README.md +191 -0
- package/key-value-store/postgres/module.d.ts +1 -0
- package/key-value-store/postgres/module.js +5 -1
- package/lock/README.md +249 -0
- package/lock/postgres/module.d.ts +1 -0
- package/lock/postgres/module.js +5 -1
- package/lock/web/web-lock.js +119 -47
- package/logger/README.md +287 -0
- package/mail/README.md +256 -0
- package/mail/module.d.ts +5 -1
- package/mail/module.js +11 -6
- package/memory/README.md +144 -0
- package/message-bus/README.md +244 -0
- package/message-bus/message-bus-base.js +1 -1
- package/module/README.md +182 -0
- package/module/module.d.ts +1 -1
- package/module/module.js +77 -17
- package/module/modules/web-server.module.js +3 -4
- package/notification/server/module.d.ts +1 -0
- package/notification/server/module.js +5 -1
- package/notification/tests/notification-flow.test.js +2 -2
- package/notification/tests/notification-type.service.test.js +24 -15
- package/object-storage/README.md +300 -0
- package/openid-connect/README.md +274 -0
- package/orm/README.md +423 -0
- package/orm/decorators.d.ts +5 -1
- package/orm/decorators.js +1 -1
- package/orm/server/drizzle/schema-converter.js +17 -30
- package/orm/server/encryption.d.ts +0 -1
- package/orm/server/encryption.js +1 -4
- package/orm/server/index.d.ts +1 -6
- package/orm/server/index.js +1 -6
- package/orm/server/migration.d.ts +19 -0
- package/orm/server/migration.js +72 -0
- package/orm/server/repository.d.ts +1 -1
- package/orm/server/transaction.d.ts +5 -10
- package/orm/server/transaction.js +22 -26
- package/orm/server/transactional.js +3 -3
- package/orm/tests/database-migration.test.d.ts +1 -0
- package/orm/tests/database-migration.test.js +82 -0
- package/orm/tests/encryption.test.js +3 -4
- package/orm/utils.d.ts +17 -2
- package/orm/utils.js +49 -1
- package/package.json +9 -6
- package/password/README.md +164 -0
- package/pdf/README.md +246 -0
- package/polyfills.js +1 -0
- package/pool/README.md +198 -0
- package/process/README.md +237 -0
- package/promise/README.md +252 -0
- package/promise/cancelable-promise.js +1 -1
- package/random/README.md +193 -0
- package/rate-limit/postgres/module.d.ts +1 -0
- package/rate-limit/postgres/module.js +5 -1
- package/reflection/README.md +305 -0
- package/reflection/decorator-data.js +11 -12
- package/rpc/README.md +386 -0
- package/rxjs-utils/README.md +262 -0
- package/schema/README.md +342 -0
- package/serializer/README.md +342 -0
- package/signals/implementation/README.md +134 -0
- package/sse/README.md +278 -0
- package/task-queue/README.md +293 -0
- package/task-queue/postgres/drizzle/{0000_simple_invisible_woman.sql → 0000_wakeful_sunspot.sql} +22 -14
- package/task-queue/postgres/drizzle/meta/0000_snapshot.json +160 -82
- package/task-queue/postgres/drizzle/meta/_journal.json +2 -2
- package/task-queue/postgres/module.d.ts +1 -0
- package/task-queue/postgres/module.js +5 -1
- package/task-queue/postgres/schemas.d.ts +9 -6
- package/task-queue/postgres/schemas.js +4 -3
- package/task-queue/postgres/task-queue.d.ts +4 -13
- package/task-queue/postgres/task-queue.js +462 -355
- package/task-queue/postgres/task.model.d.ts +12 -5
- package/task-queue/postgres/task.model.js +51 -25
- package/task-queue/task-context.d.ts +2 -2
- package/task-queue/task-context.js +8 -8
- package/task-queue/task-queue.d.ts +53 -19
- package/task-queue/task-queue.js +121 -55
- package/task-queue/tests/cascading-cancellations.test.d.ts +1 -0
- package/task-queue/tests/cascading-cancellations.test.js +38 -0
- package/task-queue/tests/complex.test.js +45 -229
- package/task-queue/tests/coverage-branch.test.d.ts +1 -0
- package/task-queue/tests/coverage-branch.test.js +407 -0
- package/task-queue/tests/coverage-enhancement.test.d.ts +1 -0
- package/task-queue/tests/coverage-enhancement.test.js +144 -0
- package/task-queue/tests/dag-dependencies.test.d.ts +1 -0
- package/task-queue/tests/dag-dependencies.test.js +41 -0
- package/task-queue/tests/dependencies.test.js +28 -26
- package/task-queue/tests/extensive-dependencies.test.js +64 -139
- package/task-queue/tests/fan-out-spawning.test.d.ts +1 -0
- package/task-queue/tests/fan-out-spawning.test.js +53 -0
- package/task-queue/tests/idempotent-replacement.test.d.ts +1 -0
- package/task-queue/tests/idempotent-replacement.test.js +61 -0
- package/task-queue/tests/missing-idempotent-tasks.test.d.ts +1 -0
- package/task-queue/tests/missing-idempotent-tasks.test.js +38 -0
- package/task-queue/tests/queue.test.js +128 -8
- package/task-queue/tests/worker.test.js +39 -16
- package/task-queue/tests/zombie-parent.test.d.ts +1 -0
- package/task-queue/tests/zombie-parent.test.js +45 -0
- package/task-queue/tests/zombie-recovery.test.d.ts +1 -0
- package/task-queue/tests/zombie-recovery.test.js +51 -0
- package/templates/README.md +287 -0
- package/test5.js +5 -5
- package/testing/README.md +157 -0
- package/testing/integration-setup.d.ts +4 -4
- package/testing/integration-setup.js +54 -29
- package/text/README.md +346 -0
- package/text/localization.service.js +2 -2
- package/threading/README.md +238 -0
- package/types/README.md +311 -0
- package/utils/README.md +322 -0
- package/utils/async-iterable-helpers/observable-iterable.d.ts +1 -1
- package/utils/async-iterable-helpers/observable-iterable.js +4 -8
- package/utils/async-iterable-helpers/take-until.js +4 -4
- package/utils/backoff.js +89 -30
- package/utils/file-reader.js +1 -2
- package/utils/retry-with-backoff.js +1 -1
- package/utils/timer.d.ts +1 -1
- package/utils/timer.js +5 -7
- package/utils/timing.d.ts +1 -1
- package/utils/timing.js +2 -4
- package/utils/z-base32.d.ts +1 -0
- package/utils/z-base32.js +1 -0
package/README.md
ADDED
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
# @tstdl/base - A Comprehensive TypeScript Library
|
|
2
|
+
|
|
3
|
+
**The Comprehensive TypeScript Standard Library for Modern Applications.**
|
|
4
|
+
|
|
5
|
+
`@tstdl/base` is a robust, modular, and strictly type-safe foundation designed to accelerate the development of enterprise-grade applications. Whether you are building a server-side microservice, a complex web application, or a cross-platform tool, this library provides the architectural building blocks you need—from Dependency Injection and ORM to AI integration and reactive state management.
|
|
6
|
+
|
|
7
|
+
## ✨ Key Philosophy
|
|
8
|
+
|
|
9
|
+
- **Type Safety First:** Built entirely in TypeScript with rigorous type definitions, schemas, and generics to catch errors at compile time.
|
|
10
|
+
- **Dependency Injection:** A powerful DI container (`@tstdl/base/injector`) sits at the core, allowing for testable, decoupled, and modular architectures.
|
|
11
|
+
- **Code-First & Definition-First**: Define your data models and API contracts once in TypeScript and let the framework generate the necessary boilerplate and ensure consistency.
|
|
12
|
+
- **Isomorphic Design:** Many modules work seamlessly across Node.js, Browsers, and Workers.
|
|
13
|
+
- **Reactive & Async:** Built-in support for Signals, RxJS, and modern async patterns (Cancellation signals, Async iterables).
|
|
14
|
+
- **Batteries Included:** Stop glueing together dozens of micro-packages. Get a cohesive set of tools that work together out of the box.
|
|
15
|
+
|
|
16
|
+
## 📦 Installation
|
|
17
|
+
|
|
18
|
+
```bash
|
|
19
|
+
npm install @tstdl/base
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
## 🚀 Quick Start
|
|
23
|
+
|
|
24
|
+
Most applications start by bootstrapping the Dependency Injection container and the Application lifecycle manager.
|
|
25
|
+
|
|
26
|
+
```typescript
|
|
27
|
+
import { Application, provideModule, provideSignalHandler } from '@tstdl/base/application';
|
|
28
|
+
import { inject, Singleton } from '@tstdl/base/injector';
|
|
29
|
+
import { Logger, provideConsoleLogTransport, PrettyPrintLogFormatter } from '@tstdl/base/logger';
|
|
30
|
+
|
|
31
|
+
// 1. Define a Service
|
|
32
|
+
@Singleton()
|
|
33
|
+
class HelloWorldService {
|
|
34
|
+
private logger = inject(Logger, 'HelloWorld');
|
|
35
|
+
|
|
36
|
+
sayHello() {
|
|
37
|
+
this.logger.info('Hello from @tstdl/base!');
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// 2. Define your Main Module
|
|
42
|
+
async function main() {
|
|
43
|
+
const service = inject(HelloWorldService);
|
|
44
|
+
service.sayHello();
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// 3. Run the Application
|
|
48
|
+
Application.run('MyFirstApp', [
|
|
49
|
+
provideModule(main),
|
|
50
|
+
provideConsoleLogTransport(PrettyPrintLogFormatter), // Enable logging
|
|
51
|
+
provideSignalHandler(), // Handle Ctrl+C gracefully
|
|
52
|
+
]);
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
## 🧩 Modules Overview
|
|
56
|
+
|
|
57
|
+
### 🏗️ Architecture & Core
|
|
58
|
+
|
|
59
|
+
Building blocks for application structure and lifecycle.
|
|
60
|
+
|
|
61
|
+
- **`@tstdl/base/application`**: Bootstrapping, lifecycle management, and graceful shutdown.
|
|
62
|
+
- **`@tstdl/base/injector`**: Powerful Dependency Injection container with scopes, tokens, and async resolution.
|
|
63
|
+
- **`@tstdl/base/module`**: Standardized contracts for start/stop components and workers.
|
|
64
|
+
- **`@tstdl/base/cancellation`**: RxJS-based cancellation tokens for managing async flows.
|
|
65
|
+
- **`@tstdl/base/message-bus`**: Decoupled communication via local subjects or BroadcastChannel.
|
|
66
|
+
- **`@tstdl/base/context`**: Type-safe execution context management (prop-drilling killer).
|
|
67
|
+
|
|
68
|
+
### 🌐 API & Networking
|
|
69
|
+
|
|
70
|
+
Robust tools for client-server communication.
|
|
71
|
+
|
|
72
|
+
- **`@tstdl/base/api`**: Contract-first API definition sharing between client and server.
|
|
73
|
+
- **`@tstdl/base/http`**: Isomorphic HTTP client/server abstraction with middleware support.
|
|
74
|
+
- **`@tstdl/base/rpc`**: Type-safe Remote Procedure Calls for Workers and cross-window comms.
|
|
75
|
+
- **`@tstdl/base/sse`**: Server-Sent Events with delta-compression support.
|
|
76
|
+
|
|
77
|
+
### 💾 Data & Storage
|
|
78
|
+
|
|
79
|
+
Persistence layers for databases, files, and caching.
|
|
80
|
+
|
|
81
|
+
- **`@tstdl/base/orm`**: Code-first ORM for PostgreSQL (built on Drizzle) featuring repositories, transactions, **transparent column encryption** and **migrations**.
|
|
82
|
+
- **`@tstdl/base/key-value-store`**: Abstract KV store with PostgreSQL implementation.
|
|
83
|
+
- **`@tstdl/base/object-storage`**: S3-compatible storage abstraction with pre-signed URL support.
|
|
84
|
+
- **`@tstdl/base/queue`**: Persistent background job queues with prioritization and batching.
|
|
85
|
+
- **`@tstdl/base/lock`**: Distributed locking (Postgres) and local locking (Web Locks).
|
|
86
|
+
- **`@tstdl/base/distributed-loop`**: Mechanism for running distributed loops synchronized across processes.
|
|
87
|
+
- **`@tstdl/base/document-management`**: Full document lifecycle, classification, and workflow engine.
|
|
88
|
+
|
|
89
|
+
### 🔒 Security & Auth
|
|
90
|
+
|
|
91
|
+
Secure your application with ease.
|
|
92
|
+
|
|
93
|
+
- **`@tstdl/base/authentication`**: Full-stack auth with JWTs, sessions, and impersonation.
|
|
94
|
+
- **`@tstdl/base/openid-connect`**: OIDC client with PKCE and auto-discovery.
|
|
95
|
+
- **`@tstdl/base/audit`**: Structured audit logging with contextual enrichment.
|
|
96
|
+
- **`@tstdl/base/password`**: Strength estimation (zxcvbn) and breach detection (HIBP).
|
|
97
|
+
|
|
98
|
+
### 🛠️ Utilities & Helpers
|
|
99
|
+
|
|
100
|
+
Essential tools for everyday coding.
|
|
101
|
+
|
|
102
|
+
- **`@tstdl/base/schema`**: Runtime validation and type inference.
|
|
103
|
+
- **`@tstdl/base/utils`**: Extensive collection of async iterable helpers, crypto, JSONPath, and deep object manipulation.
|
|
104
|
+
- **`@tstdl/base/serializer`**: JSON-compatible serialization handling circular refs and custom types.
|
|
105
|
+
- **`@tstdl/base/data-structures`**: specialized data structures like circular buffers and weak ref maps.
|
|
106
|
+
- **`@tstdl/base/error`**: Standardized serializable error classes (`NotFoundError`, `ForbiddenError`, etc.).
|
|
107
|
+
- **`@tstdl/base/promise`**: Advanced promise implementations (`DeferredPromise`, `LazyPromise`, `CancelablePromise`).
|
|
108
|
+
- **`@tstdl/base/disposable`**: Implementations of the `Disposable` and `AsyncDisposable` patterns.
|
|
109
|
+
- **`@tstdl/base/logger`**: Structured logging with hierarchical contexts and pluggable transports.
|
|
110
|
+
- **`@tstdl/base/types`**: Advanced TypeScript utility types (GeoJSON, Tagged types, DeepPartial).
|
|
111
|
+
- **`@tstdl/base/enumerable`**: LINQ-inspired fluent API for collections and async streams.
|
|
112
|
+
|
|
113
|
+
### 🤖 AI & Processing
|
|
114
|
+
|
|
115
|
+
Modern capabilities for intelligent apps.
|
|
116
|
+
|
|
117
|
+
- **`@tstdl/base/ai`**: Unified wrapper for Google Gemini and Vertex AI with structured output.
|
|
118
|
+
- **`@tstdl/base/image`**: Abstraction for image processing services (e.g., Imgproxy).
|
|
119
|
+
- **`@tstdl/base/process`**: Promise/Stream-based child process management.
|
|
120
|
+
- **`@tstdl/base/threading`**: Thread pools for Node.js threads and Web Workers with RPC.
|
|
121
|
+
- **`@tstdl/base/pool`**: Async object pooling for resource management.
|
|
122
|
+
|
|
123
|
+
### 🖥️ UI & Rendering
|
|
124
|
+
|
|
125
|
+
Tools for frontend logic and content generation.
|
|
126
|
+
|
|
127
|
+
- **`@tstdl/base/signals`**: High-performance reactive state management based on Angular Signals.
|
|
128
|
+
- **`@tstdl/base/dom`**: Reactive wrappers for DOM observers (Resize, Intersection) and file handling.
|
|
129
|
+
- **`@tstdl/base/browser`**: Controller-based wrapper for Playwright automation.
|
|
130
|
+
- **`@tstdl/base/pdf`**: Generate PDFs from HTML/Templates via headless browser.
|
|
131
|
+
- **`@tstdl/base/mail`**: Email sending service with rich template support.
|
|
132
|
+
- **`@tstdl/base/jsx`**: Lightweight SSR for JSX/TSX (Preact-based).
|
|
133
|
+
- **`@tstdl/base/text`**: Reactive i18n/localization built on Signals.
|
|
134
|
+
- **`@tstdl/base/templates`**: Multi-engine template rendering (Handlebars, JSX, MJML).
|
|
135
|
+
- **`@tstdl/base/theme`**: Services for managing color palettes and CSS variables.
|
|
136
|
+
- **`@tstdl/base/rxjs`**: A collection of RxJS operators.
|
|
137
|
+
|
|
138
|
+
---
|
|
139
|
+
|
|
140
|
+
## 🔧 Configuration
|
|
141
|
+
|
|
142
|
+
Most server-side modules (`orm`, `mail`, `queue`, `ai`) require configuration during the application bootstrap phase.
|
|
143
|
+
|
|
144
|
+
```typescript
|
|
145
|
+
import { configureOrm } from '@tstdl/base/orm/server';
|
|
146
|
+
import { configureAiService } from '@tstdl/base/ai';
|
|
147
|
+
|
|
148
|
+
// Example bootstrap function
|
|
149
|
+
export async function bootstrap() {
|
|
150
|
+
// Configure Database
|
|
151
|
+
configureOrm({
|
|
152
|
+
connection: {
|
|
153
|
+
/* Postgres config */
|
|
154
|
+
},
|
|
155
|
+
});
|
|
156
|
+
|
|
157
|
+
// Configure AI
|
|
158
|
+
configureAiService({
|
|
159
|
+
apiKey: process.env.GOOGLE_API_KEY,
|
|
160
|
+
});
|
|
161
|
+
}
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
## 📖 Getting Started & Examples
|
|
165
|
+
|
|
166
|
+
The best way to get started is to explore the **`examples/`** directory in the repository. It contains practical, runnable examples for many of the core modules, demonstrating how they work together to build a complete application.
|
|
@@ -39,7 +39,7 @@ export function vertexAiMultiLocation(options) {
|
|
|
39
39
|
...request,
|
|
40
40
|
config: {
|
|
41
41
|
...request.config,
|
|
42
|
-
location
|
|
42
|
+
location,
|
|
43
43
|
},
|
|
44
44
|
}, {
|
|
45
45
|
onChunk: streamingCallback,
|
|
@@ -65,13 +65,15 @@ export function vertexAiMultiLocation(options) {
|
|
|
65
65
|
};
|
|
66
66
|
return genkitPlugin(pluginKey, async (_ai) => {
|
|
67
67
|
// Register nothing initially, rely on lazy resolution via resolver
|
|
68
|
-
},
|
|
68
|
+
},
|
|
69
|
+
// biome-ignore lint/suspicious/useAwait: given
|
|
70
|
+
async (ai, actionType, target) => {
|
|
69
71
|
if (actionType != 'model') {
|
|
70
72
|
return;
|
|
71
73
|
}
|
|
72
74
|
// Register the action immediately so the lookup succeeds
|
|
73
75
|
// We must register it directly in the registry since ai.defineModel is async and might be too late
|
|
74
|
-
|
|
76
|
+
ai.registry.registerActionAsync('model', target, createVirtualizedModelAction(ai, target), { namespace: pluginKey });
|
|
75
77
|
});
|
|
76
78
|
}
|
|
77
79
|
vertexAiMultiLocation.model = function multiLocationModel(baseModel) {
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
/** biome-ignore-all lint/suspicious/useAwait: defineModel requires async */
|
|
1
2
|
import { genkit, GenkitError, z } from 'genkit';
|
|
2
3
|
import { beforeAll, beforeEach, describe, expect, it, vi } from 'vitest';
|
|
3
4
|
import { CircuitBreakerState } from '../../../circuit-breaker/index.js';
|
|
@@ -13,6 +14,7 @@ vi.mock('#/utils/array/index.js', async (importOriginal) => {
|
|
|
13
14
|
};
|
|
14
15
|
});
|
|
15
16
|
vi.mock('@genkit-ai/google-genai', () => ({
|
|
17
|
+
// biome-ignore lint/style/useNamingConvention: given
|
|
16
18
|
vertexAI: {
|
|
17
19
|
model: vi.fn((name) => ({
|
|
18
20
|
name: `vertexai/${name}`,
|
|
@@ -20,6 +22,7 @@ vi.mock('@genkit-ai/google-genai', () => ({
|
|
|
20
22
|
configSchema: z.object({}),
|
|
21
23
|
})),
|
|
22
24
|
},
|
|
25
|
+
// biome-ignore lint/style/useNamingConvention: given
|
|
23
26
|
googleAI: vi.fn(),
|
|
24
27
|
}));
|
|
25
28
|
describe('Genkit vertexai-multi-location Plugin Tests', () => {
|
|
@@ -39,11 +42,11 @@ describe('Genkit vertexai-multi-location Plugin Tests', () => {
|
|
|
39
42
|
locations: ['region-1', 'region-2'],
|
|
40
43
|
circuitBreakerProvider: cbProvider,
|
|
41
44
|
logger,
|
|
42
|
-
circuitBreakerConfig: { resetTimeout:
|
|
45
|
+
circuitBreakerConfig: { resetTimeout: 1_000_000, threshold: 1 },
|
|
43
46
|
}),
|
|
44
47
|
],
|
|
45
48
|
});
|
|
46
|
-
const config = { threshold: 1, resetTimeout:
|
|
49
|
+
const config = { threshold: 1, resetTimeout: 1_000_000 };
|
|
47
50
|
await cbProvider.provide('genkit:vertex-ai:location:region-1', config).recordSuccess();
|
|
48
51
|
await cbProvider.provide('genkit:vertex-ai:location:region-2', config).recordSuccess();
|
|
49
52
|
await cbProvider.provide('genkit:vertex-ai:location:cb-fail', config).recordSuccess();
|
package/ai/parser/parser.js
CHANGED
|
@@ -5,7 +5,7 @@ import { isDate, isUndefined } from '../../utils/type-guards.js';
|
|
|
5
5
|
*/
|
|
6
6
|
export function parseAiNullableText(text) {
|
|
7
7
|
const trimmed = text?.trim();
|
|
8
|
-
if (isUndefined(trimmed) || (trimmed
|
|
8
|
+
if (isUndefined(trimmed) || (trimmed === 'null')) {
|
|
9
9
|
return null;
|
|
10
10
|
}
|
|
11
11
|
return trimmed;
|
|
@@ -16,10 +16,10 @@ export function parseAiDateToNumericDate(dateOrString, allowNull = true, fallbac
|
|
|
16
16
|
}
|
|
17
17
|
const date = new Date(dateOrString);
|
|
18
18
|
if (Number.isNaN(date.getTime())) {
|
|
19
|
-
console.warn(`Invalid date string from AI: ${dateOrString}`);
|
|
20
19
|
if (allowNull) {
|
|
21
20
|
return null;
|
|
22
21
|
}
|
|
22
|
+
// biome-ignore lint/complexity/noArguments: fallback can be passed as undefined
|
|
23
23
|
if (arguments.length >= 3) {
|
|
24
24
|
return fallback;
|
|
25
25
|
}
|
package/ai/prompts/build.js
CHANGED
|
@@ -8,6 +8,7 @@ export function buildPrompts(data) {
|
|
|
8
8
|
const systemInstructions = (isDefined(data.additionalSystemInstructions) && (isString(data.additionalSystemInstructions) || (objectKeys(data.additionalSystemInstructions).length > 0)))
|
|
9
9
|
? [data.baseSystemInstructions, { 'Additional Instructions': data.additionalSystemInstructions }]
|
|
10
10
|
: data.baseSystemInstructions;
|
|
11
|
+
// biome-ignore lint/style/useNamingConvention: prompt
|
|
11
12
|
const dataInstructions = isDefined(data.data) ? { Data: `\`\`\`toon\n${formatData(data.data)}\n\`\`\`` } : undefined;
|
|
12
13
|
const outputLanguageInstructions = isDefined(data.language) ? { 'Output Language': languagePrompt(data.language) } : undefined;
|
|
13
14
|
const userInstructions = sections(((isDefined(data.additionalUserInstructions) && (isString(data.additionalUserInstructions) || (objectKeys(data.additionalUserInstructions).length > 0)))
|
|
@@ -70,6 +70,19 @@ export declare function unorderedList(items: InstructionsListContent): Instructi
|
|
|
70
70
|
* @param items - The list items or map.
|
|
71
71
|
*/
|
|
72
72
|
export declare function unorderedList(instruction: string, items: InstructionsListContent): InstructionsList;
|
|
73
|
-
|
|
73
|
+
/**
|
|
74
|
+
* Formats a structured instructions object into a string representation suitable for AI prompts (Markdown).
|
|
75
|
+
*
|
|
76
|
+
* It recursively handles:
|
|
77
|
+
* - `sections`: Creates headers (H1, H2...).
|
|
78
|
+
* - `ordered` / `unordered`: Creates indented lists.
|
|
79
|
+
* - `Instructions`: Objects are formatted as key-value pairs (e.g., `- **Key:** Value`).
|
|
80
|
+
*
|
|
81
|
+
* @param node - The root instructions object, array, or instructions list wrapper.
|
|
82
|
+
* @param options - Formatting options.
|
|
83
|
+
* @param options.initialDepth - The starting indentation level (default: 0).
|
|
84
|
+
* @returns A formatted string.
|
|
85
|
+
*/
|
|
86
|
+
export declare function formatInstructions(node: Instructions | InstructionsList, options?: {
|
|
74
87
|
initialDepth?: number;
|
|
75
|
-
})
|
|
88
|
+
}): string;
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { memoize } from '../../utils/function/memoize.js';
|
|
2
2
|
import { hasOwnProperty, objectEntries } from '../../utils/object/object.js';
|
|
3
3
|
import { assertDefined, isArray, isDefined, isObject, isString } from '../../utils/type-guards.js';
|
|
4
|
+
const whitespacePattern = /^\s*/;
|
|
4
5
|
// --- Factories ---
|
|
5
6
|
function list(style, instructionOrItems, itemsOrNothing) {
|
|
6
7
|
const instruction = isString(instructionOrItems) ? instructionOrItems : undefined;
|
|
@@ -30,7 +31,7 @@ function getPrefix(style, index, sectionDepth) {
|
|
|
30
31
|
case 'unordered':
|
|
31
32
|
return '- ';
|
|
32
33
|
case 'sections':
|
|
33
|
-
return '#'.repeat(Math.max(1, sectionDepth))
|
|
34
|
+
return `${'#'.repeat(Math.max(1, sectionDepth))} `;
|
|
34
35
|
default:
|
|
35
36
|
return '';
|
|
36
37
|
}
|
|
@@ -41,24 +42,24 @@ function dedent(text) {
|
|
|
41
42
|
return text.trim();
|
|
42
43
|
}
|
|
43
44
|
// Remove leading empty line if any (common in template literals)
|
|
44
|
-
if (lines[0].trim().length
|
|
45
|
+
if (lines[0].trim().length === 0) {
|
|
45
46
|
lines.shift();
|
|
46
47
|
}
|
|
47
48
|
// Remove trailing empty line if any
|
|
48
|
-
if (lines.length > 0 && lines
|
|
49
|
+
if (lines.length > 0 && lines.at(-1).trim().length === 0) {
|
|
49
50
|
lines.pop();
|
|
50
51
|
}
|
|
51
|
-
if (lines.length
|
|
52
|
+
if (lines.length === 0) {
|
|
52
53
|
return '';
|
|
53
54
|
}
|
|
54
55
|
// Find minimum indentation (excluding empty lines)
|
|
55
56
|
const minIndent = lines
|
|
56
57
|
.filter((line) => line.trim().length > 0)
|
|
57
58
|
.reduce((min, line) => {
|
|
58
|
-
const match =
|
|
59
|
+
const match = whitespacePattern.exec(line);
|
|
59
60
|
return Math.min(min, match ? match[0].length : 0);
|
|
60
|
-
},
|
|
61
|
-
if (minIndent ==
|
|
61
|
+
}, Number.POSITIVE_INFINITY);
|
|
62
|
+
if (minIndent == Number.POSITIVE_INFINITY) {
|
|
62
63
|
return lines.join('\n').trim();
|
|
63
64
|
}
|
|
64
65
|
return lines.map((line) => line.slice(minIndent)).join('\n').trim();
|
|
@@ -162,14 +163,12 @@ function processNode(node, context) {
|
|
|
162
163
|
// Section: Header \n\n Content
|
|
163
164
|
return `${headerLine}\n\n${dedent(effectiveValue)}`;
|
|
164
165
|
}
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
return formatWithHangingIndent(fullLine, currentBaseIndent, '');
|
|
172
|
-
}
|
|
166
|
+
// List: "- **Key:** Value"
|
|
167
|
+
// We need to construct the full string to calculate hanging indent correctly.
|
|
168
|
+
// headerLine already contains indentation + prefix + key.
|
|
169
|
+
// We strip the indentation to feed it into the formatting helper effectively.
|
|
170
|
+
const fullLine = `${headerLine} ${dedent(effectiveValue)}`.trim();
|
|
171
|
+
return formatWithHangingIndent(fullLine, currentBaseIndent, '');
|
|
173
172
|
}
|
|
174
173
|
// If Value is Object/Array/Wrapper
|
|
175
174
|
const body = processNode(effectiveValue, {
|
|
@@ -183,21 +182,7 @@ function processNode(node, context) {
|
|
|
183
182
|
return `${headerLine}${bodySeparator}${body}`;
|
|
184
183
|
}).join(separator);
|
|
185
184
|
}
|
|
186
|
-
|
|
187
|
-
/**
|
|
188
|
-
* Formats a structured instructions object into a string representation suitable for AI prompts (Markdown).
|
|
189
|
-
*
|
|
190
|
-
* It recursively handles:
|
|
191
|
-
* - `sections`: Creates headers (H1, H2...).
|
|
192
|
-
* - `ordered` / `unordered`: Creates indented lists.
|
|
193
|
-
* - `Instructions`: Objects are formatted as key-value pairs (e.g., `- **Key:** Value`).
|
|
194
|
-
*
|
|
195
|
-
* @param node - The root instructions object, array, or instructions list wrapper.
|
|
196
|
-
* @param options - Formatting options.
|
|
197
|
-
* @param options.initialDepth - The starting indentation level (default: 0).
|
|
198
|
-
* @returns A formatted string.
|
|
199
|
-
*/
|
|
200
|
-
function formatInstructions(node, options = {}) {
|
|
185
|
+
function _formatInstructions(node, options = {}) {
|
|
201
186
|
// Heuristic: If passing a raw object, assume it's a Root Section unless specified otherwise.
|
|
202
187
|
// If passing a Wrapper, the Wrapper dictates the style.
|
|
203
188
|
const initialStyle = isInstructionsList(node)
|
|
@@ -212,4 +197,24 @@ function formatInstructions(node, options = {}) {
|
|
|
212
197
|
sectionDepth: 1,
|
|
213
198
|
style: initialStyle,
|
|
214
199
|
}).trim();
|
|
215
|
-
}
|
|
200
|
+
}
|
|
201
|
+
const formatInstructionsMemoized = memoize(_formatInstructions, { weak: true });
|
|
202
|
+
/**
|
|
203
|
+
* Formats a structured instructions object into a string representation suitable for AI prompts (Markdown).
|
|
204
|
+
*
|
|
205
|
+
* It recursively handles:
|
|
206
|
+
* - `sections`: Creates headers (H1, H2...).
|
|
207
|
+
* - `ordered` / `unordered`: Creates indented lists.
|
|
208
|
+
* - `Instructions`: Objects are formatted as key-value pairs (e.g., `- **Key:** Value`).
|
|
209
|
+
*
|
|
210
|
+
* @param node - The root instructions object, array, or instructions list wrapper.
|
|
211
|
+
* @param options - Formatting options.
|
|
212
|
+
* @param options.initialDepth - The starting indentation level (default: 0).
|
|
213
|
+
* @returns A formatted string.
|
|
214
|
+
*/
|
|
215
|
+
export function formatInstructions(node, options = {}) {
|
|
216
|
+
if (isString(node)) {
|
|
217
|
+
return _formatInstructions(node, options);
|
|
218
|
+
}
|
|
219
|
+
return formatInstructionsMemoized(node, options);
|
|
220
|
+
}
|
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
import { encodeBase64 } from '../../utils/base64.js';
|
|
2
2
|
import { fromEntries, objectEntries, objectKeys } from '../../utils/object/index.js';
|
|
3
|
-
import { assertObjectPass, isDefined, isString } from '../../utils/type-guards.js';
|
|
3
|
+
import { assertObjectPass, isDefined, isString, isUndefined } from '../../utils/type-guards.js';
|
|
4
4
|
import { formatData } from './format.js';
|
|
5
5
|
import { formatInstructions, sections } from './instructions-formatter.js';
|
|
6
6
|
export class PromptBuilder {
|
|
7
|
+
#systemMedia = [];
|
|
8
|
+
#media = [];
|
|
7
9
|
#systemRole;
|
|
8
10
|
#role;
|
|
9
11
|
#systemTask;
|
|
@@ -12,8 +14,6 @@ export class PromptBuilder {
|
|
|
12
14
|
#instructions = {};
|
|
13
15
|
#systemContextParts = {};
|
|
14
16
|
#contextParts = {};
|
|
15
|
-
#systemMedia = [];
|
|
16
|
-
#media = [];
|
|
17
17
|
setSystemRole(role) {
|
|
18
18
|
this.#systemRole = role;
|
|
19
19
|
return this;
|
|
@@ -47,7 +47,7 @@ export class PromptBuilder {
|
|
|
47
47
|
return this;
|
|
48
48
|
}
|
|
49
49
|
addSystemContext(titleOrContext, contextOrNothing) {
|
|
50
|
-
if ((
|
|
50
|
+
if (isUndefined(contextOrNothing)) {
|
|
51
51
|
this.#systemContextParts = { ...this.#systemContextParts, ...assertObjectPass(titleOrContext) };
|
|
52
52
|
return this;
|
|
53
53
|
}
|
|
@@ -55,7 +55,7 @@ export class PromptBuilder {
|
|
|
55
55
|
return this;
|
|
56
56
|
}
|
|
57
57
|
addContext(titleOrContext, contextOrNothing) {
|
|
58
|
-
if ((
|
|
58
|
+
if (isUndefined(contextOrNothing)) {
|
|
59
59
|
this.#contextParts = { ...this.#contextParts, ...assertObjectPass(titleOrContext) };
|
|
60
60
|
return this;
|
|
61
61
|
}
|
package/ai/prompts/steering.d.ts
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
|
+
import type { ObjectLiteral } from '../../types/types.js';
|
|
1
2
|
import { type Instructions } from './instructions-formatter.js';
|
|
2
3
|
export type FewShotExample = {
|
|
3
|
-
input:
|
|
4
|
-
output:
|
|
4
|
+
input: ObjectLiteral | null;
|
|
5
|
+
output: ObjectLiteral | null;
|
|
5
6
|
/** Whether this is a negative example (showing what NOT to do). */
|
|
6
7
|
isNegative?: boolean;
|
|
7
8
|
/** Optional reason explaining why this example is positive or negative. */
|
package/ai/prompts/steering.js
CHANGED
|
@@ -13,9 +13,10 @@ function formatExampleValue(value) {
|
|
|
13
13
|
* @returns A formatted few-shot prompt.
|
|
14
14
|
*/
|
|
15
15
|
export function fewShotPrompt(examples) {
|
|
16
|
-
if (examples.length
|
|
16
|
+
if (examples.length === 0) {
|
|
17
17
|
return {};
|
|
18
18
|
}
|
|
19
|
+
// biome-ignore-start lint/style/useNamingConvention: prompt
|
|
19
20
|
const entries = examples
|
|
20
21
|
.map((example, index) => {
|
|
21
22
|
const type = example.isNegative ? 'NEGATIVE' : 'POSITIVE';
|
|
@@ -31,6 +32,7 @@ export function fewShotPrompt(examples) {
|
|
|
31
32
|
return {
|
|
32
33
|
Examples: orderedList('Use the following examples to understand the expected input and output format', fromEntries(entries)),
|
|
33
34
|
};
|
|
35
|
+
// biome-ignore-end lint/style/useNamingConvention: prompt
|
|
34
36
|
}
|
|
35
37
|
/**
|
|
36
38
|
* Creates a prompt addition to specify the output language.
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
/** biome-ignore-all lint/style/useNamingConvention: instructions are uppercase */
|
|
1
2
|
import { describe, expect, test } from 'vitest';
|
|
2
3
|
import { formatInstructions, orderedList, sections, unorderedList } from '../prompts/instructions-formatter.js';
|
|
3
4
|
describe('Instructions Formatter', () => {
|