@plyaz/core 1.7.0 → 1.7.1

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/dist/index.js CHANGED
@@ -7,10 +7,10 @@ require('@plyaz/types/globals');
7
7
  var logger$1 = require('@plyaz/logger');
8
8
  var errors$1 = require('@plyaz/types/errors');
9
9
  var frontend = require('@plyaz/api/frontend');
10
- var store = require('@plyaz/store');
11
10
  var events = require('events');
12
11
  var observability = require('@plyaz/types/observability');
13
12
  var middleware = require('@plyaz/errors/middleware');
13
+ var store = require('@plyaz/store');
14
14
  var core = require('@plyaz/types/core');
15
15
  var fs = require('fs');
16
16
  var path = require('path');
@@ -32735,404 +32735,6 @@ var require_cjs = __commonJS({
32735
32735
  }, "get") });
32736
32736
  }
32737
32737
  });
32738
- var BaseDomainService = class {
32739
- // ─────────────────────────────────────────────────────────────────────────
32740
- // Constructor
32741
- // ─────────────────────────────────────────────────────────────────────────
32742
- /**
32743
- * Create a new domain service instance
32744
- *
32745
- * @param config - Base service configuration (passed via super({...}))
32746
- */
32747
- // eslint-disable-next-line complexity
32748
- constructor(config) {
32749
- /** Initialization state */
32750
- this._initialized = false;
32751
- // API Client (optional)
32752
- this._apiClient = null;
32753
- this._clientInitPromise = null;
32754
- /**
32755
- * Cache prefix for namespacing cache keys (e.g., 'example', 'user', 'product').
32756
- * Subclasses can override by defining as a property.
32757
- * If not overridden, defaults to serviceName.toLowerCase().
32758
- *
32759
- * @example
32760
- * ```typescript
32761
- * protected cachePrefix = 'my-service'; // Override default
32762
- * ```
32763
- */
32764
- this.cachePrefix = "";
32765
- // Mapper and Validator instances (lazy)
32766
- this._mapperInstance = null;
32767
- this._validatorInstance = null;
32768
- this.serviceName = config.serviceName;
32769
- this.supportedRuntimes = config.supportedRuntimes;
32770
- this.config = config.serviceConfig;
32771
- this._setAsDefaultClient = config.setAsDefaultClient ?? false;
32772
- this.logger = new logger$1.PackageLogger({
32773
- packageName: "core",
32774
- service: this.serviceName
32775
- });
32776
- this._apiClientConfig = config.apiClientConfig;
32777
- this.cacheManager = config.injected?.cache;
32778
- this.dbService = config.injected?.db;
32779
- this.apiService = config.injected?.api;
32780
- this.observabilityService = config.serviceConfig.observabilityOverride ?? config.injected?.observability;
32781
- if (!this.cachePrefix) {
32782
- this.cachePrefix = this.serviceName.toLowerCase();
32783
- }
32784
- this._MapperClass = config.mapperClass;
32785
- this._ValidatorClass = config.validatorClass;
32786
- if (this._apiClientConfig) {
32787
- this.initializeApiClient();
32788
- }
32789
- this.logger.debug(`${this.serviceName} created`, {
32790
- supportedRuntimes: this.supportedRuntimes,
32791
- hasApiClient: !!this._apiClientConfig,
32792
- injected: {
32793
- cache: !!this.cacheManager,
32794
- db: !!this.dbService,
32795
- api: !!this.apiService,
32796
- observability: !!this.observabilityService
32797
- },
32798
- cachePrefix: this.cachePrefix,
32799
- hasMapper: !!this._MapperClass,
32800
- hasValidator: !!this._ValidatorClass
32801
- });
32802
- }
32803
- static {
32804
- __name(this, "BaseDomainService");
32805
- }
32806
- // ─────────────────────────────────────────────────────────────────────────
32807
- // Getters
32808
- // ─────────────────────────────────────────────────────────────────────────
32809
- /**
32810
- * Check if the service is enabled
32811
- * Service is enabled by default unless explicitly disabled
32812
- */
32813
- get isServiceEnabled() {
32814
- return this.config?.enabled !== false;
32815
- }
32816
- /**
32817
- * Check if the service is initialized (API client ready)
32818
- */
32819
- get isInitialized() {
32820
- return this._initialized;
32821
- }
32822
- /**
32823
- * Check if mapper is available
32824
- */
32825
- get hasMapper() {
32826
- return !!this._MapperClass;
32827
- }
32828
- /**
32829
- * Check if validator is available
32830
- */
32831
- get hasValidator() {
32832
- return !!this._ValidatorClass;
32833
- }
32834
- /**
32835
- * Get the mapper instance (lazy initialization)
32836
- * @throws CorePackageError if MapperClass was not provided
32837
- */
32838
- get mapper() {
32839
- if (!this._mapperInstance && this._MapperClass) {
32840
- this._mapperInstance = new this._MapperClass();
32841
- }
32842
- if (!this._mapperInstance) {
32843
- throw new errors.CorePackageError(
32844
- `${this.serviceName} does not have a Mapper configured`,
32845
- errors$1.ERROR_CODES.FEATURE_NOT_SUPPORTED
32846
- );
32847
- }
32848
- return this._mapperInstance;
32849
- }
32850
- /**
32851
- * Set the mapper instance
32852
- * Allows subclasses to override mapper initialization
32853
- */
32854
- set mapper(value) {
32855
- this._mapperInstance = value;
32856
- }
32857
- /**
32858
- * Get the validator instance (lazy initialization)
32859
- * @throws CorePackageError if ValidatorClass was not provided
32860
- */
32861
- get validator() {
32862
- if (!this._validatorInstance && this._ValidatorClass) {
32863
- this._validatorInstance = new this._ValidatorClass();
32864
- }
32865
- if (!this._validatorInstance) {
32866
- throw new errors.CorePackageError(
32867
- `${this.serviceName} does not have a Validator configured`,
32868
- errors$1.ERROR_CODES.FEATURE_NOT_SUPPORTED
32869
- );
32870
- }
32871
- return this._validatorInstance;
32872
- }
32873
- /**
32874
- * Set the validator instance
32875
- * Allows subclasses to override validator initialization
32876
- */
32877
- set validator(value) {
32878
- this._validatorInstance = value;
32879
- }
32880
- /**
32881
- * Get the API client (after initialization)
32882
- * @throws CorePackageError if API client was not configured or not initialized
32883
- */
32884
- get apiClient() {
32885
- if (!this._apiClient) {
32886
- throw new errors.CorePackageError(
32887
- `${this.serviceName} API client not initialized. Call ensureApiClientInitialized() first.`,
32888
- errors$1.ERROR_CODES.CLIENT_INITIALIZATION_FAILED
32889
- );
32890
- }
32891
- return this._apiClient;
32892
- }
32893
- /**
32894
- * Get the observability adapter (if injected)
32895
- * Returns undefined if observability is not enabled
32896
- */
32897
- get observability() {
32898
- return this.observabilityService;
32899
- }
32900
- /**
32901
- * Check if observability is available
32902
- */
32903
- get hasObservability() {
32904
- return !!this.observabilityService;
32905
- }
32906
- // ─────────────────────────────────────────────────────────────────────────
32907
- // API Client Initialization
32908
- // ─────────────────────────────────────────────────────────────────────────
32909
- /**
32910
- * Initialize API client asynchronously
32911
- * Called from constructor if apiClientConfig is provided
32912
- */
32913
- initializeApiClient() {
32914
- if (this._clientInitPromise || !this._apiClientConfig) {
32915
- return;
32916
- }
32917
- this._clientInitPromise = (async () => {
32918
- try {
32919
- this._apiClient = await frontend.createApiClient(this._apiClientConfig);
32920
- if (this._setAsDefaultClient) {
32921
- frontend.setDefaultApiClient(this._apiClient);
32922
- }
32923
- this._initialized = true;
32924
- this.logger.debug("API client initialized", {
32925
- service: this.serviceName,
32926
- baseURL: this._apiClientConfig.baseURL
32927
- });
32928
- } catch (error) {
32929
- this.logger.error("Failed to initialize API client", {
32930
- service: this.serviceName,
32931
- error: error instanceof Error ? error.message : String(error)
32932
- });
32933
- throw error;
32934
- }
32935
- })();
32936
- }
32937
- /**
32938
- * Ensure API client is initialized before use.
32939
- * Call this from methods that need the API client.
32940
- * Also called by ServiceRegistry.create() for services with async initialization.
32941
- */
32942
- async ensureApiClientInitialized() {
32943
- if (this._clientInitPromise) {
32944
- await this._clientInitPromise;
32945
- }
32946
- if (this._apiClientConfig && !this._apiClient) {
32947
- throw new errors.CorePackageError(
32948
- `${this.serviceName} API client not initialized`,
32949
- errors$1.ERROR_CODES.CLIENT_INITIALIZATION_FAILED
32950
- );
32951
- }
32952
- }
32953
- // ─────────────────────────────────────────────────────────────────────────
32954
- // Configuration Management
32955
- // ─────────────────────────────────────────────────────────────────────────
32956
- /**
32957
- * Get current configuration (immutable copy)
32958
- */
32959
- getConfig() {
32960
- return { ...this.config };
32961
- }
32962
- /**
32963
- * Get a default value
32964
- * @param key - The key to get the default for
32965
- */
32966
- getDefault(key) {
32967
- return this.config?.defaults?.[key];
32968
- }
32969
- /**
32970
- * Set a default value (mutates config.defaults)
32971
- * @param key - The key to set the default for
32972
- * @param value - The default value
32973
- */
32974
- setDefault(key, value) {
32975
- if (!this.config.defaults) {
32976
- this.config.defaults = {};
32977
- }
32978
- this.config.defaults[key] = value;
32979
- }
32980
- /**
32981
- * Set multiple default values
32982
- * @param defaults - Record of default values
32983
- */
32984
- setDefaults(defaults) {
32985
- if (!this.config.defaults) {
32986
- this.config.defaults = {};
32987
- }
32988
- Object.assign(this.config.defaults, defaults);
32989
- }
32990
- // ─────────────────────────────────────────────────────────────────────────
32991
- // Assertions
32992
- // ─────────────────────────────────────────────────────────────────────────
32993
- /**
32994
- * Assert that the service is available (configured and ready)
32995
- * @throws CorePackageError if not available
32996
- */
32997
- assertAvailable() {
32998
- if (!this.isAvailable()) {
32999
- throw new errors.CorePackageError(
33000
- `${this.serviceName} is not available. Check configuration.`,
33001
- errors$1.ERROR_CODES.CLIENT_INITIALIZATION_FAILED
33002
- );
33003
- }
33004
- }
33005
- /**
33006
- * Assert that the service is enabled
33007
- * @throws CorePackageError if disabled
33008
- */
33009
- assertEnabled() {
33010
- if (!this.isServiceEnabled) {
33011
- throw new errors.CorePackageError(
33012
- `${this.serviceName} is disabled. Set enabled: true in configuration.`,
33013
- errors$1.ERROR_CODES.FEATURE_NOT_SUPPORTED
33014
- );
33015
- }
33016
- }
33017
- /**
33018
- * Assert that the service is ready (enabled AND available)
33019
- * @throws CorePackageError if not ready
33020
- */
33021
- assertReady() {
33022
- this.assertEnabled();
33023
- this.assertAvailable();
33024
- }
33025
- // ─────────────────────────────────────────────────────────────────────────
33026
- // Helper Methods
33027
- // ─────────────────────────────────────────────────────────────────────────
33028
- /**
33029
- * Log debug message with service context
33030
- */
33031
- logDebug(message, data) {
33032
- this.logger.debug(message, {
33033
- service: this.serviceName,
33034
- ...data
33035
- });
33036
- }
33037
- /**
33038
- * Log info message with service context
33039
- */
33040
- logInfo(message, data) {
33041
- this.logger.info(message, {
33042
- service: this.serviceName,
33043
- ...data
33044
- });
33045
- }
33046
- /**
33047
- * Log warning message with service context
33048
- */
33049
- logWarn(message, data) {
33050
- this.logger.warn(message, {
33051
- service: this.serviceName,
33052
- ...data
33053
- });
33054
- }
33055
- /**
33056
- * Log error message with service context
33057
- */
33058
- logError(message, data) {
33059
- this.logger.error(message, {
33060
- service: this.serviceName,
33061
- ...data
33062
- });
33063
- }
33064
- // ─────────────────────────────────────────────────────────────────────────
33065
- // Observability Helpers
33066
- // ─────────────────────────────────────────────────────────────────────────
33067
- /**
33068
- * Record a metric (no-op if observability not available)
33069
- */
33070
- async recordMetric(name, value, tags) {
33071
- if (!this.observabilityService) return;
33072
- await this.observabilityService.recordHistogram(name, value, {
33073
- service: this.serviceName,
33074
- ...tags
33075
- });
33076
- }
33077
- /**
33078
- * Increment a counter (no-op if observability not available)
33079
- */
33080
- async incrementCounter(name, value = 1, tags) {
33081
- if (!this.observabilityService) return;
33082
- await this.observabilityService.incrementCounter(name, value, {
33083
- service: this.serviceName,
33084
- ...tags
33085
- });
33086
- }
33087
- /**
33088
- * Start a span for tracing (returns noop span if observability not available)
33089
- */
33090
- startSpan(name, attributes) {
33091
- if (!this.observabilityService) {
33092
- return {
33093
- context: { traceId: "", spanId: "" },
33094
- setAttribute: /* @__PURE__ */ __name(() => {
33095
- }, "setAttribute"),
33096
- setAttributes: /* @__PURE__ */ __name(() => {
33097
- }, "setAttributes"),
33098
- addEvent: /* @__PURE__ */ __name(() => {
33099
- }, "addEvent"),
33100
- setStatus: /* @__PURE__ */ __name(() => {
33101
- }, "setStatus"),
33102
- end: /* @__PURE__ */ __name(() => {
33103
- }, "end"),
33104
- recordException: /* @__PURE__ */ __name(() => {
33105
- }, "recordException")
33106
- };
33107
- }
33108
- return this.observabilityService.startSpan({
33109
- name,
33110
- attributes: {
33111
- "service.name": this.serviceName,
33112
- ...attributes
33113
- }
33114
- });
33115
- }
33116
- /**
33117
- * Execute a function within a traced span
33118
- */
33119
- async withSpan(name, fn, attributes) {
33120
- if (!this.observabilityService) {
33121
- const noopSpan = this.startSpan(name);
33122
- return fn(noopSpan);
33123
- }
33124
- return this.observabilityService.withSpan(
33125
- {
33126
- name,
33127
- attributes: {
33128
- "service.name": this.serviceName,
33129
- ...attributes
33130
- }
33131
- },
33132
- fn
33133
- );
33134
- }
33135
- };
33136
32738
  function hashString(str) {
33137
32739
  if (str.length === 0) return 0;
33138
32740
  let hash = config.FNV_CONSTANTS.FNV_32_OFFSET;
@@ -35710,6 +35312,13 @@ var Core = class _Core {
35710
35312
  _Core._logger = null;
35711
35313
  }
35712
35314
  }
35315
+ /**
35316
+ * Get the configured logger transport.
35317
+ * Used by services to create loggers with the same transport setting.
35318
+ */
35319
+ static get loggerTransport() {
35320
+ return _Core._loggerTransport;
35321
+ }
35713
35322
  /**
35714
35323
  * Log a message during initialization.
35715
35324
  * Uses PackageLogger, respects verbose flag.
@@ -36985,8 +36594,405 @@ var Core = class _Core {
36985
36594
  _Core.log("Feature flags initialized", verbose);
36986
36595
  }
36987
36596
  };
36988
-
36989
- // src/domain/base/BaseFrontendDomainService.ts
36597
+ var BaseDomainService = class {
36598
+ // ─────────────────────────────────────────────────────────────────────────
36599
+ // Constructor
36600
+ // ─────────────────────────────────────────────────────────────────────────
36601
+ /**
36602
+ * Create a new domain service instance
36603
+ *
36604
+ * @param config - Base service configuration (passed via super({...}))
36605
+ */
36606
+ // eslint-disable-next-line complexity
36607
+ constructor(config) {
36608
+ /** Initialization state */
36609
+ this._initialized = false;
36610
+ // API Client (optional)
36611
+ this._apiClient = null;
36612
+ this._clientInitPromise = null;
36613
+ /**
36614
+ * Cache prefix for namespacing cache keys (e.g., 'example', 'user', 'product').
36615
+ * Subclasses can override by defining as a property.
36616
+ * If not overridden, defaults to serviceName.toLowerCase().
36617
+ *
36618
+ * @example
36619
+ * ```typescript
36620
+ * protected cachePrefix = 'my-service'; // Override default
36621
+ * ```
36622
+ */
36623
+ this.cachePrefix = "";
36624
+ // Mapper and Validator instances (lazy)
36625
+ this._mapperInstance = null;
36626
+ this._validatorInstance = null;
36627
+ this.serviceName = config.serviceName;
36628
+ this.supportedRuntimes = config.supportedRuntimes;
36629
+ this.config = config.serviceConfig;
36630
+ this._setAsDefaultClient = config.setAsDefaultClient ?? false;
36631
+ this.logger = new logger$1.PackageLogger({
36632
+ packageName: "core",
36633
+ service: this.serviceName,
36634
+ transport: Core.loggerTransport
36635
+ });
36636
+ this._apiClientConfig = config.apiClientConfig;
36637
+ this.cacheManager = config.injected?.cache;
36638
+ this.dbService = config.injected?.db;
36639
+ this.apiService = config.injected?.api;
36640
+ this.observabilityService = config.serviceConfig.observabilityOverride ?? config.injected?.observability;
36641
+ if (!this.cachePrefix) {
36642
+ this.cachePrefix = this.serviceName.toLowerCase();
36643
+ }
36644
+ this._MapperClass = config.mapperClass;
36645
+ this._ValidatorClass = config.validatorClass;
36646
+ if (this._apiClientConfig) {
36647
+ this.initializeApiClient();
36648
+ }
36649
+ this.logger.debug(`${this.serviceName} created`, {
36650
+ supportedRuntimes: this.supportedRuntimes,
36651
+ hasApiClient: !!this._apiClientConfig,
36652
+ injected: {
36653
+ cache: !!this.cacheManager,
36654
+ db: !!this.dbService,
36655
+ api: !!this.apiService,
36656
+ observability: !!this.observabilityService
36657
+ },
36658
+ cachePrefix: this.cachePrefix,
36659
+ hasMapper: !!this._MapperClass,
36660
+ hasValidator: !!this._ValidatorClass
36661
+ });
36662
+ }
36663
+ static {
36664
+ __name(this, "BaseDomainService");
36665
+ }
36666
+ // ─────────────────────────────────────────────────────────────────────────
36667
+ // Getters
36668
+ // ─────────────────────────────────────────────────────────────────────────
36669
+ /**
36670
+ * Check if the service is enabled
36671
+ * Service is enabled by default unless explicitly disabled
36672
+ */
36673
+ get isServiceEnabled() {
36674
+ return this.config?.enabled !== false;
36675
+ }
36676
+ /**
36677
+ * Check if the service is initialized (API client ready)
36678
+ */
36679
+ get isInitialized() {
36680
+ return this._initialized;
36681
+ }
36682
+ /**
36683
+ * Check if mapper is available
36684
+ */
36685
+ get hasMapper() {
36686
+ return !!this._MapperClass;
36687
+ }
36688
+ /**
36689
+ * Check if validator is available
36690
+ */
36691
+ get hasValidator() {
36692
+ return !!this._ValidatorClass;
36693
+ }
36694
+ /**
36695
+ * Get the mapper instance (lazy initialization)
36696
+ * @throws CorePackageError if MapperClass was not provided
36697
+ */
36698
+ get mapper() {
36699
+ if (!this._mapperInstance && this._MapperClass) {
36700
+ this._mapperInstance = new this._MapperClass();
36701
+ }
36702
+ if (!this._mapperInstance) {
36703
+ throw new errors.CorePackageError(
36704
+ `${this.serviceName} does not have a Mapper configured`,
36705
+ errors$1.ERROR_CODES.FEATURE_NOT_SUPPORTED
36706
+ );
36707
+ }
36708
+ return this._mapperInstance;
36709
+ }
36710
+ /**
36711
+ * Set the mapper instance
36712
+ * Allows subclasses to override mapper initialization
36713
+ */
36714
+ set mapper(value) {
36715
+ this._mapperInstance = value;
36716
+ }
36717
+ /**
36718
+ * Get the validator instance (lazy initialization)
36719
+ * @throws CorePackageError if ValidatorClass was not provided
36720
+ */
36721
+ get validator() {
36722
+ if (!this._validatorInstance && this._ValidatorClass) {
36723
+ this._validatorInstance = new this._ValidatorClass();
36724
+ }
36725
+ if (!this._validatorInstance) {
36726
+ throw new errors.CorePackageError(
36727
+ `${this.serviceName} does not have a Validator configured`,
36728
+ errors$1.ERROR_CODES.FEATURE_NOT_SUPPORTED
36729
+ );
36730
+ }
36731
+ return this._validatorInstance;
36732
+ }
36733
+ /**
36734
+ * Set the validator instance
36735
+ * Allows subclasses to override validator initialization
36736
+ */
36737
+ set validator(value) {
36738
+ this._validatorInstance = value;
36739
+ }
36740
+ /**
36741
+ * Get the API client (after initialization)
36742
+ * @throws CorePackageError if API client was not configured or not initialized
36743
+ */
36744
+ get apiClient() {
36745
+ if (!this._apiClient) {
36746
+ throw new errors.CorePackageError(
36747
+ `${this.serviceName} API client not initialized. Call ensureApiClientInitialized() first.`,
36748
+ errors$1.ERROR_CODES.CLIENT_INITIALIZATION_FAILED
36749
+ );
36750
+ }
36751
+ return this._apiClient;
36752
+ }
36753
+ /**
36754
+ * Get the observability adapter (if injected)
36755
+ * Returns undefined if observability is not enabled
36756
+ */
36757
+ get observability() {
36758
+ return this.observabilityService;
36759
+ }
36760
+ /**
36761
+ * Check if observability is available
36762
+ */
36763
+ get hasObservability() {
36764
+ return !!this.observabilityService;
36765
+ }
36766
+ // ─────────────────────────────────────────────────────────────────────────
36767
+ // API Client Initialization
36768
+ // ─────────────────────────────────────────────────────────────────────────
36769
+ /**
36770
+ * Initialize API client asynchronously
36771
+ * Called from constructor if apiClientConfig is provided
36772
+ */
36773
+ initializeApiClient() {
36774
+ if (this._clientInitPromise || !this._apiClientConfig) {
36775
+ return;
36776
+ }
36777
+ this._clientInitPromise = (async () => {
36778
+ try {
36779
+ this._apiClient = await frontend.createApiClient(this._apiClientConfig);
36780
+ if (this._setAsDefaultClient) {
36781
+ frontend.setDefaultApiClient(this._apiClient);
36782
+ }
36783
+ this._initialized = true;
36784
+ this.logger.debug("API client initialized", {
36785
+ service: this.serviceName,
36786
+ baseURL: this._apiClientConfig.baseURL
36787
+ });
36788
+ } catch (error) {
36789
+ this.logger.error("Failed to initialize API client", {
36790
+ service: this.serviceName,
36791
+ error: error instanceof Error ? error.message : String(error)
36792
+ });
36793
+ throw error;
36794
+ }
36795
+ })();
36796
+ }
36797
+ /**
36798
+ * Ensure API client is initialized before use.
36799
+ * Call this from methods that need the API client.
36800
+ * Also called by ServiceRegistry.create() for services with async initialization.
36801
+ */
36802
+ async ensureApiClientInitialized() {
36803
+ if (this._clientInitPromise) {
36804
+ await this._clientInitPromise;
36805
+ }
36806
+ if (this._apiClientConfig && !this._apiClient) {
36807
+ throw new errors.CorePackageError(
36808
+ `${this.serviceName} API client not initialized`,
36809
+ errors$1.ERROR_CODES.CLIENT_INITIALIZATION_FAILED
36810
+ );
36811
+ }
36812
+ }
36813
+ // ─────────────────────────────────────────────────────────────────────────
36814
+ // Configuration Management
36815
+ // ─────────────────────────────────────────────────────────────────────────
36816
+ /**
36817
+ * Get current configuration (immutable copy)
36818
+ */
36819
+ getConfig() {
36820
+ return { ...this.config };
36821
+ }
36822
+ /**
36823
+ * Get a default value
36824
+ * @param key - The key to get the default for
36825
+ */
36826
+ getDefault(key) {
36827
+ return this.config?.defaults?.[key];
36828
+ }
36829
+ /**
36830
+ * Set a default value (mutates config.defaults)
36831
+ * @param key - The key to set the default for
36832
+ * @param value - The default value
36833
+ */
36834
+ setDefault(key, value) {
36835
+ if (!this.config.defaults) {
36836
+ this.config.defaults = {};
36837
+ }
36838
+ this.config.defaults[key] = value;
36839
+ }
36840
+ /**
36841
+ * Set multiple default values
36842
+ * @param defaults - Record of default values
36843
+ */
36844
+ setDefaults(defaults) {
36845
+ if (!this.config.defaults) {
36846
+ this.config.defaults = {};
36847
+ }
36848
+ Object.assign(this.config.defaults, defaults);
36849
+ }
36850
+ // ─────────────────────────────────────────────────────────────────────────
36851
+ // Assertions
36852
+ // ─────────────────────────────────────────────────────────────────────────
36853
+ /**
36854
+ * Assert that the service is available (configured and ready)
36855
+ * @throws CorePackageError if not available
36856
+ */
36857
+ assertAvailable() {
36858
+ if (!this.isAvailable()) {
36859
+ throw new errors.CorePackageError(
36860
+ `${this.serviceName} is not available. Check configuration.`,
36861
+ errors$1.ERROR_CODES.CLIENT_INITIALIZATION_FAILED
36862
+ );
36863
+ }
36864
+ }
36865
+ /**
36866
+ * Assert that the service is enabled
36867
+ * @throws CorePackageError if disabled
36868
+ */
36869
+ assertEnabled() {
36870
+ if (!this.isServiceEnabled) {
36871
+ throw new errors.CorePackageError(
36872
+ `${this.serviceName} is disabled. Set enabled: true in configuration.`,
36873
+ errors$1.ERROR_CODES.FEATURE_NOT_SUPPORTED
36874
+ );
36875
+ }
36876
+ }
36877
+ /**
36878
+ * Assert that the service is ready (enabled AND available)
36879
+ * @throws CorePackageError if not ready
36880
+ */
36881
+ assertReady() {
36882
+ this.assertEnabled();
36883
+ this.assertAvailable();
36884
+ }
36885
+ // ─────────────────────────────────────────────────────────────────────────
36886
+ // Helper Methods
36887
+ // ─────────────────────────────────────────────────────────────────────────
36888
+ /**
36889
+ * Log debug message with service context
36890
+ */
36891
+ logDebug(message, data) {
36892
+ this.logger.debug(message, {
36893
+ service: this.serviceName,
36894
+ ...data
36895
+ });
36896
+ }
36897
+ /**
36898
+ * Log info message with service context
36899
+ */
36900
+ logInfo(message, data) {
36901
+ this.logger.info(message, {
36902
+ service: this.serviceName,
36903
+ ...data
36904
+ });
36905
+ }
36906
+ /**
36907
+ * Log warning message with service context
36908
+ */
36909
+ logWarn(message, data) {
36910
+ this.logger.warn(message, {
36911
+ service: this.serviceName,
36912
+ ...data
36913
+ });
36914
+ }
36915
+ /**
36916
+ * Log error message with service context
36917
+ */
36918
+ logError(message, data) {
36919
+ this.logger.error(message, {
36920
+ service: this.serviceName,
36921
+ ...data
36922
+ });
36923
+ }
36924
+ // ─────────────────────────────────────────────────────────────────────────
36925
+ // Observability Helpers
36926
+ // ─────────────────────────────────────────────────────────────────────────
36927
+ /**
36928
+ * Record a metric (no-op if observability not available)
36929
+ */
36930
+ async recordMetric(name, value, tags) {
36931
+ if (!this.observabilityService) return;
36932
+ await this.observabilityService.recordHistogram(name, value, {
36933
+ service: this.serviceName,
36934
+ ...tags
36935
+ });
36936
+ }
36937
+ /**
36938
+ * Increment a counter (no-op if observability not available)
36939
+ */
36940
+ async incrementCounter(name, value = 1, tags) {
36941
+ if (!this.observabilityService) return;
36942
+ await this.observabilityService.incrementCounter(name, value, {
36943
+ service: this.serviceName,
36944
+ ...tags
36945
+ });
36946
+ }
36947
+ /**
36948
+ * Start a span for tracing (returns noop span if observability not available)
36949
+ */
36950
+ startSpan(name, attributes) {
36951
+ if (!this.observabilityService) {
36952
+ return {
36953
+ context: { traceId: "", spanId: "" },
36954
+ setAttribute: /* @__PURE__ */ __name(() => {
36955
+ }, "setAttribute"),
36956
+ setAttributes: /* @__PURE__ */ __name(() => {
36957
+ }, "setAttributes"),
36958
+ addEvent: /* @__PURE__ */ __name(() => {
36959
+ }, "addEvent"),
36960
+ setStatus: /* @__PURE__ */ __name(() => {
36961
+ }, "setStatus"),
36962
+ end: /* @__PURE__ */ __name(() => {
36963
+ }, "end"),
36964
+ recordException: /* @__PURE__ */ __name(() => {
36965
+ }, "recordException")
36966
+ };
36967
+ }
36968
+ return this.observabilityService.startSpan({
36969
+ name,
36970
+ attributes: {
36971
+ "service.name": this.serviceName,
36972
+ ...attributes
36973
+ }
36974
+ });
36975
+ }
36976
+ /**
36977
+ * Execute a function within a traced span
36978
+ */
36979
+ async withSpan(name, fn, attributes) {
36980
+ if (!this.observabilityService) {
36981
+ const noopSpan = this.startSpan(name);
36982
+ return fn(noopSpan);
36983
+ }
36984
+ return this.observabilityService.withSpan(
36985
+ {
36986
+ name,
36987
+ attributes: {
36988
+ "service.name": this.serviceName,
36989
+ ...attributes
36990
+ }
36991
+ },
36992
+ fn
36993
+ );
36994
+ }
36995
+ };
36990
36996
  var BaseFrontendDomainService = class extends BaseDomainService {
36991
36997
  // ─────────────────────────────────────────────────────────────────────────
36992
36998
  // Constructor
@@ -37406,6 +37412,94 @@ var BaseFrontendDomainService = class extends BaseDomainService {
37406
37412
  }
37407
37413
  return result2;
37408
37414
  }
37415
+ /**
37416
+ * Check if a response indicates success.
37417
+ *
37418
+ * Uses `responseSuccessKey` config if set, otherwise auto-detects from:
37419
+ * - `isSuccess` (internal format)
37420
+ * - `success` (alternative internal format)
37421
+ * - `ok` (fetchff/fetch standard)
37422
+ * - HTTP status code 200-299
37423
+ *
37424
+ * Supports nested keys via dot notation (e.g., `'meta.success'`).
37425
+ *
37426
+ * @param response - Response object from fetcher
37427
+ * @returns true if response indicates success
37428
+ */
37429
+ // eslint-disable-next-line complexity
37430
+ isResponseSuccess(response) {
37431
+ if (!response || typeof response !== "object") {
37432
+ return false;
37433
+ }
37434
+ const resp = response;
37435
+ const successKey = this.config.responseSuccessKey;
37436
+ if (successKey) {
37437
+ const keys = successKey.split(".");
37438
+ let result2 = resp;
37439
+ for (const k of keys) {
37440
+ if (result2 && typeof result2 === "object" && k in result2) {
37441
+ result2 = result2[k];
37442
+ } else {
37443
+ break;
37444
+ }
37445
+ }
37446
+ if (typeof result2 === "boolean") {
37447
+ return result2;
37448
+ }
37449
+ }
37450
+ if ("isSuccess" in resp && typeof resp.isSuccess === "boolean") {
37451
+ return resp.isSuccess;
37452
+ }
37453
+ if ("success" in resp && typeof resp.success === "boolean") {
37454
+ return resp.success;
37455
+ }
37456
+ if ("ok" in resp && typeof resp.ok === "boolean") {
37457
+ return resp.ok;
37458
+ }
37459
+ if ("status" in resp && typeof resp.status === "number") {
37460
+ return resp.status >= types.HTTP_STATUS.OK && resp.status < types.HTTP_STATUS.MULTIPLE_CHOICES;
37461
+ }
37462
+ return "data" in resp && !("error" in resp && resp.error);
37463
+ }
37464
+ /**
37465
+ * Extract error from API response.
37466
+ *
37467
+ * Uses `responseErrorKey` config if set, otherwise auto-detects from:
37468
+ * - `error` (common format)
37469
+ * - `errors` (array format, e.g., GraphQL)
37470
+ *
37471
+ * Supports nested keys via dot notation (e.g., `'meta.error'`).
37472
+ *
37473
+ * @param response - Response object from fetcher
37474
+ * @returns Extracted error or undefined if not found
37475
+ */
37476
+ // eslint-disable-next-line complexity
37477
+ extractResponseError(response) {
37478
+ if (!response || typeof response !== "object") {
37479
+ return void 0;
37480
+ }
37481
+ const resp = response;
37482
+ const errorKey = this.config.responseErrorKey;
37483
+ if (errorKey) {
37484
+ const keys = errorKey.split(".");
37485
+ let result2 = resp;
37486
+ for (const k of keys) {
37487
+ if (result2 && typeof result2 === "object" && k in result2) {
37488
+ result2 = result2[k];
37489
+ } else {
37490
+ return void 0;
37491
+ }
37492
+ }
37493
+ return result2;
37494
+ }
37495
+ if ("error" in resp && resp.error) {
37496
+ return resp.error;
37497
+ }
37498
+ if ("errors" in resp && resp.errors) {
37499
+ return resp.errors;
37500
+ }
37501
+ return void 0;
37502
+ }
37409
37503
  // ─────────────────────────────────────────────────────────────────────────
37410
37504
  // Generic CRUD Operations
37411
37505
  // ─────────────────────────────────────────────────────────────────────────
@@ -37461,13 +37555,15 @@ var BaseFrontendDomainService = class extends BaseDomainService {
37461
37555
  try {
37462
37556
  await this.beforeFetchAll?.(query);
37463
37557
  const response = await this.config.fetchers.fetchAll(query, options);
37464
- if (!response.isSuccess) {
37558
+ if (!this.isResponseSuccess(response)) {
37559
+ const extractedError = this.extractResponseError(response);
37465
37560
  throw new errors.CorePackageError(
37466
37561
  "Failed to fetch entities",
37467
37562
  errors$1.ERROR_CODES.CONTEXT_OPERATION_FAILED,
37468
37563
  {
37469
37564
  context: { operation: "fetchAll", query },
37470
- cause: response.error ?? void 0
37565
+ cause: extractedError instanceof Error ? extractedError : void 0,
37566
+ details: extractedError
37471
37567
  }
37472
37568
  );
37473
37569
  }
@@ -37515,13 +37611,16 @@ var BaseFrontendDomainService = class extends BaseDomainService {
37515
37611
  try {
37516
37612
  await this.beforeFetchById?.(id);
37517
37613
  const response = await this.config.fetchers.fetchById(String(id), options);
37518
- if (!response.isSuccess) {
37519
- if (response.error?.message?.includes("404")) {
37614
+ if (!this.isResponseSuccess(response)) {
37615
+ const extractedError = this.extractResponseError(response);
37616
+ const errorMessage = extractedError instanceof Error ? extractedError.message : String(extractedError ?? "");
37617
+ if (errorMessage.includes("404") || response.status === types.HTTP_STATUS.NOT_FOUND) {
37520
37618
  return null;
37521
37619
  }
37522
37620
  throw new errors.CorePackageError("Failed to fetch entity", errors$1.ERROR_CODES.CONTEXT_OPERATION_FAILED, {
37523
37621
  context: { operation: "fetchById", entityId: String(id) },
37524
- cause: response.error ?? void 0
37622
+ cause: extractedError instanceof Error ? extractedError : void 0,
37623
+ details: extractedError
37525
37624
  });
37526
37625
  }
37527
37626
  const rawData = this.unwrapResponseData(response.data);
@@ -37597,15 +37696,14 @@ var BaseFrontendDomainService = class extends BaseDomainService {
37597
37696
  });
37598
37697
  }
37599
37698
  const response = await this.config.fetchers.create(createDTO, options);
37600
- if (!response.isSuccess) {
37601
- throw new errors.CorePackageError(
37602
- response.error?.message ?? "Failed to create entity",
37603
- errors$1.ERROR_CODES.CONTEXT_OPERATION_FAILED,
37604
- {
37605
- context: { operation: "create" },
37606
- cause: response.error ?? void 0
37607
- }
37608
- );
37699
+ if (!this.isResponseSuccess(response)) {
37700
+ const extractedError = this.extractResponseError(response);
37701
+ const errorMessage = extractedError instanceof Error ? extractedError.message : typeof extractedError === "string" ? extractedError : "Failed to create entity";
37702
+ throw new errors.CorePackageError(errorMessage, errors$1.ERROR_CODES.CONTEXT_OPERATION_FAILED, {
37703
+ context: { operation: "create" },
37704
+ cause: extractedError instanceof Error ? extractedError : void 0,
37705
+ details: extractedError
37706
+ });
37609
37707
  }
37610
37708
  const rawData = this.unwrapResponseData(response.data);
37611
37709
  const entity = this.mapper.toDomain(rawData);
@@ -37691,15 +37789,14 @@ var BaseFrontendDomainService = class extends BaseDomainService {
37691
37789
  });
37692
37790
  }
37693
37791
  const response = await this.config.fetchers.update({ id, data: patchDTO }, options);
37694
- if (!response.isSuccess) {
37695
- throw new errors.CorePackageError(
37696
- response.error?.message ?? "Failed to update entity",
37697
- errors$1.ERROR_CODES.CONTEXT_OPERATION_FAILED,
37698
- {
37699
- context: { operation: "update", entityId: id },
37700
- cause: response.error ?? void 0
37701
- }
37702
- );
37792
+ if (!this.isResponseSuccess(response)) {
37793
+ const extractedError = this.extractResponseError(response);
37794
+ const errorMessage = extractedError instanceof Error ? extractedError.message : typeof extractedError === "string" ? extractedError : "Failed to update entity";
37795
+ throw new errors.CorePackageError(errorMessage, errors$1.ERROR_CODES.CONTEXT_OPERATION_FAILED, {
37796
+ context: { operation: "update", entityId: id },
37797
+ cause: extractedError instanceof Error ? extractedError : void 0,
37798
+ details: extractedError
37799
+ });
37703
37800
  }
37704
37801
  const rawData = this.unwrapResponseData(response.data);
37705
37802
  const serverEntity = this.mapper.toDomain(rawData);
@@ -37771,15 +37868,14 @@ var BaseFrontendDomainService = class extends BaseDomainService {
37771
37868
  });
37772
37869
  }
37773
37870
  const response = await this.config.fetchers.delete(String(id), options);
37774
- if (!response.isSuccess) {
37775
- throw new errors.CorePackageError(
37776
- response.error?.message ?? "Failed to delete entity",
37777
- errors$1.ERROR_CODES.CONTEXT_OPERATION_FAILED,
37778
- {
37779
- context: { operation: "delete", entityId: String(id) },
37780
- cause: response.error ?? void 0
37781
- }
37782
- );
37871
+ if (!this.isResponseSuccess(response)) {
37872
+ const extractedError = this.extractResponseError(response);
37873
+ const errorMessage = extractedError instanceof Error ? extractedError.message : typeof extractedError === "string" ? extractedError : "Failed to delete entity";
37874
+ throw new errors.CorePackageError(errorMessage, errors$1.ERROR_CODES.CONTEXT_OPERATION_FAILED, {
37875
+ context: { operation: "delete", entityId: String(id) },
37876
+ cause: extractedError instanceof Error ? extractedError : void 0,
37877
+ details: extractedError
37878
+ });
37783
37879
  }
37784
37880
  if (!isOptimistic) {
37785
37881
  this.removeEntityFromStore(String(id));
@@ -44672,6 +44768,8 @@ var ExampleMapperClass = class extends BaseMapper {
44672
44768
  * API DTO → Domain Model
44673
44769
  */
44674
44770
  toDomain(dto) {
44771
+ const createdAt = dto.created_at ? new Date(dto.created_at) : /* @__PURE__ */ new Date();
44772
+ const updatedAt = dto.updated_at ? new Date(dto.updated_at) : /* @__PURE__ */ new Date();
44675
44773
  return {
44676
44774
  id: dto.id,
44677
44775
  name: dto.name,
@@ -44680,8 +44778,8 @@ var ExampleMapperClass = class extends BaseMapper {
44680
44778
  status: dto.status,
44681
44779
  amount: dto.amount,
44682
44780
  isVisible: dto.is_visible,
44683
- createdAt: new Date(dto.created_at),
44684
- updatedAt: new Date(dto.updated_at),
44781
+ createdAt: isNaN(createdAt.getTime()) ? /* @__PURE__ */ new Date() : createdAt,
44782
+ updatedAt: isNaN(updatedAt.getTime()) ? /* @__PURE__ */ new Date() : updatedAt,
44685
44783
  // Computed properties
44686
44784
  isActive: dto.status === "active",
44687
44785
  hasDescription: /* @__PURE__ */ __name(() => Boolean(dto.description), "hasDescription")
@@ -45115,6 +45213,7 @@ var FrontendExampleDomainService = class _FrontendExampleDomainService extends B
45115
45213
  // ─────────────────────────────────────────────────────────────────────────
45116
45214
  // Constructor
45117
45215
  // ─────────────────────────────────────────────────────────────────────────
45216
+ // eslint-disable-next-line complexity
45118
45217
  constructor(config = {}, options) {
45119
45218
  const apiBasePath = config.apiBasePath ?? "/api/examples";
45120
45219
  super({
@@ -45128,8 +45227,12 @@ var FrontendExampleDomainService = class _FrontendExampleDomainService extends B
45128
45227
  autoFetch: false,
45129
45228
  pollingInterval: 0,
45130
45229
  ...config,
45131
- // Unwrap SuccessResponseStandard: { success, message, data, codeStatus }
45132
- // Base class will extract 'data' property automatically
45230
+ // Fetchff response format: { ok, status, data: { success, message, data, codeStatus }, error }
45231
+ // Use 'ok' for success check (HTTP level)
45232
+ responseSuccessKey: "ok",
45233
+ // Extract error from fetchff response
45234
+ responseErrorKey: "error",
45235
+ // Unwrap SuccessResponseStandard: extract 'data' from response.data
45133
45236
  responseDataKey: "data",
45134
45237
  // Fetchers - using apiClient directly for testing/example purposes
45135
45238
  // In production, these would be imported from @plyaz/api services