@poveste/app 0.18.2 → 0.19.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/THIRD_PARTY_NOTICES.md +159 -0
- package/dist/bundled/app.css +4 -4
- package/dist/bundled/components/command/PromptSelect.vue_vue_type_script_setup_true_lang.js +1 -1
- package/dist/bundled/components/story/StoryNativeScope.js +14 -0
- package/dist/bundled/components/story/StoryVariantGridItem.js +1 -1
- package/dist/bundled/components/story/StoryVariantGridItem.vue_vue_type_script_setup_true_lang.js +23 -16
- package/dist/bundled/components/story/StoryVariantSinglePreviewNative.vue_vue_type_script_setup_true_lang.js +22 -15
- package/dist/bundled/components/story/StoryVariantSinglePreviewRemote.vue_vue_type_script_setup_true_lang.js +8 -2
- package/dist/bundled/index.js +12 -3
- package/dist/bundled/sandbox.js +32 -2
- package/dist/bundled/stores/preview-settings.js +29 -4
- package/dist/bundled/util/color-scheme.js +41 -1
- package/dist/bundled/util/commands.js +16 -4
- package/dist/bundled/util/const.js +2 -1
- package/package.json +6 -4
- package/src/app/components/command/PromptSelect.vue +2 -1
- package/src/app/components/story/StoryNativeScope.vue +23 -0
- package/src/app/components/story/StoryVariantGridItem.vue +14 -10
- package/src/app/components/story/StoryVariantSinglePreviewNative.vue +15 -10
- package/src/app/components/story/StoryVariantSinglePreviewRemote.vue +13 -2
- package/src/app/index.ts +14 -2
- package/src/app/sandbox.ts +41 -2
- package/src/app/stores/preview-settings.ts +48 -10
- package/src/app/types.ts +7 -1
- package/src/app/util/color-scheme.ts +44 -0
- package/src/app/util/commands.ts +19 -7
- package/src/app/util/const.ts +1 -0
- package/src/__tests__/base-select.spec.ts +0 -25
- package/src/__tests__/color-scheme.spec.ts +0 -55
- package/src/__tests__/font.spec.ts +0 -76
- package/src/__tests__/grid-slots.spec.ts +0 -84
- package/src/__tests__/icons.spec.ts +0 -26
- package/src/__tests__/postcss-scope-wrapper.spec.ts +0 -65
- package/src/__tests__/render-tracker.spec.ts +0 -38
- package/src/__tests__/reset.spec.ts +0 -53
- package/src/__tests__/source-code.spec.ts +0 -55
- package/src/__tests__/state-bridge.spec.ts +0 -155
- package/src/__tests__/state-clone.spec.ts +0 -120
- package/src/__tests__/state-types.spec.ts +0 -175
- package/src/__tests__/uncaught.spec.ts +0 -36
|
@@ -1,175 +0,0 @@
|
|
|
1
|
-
import { isEquivalent } from '@poveste/shared'
|
|
2
|
-
import { describe, expect, it } from 'vitest'
|
|
3
|
-
import { markRaw, reactive } from 'vue'
|
|
4
|
-
import { toRawDeep } from '../app/util/state'
|
|
5
|
-
|
|
6
|
-
/**
|
|
7
|
-
* What the walker does to a value that is not a plain object.
|
|
8
|
-
*
|
|
9
|
-
* `postMessage` is structured clone, so structured clone is the boundary that
|
|
10
|
-
* actually exists — and it carries `Date`, `Map`, `Set`, `RegExp`, `Error`,
|
|
11
|
-
* typed arrays and `BigInt` intact. The walker flattened all of them to `{}`
|
|
12
|
-
* before the transport was ever reached, so the question is not which types to
|
|
13
|
-
* support but which ones we were destroying on the way (#977).
|
|
14
|
-
*/
|
|
15
|
-
class Point {
|
|
16
|
-
constructor(public x: number, public y: number) {}
|
|
17
|
-
get len() { return Math.hypot(this.x, this.y) }
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
describe('a value the transport can carry', () => {
|
|
21
|
-
it('keeps a Date, rather than emptying it', () => {
|
|
22
|
-
const out = toRawDeep({ at: new Date(1790071200000) }, true)
|
|
23
|
-
|
|
24
|
-
expect(out.at).toBeInstanceOf(Date)
|
|
25
|
-
expect(out.at.getTime()).toBe(1790071200000)
|
|
26
|
-
})
|
|
27
|
-
|
|
28
|
-
it('keeps a Map and a Set with their contents', () => {
|
|
29
|
-
const out = toRawDeep({ m: new Map([['a', 1]]), s: new Set([1, 2, 3]) }, true)
|
|
30
|
-
|
|
31
|
-
expect(out.m).toBeInstanceOf(Map)
|
|
32
|
-
expect(out.m.get('a')).toBe(1)
|
|
33
|
-
expect(out.s).toBeInstanceOf(Set)
|
|
34
|
-
expect(out.s.size).toBe(3)
|
|
35
|
-
})
|
|
36
|
-
|
|
37
|
-
it('keeps a RegExp and an Error', () => {
|
|
38
|
-
const out = toRawDeep({ re: /ab+c/gi, err: new Error('boom') }, true)
|
|
39
|
-
|
|
40
|
-
expect(out.re).toBeInstanceOf(RegExp)
|
|
41
|
-
expect(out.re.source).toBe('ab+c')
|
|
42
|
-
expect(out.err).toBeInstanceOf(Error)
|
|
43
|
-
expect(out.err.message).toBe('boom')
|
|
44
|
-
})
|
|
45
|
-
|
|
46
|
-
it('keeps a typed array as one, rather than as an index map', () => {
|
|
47
|
-
const out = toRawDeep({ bytes: new Uint8Array([1, 2, 3]) }, true)
|
|
48
|
-
|
|
49
|
-
expect(out.bytes).toBeInstanceOf(Uint8Array)
|
|
50
|
-
expect([...out.bytes]).toEqual([1, 2, 3])
|
|
51
|
-
})
|
|
52
|
-
|
|
53
|
-
it('detaches it, so a later mutation does not reach the copy', () => {
|
|
54
|
-
const at = new Date(1790071200000)
|
|
55
|
-
const out = toRawDeep({ at }, true)
|
|
56
|
-
|
|
57
|
-
at.setTime(0)
|
|
58
|
-
|
|
59
|
-
expect(out.at.getTime()).toBe(1790071200000)
|
|
60
|
-
})
|
|
61
|
-
})
|
|
62
|
-
|
|
63
|
-
describe('a value the transport refuses', () => {
|
|
64
|
-
it('drops a URL rather than flattening it, the way a function is dropped', () => {
|
|
65
|
-
// Structured clone throws `DataCloneError` on a `URL`. Passing it through
|
|
66
|
-
// would turn a silent flattening into a thrown exception mid-sync, which is
|
|
67
|
-
// worse than what it replaces.
|
|
68
|
-
const out = toRawDeep({ href: new URL('https://poveste.dev/a'), kept: 1 }, true)
|
|
69
|
-
|
|
70
|
-
expect(Object.hasOwn(out, 'href')).toBe(false)
|
|
71
|
-
expect(out.kept).toBe(1)
|
|
72
|
-
})
|
|
73
|
-
|
|
74
|
-
it('leaves what it did send intact', () => {
|
|
75
|
-
const out = toRawDeep({ before: 1, href: new URL('https://poveste.dev/a'), after: 2 }, true)
|
|
76
|
-
|
|
77
|
-
expect(out).toEqual({ before: 1, after: 2 })
|
|
78
|
-
})
|
|
79
|
-
})
|
|
80
|
-
|
|
81
|
-
describe('a class instance', () => {
|
|
82
|
-
// Structured clone carries own fields and drops the prototype, so "faithful"
|
|
83
|
-
// has no meaning here. Plain is the answer, and it is now a stated one.
|
|
84
|
-
it('arrives as plain data, with its fields but not its accessors', () => {
|
|
85
|
-
const out = toRawDeep({ p: new Point(3, 4) }, true)
|
|
86
|
-
|
|
87
|
-
expect(out.p).toEqual({ x: 3, y: 4 })
|
|
88
|
-
expect(out.p.len).toBeUndefined()
|
|
89
|
-
})
|
|
90
|
-
})
|
|
91
|
-
|
|
92
|
-
describe('the two halves of the sync agree', () => {
|
|
93
|
-
// `isEquivalent` refuses to compare anything that is not a plain object,
|
|
94
|
-
// because "two distinct `Date`s would otherwise compare equal on an empty key
|
|
95
|
-
// list". That defence could not fire: it is handed the walker's output, and
|
|
96
|
-
// by then the `Date` was already `{}` (#977).
|
|
97
|
-
it('so two different dates are not equivalent after the walk', () => {
|
|
98
|
-
const a = toRawDeep({ at: new Date(1790071200000) }, true)
|
|
99
|
-
const b = toRawDeep({ at: new Date(0) }, true)
|
|
100
|
-
|
|
101
|
-
expect(isEquivalent(a, b)).toBe(false)
|
|
102
|
-
})
|
|
103
|
-
|
|
104
|
-
it('and the in-realm walk carries one by reference, which `Object.is` settles', () => {
|
|
105
|
-
const at = new Date(1790071200000)
|
|
106
|
-
|
|
107
|
-
expect(toRawDeep({ at }, false).at).toBe(at)
|
|
108
|
-
})
|
|
109
|
-
})
|
|
110
|
-
|
|
111
|
-
describe('a value that is reactive, which state always is', () => {
|
|
112
|
-
// The one that would have shipped a second silent failure. Vue proxies `Map`
|
|
113
|
-
// and `Set` but not `Date`, `RegExp`, `Error` or typed arrays, and
|
|
114
|
-
// `structuredClone` throws `DataCloneError` on the proxy while the raw
|
|
115
|
-
// object clones fine. Without `toRaw` a `Map` in reactive state is refused
|
|
116
|
-
// here and dropped — a different silence, not a fix for the first.
|
|
117
|
-
it('clones a reactive Map rather than refusing it', () => {
|
|
118
|
-
const state = reactive({ m: new Map([['a', 1]]) })
|
|
119
|
-
|
|
120
|
-
const out = toRawDeep(state, true)
|
|
121
|
-
|
|
122
|
-
expect(out.m).toBeInstanceOf(Map)
|
|
123
|
-
expect(out.m.get('a')).toBe(1)
|
|
124
|
-
})
|
|
125
|
-
|
|
126
|
-
it('clones a reactive Set the same way', () => {
|
|
127
|
-
const out = toRawDeep(reactive({ s: new Set([1, 2]) }), true)
|
|
128
|
-
|
|
129
|
-
expect(out.s).toBeInstanceOf(Set)
|
|
130
|
-
expect(out.s.size).toBe(2)
|
|
131
|
-
})
|
|
132
|
-
|
|
133
|
-
it('and a reactive Date, which Vue does not proxy', () => {
|
|
134
|
-
const out = toRawDeep(reactive({ at: new Date(1790071200000) }), true)
|
|
135
|
-
|
|
136
|
-
expect(out.at.getTime()).toBe(1790071200000)
|
|
137
|
-
})
|
|
138
|
-
})
|
|
139
|
-
|
|
140
|
-
describe('this walk\'s own rules reach inside a Map and a Set', () => {
|
|
141
|
-
// An array already comes out shorter for a function or a marked value. A
|
|
142
|
-
// container is the same problem, and answering it by refusing the whole
|
|
143
|
-
// `Map` would lose the keys beside it.
|
|
144
|
-
it('drops a function from a Map, and keeps the rest', () => {
|
|
145
|
-
const out = toRawDeep({ m: new Map<string, any>([['fn', () => {}], ['kept', 1]]) }, true)
|
|
146
|
-
|
|
147
|
-
expect(out.m.has('fn')).toBe(false)
|
|
148
|
-
expect(out.m.get('kept')).toBe(1)
|
|
149
|
-
})
|
|
150
|
-
|
|
151
|
-
it('drops a marked value from a Set, and keeps the rest', () => {
|
|
152
|
-
const out = toRawDeep({ s: new Set([markRaw({ heavy: true }), 'kept']) }, true)
|
|
153
|
-
|
|
154
|
-
expect([...out.s]).toEqual(['kept'])
|
|
155
|
-
})
|
|
156
|
-
|
|
157
|
-
it('survives a cycle that runs through a Map', () => {
|
|
158
|
-
const root: Record<string, any> = { name: 'root' }
|
|
159
|
-
root.m = new Map([['back', root]])
|
|
160
|
-
|
|
161
|
-
const out = toRawDeep(root, true)
|
|
162
|
-
|
|
163
|
-
expect(out.m.get('back')).toBe(out)
|
|
164
|
-
})
|
|
165
|
-
})
|
|
166
|
-
|
|
167
|
-
describe('identity inside one walk', () => {
|
|
168
|
-
it('gives one Date appearing twice the same copy, as the transport would', () => {
|
|
169
|
-
const at = new Date(1790071200000)
|
|
170
|
-
|
|
171
|
-
const out = toRawDeep({ a: at, b: at }, true)
|
|
172
|
-
|
|
173
|
-
expect(out.a).toBe(out.b)
|
|
174
|
-
})
|
|
175
|
-
})
|
|
@@ -1,36 +0,0 @@
|
|
|
1
|
-
import { describe, expect, it } from 'vitest'
|
|
2
|
-
import { isBenignResizeObserverEvent } from '../app/util/uncaught'
|
|
3
|
-
|
|
4
|
-
/**
|
|
5
|
-
* The shape the sandbox's `error` listener sees. A browser-emitted
|
|
6
|
-
* ResizeObserver notification carries a null `error`; a real throw carries the
|
|
7
|
-
* `Error` that was thrown.
|
|
8
|
-
*/
|
|
9
|
-
const notification = (message: string) => ({ error: null, message })
|
|
10
|
-
const thrown = (message: string) => ({ error: new Error(message), message: `Uncaught Error: ${message}` })
|
|
11
|
-
|
|
12
|
-
describe('a ResizeObserver loop notification', () => {
|
|
13
|
-
// Chrome's exact wording, trailing period included, as captured from a live
|
|
14
|
-
// observer loop.
|
|
15
|
-
it('is recognised as benign with the period Chrome sends', () => {
|
|
16
|
-
expect(isBenignResizeObserverEvent(notification('ResizeObserver loop completed with undelivered notifications.'))).toBe(true)
|
|
17
|
-
})
|
|
18
|
-
|
|
19
|
-
it('is recognised as benign without the period Firefox omits', () => {
|
|
20
|
-
expect(isBenignResizeObserverEvent(notification('ResizeObserver loop completed with undelivered notifications'))).toBe(true)
|
|
21
|
-
})
|
|
22
|
-
|
|
23
|
-
it('is recognised as benign in the older loop limit wording', () => {
|
|
24
|
-
expect(isBenignResizeObserverEvent(notification('ResizeObserver loop limit exceeded'))).toBe(true)
|
|
25
|
-
})
|
|
26
|
-
})
|
|
27
|
-
|
|
28
|
-
describe('an error the sandbox exists to report', () => {
|
|
29
|
-
it('is not treated as benign when a component throws the same text', () => {
|
|
30
|
-
expect(isBenignResizeObserverEvent(thrown('ResizeObserver loop completed with undelivered notifications'))).toBe(false)
|
|
31
|
-
})
|
|
32
|
-
|
|
33
|
-
it('is not treated as benign when an unrelated script fails with no error object', () => {
|
|
34
|
-
expect(isBenignResizeObserverEvent(notification('Script error.'))).toBe(false)
|
|
35
|
-
})
|
|
36
|
-
})
|