@crustjs/store 0.4.1 → 0.4.3

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.
Files changed (2) hide show
  1. package/dist/index.js +26 -10
  2. package/package.json +68 -70
package/dist/index.js CHANGED
@@ -255,10 +255,22 @@ function resolveStorePath(dirPath, name) {
255
255
  //#endregion
256
256
  //#region src/merge.ts
257
257
  /**
258
+ * Sets an own enumerable data property. Plain assignment would invoke the
259
+ * inherited `__proto__` setter instead of storing a `__proto__` field.
260
+ */
261
+ function setDocumentValue(document, key, value) {
262
+ Object.defineProperty(document, key, {
263
+ value,
264
+ writable: true,
265
+ enumerable: true,
266
+ configurable: true
267
+ });
268
+ }
269
+ /**
258
270
  * Applies field defaults to a persisted config object.
259
271
  *
260
272
  * For each field defined in `fields`:
261
- * - If the key exists in `persisted`, the persisted value is used.
273
+ * - If `persisted` has the key as an own property, the persisted value is used.
262
274
  * - If the key is missing and the field has a `default`, the default is used.
263
275
  * Defaults are deep-copied to prevent shared nested mutation.
264
276
  * - If the key is missing and no default exists, the field is omitted
@@ -274,10 +286,10 @@ function resolveStorePath(dirPath, name) {
274
286
  */
275
287
  function applyFieldDefaults(persisted, fields, pruneUnknown = true) {
276
288
  const result = {};
277
- for (const [key, def] of Object.entries(fields)) if (persisted && key in persisted) result[key] = persisted[key];
278
- else if ("default" in def && def.default !== void 0) result[key] = structuredClone(def.default);
289
+ for (const [key, def] of Object.entries(fields)) if (persisted && Object.hasOwn(persisted, key)) setDocumentValue(result, key, persisted[key]);
290
+ else if ("default" in def && def.default !== void 0) setDocumentValue(result, key, structuredClone(def.default));
279
291
  if (!pruneUnknown && persisted !== void 0) {
280
- for (const [key, value] of Object.entries(persisted)) if (!(key in fields)) result[key] = value;
292
+ for (const [key, value] of Object.entries(persisted)) if (!Object.hasOwn(fields, key)) setDocumentValue(result, key, value);
281
293
  }
282
294
  return result;
283
295
  }
@@ -488,21 +500,25 @@ function createStore(options) {
488
500
  function normalizeStateTypes(state) {
489
501
  const normalized = { ...state };
490
502
  for (const [key, def] of Object.entries(fields)) {
491
- if (!(key in normalized) || def.schema !== void 0) continue;
503
+ if (!Object.hasOwn(normalized, key)) {
504
+ if (key in normalized) setDocumentValue(normalized, key, void 0);
505
+ continue;
506
+ }
507
+ if (def.schema !== void 0) continue;
492
508
  const value = normalized[key];
493
509
  if (value === void 0) continue;
494
510
  if (def.array === true && Array.isArray(value)) {
495
- normalized[key] = value.map((item) => coerceByType(item, def.type));
511
+ setDocumentValue(normalized, key, value.map((item) => coerceByType(item, def.type)));
496
512
  continue;
497
513
  }
498
- normalized[key] = coerceByType(value, def.type);
514
+ setDocumentValue(normalized, key, coerceByType(value, def.type));
499
515
  }
500
516
  return normalized;
501
517
  }
502
518
  async function runFieldValidators(mutableState, operation) {
503
519
  const issues = [];
504
520
  for (const [key, def] of Object.entries(fields)) {
505
- const value = mutableState[key];
521
+ const value = Object.hasOwn(mutableState, key) ? mutableState[key] : void 0;
506
522
  if (value === void 0 && def.schema === void 0) {
507
523
  if (def.default !== void 0) issues.push({
508
524
  message: expectedTypeMessage(def),
@@ -534,7 +550,7 @@ function createStore(options) {
534
550
  if (isFieldValueResult(result)) {
535
551
  const transformed = result.value;
536
552
  if (operation === "read") {
537
- if (def.schema !== void 0) mutableState[key] = transformed;
553
+ if (def.schema !== void 0) setDocumentValue(mutableState, key, transformed);
538
554
  continue;
539
555
  }
540
556
  if (def.schema === void 0 && !matchesDeclaredType(def, transformed)) {
@@ -563,7 +579,7 @@ function createStore(options) {
563
579
  });
564
580
  continue;
565
581
  }
566
- mutableState[key] = transformed;
582
+ setDocumentValue(mutableState, key, transformed);
567
583
  }
568
584
  }
569
585
  }
package/package.json CHANGED
@@ -1,71 +1,69 @@
1
1
  {
2
- "name": "@crustjs/store",
3
- "version": "0.4.1",
4
- "description": "DX-first, typed persistence for CLI apps with config/data/state/cache separation",
5
- "type": "module",
6
- "sideEffects": false,
7
- "license": "MIT",
8
- "author": "chenxin-yan",
9
- "repository": {
10
- "type": "git",
11
- "url": "git+https://github.com/chenxin-yan/crust.git",
12
- "directory": "packages/store"
13
- },
14
- "homepage": "https://crustjs.com",
15
- "bugs": {
16
- "url": "https://github.com/chenxin-yan/crust/issues"
17
- },
18
- "keywords": [
19
- "cli",
20
- "config",
21
- "data",
22
- "state",
23
- "cache",
24
- "store",
25
- "persistence",
26
- "xdg",
27
- "bun",
28
- "typescript"
29
- ],
30
- "files": [
31
- "dist"
32
- ],
33
- "exports": {
34
- ".": {
35
- "types": "./dist/index.d.ts",
36
- "import": "./dist/index.js"
37
- }
38
- },
39
- "publishConfig": {
40
- "access": "public"
41
- },
42
- "scripts": {
43
- "build": "tsdown",
44
- "dev": "tsdown --watch",
45
- "check:types": "tsc --noEmit",
46
- "test": "bun test",
47
- "prepack": "cp ../../LICENSE LICENSE",
48
- "postpack": "rm -f LICENSE"
49
- },
50
- "dependencies": {
51
- "@crustjs/utils": "^0.1.0"
52
- },
53
- "devDependencies": {
54
- "@crustjs/config": "0.0.0",
55
- "zod": "^4.5.4",
56
- "tsdown": "^0.23.0"
57
- },
58
- "peerDependencies": {
59
- "typescript": "^7.0.0"
60
- },
61
- "peerDependenciesMeta": {
62
- "typescript": {
63
- "optional": true
64
- }
65
- },
66
- "engines": {
67
- "bun": ">=1.4.0",
68
- "node": ">=22",
69
- "deno": ">=2.8"
70
- }
71
- }
2
+ "name": "@crustjs/store",
3
+ "version": "0.4.3",
4
+ "description": "DX-first, typed persistence for CLI apps with config/data/state/cache separation",
5
+ "type": "module",
6
+ "sideEffects": false,
7
+ "license": "MIT",
8
+ "author": "chenxin-yan",
9
+ "repository": {
10
+ "type": "git",
11
+ "url": "git+https://github.com/chenxin-yan/crust.git",
12
+ "directory": "packages/store"
13
+ },
14
+ "homepage": "https://crustjs.com",
15
+ "bugs": {
16
+ "url": "https://github.com/chenxin-yan/crust/issues"
17
+ },
18
+ "keywords": [
19
+ "cli",
20
+ "config",
21
+ "data",
22
+ "state",
23
+ "cache",
24
+ "store",
25
+ "persistence",
26
+ "xdg",
27
+ "bun",
28
+ "typescript"
29
+ ],
30
+ "files": [
31
+ "dist"
32
+ ],
33
+ "exports": {
34
+ ".": {
35
+ "types": "./dist/index.d.ts",
36
+ "import": "./dist/index.js"
37
+ }
38
+ },
39
+ "publishConfig": {
40
+ "access": "public"
41
+ },
42
+ "dependencies": {
43
+ "@crustjs/utils": "^0.2.1"
44
+ },
45
+ "devDependencies": {
46
+ "@crustjs/config": "0.0.0",
47
+ "zod": "^4.6.5",
48
+ "vite-plus": "1.0.0"
49
+ },
50
+ "peerDependencies": {
51
+ "typescript": "^7.0.0"
52
+ },
53
+ "peerDependenciesMeta": {
54
+ "typescript": {
55
+ "optional": true
56
+ }
57
+ },
58
+ "engines": {
59
+ "bun": ">=1.4.0",
60
+ "node": ">=24",
61
+ "deno": ">=2.8"
62
+ },
63
+ "scripts": {
64
+ "build": "vp run build:task",
65
+ "dev": "vp pack --watch",
66
+ "check:types": "vp run check:types:task",
67
+ "test": "vp run test:task"
68
+ }
69
+ }