@sdux-vault/engine 0.4.0 → 0.8.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.
@@ -1,4 +1,4 @@
1
- import { registerVersion, vaultDebug, ControllerVotes, ControllerMessageTypes, OperationTypes, vaultWarn, VaultController, defineControllerKey, ControllerTypes, DevMode, DecisionOutcomeTypes, vaultError, defineBehaviorKey, DEVTOOLS_LOGGING_KEY_CONSTANT, DEVTOOLS_AGGREGATE_KEY_CONSTANT, createVaultError, EventTypes, EventBoundaryTypes, setVaultLogLevel, isTestEnv, BEHAVIOR_META, validateBehaviorKey, BehaviorTypes, VAULT_STOP, isVaultNoop, VaultPrivateErrorService, isHttpResourceRef, isFunction, isStateInputShape, isolateValue, isVaultClearState, VAULT_CLEAR_STATE, VAULT_NOOP, VAULT_CONTINUE, isVaultContinue, isUndefined, isDefined, isNullish, ResolveTypes, isPromise, VaultUsagePromiseError, CONTROLLER_META, validateControllerKey, VaultLicenseError } from '@sdux-vault/shared';
1
+ import { registerVersion, vaultDebug, ControllerVotes, ControllerMessageTypes, OperationTypes, vaultWarn, VaultController, defineControllerKey, ControllerTypes, DevMode, DecisionOutcomeTypes, vaultError, defineBehaviorKey, DEVTOOLS_LOGGING_KEY_CONSTANT, DEVTOOLS_AGGREGATE_KEY_CONSTANT, createVaultError, EventTypes, EventBoundaryTypes, setVaultLogLevel, isTestEnv, BEHAVIOR_META, validateBehaviorKey, BehaviorTypes, VAULT_STOP, isVaultNoop, VaultLicenseError, VaultBehavior, VaultPrivateErrorService, isHttpResourceRef, isFunction, isStateInputShape, isolateValue, isVaultClearState, VAULT_CLEAR_STATE, VAULT_NOOP, VAULT_CONTINUE, isVaultContinue, isUndefined, isDefined, isNullish, ResolveTypes, isPromise, VaultUsagePromiseError, CONTROLLER_META, validateControllerKey } from '@sdux-vault/shared';
2
2
  import { of, map, catchError, forkJoin, Subject, ReplaySubject, isObservable, firstValueFrom, tap } from 'rxjs';
3
3
  import { __decorate } from 'tslib';
4
4
  import { filter } from 'rxjs/operators';
@@ -11,7 +11,7 @@ import { EventBus, initDevtoolsWidget } from '@sdux-vault/devtools';
11
11
  // cmd+alt+j (see .vscode/keybindings.json)
12
12
  // --- END AI MODEL FILE PATH ---
13
13
  const SDUX_PACKAGE = '@sdux-vault/engine';
14
- const SDUX_VERSION = '0.4.0';
14
+ const SDUX_VERSION = '0.8.0';
15
15
  registerVersion(SDUX_PACKAGE, SDUX_VERSION);
16
16
 
17
17
  /**
@@ -1505,40 +1505,40 @@ const KNOWN_VAULT_KEYS = new Set([
1505
1505
  // ---------------------------------------------------------------------------
1506
1506
  // Core Behaviors (libs/core)
1507
1507
  // ---------------------------------------------------------------------------
1508
+ 'SDUX::Behavior::Core::AfterTap',
1509
+ 'SDUX::Behavior::Core::ArrayMerge',
1510
+ 'SDUX::Behavior::Core::BeforeTap',
1511
+ 'SDUX::Behavior::Core::EmitState',
1508
1512
  'SDUX::Behavior::Core::Error',
1509
1513
  'SDUX::Behavior::Core::ErrorCallback',
1510
1514
  'SDUX::Behavior::Core::Filter',
1515
+ 'SDUX::Behavior::Core::FromObservable',
1516
+ 'SDUX::Behavior::Core::FromPromise',
1517
+ 'SDUX::Behavior::Core::FromStream',
1518
+ 'SDUX::Behavior::Core::ObjectMerge',
1519
+ 'SDUX::Behavior::Core::Observable',
1520
+ 'SDUX::Behavior::Core::Promise',
1511
1521
  'SDUX::Behavior::Core::Reducer',
1512
- 'SDUX::Behavior::Core::BeforeTap',
1513
- 'SDUX::Behavior::Core::AfterTap',
1514
- 'SDUX::Behavior::Core::EmitState',
1515
1522
  'SDUX::Behavior::Core::State',
1516
1523
  'SDUX::Behavior::Core::Value',
1517
- 'SDUX::Behavior::Core::Observable',
1518
- 'SDUX::Behavior::Core::Promise',
1519
- 'SDUX::Behavior::Core::FromStream',
1520
- 'SDUX::Behavior::Core::FromPromise',
1521
- 'SDUX::Behavior::Core::FromObservable',
1522
- 'SDUX::Behavior::Core::ObjectMerge',
1523
- 'SDUX::Behavior::Core::ArrayMerge',
1524
1524
  // ---------------------------------------------------------------------------
1525
1525
  // Addon Behaviors (libs/addons)
1526
1526
  // ---------------------------------------------------------------------------
1527
- 'SDUX::Behavior::Persist::SessionStorage',
1528
- 'SDUX::Behavior::Persist::LocalStorage',
1529
- 'SDUX::Behavior::Persist::CookieStorage',
1530
- 'SDUX::Behavior::Encrypt::Aes256',
1531
- 'SDUX::Behavior::Policy::StepwiseFilter',
1532
- 'SDUX::Behavior::Policy::StepwiseReducer',
1533
- 'SDUX::Behavior::Policy::StepwiseResolve',
1534
1527
  'SDUX::Behavior::Addon::DistinctUntilChanged',
1528
+ 'SDUX::Behavior::Cache::State',
1529
+ 'SDUX::Behavior::Core::Lookup',
1530
+ 'SDUX::Behavior::Core::Query',
1531
+ 'SDUX::Behavior::Encrypt::Aes256',
1535
1532
  'SDUX::Behavior::Interceptor::GlobalErrorPause',
1536
1533
  'SDUX::Behavior::Merge::ArrayAppend',
1537
1534
  'SDUX::Behavior::Merge::ArrayPush',
1538
1535
  'SDUX::Behavior::Merge::Deep',
1539
- 'SDUX::Behavior::Core::Query',
1540
- 'SDUX::Behavior::Core::Lookup',
1541
- 'SDUX::Behavior::Cache::State',
1536
+ 'SDUX::Behavior::Persist::CookieStorage',
1537
+ 'SDUX::Behavior::Persist::LocalStorage',
1538
+ 'SDUX::Behavior::Persist::SessionStorage',
1539
+ 'SDUX::Behavior::Policy::StepwiseFilter',
1540
+ 'SDUX::Behavior::Policy::StepwiseReducer',
1541
+ 'SDUX::Behavior::Policy::StepwiseResolve',
1542
1542
  // ---------------------------------------------------------------------------
1543
1543
  // Engine Behaviors (vault-engine)
1544
1544
  // ---------------------------------------------------------------------------
@@ -1552,10 +1552,10 @@ const KNOWN_VAULT_KEYS = new Set([
1552
1552
  // ---------------------------------------------------------------------------
1553
1553
  // Addon Controllers (libs/addons)
1554
1554
  // ---------------------------------------------------------------------------
1555
+ 'SDUX::Controller::Policy::Delay',
1556
+ 'SDUX::Controller::Policy::MaxFailures',
1555
1557
  'SDUX::Controller::Policy::ReplayGlobalError',
1556
1558
  'SDUX::Controller::Policy::Stepwise',
1557
- 'SDUX::Controller::Policy::MaxFailures',
1558
- 'SDUX::Controller::Policy::Delay',
1559
1559
  'SDUX::Controller::Policy::Throttle'
1560
1560
  ]);
1561
1561
 
@@ -1623,7 +1623,7 @@ class VaultCoreInstance {
1623
1623
  this.#vaultConfig = Object.freeze(vaultConfig);
1624
1624
  DevMode.setDevMode(this.#vaultConfig.devMode);
1625
1625
  setVaultLogLevel(this.#vaultConfig.logLevel);
1626
- this.#bypassLicensing = config.devMode ? (config.bypassLicensing ?? true) : false;
1626
+ this.#bypassLicensing = config.devMode ? (config.bypassLicensing ?? false) : false;
1627
1627
  this.#licenseTimeoutMs = config.licenseTimeoutMs ?? 15_000;
1628
1628
  this.#warnIfAccidentalDevMode();
1629
1629
  }
@@ -2023,8 +2023,6 @@ function isAuthorizedKey(key) {
2023
2023
  function hasVaultLicense() {
2024
2024
  if (!instance)
2025
2025
  return false;
2026
- if (instance.isBypassLicensing())
2027
- return true;
2028
2026
  return instance.hasVaultLicense();
2029
2027
  }
2030
2028
 
@@ -2295,6 +2293,210 @@ const assignTraceId = () => {
2295
2293
  */
2296
2294
  const isPipelineTerminal = (current) => isVaultNoop(current) || isSignalStop(current);
2297
2295
 
2296
+ /**
2297
+ * The public keys
2298
+ */
2299
+ const PublicKeys = {
2300
+ /**
2301
+ * the pro public key
2302
+ */
2303
+ pro: `
2304
+ -----BEGIN PUBLIC KEY-----
2305
+ -----END PUBLIC KEY-----
2306
+ `,
2307
+ /**
2308
+ * the enterprise public key
2309
+ */
2310
+ enterprise: `
2311
+ -----BEGIN PUBLIC KEY-----
2312
+ -----END PUBLIC KEY-----
2313
+ `,
2314
+ /**
2315
+ * the development public key
2316
+ */
2317
+ development: `
2318
+ -----BEGIN PUBLIC KEY-----
2319
+ MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA6RDckQGY38NrlPq70YfI
2320
+ hIpBFmc9baNLS2HIeyKIU2m+Vvnt3AYcQJlQnzmO4ZLDOUkIP+VxAjaSCaUBeTOx
2321
+ KDYXAK8ds0xEur0cRV4Kn26jhLveLZVpfVzhkOjJ6I9ggLeh5JRt7kj3NPgKwnSm
2322
+ t5DxLh4JPyLfZdBDtW8tU2tCgCfEs2cArrVo/fg7Dei8FJ8w83PDhsKmX5UazSfj
2323
+ MC57gkTZ/cZmKBvVoIhnqaWVXcRRL+wZPArIsO801DRiMAhROyQYyC3EKpGLyUc1
2324
+ 1VSKqdmQLIUE1pt151EnmStB+VOsMzCLZH4TyU7cd1Afs2Siqu947W2w4Qpm5+Y/
2325
+ FwIDAQAB
2326
+ -----END PUBLIC KEY-----
2327
+ `
2328
+ };
2329
+
2330
+ /**
2331
+ * Static license verification utility used to validate signed license tokens
2332
+ * against tier-specific public keys. Tokens use a dot-separated format:
2333
+ * `base64(payload).base64(signature)`.
2334
+ *
2335
+ * The verification process:
2336
+ * - Splits the token on `.` to extract the encoded payload and signature
2337
+ * - Decodes the payload from base64 to determine the license tier
2338
+ * - Rejects tokens with missing or unrecognized tier
2339
+ * - Loads the appropriate PEM-encoded public key for the tier
2340
+ * - Uses `crypto.subtle.verify()` (RSA-SHA256) to validate the signature
2341
+ * against the raw base64-encoded payload string
2342
+ *
2343
+ * Verification failures return `false` rather than throwing.
2344
+ */
2345
+ const VerifyLicenseToken = {
2346
+ /**
2347
+ * Verifies the supplied signed license token using the public key associated
2348
+ * with its declared tier.
2349
+ *
2350
+ * @param token - A dot-separated token in the format
2351
+ * `base64(payload).base64(signature)`.
2352
+ * @returns A promise resolving to `true` if the signature validates;
2353
+ * otherwise `false`.
2354
+ */
2355
+ verify: async (token) => {
2356
+ try {
2357
+ const dotIndex = token.indexOf('.');
2358
+ if (dotIndex === -1) {
2359
+ return false;
2360
+ }
2361
+ const encodedPayload = token.substring(0, dotIndex);
2362
+ const signatureB64 = token.substring(dotIndex + 1);
2363
+ const payloadJson = atob(encodedPayload);
2364
+ const payload = JSON.parse(payloadJson);
2365
+ const signature = Uint8Array.from(atob(signatureB64), (c) => c.charCodeAt(0));
2366
+ const licenseType = payload?.licenseType;
2367
+ if (!licenseType) {
2368
+ return false;
2369
+ }
2370
+ const keyPem = PublicKeys[licenseType];
2371
+ if (!keyPem)
2372
+ return false;
2373
+ const key = await importPublicKey(keyPem);
2374
+ const verified = await crypto.subtle.verify({
2375
+ name: 'RSASSA-PKCS1-v1_5',
2376
+ hash: 'SHA-256'
2377
+ }, key, signature, new TextEncoder().encode(encodedPayload));
2378
+ return verified;
2379
+ }
2380
+ catch {
2381
+ return false;
2382
+ }
2383
+ }
2384
+ };
2385
+ /**
2386
+ * Imports an RSA public key from PEM format into a WebCrypto `CryptoKey`
2387
+ * configured for signature verification.
2388
+ *
2389
+ * @param pem - A PEM-encoded public key in subjectPublicKeyInfo format.
2390
+ * @returns A promise resolving to a usable verification `CryptoKey`.
2391
+ */
2392
+ async function importPublicKey(pem) {
2393
+ const binaryDer = str2ab(pemToDer(pem));
2394
+ return crypto.subtle.importKey('spki', binaryDer, {
2395
+ name: 'RSASSA-PKCS1-v1_5',
2396
+ hash: 'SHA-256'
2397
+ }, false, ['verify']);
2398
+ }
2399
+ /**
2400
+ * Converts a PEM-encoded public key into its raw DER body.
2401
+ *
2402
+ * @param pem - The PEM string containing header/footer markers.
2403
+ * @returns A base64-decoded DER binary string.
2404
+ */
2405
+ function pemToDer(pem) {
2406
+ const b64 = pem.replace(/-----.*KEY-----/g, '').replace(/\s+/g, '');
2407
+ return atob(b64);
2408
+ }
2409
+ /**
2410
+ * Converts a binary string into an `ArrayBuffer`, suitable for WebCrypto key
2411
+ * import operations.
2412
+ *
2413
+ * @param str - A binary string.
2414
+ * @returns An `ArrayBuffer` containing the same byte values.
2415
+ */
2416
+ function str2ab(str) {
2417
+ const buf = new ArrayBuffer(str.length);
2418
+ const arr = new Uint8Array(buf);
2419
+ for (let i = 0; i < str.length; i++)
2420
+ arr[i] = str.charCodeAt(i);
2421
+ return buf;
2422
+ }
2423
+
2424
+ class LicensingAbstract {
2425
+ static needsLicense;
2426
+ static key;
2427
+ #licenseToken;
2428
+ #featureCellKey;
2429
+ #key;
2430
+ #licenseService;
2431
+ constructor(ctx) {
2432
+ const ctor = this.constructor;
2433
+ if (typeof ctor.key !== 'string' || !ctor.key.trim()) {
2434
+ throw new VaultLicenseError(`LicensingClass requires a static "key". Did you forget @VaultBehavior?`);
2435
+ }
2436
+ this.#licenseService = LicensingService();
2437
+ this.#key = ctor.key;
2438
+ this.#featureCellKey = ctx.featureCellKey;
2439
+ if (ctor.needsLicense) {
2440
+ this.#requestLicense();
2441
+ }
2442
+ }
2443
+ #requestLicense() {
2444
+ this.#licenseToken = this.#licenseService.requestLicense(this.#featureCellKey, this.#key);
2445
+ }
2446
+ validateLicense(valid) {
2447
+ if (!this.#licenseToken) {
2448
+ throw new VaultLicenseError(`validateLicense() called but no license was requested for "${this.#featureCellKey}" and "${this.#key}".`);
2449
+ }
2450
+ this.#licenseService.validateLicense(this.#featureCellKey, this.#key, this.#licenseToken, valid);
2451
+ }
2452
+ }
2453
+
2454
+ let withCoreLicenseBehavior = class withCoreLicenseBehavior extends LicensingAbstract {
2455
+ behaviorCtx;
2456
+ static type;
2457
+ static key;
2458
+ static critical;
2459
+ static needsLicense;
2460
+ type = BehaviorTypes.CoreLicense;
2461
+ critical = true;
2462
+ key;
2463
+ constructor(key, behaviorCtx) {
2464
+ super(behaviorCtx);
2465
+ this.behaviorCtx = behaviorCtx;
2466
+ this.key = key;
2467
+ this.#performLicenseValidation();
2468
+ }
2469
+ async #performLicenseValidation() {
2470
+ try {
2471
+ const payload = this.behaviorCtx.licensePayload;
2472
+ if (!payload?.token) {
2473
+ this.validateLicense(false);
2474
+ return;
2475
+ }
2476
+ const isValid = await VerifyLicenseToken.verify(payload.token);
2477
+ this.validateLicense(isValid);
2478
+ }
2479
+ catch {
2480
+ this.validateLicense(false);
2481
+ }
2482
+ }
2483
+ destroy() {
2484
+ vaultWarn(`${this.key} - destroy noop`);
2485
+ }
2486
+ reset() {
2487
+ vaultWarn(`${this.key} - reset noop`);
2488
+ }
2489
+ };
2490
+ withCoreLicenseBehavior = __decorate([
2491
+ VaultBehavior({
2492
+ type: BehaviorTypes.CoreLicense,
2493
+ key: defineBehaviorKey('Core', 'License'),
2494
+ critical: true,
2495
+ needsLicense: true,
2496
+ licenseId: 'sdux-vault'
2497
+ })
2498
+ ], withCoreLicenseBehavior);
2499
+
2298
2500
  // --- AI Model File Path (DO NOT DELETE) ---
2299
2501
  // FilePath: lib > src > orchestrator > orchestrator.ts
2300
2502
  // Updated: 2026-04-07 16:30
@@ -2436,6 +2638,7 @@ class Orchestrator {
2436
2638
  defaultBehaviors = this.#determineCoreCallbackErrorBehavior(defaultBehaviors, config);
2437
2639
  defaultBehaviors = this.#addDefaultMergeBehavior(defaultBehaviors, config);
2438
2640
  defaultBehaviors = this.#addCoreEmitStateCallbackBehavior(defaultBehaviors, config);
2641
+ defaultBehaviors = this.#addCoreLicenseBehavior(defaultBehaviors);
2439
2642
  return defaultBehaviors;
2440
2643
  }
2441
2644
  #determineCoreCallbackErrorBehavior(behaviors, config) {
@@ -2450,6 +2653,13 @@ class Orchestrator {
2450
2653
  }
2451
2654
  return behaviors;
2452
2655
  }
2656
+ #addCoreLicenseBehavior(behaviors) {
2657
+ behaviors = behaviors?.filter((behavior) => behavior.type !== BehaviorTypes.CoreLicense);
2658
+ if (hasVaultLicense()) {
2659
+ behaviors.push(withCoreLicenseBehavior);
2660
+ }
2661
+ return behaviors;
2662
+ }
2453
2663
  #registerBehaviorsWithVault(behaviors) {
2454
2664
  // eslint-disable-next-line
2455
2665
  const behaviorMetadata = behaviors.map((behavior) => {
@@ -2474,8 +2684,9 @@ class Orchestrator {
2474
2684
  behavior.type === BehaviorTypes.CoreBeforeTap ||
2475
2685
  behavior.type === BehaviorTypes.CoreError ||
2476
2686
  behavior.type === BehaviorTypes.CoreErrorCallback ||
2477
- behavior.type === BehaviorTypes.CoreState ||
2478
2687
  behavior.type === BehaviorTypes.CoreEmitState ||
2688
+ behavior.type === BehaviorTypes.CoreLicense ||
2689
+ behavior.type === BehaviorTypes.CoreState ||
2479
2690
  behavior.type === BehaviorTypes.Filter ||
2480
2691
  behavior.type === BehaviorTypes.FromObservable ||
2481
2692
  behavior.type === BehaviorTypes.FromPromise ||
@@ -4084,36 +4295,6 @@ class FeatureCellClass extends FeatureCellBuilder {
4084
4295
  }
4085
4296
  }
4086
4297
 
4087
- class LicensingAbstract {
4088
- static needsLicense;
4089
- static key;
4090
- #licenseToken;
4091
- #featureCellKey;
4092
- #key;
4093
- #licenseService;
4094
- constructor(ctx) {
4095
- const ctor = this.constructor;
4096
- if (typeof ctor.key !== 'string' || !ctor.key.trim()) {
4097
- throw new VaultLicenseError(`LicensingClass requires a static "key". Did you forget @VaultBehavior?`);
4098
- }
4099
- this.#licenseService = LicensingService();
4100
- this.#key = ctor.key;
4101
- this.#featureCellKey = ctx.featureCellKey;
4102
- if (ctor.needsLicense) {
4103
- this.#requestLicense();
4104
- }
4105
- }
4106
- #requestLicense() {
4107
- this.#licenseToken = this.#licenseService.requestLicense(this.#featureCellKey, this.#key);
4108
- }
4109
- validateLicense(valid) {
4110
- if (!this.#licenseToken) {
4111
- throw new VaultLicenseError(`validateLicense() called but no license was requested for "${this.#featureCellKey}" and "${this.#key}".`);
4112
- }
4113
- this.#licenseService.validateLicense(this.#featureCellKey, this.#key, this.#licenseToken, valid);
4114
- }
4115
- }
4116
-
4117
4298
  /**
4118
4299
  * The feature cell tokens map
4119
4300
  */
@@ -4205,5 +4386,5 @@ function resetFeatureCellToken() {
4205
4386
  * Generated bundle index. Do not edit.
4206
4387
  */
4207
4388
 
4208
- export { Conductor, FeatureCellClass, LicensingAbstract, VAULT_LICENSE_ID, VaultCore, createFeatureCellToken, getFeatureCellToken, getLicensePayload, getVaultRegistryForTests, hasVaultLicense, isAuthorizedKey, registerFeatureCell, registerVaultSettled, resetFeatureCellRegistry, resetVaultForTests, vaultAllSettled, vaultSettled };
4389
+ export { Conductor, FeatureCellClass, LicensingAbstract, VAULT_LICENSE_ID, VaultCore, createFeatureCellToken, getFeatureCellToken, getLicensePayload, getVaultRegistryForTests, hasVaultLicense, isAuthorizedKey, isPipelineTerminal, registerFeatureCell, registerVaultSettled, resetFeatureCellRegistry, resetVaultForTests, vaultAllSettled, vaultSettled };
4209
4390
  //# sourceMappingURL=sdux-vault-engine.mjs.map