@tstdl/base 0.93.181 → 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 +8 -3
- 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
|
@@ -0,0 +1,402 @@
|
|
|
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
|
+
*/
|
|
8
|
+
/* eslint-disable */
|
|
9
|
+
/** biome-ignore-all lint: <explanation> */
|
|
10
|
+
import { isDevMode } from '../../../core.js';
|
|
11
|
+
import { registerFinalization } from '../../../memory/finalization.js';
|
|
12
|
+
import { computed } from '../computed.js';
|
|
13
|
+
import { effect } from '../effect.js';
|
|
14
|
+
import { linkedSignal } from '../linked_signal.js';
|
|
15
|
+
import { signal, signalAsReadonlyFn } from '../signal.js';
|
|
16
|
+
import { untracked } from '../untracked.js';
|
|
17
|
+
import { ResourceDependencyError, ResourceParamsStatus } from './api.js';
|
|
18
|
+
export function resource(options) {
|
|
19
|
+
const oldNameForParams = options.request;
|
|
20
|
+
const params = options.params ?? oldNameForParams ?? (() => null);
|
|
21
|
+
return new ResourceImpl(params, getLoader(options), options.defaultValue, options.equal ? wrapEqualityFn(options.equal) : undefined, options.debugName, options.injector);
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Base class which implements `.value` as a `WritableSignal` by delegating `.set` and `.update`.
|
|
25
|
+
*/
|
|
26
|
+
class BaseWritableResource {
|
|
27
|
+
value;
|
|
28
|
+
isLoading;
|
|
29
|
+
constructor(value, debugName) {
|
|
30
|
+
this.value = value;
|
|
31
|
+
this.value.set = this.set.bind(this);
|
|
32
|
+
this.value.update = this.update.bind(this);
|
|
33
|
+
this.value.asReadonly = signalAsReadonlyFn;
|
|
34
|
+
this.isLoading = computed(() => this.status() === 'loading' || this.status() === 'reloading', isDevMode() ? createDebugNameObject(debugName, 'isLoading') : undefined);
|
|
35
|
+
}
|
|
36
|
+
isError = computed(() => this.status() === 'error');
|
|
37
|
+
update(updateFn) {
|
|
38
|
+
this.set(updateFn(untracked(this.value)));
|
|
39
|
+
}
|
|
40
|
+
// Use a computed here to avoid triggering reactive consumers if the value changes while staying
|
|
41
|
+
// either defined or undefined.
|
|
42
|
+
isValueDefined = computed(() => {
|
|
43
|
+
// Check if it's in an error state first to prevent the error from bubbling up.
|
|
44
|
+
if (this.isError()) {
|
|
45
|
+
return false;
|
|
46
|
+
}
|
|
47
|
+
return this.value() !== undefined;
|
|
48
|
+
});
|
|
49
|
+
_snapshot;
|
|
50
|
+
get snapshot() {
|
|
51
|
+
return (this._snapshot ??= computed(() => {
|
|
52
|
+
const status = this.status();
|
|
53
|
+
if (status === 'error') {
|
|
54
|
+
return { status: 'error', error: this.error() };
|
|
55
|
+
}
|
|
56
|
+
else {
|
|
57
|
+
return { status, value: this.value() };
|
|
58
|
+
}
|
|
59
|
+
}));
|
|
60
|
+
}
|
|
61
|
+
hasValue() {
|
|
62
|
+
return this.isValueDefined();
|
|
63
|
+
}
|
|
64
|
+
asReadonly() {
|
|
65
|
+
return this;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Implementation for `resource()` which uses a `linkedSignal` to manage the resource's state.
|
|
70
|
+
*/
|
|
71
|
+
export class ResourceImpl extends BaseWritableResource {
|
|
72
|
+
/**
|
|
73
|
+
* The current state of the resource. Status, value, and error are derived from this.
|
|
74
|
+
*/
|
|
75
|
+
state;
|
|
76
|
+
/**
|
|
77
|
+
* Combines the current request with a reload counter which allows the resource to be reloaded on
|
|
78
|
+
* imperative command.
|
|
79
|
+
*/
|
|
80
|
+
extRequest;
|
|
81
|
+
effectRef;
|
|
82
|
+
loaderFn;
|
|
83
|
+
defaultValue;
|
|
84
|
+
equal;
|
|
85
|
+
debugName;
|
|
86
|
+
pendingController;
|
|
87
|
+
resolvePendingTask = undefined;
|
|
88
|
+
destroyed = false;
|
|
89
|
+
unregisterOnDestroy;
|
|
90
|
+
status;
|
|
91
|
+
error;
|
|
92
|
+
constructor(request, loaderFn, defaultValue, equal, debugName, injector, getInitialStream) {
|
|
93
|
+
if (isInParamsFunction()) {
|
|
94
|
+
throw invalidResourceCreationInParams();
|
|
95
|
+
}
|
|
96
|
+
super(
|
|
97
|
+
// Feed a computed signal for the value to `BaseWritableResource`, which will upgrade it to a
|
|
98
|
+
// `WritableSignal` that delegates to `ResourceImpl.set`.
|
|
99
|
+
computed(() => {
|
|
100
|
+
const streamValue = this.state().stream?.();
|
|
101
|
+
if (!streamValue) {
|
|
102
|
+
return defaultValue;
|
|
103
|
+
}
|
|
104
|
+
// Prevents `hasValue()` from throwing an error when a reload happened in the error state
|
|
105
|
+
if (this.state().status === 'loading' && this.error()) {
|
|
106
|
+
return defaultValue;
|
|
107
|
+
}
|
|
108
|
+
if (!isResolved(streamValue)) {
|
|
109
|
+
throw new ResourceValueError(this.error());
|
|
110
|
+
}
|
|
111
|
+
return streamValue.value;
|
|
112
|
+
}, { equal, ...(isDevMode() ? createDebugNameObject(debugName, 'value') : undefined) }), debugName);
|
|
113
|
+
this.loaderFn = loaderFn;
|
|
114
|
+
this.defaultValue = defaultValue;
|
|
115
|
+
this.equal = equal;
|
|
116
|
+
this.debugName = debugName;
|
|
117
|
+
this.extRequest = linkedSignal(() => {
|
|
118
|
+
try {
|
|
119
|
+
setInParamsFunction(true);
|
|
120
|
+
return { request: request(paramsContext), reload: 0 };
|
|
121
|
+
}
|
|
122
|
+
catch (error) {
|
|
123
|
+
rethrowFatalErrors(error);
|
|
124
|
+
if (error === ResourceParamsStatus.IDLE) {
|
|
125
|
+
return { status: 'idle', reload: 0 };
|
|
126
|
+
}
|
|
127
|
+
else if (error === ResourceParamsStatus.LOADING) {
|
|
128
|
+
return { status: 'loading', reload: 0 };
|
|
129
|
+
}
|
|
130
|
+
return { error: error, reload: 0 };
|
|
131
|
+
}
|
|
132
|
+
finally {
|
|
133
|
+
setInParamsFunction(false);
|
|
134
|
+
}
|
|
135
|
+
}, isDevMode() ? createDebugNameObject(debugName, 'extRequest') : undefined);
|
|
136
|
+
// The main resource state is managed in a `linkedSignal`, which allows the resource to change
|
|
137
|
+
// state instantaneously when the request signal changes.
|
|
138
|
+
this.state = linkedSignal({
|
|
139
|
+
// Whenever the request changes,
|
|
140
|
+
source: this.extRequest,
|
|
141
|
+
// Compute the state of the resource given a change in status.
|
|
142
|
+
computation: (extRequest, previous) => {
|
|
143
|
+
let { request, status, error } = extRequest;
|
|
144
|
+
let stream;
|
|
145
|
+
if (error) {
|
|
146
|
+
status = 'resolved';
|
|
147
|
+
stream = signal({ error: encapsulateResourceError(error) }, isDevMode() ? createDebugNameObject(this.debugName, 'stream') : undefined);
|
|
148
|
+
}
|
|
149
|
+
else if (!status) {
|
|
150
|
+
if (!previous) {
|
|
151
|
+
stream = getInitialStream?.(extRequest.request);
|
|
152
|
+
// Clear getInitialStream so it doesn't hold onto memory
|
|
153
|
+
getInitialStream = undefined;
|
|
154
|
+
status = request === undefined ? 'idle' : stream ? 'resolved' : 'loading';
|
|
155
|
+
}
|
|
156
|
+
else {
|
|
157
|
+
status = request === undefined ? 'idle' : 'loading';
|
|
158
|
+
if (previous.value.extRequest.request === request) {
|
|
159
|
+
stream = previous.value.stream;
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
return {
|
|
164
|
+
extRequest,
|
|
165
|
+
status,
|
|
166
|
+
previousStatus: previous ? projectStatusOfState(previous.value) : 'idle',
|
|
167
|
+
stream,
|
|
168
|
+
};
|
|
169
|
+
},
|
|
170
|
+
...(isDevMode() ? createDebugNameObject(debugName, 'state') : undefined),
|
|
171
|
+
});
|
|
172
|
+
this.effectRef = effect(this.loadEffect.bind(this), {
|
|
173
|
+
injector,
|
|
174
|
+
manualCleanup: true,
|
|
175
|
+
...(isDevMode() ? createDebugNameObject(debugName, 'loadEffect') : undefined),
|
|
176
|
+
});
|
|
177
|
+
this.status = computed(() => projectStatusOfState(this.state()), isDevMode() ? createDebugNameObject(debugName, 'status') : undefined);
|
|
178
|
+
this.error = computed(() => {
|
|
179
|
+
const stream = this.state().stream?.();
|
|
180
|
+
return stream && !isResolved(stream) ? stream.error : undefined;
|
|
181
|
+
}, isDevMode() ? createDebugNameObject(debugName, 'error') : undefined);
|
|
182
|
+
registerFinalization(this, () => this.destroy());
|
|
183
|
+
}
|
|
184
|
+
/**
|
|
185
|
+
* Called either directly via `WritableResource.set` or via `.value.set()`.
|
|
186
|
+
*/
|
|
187
|
+
set(value) {
|
|
188
|
+
if (this.destroyed) {
|
|
189
|
+
return;
|
|
190
|
+
}
|
|
191
|
+
const error = untracked(this.error);
|
|
192
|
+
const state = untracked(this.state);
|
|
193
|
+
if (!error) {
|
|
194
|
+
const current = untracked(this.value);
|
|
195
|
+
if (state.status === 'local' &&
|
|
196
|
+
(this.equal ? this.equal(current, value) : current === value)) {
|
|
197
|
+
return;
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
// Enter Local state with the user-defined value.
|
|
201
|
+
this.state.set({
|
|
202
|
+
extRequest: state.extRequest,
|
|
203
|
+
status: 'local',
|
|
204
|
+
previousStatus: 'local',
|
|
205
|
+
stream: signal({ value }, isDevMode() ? createDebugNameObject(this.debugName, 'stream') : undefined),
|
|
206
|
+
});
|
|
207
|
+
// We're departing from whatever state the resource was in previously, so cancel any in-progress
|
|
208
|
+
// loading operations.
|
|
209
|
+
this.abortInProgressLoad();
|
|
210
|
+
}
|
|
211
|
+
reload() {
|
|
212
|
+
// We don't want to restart in-progress loads.
|
|
213
|
+
const { status } = untracked(this.state);
|
|
214
|
+
if (status === 'idle' || status === 'loading') {
|
|
215
|
+
return false;
|
|
216
|
+
}
|
|
217
|
+
// Increment the request reload to trigger the `state` linked signal to switch us to `Reload`
|
|
218
|
+
this.extRequest.update(({ request, reload }) => ({ request, reload: reload + 1 }));
|
|
219
|
+
return true;
|
|
220
|
+
}
|
|
221
|
+
destroy() {
|
|
222
|
+
this.destroyed = true;
|
|
223
|
+
this.unregisterOnDestroy();
|
|
224
|
+
this.effectRef.destroy();
|
|
225
|
+
this.abortInProgressLoad();
|
|
226
|
+
// Destroyed resources enter Idle state.
|
|
227
|
+
this.state.set({
|
|
228
|
+
extRequest: { request: undefined, reload: 0 },
|
|
229
|
+
status: 'idle',
|
|
230
|
+
previousStatus: 'idle',
|
|
231
|
+
stream: undefined,
|
|
232
|
+
});
|
|
233
|
+
}
|
|
234
|
+
async loadEffect() {
|
|
235
|
+
const extRequest = this.extRequest();
|
|
236
|
+
// Capture the previous status before any state transitions. Note that this is `untracked` since
|
|
237
|
+
// we do not want the effect to depend on the state of the resource, only on the request.
|
|
238
|
+
const { status: currentStatus, previousStatus } = untracked(this.state);
|
|
239
|
+
if (extRequest.request === undefined) {
|
|
240
|
+
// Nothing to load (and we should already be in a non-loading state).
|
|
241
|
+
return;
|
|
242
|
+
}
|
|
243
|
+
else if (currentStatus !== 'loading') {
|
|
244
|
+
// We're not in a loading or reloading state, so this loading request is stale.
|
|
245
|
+
return;
|
|
246
|
+
}
|
|
247
|
+
// Cancel any previous loading attempts.
|
|
248
|
+
this.abortInProgressLoad();
|
|
249
|
+
const { signal: abortSignal } = (this.pendingController = new AbortController());
|
|
250
|
+
try {
|
|
251
|
+
// The actual loading is run through `untracked` - only the request side of `resource` is
|
|
252
|
+
// reactive. This avoids any confusion with signals tracking or not tracking depending on
|
|
253
|
+
// which side of the `await` they are.
|
|
254
|
+
const stream = await untracked(() => {
|
|
255
|
+
return this.loaderFn({
|
|
256
|
+
params: extRequest.request,
|
|
257
|
+
abortSignal,
|
|
258
|
+
previous: {
|
|
259
|
+
status: previousStatus,
|
|
260
|
+
},
|
|
261
|
+
});
|
|
262
|
+
});
|
|
263
|
+
// If this request has been aborted, or the current request no longer
|
|
264
|
+
// matches this load, then we should ignore this resolution.
|
|
265
|
+
if (abortSignal.aborted || untracked(this.extRequest) !== extRequest) {
|
|
266
|
+
return;
|
|
267
|
+
}
|
|
268
|
+
this.state.set({
|
|
269
|
+
extRequest,
|
|
270
|
+
status: 'resolved',
|
|
271
|
+
previousStatus: 'resolved',
|
|
272
|
+
stream,
|
|
273
|
+
});
|
|
274
|
+
}
|
|
275
|
+
catch (err) {
|
|
276
|
+
rethrowFatalErrors(err);
|
|
277
|
+
if (abortSignal.aborted || untracked(this.extRequest) !== extRequest) {
|
|
278
|
+
return;
|
|
279
|
+
}
|
|
280
|
+
this.state.set({
|
|
281
|
+
extRequest,
|
|
282
|
+
status: 'resolved',
|
|
283
|
+
previousStatus: 'error',
|
|
284
|
+
stream: signal({ error: encapsulateResourceError(err) }, isDevMode() ? createDebugNameObject(this.debugName, 'stream') : undefined),
|
|
285
|
+
});
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
abortInProgressLoad() {
|
|
289
|
+
untracked(() => this.pendingController?.abort());
|
|
290
|
+
this.pendingController = undefined;
|
|
291
|
+
// Once the load is aborted, we no longer want to block stability on its resolution.
|
|
292
|
+
this.resolvePendingTask?.();
|
|
293
|
+
this.resolvePendingTask = undefined;
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
/**
|
|
297
|
+
* Wraps an equality function to handle either value being `undefined`.
|
|
298
|
+
*/
|
|
299
|
+
function wrapEqualityFn(equal) {
|
|
300
|
+
return (a, b) => (a === undefined || b === undefined ? a === b : equal(a, b));
|
|
301
|
+
}
|
|
302
|
+
function getLoader(options) {
|
|
303
|
+
if (isStreamingResourceOptions(options)) {
|
|
304
|
+
return options.stream;
|
|
305
|
+
}
|
|
306
|
+
return async (params) => {
|
|
307
|
+
try {
|
|
308
|
+
return signal({ value: await options.loader(params) }, isDevMode() ? createDebugNameObject(options.debugName, 'stream') : undefined);
|
|
309
|
+
}
|
|
310
|
+
catch (err) {
|
|
311
|
+
return signal({ error: encapsulateResourceError(err) }, isDevMode() ? createDebugNameObject(options.debugName, 'stream') : undefined);
|
|
312
|
+
}
|
|
313
|
+
};
|
|
314
|
+
}
|
|
315
|
+
function isStreamingResourceOptions(options) {
|
|
316
|
+
return !!options.stream;
|
|
317
|
+
}
|
|
318
|
+
/**
|
|
319
|
+
* Project from a state with `ResourceInternalStatus` to the user-facing `ResourceStatus`
|
|
320
|
+
*/
|
|
321
|
+
function projectStatusOfState(state) {
|
|
322
|
+
switch (state.status) {
|
|
323
|
+
case 'loading':
|
|
324
|
+
return state.extRequest.reload === 0 ? 'loading' : 'reloading';
|
|
325
|
+
case 'resolved':
|
|
326
|
+
return isResolved(state.stream()) ? 'resolved' : 'error';
|
|
327
|
+
default:
|
|
328
|
+
return state.status;
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
function isResolved(state) {
|
|
332
|
+
return state.error === undefined;
|
|
333
|
+
}
|
|
334
|
+
/**
|
|
335
|
+
* Creates a debug name object for an internal signal.
|
|
336
|
+
*/
|
|
337
|
+
function createDebugNameObject(resourceDebugName, internalSignalDebugName) {
|
|
338
|
+
return {
|
|
339
|
+
debugName: `Resource${resourceDebugName ? '#' + resourceDebugName : ''}.${internalSignalDebugName}`,
|
|
340
|
+
};
|
|
341
|
+
}
|
|
342
|
+
export function encapsulateResourceError(error) {
|
|
343
|
+
if (isErrorLike(error)) {
|
|
344
|
+
return error;
|
|
345
|
+
}
|
|
346
|
+
return new ResourceWrappedError(error);
|
|
347
|
+
}
|
|
348
|
+
export function isErrorLike(error) {
|
|
349
|
+
return (error instanceof Error ||
|
|
350
|
+
(typeof error === 'object' &&
|
|
351
|
+
typeof error.name === 'string' &&
|
|
352
|
+
typeof error.message === 'string'));
|
|
353
|
+
}
|
|
354
|
+
export class ResourceValueError extends Error {
|
|
355
|
+
constructor(error) {
|
|
356
|
+
super(isDevMode()
|
|
357
|
+
? `Resource is currently in an error state (see Error.cause for details): ${error.message}`
|
|
358
|
+
: error.message, { cause: error });
|
|
359
|
+
}
|
|
360
|
+
}
|
|
361
|
+
class ResourceWrappedError extends Error {
|
|
362
|
+
constructor(error) {
|
|
363
|
+
super(isDevMode()
|
|
364
|
+
? `Resource returned an error that's not an Error instance: ${String(error)}. Check this error's .cause for the actual error.`
|
|
365
|
+
: String(error), { cause: error });
|
|
366
|
+
}
|
|
367
|
+
}
|
|
368
|
+
/**
|
|
369
|
+
* Chains the value of another resource into the params of the current resource, returning the value
|
|
370
|
+
* of the other resource if it is available, or propagating the status to the current resource if it
|
|
371
|
+
* is not.
|
|
372
|
+
*/
|
|
373
|
+
export const paramsContext = {
|
|
374
|
+
chain(resource) {
|
|
375
|
+
switch (resource.status()) {
|
|
376
|
+
case 'idle':
|
|
377
|
+
throw ResourceParamsStatus.IDLE;
|
|
378
|
+
case 'error':
|
|
379
|
+
throw new ResourceDependencyError(resource);
|
|
380
|
+
case 'loading':
|
|
381
|
+
case 'reloading':
|
|
382
|
+
throw ResourceParamsStatus.LOADING;
|
|
383
|
+
}
|
|
384
|
+
return resource.value();
|
|
385
|
+
},
|
|
386
|
+
};
|
|
387
|
+
let inParamsFunction = false;
|
|
388
|
+
export function isInParamsFunction() {
|
|
389
|
+
return inParamsFunction;
|
|
390
|
+
}
|
|
391
|
+
export function setInParamsFunction(value) {
|
|
392
|
+
inParamsFunction = value;
|
|
393
|
+
}
|
|
394
|
+
export function invalidResourceCreationInParams() {
|
|
395
|
+
return new Error(`Cannot create a resource inside the \`params\` of another resource`);
|
|
396
|
+
}
|
|
397
|
+
export function rethrowFatalErrors(error) {
|
|
398
|
+
if (error instanceof Error &&
|
|
399
|
+
error.message === `Cannot create a resource inside the \`params\` of another resource`) {
|
|
400
|
+
throw error;
|
|
401
|
+
}
|
|
402
|
+
}
|
|
@@ -0,0 +1,50 @@
|
|
|
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
|
+
*/
|
|
8
|
+
/** biome-ignore-all lint/nursery/noExcessiveClassesPerFile: <explanation> */
|
|
9
|
+
/**
|
|
10
|
+
* Abstraction that encompasses any kind of effect that can be scheduled.
|
|
11
|
+
*/
|
|
12
|
+
export interface SchedulableEffect {
|
|
13
|
+
run(): void;
|
|
14
|
+
dirty: boolean;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* A scheduler which manages the execution of effects.
|
|
18
|
+
*/
|
|
19
|
+
export declare abstract class EffectScheduler {
|
|
20
|
+
abstract add(e: SchedulableEffect): void;
|
|
21
|
+
/**
|
|
22
|
+
* Schedule the given effect to be executed at a later time.
|
|
23
|
+
*
|
|
24
|
+
* It is an error to attempt to execute any effects synchronously during a scheduling operation.
|
|
25
|
+
*/
|
|
26
|
+
abstract schedule(e: SchedulableEffect): void;
|
|
27
|
+
/**
|
|
28
|
+
* Run any scheduled effects.
|
|
29
|
+
*/
|
|
30
|
+
abstract flush(): void;
|
|
31
|
+
/** Remove a scheduled effect */
|
|
32
|
+
abstract remove(e: SchedulableEffect): void;
|
|
33
|
+
}
|
|
34
|
+
export declare class MicrotaskEffectScheduler implements EffectScheduler {
|
|
35
|
+
private readonly queue;
|
|
36
|
+
private dirtyEffectCount;
|
|
37
|
+
private hasPendingFlush;
|
|
38
|
+
add(handle: SchedulableEffect): void;
|
|
39
|
+
schedule(handle: SchedulableEffect): void;
|
|
40
|
+
remove(handle: SchedulableEffect): void;
|
|
41
|
+
private enqueue;
|
|
42
|
+
/**
|
|
43
|
+
* Run all scheduled effects.
|
|
44
|
+
*
|
|
45
|
+
* Execution order of effects within the same zone is guaranteed to be FIFO, but there is no
|
|
46
|
+
* ordering guarantee between effects scheduled in different zones.
|
|
47
|
+
*/
|
|
48
|
+
flush(): void;
|
|
49
|
+
}
|
|
50
|
+
export declare const microtaskEffectScheduler: MicrotaskEffectScheduler;
|
|
@@ -0,0 +1,66 @@
|
|
|
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
|
+
*/
|
|
8
|
+
/**
|
|
9
|
+
* A scheduler which manages the execution of effects.
|
|
10
|
+
*/
|
|
11
|
+
export class EffectScheduler {
|
|
12
|
+
}
|
|
13
|
+
export class MicrotaskEffectScheduler {
|
|
14
|
+
queue = new Set();
|
|
15
|
+
dirtyEffectCount = 0;
|
|
16
|
+
hasPendingFlush = false;
|
|
17
|
+
add(handle) {
|
|
18
|
+
this.enqueue(handle);
|
|
19
|
+
this.schedule(handle);
|
|
20
|
+
}
|
|
21
|
+
schedule(handle) {
|
|
22
|
+
if (!handle.dirty) {
|
|
23
|
+
return;
|
|
24
|
+
}
|
|
25
|
+
this.dirtyEffectCount++;
|
|
26
|
+
if (!this.hasPendingFlush) {
|
|
27
|
+
this.hasPendingFlush = true;
|
|
28
|
+
queueMicrotask(() => {
|
|
29
|
+
this.hasPendingFlush = false;
|
|
30
|
+
this.flush();
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
remove(handle) {
|
|
35
|
+
this.queue.delete(handle);
|
|
36
|
+
if (handle.dirty) {
|
|
37
|
+
this.dirtyEffectCount--;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
enqueue(handle) {
|
|
41
|
+
if (this.queue.has(handle)) {
|
|
42
|
+
return;
|
|
43
|
+
}
|
|
44
|
+
this.queue.add(handle);
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Run all scheduled effects.
|
|
48
|
+
*
|
|
49
|
+
* Execution order of effects within the same zone is guaranteed to be FIFO, but there is no
|
|
50
|
+
* ordering guarantee between effects scheduled in different zones.
|
|
51
|
+
*/
|
|
52
|
+
flush() {
|
|
53
|
+
while (this.dirtyEffectCount > 0) {
|
|
54
|
+
for (const handle of this.queue) {
|
|
55
|
+
if (!handle.dirty) {
|
|
56
|
+
continue;
|
|
57
|
+
}
|
|
58
|
+
this.dirtyEffectCount--;
|
|
59
|
+
// TODO: what happens if this throws an error?
|
|
60
|
+
handle.run();
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
this.dirtyEffectCount = 0;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
export const microtaskEffectScheduler = new MicrotaskEffectScheduler();
|
|
@@ -3,27 +3,51 @@
|
|
|
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 type { ValueEqualityFn } from './
|
|
9
|
-
import type
|
|
10
|
-
|
|
11
|
-
export
|
|
12
|
-
|
|
13
|
-
|
|
8
|
+
import type { Signal, ValueEqualityFn } from './api.js';
|
|
9
|
+
import { type SignalGetter } from './primitive/signal.js';
|
|
10
|
+
/** Symbol used distinguish `WritableSignal` from other non-writable signals and functions. */
|
|
11
|
+
export declare const ɵWRITABLE_SIGNAL: unique symbol;
|
|
12
|
+
/**
|
|
13
|
+
* A `Signal` with a value that can be mutated via a setter interface.
|
|
14
|
+
*
|
|
15
|
+
* @publicApi 17.0
|
|
16
|
+
*/
|
|
17
|
+
export interface WritableSignal<T> extends Signal<T> {
|
|
18
|
+
[ɵWRITABLE_SIGNAL]: T;
|
|
19
|
+
/**
|
|
20
|
+
* Directly set the signal to a new value, and notify any dependents.
|
|
21
|
+
*/
|
|
22
|
+
set(value: T): void;
|
|
23
|
+
/**
|
|
24
|
+
* Update the value of the signal based on its current value, and
|
|
25
|
+
* notify any dependents.
|
|
26
|
+
*/
|
|
27
|
+
update(updateFn: (value: T) => T): void;
|
|
28
|
+
/**
|
|
29
|
+
* Returns a readonly version of this signal. Readonly signals can be accessed to read their value
|
|
30
|
+
* but can't be changed using set or update methods. The readonly signals do _not_ have
|
|
31
|
+
* any built-in mechanism that would prevent deep-mutation of their value.
|
|
32
|
+
*/
|
|
33
|
+
asReadonly(): Signal<T>;
|
|
14
34
|
}
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
export interface
|
|
19
|
-
|
|
35
|
+
/**
|
|
36
|
+
* Options passed to the `signal` creation function.
|
|
37
|
+
*/
|
|
38
|
+
export interface CreateSignalOptions<T> {
|
|
39
|
+
/**
|
|
40
|
+
* A comparison function which defines equality for signal values.
|
|
41
|
+
*/
|
|
42
|
+
equal?: ValueEqualityFn<T>;
|
|
43
|
+
/**
|
|
44
|
+
* A debug name for the signal. Used in Angular DevTools to identify the signal.
|
|
45
|
+
*/
|
|
46
|
+
debugName?: string;
|
|
20
47
|
}
|
|
21
48
|
/**
|
|
22
49
|
* Create a `Signal` that can be set or updated directly.
|
|
50
|
+
* @see [Angular Signals](guide/signals)
|
|
23
51
|
*/
|
|
24
|
-
export declare function
|
|
25
|
-
export declare function
|
|
26
|
-
export declare function signalGetFn<T>(this: SignalNode<T>): T;
|
|
27
|
-
export declare function signalSetFn<T>(node: SignalNode<T>, newValue: T): void;
|
|
28
|
-
export declare function signalUpdateFn<T>(node: SignalNode<T>, updater: (value: T) => T): void;
|
|
29
|
-
export declare const SIGNAL_NODE: SignalNode<unknown>;
|
|
52
|
+
export declare function signal<T>(initialValue: T, options?: CreateSignalOptions<T>): WritableSignal<T>;
|
|
53
|
+
export declare function signalAsReadonlyFn<T>(this: SignalGetter<T>): Signal<T>;
|
|
@@ -1,59 +1,39 @@
|
|
|
1
|
-
/* eslint-disable */
|
|
2
|
-
import { defaultEquals } from './equality.js';
|
|
3
|
-
import { throwInvalidWriteToSignalError } from './errors.js';
|
|
4
|
-
import { producerAccessed, producerIncrementEpoch, producerNotifyConsumers, producerUpdatesAllowed, REACTIVE_NODE, SIGNAL } from './graph.js';
|
|
5
1
|
/**
|
|
6
|
-
*
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright Google LLC All Rights Reserved.
|
|
7
4
|
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
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
|
|
10
7
|
*/
|
|
11
|
-
|
|
8
|
+
import { isDevMode } from '../../core.js';
|
|
9
|
+
import { SIGNAL } from '../symbol.js';
|
|
10
|
+
import { createSignal } from './primitive/signal.js';
|
|
11
|
+
/** Symbol used distinguish `WritableSignal` from other non-writable signals and functions. */
|
|
12
|
+
export const ɵWRITABLE_SIGNAL = /* @__PURE__ */ Symbol('WRITABLE_SIGNAL');
|
|
12
13
|
/**
|
|
13
14
|
* Create a `Signal` that can be set or updated directly.
|
|
15
|
+
* @see [Angular Signals](guide/signals)
|
|
14
16
|
*/
|
|
15
|
-
export function
|
|
16
|
-
const
|
|
17
|
-
|
|
18
|
-
const
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
const prev = postSignalSetFn;
|
|
27
|
-
postSignalSetFn = fn;
|
|
28
|
-
return prev;
|
|
29
|
-
}
|
|
30
|
-
export function signalGetFn() {
|
|
31
|
-
producerAccessed(this);
|
|
32
|
-
return this.value;
|
|
33
|
-
}
|
|
34
|
-
export function signalSetFn(node, newValue) {
|
|
35
|
-
if (!producerUpdatesAllowed()) {
|
|
36
|
-
throwInvalidWriteToSignalError();
|
|
37
|
-
}
|
|
38
|
-
if (!node.equal(node.value, newValue)) {
|
|
39
|
-
node.value = newValue;
|
|
40
|
-
signalValueChanged(node);
|
|
17
|
+
export function signal(initialValue, options) {
|
|
18
|
+
const [get, set, update] = createSignal(initialValue, options?.equal);
|
|
19
|
+
const signalFn = get;
|
|
20
|
+
const node = signalFn[SIGNAL];
|
|
21
|
+
signalFn.set = set;
|
|
22
|
+
signalFn.update = update;
|
|
23
|
+
signalFn.asReadonly = signalAsReadonlyFn.bind(signalFn);
|
|
24
|
+
if (isDevMode()) {
|
|
25
|
+
const debugName = options?.debugName;
|
|
26
|
+
node.debugName = debugName;
|
|
27
|
+
signalFn.toString = () => `[Signal${debugName ? ' (' + debugName + ')' : ''}: ${signalFn()}]`;
|
|
41
28
|
}
|
|
29
|
+
return signalFn;
|
|
42
30
|
}
|
|
43
|
-
export function
|
|
44
|
-
|
|
45
|
-
|
|
31
|
+
export function signalAsReadonlyFn() {
|
|
32
|
+
const node = this[SIGNAL];
|
|
33
|
+
if (node.readonlyFn === undefined) {
|
|
34
|
+
const readonlyFn = () => this();
|
|
35
|
+
readonlyFn[SIGNAL] = node;
|
|
36
|
+
node.readonlyFn = readonlyFn;
|
|
46
37
|
}
|
|
47
|
-
|
|
48
|
-
}
|
|
49
|
-
export const SIGNAL_NODE = {
|
|
50
|
-
...REACTIVE_NODE,
|
|
51
|
-
equal: defaultEquals,
|
|
52
|
-
value: undefined,
|
|
53
|
-
};
|
|
54
|
-
function signalValueChanged(node) {
|
|
55
|
-
node.version++;
|
|
56
|
-
producerIncrementEpoch();
|
|
57
|
-
producerNotifyConsumers(node);
|
|
58
|
-
postSignalSetFn?.();
|
|
38
|
+
return node.readonlyFn;
|
|
59
39
|
}
|