@poveste/shared 0.15.0 → 0.16.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/dist/state.d.ts CHANGED
@@ -1,5 +1,32 @@
1
1
  export declare function clone(data: any): any;
2
2
  export declare function omit(data: any, keys: string[]): Record<string, any>;
3
+ /**
4
+ * Whether Vue has marked this value raw, and the walkers must stop here.
5
+ *
6
+ * Read as a property rather than through Vue's `isReactive`, because this
7
+ * package does not depend on Vue — and because the flag is what has to be
8
+ * asked anyway: `markRaw` defines `__v_skip` **non-enumerable**, so it crosses
9
+ * neither `structuredClone` nor JSON, and by the time a value has been through
10
+ * the sandbox bridge the mark is gone (#957).
11
+ *
12
+ * A marked value looks plain to every predicate here: a component's exposed
13
+ * object and `markRaw({ … })` both have `Object.prototype` and own enumerable
14
+ * keys, so without this the compare and the copy descend into whatever is
15
+ * behind them.
16
+ *
17
+ * The read is guarded because state is user data and may be a proxy with a
18
+ * throwing trap; an unanswerable value is treated as unmarked, which is the
19
+ * behaviour this had before.
20
+ */
21
+ export declare function isMarkedRaw(value: any): boolean;
22
+ /**
23
+ * Exported because `poveste-app`'s walker has to draw the same line. The two
24
+ * disagreeing about what a non-plain value is was #977: the walker flattened a
25
+ * `Date` to `{}` before `isEquivalent` ever saw it, so the defence below —
26
+ * written for exactly that case — was handed two empty plain objects and
27
+ * reported them equivalent.
28
+ */
29
+ export declare function isPlainObject(value: any): boolean;
3
30
  /**
4
31
  * Structural comparison, deliberately narrow: it recurses into plain objects and
5
32
  * arrays and compares everything else by `Object.is`.
package/dist/state.js CHANGED
@@ -24,7 +24,43 @@ export function omit(data, keys) {
24
24
  }
25
25
  return copy;
26
26
  }
27
- function isPlainObject(value) {
27
+ /**
28
+ * Whether Vue has marked this value raw, and the walkers must stop here.
29
+ *
30
+ * Read as a property rather than through Vue's `isReactive`, because this
31
+ * package does not depend on Vue — and because the flag is what has to be
32
+ * asked anyway: `markRaw` defines `__v_skip` **non-enumerable**, so it crosses
33
+ * neither `structuredClone` nor JSON, and by the time a value has been through
34
+ * the sandbox bridge the mark is gone (#957).
35
+ *
36
+ * A marked value looks plain to every predicate here: a component's exposed
37
+ * object and `markRaw({ … })` both have `Object.prototype` and own enumerable
38
+ * keys, so without this the compare and the copy descend into whatever is
39
+ * behind them.
40
+ *
41
+ * The read is guarded because state is user data and may be a proxy with a
42
+ * throwing trap; an unanswerable value is treated as unmarked, which is the
43
+ * behaviour this had before.
44
+ */
45
+ export function isMarkedRaw(value) {
46
+ if (value === null || typeof value !== 'object') {
47
+ return false;
48
+ }
49
+ try {
50
+ return value.__v_skip === true;
51
+ }
52
+ catch {
53
+ return false;
54
+ }
55
+ }
56
+ /**
57
+ * Exported because `poveste-app`'s walker has to draw the same line. The two
58
+ * disagreeing about what a non-plain value is was #977: the walker flattened a
59
+ * `Date` to `{}` before `isEquivalent` ever saw it, so the defence below —
60
+ * written for exactly that case — was handed two empty plain objects and
61
+ * reported them equivalent.
62
+ */
63
+ export function isPlainObject(value) {
28
64
  if (value === null || typeof value !== 'object') {
29
65
  return false;
30
66
  }
@@ -49,6 +85,12 @@ export function isEquivalent(a, b, seen) {
49
85
  if (Object.is(a, b)) {
50
86
  return true;
51
87
  }
88
+ // Same reason as the prototype test below, reached differently: a marked
89
+ // value is one Vue itself refuses to look inside, and two distinct ones are
90
+ // not the same value whatever their keys say.
91
+ if (isMarkedRaw(a) || isMarkedRaw(b)) {
92
+ return false;
93
+ }
52
94
  const bothArrays = Array.isArray(a) && Array.isArray(b);
53
95
  if (!bothArrays && !(isPlainObject(a) && isPlainObject(b))) {
54
96
  return false;
@@ -236,7 +278,7 @@ export function diffState(baseline, next) {
236
278
  * `Object.is`, so a reference is the identity the comparison is about.
237
279
  */
238
280
  function copyState(value, seen = new WeakMap()) {
239
- if (!Array.isArray(value) && !isPlainObject(value)) {
281
+ if (isMarkedRaw(value) || (!Array.isArray(value) && !isPlainObject(value))) {
240
282
  return value;
241
283
  }
242
284
  if (seen.has(value)) {
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@poveste/shared",
3
3
  "type": "module",
4
- "version": "0.15.0",
4
+ "version": "0.16.0",
5
5
  "description": "Shared utilities for Poveste",
6
6
  "author": {
7
7
  "name": "Sorin Gitlan",
@@ -33,24 +33,26 @@
33
33
  "dist"
34
34
  ],
35
35
  "engines": {
36
- "node": "^22.22.2 || ^24.15.0 || >=26.0.0"
36
+ "node": ">=22.22.2"
37
37
  },
38
38
  "peerDependencies": {
39
39
  "vite": "^8.0.0"
40
40
  },
41
41
  "dependencies": {
42
- "@poveste/vendors": "^0.15.0",
42
+ "@poveste/vendors": "^0.16.0",
43
43
  "@types/fs-extra": "^11.0.4",
44
44
  "chokidar": "^5.0.0",
45
45
  "markdown-it": "^15.0.2",
46
46
  "pathe": "^2.0.3",
47
47
  "picocolors": "^1.1.1",
48
- "unhead": "^3.1.0"
48
+ "unhead": "^3.4.1"
49
49
  },
50
50
  "devDependencies": {
51
+ "@types/node": "^26.6.3",
51
52
  "typescript": "6.0.3",
52
- "vite": "^8.2.0",
53
- "vitest": "^5.0.0"
53
+ "vite": "^8.3.1",
54
+ "vitest": "^5.0.2",
55
+ "vue-router": "^5.3.1"
54
56
  },
55
57
  "scripts": {
56
58
  "build": "rimraf dist && tsc",