@sdux-vault/engine 0.25.0 → 0.26.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/fesm2022/sdux-vault-engine.mjs +577 -121
- package/fesm2022/sdux-vault-engine.mjs.map +1 -1
- package/package.json +5 -4
- package/types/sdux-vault-engine.d.ts +189 -10
|
@@ -4,14 +4,10 @@ import { __decorate } from 'tslib';
|
|
|
4
4
|
import { filter } from 'rxjs/operators';
|
|
5
5
|
import { EventBus, initDevtoolsWidget } from '@sdux-vault/devtools';
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
// FilePath: projects > engine > src > lib > utils > version > version.register.ts
|
|
9
|
-
// Updated: 2026-03-03 08:09
|
|
10
|
-
// Generated by pathcomment [tab] (see .vscode/typescript.code-snippets) or
|
|
11
|
-
// cmd+alt+j (see .vscode/keybindings.json)
|
|
12
|
-
// --- END AI MODEL FILE PATH ---
|
|
7
|
+
/** Package name used for version registration. */
|
|
13
8
|
const SDUX_PACKAGE = '@sdux-vault/engine';
|
|
14
|
-
|
|
9
|
+
/** Current package version registered with the Vault runtime. */
|
|
10
|
+
const SDUX_VERSION = '0.26.0';
|
|
15
11
|
registerVersion(SDUX_PACKAGE, SDUX_VERSION);
|
|
16
12
|
|
|
17
13
|
/**
|
|
@@ -24,11 +20,6 @@ var withCoreAbstainController_1;
|
|
|
24
20
|
* Core abstain controller responsible for participating in controller vote resolution.
|
|
25
21
|
* This controller observes incoming controller messages and emits a neutral vote without modifying pipeline flow.
|
|
26
22
|
*
|
|
27
|
-
* --RelatedStart--
|
|
28
|
-
* ControllerMessageShape
|
|
29
|
-
* ControllerVote
|
|
30
|
-
* ControllerVotes
|
|
31
|
-
* --RelatedEnd--
|
|
32
23
|
*/
|
|
33
24
|
let withCoreAbstainController = class withCoreAbstainController {
|
|
34
25
|
static { withCoreAbstainController_1 = this; }
|
|
@@ -57,7 +48,9 @@ let withCoreAbstainController = class withCoreAbstainController {
|
|
|
57
48
|
* Unique controller key identifying this instance.
|
|
58
49
|
*/
|
|
59
50
|
key;
|
|
51
|
+
/** Whether the FeatureCell has completed its initial pipeline cycle. */
|
|
60
52
|
#isInitialized = false;
|
|
53
|
+
/** Whether the initialization pipeline cycle failed. */
|
|
61
54
|
#initializationFailed = false;
|
|
62
55
|
/**
|
|
63
56
|
* Creates a new core conductor controller instance.
|
|
@@ -129,6 +122,11 @@ withCoreAbstainController = withCoreAbstainController_1 = __decorate([
|
|
|
129
122
|
], withCoreAbstainController);
|
|
130
123
|
|
|
131
124
|
var withCoreErrorController_1;
|
|
125
|
+
/**
|
|
126
|
+
* Core error controller responsible for handling failure messages in the
|
|
127
|
+
* controller vote resolution pipeline. This controller requests an abort
|
|
128
|
+
* on failure and abstains from all other message types.
|
|
129
|
+
*/
|
|
132
130
|
let withCoreErrorController = class withCoreErrorController {
|
|
133
131
|
static { withCoreErrorController_1 = this; }
|
|
134
132
|
controllerCtx;
|
|
@@ -156,6 +154,7 @@ let withCoreErrorController = class withCoreErrorController {
|
|
|
156
154
|
* Unique controller key identifying this instance.
|
|
157
155
|
*/
|
|
158
156
|
key;
|
|
157
|
+
/** Controller class context providing execution metadata. */
|
|
159
158
|
ctx;
|
|
160
159
|
/**
|
|
161
160
|
* Creates a new core conductor controller instance.
|
|
@@ -208,6 +207,7 @@ withCoreErrorController = withCoreErrorController_1 = __decorate([
|
|
|
208
207
|
})
|
|
209
208
|
], withCoreErrorController);
|
|
210
209
|
|
|
210
|
+
/** Enumeration of license lifecycle event identifiers. */
|
|
211
211
|
const LicenseEventTypes = {
|
|
212
212
|
RequireLicense: 'requireLicense',
|
|
213
213
|
ValidateLicense: 'validateLicense',
|
|
@@ -217,38 +217,79 @@ const LicenseEventTypes = {
|
|
|
217
217
|
DescribeControllers: 'describe-controllers'
|
|
218
218
|
};
|
|
219
219
|
|
|
220
|
+
/** Cached singleton instance of the licensing service. */
|
|
220
221
|
//eslint-disable-next-line
|
|
221
222
|
let singleton = null;
|
|
223
|
+
/**
|
|
224
|
+
* Initializes the global licensing service singleton.
|
|
225
|
+
*
|
|
226
|
+
* @param events$ - Subject used to emit licensing events.
|
|
227
|
+
* @param validation$ - Observable providing license validation results.
|
|
228
|
+
*/
|
|
222
229
|
function InitializeLicensingService(events$, validation$) {
|
|
223
230
|
if (singleton)
|
|
224
231
|
return;
|
|
225
232
|
singleton = new LicensingServiceInstance(events$, validation$);
|
|
226
233
|
}
|
|
234
|
+
/**
|
|
235
|
+
* Returns the global licensing service singleton.
|
|
236
|
+
*
|
|
237
|
+
* @returns The initialized licensing service contract.
|
|
238
|
+
*/
|
|
227
239
|
function LicensingService() {
|
|
228
240
|
if (!singleton) {
|
|
229
241
|
throw new Error('[vault] LicensingService not initialized.');
|
|
230
242
|
}
|
|
231
243
|
return singleton;
|
|
232
244
|
}
|
|
245
|
+
/** Internal implementation of the licensing service contract. */
|
|
233
246
|
class LicensingServiceInstance {
|
|
234
247
|
events$;
|
|
235
248
|
validation$;
|
|
249
|
+
/**
|
|
250
|
+
* Creates a new licensing service backed by the supplied event streams.
|
|
251
|
+
*
|
|
252
|
+
* @param events$ - Subject used to emit licensing events.
|
|
253
|
+
* @param validation$ - Observable providing license validation results.
|
|
254
|
+
*/
|
|
236
255
|
constructor(events$, validation$) {
|
|
237
256
|
this.events$ = events$;
|
|
238
257
|
this.validation$ = validation$;
|
|
239
258
|
}
|
|
259
|
+
/**
|
|
260
|
+
* Emits a feature description event for licensing inspection.
|
|
261
|
+
*
|
|
262
|
+
* @param event - Feature description event to emit.
|
|
263
|
+
*/
|
|
240
264
|
describeFeature(event) {
|
|
241
265
|
event.type = LicenseEventTypes.DescribeFeature;
|
|
242
266
|
this.events$.next(event);
|
|
243
267
|
}
|
|
268
|
+
/**
|
|
269
|
+
* Emits a behavior description event for licensing inspection.
|
|
270
|
+
*
|
|
271
|
+
* @param event - Behavior description event to emit.
|
|
272
|
+
*/
|
|
244
273
|
describeBehaviors(event) {
|
|
245
274
|
event.type = LicenseEventTypes.DescribeBehaviors;
|
|
246
275
|
this.events$.next(event);
|
|
247
276
|
}
|
|
277
|
+
/**
|
|
278
|
+
* Emits a controller description event for licensing inspection.
|
|
279
|
+
*
|
|
280
|
+
* @param event - Controller description event to emit.
|
|
281
|
+
*/
|
|
248
282
|
describeControllers(event) {
|
|
249
283
|
event.type = LicenseEventTypes.DescribeControllers;
|
|
250
284
|
this.events$.next(event);
|
|
251
285
|
}
|
|
286
|
+
/**
|
|
287
|
+
* Requests a license token for a behavior or controller.
|
|
288
|
+
*
|
|
289
|
+
* @param featureCellKey - Key of the FeatureCell requesting the license.
|
|
290
|
+
* @param key - Key of the behavior or controller requiring a license.
|
|
291
|
+
* @returns The generated license token.
|
|
292
|
+
*/
|
|
252
293
|
requestLicense(featureCellKey, key) {
|
|
253
294
|
if (!key) {
|
|
254
295
|
throw new Error('[vault] Cannot register controller license without a key.');
|
|
@@ -262,6 +303,14 @@ class LicensingServiceInstance {
|
|
|
262
303
|
});
|
|
263
304
|
return licenseToken;
|
|
264
305
|
}
|
|
306
|
+
/**
|
|
307
|
+
* Validates a previously requested license token.
|
|
308
|
+
*
|
|
309
|
+
* @param featureCellKey - Key of the FeatureCell owning the license.
|
|
310
|
+
* @param key - Key of the behavior or controller being validated.
|
|
311
|
+
* @param licenseToken - License token to validate.
|
|
312
|
+
* @param valid - Whether the license is approved.
|
|
313
|
+
*/
|
|
265
314
|
validateLicense(featureCellKey, key, licenseToken, valid) {
|
|
266
315
|
this.events$.next({
|
|
267
316
|
featureCellKey,
|
|
@@ -271,15 +320,22 @@ class LicensingServiceInstance {
|
|
|
271
320
|
valid
|
|
272
321
|
});
|
|
273
322
|
}
|
|
323
|
+
/** Generates a random alphanumeric license token. */
|
|
274
324
|
#generateLicenseToken() {
|
|
275
325
|
const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
|
|
276
326
|
const randomPart = (length) => Array.from({ length }, () => chars[Math.floor(Math.random() * chars.length)]).join('');
|
|
277
327
|
return `${randomPart(5)}-${randomPart(5)}`;
|
|
278
328
|
}
|
|
329
|
+
/**
|
|
330
|
+
* Returns an observable of license validation results.
|
|
331
|
+
*
|
|
332
|
+
* @returns Observable emitting validation outcomes.
|
|
333
|
+
*/
|
|
279
334
|
getLicenseValidation$() {
|
|
280
335
|
return this.validation$;
|
|
281
336
|
}
|
|
282
337
|
}
|
|
338
|
+
/** Resets the licensing service singleton for test isolation. */
|
|
283
339
|
const resetLicensingServiceForTests = () => {
|
|
284
340
|
if (!DevMode.active)
|
|
285
341
|
return;
|
|
@@ -287,17 +343,37 @@ const resetLicensingServiceForTests = () => {
|
|
|
287
343
|
};
|
|
288
344
|
|
|
289
345
|
var withCoreLicenseController_1;
|
|
346
|
+
/**
|
|
347
|
+
* Core license controller that gates pipeline execution based on the
|
|
348
|
+
* licensing service validation result. The controller subscribes to
|
|
349
|
+
* the license validation stream on construction and votes Deny until
|
|
350
|
+
* a license decision arrives, Abstain on approval, or Abort on denial.
|
|
351
|
+
*/
|
|
290
352
|
let withCoreLicenseController = class withCoreLicenseController {
|
|
291
353
|
static { withCoreLicenseController_1 = this; }
|
|
292
354
|
controllerCtx;
|
|
355
|
+
/** Static controller type assigned by the VaultController decorator. */
|
|
293
356
|
static type;
|
|
357
|
+
/** Static controller key assigned by the VaultController decorator. */
|
|
294
358
|
static key;
|
|
359
|
+
/** Static critical flag assigned by the VaultController decorator. */
|
|
295
360
|
static critical;
|
|
361
|
+
/** Instance controller type mirrored from the static declaration. */
|
|
296
362
|
type = withCoreLicenseController_1.type;
|
|
363
|
+
/** Instance critical flag mirrored from the static declaration. */
|
|
297
364
|
critical = withCoreLicenseController_1.critical;
|
|
365
|
+
/** Unique key identifying this controller instance. */
|
|
298
366
|
key;
|
|
367
|
+
/** Resolved license approval state: true, false, or null if pending. */
|
|
299
368
|
#licenseApproved = null;
|
|
369
|
+
/** Subscription to the licensing validation stream. */
|
|
300
370
|
#sub;
|
|
371
|
+
/**
|
|
372
|
+
* Subscribes to the licensing validation stream and signals approval or denial.
|
|
373
|
+
*
|
|
374
|
+
* @param key - Unique key for this controller instance.
|
|
375
|
+
* @param controllerCtx - Controller class context providing lifecycle hooks.
|
|
376
|
+
*/
|
|
301
377
|
constructor(key, controllerCtx) {
|
|
302
378
|
this.controllerCtx = controllerCtx;
|
|
303
379
|
this.key = key;
|
|
@@ -317,6 +393,12 @@ let withCoreLicenseController = class withCoreLicenseController {
|
|
|
317
393
|
}
|
|
318
394
|
});
|
|
319
395
|
}
|
|
396
|
+
/**
|
|
397
|
+
* Evaluates a controller message and returns a license-based vote.
|
|
398
|
+
*
|
|
399
|
+
* @param msg - The incoming controller message.
|
|
400
|
+
* @returns An observable emitting the controller vote.
|
|
401
|
+
*/
|
|
320
402
|
handleMessage(msg) {
|
|
321
403
|
vaultDebug(`${this.key} received "${msg.type}" for trace "${msg.traceId}".`);
|
|
322
404
|
switch (msg.type) {
|
|
@@ -334,10 +416,12 @@ let withCoreLicenseController = class withCoreLicenseController {
|
|
|
334
416
|
return of();
|
|
335
417
|
}
|
|
336
418
|
}
|
|
419
|
+
/** Unsubscribes from the licensing validation stream. */
|
|
337
420
|
destroy() {
|
|
338
421
|
this.#sub?.unsubscribe();
|
|
339
422
|
vaultWarn(`${this.key} - destroy unsubscribe`);
|
|
340
423
|
}
|
|
424
|
+
/** No-op reset handler for this controller. */
|
|
341
425
|
reset() {
|
|
342
426
|
vaultWarn(`${this.key} - reset noop`);
|
|
343
427
|
}
|
|
@@ -437,28 +521,21 @@ class Arbitrator {
|
|
|
437
521
|
}
|
|
438
522
|
}
|
|
439
523
|
|
|
440
|
-
|
|
441
|
-
// projects > engine > src > lib > types > monitor > vault-monitor-event-category.type.ts
|
|
524
|
+
/** Enumeration of monitor event category classifications. */
|
|
442
525
|
const VaultMonitorEventCategoryType = {
|
|
443
526
|
Boundary: 'boundary',
|
|
444
527
|
State: 'state',
|
|
445
528
|
Error: 'error'
|
|
446
529
|
};
|
|
447
530
|
|
|
448
|
-
|
|
449
|
-
// projects > engine > src > lib > types > monitor > vault-monitor-field-rule.type.ts
|
|
531
|
+
/** Enumeration of field emission rules for monitor event policies. */
|
|
450
532
|
const FieldRuleTypes = {
|
|
451
533
|
Never: 'never',
|
|
452
534
|
Optional: 'optional',
|
|
453
535
|
Required: 'required'
|
|
454
536
|
};
|
|
455
537
|
|
|
456
|
-
|
|
457
|
-
// FilePath: projects > engine > src > lib > monitor > event-policy > vault-monitor-category-field-policy.constant.ts
|
|
458
|
-
// Updated: 2026-03-06 16:29
|
|
459
|
-
// Generated by pathcomment [tab] (see .vscode/typescript.code-snippets) or
|
|
460
|
-
// cmd+alt+j (see .vscode/keybindings.json)
|
|
461
|
-
// --- END AI MODEL FILE PATH ---
|
|
538
|
+
/** Field emission rules indexed by monitor event category. */
|
|
462
539
|
const CATEGORY_FIELD_POLICY_CONSTANT = {
|
|
463
540
|
[VaultMonitorEventCategoryType.Boundary]: {
|
|
464
541
|
state: FieldRuleTypes.Never,
|
|
@@ -477,12 +554,7 @@ const CATEGORY_FIELD_POLICY_CONSTANT = {
|
|
|
477
554
|
}
|
|
478
555
|
};
|
|
479
556
|
|
|
480
|
-
|
|
481
|
-
// FilePath: projects > engine > src > lib > monitor > event-policy > vault-monitor-event-policy.constant.ts
|
|
482
|
-
// Updated: 2026-03-06 16:30
|
|
483
|
-
// Generated by pathcomment [tab] (see .vscode/typescript.code-snippets) or
|
|
484
|
-
// cmd+alt+j (see .vscode/keybindings.json)
|
|
485
|
-
// --- END AI MODEL FILE PATH ---
|
|
557
|
+
/** Maps normalized event names to their monitor event category classification. */
|
|
486
558
|
const EVENT_POLICY_CONSTANT = {
|
|
487
559
|
// ─────────────────────────────────────────────
|
|
488
560
|
// STATE
|
|
@@ -564,11 +636,6 @@ const EVENT_POLICY_CONSTANT = {
|
|
|
564
636
|
let instance$2 = null;
|
|
565
637
|
/**
|
|
566
638
|
* Returns the global VaultMonitorSharedState singleton instance.
|
|
567
|
-
* This function ensures a single shared state instance is created and reused across the runtime.
|
|
568
|
-
*
|
|
569
|
-
* --RelatedStart--
|
|
570
|
-
* VaultMonitorSharedStateInstance
|
|
571
|
-
* --RelatedEnd--
|
|
572
639
|
*
|
|
573
640
|
* @returns The global VaultMonitorSharedState instance.
|
|
574
641
|
*/
|
|
@@ -578,6 +645,13 @@ function VaultMonitorSharedState() {
|
|
|
578
645
|
}
|
|
579
646
|
return instance$2;
|
|
580
647
|
}
|
|
648
|
+
/**
|
|
649
|
+
* Mutable shared state consumed by all VaultMonitor instances.
|
|
650
|
+
*
|
|
651
|
+
* This class holds the global insight override and the per-cell
|
|
652
|
+
* insight registry so that every monitor operates on the same
|
|
653
|
+
* canonical state.
|
|
654
|
+
*/
|
|
581
655
|
class VaultMonitorSharedStateInstance {
|
|
582
656
|
/**
|
|
583
657
|
* Optional global insight definition that overrides per-cell insight
|
|
@@ -589,18 +663,11 @@ class VaultMonitorSharedStateInstance {
|
|
|
589
663
|
* Registry of FeatureCell insight configurations keyed by cell ID.
|
|
590
664
|
*
|
|
591
665
|
* Each entry indicates whether insight is enabled and contains the
|
|
592
|
-
* resolved list of
|
|
593
|
-
* that FeatureCell.
|
|
666
|
+
* resolved list of InsightConfig values associated with that FeatureCell.
|
|
594
667
|
*/
|
|
595
668
|
cellRegistry = new Map();
|
|
596
669
|
}
|
|
597
670
|
|
|
598
|
-
// --- AI Model File Path (DO NOT DELETE) ---
|
|
599
|
-
// FilePath: projects > engine > src > lib > monitor > vault-monitor.helper.ts
|
|
600
|
-
// Updated: 2026-03-06 16:27
|
|
601
|
-
// Generated by pathcomment [tab] (see .vscode/typescript.code-snippets) or
|
|
602
|
-
// cmd+alt+j (see .vscode/keybindings.json)
|
|
603
|
-
// --- END AI MODEL FILE PATH ---
|
|
604
671
|
/**
|
|
605
672
|
* Abstract helper base class used by both `VaultMonitor` and
|
|
606
673
|
* `VaultQueueMonitor`.
|
|
@@ -679,9 +746,22 @@ class VaultMonitorHelper {
|
|
|
679
746
|
activateGlobalInsights(definition) {
|
|
680
747
|
this.globalInsightOverride = definition;
|
|
681
748
|
}
|
|
749
|
+
/**
|
|
750
|
+
* Determines whether the given cell key belongs to a Chrome DevTools channel.
|
|
751
|
+
*
|
|
752
|
+
* @param cell - The FeatureCell key to check.
|
|
753
|
+
* @returns True when the cell is a DevTools-internal channel.
|
|
754
|
+
*/
|
|
682
755
|
isChromeDevTools(cell) {
|
|
683
756
|
return cell === DEVTOOLS_LOGGING_KEY_CONSTANT || cell === DEVTOOLS_AGGREGATE_KEY_CONSTANT;
|
|
684
757
|
}
|
|
758
|
+
/**
|
|
759
|
+
* Applies field-level emission policy to a pipeline event.
|
|
760
|
+
*
|
|
761
|
+
* @param event - The raw event to filter.
|
|
762
|
+
* @param insight - Optional insight configuration controlling field visibility.
|
|
763
|
+
* @returns The policy-filtered event.
|
|
764
|
+
*/
|
|
685
765
|
applyPolicy(event, insight) {
|
|
686
766
|
const category = EVENT_POLICY_CONSTANT[event.name]?.category ?? VaultMonitorEventCategoryType.Boundary;
|
|
687
767
|
const policy = CATEGORY_FIELD_POLICY_CONSTANT[category];
|
|
@@ -715,12 +795,6 @@ class VaultMonitorHelper {
|
|
|
715
795
|
}
|
|
716
796
|
}
|
|
717
797
|
|
|
718
|
-
// --- AI Model File Path (DO NOT DELETE) ---
|
|
719
|
-
// FilePath: projects > engine > src > lib > monitor > vault-monitor.service.ts
|
|
720
|
-
// Updated: 2026-03-03 09:10
|
|
721
|
-
// Generated by pathcomment [tab] (see .vscode/typescript.code-snippets) or
|
|
722
|
-
// cmd+alt+j (see .vscode/keybindings.json)
|
|
723
|
-
// --- END AI MODEL FILE PATH ---
|
|
724
798
|
/**
|
|
725
799
|
* Holds the singleton VaultMonitor instance.
|
|
726
800
|
*/
|
|
@@ -729,9 +803,6 @@ let instance$1 = null;
|
|
|
729
803
|
* Returns the global VaultMonitor singleton instance.
|
|
730
804
|
* This function ensures a single monitor instance is created and reused across the runtime.
|
|
731
805
|
*
|
|
732
|
-
* --RelatedStart--
|
|
733
|
-
* VaultMonitorContract
|
|
734
|
-
* --RelatedEnd--
|
|
735
806
|
*
|
|
736
807
|
* @returns The global VaultMonitor instance.
|
|
737
808
|
*/
|
|
@@ -745,9 +816,6 @@ function VaultMonitor() {
|
|
|
745
816
|
* Implements the VaultMonitorContract using shared helper functionality.
|
|
746
817
|
* This class provides the concrete singleton implementation backing the VaultMonitor entry point.
|
|
747
818
|
*
|
|
748
|
-
* --RelatedStart--
|
|
749
|
-
* VaultMonitorContract
|
|
750
|
-
* --RelatedEnd--
|
|
751
819
|
*/
|
|
752
820
|
class VaultMonitorInstance extends VaultMonitorHelper {
|
|
753
821
|
/**
|
|
@@ -769,10 +837,7 @@ class VaultMonitorInstance extends VaultMonitorHelper {
|
|
|
769
837
|
}
|
|
770
838
|
/**
|
|
771
839
|
* Maps a BehaviorContext into a StateSnapshot suitable for DevTools.
|
|
772
|
-
* This ensures each pipeline event includes a normalized representation
|
|
773
|
-
* of loading, value, error, and hasValue.
|
|
774
840
|
*
|
|
775
|
-
* @typeParam T - State value type.
|
|
776
841
|
* @param ctx - Immutable behavior context provided by the orchestrator.
|
|
777
842
|
* @returns A standardized StateSnapshot.
|
|
778
843
|
*/
|
|
@@ -1193,50 +1258,60 @@ class VaultMonitorInstance extends VaultMonitorHelper {
|
|
|
1193
1258
|
const name = 'warn';
|
|
1194
1259
|
this.#emitEventV2LifecycleNotification({ cell, behaviorKey, name, ctx, error: errorMessage });
|
|
1195
1260
|
}
|
|
1261
|
+
/** Prepends event type and boundary to the event name. */
|
|
1196
1262
|
#buildEventName(eventInput) {
|
|
1197
1263
|
eventInput.name = `${eventInput.type}:${eventInput.boundary}:${eventInput.name}`;
|
|
1198
1264
|
return eventInput;
|
|
1199
1265
|
}
|
|
1266
|
+
/** Emits a stage-start monitoring event. */
|
|
1200
1267
|
#emitEventV2StageStart(eventInput) {
|
|
1201
1268
|
eventInput.type = EventTypes.Stage;
|
|
1202
1269
|
eventInput.boundary = EventBoundaryTypes.Start;
|
|
1203
1270
|
this.#emitEventV2(this.#buildEventName(eventInput));
|
|
1204
1271
|
}
|
|
1272
|
+
/** Emits a stage-end monitoring event. */
|
|
1205
1273
|
#emitEventV2StageEnd(eventInput) {
|
|
1206
1274
|
eventInput.type = EventTypes.Stage;
|
|
1207
1275
|
eventInput.boundary = EventBoundaryTypes.End;
|
|
1208
1276
|
this.#emitEventV2(this.#buildEventName(eventInput));
|
|
1209
1277
|
}
|
|
1278
|
+
/** Emits a lifecycle-start monitoring event. */
|
|
1210
1279
|
#emitEventV2LifecycleStart(eventInput) {
|
|
1211
1280
|
eventInput.type = EventTypes.Lifecycle;
|
|
1212
1281
|
eventInput.boundary = EventBoundaryTypes.Start;
|
|
1213
1282
|
this.#emitEventV2(this.#buildEventName(eventInput));
|
|
1214
1283
|
}
|
|
1284
|
+
/** Emits a lifecycle-end monitoring event. */
|
|
1215
1285
|
#emitEventV2LifecycleEnd(eventInput) {
|
|
1216
1286
|
eventInput.type = EventTypes.Lifecycle;
|
|
1217
1287
|
eventInput.boundary = EventBoundaryTypes.End;
|
|
1218
1288
|
this.#emitEventV2(this.#buildEventName(eventInput));
|
|
1219
1289
|
}
|
|
1290
|
+
/** Emits a lifecycle-notification monitoring event. */
|
|
1220
1291
|
#emitEventV2LifecycleNotification(eventInput) {
|
|
1221
1292
|
eventInput.type = EventTypes.Lifecycle;
|
|
1222
1293
|
eventInput.boundary = EventBoundaryTypes.Notification;
|
|
1223
1294
|
this.#emitEventV2(this.#buildEventName(eventInput));
|
|
1224
1295
|
}
|
|
1296
|
+
/** Emits a conductor-notification monitoring event. */
|
|
1225
1297
|
#emitEventV2ConductorNotification(eventInput) {
|
|
1226
1298
|
eventInput.type = EventTypes.Conductor;
|
|
1227
1299
|
eventInput.boundary = EventBoundaryTypes.Notification;
|
|
1228
1300
|
this.#emitEventV2(this.#buildEventName(eventInput));
|
|
1229
1301
|
}
|
|
1302
|
+
/** Emits a controller-start monitoring event. */
|
|
1230
1303
|
#emitEventV2ControllerStart(eventInput) {
|
|
1231
1304
|
eventInput.type = EventTypes.Controller;
|
|
1232
1305
|
eventInput.boundary = EventBoundaryTypes.Start;
|
|
1233
1306
|
this.#emitEventV2(this.#buildEventName(eventInput));
|
|
1234
1307
|
}
|
|
1308
|
+
/** Emits a controller-end monitoring event. */
|
|
1235
1309
|
#emitEventV2ControllerEnd(eventInput) {
|
|
1236
1310
|
eventInput.type = EventTypes.Controller;
|
|
1237
1311
|
eventInput.boundary = EventBoundaryTypes.End;
|
|
1238
1312
|
this.#emitEventV2(this.#buildEventName(eventInput));
|
|
1239
1313
|
}
|
|
1314
|
+
/** Emits a controller-notification monitoring event. */
|
|
1240
1315
|
#emitEventV2ControllerNotification(eventInput) {
|
|
1241
1316
|
eventInput.type = EventTypes.Controller;
|
|
1242
1317
|
eventInput.boundary = EventBoundaryTypes.Notification;
|
|
@@ -1327,7 +1402,9 @@ const ControllerEventTypes = {
|
|
|
1327
1402
|
class DecisionEngine {
|
|
1328
1403
|
controllers;
|
|
1329
1404
|
events$;
|
|
1405
|
+
/** Arbitrator responsible for aggregating controller votes. */
|
|
1330
1406
|
#arbitrator = new Arbitrator();
|
|
1407
|
+
/** Vault monitor instance used to emit controller lifecycle events. */
|
|
1331
1408
|
#vaultMonitor = VaultMonitor();
|
|
1332
1409
|
/**
|
|
1333
1410
|
* Creates a new decision engine instance.
|
|
@@ -1472,12 +1549,7 @@ const PROTECTED_FEATURE_CELL_KEYS = new Set([
|
|
|
1472
1549
|
'hydrate'
|
|
1473
1550
|
]);
|
|
1474
1551
|
|
|
1475
|
-
|
|
1476
|
-
// FilePath: projects > engine > src > lib > types > vault-registration-license-status.type.ts
|
|
1477
|
-
// Updated: 2026-03-03 10:09
|
|
1478
|
-
// Generated by pathcomment [tab] (see .vscode/typescript.code-snippets) or
|
|
1479
|
-
// cmd+alt+j (see .vscode/keybindings.json)
|
|
1480
|
-
// --- END AI MODEL FILE PATH ---
|
|
1552
|
+
/** Enumeration of FeatureCell registration license validation statuses. */
|
|
1481
1553
|
const VaultRegistrationLicenseStatusTypes = {
|
|
1482
1554
|
NotRequired: 'not-required',
|
|
1483
1555
|
Pending: 'pending',
|
|
@@ -1572,34 +1644,58 @@ const KNOWN_VAULT_KEYS = new Set([
|
|
|
1572
1644
|
*/
|
|
1573
1645
|
const VAULT_LICENSE_ID = 'sdux-vault';
|
|
1574
1646
|
|
|
1575
|
-
|
|
1576
|
-
// FilePath: projects > engine > src > lib > factories > vault > vault-core.function.ts
|
|
1577
|
-
// Updated: 2026-03-02 19:52
|
|
1578
|
-
// Generated by pathcomment [tab] (see .vscode/typescript.code-snippets) or
|
|
1579
|
-
// cmd+alt+j (see .vscode/keybindings.json)
|
|
1580
|
-
// --- END AI MODEL FILE PATH ---
|
|
1647
|
+
/** Behavior key used to identify the core license behavior during validation. */
|
|
1581
1648
|
const CORE_LICENSE_BEHAVIOR_KEY = 'SDUX::Behavior::Core::License';
|
|
1649
|
+
/** Cached singleton instance of the VaultCore runtime. */
|
|
1582
1650
|
let instance = null;
|
|
1651
|
+
/**
|
|
1652
|
+
* Initializes the global Vault runtime singleton with the supplied configuration.
|
|
1653
|
+
*
|
|
1654
|
+
* @param config - Optional Vault configuration overriding defaults.
|
|
1655
|
+
*/
|
|
1583
1656
|
function VaultCore(config = {}) {
|
|
1584
1657
|
if (!instance) {
|
|
1585
1658
|
instance = new VaultCoreInstance(config);
|
|
1586
1659
|
}
|
|
1587
1660
|
}
|
|
1661
|
+
/**
|
|
1662
|
+
* Internal singleton managing Vault configuration, licensing, and the
|
|
1663
|
+
* FeatureCell registry. All mutable state is held in private fields and
|
|
1664
|
+
* exposed only through controlled public methods.
|
|
1665
|
+
*/
|
|
1588
1666
|
class VaultCoreInstance {
|
|
1667
|
+
/** Subscription to the licensing event bus. */
|
|
1589
1668
|
#licensingSub;
|
|
1669
|
+
/** Subscription to the validation result bus. */
|
|
1590
1670
|
#validaionSub;
|
|
1671
|
+
/** Map of license IDs to their decoded payloads. */
|
|
1591
1672
|
#licenseMap = new Map();
|
|
1673
|
+
/** Tracks FeatureCells that have reached a terminal license status. */
|
|
1592
1674
|
#terminalStatus = new Map();
|
|
1675
|
+
/** Whether AI-assist capabilities have been enabled. */
|
|
1593
1676
|
#aiAssistEnabled = false;
|
|
1677
|
+
/** Whether licensing checks are bypassed in dev mode. */
|
|
1594
1678
|
#bypassLicensing = false;
|
|
1679
|
+
/** Timeout duration in milliseconds for pending license evaluations. */
|
|
1595
1680
|
#licenseTimeoutMs;
|
|
1681
|
+
/** Active timeout handles for pending license evaluations keyed by FeatureCell. */
|
|
1596
1682
|
// eslint-disable-next-line
|
|
1597
1683
|
#licenseTimers = new Map();
|
|
1684
|
+
/** Subject emitting licensing events to the licensing service. */
|
|
1598
1685
|
#licensingBus$ = new Subject();
|
|
1686
|
+
/** ReplaySubject emitting license validation results. */
|
|
1599
1687
|
#validationBus$ = new ReplaySubject();
|
|
1688
|
+
/** Last published license status per FeatureCell key. */
|
|
1600
1689
|
#lastStatus = new Map();
|
|
1690
|
+
/** Frozen global Vault configuration. */
|
|
1601
1691
|
#vaultConfig;
|
|
1692
|
+
/** Registry of FeatureCell registration records keyed by cell key. */
|
|
1602
1693
|
#registry = new Map();
|
|
1694
|
+
/**
|
|
1695
|
+
* Creates the VaultCore singleton and initializes licensing infrastructure.
|
|
1696
|
+
*
|
|
1697
|
+
* @param config - Raw Vault configuration.
|
|
1698
|
+
*/
|
|
1603
1699
|
constructor(config) {
|
|
1604
1700
|
InitializeLicensingService(this.#licensingBus$, this.#validationBus$.asObservable());
|
|
1605
1701
|
this.setVaultConfig(config);
|
|
@@ -1634,6 +1730,7 @@ class VaultCoreInstance {
|
|
|
1634
1730
|
this.#licenseTimeoutMs = config.licenseTimeoutMs ?? 15_000;
|
|
1635
1731
|
this.#warnIfAccidentalDevMode();
|
|
1636
1732
|
}
|
|
1733
|
+
/** Resets all internal state for test isolation. */
|
|
1637
1734
|
resetForTesting() {
|
|
1638
1735
|
this.#licensingSub?.unsubscribe();
|
|
1639
1736
|
this.#licensingSub = undefined;
|
|
@@ -1644,40 +1741,87 @@ class VaultCoreInstance {
|
|
|
1644
1741
|
this.#lastStatus.clear();
|
|
1645
1742
|
this.#licenseMap.clear();
|
|
1646
1743
|
}
|
|
1744
|
+
/** Clears the FeatureCell registration registry. */
|
|
1647
1745
|
resetFeatureCellRegistry() {
|
|
1648
1746
|
this.#registry.clear();
|
|
1649
1747
|
}
|
|
1748
|
+
/**
|
|
1749
|
+
* Registers a FeatureCell key in the runtime registry.
|
|
1750
|
+
*
|
|
1751
|
+
* @param key - Unique FeatureCell identifier.
|
|
1752
|
+
*/
|
|
1650
1753
|
registerCellRuntime(key) {
|
|
1651
1754
|
this.#ensureRecord(key);
|
|
1652
1755
|
}
|
|
1756
|
+
/**
|
|
1757
|
+
* Registers behavior entities for a FeatureCell.
|
|
1758
|
+
*
|
|
1759
|
+
* @param key - FeatureCell key.
|
|
1760
|
+
* @param behaviors - Array of behavior entity descriptors.
|
|
1761
|
+
*/
|
|
1653
1762
|
registerBehaviors(key, behaviors) {
|
|
1654
1763
|
const record = this.#ensureRecord(key);
|
|
1655
1764
|
record.behaviors = this.#registerEntities(behaviors);
|
|
1656
1765
|
record.behaviorsRegistered = true;
|
|
1657
1766
|
}
|
|
1767
|
+
/**
|
|
1768
|
+
* Registers controller entities for a FeatureCell.
|
|
1769
|
+
*
|
|
1770
|
+
* @param key - FeatureCell key.
|
|
1771
|
+
* @param controllers - Array of controller entity descriptors.
|
|
1772
|
+
*/
|
|
1658
1773
|
registerControllers(key, controllers) {
|
|
1659
1774
|
const record = this.#ensureRecord(key);
|
|
1660
1775
|
record.controllers = this.#registerEntities(controllers);
|
|
1661
1776
|
record.controllersRegistered = true;
|
|
1662
1777
|
}
|
|
1778
|
+
/**
|
|
1779
|
+
* Registers fluent API callback counts for a FeatureCell.
|
|
1780
|
+
*
|
|
1781
|
+
* @param key - FeatureCell key.
|
|
1782
|
+
* @param summary - Object summarizing the count of each fluent API callback type.
|
|
1783
|
+
*/
|
|
1663
1784
|
registerFluentApis(key, summary) {
|
|
1664
1785
|
const record = this.#ensureRecord(key);
|
|
1665
1786
|
record.fluentApis = Object.freeze(summary);
|
|
1666
1787
|
}
|
|
1788
|
+
/**
|
|
1789
|
+
* Retrieves the decoded license payload for a given license ID.
|
|
1790
|
+
*
|
|
1791
|
+
* @param licenseId - License identifier to look up.
|
|
1792
|
+
* @returns The decoded payload, or undefined if not found.
|
|
1793
|
+
*/
|
|
1667
1794
|
getLicensePayload(licenseId) {
|
|
1668
1795
|
return this.#licenseMap.get(licenseId);
|
|
1669
1796
|
}
|
|
1797
|
+
/**
|
|
1798
|
+
* Returns whether licensing checks are bypassed.
|
|
1799
|
+
*
|
|
1800
|
+
* @returns True when bypass licensing is active.
|
|
1801
|
+
*/
|
|
1670
1802
|
isBypassLicensing() {
|
|
1671
1803
|
return this.#bypassLicensing;
|
|
1672
1804
|
}
|
|
1805
|
+
/**
|
|
1806
|
+
* Checks whether a key belongs to the set of known Vault-internal keys.
|
|
1807
|
+
*
|
|
1808
|
+
* @param key - The key to check.
|
|
1809
|
+
* @returns True when the key is a recognized Vault key.
|
|
1810
|
+
*/
|
|
1673
1811
|
isAuthorizedKey(key) {
|
|
1674
1812
|
return KNOWN_VAULT_KEYS.has(key);
|
|
1675
1813
|
}
|
|
1814
|
+
/**
|
|
1815
|
+
* Returns whether a Vault-level license has been registered.
|
|
1816
|
+
*
|
|
1817
|
+
* @returns True when a Vault license exists.
|
|
1818
|
+
*/
|
|
1676
1819
|
hasVaultLicense() {
|
|
1677
1820
|
return this.#licenseMap.has(VAULT_LICENSE_ID);
|
|
1678
1821
|
}
|
|
1679
1822
|
//#endregion
|
|
1680
1823
|
//#region private Methods
|
|
1824
|
+
/** Populates the license map from the supplied license configurations. */
|
|
1681
1825
|
#initializeLicenses(licenses) {
|
|
1682
1826
|
if (!licenses?.length)
|
|
1683
1827
|
return;
|
|
@@ -1687,6 +1831,7 @@ class VaultCoreInstance {
|
|
|
1687
1831
|
this.#licenseMap.set(license.licenseId, license.payload);
|
|
1688
1832
|
}
|
|
1689
1833
|
}
|
|
1834
|
+
/** Freezes and registers an array of entity descriptors into a keyed map. */
|
|
1690
1835
|
#registerEntities(entities) {
|
|
1691
1836
|
return new Map(entities.map((entity) => {
|
|
1692
1837
|
let needsLicense;
|
|
@@ -1708,6 +1853,7 @@ class VaultCoreInstance {
|
|
|
1708
1853
|
return [entity.key, Object.freeze(frozen)];
|
|
1709
1854
|
}));
|
|
1710
1855
|
}
|
|
1856
|
+
/** Subscribes to the licensing bus and routes events to the appropriate handlers. */
|
|
1711
1857
|
#subscribeToLicensingEvents() {
|
|
1712
1858
|
this.#licensingSub = this.#licensingBus$.subscribe((event) => {
|
|
1713
1859
|
switch (event.type) {
|
|
@@ -1741,6 +1887,7 @@ class VaultCoreInstance {
|
|
|
1741
1887
|
}
|
|
1742
1888
|
});
|
|
1743
1889
|
}
|
|
1890
|
+
/** Starts a timeout timer for pending licenses on a FeatureCell. */
|
|
1744
1891
|
#startLicenseTimeout(featureCellKey) {
|
|
1745
1892
|
if (!this.#licenseTimeoutMs)
|
|
1746
1893
|
return;
|
|
@@ -1752,6 +1899,7 @@ class VaultCoreInstance {
|
|
|
1752
1899
|
}, this.#licenseTimeoutMs);
|
|
1753
1900
|
this.#licenseTimers.set(featureCellKey, timer);
|
|
1754
1901
|
}
|
|
1902
|
+
/** Expires all pending licenses for a FeatureCell and publishes a denial. */
|
|
1755
1903
|
#expirePendingLicenses(featureCellKey) {
|
|
1756
1904
|
const record = this.#registry.get(featureCellKey);
|
|
1757
1905
|
if (!record)
|
|
@@ -1775,6 +1923,7 @@ class VaultCoreInstance {
|
|
|
1775
1923
|
this.#clearLicenseTimer(featureCellKey);
|
|
1776
1924
|
}
|
|
1777
1925
|
// Do not evaluate until both sides registered
|
|
1926
|
+
/** Evaluates and emits the aggregate license status for a FeatureCell. */
|
|
1778
1927
|
#emitLicenseStatus(featureCellKey) {
|
|
1779
1928
|
const record = this.#registry.get(featureCellKey);
|
|
1780
1929
|
if (!record)
|
|
@@ -1804,6 +1953,7 @@ class VaultCoreInstance {
|
|
|
1804
1953
|
// All valid
|
|
1805
1954
|
this.#publishStatus(featureCellKey, true);
|
|
1806
1955
|
}
|
|
1956
|
+
/** Clears the license timeout timer for a FeatureCell. */
|
|
1807
1957
|
#clearLicenseTimer(featureCellKey) {
|
|
1808
1958
|
const timer = this.#licenseTimers.get(featureCellKey);
|
|
1809
1959
|
if (timer) {
|
|
@@ -1811,6 +1961,7 @@ class VaultCoreInstance {
|
|
|
1811
1961
|
this.#licenseTimers.delete(featureCellKey);
|
|
1812
1962
|
}
|
|
1813
1963
|
}
|
|
1964
|
+
/** Publishes a terminal license validation result for a FeatureCell. */
|
|
1814
1965
|
#publishStatus(featureCellKey, approved) {
|
|
1815
1966
|
if (this.#terminalStatus.has(featureCellKey))
|
|
1816
1967
|
return;
|
|
@@ -1822,6 +1973,7 @@ class VaultCoreInstance {
|
|
|
1822
1973
|
approved
|
|
1823
1974
|
});
|
|
1824
1975
|
}
|
|
1976
|
+
/** Routes a license validation event to the appropriate entity maps. */
|
|
1825
1977
|
#handleLicenseValidation(event) {
|
|
1826
1978
|
const { featureCellKey, key, licenseToken, valid } = event;
|
|
1827
1979
|
if (this.#terminalStatus.has(event.featureCellKey))
|
|
@@ -1839,6 +1991,7 @@ class VaultCoreInstance {
|
|
|
1839
1991
|
this.#enableAiAssist();
|
|
1840
1992
|
}
|
|
1841
1993
|
}
|
|
1994
|
+
/** Applies a license validation result to a single entity within a map. */
|
|
1842
1995
|
#applyLicenseValidation(entities, key, licenseId, valid) {
|
|
1843
1996
|
if (!entities?.has(key))
|
|
1844
1997
|
return;
|
|
@@ -1861,6 +2014,7 @@ class VaultCoreInstance {
|
|
|
1861
2014
|
validLicense: valid ? VaultRegistrationLicenseStatusTypes.Valid : VaultRegistrationLicenseStatusTypes.Revoked
|
|
1862
2015
|
}));
|
|
1863
2016
|
}
|
|
2017
|
+
/** Records a license token against the matching entity in the registry. */
|
|
1864
2018
|
#recordLicenses(event) {
|
|
1865
2019
|
const { featureCellKey, key, licenseToken } = event;
|
|
1866
2020
|
const record = this.#registry.get(featureCellKey);
|
|
@@ -1873,6 +2027,7 @@ class VaultCoreInstance {
|
|
|
1873
2027
|
this.#recordLicense(record.behaviors, key, licenseToken);
|
|
1874
2028
|
this.#recordLicense(record.controllers, key, licenseToken);
|
|
1875
2029
|
}
|
|
2030
|
+
/** Writes a license ID to a single entity if it requires a license. */
|
|
1876
2031
|
#recordLicense(entities, key, licenseId) {
|
|
1877
2032
|
if (!entities?.has(key))
|
|
1878
2033
|
return;
|
|
@@ -1890,6 +2045,7 @@ class VaultCoreInstance {
|
|
|
1890
2045
|
licenseId
|
|
1891
2046
|
}));
|
|
1892
2047
|
}
|
|
2048
|
+
/** Emits a console warning when dev mode is active outside a test environment. */
|
|
1893
2049
|
#warnIfAccidentalDevMode() {
|
|
1894
2050
|
if (DevMode.active && !isTestEnv.active) {
|
|
1895
2051
|
// eslint-disable-next-line
|
|
@@ -1899,6 +2055,7 @@ class VaultCoreInstance {
|
|
|
1899
2055
|
`If this is intentional, you can safely ignore this message.`);
|
|
1900
2056
|
}
|
|
1901
2057
|
}
|
|
2058
|
+
/** Summarizes fluent API callback counts from a feature description event. */
|
|
1902
2059
|
#summarizeFluent(event) {
|
|
1903
2060
|
const fluent = event?.fluentApis ?? {};
|
|
1904
2061
|
return {
|
|
@@ -1910,12 +2067,14 @@ class VaultCoreInstance {
|
|
|
1910
2067
|
errorCallbacks: Array.isArray(fluent?.errorCallbacks) ? fluent.errorCallbacks.length : 0
|
|
1911
2068
|
};
|
|
1912
2069
|
}
|
|
2070
|
+
/** Returns or creates the registry record for a FeatureCell key. */
|
|
1913
2071
|
#ensureRecord(key) {
|
|
1914
2072
|
if (!this.#registry.has(key)) {
|
|
1915
2073
|
this.#registry.set(key, { key, behaviorsRegistered: false, controllersRegistered: false });
|
|
1916
2074
|
}
|
|
1917
2075
|
return this.#registry.get(key);
|
|
1918
2076
|
}
|
|
2077
|
+
/** Enables AI-assist capabilities when a core license is validated. */
|
|
1919
2078
|
#enableAiAssist() {
|
|
1920
2079
|
if (this.#aiAssistEnabled)
|
|
1921
2080
|
return;
|
|
@@ -1930,6 +2089,7 @@ class VaultCoreInstance {
|
|
|
1930
2089
|
globalThis.sdux.debugWidget.aiAssistEnabled = true;
|
|
1931
2090
|
document.dispatchEvent(new CustomEvent('sdux-license-resolved'));
|
|
1932
2091
|
}
|
|
2092
|
+
/** Injects the DevTools debug widget into the global scope. */
|
|
1933
2093
|
#injectDebugWidget() {
|
|
1934
2094
|
if (!DevMode.active)
|
|
1935
2095
|
return;
|
|
@@ -1945,10 +2105,21 @@ class VaultCoreInstance {
|
|
|
1945
2105
|
}
|
|
1946
2106
|
//#endregion
|
|
1947
2107
|
//#region Public Testing Method
|
|
2108
|
+
/**
|
|
2109
|
+
* Registers a settled callback for a FeatureCell.
|
|
2110
|
+
*
|
|
2111
|
+
* @param key - FeatureCell key.
|
|
2112
|
+
* @param fn - Async function invoked when the cell settles.
|
|
2113
|
+
*/
|
|
1948
2114
|
registerVaultSettled(key, fn) {
|
|
1949
2115
|
const record = this.#ensureRecord(key);
|
|
1950
2116
|
record.vaultSettled = fn;
|
|
1951
2117
|
}
|
|
2118
|
+
/**
|
|
2119
|
+
* Awaits the settled callback for a specific FeatureCell.
|
|
2120
|
+
*
|
|
2121
|
+
* @param key - FeatureCell key to await.
|
|
2122
|
+
*/
|
|
1952
2123
|
async awaitFeatureCellSettled(key) {
|
|
1953
2124
|
const record = this.#registry.get(key);
|
|
1954
2125
|
if (!record) {
|
|
@@ -1959,6 +2130,7 @@ class VaultCoreInstance {
|
|
|
1959
2130
|
await Promise.resolve(); // flush microtasks
|
|
1960
2131
|
}
|
|
1961
2132
|
}
|
|
2133
|
+
/** Awaits settled callbacks for all registered FeatureCells. */
|
|
1962
2134
|
async awaitAllSettled() {
|
|
1963
2135
|
for (const record of this.#registry.values()) {
|
|
1964
2136
|
if (typeof record.vaultSettled === 'function') {
|
|
@@ -1968,11 +2140,21 @@ class VaultCoreInstance {
|
|
|
1968
2140
|
await Promise.resolve();
|
|
1969
2141
|
}
|
|
1970
2142
|
// TESTING ONLY — do not use in production code
|
|
2143
|
+
/**
|
|
2144
|
+
* Returns a shallow clone of the internal registry for test inspection.
|
|
2145
|
+
*
|
|
2146
|
+
* @returns A read-only copy of the FeatureCell registry.
|
|
2147
|
+
*/
|
|
1971
2148
|
getRegistrySnapshot() {
|
|
1972
2149
|
// Return a shallow cloned map so tests cannot mutate internal state
|
|
1973
2150
|
return new Map(this.#registry);
|
|
1974
2151
|
}
|
|
1975
2152
|
}
|
|
2153
|
+
/**
|
|
2154
|
+
* Registers a FeatureCell entry in the global Vault registry.
|
|
2155
|
+
*
|
|
2156
|
+
* @param entry - Registration descriptor containing the FeatureCell key.
|
|
2157
|
+
*/
|
|
1976
2158
|
function registerFeatureCell(entry) {
|
|
1977
2159
|
if (!instance) {
|
|
1978
2160
|
throw new Error('[vault] Vault not initialized.');
|
|
@@ -1985,6 +2167,12 @@ function registerFeatureCell(entry) {
|
|
|
1985
2167
|
}
|
|
1986
2168
|
instance.registerCellRuntime(entry.key);
|
|
1987
2169
|
}
|
|
2170
|
+
/**
|
|
2171
|
+
* Retrieves the decoded license payload for a given license ID.
|
|
2172
|
+
*
|
|
2173
|
+
* @param licenseId - License identifier to look up.
|
|
2174
|
+
* @returns The decoded payload.
|
|
2175
|
+
*/
|
|
1988
2176
|
function getLicensePayload(licenseId) {
|
|
1989
2177
|
if (!instance) {
|
|
1990
2178
|
throw new Error('[vault] Vault not initialized.');
|
|
@@ -1994,17 +2182,29 @@ function getLicensePayload(licenseId) {
|
|
|
1994
2182
|
}
|
|
1995
2183
|
return instance.getLicensePayload(licenseId);
|
|
1996
2184
|
}
|
|
2185
|
+
/** Awaits settled callbacks for all registered FeatureCells. */
|
|
1997
2186
|
async function vaultAllSettled() {
|
|
1998
2187
|
if (!instance)
|
|
1999
2188
|
return;
|
|
2000
2189
|
await instance.awaitAllSettled();
|
|
2001
2190
|
}
|
|
2191
|
+
/**
|
|
2192
|
+
* Awaits the settled callback for a specific FeatureCell.
|
|
2193
|
+
*
|
|
2194
|
+
* @param key - FeatureCell key to await.
|
|
2195
|
+
*/
|
|
2002
2196
|
async function vaultSettled(key) {
|
|
2003
2197
|
if (!instance) {
|
|
2004
2198
|
throw new Error('[vault] Vault not initialized.');
|
|
2005
2199
|
}
|
|
2006
2200
|
await instance.awaitFeatureCellSettled(key);
|
|
2007
2201
|
}
|
|
2202
|
+
/**
|
|
2203
|
+
* Registers a settled callback for a FeatureCell in the global Vault.
|
|
2204
|
+
*
|
|
2205
|
+
* @param key - FeatureCell key.
|
|
2206
|
+
* @param vaultSettled - Async function invoked when the cell settles.
|
|
2207
|
+
*/
|
|
2008
2208
|
function registerVaultSettled(key, vaultSettled) {
|
|
2009
2209
|
if (!instance) {
|
|
2010
2210
|
throw new Error('[vault] Vault not initialized.');
|
|
@@ -2028,15 +2228,27 @@ function resetVaultForTests() {
|
|
|
2028
2228
|
instance?.resetForTesting();
|
|
2029
2229
|
instance = null;
|
|
2030
2230
|
}
|
|
2231
|
+
/** Clears the FeatureCell registration registry. */
|
|
2031
2232
|
function resetFeatureCellRegistry() {
|
|
2032
2233
|
instance?.resetFeatureCellRegistry();
|
|
2033
2234
|
}
|
|
2235
|
+
/**
|
|
2236
|
+
* Returns a read-only snapshot of the FeatureCell registry for test inspection.
|
|
2237
|
+
*
|
|
2238
|
+
* @returns A read-only copy of the registry.
|
|
2239
|
+
*/
|
|
2034
2240
|
function getVaultRegistryForTests() {
|
|
2035
2241
|
if (!instance) {
|
|
2036
2242
|
throw new Error('[vault] Vault not initialized.');
|
|
2037
2243
|
}
|
|
2038
2244
|
return instance.getRegistrySnapshot();
|
|
2039
2245
|
}
|
|
2246
|
+
/**
|
|
2247
|
+
* Checks whether a key is authorized by the Vault runtime.
|
|
2248
|
+
*
|
|
2249
|
+
* @param key - The key to check.
|
|
2250
|
+
* @returns True when the key is authorized or licensing is bypassed.
|
|
2251
|
+
*/
|
|
2040
2252
|
function isAuthorizedKey(key) {
|
|
2041
2253
|
if (!instance)
|
|
2042
2254
|
return false;
|
|
@@ -2044,11 +2256,21 @@ function isAuthorizedKey(key) {
|
|
|
2044
2256
|
return true;
|
|
2045
2257
|
return instance.isAuthorizedKey(key);
|
|
2046
2258
|
}
|
|
2259
|
+
/**
|
|
2260
|
+
* Returns whether licensing checks are currently bypassed.
|
|
2261
|
+
*
|
|
2262
|
+
* @returns True when bypass licensing is active.
|
|
2263
|
+
*/
|
|
2047
2264
|
function isBypassLicensing() {
|
|
2048
2265
|
if (!instance)
|
|
2049
2266
|
return false;
|
|
2050
2267
|
return instance.isBypassLicensing();
|
|
2051
2268
|
}
|
|
2269
|
+
/**
|
|
2270
|
+
* Returns whether a Vault-level license has been registered.
|
|
2271
|
+
*
|
|
2272
|
+
* @returns True when a Vault license exists.
|
|
2273
|
+
*/
|
|
2052
2274
|
function hasVaultLicense() {
|
|
2053
2275
|
if (!instance)
|
|
2054
2276
|
return false;
|
|
@@ -2366,27 +2588,19 @@ MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAuXto+eRaFm9pObys/IEI ASwV1wgGvNGJsiy
|
|
|
2366
2588
|
`
|
|
2367
2589
|
};
|
|
2368
2590
|
|
|
2369
|
-
/**
|
|
2370
|
-
* Static license verification utility used to validate signed license tokens
|
|
2371
|
-
* against tier-specific public keys. Tokens use a dot-separated format:
|
|
2372
|
-
* `base64(payload).base64(signature)`.
|
|
2373
|
-
*
|
|
2374
|
-
* The verification process:
|
|
2375
|
-
* - Splits the token on `.` to extract the encoded payload and signature
|
|
2376
|
-
* - Decodes the payload from base64 to determine the license tier
|
|
2377
|
-
* - Rejects tokens with missing or unrecognized tier
|
|
2378
|
-
* - Loads the appropriate PEM-encoded public key for the tier
|
|
2379
|
-
* - Uses `crypto.subtle.verify()` (RSA-SHA256) to validate the signature
|
|
2380
|
-
* against the raw base64-encoded payload string
|
|
2381
|
-
*
|
|
2382
|
-
* Verification failures return `false` rather than throwing.
|
|
2383
|
-
*/
|
|
2591
|
+
/** Whether license details have already been logged to the console. */
|
|
2384
2592
|
let hasLoggedLicenseDetails = false;
|
|
2593
|
+
/** Resets the license details log flag for test isolation. */
|
|
2385
2594
|
function resetLicenseDetailsLog() {
|
|
2386
2595
|
if (!DevMode.active)
|
|
2387
2596
|
return;
|
|
2388
2597
|
hasLoggedLicenseDetails = false;
|
|
2389
2598
|
}
|
|
2599
|
+
/**
|
|
2600
|
+
* Static license verification utility that validates signed license tokens
|
|
2601
|
+
* against tier-specific RSA public keys. Tokens use a dot-separated
|
|
2602
|
+
* base64(payload).base64(signature) format verified via crypto.subtle.
|
|
2603
|
+
*/
|
|
2390
2604
|
const VerifyLicenseToken = {
|
|
2391
2605
|
/**
|
|
2392
2606
|
* Verifies the supplied signed license token using the public key associated
|
|
@@ -2524,6 +2738,12 @@ function formatLicenseDate(value) {
|
|
|
2524
2738
|
}).format(new Date(value));
|
|
2525
2739
|
}
|
|
2526
2740
|
|
|
2741
|
+
/**
|
|
2742
|
+
* Verifies a signed license payload string.
|
|
2743
|
+
*
|
|
2744
|
+
* @param licensePayload - The signed license token to verify.
|
|
2745
|
+
* @returns A promise resolving to true if the license is valid.
|
|
2746
|
+
*/
|
|
2527
2747
|
async function verifyLicensePayload(licensePayload) {
|
|
2528
2748
|
try {
|
|
2529
2749
|
if (!licensePayload) {
|
|
@@ -2536,13 +2756,29 @@ async function verifyLicensePayload(licensePayload) {
|
|
|
2536
2756
|
}
|
|
2537
2757
|
}
|
|
2538
2758
|
|
|
2759
|
+
/**
|
|
2760
|
+
* Abstract base class providing license lifecycle management for behaviors
|
|
2761
|
+
* and controllers. Subclasses automatically request a license token during
|
|
2762
|
+
* construction when the static `needsLicense` flag is set.
|
|
2763
|
+
*/
|
|
2539
2764
|
class LicensingAbstract {
|
|
2765
|
+
/** Whether this class requires a valid license to operate. */
|
|
2540
2766
|
static needsLicense;
|
|
2767
|
+
/** Static key assigned by the VaultBehavior or VaultController decorator. */
|
|
2541
2768
|
static key;
|
|
2769
|
+
/** License token received from the licensing service. */
|
|
2542
2770
|
#licenseToken;
|
|
2771
|
+
/** Key of the FeatureCell this instance belongs to. */
|
|
2543
2772
|
#featureCellKey;
|
|
2773
|
+
/** Key identifying this behavior or controller. */
|
|
2544
2774
|
#key;
|
|
2775
|
+
/** Licensing service used to request and validate licenses. */
|
|
2545
2776
|
#licenseService;
|
|
2777
|
+
/**
|
|
2778
|
+
* Creates a new licensing-aware instance and requests a license if required.
|
|
2779
|
+
*
|
|
2780
|
+
* @param ctx - Context providing the FeatureCell key and optional license payload.
|
|
2781
|
+
*/
|
|
2546
2782
|
constructor(ctx) {
|
|
2547
2783
|
const ctor = this.constructor;
|
|
2548
2784
|
if (typeof ctor.key !== 'string' || !ctor.key.trim()) {
|
|
@@ -2555,9 +2791,15 @@ class LicensingAbstract {
|
|
|
2555
2791
|
this.#requestLicense();
|
|
2556
2792
|
}
|
|
2557
2793
|
}
|
|
2794
|
+
/** Requests a license token from the licensing service. */
|
|
2558
2795
|
#requestLicense() {
|
|
2559
2796
|
this.#licenseToken = this.#licenseService.requestLicense(this.#featureCellKey, this.#key);
|
|
2560
2797
|
}
|
|
2798
|
+
/**
|
|
2799
|
+
* Validates the previously requested license token.
|
|
2800
|
+
*
|
|
2801
|
+
* @param valid - Whether the license should be marked as approved.
|
|
2802
|
+
*/
|
|
2561
2803
|
validateLicense(valid) {
|
|
2562
2804
|
if (!this.#licenseToken) {
|
|
2563
2805
|
throw new VaultLicenseError(`validateLicense() called but no license was requested for "${this.#featureCellKey}" and "${this.#key}".`);
|
|
@@ -2566,24 +2808,45 @@ class LicensingAbstract {
|
|
|
2566
2808
|
}
|
|
2567
2809
|
}
|
|
2568
2810
|
|
|
2811
|
+
/**
|
|
2812
|
+
* Core license behavior that validates the vault license payload on
|
|
2813
|
+
* construction. The behavior extends LicensingAbstract to integrate
|
|
2814
|
+
* with the licensing service and gates pipeline execution based on
|
|
2815
|
+
* the asynchronous verification result.
|
|
2816
|
+
*/
|
|
2569
2817
|
let withCoreLicenseBehavior = class withCoreLicenseBehavior extends LicensingAbstract {
|
|
2570
2818
|
behaviorCtx;
|
|
2819
|
+
/** Static behavior type assigned by the VaultBehavior decorator. */
|
|
2571
2820
|
static type;
|
|
2821
|
+
/** Static behavior key assigned by the VaultBehavior decorator. */
|
|
2572
2822
|
static key;
|
|
2823
|
+
/** Static critical flag assigned by the VaultBehavior decorator. */
|
|
2573
2824
|
static critical;
|
|
2825
|
+
/** Static license requirement flag assigned by the VaultBehavior decorator. */
|
|
2574
2826
|
static needsLicense;
|
|
2827
|
+
/** Instance behavior type for core license evaluation. */
|
|
2575
2828
|
type = BehaviorTypes.CoreLicense;
|
|
2829
|
+
/** Instance critical flag indicating this behavior is required. */
|
|
2576
2830
|
critical = true;
|
|
2831
|
+
/** Unique key identifying this behavior instance. */
|
|
2577
2832
|
key;
|
|
2833
|
+
/**
|
|
2834
|
+
* Verifies the license payload asynchronously and reports the result.
|
|
2835
|
+
*
|
|
2836
|
+
* @param key - Unique key for this behavior instance.
|
|
2837
|
+
* @param behaviorCtx - Behavior class context providing the license payload.
|
|
2838
|
+
*/
|
|
2578
2839
|
constructor(key, behaviorCtx) {
|
|
2579
2840
|
super(behaviorCtx);
|
|
2580
2841
|
this.behaviorCtx = behaviorCtx;
|
|
2581
2842
|
this.key = key;
|
|
2582
2843
|
verifyLicensePayload(this.behaviorCtx.licensePayload).then((valid) => this.validateLicense(valid));
|
|
2583
2844
|
}
|
|
2845
|
+
/** No-op destroy handler for this behavior. */
|
|
2584
2846
|
destroy() {
|
|
2585
2847
|
vaultWarn(`${this.key} - destroy noop`);
|
|
2586
2848
|
}
|
|
2849
|
+
/** No-op reset handler for this behavior. */
|
|
2587
2850
|
reset() {
|
|
2588
2851
|
vaultWarn(`${this.key} - reset noop`);
|
|
2589
2852
|
}
|
|
@@ -2598,32 +2861,56 @@ withCoreLicenseBehavior = __decorate([
|
|
|
2598
2861
|
})
|
|
2599
2862
|
], withCoreLicenseBehavior);
|
|
2600
2863
|
|
|
2601
|
-
|
|
2602
|
-
|
|
2603
|
-
|
|
2604
|
-
|
|
2605
|
-
|
|
2606
|
-
|
|
2864
|
+
/**
|
|
2865
|
+
* Abstract base class that executes the FeatureCell behavior pipeline in
|
|
2866
|
+
* a deterministic stage order. The Orchestrator resolves incoming state,
|
|
2867
|
+
* runs operators, filters, reducers, tap callbacks, and persistence
|
|
2868
|
+
* stages before committing the final state snapshot.
|
|
2869
|
+
*/
|
|
2607
2870
|
class Orchestrator {
|
|
2871
|
+
/** Registered after-tap callback functions executed post-reduce. */
|
|
2608
2872
|
#afterTapCallbacks;
|
|
2873
|
+
/** Registered before-tap callback functions executed pre-reduce. */
|
|
2609
2874
|
#beforeTapCallbacks;
|
|
2875
|
+
/** Full set of instantiated behavior instances for this FeatureCell. */
|
|
2610
2876
|
#behaviors;
|
|
2877
|
+
/** Subset of behaviors eligible for stage-based pipeline execution. */
|
|
2611
2878
|
#stageBehaviors;
|
|
2879
|
+
/** Unique key identifying the FeatureCell that owns this orchestrator. */
|
|
2612
2880
|
cellKey;
|
|
2881
|
+
/** Decision engine used for controller vote evaluation. */
|
|
2613
2882
|
decisionEngine;
|
|
2883
|
+
/** Core error behavior responsible for normalizing pipeline errors. */
|
|
2614
2884
|
#coreErrorBehavior;
|
|
2885
|
+
/** Error callback behavior for dispatching errors to user callbacks. */
|
|
2615
2886
|
#coreErrorCallbackBehavior;
|
|
2887
|
+
/** Emit-state callback behavior for notifying state change listeners. */
|
|
2616
2888
|
#emitStateCallbackBehavior;
|
|
2889
|
+
/** User-provided emit-state callback functions. */
|
|
2617
2890
|
#emitStateCallbacks;
|
|
2891
|
+
/** Core state behavior managing snapshot commits and signal emission. */
|
|
2618
2892
|
#coreStateBehavior;
|
|
2893
|
+
/** Error transform behaviors for custom error shaping. */
|
|
2619
2894
|
#errorTransformBehaviors;
|
|
2895
|
+
/** User-provided error callback functions. */
|
|
2620
2896
|
#errorCallbacks;
|
|
2897
|
+
/** Global error service for broadcasting errors across FeatureCells. */
|
|
2621
2898
|
privateErrorService = VaultPrivateErrorService();
|
|
2899
|
+
/** User-provided filter functions applied during the filter stage. */
|
|
2622
2900
|
#filterFunctions = [];
|
|
2901
|
+
/** Initial state value or deferred factory for this FeatureCell. */
|
|
2623
2902
|
#initialState;
|
|
2903
|
+
/** Merge behavior responsible for combining partial and current state. */
|
|
2624
2904
|
#mergeBehavior;
|
|
2905
|
+
/** User-provided reducer functions applied during the reduce stage. */
|
|
2625
2906
|
#reducerFunctions;
|
|
2907
|
+
/** Vault monitor instance for tracing pipeline lifecycle events. */
|
|
2626
2908
|
vaultMonitor = VaultMonitor();
|
|
2909
|
+
/**
|
|
2910
|
+
* Initializes the orchestrator with configuration callbacks and initial state.
|
|
2911
|
+
*
|
|
2912
|
+
* @param config - Orchestrator configuration containing behaviors and callbacks.
|
|
2913
|
+
*/
|
|
2627
2914
|
constructor(config) {
|
|
2628
2915
|
this.#afterTapCallbacks = config.afterTapCallbacks ?? [];
|
|
2629
2916
|
this.#beforeTapCallbacks = config.beforeTapCallbacks ?? [];
|
|
@@ -2634,20 +2921,25 @@ class Orchestrator {
|
|
|
2634
2921
|
this.#initialState = config.initialState;
|
|
2635
2922
|
this.#reducerFunctions = config.reducerCallbacks ?? [];
|
|
2636
2923
|
}
|
|
2924
|
+
/** Registers all behaviors from the orchestrator configuration. */
|
|
2637
2925
|
initializeOrchestrator(config) {
|
|
2638
2926
|
config.behaviors = config.behaviors ?? [];
|
|
2639
2927
|
this.#registerBehaviors(config);
|
|
2640
2928
|
}
|
|
2641
2929
|
//#region Protected Methods
|
|
2930
|
+
/** Loads the initial state and runs the first pipeline cycle. */
|
|
2642
2931
|
async initializeFeatureCell(ctx) {
|
|
2643
2932
|
await this.#loadInitialState(ctx);
|
|
2644
2933
|
}
|
|
2934
|
+
/** Invokes destroy on all registered behaviors. */
|
|
2645
2935
|
destroyBehaviors(ctx) {
|
|
2646
2936
|
this.#destroyBehaviors(ctx);
|
|
2647
2937
|
}
|
|
2938
|
+
/** Invokes reset on all registered behaviors. */
|
|
2648
2939
|
resetBehaviors(ctx) {
|
|
2649
2940
|
this.#resetBehaviors(ctx);
|
|
2650
2941
|
}
|
|
2942
|
+
/** Routes the pipeline context to the appropriate replace or merge orchestration flow. */
|
|
2651
2943
|
async orchestrate(ctx, options) {
|
|
2652
2944
|
// no controllers → pipeline behaves exactly as before
|
|
2653
2945
|
if (ctx.operation === OperationTypes.Replace) {
|
|
@@ -2658,6 +2950,11 @@ class Orchestrator {
|
|
|
2658
2950
|
}
|
|
2659
2951
|
return;
|
|
2660
2952
|
}
|
|
2953
|
+
/**
|
|
2954
|
+
* Builds a controller context from the current behavior context.
|
|
2955
|
+
*
|
|
2956
|
+
* @returns The controller context snapshot.
|
|
2957
|
+
*/
|
|
2661
2958
|
buildControllerCtx(ctx) {
|
|
2662
2959
|
return {
|
|
2663
2960
|
traceId: ctx.traceId,
|
|
@@ -2667,6 +2964,11 @@ class Orchestrator {
|
|
|
2667
2964
|
operation: ctx.operation
|
|
2668
2965
|
};
|
|
2669
2966
|
}
|
|
2967
|
+
/**
|
|
2968
|
+
* Normalizes an incoming state input into a consistent shape.
|
|
2969
|
+
*
|
|
2970
|
+
* @returns The normalized state input value.
|
|
2971
|
+
*/
|
|
2670
2972
|
normalizeIncoming(incoming) {
|
|
2671
2973
|
if (!incoming)
|
|
2672
2974
|
return null;
|
|
@@ -2680,6 +2982,7 @@ class Orchestrator {
|
|
|
2680
2982
|
value: incoming
|
|
2681
2983
|
};
|
|
2682
2984
|
}
|
|
2985
|
+
/** Notifies the core state behavior of a controller abort or deny outcome. */
|
|
2683
2986
|
controllerOutcomeNotification(type, ctx) {
|
|
2684
2987
|
switch (type) {
|
|
2685
2988
|
case DecisionOutcomeTypes.Abort: {
|
|
@@ -2692,6 +2995,11 @@ class Orchestrator {
|
|
|
2692
2995
|
}
|
|
2693
2996
|
}
|
|
2694
2997
|
}
|
|
2998
|
+
/**
|
|
2999
|
+
* Prepares the incoming value and handles noop and clear-state short-circuits.
|
|
3000
|
+
*
|
|
3001
|
+
* @returns The normalized incoming value or undefined for short-circuit paths.
|
|
3002
|
+
*/
|
|
2695
3003
|
prepareIncoming(ctx, incoming, operation) {
|
|
2696
3004
|
ctx = this.#prepIncomingForOrchestration(ctx, incoming, operation);
|
|
2697
3005
|
const normalizedIncoming = this.#coreStateBehavior.preparePipelineIncoming(ctx);
|
|
@@ -2711,12 +3019,14 @@ class Orchestrator {
|
|
|
2711
3019
|
}
|
|
2712
3020
|
//#endregion
|
|
2713
3021
|
//#region Private Methods
|
|
3022
|
+
/** Attaches incoming, resolve type, and operation to the behavior context. */
|
|
2714
3023
|
#prepIncomingForOrchestration(ctx, incoming, operation) {
|
|
2715
3024
|
ctx.incoming = this.normalizeIncoming(incoming);
|
|
2716
3025
|
ctx.resolveType = this.#getResolveType(incoming);
|
|
2717
3026
|
ctx.operation = operation;
|
|
2718
3027
|
return ctx;
|
|
2719
3028
|
}
|
|
3029
|
+
/** Ensures at most one merge behavior is registered and adds it to the list. */
|
|
2720
3030
|
#addDefaultMergeBehavior(behaviors, config) {
|
|
2721
3031
|
const mergeBehaviors = config.behaviors.filter((behaviorClass) => {
|
|
2722
3032
|
return behaviorClass.type === BehaviorTypes.Merge;
|
|
@@ -2734,6 +3044,7 @@ class Orchestrator {
|
|
|
2734
3044
|
// ---------------------------------------------------------------------------
|
|
2735
3045
|
// NORMALIZATION
|
|
2736
3046
|
// ---------------------------------------------------------------------------
|
|
3047
|
+
/** Assembles the default behavior set from configuration and internal overrides. */
|
|
2737
3048
|
#defineDefaultBehaviors(config) {
|
|
2738
3049
|
let defaultBehaviors = config.defaultBehaviors ?? [];
|
|
2739
3050
|
defaultBehaviors = this.#determineCoreCallbackErrorBehavior(defaultBehaviors, config);
|
|
@@ -2742,18 +3053,21 @@ class Orchestrator {
|
|
|
2742
3053
|
defaultBehaviors = this.#addCoreLicenseBehavior(defaultBehaviors);
|
|
2743
3054
|
return defaultBehaviors;
|
|
2744
3055
|
}
|
|
3056
|
+
/** Removes the error callback behavior when no error callbacks are configured. */
|
|
2745
3057
|
#determineCoreCallbackErrorBehavior(behaviors, config) {
|
|
2746
3058
|
if (config?.errorCallbacks?.length === 0) {
|
|
2747
3059
|
return behaviors.filter((behavior) => behavior.type !== BehaviorTypes.CoreErrorCallback);
|
|
2748
3060
|
}
|
|
2749
3061
|
return behaviors;
|
|
2750
3062
|
}
|
|
3063
|
+
/** Removes the emit-state callback behavior when no emit-state callbacks are configured. */
|
|
2751
3064
|
#addCoreEmitStateCallbackBehavior(behaviors, config) {
|
|
2752
3065
|
if (config?.emitStateCallbacks?.length === 0) {
|
|
2753
3066
|
return behaviors.filter((behavior) => behavior.type !== BehaviorTypes.CoreEmitState);
|
|
2754
3067
|
}
|
|
2755
3068
|
return behaviors;
|
|
2756
3069
|
}
|
|
3070
|
+
/** Conditionally adds the core license behavior when a vault license is active. */
|
|
2757
3071
|
#addCoreLicenseBehavior(behaviors) {
|
|
2758
3072
|
behaviors = behaviors?.filter((behavior) => behavior.type !== BehaviorTypes.CoreLicense);
|
|
2759
3073
|
if (hasVaultLicense()) {
|
|
@@ -2761,6 +3075,7 @@ class Orchestrator {
|
|
|
2761
3075
|
}
|
|
2762
3076
|
return behaviors;
|
|
2763
3077
|
}
|
|
3078
|
+
/** Reports behavior metadata to the licensing service for license validation. */
|
|
2764
3079
|
#registerBehaviorsWithVault(behaviors) {
|
|
2765
3080
|
// eslint-disable-next-line
|
|
2766
3081
|
const behaviorMetadata = behaviors.map((behavior) => {
|
|
@@ -2777,6 +3092,7 @@ class Orchestrator {
|
|
|
2777
3092
|
behaviors: behaviorMetadata
|
|
2778
3093
|
});
|
|
2779
3094
|
}
|
|
3095
|
+
/** Initializes, filters, and registers all behaviors for this FeatureCell. */
|
|
2780
3096
|
#registerBehaviors(config) {
|
|
2781
3097
|
const defaultBehaviors = this.#defineDefaultBehaviors(config);
|
|
2782
3098
|
// Strip out any user-provided reducers; they are passed separately via `reducers`
|
|
@@ -2816,6 +3132,7 @@ class Orchestrator {
|
|
|
2816
3132
|
this.#registerStateBehavior();
|
|
2817
3133
|
behaviorInit.applyBehaviorExtensions(this.#behaviors, config.cell, this.vaultMonitor);
|
|
2818
3134
|
}
|
|
3135
|
+
/** Filters behaviors to only those eligible for stage-based pipeline execution. */
|
|
2819
3136
|
#registerStageBehaviors() {
|
|
2820
3137
|
// Remove merge behavior from the pipeline list
|
|
2821
3138
|
this.#stageBehaviors = this.#behaviors.filter((behavior) => !(behavior.type === BehaviorTypes.CoreState ||
|
|
@@ -2826,6 +3143,7 @@ class Orchestrator {
|
|
|
2826
3143
|
behavior.type === BehaviorTypes.CoreErrorCallback ||
|
|
2827
3144
|
behavior.type === BehaviorTypes.Merge));
|
|
2828
3145
|
}
|
|
3146
|
+
/** Extracts and registers the core state and emit-state callback behaviors. */
|
|
2829
3147
|
#registerStateBehavior() {
|
|
2830
3148
|
const tabSyncState = this.#behaviors.filter((behavior) => behavior.type === BehaviorTypes.TabSyncState);
|
|
2831
3149
|
const coreState = this.#behaviors.filter((behavior) => behavior.type === BehaviorTypes.CoreState);
|
|
@@ -2836,6 +3154,7 @@ class Orchestrator {
|
|
|
2836
3154
|
this.#coreStateBehavior = stateBehaviors[0] ?? null;
|
|
2837
3155
|
this.#emitStateCallbackBehavior = this.#behaviors.filter((behavior) => isCoreEmitStateCallbackBehavior(behavior))[0];
|
|
2838
3156
|
}
|
|
3157
|
+
/** Extracts and registers the core error, error callback, and error transform behaviors. */
|
|
2839
3158
|
#registerErrorBehavior() {
|
|
2840
3159
|
// Extract and remove error behavior
|
|
2841
3160
|
const coreErrors = this.#behaviors.filter((behavior) => behavior.type === BehaviorTypes.CoreError);
|
|
@@ -2846,11 +3165,17 @@ class Orchestrator {
|
|
|
2846
3165
|
this.#coreErrorCallbackBehavior = this.#behaviors.filter((behavior) => isCoreErrorCallbackBehavior(behavior))[0];
|
|
2847
3166
|
this.#errorTransformBehaviors = this.#behaviors.filter((behavior) => isErrorTransformBehavior(behavior));
|
|
2848
3167
|
}
|
|
3168
|
+
/** Extracts and registers the merge behavior from the behavior list. */
|
|
2849
3169
|
#registerMergeBehavior() {
|
|
2850
3170
|
// Extract and remove merge behavior
|
|
2851
3171
|
const merges = this.#behaviors.filter((behavior) => behavior.type === BehaviorTypes.Merge);
|
|
2852
3172
|
this.#mergeBehavior = merges[0] ?? null;
|
|
2853
3173
|
}
|
|
3174
|
+
/**
|
|
3175
|
+
* Runs a stepwise behavior and returns its pipeline signal.
|
|
3176
|
+
*
|
|
3177
|
+
* @returns A noop, clear-state, or continue signal.
|
|
3178
|
+
*/
|
|
2854
3179
|
async #runStepwise(behaviorType, ctx, current) {
|
|
2855
3180
|
const stepwise = await this.#runUpstreamStage(behaviorType, ctx, current);
|
|
2856
3181
|
if (isVaultClearState(stepwise)) {
|
|
@@ -2861,6 +3186,11 @@ class Orchestrator {
|
|
|
2861
3186
|
}
|
|
2862
3187
|
return VAULT_CONTINUE;
|
|
2863
3188
|
}
|
|
3189
|
+
/**
|
|
3190
|
+
* Runs the post-resolve pipeline stages and returns the final state data.
|
|
3191
|
+
*
|
|
3192
|
+
* @returns The pipeline data flow result or a sentinel signal.
|
|
3193
|
+
*/
|
|
2864
3194
|
async #finishPipeline(ctx, resolved) {
|
|
2865
3195
|
// Stage: operators
|
|
2866
3196
|
let pipelineDataFlow;
|
|
@@ -2901,6 +3231,7 @@ class Orchestrator {
|
|
|
2901
3231
|
// Commit the *cloned* pre-encrypted snapshot to signals
|
|
2902
3232
|
return stateData;
|
|
2903
3233
|
}
|
|
3234
|
+
/** Orchestrates a full replace pipeline cycle for the current attempt. */
|
|
2904
3235
|
async #orchestrateReplace(ctx) {
|
|
2905
3236
|
this.vaultMonitor.startReplace(this.cellKey, VAULT_ORCHESTRATOR, ctx);
|
|
2906
3237
|
await this.#safeAsync(async () => {
|
|
@@ -2918,6 +3249,7 @@ class Orchestrator {
|
|
|
2918
3249
|
return this.#handleFinalState(finalState, ctx);
|
|
2919
3250
|
}, ctx);
|
|
2920
3251
|
}
|
|
3252
|
+
/** Orchestrates a full merge pipeline cycle for the current attempt. */
|
|
2921
3253
|
async #orchestrateMerge(ctx, options) {
|
|
2922
3254
|
this.vaultMonitor.startMerge(this.cellKey, VAULT_ORCHESTRATOR, ctx);
|
|
2923
3255
|
await this.#safeAsync(async () => {
|
|
@@ -2942,6 +3274,11 @@ class Orchestrator {
|
|
|
2942
3274
|
return await this.#handleFinalState(finalState, ctx);
|
|
2943
3275
|
}, ctx);
|
|
2944
3276
|
}
|
|
3277
|
+
/**
|
|
3278
|
+
* Emits monitor events for the final pipeline state and returns the result.
|
|
3279
|
+
*
|
|
3280
|
+
* @returns The final state value passed through.
|
|
3281
|
+
*/
|
|
2945
3282
|
async #handleFinalState(finalState, ctx) {
|
|
2946
3283
|
let payload;
|
|
2947
3284
|
if (isSignalStop(finalState)) {
|
|
@@ -2961,6 +3298,7 @@ class Orchestrator {
|
|
|
2961
3298
|
}
|
|
2962
3299
|
return finalState;
|
|
2963
3300
|
}
|
|
3301
|
+
/** Wraps an async pipeline function with error handling and state finalization. */
|
|
2964
3302
|
async #safeAsync(fn, ctx) {
|
|
2965
3303
|
try {
|
|
2966
3304
|
const result = await fn();
|
|
@@ -2980,6 +3318,11 @@ class Orchestrator {
|
|
|
2980
3318
|
await this.decisionEngine?.notifyFailure(this.buildControllerCtx(ctx), pipelineError);
|
|
2981
3319
|
}
|
|
2982
3320
|
}
|
|
3321
|
+
/**
|
|
3322
|
+
* Iterates through behaviors matching the given stage and applies each one.
|
|
3323
|
+
*
|
|
3324
|
+
* @returns The accumulated pipeline value after all stage behaviors execute.
|
|
3325
|
+
*/
|
|
2983
3326
|
async #runUpstreamStage(stage, ctx, current) {
|
|
2984
3327
|
let stageBehaviors;
|
|
2985
3328
|
if (stage === BehaviorTypes.Resolve) {
|
|
@@ -3076,6 +3419,11 @@ class Orchestrator {
|
|
|
3076
3419
|
}
|
|
3077
3420
|
return current;
|
|
3078
3421
|
}
|
|
3422
|
+
/**
|
|
3423
|
+
* Runs interceptor behaviors and returns a stop signal if any interceptor halts the pipeline.
|
|
3424
|
+
*
|
|
3425
|
+
* @returns Undefined to continue or VAULT_STOP to halt.
|
|
3426
|
+
*/
|
|
3079
3427
|
async #runInterceptorStage(ctx) {
|
|
3080
3428
|
const interceptorBehaviors = this.#stageBehaviors.filter((behavior) => behavior.type === BehaviorTypes.Interceptor);
|
|
3081
3429
|
for (const behavior of interceptorBehaviors) {
|
|
@@ -3096,10 +3444,16 @@ class Orchestrator {
|
|
|
3096
3444
|
}
|
|
3097
3445
|
return;
|
|
3098
3446
|
}
|
|
3447
|
+
/** Returns whether any operator behaviors are registered in the pipeline. */
|
|
3099
3448
|
#containsOperators() {
|
|
3100
3449
|
const operatorBehaviors = this.#stageBehaviors.filter((behavior) => behavior.type === BehaviorTypes.Operator);
|
|
3101
3450
|
return operatorBehaviors.length > 0;
|
|
3102
3451
|
}
|
|
3452
|
+
/**
|
|
3453
|
+
* Runs operator behaviors in sequence and returns the transformed value.
|
|
3454
|
+
*
|
|
3455
|
+
* @returns The pipeline value after all operators execute or undefined on noop.
|
|
3456
|
+
*/
|
|
3103
3457
|
async #runOperatorStage(ctx, current) {
|
|
3104
3458
|
const operatorBehaviors = this.#stageBehaviors.filter((behavior) => behavior.type === BehaviorTypes.Operator);
|
|
3105
3459
|
for (const behavior of operatorBehaviors) {
|
|
@@ -3122,6 +3476,11 @@ class Orchestrator {
|
|
|
3122
3476
|
}
|
|
3123
3477
|
return current;
|
|
3124
3478
|
}
|
|
3479
|
+
/**
|
|
3480
|
+
* Runs encrypt and persist behaviors in sequence for state persistence.
|
|
3481
|
+
*
|
|
3482
|
+
* @returns The pipeline persist value after all stage behaviors execute.
|
|
3483
|
+
*/
|
|
3125
3484
|
async #runPersistStage(stage, ctx, current) {
|
|
3126
3485
|
let stageBehaviors;
|
|
3127
3486
|
stageBehaviors = this.#stageBehaviors.filter((behavior) => behavior.type === stage);
|
|
@@ -3156,6 +3515,7 @@ class Orchestrator {
|
|
|
3156
3515
|
}
|
|
3157
3516
|
return current;
|
|
3158
3517
|
}
|
|
3518
|
+
/** Invokes destroy on each registered behavior with monitor tracing. */
|
|
3159
3519
|
#destroyBehaviors(ctx) {
|
|
3160
3520
|
for (const behavior of this.#behaviors) {
|
|
3161
3521
|
this.vaultMonitor.startDestroy(this.cellKey, behavior.key, ctx);
|
|
@@ -3169,6 +3529,7 @@ class Orchestrator {
|
|
|
3169
3529
|
}
|
|
3170
3530
|
}
|
|
3171
3531
|
}
|
|
3532
|
+
/** Invokes reset on each registered behavior with monitor tracing. */
|
|
3172
3533
|
#resetBehaviors(ctx) {
|
|
3173
3534
|
for (const behavior of this.#behaviors) {
|
|
3174
3535
|
this.vaultMonitor.startReset(this.cellKey, behavior.key, ctx);
|
|
@@ -3182,6 +3543,7 @@ class Orchestrator {
|
|
|
3182
3543
|
}
|
|
3183
3544
|
}
|
|
3184
3545
|
}
|
|
3546
|
+
/** Runs emit-state callbacks with an isolated snapshot clone. */
|
|
3185
3547
|
async #runStateBehaviors(ctx) {
|
|
3186
3548
|
if (this.#emitStateCallbacks?.length > 0) {
|
|
3187
3549
|
const lastSnapshotClone = isolateValue(ctx.lastSnapshot);
|
|
@@ -3193,15 +3555,9 @@ class Orchestrator {
|
|
|
3193
3555
|
}
|
|
3194
3556
|
}
|
|
3195
3557
|
/**
|
|
3196
|
-
* Runs
|
|
3197
|
-
*
|
|
3198
|
-
* - Starts from a normalized ResourceError produced by resourceError().
|
|
3199
|
-
* - Each behavior gets (rawError, currentResourceError, ctx).
|
|
3200
|
-
* - If a behavior returns:
|
|
3201
|
-
* • ResourceError → becomes the next currentResourceError
|
|
3202
|
-
* • VAULT_NOOP → previous ResourceError is kept
|
|
3558
|
+
* Runs error normalization, transforms, state commit, and callbacks in sequence.
|
|
3203
3559
|
*
|
|
3204
|
-
*
|
|
3560
|
+
* @returns The final normalized vault error shape.
|
|
3205
3561
|
*/
|
|
3206
3562
|
async #runErrorBehaviors(rawError, ctx) {
|
|
3207
3563
|
let current;
|
|
@@ -3282,6 +3638,11 @@ class Orchestrator {
|
|
|
3282
3638
|
vaultDebug(`${this.cellKey} #runErrorBehaviors completed with final ResourceError: ${JSON.stringify(current)}`);
|
|
3283
3639
|
return current;
|
|
3284
3640
|
}
|
|
3641
|
+
/**
|
|
3642
|
+
* Determines the resolve type for the incoming state input.
|
|
3643
|
+
*
|
|
3644
|
+
* @returns The resolve type matching the incoming value shape.
|
|
3645
|
+
*/
|
|
3285
3646
|
#getResolveType(incoming) {
|
|
3286
3647
|
if (isHttpResourceRef(incoming)) {
|
|
3287
3648
|
return ResolveTypes.HttpResource;
|
|
@@ -3299,6 +3660,7 @@ class Orchestrator {
|
|
|
3299
3660
|
return ResolveTypes.Value;
|
|
3300
3661
|
}
|
|
3301
3662
|
}
|
|
3663
|
+
/** Loads the initial state from persistence, configuration, or a deferred factory. */
|
|
3302
3664
|
async #loadInitialState(ctx) {
|
|
3303
3665
|
const incoming = {
|
|
3304
3666
|
value: undefined,
|
|
@@ -3333,9 +3695,19 @@ class Orchestrator {
|
|
|
3333
3695
|
this.decisionEngine?.notifySuccess(this.buildControllerCtx(ctx));
|
|
3334
3696
|
}
|
|
3335
3697
|
}
|
|
3698
|
+
/**
|
|
3699
|
+
* Returns persist behaviors registered for this FeatureCell.
|
|
3700
|
+
*
|
|
3701
|
+
* @returns Array of persist behavior instances.
|
|
3702
|
+
*/
|
|
3336
3703
|
#getPersistedBehaviors() {
|
|
3337
3704
|
return this.#stageBehaviors.filter((behavior) => behavior.type === BehaviorTypes.Persist);
|
|
3338
3705
|
}
|
|
3706
|
+
/**
|
|
3707
|
+
* Loads persisted state and decrypts it if encryption behaviors are registered.
|
|
3708
|
+
*
|
|
3709
|
+
* @returns The loaded and optionally decrypted state or undefined on failure.
|
|
3710
|
+
*/
|
|
3339
3711
|
async #loadInitialPersistedState(ctx, persistBehaviors) {
|
|
3340
3712
|
let loaded = undefined;
|
|
3341
3713
|
for (const behavior of persistBehaviors) {
|
|
@@ -3384,6 +3756,7 @@ class Orchestrator {
|
|
|
3384
3756
|
}
|
|
3385
3757
|
}
|
|
3386
3758
|
|
|
3759
|
+
/** Enumeration of conductor license evaluation statuses. */
|
|
3387
3760
|
const ConductorLicenseStatusTypes = {
|
|
3388
3761
|
Pending: 'pending',
|
|
3389
3762
|
Approved: 'approved',
|
|
@@ -3540,20 +3913,32 @@ class ControllerInitializationClass {
|
|
|
3540
3913
|
}
|
|
3541
3914
|
}
|
|
3542
3915
|
|
|
3543
|
-
|
|
3544
|
-
|
|
3545
|
-
|
|
3546
|
-
|
|
3547
|
-
|
|
3548
|
-
|
|
3916
|
+
/**
|
|
3917
|
+
* Coordinates pipeline execution, controller arbitration, and license
|
|
3918
|
+
* gating for a single FeatureCell. The Conductor serializes pipeline
|
|
3919
|
+
* attempts through a FIFO queue and delegates vote resolution to the
|
|
3920
|
+
* DecisionEngine.
|
|
3921
|
+
*/
|
|
3549
3922
|
class Conductor extends Orchestrator {
|
|
3923
|
+
/** FIFO queue of pending pipeline attempts awaiting controller evaluation. */
|
|
3550
3924
|
#attemptQueue = [];
|
|
3925
|
+
/** Instantiated controller instances for this FeatureCell. */
|
|
3551
3926
|
#controllers = [];
|
|
3927
|
+
/** Subject emitting controller lifecycle events to the decision engine. */
|
|
3552
3928
|
#events$ = new Subject();
|
|
3929
|
+
/** Whether the conductor is currently processing an attempt. */
|
|
3553
3930
|
#processing = false;
|
|
3931
|
+
/** Whether the conductor is in a deny state for test flushing. */
|
|
3554
3932
|
#isDenyStateForTesting = false;
|
|
3933
|
+
/** Current license evaluation status for this conductor. */
|
|
3555
3934
|
#conductorLicenseStatus = ConductorLicenseStatusTypes.Pending;
|
|
3935
|
+
/** Subject signaling when the conductor has settled for DevMode testing. */
|
|
3556
3936
|
#settled$ = new Subject();
|
|
3937
|
+
/**
|
|
3938
|
+
* Creates a new Conductor and initializes controllers and licensing.
|
|
3939
|
+
*
|
|
3940
|
+
* @param config - Configuration describing behaviors, controllers, and callbacks.
|
|
3941
|
+
*/
|
|
3557
3942
|
constructor(config) {
|
|
3558
3943
|
super(config);
|
|
3559
3944
|
LicensingService().describeFeature({
|
|
@@ -3575,6 +3960,11 @@ class Conductor extends Orchestrator {
|
|
|
3575
3960
|
this.vaultMonitor.conductorLicenseAttempt(this.cellKey, `${this.cellKey}::license`);
|
|
3576
3961
|
this.initializeOrchestrator(config);
|
|
3577
3962
|
}
|
|
3963
|
+
/**
|
|
3964
|
+
* Enqueues an initialization attempt for the FeatureCell pipeline.
|
|
3965
|
+
*
|
|
3966
|
+
* @param ctx - Behavior context for the initialization cycle.
|
|
3967
|
+
*/
|
|
3578
3968
|
initialize(ctx) {
|
|
3579
3969
|
const initCtx = this.#buildPipelineCtx(ctx, OperationTypes.Initialize, undefined);
|
|
3580
3970
|
this.#enqueueAttempt({
|
|
@@ -3584,6 +3974,14 @@ class Conductor extends Orchestrator {
|
|
|
3584
3974
|
});
|
|
3585
3975
|
}
|
|
3586
3976
|
//#region public method
|
|
3977
|
+
/**
|
|
3978
|
+
* Enqueues a pipeline attempt for a merge or replace operation.
|
|
3979
|
+
*
|
|
3980
|
+
* @param ctx - Behavior context for the current pipeline cycle.
|
|
3981
|
+
* @param incoming - Incoming state input value.
|
|
3982
|
+
* @param operation - Pipeline operation type.
|
|
3983
|
+
* @param options - Optional merge or replace options.
|
|
3984
|
+
*/
|
|
3587
3985
|
conduct(ctx, incoming, operation, options) {
|
|
3588
3986
|
const behaviorCtx = this.#buildPipelineCtx(ctx, operation, options);
|
|
3589
3987
|
const preparedIncoming = this.prepareIncoming(behaviorCtx, incoming, operation);
|
|
@@ -3626,6 +4024,7 @@ class Conductor extends Orchestrator {
|
|
|
3626
4024
|
}
|
|
3627
4025
|
//#endregion
|
|
3628
4026
|
//#region Private Methods
|
|
4027
|
+
/** Routes a pipeline attempt to the appropriate orchestrator method. */
|
|
3629
4028
|
async #processEvent(ctx, options) {
|
|
3630
4029
|
if (ctx.operation === OperationTypes.Initialize) {
|
|
3631
4030
|
await this.initializeFeatureCell(ctx);
|
|
@@ -3638,6 +4037,7 @@ class Conductor extends Orchestrator {
|
|
|
3638
4037
|
this.vaultMonitor.runtimeError(this.cellKey, VAULT_CONDUCTOR, ctx, new Error(`Unknown operation type: "${ctx.operation}"`));
|
|
3639
4038
|
this.#completeCurrentAttempt(ctx);
|
|
3640
4039
|
}
|
|
4040
|
+
/** Schedules a microtask to signal a deny-state completion. */
|
|
3641
4041
|
#enqueueMicrotask() {
|
|
3642
4042
|
queueMicrotask(() => {
|
|
3643
4043
|
this.#denyAttemptCompleted();
|
|
@@ -3669,6 +4069,7 @@ class Conductor extends Orchestrator {
|
|
|
3669
4069
|
this.#enqueueMicrotask();
|
|
3670
4070
|
}
|
|
3671
4071
|
}
|
|
4072
|
+
/** Finalizes the current attempt and advances the queue in a microtask. */
|
|
3672
4073
|
#completeCurrentAttempt(ctx) {
|
|
3673
4074
|
const head = this.#attemptQueue[0];
|
|
3674
4075
|
/* istanbul ignore next */
|
|
@@ -3723,6 +4124,7 @@ class Conductor extends Orchestrator {
|
|
|
3723
4124
|
this.#processQueue();
|
|
3724
4125
|
});
|
|
3725
4126
|
}
|
|
4127
|
+
/** Resets the processing flag and emits a restart-attempt monitor event. */
|
|
3726
4128
|
#resetProcessingState(ctx, outcome) {
|
|
3727
4129
|
this.vaultMonitor.restartControllerAttempt(this.cellKey, ctx.traceId, ctx, outcome);
|
|
3728
4130
|
this.#processing = false;
|
|
@@ -3819,6 +4221,7 @@ class Conductor extends Orchestrator {
|
|
|
3819
4221
|
this.#processQueue();
|
|
3820
4222
|
}
|
|
3821
4223
|
}
|
|
4224
|
+
/** Registers the decision engine and subscribes to controller lifecycle events. */
|
|
3822
4225
|
#registerDecisionEngine() {
|
|
3823
4226
|
this.decisionEngine = new DecisionEngine(this.#controllers, this.#events$);
|
|
3824
4227
|
this.#events$.subscribe({
|
|
@@ -3886,6 +4289,7 @@ class Conductor extends Orchestrator {
|
|
|
3886
4289
|
}
|
|
3887
4290
|
});
|
|
3888
4291
|
}
|
|
4292
|
+
/** Builds an isolated pipeline context for a single attempt. */
|
|
3889
4293
|
#buildPipelineCtx(ctx, operation, options) {
|
|
3890
4294
|
const traceId = assignTraceId();
|
|
3891
4295
|
return {
|
|
@@ -3904,10 +4308,12 @@ class Conductor extends Orchestrator {
|
|
|
3904
4308
|
incoming: undefined
|
|
3905
4309
|
};
|
|
3906
4310
|
}
|
|
4311
|
+
/** Clears the attempt queue and resets the processing flag. */
|
|
3907
4312
|
#resetConductor() {
|
|
3908
4313
|
this.#attemptQueue.length = 0;
|
|
3909
4314
|
this.#processing = false;
|
|
3910
4315
|
}
|
|
4316
|
+
/** Invokes destroy on all registered controllers. */
|
|
3911
4317
|
#destroyControllers(ctx) {
|
|
3912
4318
|
for (const controller of this.#controllers) {
|
|
3913
4319
|
this.vaultMonitor.startDestroy(this.cellKey, controller.key, ctx);
|
|
@@ -3921,6 +4327,7 @@ class Conductor extends Orchestrator {
|
|
|
3921
4327
|
}
|
|
3922
4328
|
}
|
|
3923
4329
|
}
|
|
4330
|
+
/** Invokes reset on all registered controllers. */
|
|
3924
4331
|
#resetControllers(ctx) {
|
|
3925
4332
|
for (const controller of this.#controllers) {
|
|
3926
4333
|
this.vaultMonitor.startReset(this.cellKey, controller.key, ctx);
|
|
@@ -3934,6 +4341,7 @@ class Conductor extends Orchestrator {
|
|
|
3934
4341
|
}
|
|
3935
4342
|
}
|
|
3936
4343
|
}
|
|
4344
|
+
/** Adds the default or user-supplied error controller to the controller list. */
|
|
3937
4345
|
#addDefaultErrorController(controllers, config) {
|
|
3938
4346
|
const errorControllers = config.controllers.filter((controllerClass) => {
|
|
3939
4347
|
return controllerClass.type === ControllerTypes.Error;
|
|
@@ -3949,6 +4357,7 @@ class Conductor extends Orchestrator {
|
|
|
3949
4357
|
controllers.unshift(withCoreErrorController);
|
|
3950
4358
|
}
|
|
3951
4359
|
}
|
|
4360
|
+
/** Removes reserved controller types from the user-supplied list. */
|
|
3952
4361
|
#filterRestrictedControllers(controllers) {
|
|
3953
4362
|
return controllers.filter((controller) => {
|
|
3954
4363
|
if (controller.type === ControllerTypes.License || controller.type === ControllerTypes.CoreAbstain) {
|
|
@@ -3961,6 +4370,7 @@ class Conductor extends Orchestrator {
|
|
|
3961
4370
|
// ---------------------------------------------------------------------------
|
|
3962
4371
|
// INTERNAL SCHEDULING
|
|
3963
4372
|
// ---------------------------------------------------------------------------
|
|
4373
|
+
/** Initializes all controllers and registers the decision engine. */
|
|
3964
4374
|
#registerControllers(config) {
|
|
3965
4375
|
config.controllers = config.controllers ?? [];
|
|
3966
4376
|
const allControllers = this.#filterRestrictedControllers(config.controllers);
|
|
@@ -4001,12 +4411,14 @@ class Conductor extends Orchestrator {
|
|
|
4001
4411
|
this.vaultMonitor.endControllerVote(this.cellKey, pending.controllerCtx.traceId, pending.controllerCtx, decision);
|
|
4002
4412
|
}), map((decision) => decision.outcome));
|
|
4003
4413
|
}
|
|
4414
|
+
/** Signals a deny-state settlement for DevMode testing. */
|
|
4004
4415
|
#denyAttemptCompleted() {
|
|
4005
4416
|
if (!DevMode.active)
|
|
4006
4417
|
return;
|
|
4007
4418
|
this.#settled$.next();
|
|
4008
4419
|
}
|
|
4009
4420
|
// ADD
|
|
4421
|
+
/** Signals queue settlement when all attempts have been processed. */
|
|
4010
4422
|
#attemptCompleted() {
|
|
4011
4423
|
if (!DevMode.active || this.#attemptQueue.length > 0)
|
|
4012
4424
|
return;
|
|
@@ -4014,6 +4426,7 @@ class Conductor extends Orchestrator {
|
|
|
4014
4426
|
this.#settled$.next();
|
|
4015
4427
|
});
|
|
4016
4428
|
}
|
|
4429
|
+
/** Returns a promise that resolves when the conductor has settled. */
|
|
4017
4430
|
#conductorSettled() {
|
|
4018
4431
|
return firstValueFrom(this.#settled$);
|
|
4019
4432
|
}
|
|
@@ -4065,27 +4478,46 @@ function featureCellValidation(descriptor, behaviors = []) {
|
|
|
4065
4478
|
}
|
|
4066
4479
|
}
|
|
4067
4480
|
|
|
4068
|
-
|
|
4069
|
-
|
|
4070
|
-
|
|
4071
|
-
|
|
4072
|
-
|
|
4073
|
-
|
|
4481
|
+
/**
|
|
4482
|
+
* Abstract builder that assembles the fluent configuration API and
|
|
4483
|
+
* lifecycle wiring for a FeatureCell. Subclasses call setup() to obtain
|
|
4484
|
+
* a CellBuilderContract, then build the final FeatureCellShape from it.
|
|
4485
|
+
* The builder manages conductor creation, initialization guards, and
|
|
4486
|
+
* state operation dispatch.
|
|
4487
|
+
*/
|
|
4074
4488
|
class FeatureCellBuilder {
|
|
4075
4489
|
featureCellConfiguration;
|
|
4076
4490
|
defaultBehaviors;
|
|
4077
4491
|
behaviors;
|
|
4078
4492
|
controllers;
|
|
4493
|
+
/** Whether the cell encountered a critical failure and is permanently unusable. */
|
|
4079
4494
|
#cellCorrupt = false;
|
|
4495
|
+
/** Conductor instance managing pipeline execution for this cell. */
|
|
4080
4496
|
#conductor;
|
|
4497
|
+
/** Whether the cell has been initialized via the builder. */
|
|
4081
4498
|
#initialized = false;
|
|
4499
|
+
/** Vault monitor instance for tracing lifecycle events. */
|
|
4082
4500
|
#vaultMonitor = VaultMonitor();
|
|
4501
|
+
/** Reference to the constructed FeatureCellBaseShape. */
|
|
4083
4502
|
cell;
|
|
4503
|
+
/** Unique key identifying this FeatureCell. */
|
|
4084
4504
|
cellKey;
|
|
4505
|
+
/** Shared behavior context for pipeline execution. */
|
|
4085
4506
|
ctx;
|
|
4507
|
+
/** Subject signaling cell destruction. */
|
|
4086
4508
|
destroyed$ = new Subject();
|
|
4509
|
+
/** Subject signaling cell reset. */
|
|
4087
4510
|
reset$ = new Subject();
|
|
4511
|
+
/** Subject emitting state snapshots to subscribers. */
|
|
4088
4512
|
state$ = new Subject();
|
|
4513
|
+
/**
|
|
4514
|
+
* Creates the builder and initializes the shared behavior context.
|
|
4515
|
+
*
|
|
4516
|
+
* @param featureCellConfiguration - FeatureCell descriptor configuration.
|
|
4517
|
+
* @param defaultBehaviors - Default behavior classes provided by the framework.
|
|
4518
|
+
* @param behaviors - User-supplied behavior classes.
|
|
4519
|
+
* @param controllers - User-supplied controller classes.
|
|
4520
|
+
*/
|
|
4089
4521
|
constructor(
|
|
4090
4522
|
// kept for parity with original API, even if not used directly
|
|
4091
4523
|
featureCellConfiguration, defaultBehaviors, behaviors, controllers) {
|
|
@@ -4096,6 +4528,11 @@ class FeatureCellBuilder {
|
|
|
4096
4528
|
this.cellKey = this.featureCellConfiguration.key;
|
|
4097
4529
|
this.ctx = this.#buildCtx();
|
|
4098
4530
|
}
|
|
4531
|
+
/**
|
|
4532
|
+
* Builds the initial behavior context with locked snapshot reference.
|
|
4533
|
+
*
|
|
4534
|
+
* @returns The constructed behavior context.
|
|
4535
|
+
*/
|
|
4099
4536
|
#buildCtx() {
|
|
4100
4537
|
const destroyed$ = this.destroyed$.asObservable();
|
|
4101
4538
|
const state$ = this.state$;
|
|
@@ -4133,6 +4570,7 @@ class FeatureCellBuilder {
|
|
|
4133
4570
|
// ---------------------------------------------------------------------------
|
|
4134
4571
|
// INTERNAL LIFECYCLE
|
|
4135
4572
|
// ---------------------------------------------------------------------------
|
|
4573
|
+
/** Resets the conductor and emits a reset signal. */
|
|
4136
4574
|
reset() {
|
|
4137
4575
|
this.#vaultMonitor.startReset(this.cellKey, VAULT_FEATURE_CELL, this.ctx);
|
|
4138
4576
|
vaultWarn(`${VAULT_FEATURE_CELL}: reset`);
|
|
@@ -4141,6 +4579,7 @@ class FeatureCellBuilder {
|
|
|
4141
4579
|
this.#conductor?.reset(this.ctx);
|
|
4142
4580
|
this.#vaultMonitor.endReset(this.cellKey, VAULT_FEATURE_CELL, this.ctx);
|
|
4143
4581
|
}
|
|
4582
|
+
/** Destroys the conductor, completes all subjects, and emits destruction signals. */
|
|
4144
4583
|
destroy() {
|
|
4145
4584
|
this.#vaultMonitor.startDestroy(this.cellKey, VAULT_FEATURE_CELL, this.ctx);
|
|
4146
4585
|
vaultWarn(`${VAULT_FEATURE_CELL}: destroy`);
|
|
@@ -4152,6 +4591,7 @@ class FeatureCellBuilder {
|
|
|
4152
4591
|
this.state$.complete();
|
|
4153
4592
|
this.#vaultMonitor.endDestroy(this.cellKey, VAULT_FEATURE_CELL, this.ctx);
|
|
4154
4593
|
}
|
|
4594
|
+
/** Throws if the cell is corrupt or has not been initialized. */
|
|
4155
4595
|
#ensureInitialized() {
|
|
4156
4596
|
if (this.#cellCorrupt) {
|
|
4157
4597
|
const errorMessage = `[vault] FeatureCell "${this.featureCellConfiguration.key}" encountered a critical initialization failure and is now in a corrupted state. Further use is blocked.`;
|
|
@@ -4164,6 +4604,7 @@ class FeatureCellBuilder {
|
|
|
4164
4604
|
throw new Error(errorMessage);
|
|
4165
4605
|
}
|
|
4166
4606
|
}
|
|
4607
|
+
/** Creates the conductor, validates the cell, and starts the initialization lifecycle. */
|
|
4167
4608
|
#initialize(ctx) {
|
|
4168
4609
|
if (this.#initialized) {
|
|
4169
4610
|
const errorMessage = `[vault] FeatureCell "${this.featureCellConfiguration.key}" already initialized.`;
|
|
@@ -4216,11 +4657,17 @@ class FeatureCellBuilder {
|
|
|
4216
4657
|
throw err;
|
|
4217
4658
|
}
|
|
4218
4659
|
}
|
|
4660
|
+
/** Marks the cell as corrupt, logs a runtime error, and throws. */
|
|
4219
4661
|
#handleCorruptionError(message) {
|
|
4220
4662
|
this.#cellCorrupt = true;
|
|
4221
4663
|
this.#vaultMonitor.runtimeError(this.cellKey, VAULT_FEATURE_CELL, this.ctx, message);
|
|
4222
4664
|
throw new Error(message);
|
|
4223
4665
|
}
|
|
4666
|
+
/**
|
|
4667
|
+
* Constructs the fluent CellBuilderContract with pre-initialization guards.
|
|
4668
|
+
*
|
|
4669
|
+
* @returns The builder contract exposing configuration and initialize methods.
|
|
4670
|
+
*/
|
|
4224
4671
|
setup() {
|
|
4225
4672
|
const afterTapCallbacks = [];
|
|
4226
4673
|
const beforeTapCallbacks = [];
|
|
@@ -4333,26 +4780,41 @@ class FeatureCellBuilder {
|
|
|
4333
4780
|
// ---------------------------------------------------------------------------
|
|
4334
4781
|
// STATE OPERATIONS
|
|
4335
4782
|
// ---------------------------------------------------------------------------
|
|
4783
|
+
/** Dispatches a merge operation through the conductor pipeline. */
|
|
4336
4784
|
mergeState(incoming, options) {
|
|
4337
4785
|
this.#ensureInitialized();
|
|
4338
4786
|
return this.#conductor.conduct(this.ctx, incoming, OperationTypes.Merge, options);
|
|
4339
4787
|
}
|
|
4788
|
+
/** Dispatches a replace operation through the conductor pipeline. */
|
|
4340
4789
|
replaceState(incoming, options) {
|
|
4341
4790
|
this.#ensureInitialized();
|
|
4342
4791
|
return this.#conductor.conduct(this.ctx, incoming, OperationTypes.Replace, options);
|
|
4343
4792
|
}
|
|
4344
4793
|
}
|
|
4345
4794
|
|
|
4346
|
-
|
|
4347
|
-
|
|
4348
|
-
|
|
4349
|
-
|
|
4350
|
-
|
|
4351
|
-
|
|
4795
|
+
/**
|
|
4796
|
+
* Concrete FeatureCell factory that extends the builder to produce a
|
|
4797
|
+
* fully configured FeatureCellShape instance. The class wires fluent
|
|
4798
|
+
* API extensions from behaviors and controllers onto the resulting
|
|
4799
|
+
* cell and attaches non-enumerable context and key properties.
|
|
4800
|
+
*/
|
|
4352
4801
|
class FeatureCellClass extends FeatureCellBuilder {
|
|
4802
|
+
/**
|
|
4803
|
+
* Creates a new FeatureCellClass with the provided descriptor and behavior/controller lists.
|
|
4804
|
+
*
|
|
4805
|
+
* @param descriptor - FeatureCell configuration descriptor.
|
|
4806
|
+
* @param defaultBehaviors - Default behavior classes provided by the framework.
|
|
4807
|
+
* @param behaviors - User-supplied behavior classes.
|
|
4808
|
+
* @param controllers - User-supplied controller classes.
|
|
4809
|
+
*/
|
|
4353
4810
|
constructor(descriptor, defaultBehaviors, behaviors, controllers) {
|
|
4354
4811
|
super(descriptor, defaultBehaviors, behaviors, controllers);
|
|
4355
4812
|
}
|
|
4813
|
+
/**
|
|
4814
|
+
* Assembles and returns a fully configured FeatureCellShape with fluent API extensions.
|
|
4815
|
+
*
|
|
4816
|
+
* @returns The constructed FeatureCellShape instance.
|
|
4817
|
+
*/
|
|
4356
4818
|
build() {
|
|
4357
4819
|
const builder = this.setup();
|
|
4358
4820
|
const ctx = this.ctx;
|
|
@@ -4483,12 +4945,6 @@ function resetFeatureCellToken() {
|
|
|
4483
4945
|
featureCellTokenRequested.clear();
|
|
4484
4946
|
}
|
|
4485
4947
|
|
|
4486
|
-
// --- AI Model File Path (DO NOT DELETE) ---
|
|
4487
|
-
// FilePath: lib > public-api.ts
|
|
4488
|
-
// Updated: 2026-03-30 15:42
|
|
4489
|
-
// Generated by pathcomment [tab] (see .vscode/typescript.code-snippets) or
|
|
4490
|
-
// cmd+alt+j (see .vscode/keybindings.json)
|
|
4491
|
-
// --- END AI MODEL FILE PATH ---
|
|
4492
4948
|
/*
|
|
4493
4949
|
* This is for version support in dev mode and tracking in the devtools
|
|
4494
4950
|
*/
|