@asaidimu/utils-artifacts 3.1.7 → 4.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -3,10 +3,12 @@
3
3
  A powerful, reactive dependency injection container for managing application artifacts and their lifecycles. It provides a robust framework for building modular, maintainable, and highly responsive applications by decoupling components and managing their state-driven dependencies.
4
4
 
5
5
  [![npm version](https://img.shields.io/npm/v/@asaidimu/utils-artifacts.svg?style=flat-square)](https://www.npmjs.com/package/@asaidimu/utils-artifacts)
6
- [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg?style=flat-square)](https://opensource.org/licenses/MIT)
7
- [![Build Status](https://img.shields.io/github/actions/workflow/status/asaidimu/utils/ci.yml?branch=main&style=flat-square)](https://github.com/asaidimu/utils/actions?query=workflow%3ACI)
6
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg?style=flat-square)](https://github.com/asaidimu/erp-utils/blob/main/LICENSE)
7
+ [![Build Status](https://img.shields.io/github/actions/workflow/status/asaidimu/erp-utils/ci.yml?branch=main&label=Build&style=flat-square)](https://github.com/asaidimu/erp-utils/actions?query=workflow%3ACI)
8
8
 
9
- ## 🚀 Quick Links
9
+ ---
10
+
11
+ ### 🚀 Quick Links
10
12
 
11
13
  * [Overview & Features](#-overview--features)
12
14
  * [Installation & Setup](#-installation--setup)
@@ -16,7 +18,7 @@ A powerful, reactive dependency injection container for managing application art
16
18
  * [Artifact-to-Artifact Dependencies](#artifact-to-artifact-dependencies)
17
19
  * [Lifecycle Hooks: `onCleanup` & `onDispose`](#lifecycle-hooks-oncleanup--ondispose)
18
20
  * [Watching Artifacts for Changes](#watching-artifacts-for-changes)
19
- * [Updating Intermediate Values with `ctx.update`](#updating-intermediate-values-with-ctxupdate)
21
+ * [Streaming Artifacts with `ctx.stream()`](#streaming-artifacts-with-ctxstream)
20
22
  * [Debugging Information](#debugging-information)
21
23
  * [Project Architecture](#-project-architecture)
22
24
  * [Development & Contributing](#-development--contributing)
@@ -35,16 +37,16 @@ This package is ideal for building complex, event-driven systems, front-end appl
35
37
 
36
38
  ### ✨ Key Features
37
39
 
38
- * **Reactive Dependency Injection**: Automatically invalidates and rebuilds artifacts when their declared dependencies (other artifacts or global state slices) change.
40
+ * **Reactive Dependency Injection**: Automatically invalidates and rebuilds `Singleton` artifacts when their declared dependencies (other artifacts or global state slices) change, ensuring reactive updates throughout your application.
39
41
  * **Singleton & Transient Scopes**: Control whether an artifact is instantiated once and reused (`Singleton`) or created fresh on every request (`Transient`).
40
- * **State-Driven Invalidation with Debouncing**: Integrates with a `DataStore` to track state dependencies, triggering artifact rebuilds only when relevant state paths change, with configurable debouncing.
41
- * **Fine-grained Dependency Tracking**: Explicitly declare dependencies on other artifacts using `ctx.resolve()` and on state slices using `ctx.select()`.
42
- * **Robust Error Handling**: Built-in detection and specific error types for circular dependencies, missing artifacts, and illegal operations, distinguishing between `system` (structural) and `external` (runtime) errors.
43
- * **Lifecycle Hooks**: Register `onCleanup` callbacks for instance-specific resource release and `onDispose` for final resource teardown.
44
- * **Asynchronous Artifact Resolution**: Supports `async`/`await` in artifact factories, with configurable retries for external failures and timeouts.
45
- * **`ArtifactObserver` for Reactive Observation**: Observe changes to an artifact's resolved value over time, subscribing to updates without repeatedly resolving.
46
- * **Live Updates via `ctx.update()`**: Singleton artifacts can proactively update their value from within their factory, immediately notifying dependents and watchers.
47
- * **Comprehensive Debugging Info**: `debugInfo()` provides a snapshot of the container's state, including artifact statuses, dependencies, and dependents.
42
+ * **State-Driven Invalidation with Debouncing**: Integrates with a `DataStore` (e.g., `@asaidimu/utils-store`) to track state dependencies, triggering artifact rebuilds only when relevant state paths change, with configurable debouncing to prevent excessive rebuilds.
43
+ * **Fine-grained Dependency Tracking**: Explicitly declare dependencies on other artifacts using `ctx.use(({ resolve }) => resolve('someKey'))` and on state slices using `ctx.use(({ select }) => select(state => state.somePath))`.
44
+ * **Robust Error Handling**: Built-in detection and specific error types for circular dependencies (`CircularDependencyError`), missing artifacts (`ArtifactNotFoundError`), illegal operations (`IllegalScopeError`), and timeouts (`TimeoutError`), distinguishing between `system` (structural) and `external` (runtime) errors.
45
+ * **Lifecycle Hooks**: Register `onCleanup` callbacks for instance-specific resource release (executed before a `Singleton` instance is rebuilt) and `onDispose` for final resource teardown (executed when the artifact is unregistered).
46
+ * **Asynchronous Artifact Resolution**: Supports `async`/`await` in artifact factories, with configurable `retries` for external failures and `timeout` settings for long-running operations.
47
+ * **`ArtifactObserver` for Reactive Observation**: Use `container.watch(key)` to get an observer that allows subscribing to an artifact's resolved value over time, providing reactive updates to UI components or other systems.
48
+ * **Live Streaming with `ctx.stream()`**: `Singleton` artifacts can emit multiple values over their lifetime from within their factory using `ctx.stream()`, immediately notifying dependents and watchers of new data. This is ideal for continuous data sources like WebSockets or background processes.
49
+ * **Comprehensive Debugging Info**: `container.debugInfo()` provides a runtime snapshot of the container's state, including artifact statuses, dependencies (artifact-to-artifact and state-to-artifact), and dependents.
48
50
 
49
51
  ---
50
52
 
@@ -52,9 +54,9 @@ This package is ideal for building complex, event-driven systems, front-end appl
52
54
 
53
55
  ### Prerequisites
54
56
 
55
- * Node.js (v18 or higher recommended)
56
- * A package manager like Bun, npm, or Yarn.
57
- * An implementation of the `DataStore` interface for reactive state management. We highly recommend using [`@asaidimu/utils-store`](https://www.npmjs.com/package/@asaidimu/utils-store).
57
+ * **Node.js**: (v18 or higher recommended)
58
+ * **Package Manager**: `Bun`, `npm`, or `Yarn`.
59
+ * **DataStore Implementation**: An external implementation of a `DataStore` interface with `get` and `watch` methods. We highly recommend using [`@asaidimu/utils-store`](https://www.npmjs.com/package/@asaidimu/utils-store) as it is designed for seamless integration.
58
60
 
59
61
  ### Installation Steps
60
62
 
@@ -73,13 +75,13 @@ yarn add @asaidimu/utils-artifacts
73
75
 
74
76
  ### Configuration
75
77
 
76
- To use the `ArtifactContainer`, you need to initialize it with an object that provides `get` and `watch` methods conforming to the `DataStore` interface. This allows the container to read the current application state and subscribe to state changes for reactive invalidation.
78
+ To use the `ArtifactContainer`, you need to initialize it with an object that provides `watch` (for reactive subscriptions to state changes), `get` (for reading the current state), and `set` (for allowing artifacts to update state, e.g., via `ctx.stream` or `ctx.set`).
77
79
 
78
80
  If you're using `@asaidimu/utils-store` as your `DataStore`:
79
81
 
80
82
  ```typescript
81
- import { ArtifactContainer, ArtifactScopes } from '@asaidimu/utils-artifacts';
82
- import { ReactiveDataStore } from '@asaidimu/utils-store'; // Assuming this provides DataStore interface
83
+ import { ArtifactContainer } from '@asaidimu/utils-artifacts';
84
+ import { ReactiveDataStore } from '@asaidimu/utils-store'; // Recommended DataStore
83
85
 
84
86
  interface AppState {
85
87
  config: { theme: string; debugMode: boolean };
@@ -96,6 +98,7 @@ const initialAppState: AppState = {
96
98
  const store = new ReactiveDataStore<AppState>(initialAppState);
97
99
 
98
100
  // Initialize the ArtifactContainer
101
+ // The generic arguments <TRegistry, TState> are inferred or can be explicitly provided.
99
102
  const container = new ArtifactContainer<Record<string, any>, AppState>(store);
100
103
 
101
104
  // Now you can start registering artifacts
@@ -111,6 +114,7 @@ import { ArtifactContainer } from '@asaidimu/utils-artifacts';
111
114
  const mockStore = {
112
115
  get: () => ({}), // Returns an empty object for state
113
116
  watch: () => () => {}, // Returns a no-op unsubscribe function
117
+ set: async () => ({}), // Returns a no-op set function
114
118
  };
115
119
  const container = new ArtifactContainer(mockStore);
116
120
  console.log('ArtifactContainer initialized successfully!');
@@ -123,11 +127,11 @@ console.log('ArtifactContainer initialized successfully!');
123
127
 
124
128
  ### Basic Artifacts (Singleton & Transient)
125
129
 
126
- Artifacts can be registered with different scopes, controlling their lifecycle.
130
+ Artifacts can be registered with different scopes, controlling their lifecycle and instantiation behavior.
127
131
 
128
132
  #### Singleton Scope
129
133
 
130
- A singleton artifact is created only once. Subsequent `resolve` calls return the same instance.
134
+ A `Singleton` artifact is created only once. Subsequent `resolve` calls or reactive updates will return or use the same instance, unless its dependencies change, which triggers a rebuild. This is the default scope.
131
135
 
132
136
  ```typescript
133
137
  import { ArtifactContainer, ArtifactScopes } from '@asaidimu/utils-artifacts';
@@ -149,7 +153,7 @@ container.register({
149
153
  };
150
154
  },
151
155
  scope: ArtifactScopes.Singleton,
152
- lazy: false, // Build immediately on registration
156
+ lazy: false, // Build immediately on registration if it's a Singleton
153
157
  });
154
158
 
155
159
  async function runSingletonExample() {
@@ -174,7 +178,7 @@ runSingletonExample();
174
178
 
175
179
  #### Transient Scope
176
180
 
177
- A transient artifact creates a new instance every time it is resolved.
181
+ A `Transient` artifact creates a new instance every time it is resolved. These artifacts do not participate in caching, reactive invalidation, or lifecycle hooks like `onCleanup` and `onDispose`.
178
182
 
179
183
  ```typescript
180
184
  import { ArtifactContainer, ArtifactScopes } from '@asaidimu/utils-artifacts';
@@ -217,7 +221,7 @@ runTransientExample();
217
221
 
218
222
  ### Reactive State Dependencies
219
223
 
220
- Artifacts can react to changes in your global `DataStore` state by using `ctx.select()` within their factory's `use` block.
224
+ Artifacts can react to changes in your global `DataStore` state by using `ctx.use(({ select }) => ...)` within their factory. When the selected state slice changes, the `Singleton` artifact is automatically invalidated and rebuilt.
221
225
 
222
226
  ```typescript
223
227
  import { ArtifactContainer, ArtifactScopes } from '@asaidimu/utils-artifacts';
@@ -287,7 +291,7 @@ runReactiveStateExample();
287
291
 
288
292
  ### Artifact-to-Artifact Dependencies
289
293
 
290
- Artifacts can depend on other artifacts using `ctx.resolve()` within their factory's `use` block. Changes to a dependency will invalidate the dependent artifact.
294
+ Artifacts can depend on other artifacts using `ctx.use(({ resolve }) => resolve('otherArtifactKey'))` or `ctx.use(({ require }) => require('otherArtifactKey'))` within their factory. Changes (invalidations) to a dependency will automatically invalidate the dependent artifact.
291
295
 
292
296
  ```typescript
293
297
  import { ArtifactContainer, ArtifactScopes } from '@asaidimu/utils-artifacts';
@@ -314,10 +318,10 @@ container.register({
314
318
  factory: async ({ use }) => {
315
319
  userRepositoryBuilds++;
316
320
  console.log(`Building UserRepository (${userRepositoryBuilds})...`);
317
- const db = await use((ctx) => ctx.resolve('dbConnection'));
321
+ const db = await use((ctx) => ctx.require('dbConnection')); // Using 'require' for direct instance
318
322
  return {
319
- dbClient: db.instance?.client,
320
- findUser: (id: string) => `User ${id} from ${db.instance?.client}`
323
+ dbClient: db.client,
324
+ findUser: (id: string) => `User ${id} from ${db.client}`
321
325
  };
322
326
  },
323
327
  scope: ArtifactScopes.Singleton,
@@ -357,10 +361,10 @@ runArtifactDependencyExample();
357
361
 
358
362
  ### Lifecycle Hooks: `onCleanup` & `onDispose`
359
363
 
360
- Artifacts can register cleanup functions to manage resources.
364
+ Artifacts can register cleanup functions to manage resources, ensuring proper release when instances are no longer needed.
361
365
 
362
- * `onCleanup`: Executed *before* a new instance of a `Singleton` artifact is built (due to invalidation) or when the artifact is explicitly unregistered. Ideal for releasing resources tied to a specific instance.
363
- * `onDispose`: Executed *only* when the artifact is unregistered or the container itself is disposed. Ideal for final, permanent resource release.
366
+ * `onCleanup(callback)`: Registered callbacks are executed *before* a `Singleton` artifact's instance is replaced (due to invalidation/rebuild). Ideal for releasing resources tied to that specific *instance* (e.g., stopping timers, closing old connections).
367
+ * `onDispose(callback)`: Registered callbacks are executed *only* when the artifact is explicitly `unregister()`ed from the container or the container itself is `dispose()`d. Ideal for final, permanent resource release associated with the *artifact definition itself*.
364
368
 
365
369
  ```typescript
366
370
  import { ArtifactContainer, ArtifactScopes } from '@asaidimu/utils-artifacts';
@@ -388,7 +392,7 @@ container.register({
388
392
 
389
393
  onDispose(() => {
390
394
  console.log('Disposing Ticker artifact (final cleanup)...');
391
- // Additional final cleanup could go here
395
+ // Additional final cleanup could go here, e.g., closing file handles
392
396
  });
393
397
 
394
398
  return { start: Date.now() };
@@ -421,10 +425,10 @@ runLifecycleHooksExample();
421
425
 
422
426
  ### Watching Artifacts for Changes
423
427
 
424
- The `watch()` method provides a reactive way to observe an artifact's value without repeatedly calling `resolve()`. This is particularly useful for UI frameworks or reactive data flows.
428
+ The `container.watch(key)` method provides a reactive way to observe an artifact's value without repeatedly calling `resolve()`. This is particularly useful for UI frameworks or reactive data flows where you want to be notified of changes as they occur.
425
429
 
426
430
  ```typescript
427
- import { ArtifactContainer, ArtifactScopes } from '@asaidimu/utils-artifacts';
431
+ import { ArtifactContainer, ArtifactScopes, WatcherDisposedError } from '@asaidimu/utils-artifacts';
428
432
  import { ReactiveDataStore } from '@asaidimu/utils-store';
429
433
 
430
434
  interface AppState {
@@ -434,11 +438,13 @@ interface AppState {
434
438
  const store = new ReactiveDataStore<AppState>({ counter: 0 });
435
439
  const container = new ArtifactContainer<any, AppState>(store);
436
440
 
441
+ let reactiveCounterBuilds = 0;
437
442
  container.register({
438
443
  key: 'reactiveCounter',
439
444
  factory: async ({ use }) => {
445
+ reactiveCounterBuilds++;
440
446
  const count = await use((ctx) => ctx.select((state) => state.counter));
441
- return `Current count: ${count}`;
447
+ return `Current count: ${count} (Build: ${reactiveCounterBuilds})`;
442
448
  },
443
449
  scope: ArtifactScopes.Singleton,
444
450
  });
@@ -447,18 +453,18 @@ async function runWatcherExample() {
447
453
  console.log('Setting up watcher...');
448
454
  const watcher = container.watch('reactiveCounter');
449
455
 
450
- let unsubscribe = watcher.subscribe(() => {
456
+ let unsubscribe = watcher.subscribe((resolved) => {
451
457
  if (resolved.ready) {
452
458
  console.log('Watcher received update:', resolved.instance);
453
459
  } else if (resolved.error) {
454
460
  console.error('Watcher received error:', resolved.error.message);
455
461
  } else {
456
- console.log('Watcher received pending update (artifact not ready yet).');
462
+ console.log('Watcher received pending update (artifact not yet ready).');
457
463
  }
458
464
  });
459
465
 
460
- // Initial state should trigger a notification (pending, then ready)
461
- await new Promise(res => setTimeout(res, 10)); // Give watcher time to resolve
466
+ // Give watcher time to resolve the artifact initially
467
+ await new Promise(res => setTimeout(res, 10));
462
468
 
463
469
  console.log('\n--- Incrementing counter ---');
464
470
  await store.set({ counter: 1 });
@@ -468,141 +474,182 @@ async function runWatcherExample() {
468
474
  await store.set({ counter: 2 });
469
475
  await new Promise(res => setTimeout(res, 10));
470
476
 
471
- console.log('\n--- Disposing watcher ---');
472
- unsubscribe(); // Unsubscribe first
473
- await watcher.dispose(); // Then dispose the watcher itself
474
-
475
- // After dispose, attempts to get will yield WatcherDisposedError
476
- console.log('Watcher after dispose:', watcher.get().error.message);
477
+ console.log('\n--- Unsubscribing & Disposing watcher ---');
478
+ unsubscribe(); // Unsubscribe from updates
479
+
480
+ // After all subscribers are gone, the underlying artifact instance for watching transients is cleaned up.
481
+ // The watcher itself can then be disposed, making it unusable.
482
+ await new Promise(res => setTimeout(res, 10)); // Give cleanup a moment
483
+ // For singletons, watcher.dispose() is effectively a no-op as the underlying artifact remains
484
+ // For transients, it would clean up the internal singleton clone.
485
+
486
+ try {
487
+ watcher.get(); // Attempt to access after dispose
488
+ } catch (e) {
489
+ if (e instanceof WatcherDisposedError) {
490
+ console.error('Caught expected error after watcher dispose:', e.message);
491
+ } else {
492
+ throw e;
493
+ }
494
+ }
477
495
 
478
496
  // Output:
479
497
  // Setting up watcher...
480
- // Watcher received pending update (artifact not ready yet).
481
- // Watcher received update: Current count: 0
498
+ // Watcher received pending update (artifact not yet ready).
499
+ // Watcher received update: Current count: 0 (Build: 1)
482
500
  //
483
501
  // --- Incrementing counter ---
484
- // Watcher received pending update (artifact not ready yet).
485
- // Watcher received update: Current count: 1
502
+ // Watcher received update: Current count: 1 (Build: 2)
486
503
  //
487
504
  // --- Incrementing counter again ---
488
- // Watcher received pending update (artifact not ready yet).
489
- // Watcher received update: Current count: 2
505
+ // Watcher received update: Current count: 2 (Build: 3)
490
506
  //
491
- // --- Disposing watcher ---
492
- // Watcher after dispose: [ArtifactContainer] Artifact with key:reactiveCounter has already been disposed
507
+ // --- Unsubscribing & Disposing watcher ---
508
+ // Caught expected error after watcher dispose: [ArtifactContainer] Artifact with key:reactiveCounter has already been disposed
493
509
  }
494
510
 
495
511
  runWatcherExample();
496
512
  ```
497
513
 
498
- ### Updating Intermediate Values with `ctx.update`
514
+ ### Streaming Artifacts with `ctx.stream()`
499
515
 
500
- For long-running or multi-stage artifact factories, `ctx.update()` allows a Singleton artifact to publish intermediate values. This updates the artifact's current instance and notifies dependents/watchers without fully completing the factory execution.
516
+ `Singleton` artifacts can leverage `ctx.stream(callback)` to become continuous sources of data. The `callback` receives an `ArtifactStreamContext` which allows emitting new values over time via `ctx.emit(value)`. Each emission updates the artifact's instance, notifying all dependents and watchers immediately. This is ideal for scenarios like WebSocket clients, background workers, or long-running computations that produce intermediate results.
501
517
 
502
518
  ```typescript
503
519
  import { ArtifactContainer, ArtifactScopes } from "@asaidimu/utils-artifacts";
504
520
  import { ReactiveDataStore } from "@asaidimu/utils-store";
505
521
 
506
522
  const store = new ReactiveDataStore({});
507
- const container = new ArtifactContainer(store);
523
+ const container = new ArtifactContainer<{ producer: number; consumer: string }, {}>(store);
508
524
 
509
- container.register({
510
- key: "longProcessArtifact",
511
- factory: async ({ update: publish }) => { // `update` is renamed to `publish` for clarity in this example
512
- async function process() {
513
- await new Promise((res) => setTimeout(res, 100));
514
- console.log("Starting long process...");
515
-
516
- await publish("Step 1: Initializing...");
517
- await new Promise((res) => setTimeout(res, 100));
518
-
519
- await publish("Step 2: Loading data...");
520
- await new Promise((res) => setTimeout(res, 100));
525
+ const PRODUCER_TIMEOUT = 50; // ms
526
+ const PRODUCER_LIMIT = 3;
521
527
 
522
- await publish("Step 3: Processing data...");
523
- await new Promise((res) => setTimeout(res, 100));
528
+ container.register({
529
+ key: "producer",
530
+ factory: ({ stream }) => {
531
+ let value = 0;
532
+
533
+ // The stream callback is executed in the background
534
+ stream(async ({ emit, signal }) => {
535
+ console.log("Producer: Stream started.");
536
+ while (!signal.aborted) {
537
+ await new Promise((res) => setTimeout(res, PRODUCER_TIMEOUT)); // Simulate async work
538
+ value++;
539
+ console.log(`Producer: Emitting value: ${value}`);
540
+ await emit(value); // Emit new value, notifying dependents and watchers
541
+ if (value >= PRODUCER_LIMIT) {
542
+ console.log("Producer: Reached limit, stopping stream.");
543
+ break;
544
+ }
545
+ }
546
+ });
524
547
 
525
- console.log("Long process complete.");
526
- }
548
+ return value; // Initial value returned by the factory
549
+ },
550
+ scope: ArtifactScopes.Singleton,
551
+ });
527
552
 
528
- queueMicrotask(process); // Start the process in the background,
529
- return "Factory Starting";
553
+ container.register({
554
+ key: "consumer",
555
+ factory: async ({ use }) => {
556
+ // This artifact depends on the 'producer'
557
+ const producerValue = await use(({ require }) => require("producer"));
558
+ return `Consumed: ${producerValue}`;
530
559
  },
531
560
  scope: ArtifactScopes.Singleton,
532
561
  });
533
562
 
534
- async function runUpdateExample() {
535
- const watcher = container.watch("longProcessArtifact");
563
+ async function runStreamExample() {
564
+ const watcher = container.watch("consumer");
565
+ const receivedValues: string[] = [];
536
566
 
537
- const unsubscribe = watcher.subscribe(() => {
538
- const resolved = watcher.get();
539
- if (resolved.instance) {
540
- console.log("Watcher observed update:", resolved.instance);
567
+ const unsubscribe = watcher.subscribe((resolved) => {
568
+ if (resolved.instance && !receivedValues.includes(resolved.instance)) {
569
+ receivedValues.push(resolved.instance);
570
+ console.log(`Watcher: Received "${resolved.instance}"`);
541
571
  }
542
572
  });
543
573
 
544
- // Resolve the artifact to trigger its factory and the background process
545
- await container.resolve("longProcessArtifact");
574
+ // Initial resolve to kick-start the producer factory and stream
575
+ await container.resolve("consumer");
546
576
 
547
- // Give some time for the process to run and yield updates
548
- await new Promise((res) => setTimeout(res, 600));
577
+ // Give enough time for the stream to run and emit all values
578
+ // (PRODUCER_LIMIT * PRODUCER_TIMEOUT) + some buffer for processing
579
+ await new Promise((res) => setTimeout(res, (PRODUCER_LIMIT * PRODUCER_TIMEOUT) + 100));
549
580
 
550
- unsubscribe();
551
- await watcher.dispose();
581
+ console.log('\n--- Stream Complete ---');
582
+ console.log('All received values:', receivedValues);
583
+ // Expected: ["Consumed: 0", "Consumed: 1", "Consumed: 2", "Consumed: 3"]
552
584
 
553
- // Output will show intermediate values from `ctx.update` as they occur,
554
- // then the final result once the factory completes.
585
+ unsubscribe(); // Clean up the watcher
555
586
  }
556
587
 
557
- runUpdateExample();
588
+ runStreamExample();
558
589
  ```
559
590
 
560
591
  ### Debugging Information
561
592
 
562
- Use `debugInfo()` to inspect the current state of artifacts within a container, including their status, dependencies, and dependents.
593
+ Use `container.debugInfo()` to inspect the current state of all registered artifacts within a container, including their status, direct dependencies (artifact-to-artifact and state), and dependents. This is an invaluable tool for understanding your application's dependency graph at runtime.
563
594
 
564
595
  ```typescript
565
596
  import { ArtifactContainer, ArtifactScopes } from '@asaidimu/utils-artifacts';
566
597
  import { ReactiveDataStore } from '@asaidimu/utils-store';
567
598
 
568
- const store = new ReactiveDataStore({});
569
- const container = new ArtifactContainer(store);
599
+ interface AppState {
600
+ configVersion: number;
601
+ }
602
+
603
+ const store = new ReactiveDataStore<AppState>({ configVersion: 1 });
604
+ const container = new ArtifactContainer<any, AppState>(store);
570
605
 
571
606
  container.register({
572
- key: 'serviceA',
573
- factory: () => 'Service A Instance',
607
+ key: 'configService',
608
+ factory: async ({ use }) => {
609
+ const version = await use((ctx) => ctx.select((state) => state.configVersion));
610
+ return `Config v${version}`;
611
+ },
574
612
  scope: ArtifactScopes.Singleton,
575
613
  });
576
614
 
577
615
  container.register({
578
- key: 'serviceB',
616
+ key: 'dataProcessor',
579
617
  factory: async ({ use }) => {
580
- await use((ctx) => ctx.resolve('serviceA'));
581
- return 'Service B Instance';
618
+ const config = await use((ctx) => ctx.resolve('configService'));
619
+ return `Processing with ${config.instance}`;
582
620
  },
583
621
  scope: ArtifactScopes.Singleton,
584
622
  });
585
623
 
624
+ container.register({
625
+ key: 'ephemeralTool',
626
+ factory: () => 'A transient tool',
627
+ scope: ArtifactScopes.Transient,
628
+ });
629
+
586
630
  async function runDebugInfoExample() {
587
- await container.resolve('serviceB'); // Resolve to build dependencies
631
+ await container.resolve('dataProcessor'); // Resolve to build dependencies
632
+ await container.resolve('ephemeralTool'); // Resolve a transient (doesn't cache, but will show up if just resolved)
588
633
 
589
634
  const debugInfo = container.debugInfo();
590
635
  console.log('--- Artifact Debug Information ---');
591
636
  debugInfo.forEach(node => {
592
- console.log(`ID: ${node.id}`);
637
+ console.log(`\nID: ${node.id}`);
593
638
  console.log(` Scope: ${node.scope}`);
594
639
  console.log(` Status: ${node.status}`);
595
- console.log(` Dependencies: ${node.dependencies.join(', ') || 'None'}`);
640
+ console.log(` Dependencies (Artifacts): ${node.dependencies.join(', ') || 'None'}`);
641
+ console.log(` Dependencies (State Paths): ${node.stateDependencies.join(', ') || 'None'}`);
596
642
  console.log(` Dependents: ${node.dependents.join(', ') || 'None'}`);
597
643
  console.log(` Render Count: ${node.renderCount}`);
598
644
  console.log('---');
599
645
  });
600
646
 
601
- // Example output for 'serviceB':
602
- // ID: serviceB
647
+ // Example output for 'dataProcessor':
648
+ // ID: dataProcessor
603
649
  // Scope: singleton
604
650
  // Status: active
605
- // Dependencies: serviceA
651
+ // Dependencies (Artifacts): configService
652
+ // Dependencies (State Paths): None
606
653
  // Dependents: None
607
654
  // Render Count: 1
608
655
  }
@@ -614,39 +661,44 @@ runDebugInfoExample();
614
661
 
615
662
  ## 🏛️ Project Architecture
616
663
 
617
- The `@asaidimu/utils-artifacts` package is built around the central `ArtifactContainer` class, which orchestrates the lifecycle and dependencies of various "artifacts" (any component or value you register).
664
+ The `@asaidimu/utils-artifacts` package is built around the central `ArtifactContainer` class, which orchestrates the lifecycle and dependencies of various "artifacts" (any component or value you register). It is designed to be modular and extensible, promoting maintainability and testability.
618
665
 
619
666
  ### Core Components
620
667
 
621
- * **`ArtifactContainer`**: The public-facing entry point of the system. It orchestrates the registration, resolution, lifecycle management (Singleton/Transient), and reactive updates of all artifacts. It provides methods like `register`, `resolve`, `watch`, `invalidate`, and `debugInfo`.
622
- * **`ArtifactRegistry`**: Manages the definitions of all registered artifacts, storing their factory functions and configuration options (scope, lazy loading, retries, debounce, timeout).
623
- * **`ArtifactCache`**: Stores the runtime state of resolved singleton artifacts, including their instances, errors, cleanup functions, state dependencies, and internal synchronization primitives (`Once`, `Serializer`). It's responsible for packaging cached data into the public `ResolvedArtifact` format.
624
- * **`ArtifactDependencyGraph`**: A specialized wrapper around a generic `DependencyGraph` that tracks artifact-to-artifact dependencies and state path dependencies. It's crucial for detecting circular dependencies and determining the cascade order during invalidation.
625
- * **`ArtifactManager`**: The core engine responsible for the actual building, invalidation, and disposal of artifact instances. It interacts heavily with the `Registry`, `Cache`, `DependencyGraph`, and `DataStore`. It implements retry logic, timeouts, and manages lifecycle hooks.
626
- * **`ArtifactObserverManager`**: Handles the creation and management of `ArtifactObserver` instances, allowing external code to subscribe to artifact changes. It also manages reference counting for watchers and notifies them of updates.
627
- * **`DataStore` (External Dependency)**: The container relies on an external data store (e.g., `@asaidimu/utils-store`) to manage global application state. The container interacts with it via `watch` (for reactive subscriptions to state changes) and `get` (for reading the current state).
628
- * **Synchronization Primitives (`Mutex`, `Once`, `Serializer`)**: These internal utilities provide robust mechanisms for managing concurrency, ensuring single execution of tasks, and processing operations sequentially. They are fundamental building blocks for the `ArtifactManager` and `ArtifactCache`.
668
+ * **`ArtifactContainer`**: The public-facing entry point of the system. It orchestrates the registration, resolution, lifecycle management (`Singleton`/`Transient`), and reactive updates of all artifacts. It provides methods like `register`, `resolve`, `watch`, `invalidate`, and `debugInfo`.
669
+ * **`ArtifactRegistry`**: Manages the definitions of all registered artifacts, storing their factory functions and configuration options (scope, lazy loading, retries, debounce, timeout). It ensures unique keys and provides basic CRUD for artifact templates.
670
+ * **`ArtifactCache`**: Stores the runtime state of resolved `Singleton` artifacts, including their instances, errors, cleanup functions, state dependencies, and internal synchronization primitives (`Once`, `Serializer`). It's responsible for packaging cached data into the public `ResolvedArtifact` format, maintaining reference stability.
671
+ * **`ArtifactDependencyGraph`**: A specialized wrapper around a generic `DependencyGraph` that tracks artifact-to-artifact dependencies and state path dependencies. It's crucial for detecting circular dependencies, managing the dependency graph, and determining the cascade order during invalidation.
672
+ * **`ArtifactManager`**: The core engine responsible for the actual building, invalidation, and disposal of artifact instances. It interacts heavily with the `Registry`, `Cache`, `DependencyGraph`, and `DataStore`. It implements retry logic, timeouts, and manages lifecycle hooks (`onCleanup`, `onDispose`) and `ctx.stream` functionality.
673
+ * **`ArtifactObserverManager`**: Handles the creation and management of `ArtifactObserver` instances, allowing external code to subscribe to artifact changes. It also manages reference counting for watchers and notifies them of updates, including special handling for `Transient` artifacts to ensure they are watched via a temporary `Singleton` clone.
674
+ * **`DataStore` (External Dependency)**: The container relies on an external data store (e.g., `@asaidimu/utils-store`) to manage global application state. The container interacts with it via `watch` (for reactive subscriptions to state changes), `get` (for reading the current state), and `set` (allowing artifacts to dispatch state updates).
675
+ * **Synchronization Primitives (`Mutex`, `Once`, `Serializer`)**: These internal utilities (`src/artifacts/sync.ts`) provide robust mechanisms for managing concurrency, ensuring single execution of tasks, and processing operations sequentially. They are fundamental building blocks for the `ArtifactManager` and `ArtifactCache`.
676
+ * **Error Handling (`ArtifactError` hierarchy)**: A dedicated set of custom error classes (`src/artifacts/errors.ts`) provides clear, categorized error messages for system-level issues (e.g., circular dependencies, missing artifacts, illegal operations), simplifying debugging and error handling.
629
677
 
630
678
  ### Data Flow
631
679
 
632
680
  1. **Registration (`container.register`)**: An `ArtifactTemplate` (containing an `ArtifactFactory` and its `options`) is stored in the `ArtifactRegistry`. A corresponding node is added to the `ArtifactDependencyGraph`.
633
- 2. **Resolution (`container.resolve`)**:
681
+ 2. **Resolution (`container.resolve` or `container.require`)**:
634
682
  * If the artifact is a `Singleton` and already built, its cached instance from `ArtifactCache` is returned.
635
683
  * If not built (or `Transient`), the `ArtifactManager` invokes the `ArtifactFactory` within a specialized `ArtifactFactoryContext`.
636
684
  * **Dependency Declaration (`ctx.use`)**: Inside the factory, `ctx.use` provides methods to declare dependencies:
637
- * `ctx.resolve(key)`: Recursively triggers resolution of another artifact. The relationship is added to the `ArtifactDependencyGraph`.
685
+ * `ctx.resolve(key)`/`ctx.require(key)`: Recursively triggers resolution of another artifact. The relationship is added to the `ArtifactDependencyGraph`.
638
686
  * `ctx.select(selector)`: Extracts specific state paths from the global `DataStore`. These paths are registered as state dependencies for the artifact.
639
- 3. **Dependency Graph Construction**: As dependencies are declared (via `ctx.resolve` or `ctx.select`), the `ArtifactDependencyGraph` constructs a precise, bidirectional graph, linking artifacts and their state dependencies.
687
+ 3. **Dependency Graph Construction**: As dependencies are declared (via `ctx.resolve`/`ctx.require` or `ctx.select`), the `ArtifactDependencyGraph` constructs a precise, bidirectional graph, linking artifacts and their state dependencies.
640
688
  4. **Reactive Invalidation**:
641
- * **State Changes**: When a `DataStore` value changes, `ArtifactManager` identifies affected artifacts (those subscribed to the changed state paths) and calls `container.invalidate()` for them.
642
- * **Artifact Changes**: When an artifact is rebuilt, invalidated, or proactively `update()`s its value, its direct and transitive `dependents` are identified by the `ArtifactDependencyGraph`, and `container.invalidate()` is called for each.
689
+ * **State Changes**: When a `DataStore` value changes, `ArtifactManager` identifies affected `Singleton` artifacts (those subscribed to the changed state paths) and calls `container.invalidate()` for them.
690
+ * **Artifact Changes**: When a `Singleton` artifact is rebuilt, explicitly `invalidate()`d, or proactively `stream()`s a new value, its direct and transitive `dependents` are identified by the `ArtifactDependencyGraph`, and `container.invalidate()` is called for each, cascading updates.
643
691
  5. **Rebuild Process**: `container.invalidate()` triggers:
644
- * **Debouncing**: If configured, rebuilds are debounced (`activeDebounceMs`) to consolidate multiple rapid invalidations.
692
+ * **Debouncing**: If configured (`debounce` option), rebuilds are debounced (`activeDebounceMs`) to consolidate multiple rapid invalidations, preventing excessive factory executions.
645
693
  * **Cleanup**: `onCleanup` hooks for the *old* instance are executed.
646
694
  * **New Instance**: The `ArtifactFactory` is re-executed by the `ArtifactManager`, creating a new instance.
647
695
  * **Graph Update**: The dependency graph is updated based on any *new* dependencies declared by the re-executed factory.
648
696
  * **Notification**: `ArtifactObserver` subscribers are notified of the new instance via `ArtifactObserverManager`.
649
- 6. **Disposal (`container.dispose` / `container.unregister`)**: All resources associated with an artifact (`onDispose` hooks, state subscriptions, cache entry, graph links) are released. Full container disposal clears everything.
697
+ 6. **Streaming Updates (`ctx.stream`)**: If a `Singleton` artifact uses `ctx.stream`, subsequent `ctx.emit(value)` calls from within its factory:
698
+ * Update the artifact's cached instance in `ArtifactCache`.
699
+ * Increment the artifact's internal version.
700
+ * Trigger `invalidate()` on all its dependents and `notifyObservers()`, effectively propagating the streamed value reactively.
701
+ 7. **Disposal (`container.dispose` / `container.unregister`)**: All resources associated with an artifact (`onDispose` hooks, state subscriptions, cache entry, graph links) are released. Full container disposal clears everything.
650
702
 
651
703
  ### Extension Points
652
704
 
@@ -656,7 +708,7 @@ The primary extension point is the **`ArtifactFactory` function**. By implementi
656
708
  * Manage external resources like database connections, API clients, or message queues, ensuring they are properly initialized and cleaned up using `onCleanup` and `onDispose`.
657
709
  * Provide custom logging, monitoring, or telemetry by injecting those services as dependencies.
658
710
 
659
- The `ArtifactContainer` itself is designed to be highly configurable via `ArtifactTemplate` options, allowing fine-tuning of scope, lazy loading, error handling, and reactive behavior.
711
+ The `ArtifactContainer` itself is designed to be highly configurable via `ArtifactTemplate` options, allowing fine-tuning of scope, lazy loading, error handling, and reactive behavior.
660
712
 
661
713
  ---
662
714
 
@@ -668,8 +720,8 @@ We welcome contributions! Please read the guidelines below.
668
720
 
669
721
  1. **Clone the repository**:
670
722
  ```bash
671
- git clone https://github.com/asaidimu/utils.git
672
- cd utils/src/artifacts
723
+ git clone https://github.com/asaidimu/erp-utils.git
724
+ cd erp-utils/src/artifacts # Navigate to this package's directory
673
725
  ```
674
726
  2. **Install dependencies**:
675
727
  ```bash
@@ -684,15 +736,17 @@ We welcome contributions! Please read the guidelines below.
684
736
 
685
737
  ### Scripts
686
738
 
739
+ The `package.json` for this package includes the following scripts:
740
+
687
741
  * `bun install`: Installs project dependencies.
688
742
  * `bun run test`: Runs the test suite using Vitest.
689
743
  * `bun run test:watch`: Runs tests in watch mode for continuous feedback.
690
- * `bun run test:browser`: Runs tests in a browser environment.
744
+ * `bun run test:browser`: Runs tests in a browser environment using Playwright.
691
745
  * `bun run build`: Compiles the TypeScript source code into JavaScript.
692
746
 
693
747
  ### Testing
694
748
 
695
- This project uses [Vitest](https://vitest.dev/) for testing.
749
+ This project uses [Vitest](https://vitest.dev/) for testing, with support for browser-based tests via Playwright.
696
750
 
697
751
  * To run all tests:
698
752
  ```bash
@@ -702,21 +756,25 @@ This project uses [Vitest](https://vitest.dev/) for testing.
702
756
  ```bash
703
757
  bun run test --watch
704
758
  ```
759
+ * To run tests in a browser environment:
760
+ ```bash
761
+ bun run test:browser
762
+ ```
705
763
  * Ensure all new features or bug fixes are accompanied by appropriate unit and/or integration tests.
706
764
 
707
765
  ### Contributing Guidelines
708
766
 
709
767
  We appreciate your interest in contributing to `@asaidimu/utils-artifacts`!
710
- Please refer to the main repository's [CONTRIBUTING.md](https://github.com/asaidimu/utils/blob/main/CONTRIBUTING.md) for detailed guidelines on:
768
+ Please refer to the main repository's [CONTRIBUTING.md](https://github.com/asaidimu/erp-utils/blob/main/CONTRIBUTING.md) for detailed guidelines on:
711
769
 
712
770
  * Reporting issues
713
771
  * Submitting pull requests
714
772
  * Coding standards
715
- * Commit message conventions (we follow Conventional Commits)
773
+ * Commit message conventions (we follow [Conventional Commits](https://www.conventionalcommits.org/en/v1.0.0/))
716
774
 
717
775
  ### Issue Reporting
718
776
 
719
- Found a bug or have a feature request? Please open an issue on our [GitHub Issues page](https://github.com/asaidimu/utils/issues).
777
+ Found a bug or have a feature request? Please open an issue on our [GitHub Issues page](https://github.com/asaidimu/erp-utils/issues).
720
778
  Provide as much detail as possible, including steps to reproduce, expected behavior, and your environment.
721
779
 
722
780
  ---
@@ -728,12 +786,12 @@ Provide as much detail as possible, including steps to reproduce, expected behav
728
786
  * **`ArtifactNotFoundError`**: This typically means you're trying to `resolve` or `watch` an artifact that hasn't been `register`ed in the current container. Double-check your `key`s and registration calls.
729
787
  * **`CircularDependencyError`**: Occurs when artifact A depends on B, and B depends back on A (directly or indirectly). This indicates a structural problem in your dependency graph. Redesign your artifacts to break the cycle. The error message will show the path of the cycle.
730
788
  * **Artifact Not Updating Reactively**:
731
- * Ensure your artifact is a `Singleton` (Transient artifacts don't cache or react to invalidations).
732
- * Verify you are using `ctx.use()` and `ctx.select()` (for state) or `ctx.resolve()` (for other artifacts) inside your factory to declare dependencies.
733
- * Check that your `DataStore` implementation is correctly triggering its `watch` callbacks.
734
- * **`IllegalScopeError`**: You might be trying to `update` a value from a `Transient` artifact. `update` is only meaningful for `Singleton` artifacts, which maintain a persistent instance.
735
- * **`TimeoutError`**: Your artifact factory took longer than its configured `timeout` to complete, or a `Mutex` lock operation exceeded its timeout. Consider increasing the `timeout` or optimizing your factory's logic.
736
- * **`WatcherDisposedError`**: You are attempting to access a watcher's state (`watcher.get()`) after `watcher.dispose()` has been called. Ensure you unsubscribe and dispose of watchers when no longer needed, and avoid using them afterward.
789
+ * Ensure your artifact is a `Singleton` (Transient artifacts do not cache, react to invalidations, or support streaming).
790
+ * Verify you are using `ctx.use()` and `ctx.select()` (for state dependencies) or `ctx.use(({ resolve }) => resolve('otherKey'))` (for artifact dependencies) inside your factory to properly declare dependencies.
791
+ * Check that your `DataStore` implementation is correctly triggering its `watch` callbacks when state changes.
792
+ * **`IllegalScopeError`**: You might be trying to `stream` a value from a `Transient` artifact. The `stream` method is only meaningful for `Singleton` artifacts, which maintain a persistent, updateable instance.
793
+ * **`TimeoutError`**: Your artifact factory took longer than its configured `timeout` to complete, or an internal `Mutex` lock operation exceeded its timeout. Consider increasing the `timeout` in the artifact's `ArtifactTemplate` or optimizing your factory's logic.
794
+ * **`WatcherDisposedError`**: You are attempting to access a watcher's state (`watcher.get()`) after `watcher.dispose()` has been called. Ensure you unsubscribe from all callbacks (`unsubscribe()`) and dispose of watchers when no longer needed (`watcher.dispose()`), and avoid using them afterward.
737
795
 
738
796
  ### FAQ
739
797
 
@@ -742,22 +800,23 @@ Provide as much detail as possible, including steps to reproduce, expected behav
742
800
  * **Why use this over a simpler DI library?**
743
801
  `@asaidimu/utils-artifacts` goes beyond basic DI by adding a powerful reactive layer. It automatically handles the propagation of changes from global state or other artifacts, making it easier to build self-updating components and reactive systems without manual subscription management.
744
802
  * **When should I use `Singleton` vs. `Transient`?**
745
- * **Singleton**: For services, database connections, configuration objects, or any resource that should be shared and consistently available throughout your application. They are efficient as they are built once and reused.
746
- * **Transient**: For objects that need a fresh instance every time they are requested, such as temporary calculators, request-scoped contexts, or factory functions that produce unique outputs.
803
+ * **Singleton**: For services, database connections, configuration objects, or any resource that should be shared and consistently available throughout your application. They are efficient as they are built once and reused, and they react to dependency changes.
804
+ * **Transient**: For objects that need a fresh instance every time they are requested, such as temporary calculators, request-scoped contexts, or factory functions that produce unique, short-lived outputs. They do not react to dependencies or retain state.
747
805
  * **How does `debounce` work?**
748
- `debounce` prevents an artifact from rebuilding too frequently. If multiple invalidations occur within the specified `debounce` time, they are consolidated, and the artifact's factory is only run once after the activity ceases. This is crucial for performance with rapid state changes, especially when many state changes might trigger the same artifact rebuild.
806
+ The `debounce` option (part of `ArtifactTemplate`) prevents a `Singleton` artifact from rebuilding too frequently. If multiple invalidations occur within the specified `debounce` time, they are consolidated, and the artifact's factory is only run once after the activity ceases (or after the last invalidation within the debounce window). This is crucial for performance with rapid state changes, especially when many state changes might trigger the same artifact rebuild.
749
807
 
750
808
  ### Changelog / Roadmap
751
809
 
752
- * **Changelog**: For a detailed list of changes, please refer to the [CHANGELOG.md](https://github.com/asaidimu/utils/blob/main/CHANGELOG.md) file in the main repository.
753
- * **Roadmap**: Future plans and upcoming features are typically outlined in the main repository's [issue tracker](https://github.com/asaidimu/utils/issues) or project boards.
810
+ * **Changelog**: For a detailed list of changes, please refer to the [CHANGELOG.md](https://github.com/asaidimu/erp-utils/blob/main/CHANGELOG.md) file in the main repository.
811
+ * **Roadmap**: Future plans and upcoming features for `@asaidimu/utils-artifacts` are typically outlined in the main repository's [issue tracker](https://github.com/asaidimu/erp-utils/issues) or project boards.
754
812
 
755
813
  ### License
756
814
 
757
- This project is licensed under the MIT License. See the [LICENSE](https://github.com/asaidimu/utils/blob/main/LICENSE) file for details.
815
+ This project is licensed under the [MIT License](https://github.com/asaidimu/erp-utils/blob/main/LICENSE).
758
816
 
759
817
  ### Acknowledgments
760
818
 
761
819
  * Developed as part of the `@asaidimu` utilities collection.
820
+ * Leverages foundational packages like [`@asaidimu/utils-store`](https://www.npmjs.com/package/@asaidimu/utils-store) and `@asaidimu/events` within the `erp-utils` monorepo.
762
821
  * Uses [Semantic Release](https://semantic-release.gitbook.io/semantic-release/) for automated versioning and publishing.
763
- * Leverages [Vitest](https://vitest.dev/) for fast and reliable testing.
822
+ * Utilizes [Vitest](https://vitest.dev/) for fast and reliable testing.
package/index.d.mts CHANGED
@@ -244,6 +244,16 @@ interface UseDependencyContext<TRegistry extends Record<string, any>, TState ext
244
244
  * @throws {SystemError} if the key is missing or if resolving creates a circular dependency.
245
245
  */
246
246
  resolve<K extends keyof TRegistry>(key: K): Promise<ResolvedArtifact<TRegistry[K]>>;
247
+ /**
248
+ * Resolves another artifact from the container and registers a dependency on it.
249
+ * If the dependency changes, the current artifact will be invalidated and rebuilt.
250
+ * @template K The key of the artifact to resolve.
251
+ * @param key The key of the artifact to resolve.
252
+ * @returns A Promise that resolves to a the resolved instance, unlike
253
+ * resolve, this will throw an error if resolution fails.
254
+ * @throws {SystemError} if any error occurs during resolution.
255
+ */
256
+ require<K extends keyof TRegistry>(key: K): Promise<TRegistry[K]>;
247
257
  /**
248
258
  * Selects a slice of the global state and registers a dependency on it.
249
259
  * If the selected state slice changes (based on a deep comparison), the current
@@ -460,11 +470,13 @@ interface ArtifactObserver<TArtifact> {
460
470
  */
461
471
  subscribe(callback: (artifact: ResolvedArtifact<TArtifact>) => void): () => void;
462
472
  /**
463
- * Disposes of this specific observer reference, decrementing the reference count.
464
- * When the count reaches zero, associated resources (like cloned singletons for Transient artifacts)
465
- * are cleaned up.
473
+ * Resolves another artifact from the container and registers a dependency on it.
474
+ * If the dependency changes, the current artifact will be invalidated and rebuilt.
475
+ * @returns A Promise that resolves to a `ResolvedArtifact` containing the instance
476
+ * or an external error.
477
+ * @throws {SystemError} if resolution fails.
466
478
  */
467
- dispose(): Promise<void>;
479
+ resolve(): Promise<ResolvedArtifact<TArtifact>>;
468
480
  }
469
481
  /**
470
482
  * Configuration options for defining an artifact. These options control
@@ -621,6 +633,16 @@ declare class ArtifactContainer<TRegistry extends Record<string, any> = Record<s
621
633
  * @throws {ArtifactError} if a cycle is detected or other system errors occur
622
634
  */
623
635
  resolve<K extends keyof TRegistry>(key: K): Promise<ResolvedArtifact<TRegistry[K]>>;
636
+ /**
637
+ * Resolves an artifact by its key, returning its instance directly.
638
+ * This is a wrapper around resolve to prevent defensive programming
639
+ *
640
+ * @param key The unique identifier for the artifact
641
+ * @returns A Promise that resolves to a ResolvedArtifact
642
+ * @throws {ArtifactNotFoundError} if the artifact is not found
643
+ * @throws {ArtifactError} if a cycle is detected or other system errors occur
644
+ */
645
+ require<K extends keyof TRegistry>(key: K): Promise<TRegistry[K]>;
624
646
  /**
625
647
  * Returns an ArtifactWatcher for a given artifact key.
626
648
  * The watcher allows subscribing to changes in the artifact's resolved value.
package/index.d.ts CHANGED
@@ -244,6 +244,16 @@ interface UseDependencyContext<TRegistry extends Record<string, any>, TState ext
244
244
  * @throws {SystemError} if the key is missing or if resolving creates a circular dependency.
245
245
  */
246
246
  resolve<K extends keyof TRegistry>(key: K): Promise<ResolvedArtifact<TRegistry[K]>>;
247
+ /**
248
+ * Resolves another artifact from the container and registers a dependency on it.
249
+ * If the dependency changes, the current artifact will be invalidated and rebuilt.
250
+ * @template K The key of the artifact to resolve.
251
+ * @param key The key of the artifact to resolve.
252
+ * @returns A Promise that resolves to a the resolved instance, unlike
253
+ * resolve, this will throw an error if resolution fails.
254
+ * @throws {SystemError} if any error occurs during resolution.
255
+ */
256
+ require<K extends keyof TRegistry>(key: K): Promise<TRegistry[K]>;
247
257
  /**
248
258
  * Selects a slice of the global state and registers a dependency on it.
249
259
  * If the selected state slice changes (based on a deep comparison), the current
@@ -460,11 +470,13 @@ interface ArtifactObserver<TArtifact> {
460
470
  */
461
471
  subscribe(callback: (artifact: ResolvedArtifact<TArtifact>) => void): () => void;
462
472
  /**
463
- * Disposes of this specific observer reference, decrementing the reference count.
464
- * When the count reaches zero, associated resources (like cloned singletons for Transient artifacts)
465
- * are cleaned up.
473
+ * Resolves another artifact from the container and registers a dependency on it.
474
+ * If the dependency changes, the current artifact will be invalidated and rebuilt.
475
+ * @returns A Promise that resolves to a `ResolvedArtifact` containing the instance
476
+ * or an external error.
477
+ * @throws {SystemError} if resolution fails.
466
478
  */
467
- dispose(): Promise<void>;
479
+ resolve(): Promise<ResolvedArtifact<TArtifact>>;
468
480
  }
469
481
  /**
470
482
  * Configuration options for defining an artifact. These options control
@@ -621,6 +633,16 @@ declare class ArtifactContainer<TRegistry extends Record<string, any> = Record<s
621
633
  * @throws {ArtifactError} if a cycle is detected or other system errors occur
622
634
  */
623
635
  resolve<K extends keyof TRegistry>(key: K): Promise<ResolvedArtifact<TRegistry[K]>>;
636
+ /**
637
+ * Resolves an artifact by its key, returning its instance directly.
638
+ * This is a wrapper around resolve to prevent defensive programming
639
+ *
640
+ * @param key The unique identifier for the artifact
641
+ * @returns A Promise that resolves to a ResolvedArtifact
642
+ * @throws {ArtifactNotFoundError} if the artifact is not found
643
+ * @throws {ArtifactError} if a cycle is detected or other system errors occur
644
+ */
645
+ require<K extends keyof TRegistry>(key: K): Promise<TRegistry[K]>;
624
646
  /**
625
647
  * Returns an ArtifactWatcher for a given artifact key.
626
648
  * The watcher allows subscribing to changes in the artifact's resolved value.
package/index.js CHANGED
@@ -1 +1 @@
1
- "use strict";var e,t,r=Object.create,s=Object.defineProperty,n=Object.getOwnPropertyDescriptor,i=Object.getOwnPropertyNames,a=Object.getPrototypeOf,o=Object.prototype.hasOwnProperty,c=(e={"src/store/node_modules/@asaidimu/events/index.js"(e,t){var r,s=Object.defineProperty,n=Object.getOwnPropertyDescriptor,i=Object.getOwnPropertyNames,a=Object.prototype.hasOwnProperty,o={};((e,t)=>{for(var r in t)s(e,r,{get:t[r],enumerable:!0})})(o,{createEventBus:()=>c}),t.exports=(r=o,((e,t,r,o)=>{if(t&&"object"==typeof t||"function"==typeof t)for(let c of i(t))a.call(e,c)||c===r||s(e,c,{get:()=>t[c],enumerable:!(o=n(t,c))||o.enumerable});return e})(s({},"__esModule",{value:!0}),r));var c=(e={async:!1,batchSize:1e3,batchDelay:16,errorHandler:e=>console.error("EventBus Error:",e),crossTab:!1,channelName:"event-bus-channel"})=>{const t=new Map;let r=[],s=0,n=0;const i=new Map,a=new Map;let o=null;e.crossTab&&"undefined"!=typeof BroadcastChannel?o=new BroadcastChannel(e.channelName):e.crossTab&&console.warn("BroadcastChannel is not supported in this browser. Cross-tab notifications are disabled.");const c=(e,t)=>{s++,n+=t,i.set(e,(i.get(e)||0)+1)},h=()=>{const t=r;r=[],t.forEach((({name:t,payload:r})=>{const s=performance.now();try{(a.get(t)||[]).forEach((e=>e(r)))}catch(s){e.errorHandler({...s,eventName:t,payload:r})}c(t,performance.now()-s)}))},d=(()=>{let t;return()=>{clearTimeout(t),t=setTimeout(h,e.batchDelay)}})(),l=e=>{const r=t.get(e);r?a.set(e,Array.from(r)):a.delete(e)};return o&&(o.onmessage=e=>{const{name:t,payload:r}=e.data;(a.get(t)||[]).forEach((e=>e(r)))}),{subscribe:(e,r)=>{t.has(e)||t.set(e,new Set);const s=t.get(e);return s.add(r),l(e),()=>{s.delete(r),0===s.size?(t.delete(e),a.delete(e)):l(e)}},emit:({name:t,payload:s})=>{if(e.async)return r.push({name:t,payload:s}),r.length>=e.batchSize?h():d(),void(o&&o.postMessage({name:t,payload:s}));const n=performance.now();try{(a.get(t)||[]).forEach((e=>e(s))),o&&o.postMessage({name:t,payload:s})}catch(r){e.errorHandler({...r,eventName:t,payload:s})}c(t,performance.now()-n)},getMetrics:()=>({totalEvents:s,activeSubscriptions:Array.from(t.values()).reduce(((e,t)=>e+t.size),0),eventCounts:i,averageEmitDuration:s>0?n/s:0}),clear:()=>{t.clear(),a.clear(),r=[],s=0,n=0,i.clear(),o&&(o.close(),o=null)}}}}},function(){return t||(0,e[i(e)[0]])((t={exports:{}}).exports,t),t.exports}),h=(e=>(e.Singleton="singleton",e.Transient="transient",e))(h||{}),d=class e extends Error{category;constructor(t,r,s){super(t,{cause:s}),this.name="ArtifactError",this.category=r,Object.setPrototypeOf(this,e.prototype)}},l=class extends d{constructor(e){super(`[ArtifactContainer] Artifact "${e}" not found.`,"system")}},u=class extends d{constructor(e){super(`[ArtifactContainer] Artifact with key:${e} has already been disposed`,"system")}},p=class extends d{constructor(e){super(`[ArtifactContainer] Operation timed out: ${e}`,"system")}},g=class extends d{constructor(e){super(`[ArtifactContainer] An artifact with key:${e} already exists!`,"system")}},f=class extends d{constructor(){super("[Serializer] The serializer has been marked as done!","system")}},y=class{artifacts=new Map;register({key:e,factory:t,lazy:r,...s}){if(this.artifacts.has(e))throw new g(String(e));const{scope:n,...i}=s,a={key:e,factory:t,scope:s.scope??"singleton",lazy:void 0===r||r,...i};return this.artifacts.set(e,a),()=>this.unregister(e)}get(e){if(!this.has(e))throw new l(String(e));return this.artifacts.get(e)}has(e){return this.artifacts.has(e)}async unregister(e){this.artifacts.has(e)&&this.artifacts.delete(e)}size(){return this.artifacts.size}keys(){return Array.from(this.artifacts.keys())}clear(){this.artifacts.clear()}};((e,t,c)=>{c=null!=e?r(a(e)):{},((e,t,r,a)=>{if(t&&"object"==typeof t||"function"==typeof t)for(let c of i(t))o.call(e,c)||c===r||s(e,c,{get:()=>t[c],enumerable:!(a=n(t,c))||a.enumerable})})(e&&e.__esModule?c:s(c,"default",{value:e,enumerable:!0}),e)})(c());var m=class{_locked=!1;_capacity;waiters=[];constructor(e){this._capacity=e?.capacity??1/0}async lock(e){if(!this._locked)return void(this._locked=!0);if(this.waiters.length>=this._capacity)throw new Error(`Mutex queue is full (capacity: ${this._capacity})`);let t;const r=new Promise((e=>t=e));if(this.waiters.push(t),null!=e)try{await Promise.race([r,new Promise(((r,s)=>setTimeout((()=>{const e=this.waiters.indexOf(t);-1!==e&&this.waiters.splice(e,1),s(new p("Mutex lock timed out"))}),e)))])}catch(e){throw e}else await r}tryLock(){return!this._locked&&(this._locked=!0,!0)}unlock(){if(!this._locked)throw new Error("Mutex is not locked");const e=this.waiters.shift();e?setTimeout(e,0):this._locked=!1}locked(){return this._locked}pending(){return this.waiters.length}},w=class{constructor(e=!1,t=!1){this.retry=e,this.throws=t}mutex=new m;promise=null;_value=null;_error;_done=!1;async do(e,t){return this._done?this.peek():this.promise?this._awaitWithTimeout(this.promise,t,"Once do() timed out"):(await this.mutex.lock(),this.promise?(this.mutex.unlock(),this._awaitWithTimeout(this.promise,t,"Once do() timed out")):(this.promise=(async()=>{try{const t=await e();this._value=t,this._done=!0}catch(e){if(this._error=e,this.retry||(this._done=!0),this.throws)throw e}finally{this.retry&&!this._done&&(this.promise=null)}return this.peek()})(),this.mutex.unlock(),this._awaitWithTimeout(this.promise,t,"Once do() timed out")))}running(){return null!==this.promise&&!this._done}peek(){return{value:this._value,error:this._error}}get(){if(!this._done)throw new Error("Once operation is not yet complete");if(this._error)throw this._error;return this._value}reset(){this._done=!1,this.promise=null,this._value=null,this._error=void 0}resolved(){return this.promise}done(){return this._done}_awaitWithTimeout(e,t,r="Operation timed out"){return null==t?e:Promise.race([e,new Promise(((e,s)=>setTimeout((()=>s(new p(r))),t)))])}},v=class{mutex;_done=!1;_lastValue=null;_lastError=void 0;constructor(e){this.mutex=new m({capacity:e?.capacity??1e3})}async do(e,t){if(this._done)return{value:null,error:new f};try{await this.mutex.lock(t)}catch(e){return{value:null,error:e}}let r,s=null;try{if(this._done)throw new f;s=await e(),this._lastValue=s,this._lastError=void 0}catch(e){r=e,this._lastError=e}finally{this.mutex.unlock()}return{value:s,error:r}}peek(){return{value:this._lastValue,error:this._lastError}}close(){this._done=!0}pending(){return this.mutex.pending()}running(){return this.mutex.locked()}},b=class{constructor(e,t,r,s,n){this.registry=e,this.cache=t,this.graph=r,this.store=s,this.observer=n}async build(e,t){const r=this.registry.get(e);if(!r)throw new d(`Template not found for artifact "${String(e)}"`,"system");const s=t?new Set(t):new Set;if(s.has(e))throw new d(`Cycle detected: Artifact "${String(e)}" depends on itself via path: ${Array.from(s).join(" -> ")}`,"system");s.add(e);let n=this.cache.get(e);n||(n=this.createCachedArtifact(r),this.cache.set(e,n));const i=await("transient"===r.scope?this.executeBuild(r,n,s):n.buildOnce.do((()=>this.executeBuild(r,n,s))));if("transient"===r.scope)return i;const a=this.cache.package(e,((t,r)=>this.invalidate(e,t,r)));return n.stream&&n.streamOnce.do(n.stream),a}async executeBuild(e,t,r){const s=e.key;t.buildCount++;const n=[],i=[];let a=!0;t.activeDebounceMs=e.debounce??0,n.push((()=>{a=!1}));const o=new Set,c=new Set,h=new Map,l={state:()=>this.store.get(!0),previous:t.instance,onCleanup:e=>n.push(e),onDispose:e=>i.push(e),use:async e=>e({resolve:async e=>{if(e===s)throw new d(`Artifact "${String(s)}" depends on itself.`,"system");const t=this.graph.wouldCreateCycle(s,e,r);if(t)throw new d(`Adding dependency "${String(e)}" to "${String(s)}" would create a cycle: ${t.join(" -> ")}`,"system");c.add(e);const n=await this.build(e,r),i=this.cache.get(e);return i&&h.set(e,i.version),n},select:e=>(function(e,t="."){const r=new Set,s=(e=[])=>new Proxy({},{get:(n,i)=>{if("symbol"==typeof i)return;const a=[...e,i],o=a.join(t);return r.add(o),s(a)}});try{e(s())}catch(e){throw new Error(`Selector failed during path analysis. This usually means the selector is too complex. Selectors must be simple property accessors only. Error: ${e instanceof Error?e.message:String(e)}`)}const n=Array.from(r);return n.filter((e=>!n.some((r=>r!==e&&r.startsWith(e+t)))))}(e).forEach((e=>o.add(e))),e(this.store.get(!0)))}),stream:async r=>{if("transient"===e.scope)throw new d(`[ArtifactManager] Illegal stream on transient artifact "${String(s)}"`,"system");const n=async(e,r=void 0)=>{await t.streamSerializer.do((async()=>{void 0!==t.stream&&(t.instance=e,t.error=r,t.version++,this.cache.invalidatePackage(s),await this.processStream(s))}))},i={value:()=>t.instance,signal:t.streamController.signal,set:(...e)=>this.store.set(...e),emit:e=>n(e)};t.stream=async()=>{try{await r(i)}catch(e){await n(void 0,e),this.invalidate(s,!1,!0)}}}};let u,p,g=0;const f=(e.retries??0)+1;for(;g<f;)try{const t=e.factory(l);if(e.timeout){const r=new Promise(((t,r)=>setTimeout((()=>r(new Error(`Timeout: ${e.timeout}ms`))),e.timeout)));u=await Promise.race([t,r])}else u=await t;break}catch(e){if(e instanceof d)throw e;g++,g>=f&&(p=e)}if(a){for(const[e,t]of h){const r=this.cache.get(e);r&&r.version!==t&&(p=new d(`Build stale on arrival: Dependency "${String(e)}" changed during build.`,"system"))}if("singleton"===e.scope&&this.updateDependencyGraph(s,c,o),"singleton"===e.scope&&(t.cleanupFunctions=n,t.disposeFunctions=i),p?t.error=p:t.instance=u,t.version++,this.cache.invalidatePackage(s),"transient"===e.scope)return{instance:u,cleanup:this.createCompositeCleanup(n),error:p,ready:!0,invalidate:async()=>console.warn(`[ArtifactManager] Cannot invalidate transient "${String(s)}"`)}}else{const e=this.createCompositeCleanup(n);e&&await e()}}async invalidate(e,t=!1,r=!1){const s=this.cache.get(e);if(s)return s.debounceTimer&&(clearTimeout(s.debounceTimer),s.debounceTimer=void 0),!t&&s.activeDebounceMs>0?new Promise((n=>{s.debounceTimer=setTimeout((()=>{s.debounceTimer=void 0,this.executeInvalidation(e,t,r).then(n).catch(n)}),s.activeDebounceMs)})):this.executeInvalidation(e,t,r)}async executeInvalidation(e,t,r=!1){const s=this.cache.get(e);s&&(await s.invalidationOnce.do((async()=>{s.version++,await this.cache.invalidateInstance(e);const n=this.graph.getDependents(e);await Promise.all(n.map((e=>this.invalidate(e).catch((t=>{console.error(`[ArtifactManager] Cascade failed for "${String(e)}":`,t)})))));const i=this.registry.get(e);!i||!t&&i.lazy&&!this.hasWatchers(e)||r||await this.build(e),this.observer.notify(e)})),s.invalidationOnce.reset())}async dispose(e){const t=this.cache.get(e);t&&(t.streamController.abort(),t.buildOnce.running()&&await t.buildOnce.resolved(),t.invalidationOnce.running()&&await t.invalidationOnce.resolved(),t.streamOnce.running()&&await t.streamOnce.resolved(),await this.cache.invalidateInstance(e),this.graph.removeNode(e),this.cache.delete(e))}async processStream(e){try{const t=this.graph.getDependents(e);await Promise.all(t.map((e=>this.invalidate(e).catch((t=>{console.error(`[ArtifactManager] Failed to invalidate dependent "${String(e)}":`,t)}))))),this.observer.notify(e)}catch(t){console.error(`[ArtifactManager] Stream propagation error "${e}":`,t)}}updateDependencyGraph(e,t,r){const s=this.cache.get(e);s&&(this.graph.hasNode(e)||this.graph.registerNode(e),this.graph.setDependencies(e,t),s.stateUnsubscribe&&s.stateUnsubscribe(),s.stateDependencies=r,r.size>0&&(s.stateUnsubscribe=this.store.watch(Array.from(r),(async()=>{await this.invalidate(e)}))))}createCachedArtifact(e){return{instance:void 0,version:0,streamController:new AbortController,error:void 0,cleanupFunctions:[],disposeFunctions:[],buildCount:0,stateDependencies:new Set,buildOnce:new w(!0,!0),streamOnce:new w(!0,!0),streamSerializer:new v,activeDebounceMs:e.debounce??0,invalidationOnce:new w(!0,!1)}}createCompositeCleanup(e){if(e.length)return async()=>{for(let t=e.length-1;t>=0;t--)try{await e[t]()}catch(e){console.error("[ArtifactManager] Cleanup error:",e)}}}hasWatchers(e){return this.observer.hasWatchers(e)??!1}},C=class{dependencies=new Map;dependents=new Map;metadata=new Map;registerNode(e,t){this.dependencies.has(e)||this.dependencies.set(e,new Set),this.dependents.has(e)||this.dependents.set(e,new Set),void 0!==t&&this.metadata.set(e,t)}removeNode(e){if(!this.hasNode(e))return;const t=this.dependencies.get(e);if(t)for(const r of t)this.dependents.get(r)?.delete(e);const r=this.dependents.get(e);if(r)for(const t of r)this.dependencies.get(t)?.delete(e);this.dependencies.delete(e),this.dependents.delete(e),this.metadata.delete(e)}hasNode(e){return this.dependencies.has(e)}addDependency(e,t){this.registerNode(e),this.registerNode(t),this.dependencies.get(e).add(t),this.dependents.get(t).add(e)}removeDependency(e,t){this.dependencies.get(e)?.delete(t),this.dependents.get(t)?.delete(e)}getDependencies(e){const t=this.dependencies.get(e);return t?new Set(t):new Set}getDependents(e){const t=this.dependents.get(e);return t?new Set(t):new Set}getMetadata(e){return this.metadata.get(e)}setMetadata(e,t){this.registerNode(e),this.metadata.set(e,t)}setDependencies(e,t){this.registerNode(e);const r=this.dependencies.get(e),s=new Set(t);for(const t of r)s.has(t)||this.removeDependency(e,t);for(const t of s)r.has(t)||this.addDependency(e,t)}wouldCreateCycle(e,t){if(e===t)return[e,t];const r=[t],s=new Set([t]),n=new Map;for(;r.length>0;){const i=r.shift();if(i===e){const r=[];let s=e;for(;void 0!==s&&(r.push(s),s!==t);)s=n.get(s);return r.reverse(),[e,...r]}const a=this.dependencies.get(i);if(a)for(const e of a)s.has(e)||(s.add(e),n.set(e,i),r.push(e))}return null}topologicalSort(){const e=new Map,t=Array.from(this.dependencies.keys());for(const r of t)e.set(r,this.dependencies.get(r)?.size??0);const r=[];for(const[t,s]of e)0===s&&r.push(t);const s=[];for(;r.length>0;){const t=r.shift();s.push(t);const n=this.dependents.get(t);if(n)for(const t of n){const s=(e.get(t)||0)-1;e.set(t,s),0===s&&r.push(t)}}if(s.length!==t.length)throw new Error("Cycle detected in graph; topological sort impossible.");return s}getTransitiveDependencies(e,t=!1){return this.bfs(e,"dependencies",t)}getTransitiveDependents(e,t=!1){return this.bfs(e,"dependents",t)}bfs(e,t,r){const s=new Set,n=new Set,i=[e];n.add(e),r&&s.add(e);const a="dependencies"===t?this.dependencies:this.dependents;for(;i.length>0;){const e=i.shift(),t=a.get(e);if(t)for(const e of t)n.has(e)||(n.add(e),s.add(e),i.push(e))}return s}getAllNodes(){return Array.from(this.dependencies.keys())}size(){return this.dependencies.size}clear(){this.dependencies.clear(),this.dependents.clear(),this.metadata.clear()}toDebugString(){return Array.from(this.dependencies.entries()).map((([e,t])=>{const r=Array.from(t).join(", ");return`${String(e)} → [${r||"∅"}]`})).join("\n")}},A=class{graph;constructor(){this.graph=new C}registerNode(e){this.graph.registerNode(e)}removeNode(e){this.graph.removeNode(e)}addDependency(e,t){this.graph.addDependency(e,t)}removeDependency(e,t){this.graph.removeDependency(e,t)}getDependents(e){const t=this.graph.getDependents(e);return Array.from(t)}getDependencies(e){const t=this.graph.getDependencies(e);return Array.from(t)}getTransitiveDependents(e){const t=this.graph.getTransitiveDependents(e,!1);return new Set(Array.from(t))}setDependencies(e,t){const r=Array.from(t).map((e=>e));this.graph.setDependencies(e,r)}wouldCreateCycle(e,t,r){if(r?.has(t)){const e=Array.from(r),s=e.indexOf(t),n=e.slice(s);return n.push(t),n}const s=this.graph.wouldCreateCycle(e,t);return s||null}hasNode(e){return this.graph.hasNode(e)}getAllNodes(){return this.graph.getAllNodes()}clear(){this.graph.clear()}size(){return this.graph.size()}toDebugString(){return this.graph.toDebugString()}},_=class{cache=new Map;emptyInstance={instance:void 0,error:void 0,ready:!1,cleanup:void 0,invalidate:async(e,t)=>{}};get(e){return this.cache.get(e)}set(e,t){this.cache.set(e,t)}delete(e){this.cache.delete(e)}has(e){return this.cache.has(e)}clear(){this.cache.clear()}size(){return this.cache.size}keys(){return Array.from(this.cache.keys())}package(e,t){const r=this.get(e);if(!r)return this.emptyInstance;if(r.packagedArtifact)return this.updatePackagedArtifact(r,r.packagedArtifact,t),r.packagedArtifact;const s={instance:r.instance,error:r.error,ready:r.buildOnce.done()&&!r.buildOnce.running(),cleanup:this.createCompositeCleanup(r.cleanupFunctions),invalidate:t};return r.packagedArtifact=s,s}async invalidatePackage(e){const t=this.get(e);t&&(t.packagedArtifact=void 0)}async invalidateInstance(e){const t=this.get(e);if(t){t.stateUnsubscribe&&(t.stateUnsubscribe(),t.stateUnsubscribe=void 0),t.debounceTimer&&(clearTimeout(t.debounceTimer),t.debounceTimer=void 0),t.streamController.abort(),await t.streamOnce.resolved(),t.streamOnce.reset(),t.streamSerializer.close();for(let e=t.cleanupFunctions.length-1;e>=0;e--)try{await t.cleanupFunctions[e]()}catch(e){console.error("[ArtifactManager] Cleanup error:",e)}for(let e=t.disposeFunctions.length-1;e>=0;e--)try{await t.disposeFunctions[e]()}catch(e){console.error("[ArtifactManager] Dispose error:",e)}t.instance=void 0,t.error=void 0,t.cleanupFunctions=[],t.disposeFunctions=[],t.buildOnce.reset(),t.stream=void 0,t.streamSerializer=new v,t.streamController=new AbortController}}updatePackagedArtifact(e,t,r){t.instance=e.instance,t.error=e.error,t.ready=e.buildOnce.done()&&!e.buildOnce.running(),t.cleanup=this.createCompositeCleanup(e.cleanupFunctions),t.invalidate=r}createCompositeCleanup(e){if(e.length)return async()=>{for(let t=e.length-1;t>=0;t--)try{await e[t]()}catch(e){console.error("[ArtifactCache] Cleanup error:",e)}}}},k=class{constructor(e,t,r){this.registry=e,this.cache=t,this.container=r}listeners=new Map;watcherCache=new Map;watch(e){const t=String(e),r=this.registry.get(e);if(!r)throw new Error(`Artifact "${t}" not registered`);let s=t;if("transient"===r.scope&&(s=`${t}__watched`,this.registry.has(s)||this.registry.register({key:s,factory:r.factory,scope:"singleton",lazy:r.lazy,timeout:r.timeout,retries:r.retries,debounce:r.debounce})),this.watcherCache.has(s)){const e=this.watcherCache.get(s);return e.count++,e}let n=!1;const i={id:t,count:1,get:()=>{if(!this.cache.get(s))return this.cache.package(s,(e=>this.container.invalidate(s,e)));const e=this.cache.package(s,(e=>this.container.invalidate(s,e)));return n?{...e,ready:!1,instance:void 0,error:new u(t),cleanup:()=>{},invalidate:()=>Promise.resolve()}:e},subscribe:e=>{this.listeners.has(s)||this.listeners.set(s,new Set);const t=()=>e(i.get());return this.listeners.get(s).add(t),this.container.resolve(s).catch((e=>{console.error(`[ArtifactObserver] Background resolution failed for "${s}":`,e)})).then(t),()=>this.listeners.get(s)?.delete(t)},dispose:async()=>{i.count--,i.count<=0&&(this.watcherCache.delete(s),"transient"===r.scope&&(await this.registry.unregister(s),this.cache.delete(s)),n=!0,this.notify(s),this.listeners.delete(s))}};return this.watcherCache.set(s,i),i}notify(e){const t=this.listeners.get(e);t&&t.forEach((t=>{try{t()}catch(t){console.error(`[ArtifactObserver] Listener error for "${e}":`,t)}}))}hasWatchers(e){return this.watcherCache.has(e)||this.watcherCache.has(`${e}__watched`)}getWatcherCount(e){const t=this.watcherCache.get(e)||this.watcherCache.get(`${e}__watched`);return t?.count??0}clear(){this.watcherCache.clear(),this.listeners.clear()}};exports.ArtifactContainer=class{registry;cache;graph;manager;observer;store;constructor(e){this.store={watch:(...t)=>e.watch(...t),get:()=>e.get(!0),set:(...t)=>e.set(...t)},this.registry=new y,this.cache=new _,this.graph=new A,this.observer=new k(this.registry,this.cache,this),this.manager=new b(this.registry,this.cache,this.graph,this.store,this.observer)}debugInfo(){const e=[];return this.registry.keys().forEach((t=>{const r=this.registry.get(t),s=this.cache.get(t);if(!r)return;const n=s?s.buildOnce.running()?"pending":s.error?"error":void 0!==s.instance?"active":"idle":"idle";e.push({id:t,scope:r.scope??"singleton",status:n,dependencies:this.graph.getDependencies(t).map((e=>String(e))),dependents:this.graph.getDependents(t).map((e=>String(e))),stateDependencies:s?Array.from(s.stateDependencies):[],renderCount:s?.buildCount??0})})),e}register(e){const{key:t}=e,r=t;this.registry.has(t)&&(console.warn(`[ArtifactContainer] Overwriting "${r}".`),this.manager.dispose(t).catch((e=>{console.error(`[ArtifactContainer] Failed to dispose existing artifact "${r}":`,e)}))),this.registry.register(e),this.graph.registerNode(t);const s=e.scope??"singleton";return(e.lazy??!0)||"singleton"!==s||this.resolve(t).catch((e=>{console.error(`[ArtifactContainer] Eager load failed for "${r}":`,e)})),()=>this.unregister(t)}async unregister(e){await this.manager.dispose(e),await this.registry.unregister(e)}async resolve(e){if(!this.registry.has(e))throw new l(e);return this.manager.build(e)}watch(e){if(!this.registry.has(e))throw new l(e);return this.observer.watch(e)}peek(e){const t=this.cache.get(e);if(void 0!==t?.instance)return t?.instance}async invalidate(e,t=!1){return this.manager.invalidate(e,t)}notifyObservers(e){this.observer.notify(e)}hasWatchers(e){return this.observer.hasWatchers(e)}dispose(){this.registry.keys().forEach((e=>{this.manager.dispose(e).catch((t=>{console.error(`[ArtifactContainer] Failed to dispose artifact "${String(e)}":`,t)}))})),this.registry.clear(),this.cache.clear(),this.graph.clear(),this.observer.clear()}},exports.ArtifactScopes=h;
1
+ "use strict";var e,t,r=Object.create,s=Object.defineProperty,n=Object.getOwnPropertyDescriptor,i=Object.getOwnPropertyNames,a=Object.getPrototypeOf,o=Object.prototype.hasOwnProperty,c=(e={"node_modules/@asaidimu/events/index.js"(e,t){var r,s=Object.defineProperty,n=Object.getOwnPropertyDescriptor,i=Object.getOwnPropertyNames,a=Object.prototype.hasOwnProperty,o={};((e,t)=>{for(var r in t)s(e,r,{get:t[r],enumerable:!0})})(o,{createEventBus:()=>c}),t.exports=(r=o,((e,t,r,o)=>{if(t&&"object"==typeof t||"function"==typeof t)for(let c of i(t))a.call(e,c)||c===r||s(e,c,{get:()=>t[c],enumerable:!(o=n(t,c))||o.enumerable});return e})(s({},"__esModule",{value:!0}),r));var c=(e={async:!1,batchSize:1e3,batchDelay:16,errorHandler:e=>console.error("EventBus Error:",e),crossTab:!1,channelName:"event-bus-channel"})=>{const t=new Map;let r=[],s=0,n=0;const i=new Map,a=new Map;let o=null;e.crossTab&&"undefined"!=typeof BroadcastChannel?o=new BroadcastChannel(e.channelName):e.crossTab&&console.warn("BroadcastChannel is not supported in this browser. Cross-tab notifications are disabled.");const c=(e,t)=>{s++,n+=t,i.set(e,(i.get(e)||0)+1)},h=()=>{const t=r;r=[],t.forEach((({name:t,payload:r})=>{const s=performance.now();try{(a.get(t)||[]).forEach((e=>e(r)))}catch(s){e.errorHandler({...s,eventName:t,payload:r})}c(t,performance.now()-s)}))},d=(()=>{let t;return()=>{clearTimeout(t),t=setTimeout(h,e.batchDelay)}})(),l=e=>{const r=t.get(e);r?a.set(e,Array.from(r)):a.delete(e)};return o&&(o.onmessage=e=>{const{name:t,payload:r}=e.data;(a.get(t)||[]).forEach((e=>e(r)))}),{subscribe:(e,r)=>{t.has(e)||t.set(e,new Set);const s=t.get(e);return s.add(r),l(e),()=>{s.delete(r),0===s.size?(t.delete(e),a.delete(e)):l(e)}},emit:({name:t,payload:s})=>{if(e.async)return r.push({name:t,payload:s}),r.length>=e.batchSize?h():d(),void(o&&o.postMessage({name:t,payload:s}));const n=performance.now();try{(a.get(t)||[]).forEach((e=>e(s))),o&&o.postMessage({name:t,payload:s})}catch(r){e.errorHandler({...r,eventName:t,payload:s})}c(t,performance.now()-n)},getMetrics:()=>({totalEvents:s,activeSubscriptions:Array.from(t.values()).reduce(((e,t)=>e+t.size),0),eventCounts:i,averageEmitDuration:s>0?n/s:0}),clear:()=>{t.clear(),a.clear(),r=[],s=0,n=0,i.clear(),o&&(o.close(),o=null)}}}}},function(){return t||(0,e[i(e)[0]])((t={exports:{}}).exports,t),t.exports}),h=(e=>(e.Singleton="singleton",e.Transient="transient",e))(h||{}),d=class e extends Error{category;constructor(t,r,s){super(t,{cause:s}),this.name="ArtifactError",this.category=r,Object.setPrototypeOf(this,e.prototype)}},l=class extends d{constructor(e){super(`[ArtifactContainer] Artifact "${e}" not found.`,"system")}},u=class extends d{constructor(e){super(`[ArtifactContainer] Operation timed out: ${e}`,"system")}},p=class extends d{constructor(e){super(`[ArtifactContainer] An artifact with key:${e} already exists!`,"system")}},g=class extends d{constructor(){super("[Serializer] The serializer has been marked as done!","system")}},f=class{artifacts=new Map;register({key:e,factory:t,lazy:r,...s}){if(this.artifacts.has(e))throw new p(String(e));const{scope:n,...i}=s,a={key:e,factory:t,scope:s.scope??"singleton",lazy:void 0===r||r,...i};return this.artifacts.set(e,a),()=>this.unregister(e)}get(e){if(!this.has(e))throw new l(String(e));return this.artifacts.get(e)}has(e){return this.artifacts.has(e)}async unregister(e){this.artifacts.has(e)&&this.artifacts.delete(e)}size(){return this.artifacts.size}keys(){return Array.from(this.artifacts.keys())}clear(){this.artifacts.clear()}};((e,t,c)=>{c=null!=e?r(a(e)):{},((e,t,r,a)=>{if(t&&"object"==typeof t||"function"==typeof t)for(let c of i(t))o.call(e,c)||c===r||s(e,c,{get:()=>t[c],enumerable:!(a=n(t,c))||a.enumerable})})(e&&e.__esModule?c:s(c,"default",{value:e,enumerable:!0}),e)})(c());var y=class{_locked=!1;_capacity;waiters=[];constructor(e){this._capacity=e?.capacity??1/0}async lock(e){if(!this._locked)return void(this._locked=!0);if(this.waiters.length>=this._capacity)throw new Error(`Mutex queue is full (capacity: ${this._capacity})`);let t;const r=new Promise((e=>t=e));if(this.waiters.push(t),null!=e)try{await Promise.race([r,new Promise(((r,s)=>setTimeout((()=>{const e=this.waiters.indexOf(t);-1!==e&&this.waiters.splice(e,1),s(new u("Mutex lock timed out"))}),e)))])}catch(e){throw e}else await r}tryLock(){return!this._locked&&(this._locked=!0,!0)}unlock(){if(!this._locked)throw new Error("Mutex is not locked");const e=this.waiters.shift();e?setTimeout(e,0):this._locked=!1}locked(){return this._locked}pending(){return this.waiters.length}},m=class{constructor(e=!1,t=!1){this.retry=e,this.throws=t}mutex=new y;promise=null;_value=null;_error;_done=!1;async do(e,t){return this._done?this.peek():this.promise?this._awaitWithTimeout(this.promise,t,"Once do() timed out"):(await this.mutex.lock(),this.promise?(this.mutex.unlock(),this._awaitWithTimeout(this.promise,t,"Once do() timed out")):(this.promise=(async()=>{try{const t=await e();this._value=t,this._done=!0}catch(e){if(this._error=e,this.retry||(this._done=!0),this.throws)throw e}finally{this.retry&&!this._done&&(this.promise=null)}return this.peek()})(),this.mutex.unlock(),this._awaitWithTimeout(this.promise,t,"Once do() timed out")))}running(){return null!==this.promise&&!this._done}peek(){return{value:this._value,error:this._error}}get(){if(!this._done)throw new Error("Once operation is not yet complete");if(this._error)throw this._error;return this._value}reset(){this._done=!1,this.promise=null,this._value=null,this._error=void 0}resolved(){return this.promise}done(){return this._done}_awaitWithTimeout(e,t,r="Operation timed out"){return null==t?e:Promise.race([e,new Promise(((e,s)=>setTimeout((()=>s(new u(r))),t)))])}},w=class{mutex;_done=!1;_lastValue=null;_lastError=void 0;constructor(e){this.mutex=new y({capacity:e?.capacity??1e3})}async do(e,t){if(this._done)return{value:null,error:new g};try{await this.mutex.lock(t)}catch(e){return{value:null,error:e}}let r,s=null;try{if(this._done)throw new g;s=await e(),this._lastValue=s,this._lastError=void 0}catch(e){r=e,this._lastError=e}finally{this.mutex.unlock()}return{value:s,error:r}}peek(){return{value:this._lastValue,error:this._lastError}}close(){this._done=!0}pending(){return this.mutex.pending()}running(){return this.mutex.locked()}},v=class{constructor(e,t,r,s,n){this.registry=e,this.cache=t,this.graph=r,this.store=s,this.observer=n}async build(e,t){const r=this.registry.get(e);if(!r)throw new d(`Template not found for artifact "${String(e)}"`,"system");const s=t?new Set(t):new Set;if(s.has(e))throw new d(`Cycle detected: Artifact "${String(e)}" depends on itself via path: ${Array.from(s).join(" -> ")}`,"system");s.add(e);let n=this.cache.get(e);n||(n=this.createCachedArtifact(r),this.cache.set(e,n));const i=await("transient"===r.scope?this.executeBuild(r,n,s):n.buildOnce.do((()=>this.executeBuild(r,n,s))));if("transient"===r.scope)return i;const a=this.cache.package(e,((t,r)=>this.invalidate(e,t,r)));return n.stream&&n.streamOnce.do(n.stream),a}async executeBuild(e,t,r){const s=e.key;t.buildCount++;const n=[],i=[];let a=!0;t.activeDebounceMs=e.debounce??0,n.push((()=>{a=!1}));const o=new Set,c=new Set,h=new Map,l=this.graph,u=this.cache,p=this.build.bind(this);async function g(e){if(e===s)throw new d(`Artifact "${String(s)}" depends on itself.`,"system");const t=l.wouldCreateCycle(s,e,r);if(t)throw new d(`Adding dependency "${String(e)}" to "${String(s)}" would create a cycle: ${t.join(" -> ")}`,"system");c.add(e);const n=await p(e,r),i=u.get(e);return i&&h.set(e,i.version),n}const f={state:()=>this.store.get(!0),previous:t.instance,onCleanup:e=>n.push(e),onDispose:e=>i.push(e),use:async e=>e({resolve:g,require:async e=>{const t=await g(e);if(t.error)throw t.error;return t.instance},select:e=>(function(e,t="."){const r=new Set,s=(e=[])=>new Proxy({},{get:(n,i)=>{if("symbol"==typeof i)return;const a=[...e,i],o=a.join(t);return r.add(o),s(a)}});try{e(s())}catch(e){throw new Error(`Selector failed during path analysis. This usually means the selector is too complex. Selectors must be simple property accessors only. Error: ${e instanceof Error?e.message:String(e)}`)}const n=Array.from(r);return n.filter((e=>!n.some((r=>r!==e&&r.startsWith(e+t)))))}(e).forEach((e=>o.add(e))),e(this.store.get(!0)))}),stream:async r=>{if("transient"===e.scope)throw new d(`[ArtifactManager] Illegal stream on transient artifact "${String(s)}"`,"system");const n=async(e,r=void 0)=>{await t.streamSerializer.do((async()=>{void 0!==t.stream&&(t.instance=e,t.error=r,t.version++,this.cache.invalidatePackage(s),await this.processStream(s))}))},i={value:()=>t.instance,signal:t.streamController.signal,set:(...e)=>this.store.set(...e),emit:e=>n(e)};t.stream=async()=>{try{await r(i)}catch(e){await n(void 0,e),this.invalidate(s,!1,!0)}}}};let y,m,w=0;const v=(e.retries??0)+1;for(;w<v;)try{const t=e.factory(f);if(e.timeout){const r=new Promise(((t,r)=>setTimeout((()=>r(new Error(`Timeout: ${e.timeout}ms`))),e.timeout)));y=await Promise.race([t,r])}else y=await t;break}catch(e){if(e instanceof d)throw e;w++,w>=v&&(m=e)}if(a){for(const[e,t]of h){const r=this.cache.get(e);r&&r.version!==t&&(m=new d(`Build stale on arrival: Dependency "${String(e)}" changed during build.`,"system"))}if("singleton"===e.scope&&this.updateDependencyGraph(s,c,o),"singleton"===e.scope&&(t.cleanupFunctions=n,t.disposeFunctions=i),m?t.error=m:t.instance=y,t.version++,this.cache.invalidatePackage(s),"transient"===e.scope)return{instance:y,cleanup:this.createCompositeCleanup(n),error:m,ready:!0,invalidate:async()=>console.warn(`[ArtifactManager] Cannot invalidate transient "${String(s)}"`)}}else{const e=this.createCompositeCleanup(n);e&&await e()}}async invalidate(e,t=!1,r=!1){const s=this.cache.get(e);if(s)return s.debounceTimer&&(clearTimeout(s.debounceTimer),s.debounceTimer=void 0),!t&&s.activeDebounceMs>0?new Promise((n=>{s.debounceTimer=setTimeout((()=>{s.debounceTimer=void 0,this.executeInvalidation(e,t,r).then(n).catch(n)}),s.activeDebounceMs)})):this.executeInvalidation(e,t,r)}async executeInvalidation(e,t,r=!1){const s=this.cache.get(e);s&&(await s.invalidationOnce.do((async()=>{s.version++,await this.cache.invalidateInstance(e);const n=this.graph.getDependents(e);await Promise.all(n.map((e=>this.invalidate(e).catch((t=>{console.error(`[ArtifactManager] Cascade failed for "${String(e)}":`,t)})))));const i=this.registry.get(e);!i||!t&&i.lazy&&!this.hasWatchers(e)||r||await this.build(e),this.observer.notify(e)})),s.invalidationOnce.reset())}async dispose(e){const t=this.cache.get(e);t&&(t.streamController.abort(),t.buildOnce.running()&&await t.buildOnce.resolved(),t.invalidationOnce.running()&&await t.invalidationOnce.resolved(),t.streamOnce.running()&&await t.streamOnce.resolved(),await this.cache.invalidateInstance(e),this.graph.removeNode(e),this.cache.delete(e))}async processStream(e){try{const t=this.graph.getDependents(e);await Promise.all(t.map((e=>this.invalidate(e).catch((t=>{console.error(`[ArtifactManager] Failed to invalidate dependent "${String(e)}":`,t)}))))),this.observer.notify(e)}catch(t){console.error(`[ArtifactManager] Stream propagation error "${e}":`,t)}}updateDependencyGraph(e,t,r){const s=this.cache.get(e);s&&(this.graph.hasNode(e)||this.graph.registerNode(e),this.graph.setDependencies(e,t),s.stateUnsubscribe&&s.stateUnsubscribe(),s.stateDependencies=r,r.size>0&&(s.stateUnsubscribe=this.store.watch(Array.from(r),(async()=>{await this.invalidate(e)}))))}createCachedArtifact(e){return{instance:void 0,version:0,streamController:new AbortController,error:void 0,cleanupFunctions:[],disposeFunctions:[],buildCount:0,stateDependencies:new Set,buildOnce:new m(!0,!0),streamOnce:new m(!0,!0),streamSerializer:new w,activeDebounceMs:e.debounce??0,invalidationOnce:new m(!0,!1)}}createCompositeCleanup(e){if(e.length)return async()=>{for(let t=e.length-1;t>=0;t--)try{await e[t]()}catch(e){console.error("[ArtifactManager] Cleanup error:",e)}}}hasWatchers(e){return this.observer.hasWatchers(e)??!1}},b=class{dependencies=new Map;dependents=new Map;metadata=new Map;registerNode(e,t){this.dependencies.has(e)||this.dependencies.set(e,new Set),this.dependents.has(e)||this.dependents.set(e,new Set),void 0!==t&&this.metadata.set(e,t)}removeNode(e){if(!this.hasNode(e))return;const t=this.dependencies.get(e);if(t)for(const r of t)this.dependents.get(r)?.delete(e);const r=this.dependents.get(e);if(r)for(const t of r)this.dependencies.get(t)?.delete(e);this.dependencies.delete(e),this.dependents.delete(e),this.metadata.delete(e)}hasNode(e){return this.dependencies.has(e)}addDependency(e,t){this.registerNode(e),this.registerNode(t),this.dependencies.get(e).add(t),this.dependents.get(t).add(e)}removeDependency(e,t){this.dependencies.get(e)?.delete(t),this.dependents.get(t)?.delete(e)}getDependencies(e){const t=this.dependencies.get(e);return t?new Set(t):new Set}getDependents(e){const t=this.dependents.get(e);return t?new Set(t):new Set}getMetadata(e){return this.metadata.get(e)}setMetadata(e,t){this.registerNode(e),this.metadata.set(e,t)}setDependencies(e,t){this.registerNode(e);const r=this.dependencies.get(e),s=new Set(t);for(const t of r)s.has(t)||this.removeDependency(e,t);for(const t of s)r.has(t)||this.addDependency(e,t)}wouldCreateCycle(e,t){if(e===t)return[e,t];const r=[t],s=new Set([t]),n=new Map;for(;r.length>0;){const i=r.shift();if(i===e){const r=[];let s=e;for(;void 0!==s&&(r.push(s),s!==t);)s=n.get(s);return r.reverse(),[e,...r]}const a=this.dependencies.get(i);if(a)for(const e of a)s.has(e)||(s.add(e),n.set(e,i),r.push(e))}return null}topologicalSort(){const e=new Map,t=Array.from(this.dependencies.keys());for(const r of t)e.set(r,this.dependencies.get(r)?.size??0);const r=[];for(const[t,s]of e)0===s&&r.push(t);const s=[];for(;r.length>0;){const t=r.shift();s.push(t);const n=this.dependents.get(t);if(n)for(const t of n){const s=(e.get(t)||0)-1;e.set(t,s),0===s&&r.push(t)}}if(s.length!==t.length)throw new Error("Cycle detected in graph; topological sort impossible.");return s}getTransitiveDependencies(e,t=!1){return this.bfs(e,"dependencies",t)}getTransitiveDependents(e,t=!1){return this.bfs(e,"dependents",t)}bfs(e,t,r){const s=new Set,n=new Set,i=[e];n.add(e),r&&s.add(e);const a="dependencies"===t?this.dependencies:this.dependents;for(;i.length>0;){const e=i.shift(),t=a.get(e);if(t)for(const e of t)n.has(e)||(n.add(e),s.add(e),i.push(e))}return s}getAllNodes(){return Array.from(this.dependencies.keys())}size(){return this.dependencies.size}clear(){this.dependencies.clear(),this.dependents.clear(),this.metadata.clear()}toDebugString(){return Array.from(this.dependencies.entries()).map((([e,t])=>{const r=Array.from(t).join(", ");return`${String(e)} → [${r||"∅"}]`})).join("\n")}},C=class{graph;constructor(){this.graph=new b}registerNode(e){this.graph.registerNode(e)}removeNode(e){this.graph.removeNode(e)}addDependency(e,t){this.graph.addDependency(e,t)}removeDependency(e,t){this.graph.removeDependency(e,t)}getDependents(e){const t=this.graph.getDependents(e);return Array.from(t)}getDependencies(e){const t=this.graph.getDependencies(e);return Array.from(t)}getTransitiveDependents(e){const t=this.graph.getTransitiveDependents(e,!1);return new Set(Array.from(t))}setDependencies(e,t){const r=Array.from(t).map((e=>e));this.graph.setDependencies(e,r)}wouldCreateCycle(e,t,r){if(r?.has(t)){const e=Array.from(r),s=e.indexOf(t),n=e.slice(s);return n.push(t),n}const s=this.graph.wouldCreateCycle(e,t);return s||null}hasNode(e){return this.graph.hasNode(e)}getAllNodes(){return this.graph.getAllNodes()}clear(){this.graph.clear()}size(){return this.graph.size()}toDebugString(){return this.graph.toDebugString()}},A=class{cache=new Map;emptyInstance={instance:void 0,error:void 0,ready:!1,cleanup:void 0,invalidate:async(e,t)=>{}};get(e){return this.cache.get(e)}set(e,t){this.cache.set(e,t)}delete(e){this.cache.delete(e)}has(e){return this.cache.has(e)}clear(){this.cache.clear()}size(){return this.cache.size}keys(){return Array.from(this.cache.keys())}package(e,t){const r=this.get(e);if(!r)return this.emptyInstance;if(r.packagedArtifact)return this.updatePackagedArtifact(r,r.packagedArtifact,t),r.packagedArtifact;const s={instance:r.instance,error:r.error,ready:r.buildOnce.done()&&!r.buildOnce.running(),cleanup:this.createCompositeCleanup(r.cleanupFunctions),invalidate:t};return r.packagedArtifact=s,s}async invalidatePackage(e){const t=this.get(e);t&&(t.packagedArtifact=void 0)}async invalidateInstance(e){const t=this.get(e);if(t){t.stateUnsubscribe&&(t.stateUnsubscribe(),t.stateUnsubscribe=void 0),t.debounceTimer&&(clearTimeout(t.debounceTimer),t.debounceTimer=void 0),t.streamController.abort(),await t.streamOnce.resolved(),t.streamOnce.reset(),t.streamSerializer.close();for(let e=t.cleanupFunctions.length-1;e>=0;e--)try{await t.cleanupFunctions[e]()}catch(e){console.error("[ArtifactManager] Cleanup error:",e)}for(let e=t.disposeFunctions.length-1;e>=0;e--)try{await t.disposeFunctions[e]()}catch(e){console.error("[ArtifactManager] Dispose error:",e)}t.instance=void 0,t.error=void 0,t.cleanupFunctions=[],t.disposeFunctions=[],t.buildOnce.reset(),t.stream=void 0,t.streamSerializer=new w,t.streamController=new AbortController}}updatePackagedArtifact(e,t,r){t.instance=e.instance,t.error=e.error,t.ready=e.buildOnce.done()&&!e.buildOnce.running(),t.cleanup=this.createCompositeCleanup(e.cleanupFunctions),t.invalidate=r}createCompositeCleanup(e){if(e.length)return async()=>{for(let t=e.length-1;t>=0;t--)try{await e[t]()}catch(e){console.error("[ArtifactCache] Cleanup error:",e)}}}},_=class{constructor(e,t,r){this.registry=e,this.cache=t,this.container=r}listeners=new Map;watcherCache=new Map;watch(e){const t=String(e),r=this.registry.get(e);if(!r)throw new Error(`Artifact "${t}" not registered`);const s="transient"===r.scope?`${t}__watched`:t;if(this.watcherCache.has(s))return this.watcherCache.get(s).watcher;const n={count:0,init:new m(!1,!0),watcher:null},i=()=>n.init.do((async()=>{"transient"!==r.scope||this.registry.has(s)||this.registry.register({key:s,factory:r.factory,scope:"singleton",lazy:r.lazy,timeout:r.timeout,retries:r.retries,debounce:r.debounce})}));return n.watcher={id:t,get count(){return n.count},get:()=>{this.cache.get(s);return this.cache.package(s,(e=>this.container.invalidate(s,e)))},resolve:async()=>(await i(),this.container.resolve(s)),subscribe:e=>{0===n.count&&i(),n.count++,this.listeners.has(s)||this.listeners.set(s,new Set);const t=()=>e(n.watcher.get());return this.listeners.get(s).add(t),this.container.resolve(s).catch((e=>{console.error(`[ArtifactObserver] Background resolution failed for "${s}":`,e)})).then(t),()=>{this.listeners.get(s)?.delete(t),n.count--,0===n.count&&(this.listeners.delete(s),"transient"===r.scope&&(this.registry.unregister(s).catch((e=>{console.error(`[ArtifactObserver] Cleanup failed for "${s}":`,e)})),this.cache.delete(s)),n.init.reset())}}},this.watcherCache.set(s,n),n.watcher}notify(e){const t=this.listeners.get(e);t&&t.forEach((t=>{try{t()}catch(t){console.error(`[ArtifactObserver] Listener error for "${e}":`,t)}}))}hasWatchers(e){return this.watcherCache.has(e)||this.watcherCache.has(`${e}__watched`)}getWatcherCount(e){const t=this.watcherCache.get(e)||this.watcherCache.get(`${e}__watched`);return t?.count??0}clear(){this.watcherCache.clear(),this.listeners.clear()}};exports.ArtifactContainer=class{registry;cache;graph;manager;observer;store;constructor(e){this.store={watch:(...t)=>e.watch(...t),get:()=>e.get(!0),set:(...t)=>e.set(...t)},this.registry=new f,this.cache=new A,this.graph=new C,this.observer=new _(this.registry,this.cache,this),this.manager=new v(this.registry,this.cache,this.graph,this.store,this.observer)}debugInfo(){const e=[];return this.registry.keys().forEach((t=>{const r=this.registry.get(t),s=this.cache.get(t);if(!r)return;const n=s?s.buildOnce.running()?"pending":s.error?"error":void 0!==s.instance?"active":"idle":"idle";e.push({id:t,scope:r.scope??"singleton",status:n,dependencies:this.graph.getDependencies(t).map((e=>String(e))),dependents:this.graph.getDependents(t).map((e=>String(e))),stateDependencies:s?Array.from(s.stateDependencies):[],renderCount:s?.buildCount??0})})),e}register(e){const{key:t}=e,r=t;this.registry.has(t)&&(console.warn(`[ArtifactContainer] Overwriting "${r}".`),this.manager.dispose(t).catch((e=>{console.error(`[ArtifactContainer] Failed to dispose existing artifact "${r}":`,e)}))),this.registry.register(e),this.graph.registerNode(t);const s=e.scope??"singleton";return(e.lazy??!0)||"singleton"!==s||this.resolve(t).catch((e=>{console.error(`[ArtifactContainer] Eager load failed for "${r}":`,e)})),()=>this.unregister(t)}async unregister(e){await this.manager.dispose(e),await this.registry.unregister(e)}async resolve(e){if(!this.registry.has(e))throw new l(e);return this.manager.build(e)}async require(e){const t=await this.resolve(e);if(t.error)throw t.error;return t.instance}watch(e){if(!this.registry.has(e))throw new l(e);return this.observer.watch(e)}peek(e){const t=this.cache.get(e);if(void 0!==t?.instance)return t?.instance}async invalidate(e,t=!1){return this.manager.invalidate(e,t)}notifyObservers(e){this.observer.notify(e)}hasWatchers(e){return this.observer.hasWatchers(e)}dispose(){this.registry.keys().forEach((e=>{this.manager.dispose(e).catch((t=>{console.error(`[ArtifactContainer] Failed to dispose artifact "${String(e)}":`,t)}))})),this.registry.clear(),this.cache.clear(),this.graph.clear(),this.observer.clear()}},exports.ArtifactScopes=h;
package/index.mjs CHANGED
@@ -1 +1 @@
1
- var e,t,r=Object.create,s=Object.defineProperty,n=Object.getOwnPropertyDescriptor,i=Object.getOwnPropertyNames,a=Object.getPrototypeOf,o=Object.prototype.hasOwnProperty,c=(e={"src/store/node_modules/@asaidimu/events/index.js"(e,t){var r,s=Object.defineProperty,n=Object.getOwnPropertyDescriptor,i=Object.getOwnPropertyNames,a=Object.prototype.hasOwnProperty,o={};((e,t)=>{for(var r in t)s(e,r,{get:t[r],enumerable:!0})})(o,{createEventBus:()=>c}),t.exports=(r=o,((e,t,r,o)=>{if(t&&"object"==typeof t||"function"==typeof t)for(let c of i(t))a.call(e,c)||c===r||s(e,c,{get:()=>t[c],enumerable:!(o=n(t,c))||o.enumerable});return e})(s({},"__esModule",{value:!0}),r));var c=(e={async:!1,batchSize:1e3,batchDelay:16,errorHandler:e=>console.error("EventBus Error:",e),crossTab:!1,channelName:"event-bus-channel"})=>{const t=new Map;let r=[],s=0,n=0;const i=new Map,a=new Map;let o=null;e.crossTab&&"undefined"!=typeof BroadcastChannel?o=new BroadcastChannel(e.channelName):e.crossTab&&console.warn("BroadcastChannel is not supported in this browser. Cross-tab notifications are disabled.");const c=(e,t)=>{s++,n+=t,i.set(e,(i.get(e)||0)+1)},h=()=>{const t=r;r=[],t.forEach((({name:t,payload:r})=>{const s=performance.now();try{(a.get(t)||[]).forEach((e=>e(r)))}catch(s){e.errorHandler({...s,eventName:t,payload:r})}c(t,performance.now()-s)}))},d=(()=>{let t;return()=>{clearTimeout(t),t=setTimeout(h,e.batchDelay)}})(),l=e=>{const r=t.get(e);r?a.set(e,Array.from(r)):a.delete(e)};return o&&(o.onmessage=e=>{const{name:t,payload:r}=e.data;(a.get(t)||[]).forEach((e=>e(r)))}),{subscribe:(e,r)=>{t.has(e)||t.set(e,new Set);const s=t.get(e);return s.add(r),l(e),()=>{s.delete(r),0===s.size?(t.delete(e),a.delete(e)):l(e)}},emit:({name:t,payload:s})=>{if(e.async)return r.push({name:t,payload:s}),r.length>=e.batchSize?h():d(),void(o&&o.postMessage({name:t,payload:s}));const n=performance.now();try{(a.get(t)||[]).forEach((e=>e(s))),o&&o.postMessage({name:t,payload:s})}catch(r){e.errorHandler({...r,eventName:t,payload:s})}c(t,performance.now()-n)},getMetrics:()=>({totalEvents:s,activeSubscriptions:Array.from(t.values()).reduce(((e,t)=>e+t.size),0),eventCounts:i,averageEmitDuration:s>0?n/s:0}),clear:()=>{t.clear(),a.clear(),r=[],s=0,n=0,i.clear(),o&&(o.close(),o=null)}}}}},function(){return t||(0,e[i(e)[0]])((t={exports:{}}).exports,t),t.exports}),h=(e=>(e.Singleton="singleton",e.Transient="transient",e))(h||{}),d=class e extends Error{category;constructor(t,r,s){super(t,{cause:s}),this.name="ArtifactError",this.category=r,Object.setPrototypeOf(this,e.prototype)}},l=class extends d{constructor(e){super(`[ArtifactContainer] Artifact "${e}" not found.`,"system")}},u=class extends d{constructor(e){super(`[ArtifactContainer] Artifact with key:${e} has already been disposed`,"system")}},p=class extends d{constructor(e){super(`[ArtifactContainer] Operation timed out: ${e}`,"system")}},g=class extends d{constructor(e){super(`[ArtifactContainer] An artifact with key:${e} already exists!`,"system")}},f=class extends d{constructor(){super("[Serializer] The serializer has been marked as done!","system")}},y=class{artifacts=new Map;register({key:e,factory:t,lazy:r,...s}){if(this.artifacts.has(e))throw new g(String(e));const{scope:n,...i}=s,a={key:e,factory:t,scope:s.scope??"singleton",lazy:void 0===r||r,...i};return this.artifacts.set(e,a),()=>this.unregister(e)}get(e){if(!this.has(e))throw new l(String(e));return this.artifacts.get(e)}has(e){return this.artifacts.has(e)}async unregister(e){this.artifacts.has(e)&&this.artifacts.delete(e)}size(){return this.artifacts.size}keys(){return Array.from(this.artifacts.keys())}clear(){this.artifacts.clear()}};((e,t,c)=>{c=null!=e?r(a(e)):{},((e,t,r,a)=>{if(t&&"object"==typeof t||"function"==typeof t)for(let c of i(t))o.call(e,c)||c===r||s(e,c,{get:()=>t[c],enumerable:!(a=n(t,c))||a.enumerable})})(e&&e.__esModule?c:s(c,"default",{value:e,enumerable:!0}),e)})(c());var m=class{_locked=!1;_capacity;waiters=[];constructor(e){this._capacity=e?.capacity??1/0}async lock(e){if(!this._locked)return void(this._locked=!0);if(this.waiters.length>=this._capacity)throw new Error(`Mutex queue is full (capacity: ${this._capacity})`);let t;const r=new Promise((e=>t=e));if(this.waiters.push(t),null!=e)try{await Promise.race([r,new Promise(((r,s)=>setTimeout((()=>{const e=this.waiters.indexOf(t);-1!==e&&this.waiters.splice(e,1),s(new p("Mutex lock timed out"))}),e)))])}catch(e){throw e}else await r}tryLock(){return!this._locked&&(this._locked=!0,!0)}unlock(){if(!this._locked)throw new Error("Mutex is not locked");const e=this.waiters.shift();e?setTimeout(e,0):this._locked=!1}locked(){return this._locked}pending(){return this.waiters.length}},w=class{constructor(e=!1,t=!1){this.retry=e,this.throws=t}mutex=new m;promise=null;_value=null;_error;_done=!1;async do(e,t){return this._done?this.peek():this.promise?this._awaitWithTimeout(this.promise,t,"Once do() timed out"):(await this.mutex.lock(),this.promise?(this.mutex.unlock(),this._awaitWithTimeout(this.promise,t,"Once do() timed out")):(this.promise=(async()=>{try{const t=await e();this._value=t,this._done=!0}catch(e){if(this._error=e,this.retry||(this._done=!0),this.throws)throw e}finally{this.retry&&!this._done&&(this.promise=null)}return this.peek()})(),this.mutex.unlock(),this._awaitWithTimeout(this.promise,t,"Once do() timed out")))}running(){return null!==this.promise&&!this._done}peek(){return{value:this._value,error:this._error}}get(){if(!this._done)throw new Error("Once operation is not yet complete");if(this._error)throw this._error;return this._value}reset(){this._done=!1,this.promise=null,this._value=null,this._error=void 0}resolved(){return this.promise}done(){return this._done}_awaitWithTimeout(e,t,r="Operation timed out"){return null==t?e:Promise.race([e,new Promise(((e,s)=>setTimeout((()=>s(new p(r))),t)))])}},v=class{mutex;_done=!1;_lastValue=null;_lastError=void 0;constructor(e){this.mutex=new m({capacity:e?.capacity??1e3})}async do(e,t){if(this._done)return{value:null,error:new f};try{await this.mutex.lock(t)}catch(e){return{value:null,error:e}}let r,s=null;try{if(this._done)throw new f;s=await e(),this._lastValue=s,this._lastError=void 0}catch(e){r=e,this._lastError=e}finally{this.mutex.unlock()}return{value:s,error:r}}peek(){return{value:this._lastValue,error:this._lastError}}close(){this._done=!0}pending(){return this.mutex.pending()}running(){return this.mutex.locked()}},b=class{constructor(e,t,r,s,n){this.registry=e,this.cache=t,this.graph=r,this.store=s,this.observer=n}async build(e,t){const r=this.registry.get(e);if(!r)throw new d(`Template not found for artifact "${String(e)}"`,"system");const s=t?new Set(t):new Set;if(s.has(e))throw new d(`Cycle detected: Artifact "${String(e)}" depends on itself via path: ${Array.from(s).join(" -> ")}`,"system");s.add(e);let n=this.cache.get(e);n||(n=this.createCachedArtifact(r),this.cache.set(e,n));const i=await("transient"===r.scope?this.executeBuild(r,n,s):n.buildOnce.do((()=>this.executeBuild(r,n,s))));if("transient"===r.scope)return i;const a=this.cache.package(e,((t,r)=>this.invalidate(e,t,r)));return n.stream&&n.streamOnce.do(n.stream),a}async executeBuild(e,t,r){const s=e.key;t.buildCount++;const n=[],i=[];let a=!0;t.activeDebounceMs=e.debounce??0,n.push((()=>{a=!1}));const o=new Set,c=new Set,h=new Map,l={state:()=>this.store.get(!0),previous:t.instance,onCleanup:e=>n.push(e),onDispose:e=>i.push(e),use:async e=>e({resolve:async e=>{if(e===s)throw new d(`Artifact "${String(s)}" depends on itself.`,"system");const t=this.graph.wouldCreateCycle(s,e,r);if(t)throw new d(`Adding dependency "${String(e)}" to "${String(s)}" would create a cycle: ${t.join(" -> ")}`,"system");c.add(e);const n=await this.build(e,r),i=this.cache.get(e);return i&&h.set(e,i.version),n},select:e=>(function(e,t="."){const r=new Set,s=(e=[])=>new Proxy({},{get:(n,i)=>{if("symbol"==typeof i)return;const a=[...e,i],o=a.join(t);return r.add(o),s(a)}});try{e(s())}catch(e){throw new Error(`Selector failed during path analysis. This usually means the selector is too complex. Selectors must be simple property accessors only. Error: ${e instanceof Error?e.message:String(e)}`)}const n=Array.from(r);return n.filter((e=>!n.some((r=>r!==e&&r.startsWith(e+t)))))}(e).forEach((e=>o.add(e))),e(this.store.get(!0)))}),stream:async r=>{if("transient"===e.scope)throw new d(`[ArtifactManager] Illegal stream on transient artifact "${String(s)}"`,"system");const n=async(e,r=void 0)=>{await t.streamSerializer.do((async()=>{void 0!==t.stream&&(t.instance=e,t.error=r,t.version++,this.cache.invalidatePackage(s),await this.processStream(s))}))},i={value:()=>t.instance,signal:t.streamController.signal,set:(...e)=>this.store.set(...e),emit:e=>n(e)};t.stream=async()=>{try{await r(i)}catch(e){await n(void 0,e),this.invalidate(s,!1,!0)}}}};let u,p,g=0;const f=(e.retries??0)+1;for(;g<f;)try{const t=e.factory(l);if(e.timeout){const r=new Promise(((t,r)=>setTimeout((()=>r(new Error(`Timeout: ${e.timeout}ms`))),e.timeout)));u=await Promise.race([t,r])}else u=await t;break}catch(e){if(e instanceof d)throw e;g++,g>=f&&(p=e)}if(a){for(const[e,t]of h){const r=this.cache.get(e);r&&r.version!==t&&(p=new d(`Build stale on arrival: Dependency "${String(e)}" changed during build.`,"system"))}if("singleton"===e.scope&&this.updateDependencyGraph(s,c,o),"singleton"===e.scope&&(t.cleanupFunctions=n,t.disposeFunctions=i),p?t.error=p:t.instance=u,t.version++,this.cache.invalidatePackage(s),"transient"===e.scope)return{instance:u,cleanup:this.createCompositeCleanup(n),error:p,ready:!0,invalidate:async()=>console.warn(`[ArtifactManager] Cannot invalidate transient "${String(s)}"`)}}else{const e=this.createCompositeCleanup(n);e&&await e()}}async invalidate(e,t=!1,r=!1){const s=this.cache.get(e);if(s)return s.debounceTimer&&(clearTimeout(s.debounceTimer),s.debounceTimer=void 0),!t&&s.activeDebounceMs>0?new Promise((n=>{s.debounceTimer=setTimeout((()=>{s.debounceTimer=void 0,this.executeInvalidation(e,t,r).then(n).catch(n)}),s.activeDebounceMs)})):this.executeInvalidation(e,t,r)}async executeInvalidation(e,t,r=!1){const s=this.cache.get(e);s&&(await s.invalidationOnce.do((async()=>{s.version++,await this.cache.invalidateInstance(e);const n=this.graph.getDependents(e);await Promise.all(n.map((e=>this.invalidate(e).catch((t=>{console.error(`[ArtifactManager] Cascade failed for "${String(e)}":`,t)})))));const i=this.registry.get(e);!i||!t&&i.lazy&&!this.hasWatchers(e)||r||await this.build(e),this.observer.notify(e)})),s.invalidationOnce.reset())}async dispose(e){const t=this.cache.get(e);t&&(t.streamController.abort(),t.buildOnce.running()&&await t.buildOnce.resolved(),t.invalidationOnce.running()&&await t.invalidationOnce.resolved(),t.streamOnce.running()&&await t.streamOnce.resolved(),await this.cache.invalidateInstance(e),this.graph.removeNode(e),this.cache.delete(e))}async processStream(e){try{const t=this.graph.getDependents(e);await Promise.all(t.map((e=>this.invalidate(e).catch((t=>{console.error(`[ArtifactManager] Failed to invalidate dependent "${String(e)}":`,t)}))))),this.observer.notify(e)}catch(t){console.error(`[ArtifactManager] Stream propagation error "${e}":`,t)}}updateDependencyGraph(e,t,r){const s=this.cache.get(e);s&&(this.graph.hasNode(e)||this.graph.registerNode(e),this.graph.setDependencies(e,t),s.stateUnsubscribe&&s.stateUnsubscribe(),s.stateDependencies=r,r.size>0&&(s.stateUnsubscribe=this.store.watch(Array.from(r),(async()=>{await this.invalidate(e)}))))}createCachedArtifact(e){return{instance:void 0,version:0,streamController:new AbortController,error:void 0,cleanupFunctions:[],disposeFunctions:[],buildCount:0,stateDependencies:new Set,buildOnce:new w(!0,!0),streamOnce:new w(!0,!0),streamSerializer:new v,activeDebounceMs:e.debounce??0,invalidationOnce:new w(!0,!1)}}createCompositeCleanup(e){if(e.length)return async()=>{for(let t=e.length-1;t>=0;t--)try{await e[t]()}catch(e){console.error("[ArtifactManager] Cleanup error:",e)}}}hasWatchers(e){return this.observer.hasWatchers(e)??!1}},C=class{dependencies=new Map;dependents=new Map;metadata=new Map;registerNode(e,t){this.dependencies.has(e)||this.dependencies.set(e,new Set),this.dependents.has(e)||this.dependents.set(e,new Set),void 0!==t&&this.metadata.set(e,t)}removeNode(e){if(!this.hasNode(e))return;const t=this.dependencies.get(e);if(t)for(const r of t)this.dependents.get(r)?.delete(e);const r=this.dependents.get(e);if(r)for(const t of r)this.dependencies.get(t)?.delete(e);this.dependencies.delete(e),this.dependents.delete(e),this.metadata.delete(e)}hasNode(e){return this.dependencies.has(e)}addDependency(e,t){this.registerNode(e),this.registerNode(t),this.dependencies.get(e).add(t),this.dependents.get(t).add(e)}removeDependency(e,t){this.dependencies.get(e)?.delete(t),this.dependents.get(t)?.delete(e)}getDependencies(e){const t=this.dependencies.get(e);return t?new Set(t):new Set}getDependents(e){const t=this.dependents.get(e);return t?new Set(t):new Set}getMetadata(e){return this.metadata.get(e)}setMetadata(e,t){this.registerNode(e),this.metadata.set(e,t)}setDependencies(e,t){this.registerNode(e);const r=this.dependencies.get(e),s=new Set(t);for(const t of r)s.has(t)||this.removeDependency(e,t);for(const t of s)r.has(t)||this.addDependency(e,t)}wouldCreateCycle(e,t){if(e===t)return[e,t];const r=[t],s=new Set([t]),n=new Map;for(;r.length>0;){const i=r.shift();if(i===e){const r=[];let s=e;for(;void 0!==s&&(r.push(s),s!==t);)s=n.get(s);return r.reverse(),[e,...r]}const a=this.dependencies.get(i);if(a)for(const e of a)s.has(e)||(s.add(e),n.set(e,i),r.push(e))}return null}topologicalSort(){const e=new Map,t=Array.from(this.dependencies.keys());for(const r of t)e.set(r,this.dependencies.get(r)?.size??0);const r=[];for(const[t,s]of e)0===s&&r.push(t);const s=[];for(;r.length>0;){const t=r.shift();s.push(t);const n=this.dependents.get(t);if(n)for(const t of n){const s=(e.get(t)||0)-1;e.set(t,s),0===s&&r.push(t)}}if(s.length!==t.length)throw new Error("Cycle detected in graph; topological sort impossible.");return s}getTransitiveDependencies(e,t=!1){return this.bfs(e,"dependencies",t)}getTransitiveDependents(e,t=!1){return this.bfs(e,"dependents",t)}bfs(e,t,r){const s=new Set,n=new Set,i=[e];n.add(e),r&&s.add(e);const a="dependencies"===t?this.dependencies:this.dependents;for(;i.length>0;){const e=i.shift(),t=a.get(e);if(t)for(const e of t)n.has(e)||(n.add(e),s.add(e),i.push(e))}return s}getAllNodes(){return Array.from(this.dependencies.keys())}size(){return this.dependencies.size}clear(){this.dependencies.clear(),this.dependents.clear(),this.metadata.clear()}toDebugString(){return Array.from(this.dependencies.entries()).map((([e,t])=>{const r=Array.from(t).join(", ");return`${String(e)} → [${r||"∅"}]`})).join("\n")}},A=class{graph;constructor(){this.graph=new C}registerNode(e){this.graph.registerNode(e)}removeNode(e){this.graph.removeNode(e)}addDependency(e,t){this.graph.addDependency(e,t)}removeDependency(e,t){this.graph.removeDependency(e,t)}getDependents(e){const t=this.graph.getDependents(e);return Array.from(t)}getDependencies(e){const t=this.graph.getDependencies(e);return Array.from(t)}getTransitiveDependents(e){const t=this.graph.getTransitiveDependents(e,!1);return new Set(Array.from(t))}setDependencies(e,t){const r=Array.from(t).map((e=>e));this.graph.setDependencies(e,r)}wouldCreateCycle(e,t,r){if(r?.has(t)){const e=Array.from(r),s=e.indexOf(t),n=e.slice(s);return n.push(t),n}const s=this.graph.wouldCreateCycle(e,t);return s||null}hasNode(e){return this.graph.hasNode(e)}getAllNodes(){return this.graph.getAllNodes()}clear(){this.graph.clear()}size(){return this.graph.size()}toDebugString(){return this.graph.toDebugString()}},_=class{cache=new Map;emptyInstance={instance:void 0,error:void 0,ready:!1,cleanup:void 0,invalidate:async(e,t)=>{}};get(e){return this.cache.get(e)}set(e,t){this.cache.set(e,t)}delete(e){this.cache.delete(e)}has(e){return this.cache.has(e)}clear(){this.cache.clear()}size(){return this.cache.size}keys(){return Array.from(this.cache.keys())}package(e,t){const r=this.get(e);if(!r)return this.emptyInstance;if(r.packagedArtifact)return this.updatePackagedArtifact(r,r.packagedArtifact,t),r.packagedArtifact;const s={instance:r.instance,error:r.error,ready:r.buildOnce.done()&&!r.buildOnce.running(),cleanup:this.createCompositeCleanup(r.cleanupFunctions),invalidate:t};return r.packagedArtifact=s,s}async invalidatePackage(e){const t=this.get(e);t&&(t.packagedArtifact=void 0)}async invalidateInstance(e){const t=this.get(e);if(t){t.stateUnsubscribe&&(t.stateUnsubscribe(),t.stateUnsubscribe=void 0),t.debounceTimer&&(clearTimeout(t.debounceTimer),t.debounceTimer=void 0),t.streamController.abort(),await t.streamOnce.resolved(),t.streamOnce.reset(),t.streamSerializer.close();for(let e=t.cleanupFunctions.length-1;e>=0;e--)try{await t.cleanupFunctions[e]()}catch(e){console.error("[ArtifactManager] Cleanup error:",e)}for(let e=t.disposeFunctions.length-1;e>=0;e--)try{await t.disposeFunctions[e]()}catch(e){console.error("[ArtifactManager] Dispose error:",e)}t.instance=void 0,t.error=void 0,t.cleanupFunctions=[],t.disposeFunctions=[],t.buildOnce.reset(),t.stream=void 0,t.streamSerializer=new v,t.streamController=new AbortController}}updatePackagedArtifact(e,t,r){t.instance=e.instance,t.error=e.error,t.ready=e.buildOnce.done()&&!e.buildOnce.running(),t.cleanup=this.createCompositeCleanup(e.cleanupFunctions),t.invalidate=r}createCompositeCleanup(e){if(e.length)return async()=>{for(let t=e.length-1;t>=0;t--)try{await e[t]()}catch(e){console.error("[ArtifactCache] Cleanup error:",e)}}}},k=class{constructor(e,t,r){this.registry=e,this.cache=t,this.container=r}listeners=new Map;watcherCache=new Map;watch(e){const t=String(e),r=this.registry.get(e);if(!r)throw new Error(`Artifact "${t}" not registered`);let s=t;if("transient"===r.scope&&(s=`${t}__watched`,this.registry.has(s)||this.registry.register({key:s,factory:r.factory,scope:"singleton",lazy:r.lazy,timeout:r.timeout,retries:r.retries,debounce:r.debounce})),this.watcherCache.has(s)){const e=this.watcherCache.get(s);return e.count++,e}let n=!1;const i={id:t,count:1,get:()=>{if(!this.cache.get(s))return this.cache.package(s,(e=>this.container.invalidate(s,e)));const e=this.cache.package(s,(e=>this.container.invalidate(s,e)));return n?{...e,ready:!1,instance:void 0,error:new u(t),cleanup:()=>{},invalidate:()=>Promise.resolve()}:e},subscribe:e=>{this.listeners.has(s)||this.listeners.set(s,new Set);const t=()=>e(i.get());return this.listeners.get(s).add(t),this.container.resolve(s).catch((e=>{console.error(`[ArtifactObserver] Background resolution failed for "${s}":`,e)})).then(t),()=>this.listeners.get(s)?.delete(t)},dispose:async()=>{i.count--,i.count<=0&&(this.watcherCache.delete(s),"transient"===r.scope&&(await this.registry.unregister(s),this.cache.delete(s)),n=!0,this.notify(s),this.listeners.delete(s))}};return this.watcherCache.set(s,i),i}notify(e){const t=this.listeners.get(e);t&&t.forEach((t=>{try{t()}catch(t){console.error(`[ArtifactObserver] Listener error for "${e}":`,t)}}))}hasWatchers(e){return this.watcherCache.has(e)||this.watcherCache.has(`${e}__watched`)}getWatcherCount(e){const t=this.watcherCache.get(e)||this.watcherCache.get(`${e}__watched`);return t?.count??0}clear(){this.watcherCache.clear(),this.listeners.clear()}},S=class{registry;cache;graph;manager;observer;store;constructor(e){this.store={watch:(...t)=>e.watch(...t),get:()=>e.get(!0),set:(...t)=>e.set(...t)},this.registry=new y,this.cache=new _,this.graph=new A,this.observer=new k(this.registry,this.cache,this),this.manager=new b(this.registry,this.cache,this.graph,this.store,this.observer)}debugInfo(){const e=[];return this.registry.keys().forEach((t=>{const r=this.registry.get(t),s=this.cache.get(t);if(!r)return;const n=s?s.buildOnce.running()?"pending":s.error?"error":void 0!==s.instance?"active":"idle":"idle";e.push({id:t,scope:r.scope??"singleton",status:n,dependencies:this.graph.getDependencies(t).map((e=>String(e))),dependents:this.graph.getDependents(t).map((e=>String(e))),stateDependencies:s?Array.from(s.stateDependencies):[],renderCount:s?.buildCount??0})})),e}register(e){const{key:t}=e,r=t;this.registry.has(t)&&(console.warn(`[ArtifactContainer] Overwriting "${r}".`),this.manager.dispose(t).catch((e=>{console.error(`[ArtifactContainer] Failed to dispose existing artifact "${r}":`,e)}))),this.registry.register(e),this.graph.registerNode(t);const s=e.scope??"singleton";return(e.lazy??!0)||"singleton"!==s||this.resolve(t).catch((e=>{console.error(`[ArtifactContainer] Eager load failed for "${r}":`,e)})),()=>this.unregister(t)}async unregister(e){await this.manager.dispose(e),await this.registry.unregister(e)}async resolve(e){if(!this.registry.has(e))throw new l(e);return this.manager.build(e)}watch(e){if(!this.registry.has(e))throw new l(e);return this.observer.watch(e)}peek(e){const t=this.cache.get(e);if(void 0!==t?.instance)return t?.instance}async invalidate(e,t=!1){return this.manager.invalidate(e,t)}notifyObservers(e){this.observer.notify(e)}hasWatchers(e){return this.observer.hasWatchers(e)}dispose(){this.registry.keys().forEach((e=>{this.manager.dispose(e).catch((t=>{console.error(`[ArtifactContainer] Failed to dispose artifact "${String(e)}":`,t)}))})),this.registry.clear(),this.cache.clear(),this.graph.clear(),this.observer.clear()}};export{S as ArtifactContainer,h as ArtifactScopes};
1
+ var e,t,r=Object.create,s=Object.defineProperty,n=Object.getOwnPropertyDescriptor,i=Object.getOwnPropertyNames,a=Object.getPrototypeOf,o=Object.prototype.hasOwnProperty,c=(e={"node_modules/@asaidimu/events/index.js"(e,t){var r,s=Object.defineProperty,n=Object.getOwnPropertyDescriptor,i=Object.getOwnPropertyNames,a=Object.prototype.hasOwnProperty,o={};((e,t)=>{for(var r in t)s(e,r,{get:t[r],enumerable:!0})})(o,{createEventBus:()=>c}),t.exports=(r=o,((e,t,r,o)=>{if(t&&"object"==typeof t||"function"==typeof t)for(let c of i(t))a.call(e,c)||c===r||s(e,c,{get:()=>t[c],enumerable:!(o=n(t,c))||o.enumerable});return e})(s({},"__esModule",{value:!0}),r));var c=(e={async:!1,batchSize:1e3,batchDelay:16,errorHandler:e=>console.error("EventBus Error:",e),crossTab:!1,channelName:"event-bus-channel"})=>{const t=new Map;let r=[],s=0,n=0;const i=new Map,a=new Map;let o=null;e.crossTab&&"undefined"!=typeof BroadcastChannel?o=new BroadcastChannel(e.channelName):e.crossTab&&console.warn("BroadcastChannel is not supported in this browser. Cross-tab notifications are disabled.");const c=(e,t)=>{s++,n+=t,i.set(e,(i.get(e)||0)+1)},h=()=>{const t=r;r=[],t.forEach((({name:t,payload:r})=>{const s=performance.now();try{(a.get(t)||[]).forEach((e=>e(r)))}catch(s){e.errorHandler({...s,eventName:t,payload:r})}c(t,performance.now()-s)}))},d=(()=>{let t;return()=>{clearTimeout(t),t=setTimeout(h,e.batchDelay)}})(),l=e=>{const r=t.get(e);r?a.set(e,Array.from(r)):a.delete(e)};return o&&(o.onmessage=e=>{const{name:t,payload:r}=e.data;(a.get(t)||[]).forEach((e=>e(r)))}),{subscribe:(e,r)=>{t.has(e)||t.set(e,new Set);const s=t.get(e);return s.add(r),l(e),()=>{s.delete(r),0===s.size?(t.delete(e),a.delete(e)):l(e)}},emit:({name:t,payload:s})=>{if(e.async)return r.push({name:t,payload:s}),r.length>=e.batchSize?h():d(),void(o&&o.postMessage({name:t,payload:s}));const n=performance.now();try{(a.get(t)||[]).forEach((e=>e(s))),o&&o.postMessage({name:t,payload:s})}catch(r){e.errorHandler({...r,eventName:t,payload:s})}c(t,performance.now()-n)},getMetrics:()=>({totalEvents:s,activeSubscriptions:Array.from(t.values()).reduce(((e,t)=>e+t.size),0),eventCounts:i,averageEmitDuration:s>0?n/s:0}),clear:()=>{t.clear(),a.clear(),r=[],s=0,n=0,i.clear(),o&&(o.close(),o=null)}}}}},function(){return t||(0,e[i(e)[0]])((t={exports:{}}).exports,t),t.exports}),h=(e=>(e.Singleton="singleton",e.Transient="transient",e))(h||{}),d=class e extends Error{category;constructor(t,r,s){super(t,{cause:s}),this.name="ArtifactError",this.category=r,Object.setPrototypeOf(this,e.prototype)}},l=class extends d{constructor(e){super(`[ArtifactContainer] Artifact "${e}" not found.`,"system")}},u=class extends d{constructor(e){super(`[ArtifactContainer] Operation timed out: ${e}`,"system")}},p=class extends d{constructor(e){super(`[ArtifactContainer] An artifact with key:${e} already exists!`,"system")}},g=class extends d{constructor(){super("[Serializer] The serializer has been marked as done!","system")}},f=class{artifacts=new Map;register({key:e,factory:t,lazy:r,...s}){if(this.artifacts.has(e))throw new p(String(e));const{scope:n,...i}=s,a={key:e,factory:t,scope:s.scope??"singleton",lazy:void 0===r||r,...i};return this.artifacts.set(e,a),()=>this.unregister(e)}get(e){if(!this.has(e))throw new l(String(e));return this.artifacts.get(e)}has(e){return this.artifacts.has(e)}async unregister(e){this.artifacts.has(e)&&this.artifacts.delete(e)}size(){return this.artifacts.size}keys(){return Array.from(this.artifacts.keys())}clear(){this.artifacts.clear()}};((e,t,c)=>{c=null!=e?r(a(e)):{},((e,t,r,a)=>{if(t&&"object"==typeof t||"function"==typeof t)for(let c of i(t))o.call(e,c)||c===r||s(e,c,{get:()=>t[c],enumerable:!(a=n(t,c))||a.enumerable})})(e&&e.__esModule?c:s(c,"default",{value:e,enumerable:!0}),e)})(c());var y=class{_locked=!1;_capacity;waiters=[];constructor(e){this._capacity=e?.capacity??1/0}async lock(e){if(!this._locked)return void(this._locked=!0);if(this.waiters.length>=this._capacity)throw new Error(`Mutex queue is full (capacity: ${this._capacity})`);let t;const r=new Promise((e=>t=e));if(this.waiters.push(t),null!=e)try{await Promise.race([r,new Promise(((r,s)=>setTimeout((()=>{const e=this.waiters.indexOf(t);-1!==e&&this.waiters.splice(e,1),s(new u("Mutex lock timed out"))}),e)))])}catch(e){throw e}else await r}tryLock(){return!this._locked&&(this._locked=!0,!0)}unlock(){if(!this._locked)throw new Error("Mutex is not locked");const e=this.waiters.shift();e?setTimeout(e,0):this._locked=!1}locked(){return this._locked}pending(){return this.waiters.length}},m=class{constructor(e=!1,t=!1){this.retry=e,this.throws=t}mutex=new y;promise=null;_value=null;_error;_done=!1;async do(e,t){return this._done?this.peek():this.promise?this._awaitWithTimeout(this.promise,t,"Once do() timed out"):(await this.mutex.lock(),this.promise?(this.mutex.unlock(),this._awaitWithTimeout(this.promise,t,"Once do() timed out")):(this.promise=(async()=>{try{const t=await e();this._value=t,this._done=!0}catch(e){if(this._error=e,this.retry||(this._done=!0),this.throws)throw e}finally{this.retry&&!this._done&&(this.promise=null)}return this.peek()})(),this.mutex.unlock(),this._awaitWithTimeout(this.promise,t,"Once do() timed out")))}running(){return null!==this.promise&&!this._done}peek(){return{value:this._value,error:this._error}}get(){if(!this._done)throw new Error("Once operation is not yet complete");if(this._error)throw this._error;return this._value}reset(){this._done=!1,this.promise=null,this._value=null,this._error=void 0}resolved(){return this.promise}done(){return this._done}_awaitWithTimeout(e,t,r="Operation timed out"){return null==t?e:Promise.race([e,new Promise(((e,s)=>setTimeout((()=>s(new u(r))),t)))])}},w=class{mutex;_done=!1;_lastValue=null;_lastError=void 0;constructor(e){this.mutex=new y({capacity:e?.capacity??1e3})}async do(e,t){if(this._done)return{value:null,error:new g};try{await this.mutex.lock(t)}catch(e){return{value:null,error:e}}let r,s=null;try{if(this._done)throw new g;s=await e(),this._lastValue=s,this._lastError=void 0}catch(e){r=e,this._lastError=e}finally{this.mutex.unlock()}return{value:s,error:r}}peek(){return{value:this._lastValue,error:this._lastError}}close(){this._done=!0}pending(){return this.mutex.pending()}running(){return this.mutex.locked()}},v=class{constructor(e,t,r,s,n){this.registry=e,this.cache=t,this.graph=r,this.store=s,this.observer=n}async build(e,t){const r=this.registry.get(e);if(!r)throw new d(`Template not found for artifact "${String(e)}"`,"system");const s=t?new Set(t):new Set;if(s.has(e))throw new d(`Cycle detected: Artifact "${String(e)}" depends on itself via path: ${Array.from(s).join(" -> ")}`,"system");s.add(e);let n=this.cache.get(e);n||(n=this.createCachedArtifact(r),this.cache.set(e,n));const i=await("transient"===r.scope?this.executeBuild(r,n,s):n.buildOnce.do((()=>this.executeBuild(r,n,s))));if("transient"===r.scope)return i;const a=this.cache.package(e,((t,r)=>this.invalidate(e,t,r)));return n.stream&&n.streamOnce.do(n.stream),a}async executeBuild(e,t,r){const s=e.key;t.buildCount++;const n=[],i=[];let a=!0;t.activeDebounceMs=e.debounce??0,n.push((()=>{a=!1}));const o=new Set,c=new Set,h=new Map,l=this.graph,u=this.cache,p=this.build.bind(this);async function g(e){if(e===s)throw new d(`Artifact "${String(s)}" depends on itself.`,"system");const t=l.wouldCreateCycle(s,e,r);if(t)throw new d(`Adding dependency "${String(e)}" to "${String(s)}" would create a cycle: ${t.join(" -> ")}`,"system");c.add(e);const n=await p(e,r),i=u.get(e);return i&&h.set(e,i.version),n}const f={state:()=>this.store.get(!0),previous:t.instance,onCleanup:e=>n.push(e),onDispose:e=>i.push(e),use:async e=>e({resolve:g,require:async e=>{const t=await g(e);if(t.error)throw t.error;return t.instance},select:e=>(function(e,t="."){const r=new Set,s=(e=[])=>new Proxy({},{get:(n,i)=>{if("symbol"==typeof i)return;const a=[...e,i],o=a.join(t);return r.add(o),s(a)}});try{e(s())}catch(e){throw new Error(`Selector failed during path analysis. This usually means the selector is too complex. Selectors must be simple property accessors only. Error: ${e instanceof Error?e.message:String(e)}`)}const n=Array.from(r);return n.filter((e=>!n.some((r=>r!==e&&r.startsWith(e+t)))))}(e).forEach((e=>o.add(e))),e(this.store.get(!0)))}),stream:async r=>{if("transient"===e.scope)throw new d(`[ArtifactManager] Illegal stream on transient artifact "${String(s)}"`,"system");const n=async(e,r=void 0)=>{await t.streamSerializer.do((async()=>{void 0!==t.stream&&(t.instance=e,t.error=r,t.version++,this.cache.invalidatePackage(s),await this.processStream(s))}))},i={value:()=>t.instance,signal:t.streamController.signal,set:(...e)=>this.store.set(...e),emit:e=>n(e)};t.stream=async()=>{try{await r(i)}catch(e){await n(void 0,e),this.invalidate(s,!1,!0)}}}};let y,m,w=0;const v=(e.retries??0)+1;for(;w<v;)try{const t=e.factory(f);if(e.timeout){const r=new Promise(((t,r)=>setTimeout((()=>r(new Error(`Timeout: ${e.timeout}ms`))),e.timeout)));y=await Promise.race([t,r])}else y=await t;break}catch(e){if(e instanceof d)throw e;w++,w>=v&&(m=e)}if(a){for(const[e,t]of h){const r=this.cache.get(e);r&&r.version!==t&&(m=new d(`Build stale on arrival: Dependency "${String(e)}" changed during build.`,"system"))}if("singleton"===e.scope&&this.updateDependencyGraph(s,c,o),"singleton"===e.scope&&(t.cleanupFunctions=n,t.disposeFunctions=i),m?t.error=m:t.instance=y,t.version++,this.cache.invalidatePackage(s),"transient"===e.scope)return{instance:y,cleanup:this.createCompositeCleanup(n),error:m,ready:!0,invalidate:async()=>console.warn(`[ArtifactManager] Cannot invalidate transient "${String(s)}"`)}}else{const e=this.createCompositeCleanup(n);e&&await e()}}async invalidate(e,t=!1,r=!1){const s=this.cache.get(e);if(s)return s.debounceTimer&&(clearTimeout(s.debounceTimer),s.debounceTimer=void 0),!t&&s.activeDebounceMs>0?new Promise((n=>{s.debounceTimer=setTimeout((()=>{s.debounceTimer=void 0,this.executeInvalidation(e,t,r).then(n).catch(n)}),s.activeDebounceMs)})):this.executeInvalidation(e,t,r)}async executeInvalidation(e,t,r=!1){const s=this.cache.get(e);s&&(await s.invalidationOnce.do((async()=>{s.version++,await this.cache.invalidateInstance(e);const n=this.graph.getDependents(e);await Promise.all(n.map((e=>this.invalidate(e).catch((t=>{console.error(`[ArtifactManager] Cascade failed for "${String(e)}":`,t)})))));const i=this.registry.get(e);!i||!t&&i.lazy&&!this.hasWatchers(e)||r||await this.build(e),this.observer.notify(e)})),s.invalidationOnce.reset())}async dispose(e){const t=this.cache.get(e);t&&(t.streamController.abort(),t.buildOnce.running()&&await t.buildOnce.resolved(),t.invalidationOnce.running()&&await t.invalidationOnce.resolved(),t.streamOnce.running()&&await t.streamOnce.resolved(),await this.cache.invalidateInstance(e),this.graph.removeNode(e),this.cache.delete(e))}async processStream(e){try{const t=this.graph.getDependents(e);await Promise.all(t.map((e=>this.invalidate(e).catch((t=>{console.error(`[ArtifactManager] Failed to invalidate dependent "${String(e)}":`,t)}))))),this.observer.notify(e)}catch(t){console.error(`[ArtifactManager] Stream propagation error "${e}":`,t)}}updateDependencyGraph(e,t,r){const s=this.cache.get(e);s&&(this.graph.hasNode(e)||this.graph.registerNode(e),this.graph.setDependencies(e,t),s.stateUnsubscribe&&s.stateUnsubscribe(),s.stateDependencies=r,r.size>0&&(s.stateUnsubscribe=this.store.watch(Array.from(r),(async()=>{await this.invalidate(e)}))))}createCachedArtifact(e){return{instance:void 0,version:0,streamController:new AbortController,error:void 0,cleanupFunctions:[],disposeFunctions:[],buildCount:0,stateDependencies:new Set,buildOnce:new m(!0,!0),streamOnce:new m(!0,!0),streamSerializer:new w,activeDebounceMs:e.debounce??0,invalidationOnce:new m(!0,!1)}}createCompositeCleanup(e){if(e.length)return async()=>{for(let t=e.length-1;t>=0;t--)try{await e[t]()}catch(e){console.error("[ArtifactManager] Cleanup error:",e)}}}hasWatchers(e){return this.observer.hasWatchers(e)??!1}},b=class{dependencies=new Map;dependents=new Map;metadata=new Map;registerNode(e,t){this.dependencies.has(e)||this.dependencies.set(e,new Set),this.dependents.has(e)||this.dependents.set(e,new Set),void 0!==t&&this.metadata.set(e,t)}removeNode(e){if(!this.hasNode(e))return;const t=this.dependencies.get(e);if(t)for(const r of t)this.dependents.get(r)?.delete(e);const r=this.dependents.get(e);if(r)for(const t of r)this.dependencies.get(t)?.delete(e);this.dependencies.delete(e),this.dependents.delete(e),this.metadata.delete(e)}hasNode(e){return this.dependencies.has(e)}addDependency(e,t){this.registerNode(e),this.registerNode(t),this.dependencies.get(e).add(t),this.dependents.get(t).add(e)}removeDependency(e,t){this.dependencies.get(e)?.delete(t),this.dependents.get(t)?.delete(e)}getDependencies(e){const t=this.dependencies.get(e);return t?new Set(t):new Set}getDependents(e){const t=this.dependents.get(e);return t?new Set(t):new Set}getMetadata(e){return this.metadata.get(e)}setMetadata(e,t){this.registerNode(e),this.metadata.set(e,t)}setDependencies(e,t){this.registerNode(e);const r=this.dependencies.get(e),s=new Set(t);for(const t of r)s.has(t)||this.removeDependency(e,t);for(const t of s)r.has(t)||this.addDependency(e,t)}wouldCreateCycle(e,t){if(e===t)return[e,t];const r=[t],s=new Set([t]),n=new Map;for(;r.length>0;){const i=r.shift();if(i===e){const r=[];let s=e;for(;void 0!==s&&(r.push(s),s!==t);)s=n.get(s);return r.reverse(),[e,...r]}const a=this.dependencies.get(i);if(a)for(const e of a)s.has(e)||(s.add(e),n.set(e,i),r.push(e))}return null}topologicalSort(){const e=new Map,t=Array.from(this.dependencies.keys());for(const r of t)e.set(r,this.dependencies.get(r)?.size??0);const r=[];for(const[t,s]of e)0===s&&r.push(t);const s=[];for(;r.length>0;){const t=r.shift();s.push(t);const n=this.dependents.get(t);if(n)for(const t of n){const s=(e.get(t)||0)-1;e.set(t,s),0===s&&r.push(t)}}if(s.length!==t.length)throw new Error("Cycle detected in graph; topological sort impossible.");return s}getTransitiveDependencies(e,t=!1){return this.bfs(e,"dependencies",t)}getTransitiveDependents(e,t=!1){return this.bfs(e,"dependents",t)}bfs(e,t,r){const s=new Set,n=new Set,i=[e];n.add(e),r&&s.add(e);const a="dependencies"===t?this.dependencies:this.dependents;for(;i.length>0;){const e=i.shift(),t=a.get(e);if(t)for(const e of t)n.has(e)||(n.add(e),s.add(e),i.push(e))}return s}getAllNodes(){return Array.from(this.dependencies.keys())}size(){return this.dependencies.size}clear(){this.dependencies.clear(),this.dependents.clear(),this.metadata.clear()}toDebugString(){return Array.from(this.dependencies.entries()).map((([e,t])=>{const r=Array.from(t).join(", ");return`${String(e)} → [${r||"∅"}]`})).join("\n")}},C=class{graph;constructor(){this.graph=new b}registerNode(e){this.graph.registerNode(e)}removeNode(e){this.graph.removeNode(e)}addDependency(e,t){this.graph.addDependency(e,t)}removeDependency(e,t){this.graph.removeDependency(e,t)}getDependents(e){const t=this.graph.getDependents(e);return Array.from(t)}getDependencies(e){const t=this.graph.getDependencies(e);return Array.from(t)}getTransitiveDependents(e){const t=this.graph.getTransitiveDependents(e,!1);return new Set(Array.from(t))}setDependencies(e,t){const r=Array.from(t).map((e=>e));this.graph.setDependencies(e,r)}wouldCreateCycle(e,t,r){if(r?.has(t)){const e=Array.from(r),s=e.indexOf(t),n=e.slice(s);return n.push(t),n}const s=this.graph.wouldCreateCycle(e,t);return s||null}hasNode(e){return this.graph.hasNode(e)}getAllNodes(){return this.graph.getAllNodes()}clear(){this.graph.clear()}size(){return this.graph.size()}toDebugString(){return this.graph.toDebugString()}},_=class{cache=new Map;emptyInstance={instance:void 0,error:void 0,ready:!1,cleanup:void 0,invalidate:async(e,t)=>{}};get(e){return this.cache.get(e)}set(e,t){this.cache.set(e,t)}delete(e){this.cache.delete(e)}has(e){return this.cache.has(e)}clear(){this.cache.clear()}size(){return this.cache.size}keys(){return Array.from(this.cache.keys())}package(e,t){const r=this.get(e);if(!r)return this.emptyInstance;if(r.packagedArtifact)return this.updatePackagedArtifact(r,r.packagedArtifact,t),r.packagedArtifact;const s={instance:r.instance,error:r.error,ready:r.buildOnce.done()&&!r.buildOnce.running(),cleanup:this.createCompositeCleanup(r.cleanupFunctions),invalidate:t};return r.packagedArtifact=s,s}async invalidatePackage(e){const t=this.get(e);t&&(t.packagedArtifact=void 0)}async invalidateInstance(e){const t=this.get(e);if(t){t.stateUnsubscribe&&(t.stateUnsubscribe(),t.stateUnsubscribe=void 0),t.debounceTimer&&(clearTimeout(t.debounceTimer),t.debounceTimer=void 0),t.streamController.abort(),await t.streamOnce.resolved(),t.streamOnce.reset(),t.streamSerializer.close();for(let e=t.cleanupFunctions.length-1;e>=0;e--)try{await t.cleanupFunctions[e]()}catch(e){console.error("[ArtifactManager] Cleanup error:",e)}for(let e=t.disposeFunctions.length-1;e>=0;e--)try{await t.disposeFunctions[e]()}catch(e){console.error("[ArtifactManager] Dispose error:",e)}t.instance=void 0,t.error=void 0,t.cleanupFunctions=[],t.disposeFunctions=[],t.buildOnce.reset(),t.stream=void 0,t.streamSerializer=new w,t.streamController=new AbortController}}updatePackagedArtifact(e,t,r){t.instance=e.instance,t.error=e.error,t.ready=e.buildOnce.done()&&!e.buildOnce.running(),t.cleanup=this.createCompositeCleanup(e.cleanupFunctions),t.invalidate=r}createCompositeCleanup(e){if(e.length)return async()=>{for(let t=e.length-1;t>=0;t--)try{await e[t]()}catch(e){console.error("[ArtifactCache] Cleanup error:",e)}}}},A=class{constructor(e,t,r){this.registry=e,this.cache=t,this.container=r}listeners=new Map;watcherCache=new Map;watch(e){const t=String(e),r=this.registry.get(e);if(!r)throw new Error(`Artifact "${t}" not registered`);const s="transient"===r.scope?`${t}__watched`:t;if(this.watcherCache.has(s))return this.watcherCache.get(s).watcher;const n={count:0,init:new m(!1,!0),watcher:null},i=()=>n.init.do((async()=>{"transient"!==r.scope||this.registry.has(s)||this.registry.register({key:s,factory:r.factory,scope:"singleton",lazy:r.lazy,timeout:r.timeout,retries:r.retries,debounce:r.debounce})}));return n.watcher={id:t,get count(){return n.count},get:()=>{this.cache.get(s);return this.cache.package(s,(e=>this.container.invalidate(s,e)))},resolve:async()=>(await i(),this.container.resolve(s)),subscribe:e=>{0===n.count&&i(),n.count++,this.listeners.has(s)||this.listeners.set(s,new Set);const t=()=>e(n.watcher.get());return this.listeners.get(s).add(t),this.container.resolve(s).catch((e=>{console.error(`[ArtifactObserver] Background resolution failed for "${s}":`,e)})).then(t),()=>{this.listeners.get(s)?.delete(t),n.count--,0===n.count&&(this.listeners.delete(s),"transient"===r.scope&&(this.registry.unregister(s).catch((e=>{console.error(`[ArtifactObserver] Cleanup failed for "${s}":`,e)})),this.cache.delete(s)),n.init.reset())}}},this.watcherCache.set(s,n),n.watcher}notify(e){const t=this.listeners.get(e);t&&t.forEach((t=>{try{t()}catch(t){console.error(`[ArtifactObserver] Listener error for "${e}":`,t)}}))}hasWatchers(e){return this.watcherCache.has(e)||this.watcherCache.has(`${e}__watched`)}getWatcherCount(e){const t=this.watcherCache.get(e)||this.watcherCache.get(`${e}__watched`);return t?.count??0}clear(){this.watcherCache.clear(),this.listeners.clear()}},k=class{registry;cache;graph;manager;observer;store;constructor(e){this.store={watch:(...t)=>e.watch(...t),get:()=>e.get(!0),set:(...t)=>e.set(...t)},this.registry=new f,this.cache=new _,this.graph=new C,this.observer=new A(this.registry,this.cache,this),this.manager=new v(this.registry,this.cache,this.graph,this.store,this.observer)}debugInfo(){const e=[];return this.registry.keys().forEach((t=>{const r=this.registry.get(t),s=this.cache.get(t);if(!r)return;const n=s?s.buildOnce.running()?"pending":s.error?"error":void 0!==s.instance?"active":"idle":"idle";e.push({id:t,scope:r.scope??"singleton",status:n,dependencies:this.graph.getDependencies(t).map((e=>String(e))),dependents:this.graph.getDependents(t).map((e=>String(e))),stateDependencies:s?Array.from(s.stateDependencies):[],renderCount:s?.buildCount??0})})),e}register(e){const{key:t}=e,r=t;this.registry.has(t)&&(console.warn(`[ArtifactContainer] Overwriting "${r}".`),this.manager.dispose(t).catch((e=>{console.error(`[ArtifactContainer] Failed to dispose existing artifact "${r}":`,e)}))),this.registry.register(e),this.graph.registerNode(t);const s=e.scope??"singleton";return(e.lazy??!0)||"singleton"!==s||this.resolve(t).catch((e=>{console.error(`[ArtifactContainer] Eager load failed for "${r}":`,e)})),()=>this.unregister(t)}async unregister(e){await this.manager.dispose(e),await this.registry.unregister(e)}async resolve(e){if(!this.registry.has(e))throw new l(e);return this.manager.build(e)}async require(e){const t=await this.resolve(e);if(t.error)throw t.error;return t.instance}watch(e){if(!this.registry.has(e))throw new l(e);return this.observer.watch(e)}peek(e){const t=this.cache.get(e);if(void 0!==t?.instance)return t?.instance}async invalidate(e,t=!1){return this.manager.invalidate(e,t)}notifyObservers(e){this.observer.notify(e)}hasWatchers(e){return this.observer.hasWatchers(e)}dispose(){this.registry.keys().forEach((e=>{this.manager.dispose(e).catch((t=>{console.error(`[ArtifactContainer] Failed to dispose artifact "${String(e)}":`,t)}))})),this.registry.clear(),this.cache.clear(),this.graph.clear(),this.observer.clear()}};export{k as ArtifactContainer,h as ArtifactScopes};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@asaidimu/utils-artifacts",
3
- "version": "3.1.7",
3
+ "version": "4.0.0",
4
4
  "description": "Reactive artifact container.",
5
5
  "main": "index.js",
6
6
  "module": "index.mjs",