@qlover/fe-corekit 3.1.1 → 3.2.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
@@ -2809,6 +2809,9 @@ var RequestAdapterFetch = class {
2809
2809
  if (!baseURL) {
2810
2810
  return url;
2811
2811
  }
2812
+ if (url.startsWith(baseURL)) {
2813
+ return url;
2814
+ }
2812
2815
  if (baseURL.endsWith("/") && url.startsWith("/")) {
2813
2816
  return baseURL.slice(0, -1) + url;
2814
2817
  }
@@ -3755,12 +3758,18 @@ var SimpleUrlBuilder = class {
3755
3758
  basePath = "/" + basePath;
3756
3759
  }
3757
3760
  let combinedPath = basePath;
3758
- if (!combinedPath.endsWith("/") && !url.startsWith("/")) {
3759
- combinedPath += "/";
3761
+ if (!combinedPath.endsWith("/") || !url.startsWith("/")) {
3762
+ if (!combinedPath.endsWith("/")) {
3763
+ combinedPath += "/";
3764
+ }
3760
3765
  }
3761
3766
  combinedPath += url.replace(/^\//, "");
3762
3767
  urlObject = new URL(combinedPath, "http://temp");
3763
3768
  shouldReturnPathOnly = true;
3769
+ } else if (base && base === "api") {
3770
+ const combinedPath = "/api/" + url.replace(/^\//, "");
3771
+ urlObject = new URL(combinedPath, "http://temp");
3772
+ shouldReturnPathOnly = true;
3764
3773
  } else if (base) {
3765
3774
  urlObject = new URL(url, "http://temp");
3766
3775
  shouldReturnPathOnly = true;
@@ -3966,6 +3975,9 @@ var RequestPlugin = class {
3966
3975
  onBefore(ctx) {
3967
3976
  ctx.setParameters(this.mergeConfig(ctx.parameters));
3968
3977
  }
3978
+ startsWith(url, baseUrl) {
3979
+ return url.startsWith(baseUrl);
3980
+ }
3969
3981
  /**
3970
3982
  * Main request handler
3971
3983
  *
@@ -3979,6 +3991,9 @@ var RequestPlugin = class {
3979
3991
  const mergedConfig = this.createConfig(config);
3980
3992
  const processedData = this.processRequestData(mergedConfig);
3981
3993
  const builtUrl = this.buildUrl(mergedConfig);
3994
+ if (builtUrl && mergedConfig.baseURL && this.startsWith(builtUrl, mergedConfig.baseURL)) {
3995
+ delete mergedConfig.baseURL;
3996
+ }
3982
3997
  const injectedHeaders = this.injectHeaders(mergedConfig);
3983
3998
  return Object.assign(mergedConfig, {
3984
3999
  data: processedData,
@@ -4839,71 +4854,45 @@ var Base64Serializer = class {
4839
4854
 
4840
4855
  // src/storage/impl/KeyStorage.ts
4841
4856
  var KeyStorage = class {
4842
- constructor(key, options = {}) {
4857
+ constructor(key, storage) {
4843
4858
  this.key = key;
4844
- this.options = options;
4845
- try {
4846
- const localValue = options.storage?.getItem(key);
4847
- this.value = localValue ?? null;
4848
- } catch {
4849
- this.value = null;
4850
- }
4859
+ this.storage = storage;
4851
4860
  }
4852
4861
  value;
4853
- mergeOptions(options) {
4854
- return {
4855
- ...this.options,
4856
- ...options
4857
- };
4858
- }
4859
- /**
4860
- * @override
4861
- */
4862
- getKey() {
4863
- return this.key;
4864
- }
4865
- /**
4866
- * @override
4867
- */
4868
- getValue() {
4869
- return this.value;
4870
- }
4871
4862
  /**
4872
4863
  * @override
4873
4864
  */
4874
4865
  get(options) {
4875
- const { storage, ...reset } = this.mergeOptions(options);
4876
- if (this.value != null) {
4877
- return this.value;
4866
+ if (!this.storage) {
4867
+ return this.value ?? null;
4878
4868
  }
4879
- if (storage) {
4880
- const val = storage.getItem(this.key, void 0, reset);
4881
- if (val == null) {
4882
- this.remove();
4883
- return null;
4884
- }
4885
- this.value = val;
4886
- return val;
4869
+ const val = this.storage.getItem(this.key, options);
4870
+ if (val == null) {
4871
+ this.remove();
4872
+ return null;
4887
4873
  }
4888
- return this.value;
4874
+ this.value = val;
4875
+ return val;
4889
4876
  }
4890
4877
  /**
4891
4878
  * @override
4892
4879
  */
4893
- set(token, options) {
4894
- const { storage, ...reset } = this.mergeOptions(options);
4895
- this.value = token;
4896
- if (storage) {
4897
- storage.setItem(this.key, token, reset);
4880
+ set(value, options) {
4881
+ if (!this.storage) {
4882
+ this.value = value;
4883
+ return;
4898
4884
  }
4885
+ this.storage.setItem(this.key, value, options);
4899
4886
  }
4900
4887
  /**
4901
4888
  * @override
4902
4889
  */
4903
4890
  remove(options) {
4904
- const { storage, ...reset } = this.mergeOptions(options);
4905
- this.value = null;
4906
- storage?.removeItem(this.key, reset);
4891
+ if (!this.storage) {
4892
+ this.value = null;
4893
+ return;
4894
+ }
4895
+ this.storage.removeItem(this.key, options);
4907
4896
  }
4908
4897
  };
4909
4898
 
@@ -4938,7 +4927,6 @@ var ObjectStorage = class {
4938
4927
  /**
4939
4928
  * Gets the number of items stored in the memory cache
4940
4929
  *
4941
- * @override
4942
4930
  * @returns The number of stored items in memory
4943
4931
  *
4944
4932
  * @example
@@ -5016,12 +5004,9 @@ var ObjectStorage = class {
5016
5004
  if (!storeValue) {
5017
5005
  return _dv;
5018
5006
  }
5019
- const value = this.serializer ? this.serializer.deserialize(storeValue, _dv) : storeValue;
5007
+ const value = typeof storeValue === "string" && this.serializer ? this.serializer.deserialize(storeValue, _dv) : storeValue;
5020
5008
  return this.getRawValue(value, _dv);
5021
5009
  }
5022
- /**
5023
- * @override
5024
- */
5025
5010
  getRawValue(value, defaultValue) {
5026
5011
  if (this.isStorageValue(value)) {
5027
5012
  if (this.isExpired(value)) {
@@ -5115,268 +5100,146 @@ var ObjectStorage = class {
5115
5100
  isStorageValue(value) {
5116
5101
  return typeof value === "object" && value !== null && "key" in value && "value" in value;
5117
5102
  }
5118
- /**
5119
- * Gets the serializer instance
5120
- *
5121
- * Significance: Provides access to the serialization logic
5122
- * Core idea: Expose serializer for advanced use cases
5123
- * Main function: Return the serializer instance
5124
- * Main purpose: Enable direct access to serialization when needed
5125
- *
5126
- * @returns The serializer instance
5127
- *
5128
- * @example
5129
- * ```typescript
5130
- * const serializer = storage.getSerializer();
5131
- * if (serializer) {
5132
- * // Direct access to serializer
5133
- * }
5134
- * ```
5135
- */
5136
- getSerializer() {
5137
- return this.serializer;
5138
- }
5139
5103
  };
5140
5104
 
5141
- // src/storage/impl/SyncStorage.ts
5142
- function toPipeValue(pipe) {
5143
- if ("type" in pipe && "pipe" in pipe) {
5144
- return pipe;
5145
- }
5146
- if ("serialize" in pipe && "deserialize" in pipe) {
5147
- return { pipe, type: "serialize" };
5148
- }
5149
- if ("encrypt" in pipe && "decrypt" in pipe) {
5150
- return { pipe, type: "encrypt" };
5105
+ // src/storage/utils/isStorage.ts
5106
+ var storageMethods = ["setItem", "getItem", "removeItem", "clear"];
5107
+ function isStorage(storage) {
5108
+ if (storage == null || typeof storage !== "object") {
5109
+ return false;
5151
5110
  }
5152
- if ("setItem" in pipe && "getItem" in pipe && "removeItem" in pipe && "clear" in pipe) {
5153
- return { pipe, type: "storage" };
5111
+ for (const method of storageMethods) {
5112
+ if (storage[method] == null || typeof storage[method] !== "function") {
5113
+ return false;
5114
+ }
5154
5115
  }
5155
- return null;
5116
+ return true;
5156
5117
  }
5157
- var operationMaps = {
5158
- setItem: {
5159
- serialize: (pipe, args) => pipe.serialize(...args),
5160
- encrypt: (pipe, args) => pipe.encrypt(...args),
5161
- storage: (pipe, args) => pipe.setItem(...args)
5162
- },
5163
- getItem: {
5164
- serialize: (pipe, args) => pipe.deserialize(...args),
5165
- encrypt: (pipe, args) => pipe.decrypt(...args),
5166
- storage: (pipe, args) => pipe.getItem(...args)
5167
- }
5168
- };
5169
- var SyncStorage = class {
5170
- /**
5171
- * Creates a new SyncStorage instance with pipeline support
5172
- *
5173
- * @param storage - Primary storage backend (e.g., localStorage, sessionStorage)
5174
- * @param pipes - Optional pipe or array of pipes for data transformation
5175
- *
5176
- * @example Single pipe
5177
- * ```typescript
5178
- * const storage = new SyncStorage(
5179
- * localStorage,
5180
- * new JSONSerializer()
5181
- * );
5182
- * ```
5183
- *
5184
- * @example Multiple pipes
5185
- * ```typescript
5186
- * const storage = new SyncStorage(
5187
- * localStorage,
5188
- * [
5189
- * new JSONSerializer(),
5190
- * new AESEncryptor('secret-key')
5191
- * ]
5192
- * );
5193
- * ```
5194
- *
5195
- * @example No pipes (direct storage)
5196
- * ```typescript
5197
- * const storage = new SyncStorage(localStorage);
5198
- * // Data stored as-is without transformation
5199
- * ```
5200
- */
5201
- constructor(storage, pipes = []) {
5202
- this.storage = storage;
5203
- this.pipes = (Array.isArray(pipes) ? pipes : [pipes]).map((p) => toPipeValue(p)).filter((p) => p != null);
5118
+
5119
+ // src/storage/utils/createStoragePlugin.ts
5120
+ function createStoragePluginWithStorage(storage) {
5121
+ return {
5122
+ get: storage.getItem.bind(storage),
5123
+ set: storage.setItem.bind(storage),
5124
+ remove: storage.removeItem.bind(storage),
5125
+ clear: storage.clear.bind(storage),
5126
+ type: "storage"
5127
+ };
5128
+ }
5129
+ function isSerializer(plugin) {
5130
+ return typeof plugin === "object" && plugin !== null && "serialize" in plugin && "deserialize" in plugin;
5131
+ }
5132
+ function isEncryptor(plugin) {
5133
+ return typeof plugin === "object" && plugin !== null && "encrypt" in plugin && "decrypt" in plugin;
5134
+ }
5135
+ function createStoragePlugin(plugins) {
5136
+ if (Array.isArray(plugins)) {
5137
+ return plugins.map((plugin) => {
5138
+ if (isSerializer(plugin)) {
5139
+ return {
5140
+ get: (_key, value) => plugin.deserialize(
5141
+ value
5142
+ ),
5143
+ set: (_key, value) => plugin.serialize(value),
5144
+ type: "serializer"
5145
+ };
5146
+ } else if (isEncryptor(plugin)) {
5147
+ return {
5148
+ get: (_key, value) => plugin.decrypt(
5149
+ value
5150
+ ),
5151
+ set: (_key, value) => plugin.encrypt(value),
5152
+ type: "encryptor"
5153
+ };
5154
+ } else if (isStorage(plugin)) {
5155
+ return createStoragePluginWithStorage(plugin);
5156
+ }
5157
+ return plugin;
5158
+ });
5204
5159
  }
5160
+ return [createStoragePluginWithStorage(plugins)];
5161
+ }
5162
+
5163
+ // src/storage/impl/StorageExecutor.ts
5164
+ var StorageExecutor = class {
5165
+ plugins = [];
5205
5166
  /**
5206
- * Internal pipe value list with pre-determined types
5207
- *
5208
- * Stores the processed pipeline of transformations that will be
5209
- * applied to data during storage and retrieval operations.
5210
- *
5211
- * @protected
5212
- */
5213
- pipes;
5214
- /**
5215
- * Get the number of items in the primary storage
5216
- *
5217
- * Returns the count of items in the primary storage backend only.
5218
- * Does not include items in intermediate storage layers.
5219
- *
5220
- * @override
5221
- * @returns Number of items in primary storage
5167
+ * Builds the plugin list from either a single `StorageInterface` or an array whose last element
5168
+ * is the backing storage and preceding elements are transformers (e.g. serializer, encryptor).
5222
5169
  *
5223
- * @example
5224
- * ```typescript
5225
- * console.log(storage.length); // 5
5226
- * storage.setItem('newKey', 'value');
5227
- * console.log(storage.length); // 6
5228
- * ```
5170
+ * @param plugins - Single storage instance or tuple of `[ ...transformers, storage ]`
5229
5171
  */
5230
- get length() {
5231
- return this.storage.length;
5172
+ constructor(plugins) {
5173
+ this.plugins = createStoragePlugin(plugins);
5232
5174
  }
5233
5175
  /**
5234
- * Store a value with pipeline processing
5235
- *
5236
- * Processes the value through the configured pipeline (serialization,
5237
- * encryption, intermediate storage) before storing in the primary storage.
5238
- *
5239
- * Pipeline execution:
5240
- * 1. Apply serialization (if configured)
5241
- * 2. Apply encryption (if configured)
5242
- * 3. Store in intermediate storage layers (if configured)
5243
- * 4. Store in primary storage
5176
+ * Writes value through the plugin chain (forward). Each plugin may return a transformed value for the next.
5244
5177
  *
5245
5178
  * @override
5246
- * @template T - Type of value to store
5247
- * @param key - Storage key
5248
- * @param value - Value to store
5249
- * @param options - Optional storage options (e.g., expiration)
5250
- *
5251
- * @example Basic storage
5252
- * ```typescript
5253
- * storage.setItem('user', { id: 1, name: 'John' });
5254
- * ```
5255
- *
5256
- * @example With options
5257
- * ```typescript
5258
- * storage.setItem('session', { token: 'abc' }, { expire: 3600 });
5259
- * ```
5260
5179
  */
5261
5180
  setItem(key, value, options) {
5262
- let processedValue = value;
5263
- for (const currentPipe of this.pipes) {
5264
- const { type, pipe } = currentPipe;
5265
- if (type === "storage") {
5266
- pipe.setItem(
5267
- key,
5268
- processedValue,
5269
- options
5270
- );
5271
- } else {
5272
- const result = operationMaps.setItem[type](
5273
- // @ts-expect-error
5274
- pipe,
5275
- [processedValue]
5276
- );
5277
- if (result != null) {
5278
- processedValue = result;
5279
- }
5181
+ let finalValue = value;
5182
+ for (const plugin of this.plugins) {
5183
+ const result = plugin.set(key, finalValue, options);
5184
+ if (result !== void 0) {
5185
+ finalValue = result;
5280
5186
  }
5281
5187
  }
5282
- this.storage.setItem(key, processedValue, options);
5283
5188
  }
5284
5189
  /**
5285
- * Retrieve a value with pipeline processing
5190
+ * Reads value through the plugin chain in reverse order.
5286
5191
  *
5287
- * Retrieves the value from storage and processes it through the pipeline
5288
- * in reverse order (decryption, deserialization) to restore the original value.
5192
+ * **Multiple storage plugins:** When the pipeline contains more than one storage plugin (e.g.
5193
+ * `[sessionStorage, localStorage]`), getItem **only uses the value from the last storage** (the
5194
+ * plugin at the end of the array). The iteration runs from tail to head; the first storage plugin
5195
+ * encountered (which is the last in the array) is the only one whose `get` result is used. All
5196
+ * other storage plugins are skipped and their values are ignored. This ensures a single, well-defined
5197
+ * read source (e.g. prefer localStorage over sessionStorage when both are in the chain).
5289
5198
  *
5290
- * Retrieval strategy:
5291
- * 1. Try to retrieve from primary storage
5292
- * 2. If not found, try intermediate storage layers (in reverse order)
5293
- * 3. Apply decryption (if configured)
5294
- * 4. Apply deserialization (if configured)
5295
- * 5. Return processed value or default
5199
+ * Pipe plugins (serializer, encryptor, etc.) are always applied to transform the value read from
5200
+ * that single storage. If no value is found, returns `defaultValue` when provided, otherwise `null`.
5296
5201
  *
5297
5202
  * @override
5298
- * @template T - Type of value to retrieve
5299
- * @param key - Storage key
5300
- * @param defaultValue - Default value if key not found
5301
- * @param options - Optional retrieval options
5302
- * @returns Retrieved value or default, `null` if not found and no default
5303
- *
5304
- * @example Basic retrieval
5305
- * ```typescript
5306
- * const user = storage.getItem('user');
5307
- * if (user) {
5308
- * console.log(user.name);
5309
- * }
5310
- * ```
5311
- *
5312
- * @example With default value
5313
- * ```typescript
5314
- * const config = storage.getItem('config', { theme: 'light' });
5315
- * console.log(config.theme); // 'light' if not found
5316
- * ```
5317
5203
  */
5318
5204
  getItem(key, defaultValue, options) {
5319
- let processedValue = this.storage.getItem(key, defaultValue, options);
5320
- if (processedValue == null) {
5321
- const reversedPipes2 = [...this.pipes].reverse();
5322
- for (const currentPipe of reversedPipes2) {
5323
- const { type, pipe } = currentPipe;
5324
- if (type !== "storage") {
5325
- continue;
5326
- }
5327
- const res = operationMaps.getItem[type](pipe, [
5328
- key,
5329
- processedValue,
5330
- options
5331
- ]);
5332
- if (res != null) {
5333
- processedValue = res;
5334
- break;
5335
- }
5336
- }
5337
- }
5338
- if (processedValue == null) {
5339
- return defaultValue ?? null;
5340
- }
5341
- const reversedPipes = [...this.pipes].reverse();
5342
- for (const currentPipe of reversedPipes) {
5343
- const { type, pipe } = currentPipe;
5344
- if (type === "storage") {
5205
+ const lastIndex = this.plugins.length - 1;
5206
+ let finalValue;
5207
+ let storageHasValue = false;
5208
+ for (let i = lastIndex; i >= 0; i--) {
5209
+ const plugin = this.plugins[i];
5210
+ const isStoragePlugin = plugin.type === "storage";
5211
+ if (isStoragePlugin && storageHasValue) {
5345
5212
  continue;
5346
5213
  }
5347
- processedValue = operationMaps.getItem[type](
5348
- pipe,
5349
- [processedValue]
5350
- );
5351
- }
5352
- if (processedValue !== null && this.storage.getRawValue) {
5353
- processedValue = this.storage.getRawValue(processedValue, options);
5214
+ const result = plugin.get(key, finalValue, options);
5215
+ if (result !== void 0) {
5216
+ finalValue = result;
5217
+ if (isStoragePlugin) {
5218
+ storageHasValue = true;
5219
+ }
5220
+ }
5354
5221
  }
5355
- return processedValue ?? null;
5222
+ return finalValue ?? defaultValue ?? null;
5356
5223
  }
5357
5224
  /**
5358
- * Delete data items, delete from all storage layers
5225
+ * Removes item for the given key from all plugins that implement `remove`.
5359
5226
  *
5360
5227
  * @override
5361
- * @param key - Storage key
5362
- * @param options - Delete options
5363
5228
  */
5364
5229
  removeItem(key, options) {
5365
- this.storage.removeItem(key, options);
5366
- this.pipes.filter((p) => p.type === "storage").forEach((p) => {
5367
- p.pipe.removeItem(key, options);
5368
- });
5230
+ for (const plugin of this.plugins) {
5231
+ plugin.remove?.(key, options);
5232
+ }
5369
5233
  }
5370
5234
  /**
5371
- * Clear all data, including storage in the pipeline
5372
-
5373
- * @override
5374
- */
5235
+ * Clears data in all plugins that implement `clear`.
5236
+ *
5237
+ * @override
5238
+ */
5375
5239
  clear() {
5376
- this.storage.clear();
5377
- this.pipes.filter((p) => p.type === "storage").forEach((p) => {
5378
- p.pipe.clear();
5379
- });
5240
+ for (const plugin of this.plugins) {
5241
+ plugin.clear?.();
5242
+ }
5380
5243
  }
5381
5244
  };
5382
5245
  export {
@@ -5411,15 +5274,20 @@ export {
5411
5274
  ResponsePlugin,
5412
5275
  RetryPlugin,
5413
5276
  SimpleUrlBuilder,
5414
- SyncStorage,
5277
+ StorageExecutor,
5415
5278
  appendHeaders,
5416
5279
  createAbortPromise,
5280
+ createStoragePlugin,
5281
+ createStoragePluginWithStorage,
5417
5282
  hasObjectKey,
5418
5283
  hasObjectKeyWithValue,
5419
5284
  isAbortError,
5420
5285
  isAbsoluteUrl,
5421
5286
  isAsString,
5287
+ isEncryptor,
5422
5288
  isRequestAdapterResponse,
5289
+ isSerializer,
5290
+ isStorage,
5423
5291
  normalizeHookNames,
5424
5292
  raceWithAbort,
5425
5293
  runPluginHook,
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@qlover/fe-corekit",
3
3
  "description": "A corekit for frontwork",
4
- "version": "3.1.1",
4
+ "version": "3.2.1",
5
5
  "private": false,
6
6
  "type": "module",
7
7
  "files": [
@@ -11,15 +11,43 @@
11
11
  ],
12
12
  "main": "./dist/index.cjs",
13
13
  "module": "./dist/index.js",
14
- "browser": "./dist/index.iife.js",
15
14
  "types": "./dist/index.d.ts",
16
15
  "exports": {
17
16
  "./package.json": "./package.json",
18
17
  ".": {
19
18
  "types": "./dist/index.d.ts",
20
19
  "import": "./dist/index.js",
21
- "browser": "./dist/index.iife.js",
22
20
  "require": "./dist/index.cjs"
21
+ },
22
+ "./aborter": {
23
+ "types": "./dist/aborter/index.d.ts",
24
+ "import": "./dist/aborter/index.js",
25
+ "require": "./dist/aborter/index.cjs"
26
+ },
27
+ "./encrypt": {
28
+ "types": "./dist/encrypt/index.d.ts",
29
+ "import": "./dist/encrypt/index.js",
30
+ "require": "./dist/encrypt/index.cjs"
31
+ },
32
+ "./executor": {
33
+ "types": "./dist/executor/index.d.ts",
34
+ "import": "./dist/executor/index.js",
35
+ "require": "./dist/executor/index.cjs"
36
+ },
37
+ "./request": {
38
+ "types": "./dist/request/index.d.ts",
39
+ "import": "./dist/request/index.js",
40
+ "require": "./dist/request/index.cjs"
41
+ },
42
+ "./serializer": {
43
+ "types": "./dist/serializer/index.d.ts",
44
+ "import": "./dist/serializer/index.js",
45
+ "require": "./dist/serializer/index.cjs"
46
+ },
47
+ "./storage": {
48
+ "types": "./dist/storage/index.d.ts",
49
+ "import": "./dist/storage/index.js",
50
+ "require": "./dist/storage/index.cjs"
23
51
  }
24
52
  },
25
53
  "repository": {