rxjs-interop.mjs 29 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795
  1. /**
  2. * @license Angular v16.2.9
  3. * (c) 2010-2022 Google LLC. https://angular.io/
  4. * License: MIT
  5. */
  6. import { assertInInjectionContext, inject, DestroyRef, Injector, effect, untracked as untracked$1, signal as signal$1, computed as computed$1 } from '@angular/core';
  7. import { Observable, ReplaySubject } from 'rxjs';
  8. import { takeUntil } from 'rxjs/operators';
  9. /**
  10. * Operator which completes the Observable when the calling context (component, directive, service,
  11. * etc) is destroyed.
  12. *
  13. * @param destroyRef optionally, the `DestroyRef` representing the current context. This can be
  14. * passed explicitly to use `takeUntilDestroyed` outside of an [injection
  15. * context](guide/dependency-injection-context). Otherwise, the current `DestroyRef` is injected.
  16. *
  17. * @developerPreview
  18. */
  19. function takeUntilDestroyed(destroyRef) {
  20. if (!destroyRef) {
  21. assertInInjectionContext(takeUntilDestroyed);
  22. destroyRef = inject(DestroyRef);
  23. }
  24. const destroyed$ = new Observable(observer => {
  25. const unregisterFn = destroyRef.onDestroy(observer.next.bind(observer));
  26. return unregisterFn;
  27. });
  28. return (source) => {
  29. return source.pipe(takeUntil(destroyed$));
  30. };
  31. }
  32. /**
  33. * Exposes the value of an Angular `Signal` as an RxJS `Observable`.
  34. *
  35. * The signal's value will be propagated into the `Observable`'s subscribers using an `effect`.
  36. *
  37. * `toObservable` must be called in an injection context unless an injector is provided via options.
  38. *
  39. * @developerPreview
  40. */
  41. function toObservable(source, options) {
  42. !options?.injector && assertInInjectionContext(toObservable);
  43. const injector = options?.injector ?? inject(Injector);
  44. const subject = new ReplaySubject(1);
  45. const watcher = effect(() => {
  46. let value;
  47. try {
  48. value = source();
  49. }
  50. catch (err) {
  51. untracked$1(() => subject.error(err));
  52. return;
  53. }
  54. untracked$1(() => subject.next(value));
  55. }, { injector, manualCleanup: true });
  56. injector.get(DestroyRef).onDestroy(() => {
  57. watcher.destroy();
  58. subject.complete();
  59. });
  60. return subject.asObservable();
  61. }
  62. /**
  63. * Base URL for the error details page.
  64. *
  65. * Keep this constant in sync across:
  66. * - packages/compiler-cli/src/ngtsc/diagnostics/src/error_details_base_url.ts
  67. * - packages/core/src/error_details_base_url.ts
  68. */
  69. const ERROR_DETAILS_PAGE_BASE_URL = 'https://angular.io/errors';
  70. /**
  71. * URL for the XSS security documentation.
  72. */
  73. const XSS_SECURITY_URL = 'https://g.co/ng/security#xss';
  74. /**
  75. * Class that represents a runtime error.
  76. * Formats and outputs the error message in a consistent way.
  77. *
  78. * Example:
  79. * ```
  80. * throw new RuntimeError(
  81. * RuntimeErrorCode.INJECTOR_ALREADY_DESTROYED,
  82. * ngDevMode && 'Injector has already been destroyed.');
  83. * ```
  84. *
  85. * Note: the `message` argument contains a descriptive error message as a string in development
  86. * mode (when the `ngDevMode` is defined). In production mode (after tree-shaking pass), the
  87. * `message` argument becomes `false`, thus we account for it in the typings and the runtime
  88. * logic.
  89. */
  90. class RuntimeError extends Error {
  91. constructor(code, message) {
  92. super(formatRuntimeError(code, message));
  93. this.code = code;
  94. }
  95. }
  96. /**
  97. * Called to format a runtime error.
  98. * See additional info on the `message` argument type in the `RuntimeError` class description.
  99. */
  100. function formatRuntimeError(code, message) {
  101. // Error code might be a negative number, which is a special marker that instructs the logic to
  102. // generate a link to the error details page on angular.io.
  103. // We also prepend `0` to non-compile-time errors.
  104. const fullCode = `NG0${Math.abs(code)}`;
  105. let errorMessage = `${fullCode}${message ? ': ' + message : ''}`;
  106. if (ngDevMode && code < 0) {
  107. const addPeriodSeparator = !errorMessage.match(/[.,;!?\n]$/);
  108. const separator = addPeriodSeparator ? '.' : '';
  109. errorMessage =
  110. `${errorMessage}${separator} Find more at ${ERROR_DETAILS_PAGE_BASE_URL}/${fullCode}`;
  111. }
  112. return errorMessage;
  113. }
  114. /**
  115. * Symbol used to tell `Signal`s apart from other functions.
  116. *
  117. * This can be used to auto-unwrap signals in various cases, or to auto-wrap non-signal values.
  118. */
  119. const SIGNAL = /* @__PURE__ */ Symbol('SIGNAL');
  120. /**
  121. * Checks if the given `value` is a reactive `Signal`.
  122. *
  123. * @developerPreview
  124. */
  125. function isSignal(value) {
  126. return typeof value === 'function' && value[SIGNAL] !== undefined;
  127. }
  128. /**
  129. * The default equality function used for `signal` and `computed`, which treats objects and arrays
  130. * as never equal, and all other primitive values using identity semantics.
  131. *
  132. * This allows signals to hold non-primitive values (arrays, objects, other collections) and still
  133. * propagate change notification upon explicit mutation without identity change.
  134. *
  135. * @developerPreview
  136. */
  137. function defaultEquals(a, b) {
  138. // `Object.is` compares two values using identity semantics which is desired behavior for
  139. // primitive values. If `Object.is` determines two values to be equal we need to make sure that
  140. // those don't represent objects (we want to make sure that 2 objects are always considered
  141. // "unequal"). The null check is needed for the special case of JavaScript reporting null values
  142. // as objects (`typeof null === 'object'`).
  143. return (a === null || typeof a !== 'object') && Object.is(a, b);
  144. }
  145. const _global = globalThis;
  146. function ngDevModeResetPerfCounters() {
  147. const locationString = typeof location !== 'undefined' ? location.toString() : '';
  148. const newCounters = {
  149. namedConstructors: locationString.indexOf('ngDevMode=namedConstructors') != -1,
  150. firstCreatePass: 0,
  151. tNode: 0,
  152. tView: 0,
  153. rendererCreateTextNode: 0,
  154. rendererSetText: 0,
  155. rendererCreateElement: 0,
  156. rendererAddEventListener: 0,
  157. rendererSetAttribute: 0,
  158. rendererRemoveAttribute: 0,
  159. rendererSetProperty: 0,
  160. rendererSetClassName: 0,
  161. rendererAddClass: 0,
  162. rendererRemoveClass: 0,
  163. rendererSetStyle: 0,
  164. rendererRemoveStyle: 0,
  165. rendererDestroy: 0,
  166. rendererDestroyNode: 0,
  167. rendererMoveNode: 0,
  168. rendererRemoveNode: 0,
  169. rendererAppendChild: 0,
  170. rendererInsertBefore: 0,
  171. rendererCreateComment: 0,
  172. hydratedNodes: 0,
  173. hydratedComponents: 0,
  174. dehydratedViewsRemoved: 0,
  175. dehydratedViewsCleanupRuns: 0,
  176. componentsSkippedHydration: 0,
  177. };
  178. // Make sure to refer to ngDevMode as ['ngDevMode'] for closure.
  179. const allowNgDevModeTrue = locationString.indexOf('ngDevMode=false') === -1;
  180. _global['ngDevMode'] = allowNgDevModeTrue && newCounters;
  181. return newCounters;
  182. }
  183. /**
  184. * This function checks to see if the `ngDevMode` has been set. If yes,
  185. * then we honor it, otherwise we default to dev mode with additional checks.
  186. *
  187. * The idea is that unless we are doing production build where we explicitly
  188. * set `ngDevMode == false` we should be helping the developer by providing
  189. * as much early warning and errors as possible.
  190. *
  191. * `ɵɵdefineComponent` is guaranteed to have been called before any component template functions
  192. * (and thus Ivy instructions), so a single initialization there is sufficient to ensure ngDevMode
  193. * is defined for the entire instruction set.
  194. *
  195. * When checking `ngDevMode` on toplevel, always init it before referencing it
  196. * (e.g. `((typeof ngDevMode === 'undefined' || ngDevMode) && initNgDevMode())`), otherwise you can
  197. * get a `ReferenceError` like in https://github.com/angular/angular/issues/31595.
  198. *
  199. * Details on possible values for `ngDevMode` can be found on its docstring.
  200. *
  201. * NOTE:
  202. * - changes to the `ngDevMode` name must be synced with `compiler-cli/src/tooling.ts`.
  203. */
  204. function initNgDevMode() {
  205. // The below checks are to ensure that calling `initNgDevMode` multiple times does not
  206. // reset the counters.
  207. // If the `ngDevMode` is not an object, then it means we have not created the perf counters
  208. // yet.
  209. if (typeof ngDevMode === 'undefined' || ngDevMode) {
  210. if (typeof ngDevMode !== 'object') {
  211. ngDevModeResetPerfCounters();
  212. }
  213. return typeof ngDevMode !== 'undefined' && !!ngDevMode;
  214. }
  215. return false;
  216. }
  217. // Required as the signals library is in a separate package, so we need to explicitly ensure the
  218. /**
  219. * The currently active consumer `ReactiveNode`, if running code in a reactive context.
  220. *
  221. * Change this via `setActiveConsumer`.
  222. */
  223. let activeConsumer = null;
  224. let inNotificationPhase = false;
  225. function setActiveConsumer(consumer) {
  226. const prev = activeConsumer;
  227. activeConsumer = consumer;
  228. return prev;
  229. }
  230. const REACTIVE_NODE = {
  231. version: 0,
  232. dirty: false,
  233. producerNode: undefined,
  234. producerLastReadVersion: undefined,
  235. producerIndexOfThis: undefined,
  236. nextProducerIndex: 0,
  237. liveConsumerNode: undefined,
  238. liveConsumerIndexOfThis: undefined,
  239. consumerAllowSignalWrites: false,
  240. consumerIsAlwaysLive: false,
  241. producerMustRecompute: () => false,
  242. producerRecomputeValue: () => { },
  243. consumerMarkedDirty: () => { },
  244. };
  245. /**
  246. * Called by implementations when a producer's signal is read.
  247. */
  248. function producerAccessed(node) {
  249. if (inNotificationPhase) {
  250. throw new Error(typeof ngDevMode !== 'undefined' && ngDevMode ?
  251. `Assertion error: signal read during notification phase` :
  252. '');
  253. }
  254. if (activeConsumer === null) {
  255. // Accessed outside of a reactive context, so nothing to record.
  256. return;
  257. }
  258. // This producer is the `idx`th dependency of `activeConsumer`.
  259. const idx = activeConsumer.nextProducerIndex++;
  260. assertConsumerNode(activeConsumer);
  261. if (idx < activeConsumer.producerNode.length && activeConsumer.producerNode[idx] !== node) {
  262. // There's been a change in producers since the last execution of `activeConsumer`.
  263. // `activeConsumer.producerNode[idx]` holds a stale dependency which will be be removed and
  264. // replaced with `this`.
  265. //
  266. // If `activeConsumer` isn't live, then this is a no-op, since we can replace the producer in
  267. // `activeConsumer.producerNode` directly. However, if `activeConsumer` is live, then we need
  268. // to remove it from the stale producer's `liveConsumer`s.
  269. if (consumerIsLive(activeConsumer)) {
  270. const staleProducer = activeConsumer.producerNode[idx];
  271. producerRemoveLiveConsumerAtIndex(staleProducer, activeConsumer.producerIndexOfThis[idx]);
  272. // At this point, the only record of `staleProducer` is the reference at
  273. // `activeConsumer.producerNode[idx]` which will be overwritten below.
  274. }
  275. }
  276. if (activeConsumer.producerNode[idx] !== node) {
  277. // We're a new dependency of the consumer (at `idx`).
  278. activeConsumer.producerNode[idx] = node;
  279. // If the active consumer is live, then add it as a live consumer. If not, then use 0 as a
  280. // placeholder value.
  281. activeConsumer.producerIndexOfThis[idx] =
  282. consumerIsLive(activeConsumer) ? producerAddLiveConsumer(node, activeConsumer, idx) : 0;
  283. }
  284. activeConsumer.producerLastReadVersion[idx] = node.version;
  285. }
  286. /**
  287. * Ensure this producer's `version` is up-to-date.
  288. */
  289. function producerUpdateValueVersion(node) {
  290. if (consumerIsLive(node) && !node.dirty) {
  291. // A live consumer will be marked dirty by producers, so a clean state means that its version
  292. // is guaranteed to be up-to-date.
  293. return;
  294. }
  295. if (!node.producerMustRecompute(node) && !consumerPollProducersForChange(node)) {
  296. // None of our producers report a change since the last time they were read, so no
  297. // recomputation of our value is necessary, and we can consider ourselves clean.
  298. node.dirty = false;
  299. return;
  300. }
  301. node.producerRecomputeValue(node);
  302. // After recomputing the value, we're no longer dirty.
  303. node.dirty = false;
  304. }
  305. /**
  306. * Propagate a dirty notification to live consumers of this producer.
  307. */
  308. function producerNotifyConsumers(node) {
  309. if (node.liveConsumerNode === undefined) {
  310. return;
  311. }
  312. // Prevent signal reads when we're updating the graph
  313. const prev = inNotificationPhase;
  314. inNotificationPhase = true;
  315. try {
  316. for (const consumer of node.liveConsumerNode) {
  317. if (!consumer.dirty) {
  318. consumerMarkDirty(consumer);
  319. }
  320. }
  321. }
  322. finally {
  323. inNotificationPhase = prev;
  324. }
  325. }
  326. /**
  327. * Whether this `ReactiveNode` in its producer capacity is currently allowed to initiate updates,
  328. * based on the current consumer context.
  329. */
  330. function producerUpdatesAllowed() {
  331. return activeConsumer?.consumerAllowSignalWrites !== false;
  332. }
  333. function consumerMarkDirty(node) {
  334. node.dirty = true;
  335. producerNotifyConsumers(node);
  336. node.consumerMarkedDirty?.(node);
  337. }
  338. /**
  339. * Prepare this consumer to run a computation in its reactive context.
  340. *
  341. * Must be called by subclasses which represent reactive computations, before those computations
  342. * begin.
  343. */
  344. function consumerBeforeComputation(node) {
  345. node && (node.nextProducerIndex = 0);
  346. return setActiveConsumer(node);
  347. }
  348. /**
  349. * Finalize this consumer's state after a reactive computation has run.
  350. *
  351. * Must be called by subclasses which represent reactive computations, after those computations
  352. * have finished.
  353. */
  354. function consumerAfterComputation(node, prevConsumer) {
  355. setActiveConsumer(prevConsumer);
  356. if (!node || node.producerNode === undefined || node.producerIndexOfThis === undefined ||
  357. node.producerLastReadVersion === undefined) {
  358. return;
  359. }
  360. if (consumerIsLive(node)) {
  361. // For live consumers, we need to remove the producer -> consumer edge for any stale producers
  362. // which weren't dependencies after the recomputation.
  363. for (let i = node.nextProducerIndex; i < node.producerNode.length; i++) {
  364. producerRemoveLiveConsumerAtIndex(node.producerNode[i], node.producerIndexOfThis[i]);
  365. }
  366. }
  367. // Truncate the producer tracking arrays.
  368. // Perf note: this is essentially truncating the length to `node.nextProducerIndex`, but
  369. // benchmarking has shown that individual pop operations are faster.
  370. while (node.producerNode.length > node.nextProducerIndex) {
  371. node.producerNode.pop();
  372. node.producerLastReadVersion.pop();
  373. node.producerIndexOfThis.pop();
  374. }
  375. }
  376. /**
  377. * Determine whether this consumer has any dependencies which have changed since the last time
  378. * they were read.
  379. */
  380. function consumerPollProducersForChange(node) {
  381. assertConsumerNode(node);
  382. // Poll producers for change.
  383. for (let i = 0; i < node.producerNode.length; i++) {
  384. const producer = node.producerNode[i];
  385. const seenVersion = node.producerLastReadVersion[i];
  386. // First check the versions. A mismatch means that the producer's value is known to have
  387. // changed since the last time we read it.
  388. if (seenVersion !== producer.version) {
  389. return true;
  390. }
  391. // The producer's version is the same as the last time we read it, but it might itself be
  392. // stale. Force the producer to recompute its version (calculating a new value if necessary).
  393. producerUpdateValueVersion(producer);
  394. // Now when we do this check, `producer.version` is guaranteed to be up to date, so if the
  395. // versions still match then it has not changed since the last time we read it.
  396. if (seenVersion !== producer.version) {
  397. return true;
  398. }
  399. }
  400. return false;
  401. }
  402. /**
  403. * Disconnect this consumer from the graph.
  404. */
  405. function consumerDestroy(node) {
  406. assertConsumerNode(node);
  407. if (consumerIsLive(node)) {
  408. // Drop all connections from the graph to this node.
  409. for (let i = 0; i < node.producerNode.length; i++) {
  410. producerRemoveLiveConsumerAtIndex(node.producerNode[i], node.producerIndexOfThis[i]);
  411. }
  412. }
  413. // Truncate all the arrays to drop all connection from this node to the graph.
  414. node.producerNode.length = node.producerLastReadVersion.length = node.producerIndexOfThis.length =
  415. 0;
  416. if (node.liveConsumerNode) {
  417. node.liveConsumerNode.length = node.liveConsumerIndexOfThis.length = 0;
  418. }
  419. }
  420. /**
  421. * Add `consumer` as a live consumer of this node.
  422. *
  423. * Note that this operation is potentially transitive. If this node becomes live, then it becomes
  424. * a live consumer of all of its current producers.
  425. */
  426. function producerAddLiveConsumer(node, consumer, indexOfThis) {
  427. assertProducerNode(node);
  428. assertConsumerNode(node);
  429. if (node.liveConsumerNode.length === 0) {
  430. // When going from 0 to 1 live consumers, we become a live consumer to our producers.
  431. for (let i = 0; i < node.producerNode.length; i++) {
  432. node.producerIndexOfThis[i] = producerAddLiveConsumer(node.producerNode[i], node, i);
  433. }
  434. }
  435. node.liveConsumerIndexOfThis.push(indexOfThis);
  436. return node.liveConsumerNode.push(consumer) - 1;
  437. }
  438. /**
  439. * Remove the live consumer at `idx`.
  440. */
  441. function producerRemoveLiveConsumerAtIndex(node, idx) {
  442. assertProducerNode(node);
  443. assertConsumerNode(node);
  444. if (typeof ngDevMode !== 'undefined' && ngDevMode && idx >= node.liveConsumerNode.length) {
  445. throw new Error(`Assertion error: active consumer index ${idx} is out of bounds of ${node.liveConsumerNode.length} consumers)`);
  446. }
  447. if (node.liveConsumerNode.length === 1) {
  448. // When removing the last live consumer, we will no longer be live. We need to remove
  449. // ourselves from our producers' tracking (which may cause consumer-producers to lose
  450. // liveness as well).
  451. for (let i = 0; i < node.producerNode.length; i++) {
  452. producerRemoveLiveConsumerAtIndex(node.producerNode[i], node.producerIndexOfThis[i]);
  453. }
  454. }
  455. // Move the last value of `liveConsumers` into `idx`. Note that if there's only a single
  456. // live consumer, this is a no-op.
  457. const lastIdx = node.liveConsumerNode.length - 1;
  458. node.liveConsumerNode[idx] = node.liveConsumerNode[lastIdx];
  459. node.liveConsumerIndexOfThis[idx] = node.liveConsumerIndexOfThis[lastIdx];
  460. // Truncate the array.
  461. node.liveConsumerNode.length--;
  462. node.liveConsumerIndexOfThis.length--;
  463. // If the index is still valid, then we need to fix the index pointer from the producer to this
  464. // consumer, and update it from `lastIdx` to `idx` (accounting for the move above).
  465. if (idx < node.liveConsumerNode.length) {
  466. const idxProducer = node.liveConsumerIndexOfThis[idx];
  467. const consumer = node.liveConsumerNode[idx];
  468. assertConsumerNode(consumer);
  469. consumer.producerIndexOfThis[idxProducer] = idx;
  470. }
  471. }
  472. function consumerIsLive(node) {
  473. return node.consumerIsAlwaysLive || (node?.liveConsumerNode?.length ?? 0) > 0;
  474. }
  475. function assertConsumerNode(node) {
  476. node.producerNode ??= [];
  477. node.producerIndexOfThis ??= [];
  478. node.producerLastReadVersion ??= [];
  479. }
  480. function assertProducerNode(node) {
  481. node.liveConsumerNode ??= [];
  482. node.liveConsumerIndexOfThis ??= [];
  483. }
  484. /**
  485. * Create a computed `Signal` which derives a reactive value from an expression.
  486. *
  487. * @developerPreview
  488. */
  489. function computed(computation, options) {
  490. const node = Object.create(COMPUTED_NODE);
  491. node.computation = computation;
  492. options?.equal && (node.equal = options.equal);
  493. const computed = () => {
  494. // Check if the value needs updating before returning it.
  495. producerUpdateValueVersion(node);
  496. // Record that someone looked at this signal.
  497. producerAccessed(node);
  498. if (node.value === ERRORED) {
  499. throw node.error;
  500. }
  501. return node.value;
  502. };
  503. computed[SIGNAL] = node;
  504. return computed;
  505. }
  506. /**
  507. * A dedicated symbol used before a computed value has been calculated for the first time.
  508. * Explicitly typed as `any` so we can use it as signal's value.
  509. */
  510. const UNSET = /* @__PURE__ */ Symbol('UNSET');
  511. /**
  512. * A dedicated symbol used in place of a computed signal value to indicate that a given computation
  513. * is in progress. Used to detect cycles in computation chains.
  514. * Explicitly typed as `any` so we can use it as signal's value.
  515. */
  516. const COMPUTING = /* @__PURE__ */ Symbol('COMPUTING');
  517. /**
  518. * A dedicated symbol used in place of a computed signal value to indicate that a given computation
  519. * failed. The thrown error is cached until the computation gets dirty again.
  520. * Explicitly typed as `any` so we can use it as signal's value.
  521. */
  522. const ERRORED = /* @__PURE__ */ Symbol('ERRORED');
  523. // Note: Using an IIFE here to ensure that the spread assignment is not considered
  524. // a side-effect, ending up preserving `COMPUTED_NODE` and `REACTIVE_NODE`.
  525. // TODO: remove when https://github.com/evanw/esbuild/issues/3392 is resolved.
  526. const COMPUTED_NODE = /* @__PURE__ */ (() => {
  527. return {
  528. ...REACTIVE_NODE,
  529. value: UNSET,
  530. dirty: true,
  531. error: null,
  532. equal: defaultEquals,
  533. producerMustRecompute(node) {
  534. // Force a recomputation if there's no current value, or if the current value is in the
  535. // process of being calculated (which should throw an error).
  536. return node.value === UNSET || node.value === COMPUTING;
  537. },
  538. producerRecomputeValue(node) {
  539. if (node.value === COMPUTING) {
  540. // Our computation somehow led to a cyclic read of itself.
  541. throw new Error('Detected cycle in computations.');
  542. }
  543. const oldValue = node.value;
  544. node.value = COMPUTING;
  545. const prevConsumer = consumerBeforeComputation(node);
  546. let newValue;
  547. try {
  548. newValue = node.computation();
  549. }
  550. catch (err) {
  551. newValue = ERRORED;
  552. node.error = err;
  553. }
  554. finally {
  555. consumerAfterComputation(node, prevConsumer);
  556. }
  557. if (oldValue !== UNSET && oldValue !== ERRORED && newValue !== ERRORED &&
  558. node.equal(oldValue, newValue)) {
  559. // No change to `valueVersion` - old and new values are
  560. // semantically equivalent.
  561. node.value = oldValue;
  562. return;
  563. }
  564. node.value = newValue;
  565. node.version++;
  566. },
  567. };
  568. })();
  569. function defaultThrowError() {
  570. throw new Error();
  571. }
  572. let throwInvalidWriteToSignalErrorFn = defaultThrowError;
  573. function throwInvalidWriteToSignalError() {
  574. throwInvalidWriteToSignalErrorFn();
  575. }
  576. function setThrowInvalidWriteToSignalError(fn) {
  577. throwInvalidWriteToSignalErrorFn = fn;
  578. }
  579. /**
  580. * If set, called after `WritableSignal`s are updated.
  581. *
  582. * This hook can be used to achieve various effects, such as running effects synchronously as part
  583. * of setting a signal.
  584. */
  585. let postSignalSetFn = null;
  586. /**
  587. * Create a `Signal` that can be set or updated directly.
  588. *
  589. * @developerPreview
  590. */
  591. function signal(initialValue, options) {
  592. const node = Object.create(SIGNAL_NODE);
  593. node.value = initialValue;
  594. options?.equal && (node.equal = options.equal);
  595. function signalFn() {
  596. producerAccessed(node);
  597. return node.value;
  598. }
  599. signalFn.set = signalSetFn;
  600. signalFn.update = signalUpdateFn;
  601. signalFn.mutate = signalMutateFn;
  602. signalFn.asReadonly = signalAsReadonlyFn;
  603. signalFn[SIGNAL] = node;
  604. return signalFn;
  605. }
  606. function setPostSignalSetFn(fn) {
  607. const prev = postSignalSetFn;
  608. postSignalSetFn = fn;
  609. return prev;
  610. }
  611. // Note: Using an IIFE here to ensure that the spread assignment is not considered
  612. // a side-effect, ending up preserving `COMPUTED_NODE` and `REACTIVE_NODE`.
  613. // TODO: remove when https://github.com/evanw/esbuild/issues/3392 is resolved.
  614. const SIGNAL_NODE = /* @__PURE__ */ (() => {
  615. return {
  616. ...REACTIVE_NODE,
  617. equal: defaultEquals,
  618. readonlyFn: undefined,
  619. };
  620. })();
  621. function signalValueChanged(node) {
  622. node.version++;
  623. producerNotifyConsumers(node);
  624. postSignalSetFn?.();
  625. }
  626. function signalSetFn(newValue) {
  627. const node = this[SIGNAL];
  628. if (!producerUpdatesAllowed()) {
  629. throwInvalidWriteToSignalError();
  630. }
  631. if (!node.equal(node.value, newValue)) {
  632. node.value = newValue;
  633. signalValueChanged(node);
  634. }
  635. }
  636. function signalUpdateFn(updater) {
  637. if (!producerUpdatesAllowed()) {
  638. throwInvalidWriteToSignalError();
  639. }
  640. signalSetFn.call(this, updater(this[SIGNAL].value));
  641. }
  642. function signalMutateFn(mutator) {
  643. const node = this[SIGNAL];
  644. if (!producerUpdatesAllowed()) {
  645. throwInvalidWriteToSignalError();
  646. }
  647. // Mutate bypasses equality checks as it's by definition changing the value.
  648. mutator(node.value);
  649. signalValueChanged(node);
  650. }
  651. function signalAsReadonlyFn() {
  652. const node = this[SIGNAL];
  653. if (node.readonlyFn === undefined) {
  654. const readonlyFn = () => this();
  655. readonlyFn[SIGNAL] = node;
  656. node.readonlyFn = readonlyFn;
  657. }
  658. return node.readonlyFn;
  659. }
  660. /**
  661. * Execute an arbitrary function in a non-reactive (non-tracking) context. The executed function
  662. * can, optionally, return a value.
  663. *
  664. * @developerPreview
  665. */
  666. function untracked(nonReactiveReadsFn) {
  667. const prevConsumer = setActiveConsumer(null);
  668. // We are not trying to catch any particular errors here, just making sure that the consumers
  669. // stack is restored in case of errors.
  670. try {
  671. return nonReactiveReadsFn();
  672. }
  673. finally {
  674. setActiveConsumer(prevConsumer);
  675. }
  676. }
  677. function watch(fn, schedule, allowSignalWrites) {
  678. const node = Object.create(WATCH_NODE);
  679. if (allowSignalWrites) {
  680. node.consumerAllowSignalWrites = true;
  681. }
  682. node.fn = fn;
  683. node.schedule = schedule;
  684. const registerOnCleanup = (cleanupFn) => {
  685. node.cleanupFn = cleanupFn;
  686. };
  687. const run = () => {
  688. node.dirty = false;
  689. if (node.hasRun && !consumerPollProducersForChange(node)) {
  690. return;
  691. }
  692. node.hasRun = true;
  693. const prevConsumer = consumerBeforeComputation(node);
  694. try {
  695. node.cleanupFn();
  696. node.cleanupFn = NOOP_CLEANUP_FN;
  697. node.fn(registerOnCleanup);
  698. }
  699. finally {
  700. consumerAfterComputation(node, prevConsumer);
  701. }
  702. };
  703. node.ref = {
  704. notify: () => consumerMarkDirty(node),
  705. run,
  706. cleanup: () => node.cleanupFn(),
  707. };
  708. return node.ref;
  709. }
  710. const NOOP_CLEANUP_FN = () => { };
  711. // Note: Using an IIFE here to ensure that the spread assignment is not considered
  712. // a side-effect, ending up preserving `COMPUTED_NODE` and `REACTIVE_NODE`.
  713. // TODO: remove when https://github.com/evanw/esbuild/issues/3392 is resolved.
  714. const WATCH_NODE = /* @__PURE__ */ (() => {
  715. return {
  716. ...REACTIVE_NODE,
  717. consumerIsAlwaysLive: true,
  718. consumerAllowSignalWrites: false,
  719. consumerMarkedDirty: (node) => {
  720. node.schedule(node.ref);
  721. },
  722. hasRun: false,
  723. cleanupFn: NOOP_CLEANUP_FN,
  724. };
  725. })();
  726. function setAlternateWeakRefImpl(impl) {
  727. // TODO: remove this function
  728. }
  729. function toSignal(source, options) {
  730. const requiresCleanup = !options?.manualCleanup;
  731. requiresCleanup && !options?.injector && assertInInjectionContext(toSignal);
  732. const cleanupRef = requiresCleanup ? options?.injector?.get(DestroyRef) ?? inject(DestroyRef) : null;
  733. // Note: T is the Observable value type, and U is the initial value type. They don't have to be
  734. // the same - the returned signal gives values of type `T`.
  735. let state;
  736. if (options?.requireSync) {
  737. // Initially the signal is in a `NoValue` state.
  738. state = signal$1({ kind: 0 /* StateKind.NoValue */ });
  739. }
  740. else {
  741. // If an initial value was passed, use it. Otherwise, use `undefined` as the initial value.
  742. state = signal$1({ kind: 1 /* StateKind.Value */, value: options?.initialValue });
  743. }
  744. untracked(() => {
  745. const sub = source.subscribe({
  746. next: value => state.set({ kind: 1 /* StateKind.Value */, value }),
  747. error: error => state.set({ kind: 2 /* StateKind.Error */, error }),
  748. // Completion of the Observable is meaningless to the signal. Signals don't have a concept of
  749. // "complete".
  750. });
  751. if (ngDevMode && options?.requireSync && state().kind === 0 /* StateKind.NoValue */) {
  752. throw new RuntimeError(601 /* RuntimeErrorCode.REQUIRE_SYNC_WITHOUT_SYNC_EMIT */, '`toSignal()` called with `requireSync` but `Observable` did not emit synchronously.');
  753. }
  754. // Unsubscribe when the current context is destroyed, if requested.
  755. cleanupRef?.onDestroy(sub.unsubscribe.bind(sub));
  756. });
  757. // The actual returned signal is a `computed` of the `State` signal, which maps the various states
  758. // to either values or errors.
  759. return computed$1(() => {
  760. const current = state();
  761. switch (current.kind) {
  762. case 1 /* StateKind.Value */:
  763. return current.value;
  764. case 2 /* StateKind.Error */:
  765. throw current.error;
  766. case 0 /* StateKind.NoValue */:
  767. // This shouldn't really happen because the error is thrown on creation.
  768. // TODO(alxhub): use a RuntimeError when we finalize the error semantics
  769. throw new RuntimeError(601 /* RuntimeErrorCode.REQUIRE_SYNC_WITHOUT_SYNC_EMIT */, '`toSignal()` called with `requireSync` but `Observable` did not emit synchronously.');
  770. }
  771. });
  772. }
  773. /**
  774. * Generated bundle index. Do not edit.
  775. */
  776. export { takeUntilDestroyed, toObservable, toSignal };
  777. //# sourceMappingURL=rxjs-interop.mjs.map