@tstdl/base 0.93.182 → 0.93.183
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/api/server/api-request-token.provider.js +1 -1
- package/api/server/gateway.js +6 -1
- package/authentication/authentication.api.d.ts +13 -40
- package/authentication/authentication.api.js +5 -14
- package/authentication/client/authentication.service.d.ts +6 -14
- package/authentication/client/authentication.service.js +22 -4
- package/authentication/client/module.d.ts +1 -1
- package/authentication/client/module.js +4 -4
- package/authentication/models/index.d.ts +1 -0
- package/authentication/models/index.js +1 -0
- package/authentication/models/totp-results.model.d.ts +11 -0
- package/authentication/models/totp-results.model.js +37 -0
- package/authentication/server/authentication.api-controller.d.ts +3 -3
- package/authentication/server/authentication.api-controller.js +31 -4
- package/authentication/server/authentication.service.d.ts +5 -14
- package/authentication/server/authentication.service.js +6 -4
- package/core.d.ts +0 -5
- package/core.js +0 -8
- package/document-management/api/document-management.api.d.ts +2 -2
- package/document-management/service-models/document.service-model.d.ts +1 -1
- package/examples/config.d.ts +25 -0
- package/examples/config.js +26 -0
- package/notification/server/module.d.ts +1 -1
- package/notification/server/module.js +1 -1
- package/package.json +5 -5
- package/signals/api.d.ts +5 -1
- package/signals/api.js +3 -1
- package/signals/implementation/api.d.ts +13 -5
- package/signals/implementation/api.js +7 -1
- package/signals/implementation/asserts.d.ts +2 -2
- package/signals/implementation/asserts.js +3 -3
- package/signals/implementation/computed.d.ts +7 -34
- package/signals/implementation/computed.js +14 -83
- package/signals/implementation/configure.js +6 -2
- package/signals/implementation/effect.d.ts +65 -46
- package/signals/implementation/effect.js +97 -62
- package/signals/implementation/index.d.ts +2 -4
- package/signals/implementation/index.js +2 -4
- package/signals/implementation/linked_signal.d.ts +36 -0
- package/signals/implementation/linked_signal.js +34 -0
- package/signals/implementation/primitive/computed.d.ts +55 -0
- package/signals/implementation/primitive/computed.js +107 -0
- package/signals/implementation/primitive/effect.d.ts +26 -0
- package/signals/implementation/primitive/effect.js +31 -0
- package/signals/implementation/{equality.d.ts → primitive/equality.d.ts} +1 -1
- package/signals/implementation/{equality.js → primitive/equality.js} +1 -1
- package/signals/implementation/primitive/errors.d.ts +10 -0
- package/signals/implementation/{errors.js → primitive/errors.js} +3 -4
- package/signals/implementation/primitive/formatter.d.ts +19 -0
- package/signals/implementation/primitive/formatter.js +136 -0
- package/signals/implementation/{graph.d.ts → primitive/graph.d.ts} +68 -36
- package/signals/implementation/primitive/graph.js +386 -0
- package/signals/implementation/primitive/linked_signal.d.ts +46 -0
- package/signals/implementation/primitive/linked_signal.js +110 -0
- package/signals/implementation/primitive/signal.d.ts +31 -0
- package/signals/implementation/primitive/signal.js +80 -0
- package/signals/implementation/primitive/untracked.d.ts +12 -0
- package/signals/implementation/primitive/untracked.js +23 -0
- package/signals/implementation/{watch.d.ts → primitive/watch.d.ts} +1 -2
- package/signals/implementation/{watch.js → primitive/watch.js} +22 -16
- package/signals/implementation/resource/api.d.ts +275 -0
- package/signals/implementation/resource/api.js +26 -0
- package/signals/implementation/resource/debounce.d.ts +13 -0
- package/signals/implementation/resource/debounce.js +113 -0
- package/signals/implementation/resource/from_snapshots.d.ts +16 -0
- package/signals/implementation/resource/from_snapshots.js +44 -0
- package/signals/implementation/resource/index.d.ts +11 -0
- package/signals/implementation/resource/index.js +11 -0
- package/signals/implementation/resource/resource.d.ts +110 -0
- package/signals/implementation/resource/resource.js +402 -0
- package/signals/implementation/root_effect_scheduler.d.ts +50 -0
- package/signals/implementation/root_effect_scheduler.js +66 -0
- package/signals/implementation/signal.d.ts +42 -18
- package/signals/implementation/signal.js +29 -49
- package/signals/implementation/to-observable.d.ts +12 -5
- package/signals/implementation/to-observable.js +12 -2
- package/signals/implementation/to-signal.d.ts +9 -18
- package/signals/implementation/to-signal.js +46 -13
- package/signals/implementation/untracked.d.ts +1 -1
- package/signals/implementation/untracked.js +3 -11
- package/signals/operators/debounce.d.ts +8 -0
- package/signals/operators/debounce.js +19 -0
- package/signals/operators/derive-async.js +43 -15
- package/signals/operators/index.d.ts +2 -0
- package/signals/operators/index.js +2 -0
- package/signals/operators/throttle.d.ts +8 -0
- package/signals/operators/throttle.js +31 -0
- package/ai/genkit/tests/multi-region.test.d.ts +0 -2
- package/ai/genkit/tests/multi-region.test.js +0 -179
- package/ai/genkit/tests/token-limit-fallback.test.d.ts +0 -2
- package/ai/genkit/tests/token-limit-fallback.test.js +0 -209
- package/ai/prompts/tests/prompt-builder.test.d.ts +0 -1
- package/ai/prompts/tests/prompt-builder.test.js +0 -22
- package/ai/tests/instructions-formatter.test.d.ts +0 -1
- package/ai/tests/instructions-formatter.test.js +0 -116
- package/ai/tests/steering.test.d.ts +0 -1
- package/ai/tests/steering.test.js +0 -37
- package/api/client/tests/api-client.test.d.ts +0 -1
- package/api/client/tests/api-client.test.js +0 -194
- package/api/server/tests/csrf.middleware.test.d.ts +0 -1
- package/api/server/tests/csrf.middleware.test.js +0 -91
- package/authentication/tests/authentication-password-requirements.validator.test.d.ts +0 -1
- package/authentication/tests/authentication-password-requirements.validator.test.js +0 -29
- package/authentication/tests/authentication.api-controller.test.d.ts +0 -1
- package/authentication/tests/authentication.api-controller.test.js +0 -156
- package/authentication/tests/authentication.api-request-token.provider.test.d.ts +0 -1
- package/authentication/tests/authentication.api-request-token.provider.test.js +0 -48
- package/authentication/tests/authentication.client-error-handling.test.d.ts +0 -1
- package/authentication/tests/authentication.client-error-handling.test.js +0 -123
- package/authentication/tests/authentication.client-middleware.test.d.ts +0 -1
- package/authentication/tests/authentication.client-middleware.test.js +0 -118
- package/authentication/tests/authentication.client-service-methods.test.d.ts +0 -1
- package/authentication/tests/authentication.client-service-methods.test.js +0 -177
- package/authentication/tests/authentication.client-service-refresh.test.d.ts +0 -1
- package/authentication/tests/authentication.client-service-refresh.test.js +0 -153
- package/authentication/tests/authentication.client-service.test.d.ts +0 -1
- package/authentication/tests/authentication.client-service.test.js +0 -76
- package/authentication/tests/authentication.refresh-busy-loop.test.d.ts +0 -1
- package/authentication/tests/authentication.refresh-busy-loop.test.js +0 -84
- package/authentication/tests/authentication.service.test.d.ts +0 -1
- package/authentication/tests/authentication.service.test.js +0 -167
- package/authentication/tests/authentication.test-ancillary-service.d.ts +0 -9
- package/authentication/tests/authentication.test-ancillary-service.js +0 -27
- package/authentication/tests/brute-force-protection.test.d.ts +0 -1
- package/authentication/tests/brute-force-protection.test.js +0 -211
- package/authentication/tests/helper.test.d.ts +0 -1
- package/authentication/tests/helper.test.js +0 -122
- package/authentication/tests/password-requirements.error.test.d.ts +0 -1
- package/authentication/tests/password-requirements.error.test.js +0 -14
- package/authentication/tests/remember.api.test.d.ts +0 -1
- package/authentication/tests/remember.api.test.js +0 -117
- package/authentication/tests/remember.service.test.d.ts +0 -1
- package/authentication/tests/remember.service.test.js +0 -83
- package/authentication/tests/subject.service.test.d.ts +0 -1
- package/authentication/tests/subject.service.test.js +0 -140
- package/authentication/tests/suspended-subject.test.d.ts +0 -1
- package/authentication/tests/suspended-subject.test.js +0 -120
- package/authentication/tests/totp.enrollment.test.d.ts +0 -1
- package/authentication/tests/totp.enrollment.test.js +0 -123
- package/authentication/tests/totp.login.test.d.ts +0 -1
- package/authentication/tests/totp.login.test.js +0 -213
- package/authentication/tests/totp.recovery-codes.test.d.ts +0 -1
- package/authentication/tests/totp.recovery-codes.test.js +0 -97
- package/authentication/tests/totp.status.test.d.ts +0 -1
- package/authentication/tests/totp.status.test.js +0 -72
- package/cancellation/tests/coverage.test.d.ts +0 -1
- package/cancellation/tests/coverage.test.js +0 -49
- package/cancellation/tests/leak.test.d.ts +0 -1
- package/cancellation/tests/leak.test.js +0 -35
- package/cancellation/tests/token.test.d.ts +0 -1
- package/cancellation/tests/token.test.js +0 -136
- package/circuit-breaker/tests/circuit-breaker.test.d.ts +0 -1
- package/circuit-breaker/tests/circuit-breaker.test.js +0 -116
- package/cryptography/tests/cryptography.test.d.ts +0 -1
- package/cryptography/tests/cryptography.test.js +0 -175
- package/cryptography/tests/jwt.test.d.ts +0 -1
- package/cryptography/tests/jwt.test.js +0 -54
- package/cryptography/tests/modern.test.d.ts +0 -1
- package/cryptography/tests/modern.test.js +0 -105
- package/cryptography/tests/module.test.d.ts +0 -1
- package/cryptography/tests/module.test.js +0 -100
- package/cryptography/tests/totp.test.d.ts +0 -1
- package/cryptography/tests/totp.test.js +0 -108
- package/document-management/tests/ai-config-hierarchy.test.d.ts +0 -1
- package/document-management/tests/ai-config-hierarchy.test.js +0 -59
- package/document-management/tests/ai-config-integration.test.d.ts +0 -1
- package/document-management/tests/ai-config-integration.test.js +0 -125
- package/document-management/tests/ai-config-merge.test.d.ts +0 -1
- package/document-management/tests/ai-config-merge.test.js +0 -46
- package/document-management/tests/document-management-ai-overrides.test.d.ts +0 -1
- package/document-management/tests/document-management-ai-overrides.test.js +0 -63
- package/document-management/tests/document-management-core.test.d.ts +0 -1
- package/document-management/tests/document-management-core.test.js +0 -157
- package/document-management/tests/document-management.api.test.d.ts +0 -1
- package/document-management/tests/document-management.api.test.js +0 -101
- package/document-management/tests/document-statistics.service.test.d.ts +0 -1
- package/document-management/tests/document-statistics.service.test.js +0 -498
- package/document-management/tests/document-validation-ai-overrides.test.d.ts +0 -1
- package/document-management/tests/document-validation-ai-overrides.test.js +0 -87
- package/document-management/tests/document.service.test.d.ts +0 -1
- package/document-management/tests/document.service.test.js +0 -143
- package/document-management/tests/enum-helpers.test.d.ts +0 -1
- package/document-management/tests/enum-helpers.test.js +0 -452
- package/document-management/tests/helper.d.ts +0 -24
- package/document-management/tests/helper.js +0 -39
- package/errors/tests/format.test.d.ts +0 -1
- package/errors/tests/format.test.js +0 -84
- package/http/tests/server-timing.test.d.ts +0 -1
- package/http/tests/server-timing.test.js +0 -42
- package/injector/tests/advanced.test.d.ts +0 -1
- package/injector/tests/advanced.test.js +0 -116
- package/injector/tests/async-init.test.d.ts +0 -1
- package/injector/tests/async-init.test.js +0 -77
- package/injector/tests/basic.test.d.ts +0 -1
- package/injector/tests/basic.test.js +0 -114
- package/injector/tests/hierarchical.test.d.ts +0 -1
- package/injector/tests/hierarchical.test.js +0 -59
- package/injector/tests/leak.test.d.ts +0 -1
- package/injector/tests/leak.test.js +0 -45
- package/injector/tests/lifecycles.test.d.ts +0 -1
- package/injector/tests/lifecycles.test.js +0 -109
- package/logger/tests/pretty-print.test.d.ts +0 -1
- package/logger/tests/pretty-print.test.js +0 -60
- package/notification/tests/notification-api.test.d.ts +0 -1
- package/notification/tests/notification-api.test.js +0 -124
- package/notification/tests/notification-client.test.d.ts +0 -1
- package/notification/tests/notification-client.test.js +0 -101
- package/notification/tests/notification-flow.test.d.ts +0 -1
- package/notification/tests/notification-flow.test.js +0 -296
- package/notification/tests/notification-sse.service.test.d.ts +0 -1
- package/notification/tests/notification-sse.service.test.js +0 -43
- package/notification/tests/notification-type.service.test.d.ts +0 -1
- package/notification/tests/notification-type.service.test.js +0 -41
- package/object-storage/s3/tests/s3.object-storage.integration.test.d.ts +0 -1
- package/object-storage/s3/tests/s3.object-storage.integration.test.js +0 -303
- package/orm/tests/build-jsonb.test.d.ts +0 -1
- package/orm/tests/build-jsonb.test.js +0 -39
- package/orm/tests/data-types.test.d.ts +0 -1
- package/orm/tests/data-types.test.js +0 -39
- package/orm/tests/database-extension.test.d.ts +0 -1
- package/orm/tests/database-extension.test.js +0 -63
- package/orm/tests/database-migration.test.d.ts +0 -1
- package/orm/tests/database-migration.test.js +0 -83
- package/orm/tests/decorators.test.d.ts +0 -1
- package/orm/tests/decorators.test.js +0 -77
- package/orm/tests/encryption.test.d.ts +0 -1
- package/orm/tests/encryption.test.js +0 -31
- package/orm/tests/query-complex.test.d.ts +0 -1
- package/orm/tests/query-complex.test.js +0 -172
- package/orm/tests/query-converter-complex.test.d.ts +0 -1
- package/orm/tests/query-converter-complex.test.js +0 -131
- package/orm/tests/query-converter.test.d.ts +0 -1
- package/orm/tests/query-converter.test.js +0 -123
- package/orm/tests/repository-advanced.test.d.ts +0 -1
- package/orm/tests/repository-advanced.test.js +0 -189
- package/orm/tests/repository-attributes.test.d.ts +0 -1
- package/orm/tests/repository-attributes.test.js +0 -83
- package/orm/tests/repository-compound-primary-key.test.d.ts +0 -2
- package/orm/tests/repository-compound-primary-key.test.js +0 -226
- package/orm/tests/repository-comprehensive.test.d.ts +0 -1
- package/orm/tests/repository-comprehensive.test.js +0 -162
- package/orm/tests/repository-coverage.test.d.ts +0 -2
- package/orm/tests/repository-coverage.test.js +0 -242
- package/orm/tests/repository-cti-complex.test.d.ts +0 -1
- package/orm/tests/repository-cti-complex.test.js +0 -151
- package/orm/tests/repository-cti-embedded.test.d.ts +0 -1
- package/orm/tests/repository-cti-embedded.test.js +0 -178
- package/orm/tests/repository-cti-extensive.test.d.ts +0 -2
- package/orm/tests/repository-cti-extensive.test.js +0 -279
- package/orm/tests/repository-cti-mapping.test.d.ts +0 -2
- package/orm/tests/repository-cti-mapping.test.js +0 -108
- package/orm/tests/repository-cti-search.test.d.ts +0 -1
- package/orm/tests/repository-cti-search.test.js +0 -141
- package/orm/tests/repository-cti-soft-delete.test.d.ts +0 -2
- package/orm/tests/repository-cti-soft-delete.test.js +0 -103
- package/orm/tests/repository-cti-transactions.test.d.ts +0 -1
- package/orm/tests/repository-cti-transactions.test.js +0 -112
- package/orm/tests/repository-cti-upsert-many.test.d.ts +0 -2
- package/orm/tests/repository-cti-upsert-many.test.js +0 -115
- package/orm/tests/repository-cti.test.d.ts +0 -2
- package/orm/tests/repository-cti.test.js +0 -390
- package/orm/tests/repository-edge-cases.test.d.ts +0 -1
- package/orm/tests/repository-edge-cases.test.js +0 -178
- package/orm/tests/repository-expiration.test.d.ts +0 -2
- package/orm/tests/repository-expiration.test.js +0 -140
- package/orm/tests/repository-extra-coverage.test.d.ts +0 -2
- package/orm/tests/repository-extra-coverage.test.js +0 -402
- package/orm/tests/repository-mapping.test.d.ts +0 -2
- package/orm/tests/repository-mapping.test.js +0 -65
- package/orm/tests/repository-regression.test.d.ts +0 -1
- package/orm/tests/repository-regression.test.js +0 -288
- package/orm/tests/repository-search-coverage.test.d.ts +0 -1
- package/orm/tests/repository-search-coverage.test.js +0 -107
- package/orm/tests/repository-search.test.d.ts +0 -1
- package/orm/tests/repository-search.test.js +0 -105
- package/orm/tests/repository-soft-delete.test.d.ts +0 -1
- package/orm/tests/repository-soft-delete.test.js +0 -118
- package/orm/tests/repository-transactions-nested.test.d.ts +0 -1
- package/orm/tests/repository-transactions-nested.test.js +0 -178
- package/orm/tests/repository-types.test.d.ts +0 -1
- package/orm/tests/repository-types.test.js +0 -184
- package/orm/tests/repository-undelete.test.d.ts +0 -2
- package/orm/tests/repository-undelete.test.js +0 -201
- package/orm/tests/schema-converter.test.d.ts +0 -1
- package/orm/tests/schema-converter.test.js +0 -82
- package/orm/tests/schema-generation.test.d.ts +0 -2
- package/orm/tests/schema-generation.test.js +0 -174
- package/orm/tests/sql-helpers.test.d.ts +0 -1
- package/orm/tests/sql-helpers.test.js +0 -67
- package/orm/tests/transaction-safety.test.d.ts +0 -1
- package/orm/tests/transaction-safety.test.js +0 -81
- package/orm/tests/transactional.test.d.ts +0 -1
- package/orm/tests/transactional.test.js +0 -215
- package/orm/tests/utils.test.d.ts +0 -1
- package/orm/tests/utils.test.js +0 -70
- package/pdf/tests/utils.test.d.ts +0 -1
- package/pdf/tests/utils.test.js +0 -187
- package/process/tests/spawn.test.d.ts +0 -1
- package/process/tests/spawn.test.js +0 -182
- package/rate-limit/tests/postgres-rate-limiter.test.d.ts +0 -1
- package/rate-limit/tests/postgres-rate-limiter.test.js +0 -84
- package/renderer/tests/renderer.test.d.ts +0 -1
- package/renderer/tests/renderer.test.js +0 -88
- package/rpc/tests/rpc.integration.test.d.ts +0 -1
- package/rpc/tests/rpc.integration.test.js +0 -615
- package/signals/implementation/errors.d.ts +0 -2
- package/signals/implementation/graph.js +0 -312
- package/signals/implementation/writable-signal.d.ts +0 -48
- package/signals/implementation/writable-signal.js +0 -32
- package/task-queue/tests/coverage-branch.test.d.ts +0 -1
- package/task-queue/tests/coverage-branch.test.js +0 -395
- package/task-queue/tests/coverage-enhancement.test.d.ts +0 -1
- package/task-queue/tests/coverage-enhancement.test.js +0 -150
- package/task-queue/tests/dag.test.d.ts +0 -1
- package/task-queue/tests/dag.test.js +0 -188
- package/task-queue/tests/dependencies.test.d.ts +0 -1
- package/task-queue/tests/dependencies.test.js +0 -296
- package/task-queue/tests/enqueue-batch.test.d.ts +0 -1
- package/task-queue/tests/enqueue-batch.test.js +0 -125
- package/task-queue/tests/enqueue-item.test.d.ts +0 -1
- package/task-queue/tests/enqueue-item.test.js +0 -12
- package/task-queue/tests/fan-out-spawning.test.d.ts +0 -1
- package/task-queue/tests/fan-out-spawning.test.js +0 -94
- package/task-queue/tests/idempotent-replacement.test.d.ts +0 -1
- package/task-queue/tests/idempotent-replacement.test.js +0 -114
- package/task-queue/tests/missing-idempotent-tasks.test.d.ts +0 -1
- package/task-queue/tests/missing-idempotent-tasks.test.js +0 -39
- package/task-queue/tests/optimization-edge-cases.test.d.ts +0 -1
- package/task-queue/tests/optimization-edge-cases.test.js +0 -124
- package/task-queue/tests/queue-generic.test.d.ts +0 -1
- package/task-queue/tests/queue-generic.test.js +0 -8
- package/task-queue/tests/queue.test.d.ts +0 -1
- package/task-queue/tests/queue.test.js +0 -756
- package/task-queue/tests/shutdown.test.d.ts +0 -1
- package/task-queue/tests/shutdown.test.js +0 -41
- package/task-queue/tests/task-context.test.d.ts +0 -1
- package/task-queue/tests/task-context.test.js +0 -7
- package/task-queue/tests/task-union.test.d.ts +0 -1
- package/task-queue/tests/task-union.test.js +0 -18
- package/task-queue/tests/transactions.test.d.ts +0 -1
- package/task-queue/tests/transactions.test.js +0 -47
- package/task-queue/tests/typing.test.d.ts +0 -1
- package/task-queue/tests/typing.test.js +0 -9
- package/task-queue/tests/worker.test.d.ts +0 -1
- package/task-queue/tests/worker.test.js +0 -258
- package/task-queue/tests/zombie-parent.test.d.ts +0 -1
- package/task-queue/tests/zombie-parent.test.js +0 -45
- package/task-queue/tests/zombie-recovery.test.d.ts +0 -1
- package/task-queue/tests/zombie-recovery.test.js +0 -51
- package/utils/tests/backoff.test.d.ts +0 -1
- package/utils/tests/backoff.test.js +0 -41
- package/utils/tests/retry-with-backoff.test.d.ts +0 -1
- package/utils/tests/retry-with-backoff.test.js +0 -49
|
@@ -1,10 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright Google LLC All Rights Reserved.
|
|
4
|
+
*
|
|
5
|
+
* Use of this source code is governed by an MIT-style license that can be
|
|
6
|
+
* found in the LICENSE file at https://angular.dev/license
|
|
7
|
+
*/
|
|
1
8
|
import { Observable } from 'rxjs';
|
|
2
|
-
import type { SignalsInjector } from '../api.js';
|
|
3
|
-
import type { Signal } from './api.js';
|
|
9
|
+
import type { Signal, SignalsInjector } from '../api.js';
|
|
4
10
|
/**
|
|
5
11
|
* Options for `toObservable`.
|
|
6
|
-
*
|
|
7
|
-
* @developerPreview
|
|
8
12
|
*/
|
|
9
13
|
export interface ToObservableOptions {
|
|
10
14
|
/**
|
|
@@ -16,8 +20,11 @@ export interface ToObservableOptions {
|
|
|
16
20
|
injector?: SignalsInjector;
|
|
17
21
|
}
|
|
18
22
|
/**
|
|
19
|
-
* Exposes the value of an `Signal` as an RxJS `Observable`.
|
|
23
|
+
* Exposes the value of an Angular `Signal` as an RxJS `Observable`.
|
|
24
|
+
* As it reflects a state, the observable will always emit the latest value upon subscription.
|
|
20
25
|
*
|
|
21
26
|
* The signal's value will be propagated into the `Observable`'s subscribers using an `effect`.
|
|
27
|
+
*
|
|
28
|
+
* `toObservable` must be called in an injection context unless an injector is provided via options.
|
|
22
29
|
*/
|
|
23
30
|
export declare function toObservable<T>(source: Signal<T>, options?: ToObservableOptions): Observable<T>;
|
|
@@ -1,10 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright Google LLC All Rights Reserved.
|
|
4
|
+
*
|
|
5
|
+
* Use of this source code is governed by an MIT-style license that can be
|
|
6
|
+
* found in the LICENSE file at https://angular.dev/license
|
|
7
|
+
*/
|
|
1
8
|
import { Observable } from 'rxjs';
|
|
2
9
|
import { effect } from './effect.js';
|
|
3
10
|
import { untracked } from './untracked.js';
|
|
4
11
|
/**
|
|
5
|
-
* Exposes the value of an `Signal` as an RxJS `Observable`.
|
|
12
|
+
* Exposes the value of an Angular `Signal` as an RxJS `Observable`.
|
|
13
|
+
* As it reflects a state, the observable will always emit the latest value upon subscription.
|
|
6
14
|
*
|
|
7
15
|
* The signal's value will be propagated into the `Observable`'s subscribers using an `effect`.
|
|
16
|
+
*
|
|
17
|
+
* `toObservable` must be called in an injection context unless an injector is provided via options.
|
|
8
18
|
*/
|
|
9
19
|
export function toObservable(source, options) {
|
|
10
20
|
return new Observable((subscriber) => {
|
|
@@ -16,7 +26,7 @@ export function toObservable(source, options) {
|
|
|
16
26
|
catch (error) {
|
|
17
27
|
untracked(() => subscriber.error(error));
|
|
18
28
|
}
|
|
19
|
-
}, {
|
|
29
|
+
}, { injector: options?.injector, manualCleanup: true });
|
|
20
30
|
return () => effectRef.destroy();
|
|
21
31
|
});
|
|
22
32
|
}
|
|
@@ -1,14 +1,10 @@
|
|
|
1
|
+
/** biome-ignore-all lint: <explanation> */
|
|
2
|
+
import type { Observable, Subscribable } from 'rxjs';
|
|
3
|
+
import { type SignalsInjector } from '../api.js';
|
|
4
|
+
import type { Signal, ValueEqualityFn } from './api.js';
|
|
1
5
|
/**
|
|
2
|
-
*
|
|
3
|
-
* Copyright Google LLC All Rights Reserved.
|
|
4
|
-
*
|
|
5
|
-
* Use of this source code is governed by an MIT-style license that can be
|
|
6
|
-
* found in the LICENSE file at https://angular.io/license
|
|
6
|
+
* Options for `toSignal`.
|
|
7
7
|
*/
|
|
8
|
-
import type { Observable, Subscribable } from 'rxjs';
|
|
9
|
-
import type { SignalsInjector } from '../api.js';
|
|
10
|
-
import type { Signal } from './api.js';
|
|
11
|
-
import type { ValueEqualityFn } from './equality.js';
|
|
12
8
|
export interface ToSignalOptions<T> {
|
|
13
9
|
/**
|
|
14
10
|
* Initial value for the signal produced by `toSignal`.
|
|
@@ -40,21 +36,16 @@ export interface ToSignalOptions<T> {
|
|
|
40
36
|
* until the `Observable` itself completes.
|
|
41
37
|
*/
|
|
42
38
|
manualCleanup?: boolean;
|
|
43
|
-
/**
|
|
44
|
-
* Whether `toSignal` should throw errors from the Observable error channel back to RxJS, where
|
|
45
|
-
* they'll be processed as uncaught exceptions.
|
|
46
|
-
*
|
|
47
|
-
* In practice, this means that the signal returned by `toSignal` will keep returning the last
|
|
48
|
-
* good value forever, as Observables which error produce no further values. This option emulates
|
|
49
|
-
* the behavior of the `async` pipe.
|
|
50
|
-
*/
|
|
51
|
-
rejectErrors?: boolean;
|
|
52
39
|
/**
|
|
53
40
|
* A comparison function which defines equality for values emitted by the observable.
|
|
54
41
|
*
|
|
55
42
|
* Equality comparisons are executed against the initial value if one is provided.
|
|
56
43
|
*/
|
|
57
44
|
equal?: ValueEqualityFn<T>;
|
|
45
|
+
/**
|
|
46
|
+
* A debug name for the signal. Used in Angular DevTools to identify the signal.
|
|
47
|
+
*/
|
|
48
|
+
debugName?: string;
|
|
58
49
|
}
|
|
59
50
|
export declare function toSignal<T>(source: Observable<T> | Subscribable<T>): Signal<T | undefined>;
|
|
60
51
|
export declare function toSignal<T>(source: Observable<T> | Subscribable<T>, options: NoInfer<ToSignalOptions<T | undefined>> & {
|
|
@@ -1,9 +1,18 @@
|
|
|
1
1
|
/* eslint-disable */
|
|
2
|
-
|
|
2
|
+
/** biome-ignore-all lint: <explanation> */
|
|
3
|
+
/**
|
|
4
|
+
* @license
|
|
5
|
+
* Copyright Google LLC All Rights Reserved.
|
|
6
|
+
*
|
|
7
|
+
* Use of this source code is governed by an MIT-style license that can be
|
|
8
|
+
* found in the LICENSE file at https://angular.dev/license
|
|
9
|
+
*/
|
|
10
|
+
import { isDevMode } from '../../core.js';
|
|
11
|
+
import { defineEnum } from '../../enumeration/index.js';
|
|
12
|
+
import { registerFinalization } from '../../memory/index.js';
|
|
13
|
+
import { signal } from '../api.js';
|
|
3
14
|
import { assertNotInReactiveContext } from './asserts.js';
|
|
4
15
|
import { computed } from './computed.js';
|
|
5
|
-
import { signal } from './writable-signal.js';
|
|
6
|
-
import { defineEnum } from '../../enumeration/enumeration.js';
|
|
7
16
|
/**
|
|
8
17
|
* Get the current value of an `Observable` as a reactive `Signal`.
|
|
9
18
|
*
|
|
@@ -15,22 +24,36 @@ import { defineEnum } from '../../enumeration/enumeration.js';
|
|
|
15
24
|
* With `requireSync` set to `true`, `toSignal` will assert that the `Observable` produces a value
|
|
16
25
|
* immediately upon subscription. No `initialValue` is needed in this case, and the returned signal
|
|
17
26
|
* does not include an `undefined` type.
|
|
27
|
+
*
|
|
28
|
+
* By default, the subscription will be automatically cleaned up when the current [injection
|
|
29
|
+
* context](guide/di/dependency-injection-context) is destroyed. For example, when `toSignal` is
|
|
30
|
+
* called during the construction of a component, the subscription will be cleaned up when the
|
|
31
|
+
* component is destroyed. If an injection context is not available, an explicit `Injector` can be
|
|
32
|
+
* passed instead.
|
|
33
|
+
*
|
|
34
|
+
* If the subscription should persist until the `Observable` itself completes, the `manualCleanup`
|
|
35
|
+
* option can be specified instead, which disables the automatic subscription teardown. No injection
|
|
36
|
+
* context is needed in this configuration as well.
|
|
37
|
+
*
|
|
38
|
+
* @see [RxJS interop with Angular signals](ecosystem/rxjs-interop)
|
|
18
39
|
*/
|
|
19
40
|
export function toSignal(source, options) {
|
|
20
|
-
|
|
21
|
-
'
|
|
41
|
+
isDevMode() &&
|
|
42
|
+
assertNotInReactiveContext(toSignal, 'Invoking `toSignal` causes new subscriptions every time. ' +
|
|
43
|
+
'Consider moving `toSignal` outside of the reactive context and read the signal value where needed.');
|
|
22
44
|
const equal = makeToSignalEqual(options?.equal);
|
|
23
45
|
// Note: T is the Observable value type, and U is the initial value type. They don't have to be
|
|
24
46
|
// the same - the returned signal gives values of type `T`.
|
|
25
47
|
let state;
|
|
26
48
|
if (options?.requireSync) {
|
|
27
49
|
// Initially the signal is in a `NoValue` state.
|
|
28
|
-
state = signal({ kind: StateKind.NoValue }, { equal });
|
|
50
|
+
state = signal({ kind: StateKind.NoValue }, { equal, ...(isDevMode() ? createDebugNameObject(options?.debugName, 'state') : undefined) });
|
|
29
51
|
}
|
|
30
52
|
else {
|
|
31
53
|
// If an initial value was passed, use it. Otherwise, use `undefined` as the initial value.
|
|
32
|
-
state = signal({ kind: StateKind.Value, value: options?.initialValue }, { equal });
|
|
54
|
+
state = signal({ kind: StateKind.Value, value: options?.initialValue }, { equal, ...(isDevMode() ? createDebugNameObject(options?.debugName, 'state') : undefined) });
|
|
33
55
|
}
|
|
56
|
+
let destroyUnregisterFn;
|
|
34
57
|
// Note: This code cannot run inside a reactive context (see assertion above). If we'd support
|
|
35
58
|
// this, we would subscribe to the observable outside of the current reactive context, avoiding
|
|
36
59
|
// that side-effect signal reads/writes are attribute to the current consumer. The current
|
|
@@ -40,12 +63,11 @@ export function toSignal(source, options) {
|
|
|
40
63
|
const sub = source.subscribe({
|
|
41
64
|
next: (value) => state.set({ kind: StateKind.Value, value }),
|
|
42
65
|
error: (error) => {
|
|
43
|
-
if (options?.rejectErrors) {
|
|
44
|
-
// Kick the error back to RxJS. It will be caught and rethrown in a macrotask, which causes
|
|
45
|
-
// the error to end up as an uncaught exception.
|
|
46
|
-
throw error;
|
|
47
|
-
}
|
|
48
66
|
state.set({ kind: StateKind.Error, error });
|
|
67
|
+
destroyUnregisterFn?.();
|
|
68
|
+
},
|
|
69
|
+
complete: () => {
|
|
70
|
+
destroyUnregisterFn?.();
|
|
49
71
|
},
|
|
50
72
|
// Completion of the Observable is meaningless to the signal. Signals don't have a concept of
|
|
51
73
|
// "complete".
|
|
@@ -66,13 +88,24 @@ export function toSignal(source, options) {
|
|
|
66
88
|
// This shouldn't really happen because the error is thrown on creation.
|
|
67
89
|
throw new Error('`toSignal()` called with `requireSync` but `Observable` did not emit synchronously.');
|
|
68
90
|
}
|
|
69
|
-
}, {
|
|
91
|
+
}, {
|
|
92
|
+
equal: options?.equal,
|
|
93
|
+
...(isDevMode() ? createDebugNameObject(options?.debugName, 'source') : undefined),
|
|
94
|
+
});
|
|
70
95
|
registerFinalization(result, () => sub.unsubscribe());
|
|
71
96
|
return result;
|
|
72
97
|
}
|
|
73
98
|
function makeToSignalEqual(userEquality = Object.is) {
|
|
74
99
|
return (a, b) => a.kind === StateKind.Value && b.kind === StateKind.Value && userEquality(a.value, b.value);
|
|
75
100
|
}
|
|
101
|
+
/**
|
|
102
|
+
* Creates a debug name object for an internal toSignal signal.
|
|
103
|
+
*/
|
|
104
|
+
function createDebugNameObject(toSignalDebugName, internalSignalDebugName) {
|
|
105
|
+
return {
|
|
106
|
+
debugName: `toSignal${toSignalDebugName ? '#' + toSignalDebugName : ''}.${internalSignalDebugName}`,
|
|
107
|
+
};
|
|
108
|
+
}
|
|
76
109
|
const StateKind = defineEnum('StateKind', {
|
|
77
110
|
NoValue: 0,
|
|
78
111
|
Value: 1,
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
* Copyright Google LLC All Rights Reserved.
|
|
4
4
|
*
|
|
5
5
|
* Use of this source code is governed by an MIT-style license that can be
|
|
6
|
-
* found in the LICENSE file at https://angular.
|
|
6
|
+
* found in the LICENSE file at https://angular.dev/license
|
|
7
7
|
*/
|
|
8
8
|
/**
|
|
9
9
|
* Execute an arbitrary function in a non-reactive (non-tracking) context. The executed function
|
|
@@ -3,21 +3,13 @@
|
|
|
3
3
|
* Copyright Google LLC All Rights Reserved.
|
|
4
4
|
*
|
|
5
5
|
* Use of this source code is governed by an MIT-style license that can be
|
|
6
|
-
* found in the LICENSE file at https://angular.
|
|
6
|
+
* found in the LICENSE file at https://angular.dev/license
|
|
7
7
|
*/
|
|
8
|
-
import {
|
|
8
|
+
import { untracked as untrackedPrimitive } from './primitive/untracked.js';
|
|
9
9
|
/**
|
|
10
10
|
* Execute an arbitrary function in a non-reactive (non-tracking) context. The executed function
|
|
11
11
|
* can, optionally, return a value.
|
|
12
12
|
*/
|
|
13
13
|
export function untracked(nonReactiveReadsFn) {
|
|
14
|
-
|
|
15
|
-
// We are not trying to catch any particular errors here, just making sure that the consumers
|
|
16
|
-
// stack is restored in case of errors.
|
|
17
|
-
try {
|
|
18
|
-
return nonReactiveReadsFn();
|
|
19
|
-
}
|
|
20
|
-
finally {
|
|
21
|
-
setActiveConsumer(prevConsumer);
|
|
22
|
-
}
|
|
14
|
+
return untrackedPrimitive(nonReactiveReadsFn);
|
|
23
15
|
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { type Signal } from '../api.js';
|
|
2
|
+
/**
|
|
3
|
+
* Creates a signal that debounces the value of the source signal.
|
|
4
|
+
* @param source The source signal.
|
|
5
|
+
* @param ms The debounce time in milliseconds.
|
|
6
|
+
* @returns A signal that debounces the source signal.
|
|
7
|
+
*/
|
|
8
|
+
export declare function debounce<T>(source: Signal<T>, ms: number): Signal<T>;
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { registerFinalization } from '../../memory/finalization.js';
|
|
2
|
+
import { effect, signal, untracked } from '../api.js';
|
|
3
|
+
/**
|
|
4
|
+
* Creates a signal that debounces the value of the source signal.
|
|
5
|
+
* @param source The source signal.
|
|
6
|
+
* @param ms The debounce time in milliseconds.
|
|
7
|
+
* @returns A signal that debounces the source signal.
|
|
8
|
+
*/
|
|
9
|
+
export function debounce(source, ms) {
|
|
10
|
+
const debouncedSignal = signal(untracked(source));
|
|
11
|
+
const effectRef = effect((onCleanup) => {
|
|
12
|
+
const value = source();
|
|
13
|
+
const timeout = setTimeout(() => debouncedSignal.set(value), ms);
|
|
14
|
+
onCleanup(() => clearTimeout(timeout));
|
|
15
|
+
});
|
|
16
|
+
const result = debouncedSignal.asReadonly();
|
|
17
|
+
registerFinalization(result, () => effectRef.destroy());
|
|
18
|
+
return result;
|
|
19
|
+
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Subject, concatAll, exhaustAll, from, isObservable, mergeAll, of, switchAll, takeUntil } from 'rxjs';
|
|
1
|
+
import { BehaviorSubject, Subject, concatAll, exhaustAll, filter, from, isObservable, map, materialize, mergeAll, of, switchAll, takeUntil } from 'rxjs';
|
|
2
2
|
import { registerFinalization } from '../../memory/finalization.js';
|
|
3
3
|
import { isPromise } from '../../utils/type-guards.js';
|
|
4
4
|
import { computed, effect, toSignal, untracked } from '../api.js';
|
|
@@ -8,28 +8,56 @@ const operatorMap = {
|
|
|
8
8
|
exhaust: exhaustAll,
|
|
9
9
|
switch: switchAll,
|
|
10
10
|
};
|
|
11
|
+
/**
|
|
12
|
+
* Derives a signal from an asynchronous source (Promise or Observable).
|
|
13
|
+
*
|
|
14
|
+
* @param source A function that returns a value, a Promise, or an Observable.
|
|
15
|
+
* @param options Options for the signal and the async behavior.
|
|
16
|
+
*
|
|
17
|
+
* @warning If `requireSync: true` is used, the source MUST emit synchronously.
|
|
18
|
+
* Since Promises are always resolved asynchronously (on the microtask queue),
|
|
19
|
+
* they will ALWAYS throw an error if used with `requireSync: true`.
|
|
20
|
+
*
|
|
21
|
+
* @returns A Signal that represents the latest value from the async source.
|
|
22
|
+
*/
|
|
11
23
|
export function deriveAsync(source, options) {
|
|
12
|
-
const
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
return from(rawSource);
|
|
16
|
-
}
|
|
17
|
-
if (isObservable(rawSource)) {
|
|
18
|
-
return rawSource;
|
|
19
|
-
}
|
|
20
|
-
return of(rawSource);
|
|
21
|
-
});
|
|
22
|
-
const source$ = new Subject();
|
|
24
|
+
const initialRaw = untracked(source);
|
|
25
|
+
const initialSource = isPromise(initialRaw) ? from(initialRaw) : isObservable(initialRaw) ? initialRaw : of(initialRaw);
|
|
26
|
+
const source$ = new BehaviorSubject(initialSource);
|
|
23
27
|
const destroy$ = new Subject();
|
|
24
28
|
const operator = operatorMap[options?.behavior ?? 'switch'];
|
|
25
|
-
const valueSource$ = source$.pipe(operator(), takeUntil(destroy$));
|
|
26
|
-
const
|
|
29
|
+
const valueSource$ = source$.pipe(map((inner$) => inner$.pipe(materialize(), filter((n) => n.kind != 'C'))), operator(), takeUntil(destroy$));
|
|
30
|
+
const notificationSignal = toSignal(valueSource$, {
|
|
31
|
+
requireSync: options?.requireSync,
|
|
32
|
+
injector: options?.injector,
|
|
33
|
+
manualCleanup: options?.manualCleanup,
|
|
34
|
+
debugName: options?.debugName ? `${options.debugName}-notifications` : undefined,
|
|
35
|
+
initialValue: undefined,
|
|
36
|
+
});
|
|
37
|
+
const result = computed(() => {
|
|
38
|
+
const notification = notificationSignal();
|
|
39
|
+
if (notification === undefined) {
|
|
40
|
+
return options?.initialValue;
|
|
41
|
+
}
|
|
42
|
+
if (notification.kind === 'E') {
|
|
43
|
+
throw notification.error;
|
|
44
|
+
}
|
|
45
|
+
return notification.value;
|
|
46
|
+
}, { equal: options?.equal });
|
|
47
|
+
let firstRun = true;
|
|
27
48
|
const effectRef = effect(() => {
|
|
28
|
-
const
|
|
49
|
+
const rawSource = source();
|
|
50
|
+
if (firstRun) {
|
|
51
|
+
firstRun = false;
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
54
|
+
const observableInput = isPromise(rawSource) ? from(rawSource) : isObservable(rawSource) ? rawSource : of(rawSource);
|
|
29
55
|
untracked(() => source$.next(observableInput));
|
|
30
56
|
});
|
|
31
57
|
registerFinalization(result, () => {
|
|
32
58
|
destroy$.next();
|
|
59
|
+
destroy$.complete();
|
|
60
|
+
source$.complete();
|
|
33
61
|
effectRef.destroy();
|
|
34
62
|
});
|
|
35
63
|
return result;
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { type Signal } from '../api.js';
|
|
2
|
+
/**
|
|
3
|
+
* Creates a signal that throttles the value of the source signal.
|
|
4
|
+
* @param source The source signal.
|
|
5
|
+
* @param ms The throttle time in milliseconds.
|
|
6
|
+
* @returns A signal that throttles the source signal.
|
|
7
|
+
*/
|
|
8
|
+
export declare function throttle<T>(source: Signal<T>, ms: number): Signal<T>;
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { registerFinalization } from '../../memory/finalization.js';
|
|
2
|
+
import { effect, signal, untracked } from '../api.js';
|
|
3
|
+
/**
|
|
4
|
+
* Creates a signal that throttles the value of the source signal.
|
|
5
|
+
* @param source The source signal.
|
|
6
|
+
* @param ms The throttle time in milliseconds.
|
|
7
|
+
* @returns A signal that throttles the source signal.
|
|
8
|
+
*/
|
|
9
|
+
export function throttle(source, ms) {
|
|
10
|
+
const throttledSignal = signal(untracked(source));
|
|
11
|
+
let lastUpdate = 0;
|
|
12
|
+
const effectRef = effect((onCleanup) => {
|
|
13
|
+
const value = source();
|
|
14
|
+
const now = Date.now();
|
|
15
|
+
const remaining = ms - (now - lastUpdate);
|
|
16
|
+
if (remaining <= 0) {
|
|
17
|
+
lastUpdate = now;
|
|
18
|
+
throttledSignal.set(value);
|
|
19
|
+
}
|
|
20
|
+
else {
|
|
21
|
+
const timeout = setTimeout(() => {
|
|
22
|
+
lastUpdate = Date.now();
|
|
23
|
+
throttledSignal.set(value);
|
|
24
|
+
}, remaining);
|
|
25
|
+
onCleanup(() => clearTimeout(timeout));
|
|
26
|
+
}
|
|
27
|
+
});
|
|
28
|
+
const result = throttledSignal.asReadonly();
|
|
29
|
+
registerFinalization(result, () => effectRef.destroy());
|
|
30
|
+
return result;
|
|
31
|
+
}
|
|
@@ -1,179 +0,0 @@
|
|
|
1
|
-
/** biome-ignore-all lint/suspicious/useAwait: defineModel requires async */
|
|
2
|
-
import { genkit, GenkitError, z } from 'genkit';
|
|
3
|
-
import { beforeAll, beforeEach, describe, expect, it, vi } from 'vitest';
|
|
4
|
-
import { CircuitBreakerState } from '../../../circuit-breaker/index.js';
|
|
5
|
-
import { CircuitBreakerProvider } from '../../../circuit-breaker/provider.js';
|
|
6
|
-
import { Logger } from '../../../logger/logger.js';
|
|
7
|
-
import { setupIntegrationTest } from '../../../testing/index.js';
|
|
8
|
-
import { vertexAiMultiLocation } from '../multi-region.plugin.js';
|
|
9
|
-
vi.mock('../../../utils/array/index.js', async (importOriginal) => {
|
|
10
|
-
const actual = await importOriginal();
|
|
11
|
-
return {
|
|
12
|
-
...actual,
|
|
13
|
-
shuffle: vi.fn((items) => [...items]),
|
|
14
|
-
};
|
|
15
|
-
});
|
|
16
|
-
vi.mock('@genkit-ai/google-genai', () => ({
|
|
17
|
-
// biome-ignore lint/style/useNamingConvention: given
|
|
18
|
-
vertexAI: {
|
|
19
|
-
model: vi.fn((name) => ({
|
|
20
|
-
name: `vertexai/${name}`,
|
|
21
|
-
info: { label: 'mock' },
|
|
22
|
-
configSchema: z.object({}),
|
|
23
|
-
})),
|
|
24
|
-
},
|
|
25
|
-
// biome-ignore lint/style/useNamingConvention: given
|
|
26
|
-
googleAI: vi.fn(),
|
|
27
|
-
}));
|
|
28
|
-
describe('Genkit vertexai-multi-location Plugin Tests', () => {
|
|
29
|
-
let ai;
|
|
30
|
-
let cbProvider;
|
|
31
|
-
let logger;
|
|
32
|
-
beforeAll(async () => {
|
|
33
|
-
const { injector } = await setupIntegrationTest({ modules: { circuitBreaker: true } });
|
|
34
|
-
cbProvider = injector.resolve(CircuitBreakerProvider);
|
|
35
|
-
logger = injector.resolve(Logger, 'Test');
|
|
36
|
-
});
|
|
37
|
-
beforeEach(async () => {
|
|
38
|
-
vi.clearAllMocks();
|
|
39
|
-
ai = genkit({
|
|
40
|
-
plugins: [
|
|
41
|
-
vertexAiMultiLocation({
|
|
42
|
-
locations: ['region-1', 'region-2'],
|
|
43
|
-
circuitBreakerProvider: cbProvider,
|
|
44
|
-
logger,
|
|
45
|
-
circuitBreakerConfig: { resetTimeout: 1_000_000, threshold: 1 },
|
|
46
|
-
}),
|
|
47
|
-
],
|
|
48
|
-
});
|
|
49
|
-
const config = { threshold: 1, resetTimeout: 1_000_000 };
|
|
50
|
-
await cbProvider.provide('genkit:vertex-ai:location:region-1', config).recordSuccess();
|
|
51
|
-
await cbProvider.provide('genkit:vertex-ai:location:region-2', config).recordSuccess();
|
|
52
|
-
await cbProvider.provide('genkit:vertex-ai:location:cb-fail', config).recordSuccess();
|
|
53
|
-
await cbProvider.provide('genkit:vertex-ai:location:cb-success', config).recordSuccess();
|
|
54
|
-
});
|
|
55
|
-
it('should route to a location successfully', async () => {
|
|
56
|
-
// Register a mock for the regional model in the registry
|
|
57
|
-
ai.defineModel({
|
|
58
|
-
name: 'vertexai/gemini-2.5-flash',
|
|
59
|
-
}, async (request) => {
|
|
60
|
-
return {
|
|
61
|
-
message: {
|
|
62
|
-
role: 'model',
|
|
63
|
-
content: [{ text: `hello from ${request.config?.location}` }],
|
|
64
|
-
},
|
|
65
|
-
};
|
|
66
|
-
});
|
|
67
|
-
const response = await ai.generate({
|
|
68
|
-
model: 'vertexai-multi-location/gemini-2.5-flash',
|
|
69
|
-
prompt: 'test',
|
|
70
|
-
});
|
|
71
|
-
expect(response.text).toContain('hello from');
|
|
72
|
-
});
|
|
73
|
-
it('should failover on 429 error', async () => {
|
|
74
|
-
let callCount = 0;
|
|
75
|
-
// Overwrite the mock model to fail once
|
|
76
|
-
ai.defineModel({
|
|
77
|
-
name: 'vertexai/gemini-2.5-flash',
|
|
78
|
-
}, async (request) => {
|
|
79
|
-
callCount++;
|
|
80
|
-
if (callCount === 1) {
|
|
81
|
-
throw new GenkitError({ status: 'RESOURCE_EXHAUSTED', message: 'Rate limit exceeded' });
|
|
82
|
-
}
|
|
83
|
-
return {
|
|
84
|
-
message: {
|
|
85
|
-
role: 'model',
|
|
86
|
-
content: [{ text: `success from ${request.config?.location}` }],
|
|
87
|
-
},
|
|
88
|
-
};
|
|
89
|
-
});
|
|
90
|
-
const response = await ai.generate({
|
|
91
|
-
model: 'vertexai-multi-location/gemini-2.5-flash',
|
|
92
|
-
prompt: 'test',
|
|
93
|
-
});
|
|
94
|
-
expect(response.text).toContain('success from');
|
|
95
|
-
expect(callCount).toBe(2);
|
|
96
|
-
});
|
|
97
|
-
it('should trip circuit breaker on 429 and skip location', async () => {
|
|
98
|
-
const locationToFail = 'cb-fail';
|
|
99
|
-
const locationToSuccess = 'cb-success';
|
|
100
|
-
const locations = [locationToFail, locationToSuccess];
|
|
101
|
-
ai = genkit({
|
|
102
|
-
plugins: [
|
|
103
|
-
vertexAiMultiLocation({
|
|
104
|
-
locations,
|
|
105
|
-
circuitBreakerProvider: cbProvider,
|
|
106
|
-
logger,
|
|
107
|
-
circuitBreakerConfig: { resetTimeout: 1000000, threshold: 1 },
|
|
108
|
-
}),
|
|
109
|
-
],
|
|
110
|
-
});
|
|
111
|
-
ai.defineModel({
|
|
112
|
-
name: 'vertexai/gemini-2.5-flash',
|
|
113
|
-
}, async (request) => {
|
|
114
|
-
if (request.config?.location === locationToFail) {
|
|
115
|
-
throw new GenkitError({ status: 'RESOURCE_EXHAUSTED', message: 'Rate limit' });
|
|
116
|
-
}
|
|
117
|
-
return {
|
|
118
|
-
message: {
|
|
119
|
-
role: 'model',
|
|
120
|
-
content: [{ text: `success from ${request.config?.location}` }],
|
|
121
|
-
},
|
|
122
|
-
};
|
|
123
|
-
});
|
|
124
|
-
// First call to trip the breaker for 'locationToFail'
|
|
125
|
-
const response1 = await ai.generate({
|
|
126
|
-
model: 'vertexai-multi-location/gemini-2.5-flash',
|
|
127
|
-
prompt: 'test',
|
|
128
|
-
});
|
|
129
|
-
expect(response1.text).toBe('success from cb-success');
|
|
130
|
-
const cb = cbProvider.provide(`genkit:vertex-ai:location:${locationToFail}`, { threshold: 1, resetTimeout: 1000000 });
|
|
131
|
-
const status = await cb.check();
|
|
132
|
-
expect(status.state).toBe(CircuitBreakerState.Open);
|
|
133
|
-
expect(status.allowed).toBe(false);
|
|
134
|
-
// Second call should skip 'locationToFail' automatically
|
|
135
|
-
const response2 = await ai.generate({
|
|
136
|
-
model: 'vertexai-multi-location/gemini-2.5-flash',
|
|
137
|
-
prompt: 'test',
|
|
138
|
-
});
|
|
139
|
-
expect(response2.text).toBe('success from cb-success');
|
|
140
|
-
});
|
|
141
|
-
it('should throw if no locations are provided', async () => {
|
|
142
|
-
expect(() => {
|
|
143
|
-
vertexAiMultiLocation({
|
|
144
|
-
locations: [],
|
|
145
|
-
circuitBreakerProvider: cbProvider,
|
|
146
|
-
logger,
|
|
147
|
-
});
|
|
148
|
-
}).toThrow('At least one location must be provided');
|
|
149
|
-
});
|
|
150
|
-
it('should throw if all locations are unhealthy', async () => {
|
|
151
|
-
const ai2 = genkit({
|
|
152
|
-
plugins: [
|
|
153
|
-
vertexAiMultiLocation({
|
|
154
|
-
locations: ['unhealthy-1'],
|
|
155
|
-
circuitBreakerProvider: cbProvider,
|
|
156
|
-
logger,
|
|
157
|
-
circuitBreakerConfig: { resetTimeout: 1000000, threshold: 1 },
|
|
158
|
-
}),
|
|
159
|
-
],
|
|
160
|
-
});
|
|
161
|
-
// Manually trip the circuit breaker
|
|
162
|
-
const cb = cbProvider.provide('genkit:vertex-ai:location:unhealthy-1', { threshold: 1, resetTimeout: 1000000 });
|
|
163
|
-
await cb.recordFailure();
|
|
164
|
-
ai2.defineModel({
|
|
165
|
-
name: 'vertexai/gemini-2.5-flash',
|
|
166
|
-
}, async () => {
|
|
167
|
-
return {
|
|
168
|
-
message: {
|
|
169
|
-
role: 'model',
|
|
170
|
-
content: [{ text: 'success' }],
|
|
171
|
-
},
|
|
172
|
-
};
|
|
173
|
-
});
|
|
174
|
-
await expect(ai2.generate({
|
|
175
|
-
model: 'vertexai-multi-location/gemini-2.5-flash',
|
|
176
|
-
prompt: 'test',
|
|
177
|
-
})).rejects.toThrow('All locations were skipped because they are unhealthy');
|
|
178
|
-
});
|
|
179
|
-
});
|